View Javadoc
1   /*
2    * Licensed under the GPL License. You may not use this file except in compliance with the License.
3    * You may obtain a copy of the License at
4    *
5    *   https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
6    *
7    * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
8    * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
9    * PURPOSE.
10   */
11  package psiprobe;
12  
13  import java.io.ByteArrayInputStream;
14  import java.io.File;
15  import java.io.IOException;
16  import java.nio.charset.StandardCharsets;
17  import java.nio.file.Files;
18  import java.nio.file.Path;
19  import java.util.List;
20  import java.util.Locale;
21  import java.util.UUID;
22  import java.util.concurrent.CountDownLatch;
23  import java.util.concurrent.TimeUnit;
24  
25  import org.junit.jupiter.api.Assertions;
26  import org.junit.jupiter.api.Test;
27  import org.springframework.mock.web.MockHttpServletRequest;
28  import org.springframework.mock.web.MockHttpServletResponse;
29  
30  /**
31   * The Class UtilsTest.
32   */
33  class UtilsTest {
34  
35    /**
36     * To int test.
37     */
38    @Test
39    void toIntTest() {
40      Assertions.assertEquals(5, Utils.toInt("garbage", 5));
41      Assertions.assertEquals(3, Utils.toInt("3", 5));
42      Assertions.assertEquals(5, Utils.toInt("3 3", 5));
43      Assertions.assertEquals(5, Utils.toInt((String) null, 5));
44    }
45  
46    /**
47     * To int hex test.
48     */
49    @Test
50    void toIntHexTest() {
51      Assertions.assertEquals(5, Utils.toIntHex("garbage", 5));
52      Assertions.assertEquals(3, Utils.toIntHex("3", 5));
53      Assertions.assertEquals(3, Utils.toIntHex("#3", 5));
54      Assertions.assertEquals(5, Utils.toIntHex("3 3", 5));
55      Assertions.assertEquals(5, Utils.toIntHex((String) null, 5));
56    }
57  
58    /**
59     * To long test.
60     */
61    @Test
62    void toLongTest() {
63      Assertions.assertEquals(5L, Utils.toLong("garbage", 5L));
64      Assertions.assertEquals(3L, Utils.toLong("3", 5L));
65      Assertions.assertEquals(5L, Utils.toLong("3 3", 5L));
66      Assertions.assertEquals(5L, Utils.toLong((String) null, 5L));
67    }
68  
69    /**
70     * To long int test.
71     */
72    @Test
73    void toLongIntTest() {
74      Assertions.assertEquals(5, Utils.toLong((Long) null, 5));
75      Assertions.assertEquals(1, Utils.toLong(Long.valueOf(1), 5));
76    }
77  
78    /**
79     * To float test.
80     */
81    @Test
82    void toFloatTest() {
83      Assertions.assertEquals(5.0f, Utils.toFloat("garbage", 5.0f), 0.0);
84      Assertions.assertEquals(3.0f, Utils.toFloat("3", 5.0f), 0.0);
85      Assertions.assertEquals(5.0f, Utils.toFloat("3 3", 5.0f), 0.0);
86      Assertions.assertEquals(5.0f, Utils.toFloat((String) null, 5.0f), 0.0);
87    }
88  
89    /**
90     * Left pad test.
91     */
92    @Test
93    void leftPadTest() {
94      Assertions.assertEquals("0005", Utils.leftPad("5", 4, "0"));
95      Assertions.assertEquals("5", Utils.leftPad("5", 1, "0"));
96      Assertions.assertEquals("", Utils.leftPad(null, 4, "0"));
97    }
98  
99    /**
100    * Read stream and file test.
101    *
102    * @throws IOException Signals that an I/O exception has occurred.
103    */
104   @Test
105   void readStreamAndFileTest() throws IOException {
106     String payload = "alpha\nbeta";
107     byte[] bytes = payload.getBytes(StandardCharsets.UTF_8);
108     Assertions.assertEquals("alpha\nbeta\n",
109         Utils.readStream(new ByteArrayInputStream(bytes), StandardCharsets.UTF_8.name()));
110     // Unsupported charset names fall back to the platform default charset.
111     Assertions.assertEquals("alpha\nbeta\n",
112         Utils.readStream(new ByteArrayInputStream(bytes), "unsupported-charset"));
113 
114     File file = Files.createTempFile("utils-test-", ".txt").toFile();
115     Files.writeString(file.toPath(), payload, StandardCharsets.UTF_8);
116     try {
117       Assertions.assertEquals("alpha\nbeta\n", Utils.readFile(file, StandardCharsets.UTF_8.name()));
118     } finally {
119       Utils.delete(file);
120     }
121   }
122 
123   /**
124    * JSP encoding parser test.
125    *
126    * @throws IOException Signals that an I/O exception has occurred.
127    */
128   @Test
129   void getJspEncodingTest() throws IOException {
130     String pageEncodingJsp = "<%@ page pageEncoding=\"ISO-8859-1\" %>";
131     Assertions.assertEquals("ISO-8859-1", Utils.getJspEncoding(
132         new ByteArrayInputStream(pageEncodingJsp.getBytes(StandardCharsets.UTF_8))));
133 
134     String contentTypeJsp = "<%@ page contentType=\"text/html; charset=UTF-16\" %>";
135     Assertions.assertEquals("UTF-16", Utils
136         .getJspEncoding(new ByteArrayInputStream(contentTypeJsp.getBytes(StandardCharsets.UTF_8))));
137 
138     String defaultJsp = "<html><body>no directives</body></html>";
139     Assertions.assertEquals(StandardCharsets.UTF_8.name(), Utils
140         .getJspEncoding(new ByteArrayInputStream(defaultJsp.getBytes(StandardCharsets.UTF_8))));
141   }
142 
143   /**
144    * Locale name expansion test.
145    */
146   @Test
147   void getNamesForLocaleTest() {
148     List<String> names = Utils.getNamesForLocale("messages", new Locale("en", "US", "TX"));
149     Assertions.assertEquals(List.of("messages_en_US_TX", "messages_en_US", "messages_en"), names);
150     Assertions.assertTrue(Utils.getNamesForLocale("messages", Locale.ROOT).isEmpty());
151   }
152 
153   /**
154    * Thread lookup and threading support test.
155    *
156    * @throws InterruptedException Signals that an interrupted exception has occurred.
157    */
158   @Test
159   void threadAndThreadingSupportTest() throws InterruptedException {
160     Assertions.assertNull(Utils.getThreadByName(null));
161 
162     String threadName = "utils-test-" + UUID.randomUUID();
163     CountDownLatch started = new CountDownLatch(1);
164     CountDownLatch stop = new CountDownLatch(1);
165     Thread thread = new Thread(() -> {
166       started.countDown();
167       try {
168         stop.await(5, TimeUnit.SECONDS);
169       } catch (InterruptedException e) {
170         Thread.currentThread().interrupt();
171       }
172     }, threadName);
173     thread.start();
174     try {
175       Assertions.assertTrue(started.await(2, TimeUnit.SECONDS));
176       Assertions.assertNotNull(Utils.getThreadByName(threadName));
177     } finally {
178       stop.countDown();
179       thread.join(2000);
180     }
181 
182     Assertions.assertTrue(Utils.isThreadingEnabled());
183   }
184 
185   /**
186    * Recursive delete test.
187    *
188    * @throws IOException Signals that an I/O exception has occurred.
189    */
190   @Test
191   void deleteRecursivelyTest() throws IOException {
192     Path dirPath = Files.createTempDirectory("utils-delete-test-");
193     Path nestedPath = dirPath.resolve("nested");
194     Path childPath = nestedPath.resolve("child.txt");
195     Files.createDirectories(nestedPath);
196     Files.writeString(childPath, "child", StandardCharsets.UTF_8);
197     File dir = dirPath.toFile();
198 
199     Utils.delete(dir);
200     Assertions.assertFalse(dir.exists());
201 
202     Assertions.assertDoesNotThrow(() -> Utils.delete(dir));
203   }
204 
205   /**
206    * Highlight stream test.
207    *
208    * @throws IOException Signals that an I/O exception has occurred.
209    */
210   @Test
211   void highlightStreamTest() throws IOException {
212     // Test with a JSP renderer
213     String jspContent = "<html><body>hello</body></html>";
214     byte[] bytes = jspContent.getBytes(StandardCharsets.UTF_8);
215     String result =
216         Utils.highlightStream("test.jsp", new ByteArrayInputStream(bytes), "JSP", "UTF-8");
217     // result may be null if no renderer found; just verify no exception
218     // if not null, verify it contains HTML content
219     if (result != null) {
220       Assertions.assertTrue(result.contains("hello") || result.contains("codeline"));
221     }
222   }
223 
224   /**
225    * Highlight stream null renderer test.
226    *
227    * @throws IOException Signals that an I/O exception has occurred.
228    */
229   @Test
230   void highlightStreamNullRendererTest() throws IOException {
231     byte[] bytes = "content".getBytes(StandardCharsets.UTF_8);
232     String result = Utils.highlightStream("test.xyz", new ByteArrayInputStream(bytes),
233         "unknownrenderer", "UTF-8");
234     Assertions.assertNull(result);
235   }
236 
237   /**
238    * Get JSP encoding with single quote test.
239    *
240    * @throws IOException Signals that an I/O exception has occurred.
241    */
242   @Test
243   void getJspEncodingWithSingleQuoteTest() throws IOException {
244     String jsp = "<%@ page pageEncoding='ISO-8859-2' %>";
245     String encoding =
246         Utils.getJspEncoding(new ByteArrayInputStream(jsp.getBytes(StandardCharsets.UTF_8)));
247     Assertions.assertEquals("ISO-8859-2", encoding);
248   }
249 
250   /**
251    * Get JSP encoding with contentType charset test.
252    *
253    * @throws IOException Signals that an I/O exception has occurred.
254    */
255   @Test
256   void getJspEncodingContentTypeCharsetTest() throws IOException {
257     String jsp = "<%@ page contentType='text/html;charset=ISO-8859-3' %>";
258     String encoding =
259         Utils.getJspEncoding(new ByteArrayInputStream(jsp.getBytes(StandardCharsets.UTF_8)));
260     Assertions.assertEquals("ISO-8859-3", encoding);
261   }
262 
263   /**
264    * Delete null file test.
265    */
266   @Test
267   void deleteNullFileTest() {
268     Assertions.assertDoesNotThrow(() -> Utils.delete(null));
269   }
270 
271   /**
272    * Send file test.
273    *
274    * @throws IOException Signals that an I/O exception has occurred.
275    */
276   @Test
277   void sendFileTest() throws IOException {
278     File file = Files.createTempFile("utils-send-", ".txt").toFile();
279     Files.writeString(file.toPath(), "Hello File Content", StandardCharsets.UTF_8);
280 
281     try {
282       MockHttpServletRequest request = new MockHttpServletRequest();
283       MockHttpServletResponse response = new MockHttpServletResponse();
284 
285       Utils.sendFile(request, response, file);
286 
287       Assertions.assertEquals("application/x-download", response.getContentType());
288       Assertions.assertTrue(response.getHeader("Content-Disposition").contains(file.getName()));
289       Assertions.assertEquals("Hello File Content", response.getContentAsString());
290     } finally {
291       Files.deleteIfExists(file.toPath());
292     }
293   }
294 
295   /**
296    * Send file with range test.
297    *
298    * @throws IOException Signals that an I/O exception has occurred.
299    */
300   @Test
301   void sendFileWithRangeTest() throws IOException {
302     File file = Files.createTempFile("utils-range-", ".txt").toFile();
303     Files.writeString(file.toPath(), "ABCDEFGHIJ", StandardCharsets.UTF_8);
304 
305     try {
306       MockHttpServletRequest request = new MockHttpServletRequest();
307       request.addHeader("Range", "bytes=2-5");
308       MockHttpServletResponse response = new MockHttpServletResponse();
309 
310       Utils.sendFile(request, response, file);
311 
312       String content = response.getContentAsString();
313       Assertions.assertEquals("CDEF", content);
314     } finally {
315       Files.deleteIfExists(file.toPath());
316     }
317   }
318 
319   /**
320    * Send compressed file test.
321    *
322    * @throws IOException Signals that an I/O exception has occurred.
323    */
324   @Test
325   void sendCompressedFileTest() throws IOException {
326     File file = Files.createTempFile("utils-zip-", ".txt").toFile();
327     Files.writeString(file.toPath(), "compress me", StandardCharsets.UTF_8);
328 
329     try {
330       MockHttpServletResponse response = new MockHttpServletResponse();
331 
332       Utils.sendCompressedFile(response, file);
333 
334       Assertions.assertEquals("application/zip", response.getContentType());
335       Assertions.assertTrue(response.getHeader("Content-Disposition").contains(".zip"));
336       // Verify the response has some content (it's a valid zip)
337       byte[] content = response.getContentAsByteArray();
338       Assertions.assertTrue(content.length > 0);
339     } finally {
340       Files.deleteIfExists(file.toPath());
341     }
342   }
343 
344 }