1 /*
2  * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test
26  * @bug 8178982 8220497 8210683 8241982
27  * @summary Test the search feature of javadoc.
28  * @library ../../lib
29  * @library /test/lib
30  * @modules jdk.javadoc/jdk.javadoc.internal.tool
31  * @build javadoc.tester.*
32  * @build jtreg.SkippedException
33  * @run main TestSearchScript
34  */
35 
36 import javadoc.tester.JavadocTester;
37 
38 import javax.script.Bindings;
39 import javax.script.Invocable;
40 import javax.script.ScriptContext;
41 import javax.script.ScriptEngine;
42 import javax.script.ScriptEngineManager;
43 import javax.script.ScriptException;
44 import java.io.BufferedReader;
45 import java.io.File;
46 import java.io.FileReader;
47 import java.io.IOException;
48 import java.util.List;
49 
50 import jtreg.SkippedException;
51 
52 /*
53  * Tests for the search feature using any available javax.script JavaScript engine.
54  * The test is skipped if no JavaScript engine is available.
55  */
56 public class TestSearchScript extends JavadocTester {
57 
main(String... args)58     public static void main(String... args) throws Exception {
59         TestSearchScript tester = new TestSearchScript();
60         tester.runTests();
61     }
62 
getEngine()63     private Invocable getEngine() throws ScriptException, IOException, NoSuchMethodException {
64         ScriptEngineManager engineManager = new ScriptEngineManager();
65         // Use "js" engine name to use any available JavaScript engine.
66         ScriptEngine engine = engineManager.getEngineByName("js");
67         if (engine == null) {
68             throw new SkippedException("JavaScript engine is not available.");
69         }
70         // For GraalJS set Nashorn compatibility mode via Bindings,
71         // see https://github.com/graalvm/graaljs/blob/master/docs/user/ScriptEngine.md
72         Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
73         bindings.put("polyglot.js.nashorn-compat", true);
74         engine.eval(new BufferedReader(new FileReader(new File(testSrc, "javadoc-search.js"))));
75         Invocable inv = (Invocable) engine;
76         inv.invokeFunction("loadIndexFiles", outputDir.getAbsolutePath());
77         return inv;
78     }
79 
80     @Test
testModuleSearch()81     public void testModuleSearch() throws ScriptException, IOException, NoSuchMethodException {
82         javadoc("-d", "out-full",
83                 "-Xdoclint:none",
84                 "-use",
85                 "--module-source-path", testSrc,
86                 "--module", "mapmodule",
87                 "mappkg", "mappkg.impl");
88         checkExit(Exit.OK);
89 
90         Invocable inv = getEngine();
91 
92         // exact match, case sensitivity
93         checkSearch(inv, "mapmodule", List.of("mapmodule"));
94         checkSearch(inv, "mappkg", List.of("mapmodule/mappkg", "mapmodule/mappkg.impl", "mappkg.system.property"));
95         checkSearch(inv, "Mapmodule", List.of("mapmodule"));
96         checkSearch(inv, "Mappkg", List.of("mapmodule/mappkg", "mapmodule/mappkg.impl", "mappkg.system.property"));
97         checkSearch(inv, "mymap", List.of("mappkg.impl.MyMap", "mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)"));
98         checkSearch(inv, "MyMap", List.of("mappkg.impl.MyMap", "mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)"));
99         checkSearch(inv, "mymap(", List.of("mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)"));
100         checkSearch(inv, "MyMap(", List.of("mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)"));
101         checkSearch(inv, "mymap()", List.of("mappkg.impl.MyMap.MyMap()"));
102         checkSearch(inv, "MyMap()", List.of("mappkg.impl.MyMap.MyMap()"));
103         checkSearch(inv, "Mymap", List.of("mappkg.impl.MyMap", "mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)"));
104         checkSearch(inv, "Mymap()", List.of("mappkg.impl.MyMap.MyMap()"));
105 
106         // left boundaries, ranking
107         checkSearch(inv, "map", List.of("mapmodule", "mapmodule/mappkg", "mapmodule/mappkg.impl", "mappkg.Map", "mappkg.impl.MyMap",
108                                         "mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)", "mappkg.system.property"));
109         checkSearch(inv, "Map", List.of("mapmodule", "mapmodule/mappkg", "mapmodule/mappkg.impl", "mappkg.Map", "mappkg.impl.MyMap",
110                                         "mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)", "mappkg.system.property"));
111         checkSearch(inv, "MAP", List.of("mapmodule", "mapmodule/mappkg", "mapmodule/mappkg.impl", "mappkg.Map", "mappkg.impl.MyMap",
112                                         "mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)", "mappkg.system.property"));
113         checkSearch(inv, "value", List.of("mappkg.impl.MyMap.OTHER_VALUE", "mappkg.impl.MyMap.some_value"));
114         checkSearch(inv, "VALUE", List.of("mappkg.impl.MyMap.OTHER_VALUE", "mappkg.impl.MyMap.some_value"));
115         checkSearch(inv, "map.other", List.of("mappkg.impl.MyMap.OTHER_VALUE"));
116         checkSearch(inv, "Map.Some_", List.of("mappkg.impl.MyMap.some_value"));
117 
118         checkSearch(inv, "Mm", List.of());
119         checkSearch(inv, "mym", List.of("mappkg.impl.MyMap", "mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)"));
120         checkSearch(inv, "imp.mym.mym(", List.of("mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)"));
121         checkSearch(inv, "imp.mym.mym(m", List.of("mappkg.impl.MyMap.MyMap(Map)"));
122 
123         // camel case
124         checkSearch(inv, "MM", List.of("mappkg.impl.MyMap", "mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)"));
125         checkSearch(inv, "MyM", List.of("mappkg.impl.MyMap", "mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)"));
126         checkSearch(inv, "Mym", List.of("mappkg.impl.MyMap", "mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)"));
127         checkSearch(inv, "i.MyM.MyM(", List.of("mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)"));
128         checkSearch(inv, "i.MMa.MMa(", List.of("mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)"));
129         checkSearch(inv, "i.MyM.MyM(Ma", List.of("mappkg.impl.MyMap.MyMap(Map)"));
130         checkSearch(inv, "i.MMa.MMa(M", List.of("mappkg.impl.MyMap.MyMap(Map)"));
131         checkSearch(inv, "i.Mym.MyM(", List.of("mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)"));
132         checkSearch(inv, "i.Mym.Ma(", List.of());
133 
134         checkSearch(inv, "mapm", List.of("mapmodule"));
135 
136         // child entity listing
137         checkSearch(inv, "mapmodule/", List.of("mapmodule/mappkg", "mapmodule/mappkg.impl"));
138         checkSearch(inv, "mapmod/", List.of("mapmodule/mappkg", "mapmodule/mappkg.impl"));
139         checkSearch(inv, "module/", List.of());
140         checkSearch(inv, "le/", List.of());
141         checkSearch(inv, "mapmodule.", List.of());
142         checkSearch(inv, "mapmod.", List.of());
143         checkSearch(inv, "mappkg.", List.of("mapmodule/mappkg.impl", "mappkg.Map", "mappkg.system.property"));
144         checkSearch(inv, "mappkg.", List.of("mapmodule/mappkg.impl", "mappkg.Map", "mappkg.system.property"));
145         checkSearch(inv, "Map.", List.of("mapmodule/mappkg.impl", "mappkg.Map", "mappkg.Map.contains(Object)",
146                                          "mappkg.Map.get(Object)", "mappkg.Map.iterate()", "mappkg.Map.put(Object, Object)",
147                                          "mappkg.Map.remove(Object)", "mappkg.impl.MyMap.contains(Object)",
148                                          "mappkg.impl.MyMap.get(Object)", "mappkg.impl.MyMap.iterate()",
149                                          "mappkg.impl.MyMap.MyMap()", "mappkg.impl.MyMap.MyMap(Map)",
150                                          "mappkg.impl.MyMap.OTHER_VALUE", "mappkg.impl.MyMap.put(Object, Object)",
151                                          "mappkg.impl.MyMap.remove(Object)", "mappkg.impl.MyMap.some_value",
152                                          "mappkg.system.property"));
153         checkSearch(inv, "mym.", List.of("mappkg.impl.MyMap.contains(Object)", "mappkg.impl.MyMap.get(Object)",
154                                          "mappkg.impl.MyMap.iterate()", "mappkg.impl.MyMap.MyMap()",
155                                          "mappkg.impl.MyMap.MyMap(Map)", "mappkg.impl.MyMap.OTHER_VALUE",
156                                          "mappkg.impl.MyMap.put(Object, Object)", "mappkg.impl.MyMap.remove(Object)",
157                                          "mappkg.impl.MyMap.some_value"));
158         checkSearch(inv, "MyMap.i", List.of("mappkg.impl.MyMap.iterate()"));
159 
160         // system properties
161         checkSearch(inv, "mappkg.system.property", List.of("mappkg.system.property"));
162         checkSearch(inv, "system.property", List.of("mappkg.system.property"));
163         checkSearch(inv, "property", List.of("mappkg.system.property"));
164         checkSearch(inv, "sys.prop", List.of("mappkg.system.property"));
165         checkSearch(inv, "m.s.p", List.of("mappkg.system.property"));
166         checkSearch(inv, "operty", List.of());
167 
168         // search tag
169         checkSearch(inv, "search tag", List.of("search tag", "multiline search tag"));
170         checkSearch(inv, "search   tag", List.of("search tag", "multiline search tag"));
171         checkSearch(inv, "search ", List.of("multiline search tag", "search tag"));
172         checkSearch(inv, "tag", List.of("multiline search tag", "search tag"));
173         checkSearch(inv, "sea", List.of("multiline search tag", "search tag"));
174         checkSearch(inv, "multi", List.of("multiline search tag"));
175         checkSearch(inv, "ear", List.of());
176     }
177 
178 
179     @Test
testPackageSource()180     public void testPackageSource() throws ScriptException, IOException, NoSuchMethodException {
181         javadoc("-d", "out-overload",
182                 "-Xdoclint:none",
183                 "-use",
184                 "-sourcepath", testSrc,
185                 "listpkg");
186         checkExit(Exit.OK);
187 
188         Invocable inv = getEngine();
189 
190         // exact match, case sensitvity, left boundaries
191         checkSearch(inv, "list", List.of("listpkg", "listpkg.List", "listpkg.ListProvider", "listpkg.MyList",
192                                          "listpkg.MyListFactory", "listpkg.ListProvider.ListProvider()",
193                                          "listpkg.MyListFactory.createList(ListProvider, MyListFactory)",
194                                          "listpkg.ListProvider.makeNewList()",
195                                          "listpkg.MyList.MyList()", "listpkg.MyListFactory.MyListFactory()"));
196         checkSearch(inv, "List", List.of("listpkg", "listpkg.List", "listpkg.ListProvider", "listpkg.MyList",
197                                          "listpkg.MyListFactory", "listpkg.ListProvider.ListProvider()",
198                                          "listpkg.MyListFactory.createList(ListProvider, MyListFactory)",
199                                          "listpkg.ListProvider.makeNewList()",
200                                          "listpkg.MyList.MyList()", "listpkg.MyListFactory.MyListFactory()"));
201         // partial match
202         checkSearch(inv, "fact", List.of("listpkg.MyListFactory", "listpkg.MyListFactory.MyListFactory()"));
203         checkSearch(inv, "pro", List.of("listpkg.ListProvider", "listpkg.ListProvider.ListProvider()"));
204         checkSearch(inv, "listpro", List.of("listpkg.ListProvider", "listpkg.ListProvider.ListProvider()"));
205 
206         // camel case
207         checkSearch(inv, "l.MLF.cL(LP, MLF)", List.of("listpkg.MyListFactory.createList(ListProvider, MyListFactory)"));
208         checkSearch(inv, "Fact.creaLi(LiPro,MLiFact)", List.of("listpkg.MyListFactory.createList(ListProvider, MyListFactory)"));
209         checkSearch(inv, "(LP,ML", List.of("listpkg.MyListFactory.createList(ListProvider, MyListFactory)"));
210 
211         // ranking of overloaded methods JDK-8210683
212         checkSearch(inv, "list.of",
213                 List.of("listpkg.List.of()", "listpkg.List.of(E)", "listpkg.List.of(E, E)",
214                         "listpkg.List.of(E, E, E)", "listpkg.List.of(E, E, E, E)",
215                         "listpkg.List.of(E, E, E, E, E)", "listpkg.List.of(E...)"));
216         checkSearch(inv, "Li.of",
217                 List.of("listpkg.List.of()", "listpkg.List.of(E)", "listpkg.List.of(E, E)",
218                         "listpkg.List.of(E, E, E)", "listpkg.List.of(E, E, E, E)",
219                         "listpkg.List.of(E, E, E, E, E)", "listpkg.List.of(E...)"));
220         checkSearch(inv, "li.Li.o",
221                 List.of("listpkg.List.of()", "listpkg.List.of(E)", "listpkg.List.of(E, E)",
222                         "listpkg.List.of(E, E, E)", "listpkg.List.of(E, E, E, E)",
223                         "listpkg.List.of(E, E, E, E, E)", "listpkg.List.of(E...)"));
224         checkSearch(inv, "l.l.o",
225                 List.of("listpkg.List.of()", "listpkg.List.of(E)", "listpkg.List.of(E, E)",
226                         "listpkg.List.of(E, E, E)", "listpkg.List.of(E, E, E, E)",
227                         "listpkg.List.of(E, E, E, E, E)", "listpkg.List.of(E...)"));
228         checkSearch(inv, "L.l.o", List.of("listpkg.List.of()", "listpkg.List.of(E)", "listpkg.List.of(E, E)",
229                         "listpkg.List.of(E, E, E)", "listpkg.List.of(E, E, E, E)", "listpkg.List.of(E, E, E, E, E)",
230                         "listpkg.List.of(E...)"));
231 
232         // whitespace
233         checkSearch(inv, "(e,e,e",
234                 List.of("listpkg.List.of(E, E, E)", "listpkg.List.of(E, E, E, E)",
235                         "listpkg.List.of(E, E, E, E, E)"));
236         checkSearch(inv, "(e, e,e",
237                 List.of("listpkg.List.of(E, E, E)", "listpkg.List.of(E, E, E, E)",
238                         "listpkg.List.of(E, E, E, E, E)"));
239         checkSearch(inv, "(e, e, e",
240                 List.of("listpkg.List.of(E, E, E)", "listpkg.List.of(E, E, E, E)",
241                         "listpkg.List.of(E, E, E, E, E)"));
242         checkSearch(inv, "(e,   e,  e",
243                 List.of("listpkg.List.of(E, E, E)", "listpkg.List.of(E, E, E, E)",
244                 "listpkg.List.of(E, E, E, E, E)"));
245         checkSearch(inv, "(e, e, e ,",
246                 List.of("listpkg.List.of(E, E, E, E)", "listpkg.List.of(E, E, E, E, E)"));
247         checkSearch(inv, "(e   ,   e,  e,",
248                 List.of("listpkg.List.of(E, E, E, E)", "listpkg.List.of(E, E, E, E, E)"));
249         checkSearch(inv, "  listpkg  .list .of ",
250                 List.of("listpkg.List.of()", "listpkg.List.of(E)", "listpkg.List.of(E, E)",
251                         "listpkg.List.of(E, E, E)", "listpkg.List.of(E, E, E, E)",
252                         "listpkg.List.of(E, E, E, E, E)", "listpkg.List.of(E...)"));
253         checkSearch(inv, " l. l. o",
254                 List.of("listpkg.List.of()", "listpkg.List.of(E)", "listpkg.List.of(E, E)",
255                         "listpkg.List.of(E, E, E)", "listpkg.List.of(E, E, E, E)",
256                         "listpkg.List.of(E, E, E, E, E)", "listpkg.List.of(E...)"));
257         checkSearch(inv, "list . of",
258                 List.of("listpkg.List.of()", "listpkg.List.of(E)", "listpkg.List.of(E, E)",
259                         "listpkg.List.of(E, E, E)", "listpkg.List.of(E, E, E, E)",
260                         "listpkg.List.of(E, E, E, E, E)", "listpkg.List.of(E...)"));
261         checkSearch(inv, "lis t.of", List.of());
262         checkSearch(inv, "list . of(e,e,e,",
263                 List.of("listpkg.List.of(E, E, E, E)", "listpkg.List.of(E, E, E, E, E)"));
264         checkSearch(inv, "l . o (e,e,e,",
265                 List.of("listpkg.List.of(E, E, E, E)", "listpkg.List.of(E, E, E, E, E)"));
266         checkSearch(inv, "search    \tt", List.of("other search tag"));
267         checkSearch(inv, "sear ch", List.of());
268         checkSearch(inv, "( e ..", List.of("listpkg.List.of(E...)"));
269         checkSearch(inv, "( i [ ]", List.of("listpkg.Nolist.withArrayArg(int[])"));
270 
271         // empty/white space search should not trigger results
272         checkNullSearch(inv, "");
273         checkNullSearch(inv, " ");
274         checkNullSearch(inv, "    ");
275         checkNullSearch(inv, " \t\t ");
276 
277 
278         // _ word boundaries and case sensitivity
279         checkSearch(inv, "some", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
280         checkSearch(inv, "SOME", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
281         checkSearch(inv, "Some", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
282         checkSearch(inv, "int", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
283         checkSearch(inv, "INT", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
284         checkSearch(inv, "Int", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
285         checkSearch(inv, "int_con", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
286         checkSearch(inv, "INT_CON", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
287         checkSearch(inv, "NT", List.of());
288         checkSearch(inv, "NT_", List.of());
289         checkSearch(inv, "_const", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
290         checkSearch(inv, "_CONST", List.of("listpkg.Nolist.SOME_INT_CONSTANT"));
291 
292         // Test for all packages, all classes links
293         checkSearch(inv, "all", List.of("All Packages", "All Classes"));
294         checkSearch(inv, "All", List.of("All Packages", "All Classes"));
295         checkSearch(inv, "ALL", List.of("All Packages", "All Classes"));
296 
297         // test for generic types, var-arg and array args
298         checkSearch(inv, "(map<string, ? ext collection>)",
299                 List.of("listpkg.Nolist.withTypeParams(Map<String, ? extends Collection>)"));
300         checkSearch(inv, "(m<str,? ext coll>",
301                 List.of("listpkg.Nolist.withTypeParams(Map<String, ? extends Collection>)"));
302         checkSearch(inv, "(object...", List.of("listpkg.Nolist.withVarArgs(Object...)"));
303         checkSearch(inv, "(obj...", List.of("listpkg.Nolist.withVarArgs(Object...)"));
304         checkSearch(inv, "(e..", List.of("listpkg.List.of(E...)"));
305         checkSearch(inv, "(int[]", List.of("listpkg.Nolist.withArrayArg(int[])"));
306         checkSearch(inv, "(i[]", List.of("listpkg.Nolist.withArrayArg(int[])"));
307     }
308 
checkSearch(Invocable inv, String query, List<String> results)309     void checkSearch(Invocable inv, String query, List<String> results) throws ScriptException, NoSuchMethodException {
310         checkList((List) inv.invokeFunction("search", query), results);
311     }
312 
checkList(List<?> result, List<?> expected)313     void checkList(List<?> result, List<?> expected) {
314         checking("Checking list: " + result);
315         if (!expected.equals(result)) {
316             failed("Expected: " + expected + ", got: " + result);
317         } else {
318             passed("List matches expected result");
319         }
320     }
321 
checkNullSearch(Invocable inv, String query)322     void checkNullSearch(Invocable inv, String query) throws ScriptException, NoSuchMethodException {
323         Object result = inv.invokeFunction("search", query);
324         checking("Checking null result");
325         if (result == null) {
326             passed("Result is null as expected");
327         } else {
328             failed("Expected: null, got: " + result);
329         }
330     }
331 }
332