xref: /OpenGrok/opengrok-indexer/src/test/java/org/opengrok/indexer/web/UtilTest.java (revision d6df19e1b22784c78f567cf74c42f18e3901b900)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * See LICENSE.txt included in this distribution for the specific
9  * language governing permissions and limitations under the License.
10  *
11  * When distributing Covered Code, include this CDDL HEADER in each
12  * file and include the License file at LICENSE.txt.
13  * If applicable, add the following below this CDDL HEADER, with the
14  * fields enclosed by brackets "[]" replaced with your own identifying
15  * information: Portions Copyright [yyyy] [name of copyright owner]
16  *
17  * CDDL HEADER END
18  */
19 
20 /*
21  * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
22  * Portions Copyright (c) 2017, 2019, Chris Fraire <cfraire@me.com>.
23  */
24 package org.opengrok.indexer.web;
25 
26 import java.io.ByteArrayInputStream;
27 import java.io.IOException;
28 import java.io.StringWriter;
29 import java.net.MalformedURLException;
30 import java.net.URISyntaxException;
31 import java.net.URL;
32 import java.nio.charset.StandardCharsets;
33 import java.util.List;
34 import java.util.Locale;
35 import java.util.Map;
36 import java.util.TreeMap;
37 import java.util.regex.Pattern;
38 import javax.xml.parsers.DocumentBuilderFactory;
39 
40 import jakarta.servlet.http.HttpServletRequest;
41 import org.junit.jupiter.api.AfterAll;
42 import org.junit.jupiter.api.BeforeAll;
43 import org.junit.jupiter.api.Test;
44 import org.junit.jupiter.api.condition.EnabledOnOs;
45 import org.junit.jupiter.api.condition.OS;
46 
47 import static org.hamcrest.MatcherAssert.assertThat;
48 import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
49 import static org.junit.jupiter.api.Assertions.assertEquals;
50 import static org.junit.jupiter.api.Assertions.assertFalse;
51 import static org.junit.jupiter.api.Assertions.assertNull;
52 import static org.junit.jupiter.api.Assertions.assertThrows;
53 import static org.junit.jupiter.api.Assertions.assertTrue;
54 
55 /**
56  * Test of the methods in <code>org.opengrok.indexer.web.Util</code>.
57  */
58 public class UtilTest {
59 
60     private static Locale savedLocale;
61 
62     @BeforeAll
setUpClass()63     public static void setUpClass() {
64         // Some of the methods have different results in different locales.
65         // Set locale to en_US for these tests.
66         savedLocale = Locale.getDefault();
67         Locale.setDefault(Locale.US);
68     }
69 
70     @AfterAll
tearDownClass()71     public static void tearDownClass() {
72         Locale.setDefault(savedLocale);
73         savedLocale = null;
74     }
75 
76     @Test
htmlize()77     public void htmlize() throws IOException {
78         String[][] input_output = {
79                 {"This is a test", "This is a test"},
80                 {"Newline\nshould become <br/>", "Newline<br/>should become &lt;br/&gt;"},
81                 {"Open & Grok", "Open &amp; Grok"},
82                 {"&amp;&lt;&gt;", "&amp;amp;&amp;lt;&amp;gt;"}};
83         for (String[] in_out : input_output) {
84             // 1 arg
85             assertEquals(in_out[1], Util.htmlize(in_out[0]));
86             // 2 args
87             StringBuilder sb = new StringBuilder();
88             Util.htmlize(in_out[0], sb);
89             assertEquals(in_out[1], sb.toString());
90         }
91     }
92 
93     @Test
breadcrumbPath()94     public void breadcrumbPath() {
95         assertNull(Util.breadcrumbPath("/root/", null));
96 
97         assertEquals("", Util.breadcrumbPath("/root/", ""));
98 
99         assertEquals("<a href=\"/root/x\">x</a>",
100                 Util.breadcrumbPath("/root/", "x"));
101         assertEquals("<a href=\"/root/xx\">xx</a>",
102                 Util.breadcrumbPath("/root/", "xx"));
103 
104         // parent directories have a trailing slash in href
105         assertEquals("<a href=\"/r/a/\">a</a>/<a href=\"/r/a/b\">b</a>",
106                 Util.breadcrumbPath("/r/", "a/b"));
107         // if basename is a dir (ends with file seperator), href link also
108         // ends with a '/'
109         assertEquals("<a href=\"/r/a/\">a</a>/<a href=\"/r/a/b/\">b</a>/",
110                 Util.breadcrumbPath("/r/", "a/b/"));
111         // should work the same way with a '.' as file separator
112         assertEquals("<a href=\"/r/java/\">java</a>."
113                         + "<a href=\"/r/java/lang/\">lang</a>."
114                         + "<a href=\"/r/java/lang/String\">String</a>",
115                 Util.breadcrumbPath("/r/", "java.lang.String", '.'));
116         // suffix added to the link?
117         assertEquals("<a href=\"/root/xx&project=y\">xx</a>",
118                 Util.breadcrumbPath("/root/", "xx", '/', "&project=y", false));
119         // compact: path needs to be resolved to /xx and no link is added
120         // for the virtual root directory (parent) but emitted as plain text.
121         // Prefix gets just prefixed as is and not mangled wrt. path -> "//"
122         assertEquals("/<a href=\"/root//xx&project=y\">xx</a>",
123                 Util.breadcrumbPath("/root/", "../xx", '/', "&project=y", true));
124         // relative pathes are resolved wrt. / , so path resolves to /a/c/d
125         assertEquals("/<a href=\"/r//a/\">a</a>/"
126                         + "<a href=\"/r//a/c/\">c</a>/"
127                         + "<a href=\"/r//a/c/d\">d</a>",
128                 Util.breadcrumbPath("/r/", "../a/b/../c//d", '/', "", true));
129     }
130 
131     @Test
redableSize()132     public void redableSize() {
133         assertEquals("0 ", Util.readableSize(0));
134         assertEquals("1 ", Util.readableSize(1));
135         assertEquals("-1 ", Util.readableSize(-1));
136         assertEquals("1,000 ", Util.readableSize(1000));
137         assertEquals("1 KiB", Util.readableSize(1024));
138         assertEquals("2.4 KiB", Util.readableSize(2500));
139         assertEquals("<b>1.4 MiB</b>", Util.readableSize(1474560));
140         assertEquals("<b>3.5 GiB</b>", Util.readableSize(3758489600L));
141         assertEquals("<b>8,589,934,592 GiB</b>",
142                 Util.readableSize(Long.MAX_VALUE));
143     }
144 
145     @Test
readableLine()146     public void readableLine() throws Exception {
147         StringWriter out = new StringWriter();
148         // hmmm - where do meaningful tests start?
149         Util.readableLine(42, out, null, null, null, null);
150         assertEquals("\n<a class=\"l\" name=\"42\" href=\"#42\">42</a>",
151                 out.toString());
152 
153         out.getBuffer().setLength(0); // clear buffer
154         Util.readableLine(110, out, null, null, null, null);
155         assertEquals("\n<a class=\"hl\" name=\"110\" href=\"#110\">110</a>",
156                 out.toString());
157     }
158 
159     @Test
path2uid()160     public void path2uid() {
161         assertEquals("\u0000etc\u0000passwd\u0000date",
162                 Util.path2uid("/etc/passwd", "date"));
163     }
164 
165     @Test
166     @EnabledOnOs(OS.WINDOWS)
fixPathIfWindows()167     void fixPathIfWindows() {
168         assertEquals("/var/opengrok", Util.fixPathIfWindows("\\var\\opengrok"));
169     }
170 
171     @Test
uid2url()172     public void uid2url() {
173         assertEquals("/etc/passwd", Util.uid2url(
174                 Util.path2uid("/etc/passwd", "date")));
175     }
176 
177     @Test
testUriEncode()178     public void testUriEncode() {
179         assertEquals("", Util.uriEncode(""));
180         assertEquals("a+b", Util.uriEncode("a b"));
181         assertEquals("a%23b", Util.uriEncode("a#b"));
182         assertEquals("a%2Fb", Util.uriEncode("a/b"));
183         assertEquals("README.txt", Util.uriEncode("README.txt"));
184     }
185 
186     @Test
testUriEncodePath()187     public void testUriEncodePath() {
188         assertEquals("", Util.uriEncodePath(""));
189         assertEquals("/", Util.uriEncodePath("/"));
190         assertEquals("a", Util.uriEncodePath("a"));
191         assertEquals("%09", Util.uriEncodePath("\t"));
192         assertEquals("a%2Bb", Util.uriEncodePath("a+b"));
193         assertEquals("a%20b", Util.uriEncodePath("a b"));
194         assertEquals("/a//x/yz/%23%23/%20/%20%3F",
195                 Util.uriEncodePath("/a//x/yz/##/ / ?"));
196         assertEquals("foo%3A%3Abar%3A%3Atest.js",
197                 Util.uriEncodePath("foo::bar::test.js"));
198         assertEquals("bl%C3%A5b%C3%A6rsyltet%C3%B8y",
199                 Util.uriEncodePath("bl\u00E5b\u00E6rsyltet\u00F8y"));
200     }
201 
202     @Test
formQuoteEscape()203     public void formQuoteEscape() {
204         assertEquals("", Util.formQuoteEscape(null));
205         assertEquals("abc", Util.formQuoteEscape("abc"));
206         assertEquals("&quot;abc&quot;", Util.formQuoteEscape("\"abc\""));
207         assertEquals("&amp;aring;", Util.formQuoteEscape("&aring;"));
208     }
209 
210     @Test
diffline()211     public void diffline() {
212         String[][] tests = {
213                 {
214                         "if (a < b && foo < bar && c > d)",
215                         "if (a < b && foo > bar && c > d)",
216                         "if (a &lt; b &amp;&amp; foo <span class=\"d\">&lt;</span> bar &amp;&amp; c &gt; d)",
217                         "if (a &lt; b &amp;&amp; foo <span class=\"a\">&gt;</span> bar &amp;&amp; c &gt; d)"
218                 },
219                 {
220                         "foo << 1",
221                         "foo >> 1",
222                         "foo <span class=\"d\">&lt;&lt;</span> 1",
223                         "foo <span class=\"a\">&gt;&gt;</span> 1"
224                 },
225                 {
226                         "\"(ses_id, mer_id, pass_id, \" + refCol +\" , mer_ref, amnt, "
227                                 + "cur, ps_id, ret_url, d_req_time, d_req_mil, h_resp_time, "
228                                 + "h_resp_mil) \"",
229                         "\"(ses_id, mer_id, pass_id, \" + refCol +\" , mer_ref, amnt, "
230                                 + "cur, ps_id, ret_url, exp_url, d_req_time, d_req_mil, "
231                                 + "h_resp_time, h_resp_mil) \"",
232                         "&quot;(ses_id, mer_id, pass_id, &quot; + refCol +&quot; , mer_ref, amnt, "
233                                 + "cur, ps_id, ret_url, d_req_time, d_req_mil, h_resp_time, "
234                                 + "h_resp_mil) &quot;",
235                         "&quot;(ses_id, mer_id, pass_id, &quot; + refCol +&quot; , mer_ref, amnt, "
236                                 + "cur, ps_id, ret_url, <span class=\"a\">exp_url, "
237                                 + "</span>d_req_time, d_req_mil, h_resp_time, h_resp_mil) &quot;"
238                 },
239                 {
240                         "\"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\", values);",
241                         "\"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\", values);",
242                         "&quot;VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)&quot;, values);",
243                         "&quot;VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?<span "
244                                 + "class=\"a\">, ?</span>)&quot;, values);"
245                 },
246                 {
247                         "char    *config_list = NULL;",
248                         "char    **config_list = NULL;",
249                         "char    *config_list = NULL;",
250                         "char    *<span class=\"a\">*</span>config_list = NULL;"
251                 },
252                 {
253                         "char    **config_list = NULL;",
254                         "char    *config_list = NULL;",
255                         "char    *<span class=\"d\">*</span>config_list = NULL;",
256                         "char    *config_list = NULL;"
257                 },
258                 {
259                         "* An error occured or there is non-numeric stuff at the end",
260                         "* An error occurred or there is non-numeric stuff at the end",
261                         "* An error occured or there is non-numeric stuff at the end",
262                         "* An error occur<span class=\"a\">r</span>ed or there is "
263                                 + "non-numeric stuff at the end"
264                 }
265         };
266         for (int i = 0; i < tests.length; i++) {
267             String[] strings = Util.diffline(
268                     new StringBuilder(tests[i][0]),
269                     new StringBuilder(tests[i][1]));
270             assertEquals(tests[i][2], strings[0], "" + i + "," + 0);
271             assertEquals(tests[i][3], strings[1], "" + i + "," + 1);
272         }
273     }
274 
275     @Test
testEncode()276     public void testEncode() {
277         String[][] tests = new String[][] {
278                 {"Test <code>title</code>", "Test&nbsp;&#60;code&#62;title&#60;/code&#62;"},
279                 {"ahoj", "ahoj"},
280                 {"<>|&\"'", "&#60;&#62;|&#38;&#34;&#39;"},
281                 {"tab\ttab", "tab&nbsp;&nbsp;&nbsp;&nbsp;tab"},
282                 {"multi\nline\t\nstring", "multi&lt;br/&gt;line&nbsp;&nbsp;&nbsp;&nbsp;&lt;br/&gt;string"}
283         };
284 
285         for (String[] test : tests) {
286             assertEquals(test[1], Util.encode(test[0]));
287         }
288     }
289 
290     @Test
dumpConfiguration()291     public void dumpConfiguration() throws Exception {
292         StringBuilder out = new StringBuilder();
293         Util.dumpConfiguration(out);
294         String s = out.toString();
295 
296         // Verify that we got a table.
297         assertTrue(s.startsWith("<table"));
298 
299         // Verify that the output is well-formed.
300         String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + s;
301         DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(
302                 new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));
303     }
304 
305     @Test
jsStringLiteral()306     public void jsStringLiteral() {
307         assertEquals("\"abc\\n\\r\\\"\\\\\"",
308                 Util.jsStringLiteral("abc\n\r\"\\"));
309     }
310 
311     @Test
stripPathPrefix()312     public void stripPathPrefix() {
313         assertEquals("/", Util.stripPathPrefix("/", "/"));
314         assertEquals("/abc", Util.stripPathPrefix("/abc", "/abc"));
315         assertEquals("/abc/", Util.stripPathPrefix("/abc", "/abc/"));
316         assertEquals("/abc", Util.stripPathPrefix("/abc/", "/abc"));
317         assertEquals("/abc/", Util.stripPathPrefix("/abc/", "/abc/"));
318         assertEquals("abc", Util.stripPathPrefix("/", "/abc"));
319         assertEquals("abc/def", Util.stripPathPrefix("/", "/abc/def"));
320         assertEquals("def", Util.stripPathPrefix("/abc", "/abc/def"));
321         assertEquals("def", Util.stripPathPrefix("/abc/", "/abc/def"));
322         assertEquals("/abcdef", Util.stripPathPrefix("/abc", "/abcdef"));
323         assertEquals("/abcdef", Util.stripPathPrefix("/abc/", "/abcdef"));
324         assertEquals("def/ghi", Util.stripPathPrefix("/abc", "/abc/def/ghi"));
325         assertEquals("def/ghi", Util.stripPathPrefix("/abc/", "/abc/def/ghi"));
326     }
327 
328     @Test
testSlider()329     public void testSlider() {
330         /*
331          * Test if contains all five pages for 55 results paginated by 10
332          */
333         for (int i = 0; i < 10; i++) {
334             for (int j = 1; j <= 5; j++) {
335                 assertTrue(Util.createSlider(i * 10, 10, 55).contains(">" + j + "<"), "Contains page " + j);
336             }
337         }
338 
339         assertFalse(Util.createSlider(0, 10, 4).contains(">1<"), "Does not contain page 1");
340         assertFalse(Util.createSlider(0, 10, 2).contains(">5<"), "Does not contain page 5");
341         assertFalse(Util.createSlider(0, 10, 0).contains(">1<"), "Does not contain page 1");
342     }
343 
344     @Test
testIsUrl()345     public void testIsUrl() {
346         assertTrue(Util.isHttpUri("http://www.example.com"));
347         assertTrue(Util.isHttpUri("http://example.com"));
348         assertTrue(Util.isHttpUri("https://example.com"));
349         assertTrue(Util.isHttpUri("https://www.example.com"));
350         assertTrue(Util.isHttpUri("http://www.example.com?param=1&param2=2"));
351         assertTrue(Util.isHttpUri("http://www.example.com/other/page"));
352         assertTrue(Util.isHttpUri("https://www.example.com?param=1&param2=2"));
353         assertTrue(Util.isHttpUri("https://www.example.com/other/page"));
354         assertTrue(Util.isHttpUri("http://www.example.com:80/other/page"));
355         assertTrue(Util.isHttpUri("http://www.example.com:8080/other/page"));
356         assertTrue(Util.isHttpUri("https://www.example.com:80/other/page"));
357         assertTrue(Util.isHttpUri("https://www.example.com:8080/other/page"));
358 
359         assertFalse(Util.isHttpUri("git@github.com:OpenGrok/OpenGrok"));
360         assertFalse(Util.isHttpUri("hg@mercurial.com:OpenGrok/OpenGrok"));
361         assertFalse(Util.isHttpUri("ssh://git@github.com:OpenGrok/OpenGrok"));
362         assertFalse(Util.isHttpUri("ldap://example.com/OpenGrok/OpenGrok"));
363         assertFalse(Util.isHttpUri("smtp://example.com/OpenGrok/OpenGrok"));
364     }
365 
366     @Test
testRedactUrl()367     public void testRedactUrl() {
368         assertEquals("/foo/bar", Util.redactUrl("/foo/bar"));
369         assertEquals("http://foo/bar?r=xxx", Util.redactUrl("http://foo/bar?r=xxx"));
370         assertEquals("http://" + Util.REDACTED_USER_INFO + "@foo/bar?r=xxx",
371                 Util.redactUrl("http://user@foo/bar?r=xxx"));
372         assertEquals("http://" + Util.REDACTED_USER_INFO + "@foo/bar?r=xxx",
373                 Util.redactUrl("http://user:pass@foo/bar?r=xxx"));
374     }
375 
376     @Test
testLinkify()377     public void testLinkify() throws URISyntaxException, MalformedURLException {
378         assertTrue(Util.linkify("http://www.example.com")
379                 .matches("<a.*?href=\"http://www\\.example\\.com\".*?>.*?</a>"));
380         assertTrue(Util.linkify("https://example.com")
381                 .matches("<a.*?href=\"https://example\\.com\".*?>.*?</a>"));
382         assertTrue(Util.linkify("http://www.example.com?param=1&param2=2")
383                 .matches("<a.*?href=\"http://www\\.example\\.com\\?param=1&param2=2\".*?>.*?</a>"));
384         assertTrue(Util.linkify("https://www.example.com:8080/other/page")
385                 .matches("<a.*?href=\"https://www\\.example\\.com:8080/other/page\".*?>.*?</a>"));
386 
387         assertTrue(Util.linkify("http://www.example.com", true).contains("target=\"_blank\""));
388         assertTrue(Util.linkify("https://example.com", true).contains("target=\"_blank\""));
389         assertTrue(Util.linkify("http://www.example.com?param=1&param2=2", true).contains("target=\"_blank\""));
390         assertTrue(Util.linkify("https://www.example.com:8080/other/page", true).contains("target=\"_blank\""));
391         assertTrue(Util.linkify("https://www.example.com:8080/other/page", true).contains("rel=\"noreferrer\""));
392 
393         assertFalse(Util.linkify("http://www.example.com", false).contains("target=\"_blank\""));
394         assertFalse(Util.linkify("https://example.com", false).contains("target=\"_blank\""));
395         assertFalse(Util.linkify("http://www.example.com?param=1&param2=2", false).contains("target=\"_blank\""));
396         assertFalse(Util.linkify("https://www.example.com:8080/other/page", false).contains("target=\"_blank\""));
397 
398         assertEquals("git@github.com:OpenGrok/OpenGrok", Util.linkify("git@github.com:OpenGrok/OpenGrok"));
399         assertEquals("hg@mercurial.com:OpenGrok/OpenGrok", Util.linkify("hg@mercurial.com:OpenGrok/OpenGrok"));
400         assertEquals("ssh://git@github.com:OpenGrok/OpenGrok", Util.linkify("ssh://git@github.com:OpenGrok/OpenGrok"));
401         assertEquals("ldap://example.com/OpenGrok/OpenGrok", Util.linkify("ldap://example.com/OpenGrok/OpenGrok"));
402         assertEquals("smtp://example.com/OpenGrok/OpenGrok", Util.linkify("smtp://example.com/OpenGrok/OpenGrok"));
403         assertEquals("just some crazy text", Util.linkify("just some crazy text"));
404 
405         // escaping url
406         assertTrue(Util.linkify("http://www.example.com/\"quotation\"/else")
407                 .contains("href=\"" + Util.encodeURL("http://www.example.com/\"quotation\"/else") + "\""));
408         assertTrue(Util.linkify("https://example.com/><\"")
409                 .contains("href=\"" + Util.encodeURL("https://example.com/><\"") + "\""));
410         assertTrue(Util.linkify("http://www.example.com?param=1&param2=2&param3=\"quoted>\"")
411                 .contains("href=\"" + Util.encodeURL("http://www.example.com?param=1&param2=2&param3=\"quoted>\"") + "\""));
412         // escaping titles
413         assertTrue(Util.linkify("http://www.example.com/\"quotation\"/else")
414                 .contains("title=\"Link to " + Util.encode("http://www.example.com/\"quotation\"/else") + "\""));
415         assertTrue(Util.linkify("https://example.com/><\"")
416                 .contains("title=\"Link to " + Util.encode("https://example.com/><\"") + "\""));
417         assertTrue(Util.linkify("http://www.example.com?param=1&param2=2&param3=\"quoted>\"")
418                 .contains("title=\"Link to " + Util.encode("http://www.example.com?param=1&param2=2&param3=\"quoted>\"") + "\""));
419     }
420 
421     @Test
testBuildLink()422     public void testBuildLink() throws URISyntaxException, MalformedURLException {
423         assertEquals("<a href=\"http://www.example.com\">link</a>", Util.buildLink("link", "http://www.example.com"));
424         assertEquals("<a href=\"http://www.example.com?url=xasw&beta=gama\">link</a>",
425                 Util.buildLink("link", "http://www.example.com?url=xasw&beta=gama"));
426 
427         String link = Util.buildLink("link", "http://www.example.com", true);
428         assertTrue(link.contains("href=\"http://www.example.com\""));
429         assertTrue(link.contains("target=\"_blank\""));
430 
431         link = Util.buildLink("link", "http://www.example.com?url=xasw&beta=gama", true);
432         assertTrue(link.contains("href=\"http://www.example.com?url=xasw&beta=gama\""));
433         assertTrue(link.contains("target=\"_blank\""));
434 
435         Map<String, String> attrs = new TreeMap<>();
436         attrs.put("href", "https://www.example.com/abcd/acbd");
437         attrs.put("title", "Some important title");
438         attrs.put("data-id", "123456");
439 
440         link = Util.buildLink("link", attrs);
441         assertTrue(link.contains("href=\"https://www.example.com/abcd/acbd\""));
442         assertTrue(link.contains("title=\"Some important title\""));
443         assertTrue(link.contains("data-id=\"123456\""));
444     }
445 
446     @Test
testBuildLinkInvalidUrl1()447     public void testBuildLinkInvalidUrl1() {
448         assertThrows(MalformedURLException.class, () -> Util.buildLink("link", "www.example.com")); // invalid protocol
449     }
450 
451     @Test
testBuildLinkInvalidUrl2()452     public void testBuildLinkInvalidUrl2() {
453         assertThrows(URISyntaxException.class, () -> Util.buildLink("link", "http://www.exa\"mp\"le.com")); // invalid authority
454     }
455 
456     @Test
testLinkifyPattern()457     public void testLinkifyPattern() {
458         String text
459                 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, "
460                 + "sed do eiusmod tempor incididunt as per 12345698 ut labore et dolore magna "
461                 + "aliqua. bug3333fff Ut enim ad minim veniam, quis nostrud exercitation "
462                 + "ullamco laboris nisi ut aliquip ex ea introduced in 9791216541 commodo consequat. "
463                 + "Duis aute irure dolor in reprehenderit in voluptate velit "
464                 + "esse cillum dolore eu fixes 132469187 fugiat nulla pariatur. Excepteur sint "
465                 + "occaecat bug6478abc cupidatat non proident, sunt in culpa qui officia "
466                 + "deserunt mollit anim id est laborum.";
467         String expected
468                 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, "
469                 + "sed do eiusmod tempor incididunt as per "
470                 + "<a href=\"http://www.example.com?bug=12345698\" rel=\"noreferrer\" target=\"_blank\">12345698</a>"
471                 + " ut labore et dolore magna "
472                 + "aliqua. bug3333fff Ut enim ad minim veniam, quis nostrud exercitation "
473                 + "ullamco laboris nisi ut aliquip ex ea introduced in "
474                 + "<a href=\"http://www.example.com?bug=9791216541\" rel=\"noreferrer\" target=\"_blank\">9791216541</a>"
475                 + " commodo consequat. "
476                 + "Duis aute irure dolor in reprehenderit in voluptate velit "
477                 + "esse cillum dolore eu fixes "
478                 + "<a href=\"http://www.example.com?bug=132469187\" rel=\"noreferrer\" target=\"_blank\">132469187</a>"
479                 + " fugiat nulla pariatur. Excepteur sint "
480                 + "occaecat bug6478abc cupidatat non proident, sunt in culpa qui officia "
481                 + "deserunt mollit anim id est laborum.";
482         String expected2
483                 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, "
484                 + "sed do eiusmod tempor incididunt as per 12345698 ut labore et dolore magna "
485                 + "aliqua. "
486                 + "<a href=\"http://www.other-example.com?bug=3333\" rel=\"noreferrer\" target=\"_blank\">bug3333fff</a>"
487                 + " Ut enim ad minim veniam, quis nostrud exercitation "
488                 + "ullamco laboris nisi ut aliquip ex ea introduced in 9791216541 commodo consequat. "
489                 + "Duis aute irure dolor in reprehenderit in voluptate velit "
490                 + "esse cillum dolore eu fixes 132469187 fugiat nulla pariatur. Excepteur sint "
491                 + "occaecat "
492                 + "<a href=\"http://www.other-example.com?bug=6478\" rel=\"noreferrer\" target=\"_blank\">bug6478abc</a>"
493                 + " cupidatat non proident, sunt in culpa qui officia "
494                 + "deserunt mollit anim id est laborum.";
495 
496         assertEquals(expected, Util.linkifyPattern(text, Pattern.compile("\\b([0-9]{8,})\\b"), "$1", "http://www.example.com?bug=$1"));
497         assertEquals(expected2, Util.linkifyPattern(text, Pattern.compile("\\b(bug([0-9]{4})\\w{3})\\b"), "$1",
498                 "http://www.other-example.com?bug=$2"));
499     }
500 
501     @Test
testCompleteUrl()502     public void testCompleteUrl() {
503         HttpServletRequest req = new DummyHttpServletRequest() {
504             @Override
505             public int getServerPort() {
506                 return 8080;
507             }
508 
509             @Override
510             public String getServerName() {
511                 return "www.example.com";
512             }
513 
514             @Override
515             public String getScheme() {
516                 return "http";
517             }
518 
519             @Override
520             public StringBuffer getRequestURL() {
521                 return new StringBuffer(getScheme() + "://" + getServerName() + ':' + getServerPort() + "/source/location/undefined");
522             }
523         };
524 
525         // Absolute including hostname.
526         assertEquals("http://opengrok.com/user=", Util.completeUrl("http://opengrok.com/user=", req));
527         assertEquals("http://opengrok.cz.grok.com/user=", Util.completeUrl("http://opengrok.cz.grok.com/user=", req));
528         assertEquals("http://opengrok.com/user=123&id=", Util.completeUrl("http://opengrok.com/user=123&id=", req));
529 
530         // Absolute/relative without the hostname.
531         assertEquals("http://www.example.com:8080/cgi-bin/user=", Util.completeUrl("/cgi-bin/user=", req));
532         assertEquals("http://www.example.com:8080/cgi-bin/user=123&id=", Util.completeUrl("/cgi-bin/user=123&id=", req));
533         assertEquals("http://www.example.com:8080/source/location/undefined/cgi-bin/user=", Util.completeUrl("cgi-bin/user=", req));
534         assertEquals("http://www.example.com:8080/source/location/undefined/cgi-bin/user=123&id=", Util.completeUrl("cgi-bin/user=123&id=", req));
535 
536         assertEquals("http://www.example.com:8080/source/location/undefined", Util.completeUrl("", req));
537 
538         // Escaping should work.
539         assertEquals("http://www.example.com:8080/cgi-%22bin/user=123&id=", Util.completeUrl("/cgi-\"bin/user=123&id=", req));
540     }
541 
542     @Test
getQueryParamsNullTest()543     public void getQueryParamsNullTest() {
544         assertThrows(IllegalArgumentException.class, () -> Util.getQueryParams(null));
545     }
546 
547     @Test
getQueryParamsEmptyTest()548     public void getQueryParamsEmptyTest() throws MalformedURLException {
549         URL url = new URL("http://test.com/test");
550         assertTrue(Util.getQueryParams(url).isEmpty());
551     }
552 
553     @Test
getQueryParamsEmptyTest2()554     public void getQueryParamsEmptyTest2() throws MalformedURLException {
555         URL url = new URL("http://test.com/test?");
556         assertTrue(Util.getQueryParams(url).isEmpty());
557     }
558 
559     @Test
getQueryParamsSingleTest()560     public void getQueryParamsSingleTest() throws MalformedURLException {
561         URL url = new URL("http://test.com?param1=value1");
562         Map<String, List<String>> params = Util.getQueryParams(url);
563 
564         assertEquals(1, params.size());
565 
566         assertThat(params.get("param1"), contains("value1"));
567     }
568 
569     @Test
getQueryParamsMultipleTest()570     public void getQueryParamsMultipleTest() throws MalformedURLException {
571         URL url = new URL("http://test.com?param1=value1&param2=value2");
572         Map<String, List<String>> params = Util.getQueryParams(url);
573 
574         assertEquals(2, params.size());
575 
576         assertThat(params.get("param1"), contains("value1"));
577         assertThat(params.get("param2"), contains("value2"));
578     }
579 
580     @Test
getQueryParamsMultipleSameTest()581     public void getQueryParamsMultipleSameTest() throws MalformedURLException {
582         URL url = new URL("http://test.com?param1=value1&param1=value2");
583         Map<String, List<String>> params = Util.getQueryParams(url);
584 
585         assertEquals(1, params.size());
586 
587         assertThat(params.get("param1"), contains("value1", "value2"));
588     }
589 
590     @Test
getQueryParamsEncodedTest()591     public void getQueryParamsEncodedTest() throws MalformedURLException {
592         URL url = new URL("http://test.com?param1=%3Fvalue%3F");
593         Map<String, List<String>> params = Util.getQueryParams(url);
594 
595         assertEquals(1, params.size());
596 
597         assertThat(params.get("param1"), contains("?value?"));
598     }
599 
600     @Test
getQueryParamsEmptyValueTest()601     public void getQueryParamsEmptyValueTest() throws MalformedURLException {
602         URL url = new URL("http://test.com?param1=");
603 
604         Map<String, List<String>> params = Util.getQueryParams(url);
605 
606         assertThat(params.get("param1"), contains(""));
607     }
608 
609     @Test
getQueryParamsEmptyAndNormalValuesCombinedTest()610     public void getQueryParamsEmptyAndNormalValuesCombinedTest() throws MalformedURLException {
611         URL url = new URL("http://test.com?param1=value1&param2=&param3&param4=value4");
612 
613         Map<String, List<String>> params = Util.getQueryParams(url);
614 
615         assertThat(params.get("param1"), contains("value1"));
616         assertThat(params.get("param2"), contains(""));
617         assertTrue(params.containsKey("param3"));
618         assertThat(params.get("param4"), contains("value4"));
619     }
620 
621 }
622