// Copyright 2012 Georg-August-Universität Göttingen, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package de.ugoe.cs.autoquest.genericeventmonitor; import static org.junit.Assert.*; import java.util.HashMap; import java.util.Map; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.junit.After; import org.junit.Before; import org.junit.Test; import de.ugoe.cs.autoquest.genericeventmonitor.ClientInfos; import de.ugoe.cs.autoquest.genericeventmonitor.GenericEvent; import de.ugoe.cs.autoquest.genericeventmonitor.GenericEventMonitorMessageListener; import de.ugoe.cs.autoquest.genericeventmonitor.GenericEventMonitorServer; import de.ugoe.cs.autoquest.genericeventmonitor.GenericEventTarget; import de.ugoe.cs.util.console.TextConsole; /** * * @author Patrick Harms */ public class GenericEventMonitorServerTest implements GenericEventMonitorMessageListener { /** * */ public static final TextConsole CONSOLE = new TextConsole(); /** * */ private static final int PORT = 19098; /** * */ private GenericEvent[] events; /** * */ private GenericEventTarget[] eventTargetStructures; /** * */ private String eventHandlingError; /** * */ private GenericEventMonitorServer server; /** * */ @Before public void setUp() throws Exception { server = new GenericEventMonitorServer(PORT, this); server.init(); server.start(); } /** * */ @After public void tearDown() throws Exception { events = null; eventHandlingError = null; if (server != null) { try { server.stop(); } finally { server = null; } } } /** * */ @Test public void testSimpleMessage() throws Exception { String clientId = "123"; String appId = "456"; String message = "{" + " \"message\": {" + " \"clientInfos\": {" + " \"clientId\":\"" + clientId + "\"," + " \"appId\":\"" + appId + "\"," + " }," + " \"targetStructure\": [{" + " \"targetId\":\"target1\"," + " \"param1\":\"value1\"," + " \"param3\":\"value3\"," + " \"param2\":\"value2\"," + " \"children\":" + " [ {" + " \"targetId\":\"target3\"," + " \"index\":\"0\"," + " }," + " {" + " \"targetId\":\"target2\"," + " \"htmlId\":\"gsr\"," + " }" + " ]" + " }]," + " \"events\":" + " [ {" + " \"time\":\"12345\"," + " \"targetId\":\"target2\"," + " \"type\":\"gaze\"," + " \"xcoordinate\": \"194\"," + " \"ycoordinate\": \"12\"" + " }" + " ]" + " }" + "}"; sendMessageAndAssertResponse(message); if (eventHandlingError != null) { fail(eventHandlingError); } // check the target structure GenericEventTarget parent = null; assertEquals(1, eventTargetStructures.length); GenericEventTarget target = eventTargetStructures[0]; assertTarget(target, parent, 2, new String[] {"param1", "value1"}, new String[] {"param2", "value2"}, new String[] {"param3", "value3"}); parent = target; target = parent.getChildren().get(0); assertTarget(target, parent, 0, new String[] {"index", "0"}); target = parent.getChildren().get(1); assertTarget(target, parent, 0, new String[] {"htmlId", "gsr"}); // check the event assertNotNull(events); assertEquals(1, events.length); assertEvent(0, 12345, target, "gaze", new String[] {"xcoordinate", "194"}, new String[] {"ycoordinate", "12"}); } /** * */ @Test public void testComplexMessage() throws Exception { String clientId = "123"; String appId = "456"; String message = "{" + " \"message\": {" + " \"clientInfos\": {" + " \"clientId\":\"" + clientId + "\"," + " \"appId\":\"" + appId + "\"," + " }," + " \"targetStructure\": [{" + " \"targetId\":\"html\"," + " \"index\":\"0\"," + " \"children\":" + " [ {" + " \"targetId\":\"head\"," + " \"index\":\"0\"," + " }," + " {" + " \"tagName\":\"body\"," + " \"targetId\":\"gsr\"," + " \"children\":" + " [ {" + " \"tagName\":\"input_button\"," + " \"targetId\":\"input1\"," + " }," + " {" + " \"tagName\":\"input_button\"," + " \"targetId\":\"input2\"," + " }," + " {" + " \"tagName\":\"input_button\"," + " \"targetId\":\"input3\"," + " }," + " {" + " \"tagName\":\"input_button\"," + " \"targetId\":\"input4\"," + " }," + " {" + " \"tagName\":\"input_button\"," + " \"targetId\":\"input5\"," + " }," + " {" + " \"tagName\":\"input_button\"," + " \"targetId\":\"input6\"," + " }," + " {" + " \"tagName\":\"input_button\"," + " \"targetId\":\"input7\"," + " }," + " {" + " \"tagName\":\"input_button\"," + " \"targetId\":\"input8\"," + " }," + " {" + " \"tagName\":\"input_button\"," + " \"targetId\":\"input9\"," + " }," + " {" + " \"tagName\":\"input_button\"," + " \"targetId\":\"input10\"," + " }," + " ]" + " }" + " ]" + " }]," + " \"events\":" + " [ {" + " \"time\":\"1\"," + " \"targetId\":\"input1\"," + " \"type\":\"onclick\"," + " \"coordinates\": \"194\"" + " }," + " {" + " \"time\":\"2\"," + " \"targetId\":\"input2\"," + " \"type\":\"ondblclick\"," + " \"coordinates\": \"194\"" + " }," + " {" + " \"time\":\"3\"," + " \"targetId\":\"input3\"," + " \"type\":\"onfocus\"" + " }," + " {" + " \"time\":\"4\"," + " \"targetId\":\"input4\"," + " \"type\":\"onclick\"," + " \"coordinates\": \"255\"" + " }," + " {" + " \"time\":\"5\"," + " \"targetId\":\"input5\"," + " \"type\":\"onfocus\"" + " }," + " {" + " \"time\":\"6\"," + " \"targetId\":\"input6\"," + " \"type\":\"onfocus\"" + " }," + " {" + " \"time\":\"7\"," + " \"targetId\":\"input7\"," + " \"type\":\"onfocus\"" + " }," + " {" + " \"time\":\"8\"," + " \"targetId\":\"input8\"," + " \"type\":\"onclick\"," + " \"coordinates\": \"255\"" + " }," + " {" + " \"time\":\"9\"," + " \"targetId\":\"input9\"," + " \"type\":\"onscroll\"," + " \"scrollPosition\": \"194\"" + " }," + " {" + " \"time\":\"10\"," + " \"targetId\":\"input10\"," + " \"type\":\"onclick\"," + " \"coordinates\": \"194\"" + " }" + " ]" + " }" + "}"; sendMessageAndAssertResponse(message); if (eventHandlingError != null) { fail(eventHandlingError); } // check the target structure GenericEventTarget parent = null; assertEquals(1, eventTargetStructures.length); GenericEventTarget target = eventTargetStructures[0]; assertTarget(target, parent, 2, new String[] {"index", "0"}); parent = target; target = parent.getChildren().get(0); assertTarget(target, parent, 0, new String[] {"index", "0"}); target = parent.getChildren().get(1); assertTarget(target, parent, 10, new String[] {"tagName", "body"}); parent = target; for (GenericEventTarget child : parent.getChildren()) { assertTarget(child, parent, 0, new String[] {"tagName", "input_button"}); } // check the events assertNotNull(events); assertEquals(10, events.length); for (int i = 0; i < events.length; i++) { String eventType; if ((i == 1)) { eventType = "ondblclick"; } else if ((i == 2) || ((4 <= i) && (i <= 6))) { eventType = "onfocus"; } else if ((i == 8)) { eventType = "onscroll"; } else { eventType = "onclick"; } String[] parameter = null; if (i <= 1) { parameter = new String[] { "coordinates", "194" }; } else if (i == 3) { parameter = new String[] { "coordinates", "255" }; } else if (i == 7) { parameter = new String[] { "coordinates", "255" }; } else if (i == 8) { parameter = new String[] { "scrollPosition", "194" }; } else if (i == 9) { parameter = new String[] { "coordinates", "194" }; } if (parameter != null) { assertEvent(i, i + 1, target.getChildren().get(i), eventType, parameter); } else { assertEvent(i, i + 1, target.getChildren().get(i), eventType); } } } /** * */ @Test public void testInvalidMessages() throws Exception { String message = "}"; sendMessageAndAssertResponse(message); if (eventHandlingError != null) { fail(eventHandlingError); } assertNull(events); message = "blaublbidalslkdjflqkerowercalksdjfdlkkjdjfk"; sendMessageAndAssertResponse(message); if (eventHandlingError != null) { fail(eventHandlingError); } assertNull(events); // the following message doesn't work because of the missing scroll position in the event message = "{" + " \"message\": {" + " \"clientInfos\": {" + " \"clientId\":\"123\"," + " \"userAgent\":\"Agent\"," + " \"title\":\"Title\"," + " \"url\":\"http://host/path\"" + " }," + " \"guiModel\": {" + " \"tagName\":\"html\"," + " \"index\":\"0\"," + " \"children\":" + " [ {" + " \"tagName\":\"head\"," + " \"index\":\"0\"," + " }," + " {" + " \"tagName\":\"body\"," + " \"htmlId\":\"gsr\"," + " }" + " ]" + " }," + " \"events\":" + " [ {" + " \"time\":\"9\"," + " \"path\":\"/html[0]/body(htmlId=gsr)\"," + " \"type\":\"onscroll\"," + " }" + " ]" + " }" + "}"; sendMessageAndAssertResponse(message); if (eventHandlingError != null) { fail(eventHandlingError); } assertNull(events); // the following message doesn't work because of the missing client id message = "{" + " \"message\": {" + " \"clientInfos\": {" + " \"userAgent\":\"Agent\"," + " \"title\":\"Title\"," + " \"url\":\"http://host/path\"" + " }," + " \"guiModel\": {" + " \"tagName\":\"html\"," + " \"index\":\"0\"," + " \"children\":" + " [ {" + " \"tagName\":\"head\"," + " \"index\":\"0\"," + " }," + " {" + " \"tagName\":\"body\"," + " \"htmlId\":\"gsr\"," + " }" + " ]" + " }," + " \"events\":" + " [ {" + " \"time\":\"12345\"," + " \"path\":\"/html[0]/body(htmlId=gsr)\"," + " \"type\":\"onunload\"" + " }" + " ]" + " }" + "}"; sendMessageAndAssertResponse(message); if (eventHandlingError != null) { fail(eventHandlingError); } assertNull(events); // the following message doesn't work because of the invalid time stamp message = "{" + " \"message\": {" + " \"clientInfos\": {" + " \"clientId\":\"123\"," + " \"userAgent\":\"Agent\"," + " \"title\":\"Title\"," + " \"url\":\"http://host/path\"" + " }," + " \"guiModel\": {" + " \"tagName\":\"html\"," + " \"index\":\"0\"," + " \"children\":" + " [ {" + " \"tagName\":\"head\"," + " \"index\":\"0\"," + " }," + " {" + " \"tagName\":\"body\"," + " \"htmlId\":\"gsr\"," + " }" + " ]" + " }," + " \"events\":" + " [ {" + " \"time\":\"blub\"," + " \"path\":\"/html[0]/body(htmlId=gsr)\"," + " \"type\":\"onunload\"" + " }" + " ]" + " }" + "}"; sendMessageAndAssertResponse(message); if (eventHandlingError != null) { fail(eventHandlingError); } assertNull(events); } /* (non-Javadoc) * @see GenericEventMonitorMessageListener#handleEvents(ClientInfos, GenericEvent[]) */ @Override public void handleEvents(ClientInfos clientInfos, GenericEvent[] events) { if (clientInfos == null) { eventHandlingError = "client infos were null"; } else if (events == null) { eventHandlingError = "events were null"; } else { Map roots = new HashMap<>(); for (GenericEvent event : events) { if (!clientInfos.equals(event.getClientInfos())) { eventHandlingError = "one of the events did not have a correct client info"; } GenericEventTarget parent = event.getTarget(); while (parent.getParent() != null) { parent = parent.getParent(); } roots.put(parent.getId(), parent); } this.events = events; this.eventTargetStructures = roots.values().toArray(new GenericEventTarget[roots.size()]); } } /** * */ private void sendMessageAndAssertResponse(String message) throws Exception { DefaultHttpClient httpclient = new DefaultHttpClient(); HttpPost httpPost = new HttpPost("http://localhost:" + PORT + "/"); //HttpPost httpPost = new HttpPost("http://localhost:8090/"); HttpEntity entity = new StringEntity(message, ContentType.APPLICATION_JSON); httpPost.setEntity(entity); try { HttpResponse response = httpclient.execute(httpPost); // the monitor always returns 200 without any additional information. The client must // never get more or less information. This is especially important for preventing // hackers from finding out more assertEquals(200, response.getStatusLine().getStatusCode()); assertTrue ((response.getEntity() == null) || (response.getEntity().getContentLength() == 0) || ((response.getEntity().getContentLength() == 1) && (response.getEntity().getContent().read() == ' '))); } finally { httpPost.releaseConnection(); } } /** * */ private void assertTarget(GenericEventTarget target, GenericEventTarget parent, int noOfChildren, String[] ... parameters) { if (parent != null) { assertNotNull(target.getParent()); assertEquals(parent.getId(), target.getParent().getId()); } else { assertNull(target.getParent()); } assertNotNull(target.getId()); if (noOfChildren > 0) { assertNotNull(target.getChildren()); assertEquals(noOfChildren, target.getChildren().size()); } else { assertNull(target.getChildren()); } assertEquals(parameters.length, target.getParameters().size()); for (String[] parameter : parameters) { assertEquals(parameter[1], target.getParameters().get(parameter[0])); } } /** * */ private void assertEvent(int index, long timestamp, GenericEventTarget target, String eventType, String[] ... parameters) { assertEquals("event " + index, new Long(timestamp), events[index].getTime()); assertEquals("event " + index, target, events[index].getTarget()); assertEquals("event " + index, eventType, events[index].getEventType()); assertEquals("event " + index, parameters.length, events[index].getParameters().size()); for (String[] parameter : parameters) { assertEquals ("event " + index, parameter[1], events[index].getParameters().get(parameter[0])); } } }