Java tutorial
// This code is part of the CPCC-NG project. // // Copyright (c) 2013 Clemens Krainer <clemens.krainer@gmail.com> // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software Foundation, // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. package cpcc.vvrte.services.js; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; import org.apache.tapestry5.hibernate.HibernateSessionManager; import org.apache.tapestry5.ioc.ServiceResources; import org.apache.tapestry5.ioc.services.PerthreadManager; import org.assertj.core.api.Fail; import org.hibernate.internal.util.SerializationHelper; import org.mozilla.javascript.Context; import org.mozilla.javascript.ContinuationPending; import org.mozilla.javascript.NativeArray; import org.mozilla.javascript.NativeObject; import org.mozilla.javascript.ScriptableObject; import org.slf4j.Logger; import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import cpcc.vvrte.base.VirtualVehicleMappingDecision; import cpcc.vvrte.entities.VirtualVehicle; import cpcc.vvrte.entities.VirtualVehicleState; /** * JavascriptServiceTest */ @Test(singleThreaded = true) public class JavascriptServiceTest { private PerthreadManager perthreadManager; private HibernateSessionManager sessionManager; private ServiceResources serviceResources; private Logger logger; @BeforeMethod public void setUp() { perthreadManager = mock(PerthreadManager.class); sessionManager = mock(HibernateSessionManager.class); logger = mock(Logger.class); serviceResources = mock(ServiceResources.class); when(serviceResources.getService(PerthreadManager.class)).thenReturn(perthreadManager); when(serviceResources.getService(HibernateSessionManager.class)).thenReturn(sessionManager); } @Test public void shouldExecuteSimpleJS() throws InterruptedException, IOException { VirtualVehicle vv = new VirtualVehicle(); vv.setId(123); vv.setCode("function f(x){return x+1} f(7)"); vv.setApiVersion(1); vv.setUuid("27369070-a042-11e5-a35d-0f12a6b8b54e"); JavascriptService jss = new JavascriptServiceImpl(logger, serviceResources, null); JavascriptWorker sut = jss.createWorker(vv, false); MyWorkerStateListener workerListener = new MyWorkerStateListener(); sut.addStateListener(workerListener); sut.run(); assertThat(sut.getResult()).isNotNull().isEqualTo("undefined"); assertThat(sut.getWorkerState()).isNotNull().isEqualTo(VirtualVehicleState.FINISHED); assertThat(workerListener.getWorker()).isNotNull().isEqualTo(sut); assertThat(workerListener.getState()).isNotNull().isEqualTo(VirtualVehicleState.FINISHED); verify(sessionManager, times(3)).commit(); verify(perthreadManager).cleanup(); } @Test public void shouldNotExecuteNaughtyScript() throws InterruptedException, IOException { VirtualVehicle vv = new VirtualVehicle(); vv.setId(123); vv.setCode("java.lang.System.currentTimeMillis()"); vv.setApiVersion(1); vv.setUuid("599b7ada-a042-11e5-98b1-9767a13e60f2"); JavascriptService jss = new JavascriptServiceImpl(logger, serviceResources, null); JavascriptWorker sut = jss.createWorker(vv, false); sut.run(); assertThat(sut.getWorkerState()).isNotNull().isEqualTo(VirtualVehicleState.DEFECTIVE); assertThat(sut.getResult()).isNotNull() .startsWith("TypeError: Cannot call property currentTimeMillis in object"); verify(sessionManager, times(3)).commit(); verify(perthreadManager).cleanup(); } @Test public void shouldDenyWrongApiVersion() throws InterruptedException, IOException { VirtualVehicle vv = new VirtualVehicle(); vv.setId(123); vv.setCode("function f(x){return x+1} f(7)"); vv.setApiVersion(1000); vv.setUuid("599bf294-a042-11e5-a552-abf9c5301b8b"); JavascriptService jss = new JavascriptServiceImpl(logger, serviceResources, null); try { jss.createWorker(vv, false); Fail.failBecauseExceptionWasNotThrown(IOException.class); } catch (IOException e) { assertThat(e.getMessage()).isEqualTo("Can not handle API version 1000"); } verifyZeroInteractions(sessionManager); verifyZeroInteractions(perthreadManager); } @Test public void shouldHandleVvRte() throws IOException, InterruptedException { MyBuiltInFunctions functions = new MyBuiltInFunctions(); JavascriptService jss = new JavascriptServiceImpl(logger, serviceResources, functions); jss.addAllowedClassRegex("cpcc.vvrte.services.js.JavascriptServiceTest\\$MyBuiltInFunctions"); ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream stdOut = new PrintStream(out, true); VvRteFunctions.setStdOut(stdOut); InputStream scriptStream = this.getClass().getResourceAsStream("simple-vv.js"); String script = IOUtils.toString(scriptStream, "UTF-8"); assertThat(script).isNotNull().isNotEmpty(); VirtualVehicle vv = new VirtualVehicle(); vv.setId(123); vv.setCode(script); vv.setApiVersion(1); vv.setUuid("599c6710-a042-11e5-a0fb-83f9ea30f21e"); functions.setMigrate(true); JavascriptWorker sut = jss.createWorker(vv, false); sut.run(); // System.out.println("shouldHandleVvRte() result1: '" + sut.getResult() + "'"); // System.out.println("shouldHandleVvRte() output1: '" + out.toString("UTF-8") + "'"); assertThat(sut.getWorkerState()).isNotNull().isEqualTo(VirtualVehicleState.INTERRUPTED); InputStream resultStream = this.getClass().getResourceAsStream("simple-vv-expected-result-1.txt"); String expectedResult = IOUtils.toString(resultStream, "UTF-8"); assertThat(out.toString("UTF-8")).isNotNull().isEqualTo(expectedResult); functions.setMigrate(false); byte[] snapshot = sut.getSnapshot(); vv.setContinuation(snapshot); sut = jss.createWorker(vv, true); sut.run(); stdOut.flush(); assertThat(sut.getWorkerState()).isNotNull().isEqualTo(VirtualVehicleState.FINISHED); // System.out.println("shouldHandleVvRte() result2: '" + x.getResult() + "'"); // System.out.println("shouldHandleVvRte() output2: '" + out.toString("UTF-8") + "'"); resultStream = this.getClass().getResourceAsStream("simple-vv-expected-result-2.txt"); expectedResult = IOUtils.toString(resultStream, "UTF-8"); assertThat(out.toString("UTF-8")).isNotNull().isEqualTo(expectedResult); verify(sessionManager, times(6)).commit(); verify(perthreadManager, times(2)).cleanup(); } @Test public void shouldHandleVvStorage() throws IOException { MyBuiltInFunctions functions = new MyBuiltInFunctions(); JavascriptService jss = new JavascriptServiceImpl(logger, serviceResources, functions); jss.addAllowedClassRegex("cpcc.vvrte.services.js.JavascriptServiceTest\\$MyBuiltInFunctions"); ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream stdOut = new PrintStream(out, true); VvRteFunctions.setStdOut(stdOut); InputStream scriptStream = this.getClass().getResourceAsStream("storage-test.js"); String script = IOUtils.toString(scriptStream, "UTF-8"); assertThat(script).isNotNull().isNotEmpty(); VirtualVehicle vv = new VirtualVehicle(); vv.setId(123); vv.setCode(script); vv.setApiVersion(1); vv.setUuid("599cda7e-a042-11e5-a5b4-ffed137d569e"); functions.setMigrate(false); JavascriptWorker sut = jss.createWorker(vv, false); sut.run(); stdOut.flush(); // System.out.println("shouldHandleVvRte() result: '" + x.getResult() + "'"); // System.out.println("shouldHandleVvRte() output: '" + out.toString("UTF-8") + "'"); assertThat(sut.getWorkerState()).isNotNull().isEqualTo(VirtualVehicleState.FINISHED); InputStream resultStream = this.getClass().getResourceAsStream("storage-test-expected-result.txt"); String expectedResult = IOUtils.toString(resultStream, "UTF-8"); assertThat(out.toString("UTF-8")).isNotNull().isEqualTo(expectedResult); verify(sessionManager, times(3)).commit(); verify(perthreadManager).cleanup(); } @DataProvider public static Object[][] emptyScriptDataProvider() { return new Object[][] { new Object[] { null }, new Object[] { "" }, new Object[] { "\n" }, new Object[] { "\n\n\n\n\r\n" }, }; } @Test(dataProvider = "emptyScriptDataProvider") public void shouldHandleEmptyScript(String script) throws IOException, InterruptedException { VirtualVehicle vv = new VirtualVehicle(); vv.setId(123); vv.setCode(script); vv.setApiVersion(1); vv.setUuid("599d4f4a-a042-11e5-a1d0-474802529ab0"); JavascriptService jss = new JavascriptServiceImpl(logger, serviceResources, null); JavascriptWorker sut = jss.createWorker(vv, false); sut.run(); assertThat(sut.getWorkerState()).isNotNull().isEqualTo(VirtualVehicleState.DEFECTIVE); verify(sessionManager, times(3)).commit(); verify(perthreadManager).cleanup(); } @Test(dataProvider = "emptyScriptDataProvider") public void shouldCompileEmptyScript(String script) throws IOException { JavascriptService jss = new JavascriptServiceImpl(logger, serviceResources, null); Object[] result = jss.codeVerification(script, 1); assertThat(result).isNotNull().hasSize(0); } @Test public void shouldHandleNullContinuation() throws InterruptedException, IOException { VirtualVehicle vv = new VirtualVehicle(); vv.setId(123); vv.setCode(null); vv.setApiVersion(1); vv.setContinuation(null); vv.setUuid("599d9932-a042-11e5-911b-f785d3884ce0"); JavascriptService jss = new JavascriptServiceImpl(logger, serviceResources, null); JavascriptWorker sut = jss.createWorker(vv, true); sut.run(); assertThat(sut.getWorkerState()).isNotNull().isEqualTo(VirtualVehicleState.DEFECTIVE); verify(sessionManager, times(3)).commit(); verify(perthreadManager).cleanup(); } @Test public void shouldReturnScriptWithApiPrefix() throws IOException { String script = "function f(x){return x+1} f(7)"; VirtualVehicle vv = new VirtualVehicle(); vv.setId(123); vv.setCode(script); vv.setApiVersion(1); vv.setUuid("599ddad2-a042-11e5-ab97-e3af3b4f34e8"); JavascriptService jss = new JavascriptServiceImpl(logger, serviceResources, null); JavascriptWorker sut = jss.createWorker(vv, false); assertThat(sut.getScript()).isNotNull().endsWith(script + "\n})();"); verifyZeroInteractions(sessionManager); verifyZeroInteractions(perthreadManager); } @Test public void shouldNotCompileErroneousScript() throws IOException { String script = "var x = 0;\nx x x"; JavascriptService jss = new JavascriptServiceImpl(logger, serviceResources, null); Object[] result = jss.codeVerification(script, 1); assertThat(result).isNotNull().hasSize(4); Integer column = (Integer) result[0]; Integer line = (Integer) result[1]; String errorMessage = (String) result[2]; String sourceLine = (String) result[3]; assertThat(column).isNotNull().isEqualTo(4); assertThat(line).isNotNull().isEqualTo(2); assertThat(errorMessage).isNotNull().isEqualTo("missing ; before statement"); assertThat(sourceLine).isNotNull().isEqualTo("x x x"); verifyZeroInteractions(sessionManager); verifyZeroInteractions(perthreadManager); } @Test public void shouldCompileProperScript() throws IOException { String script = "function f(x){return x+1} f(7)"; JavascriptService jss = new JavascriptServiceImpl(logger, serviceResources, null); Object[] result = jss.codeVerification(script, 1); assertThat(result).isNotNull().hasSize(0); verifyZeroInteractions(perthreadManager); } /** * MyBuiltInFunctions */ private static class MyBuiltInFunctions implements BuiltInFunctions { private boolean migrate = false; private Map<String, ScriptableObject> storageMap = new HashMap<String, ScriptableObject>(); /** * @param migrate the migrate to set */ public void setMigrate(boolean migrate) { this.migrate = migrate; } /** * {@inheritDoc} */ @Override public List<ScriptableObject> listSensors() { // TODO fix this. return listActiveSensors(); } /** * {@inheritDoc} */ @Override public List<ScriptableObject> listActiveSensors() { // System.out.println("listSensors start"); NativeObject barometer = new NativeObject(); barometer.put("name", barometer, "barometer"); NativeObject camera = new NativeObject(); camera.put("name", camera, "camera"); NativeObject thermometer = new NativeObject(); thermometer.put("name", thermometer, "thermometer"); // NativeArray sensors = new NativeArray(3); List<ScriptableObject> sensors = new ArrayList<ScriptableObject>(); sensors.add(barometer); sensors.add(thermometer); sensors.add(camera); // System.out.println("listSensors end"); return sensors; } /** * {@inheritDoc} */ @Override public ScriptableObject getSensor(String name) { NativeObject sensor = new NativeObject(); sensor.put("name", sensor, name); // System.out.println("getSensor"); return sensor; } /** * {@inheritDoc} */ @Override public ScriptableObject getSensorValue(ScriptableObject sensor) { NativeObject sensorValue = new NativeObject(); sensorValue.put("name", sensorValue, sensor.get("name")); sensorValue.put("value", sensorValue, "value"); // System.out.println("getSensorValue for " + sensor.get("name")); return sensorValue; } /** * {@inheritDoc} */ @Override public void executeTask(ScriptableObject managementParameters, ScriptableObject taskParameters) { // System.out.println("executeTask1"); if (!verifyTaskParameters(taskParameters)) { managementParameters.put("repeat", managementParameters, Boolean.FALSE); return; } // System.out.println("executeTask2"); Number sequence = (Number) managementParameters.get("sequence"); if (sequence.intValue() == 0) { // TODO decide for migration or not. // TODO migration: initiate migration by throwing CP-Exception. if (migrate) { // System.out.println("migration"); Context cx = Context.enter(); try { ContinuationPending cp = cx.captureContinuation(); VirtualVehicleMappingDecision decision = new VirtualVehicleMappingDecision(); decision.setMigration(true); cp.setApplicationState(new ApplicationState(decision)); throw cp; } finally { Context.exit(); } } // System.out.println("no migration"); // TODO no migration: schedule task and wait for completion. managementParameters.put("valid", managementParameters, Boolean.TRUE); managementParameters.put("sequence", managementParameters, Integer.valueOf(sequence.intValue() + 1)); NativeArray sensors = (NativeArray) taskParameters.get("sensors"); NativeArray sensorValues = new NativeArray(sensors.getLength()); for (int k = 0; k < sensors.getLength(); ++k) { NativeObject s = (NativeObject) sensors.get(k); sensorValues.put(k, sensorValues, getSensorValue(s)); } managementParameters.put("sensorValues", managementParameters, sensorValues); managementParameters.put("repeat", managementParameters, Boolean.TRUE); return; } // String type = (String) taskParameters.get("type"); Number tolerance = (Number) taskParameters.get("tolerance"); tolerance.doubleValue(); // NativeArray sensors = (NativeArray) taskParameters.get("sensors"); // TODO Auto-generated method stub managementParameters.put("repeat", managementParameters, Boolean.FALSE); return; } private boolean verifyTaskParameters(ScriptableObject taskParameters) { Object sensors = taskParameters.get("sensors"); Object type = taskParameters.get("type"); if (sensors == null || !(sensors instanceof NativeArray) || ((NativeArray) sensors).getLength() == 0) { return false; } if (type == null || !(type instanceof String) || !"point".equalsIgnoreCase((String) type)) { return false; } // !('sensors' in taskParams) || !('length' in taskParams.sensors) || taskParams.sensors.length == 0 || taskParams.type) // TODO Auto-generated method stub return true; } @Override public ScriptableObject loadObject(String name) { // System.out.println("loadObject " + name); return storageMap.get(name); } @Override public void storeObject(String name, ScriptableObject obj) { // System.out.println("storeObject " + name); storageMap.put(name, (ScriptableObject) SerializationHelper.clone(obj)); } @Override public List<String> listObjects(String pattern) { // System.out.println("listObjects " + pattern); List<String> result = new ArrayList<String>(); for (String entry : storageMap.keySet()) { if (entry.matches(pattern)) { result.add(entry); } } return result; } @Override public void removeObject(String name) { // System.out.println("removeObject " + name); storageMap.remove(name); } } /** * MyWorkerStateListener */ private static class MyWorkerStateListener implements JavascriptWorkerStateListener { private JavascriptWorker worker = null; private VirtualVehicleState state = null; /** * {@inheritDoc} */ @Override public void notify(JavascriptWorker worker, VirtualVehicleState state) { this.worker = worker; this.state = state; } /** * @return the worker */ public JavascriptWorker getWorker() { return worker; } /** * @return the state */ public VirtualVehicleState getState() { return state; } } }