View Javadoc
1   /*
2    * SPDX-License-Identifier: LGPL-2.1-or-later
3    * See LICENSE file for details.
4    *
5    * Copyright 2012-2026 Hazendaz.
6    */
7   package net.alchim31.maven.yuicompressor;
8   
9   import java.nio.charset.StandardCharsets;
10  import java.nio.file.Files;
11  import java.nio.file.Path;
12  
13  import org.junit.jupiter.api.AfterEach;
14  import org.junit.jupiter.api.Assertions;
15  import org.junit.jupiter.api.BeforeEach;
16  import org.junit.jupiter.api.Test;
17  import org.mockito.Mockito;
18  import org.mozilla.javascript.Context;
19  import org.mozilla.javascript.Function;
20  import org.mozilla.javascript.Scriptable;
21  
22  /**
23   * The Class BasicRhinoShellTest.
24   */
25  class BasicRhinoShellTest {
26  
27      /** The temp file. */
28      private Path tempFile;
29  
30      /**
31       * Sets the up.
32       *
33       * @throws Exception
34       *             the exception
35       */
36      @BeforeEach
37      void setUp() throws Exception {
38          this.tempFile = Files.createTempFile("rhino-test", ".js");
39      }
40  
41      /**
42       * Tear down.
43       *
44       * @throws Exception
45       *             the exception
46       */
47      @AfterEach
48      void tearDown() throws Exception {
49          Files.deleteIfExists(this.tempFile);
50      }
51  
52      /**
53       * Test read file returns content.
54       *
55       * @throws Exception
56       *             the exception
57       */
58      @Test
59      void testReadFileReturnsContent() throws Exception {
60          final var content = "var a = 1;";
61          try (var writer = Files.newBufferedWriter(this.tempFile, StandardCharsets.UTF_8)) {
62              writer.write(content);
63          }
64          final var shell = new BasicRhinoShell();
65          final var result = shell.readFile(this.tempFile.toString());
66          Assertions.assertEquals(content, result.trim());
67      }
68  
69      /**
70       * Test read file throws runtime exception.
71       */
72      @Test
73      void testReadFileThrowsRuntimeException() {
74          final var shell = new BasicRhinoShell();
75          Assertions.assertThrows(RuntimeException.class, () -> shell.readFile("nonexistent.js"));
76      }
77  
78      /**
79       * Test process options version.
80       */
81      @Test
82      void testProcessOptionsVersion() {
83          final var cx = Context.enter();
84          final String[] args = { "-version", "170", "script.js" };
85          final var result = BasicRhinoShell.processOptions(cx, args);
86          Assertions.assertEquals("script.js", result[0]);
87          Assertions.assertEquals(170, cx.getLanguageVersion());
88          Context.exit();
89      }
90  
91      /**
92       * Test help does not throw.
93       */
94      @Test
95      void testHelpDoesNotThrow() {
96          final var shell = new BasicRhinoShell();
97          shell.help(); // Should not throw
98      }
99  
100     /**
101      * Test quit sets flag.
102      *
103      * @throws NoSuchFieldException
104      *             the no such field exception
105      * @throws SecurityException
106      *             the security exception
107      * @throws IllegalArgumentException
108      *             the illegal argument exception
109      * @throws IllegalAccessException
110      *             the illegal access exception
111      */
112     @Test
113     void testQuitSetsFlag()
114             throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
115         final var shell = new BasicRhinoShell();
116         shell.quit();
117         // Reflection to check private field
118         final var field = BasicRhinoShell.class.getDeclaredField("quitting");
119         field.setAccessible(true);
120         Assertions.assertTrue((Boolean) field.get(shell));
121     }
122 
123     /**
124      * Test print logs.
125      */
126     @Test
127     void testPrintLogs() {
128         final var cx = Context.enter();
129         final Scriptable scope = cx.initStandardObjects();
130         final Object[] args = { "Hello", "World" };
131         final var funObj = Mockito.mock(Function.class);
132         // Should not throw
133         BasicRhinoShell.print(cx, scope, args, funObj);
134         Context.exit();
135     }
136 
137     /**
138      * Test warn does not throw.
139      */
140     @Test
141     void testWarnDoesNotThrow() {
142         final var cx = Context.enter();
143         final Scriptable scope = cx.initStandardObjects();
144         final Object[] args = { "Warning", 1, "source.js", 2 };
145         final var funObj = Mockito.mock(Function.class);
146         BasicRhinoShell.warn(cx, scope, args, funObj);
147         Context.exit();
148     }
149 
150     /**
151      * Test exec() with a valid script file executes without throwing.
152      *
153      * @throws Exception
154      *             the exception
155      */
156     @Test
157     void testExec_withValidScriptFile_doesNotThrow() throws Exception {
158         final var content = "var result = 1 + 1;";
159         try (var writer = Files.newBufferedWriter(this.tempFile, StandardCharsets.UTF_8)) {
160             writer.write(content);
161         }
162         final var reporter = Mockito.mock(org.mozilla.javascript.ErrorReporter.class);
163         // Should complete without throwing
164         Assertions.assertDoesNotThrow(() -> BasicRhinoShell.exec(new String[] { this.tempFile.toString() }, reporter),
165                 "exec() with a valid script file should not throw");
166     }
167 
168     /**
169      * Test exec() with an empty argument list (would trigger interactive mode with stdin) – we just test no exception
170      * for a simple evaluation scenario by providing a trivial script.
171      *
172      * @throws Exception
173      *             the exception
174      */
175     @Test
176     void testExec_withScriptThatCallsPrint_doesNotThrow() throws Exception {
177         Files.write(this.tempFile, "print('hello');".getBytes(StandardCharsets.UTF_8));
178         final var reporter = Mockito.mock(org.mozilla.javascript.ErrorReporter.class);
179         Assertions.assertDoesNotThrow(() -> BasicRhinoShell.exec(new String[] { this.tempFile.toString() }, reporter),
180                 "exec() with a print() call should not throw");
181     }
182 
183     /**
184      * Test load() static function – loading a valid script file should not throw.
185      *
186      * @throws Exception
187      *             the exception
188      */
189     @Test
190     void testLoad_withValidFile_doesNotThrow() throws Exception {
191         Files.write(this.tempFile, "var loaded = true;".getBytes(StandardCharsets.UTF_8));
192         final var cx = Context.enter();
193         try {
194             final BasicRhinoShell shell = new BasicRhinoShell();
195             cx.initStandardObjects(shell);
196             final Scriptable scope = shell;
197             final Object[] args = { this.tempFile.toString() };
198             final var funObj = Mockito.mock(Function.class);
199             Assertions.assertDoesNotThrow(() -> BasicRhinoShell.load(cx, scope, args, funObj),
200                     "load() with a valid JS file should not throw");
201         } finally {
202             Context.exit();
203         }
204     }
205 
206     /**
207      * Test getClassName() returns "global".
208      */
209     @Test
210     void testGetClassName_returnsGlobal() {
211         final var shell = new BasicRhinoShell();
212         Assertions.assertEquals("global", shell.getClassName(), "getClassName() should return 'global'");
213     }
214 
215     /**
216      * Test processOptions with no arguments returns empty array.
217      */
218     @Test
219     void testProcessOptions_noArgs_returnsEmptyArray() {
220         final var cx = Context.enter();
221         try {
222             final String[] result = BasicRhinoShell.processOptions(cx, new String[0]);
223             Assertions.assertEquals(0, result.length, "Empty args should return empty result");
224         } finally {
225             Context.exit();
226         }
227     }
228 
229     /**
230      * Test processOptions with a non-flag argument returns the remaining args starting from that argument.
231      */
232     @Test
233     void testProcessOptions_nonFlagArg_returnsRemainingArgs() {
234         final var cx = Context.enter();
235         try {
236             final String[] result = BasicRhinoShell.processOptions(cx, new String[] { "script.js", "arg1" });
237             Assertions.assertEquals(2, result.length, "Expected both remaining args");
238             Assertions.assertEquals("script.js", result[0]);
239             Assertions.assertEquals("arg1", result[1]);
240         } finally {
241             Context.exit();
242         }
243     }
244 
245     /**
246      * Test version get set.
247      */
248     @Test
249     void testVersionGetSet() {
250         final var cx = Context.enter();
251         final Scriptable scope = cx.initStandardObjects();
252         final Object[] args = { 170 };
253         final var funObj = Mockito.mock(Function.class);
254         final var oldVersion = BasicRhinoShell.version(cx, scope, new Object[0], funObj);
255         BasicRhinoShell.version(cx, scope, args, funObj);
256         Assertions.assertEquals(0.0, oldVersion);
257         Assertions.assertEquals(170, cx.getLanguageVersion());
258         Context.exit();
259     }
260 
261 }