// 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.htmlmonitor;
import static org.junit.Assert.*;
import java.io.File;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
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.eventcore.Event;
import de.ugoe.cs.autoquest.eventcore.IEventType;
import de.ugoe.cs.autoquest.eventcore.gui.KeyboardFocusChange;
import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction;
import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
import de.ugoe.cs.autoquest.eventcore.gui.MouseDoubleClick;
import de.ugoe.cs.autoquest.eventcore.gui.Scroll;
import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
import de.ugoe.cs.autoquest.plugin.html.HTMLLogParser;
import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLDocument;
import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLPageElement;
import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLServer;
import de.ugoe.cs.util.console.TextConsole;
/**
*
* TODO comment
*
*
* @author Patrick Harms
*/
public class HtmlMonitorTest {
/**
*
*/
public static final TextConsole CONSOLE = new TextConsole();
/**
*
*/
private final static String LOG_FILE_DIR = "target/tmp/logfiles/";
/**
*
*/
private static final int PORT = 19098;
/**
*
*/
private HtmlMonitor htmlMonitor;
/**
*
*/
@Before
public void setUp() throws Exception {
htmlMonitor = new HtmlMonitor(new String[] { LOG_FILE_DIR, Integer.toString(PORT) });
htmlMonitor.init();
htmlMonitor.start();
}
/**
*
*/
@After
public void tearDown() throws Exception {
if (htmlMonitor != null) {
try {
htmlMonitor.stop();
}
finally {
htmlMonitor = null;
}
}
deleteFiles(new File(LOG_FILE_DIR));
}
/**
*
*/
@Test
public void testOneSimpleMessage() throws Exception {
String clientId = "123";
String message =
"{" +
" \"message\": {" +
" \"clientInfos\": {" +
" \"clientId\":\"" + clientId + "\"," +
" \"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)\"," +
" \"eventType\":\"onclick\"" +
" \"coordinates\": [\"194\", \"7\"]" +
" }" +
" ]" +
" }" +
"}";
sendMessageAndAssertResponse(message);
htmlMonitor.stop();
htmlMonitor = null;
File logFile = new File(LOG_FILE_DIR + File.separator + clientId + File.separator +
"htmlmonitor_" + clientId + "_000.log");
assertTrue(logFile.exists());
HTMLLogParser parser = new HTMLLogParser();
parser.parseFile(logFile);
// check the GUI model
GUIModel guiModel = parser.getGuiModel();
assertNotNull(guiModel);
List nodes = guiModel.getRootElements();
assertNotNull(nodes);
assertEquals(1, nodes.size());
// get server node
IGUIElement node = nodes.get(0);
assertNotNull(node);
assertTrue(node instanceof HTMLServer);
assertEquals("HTML", node.getPlatform());
assertFalse(node.isUsed());
nodes = guiModel.getChildren(node);
assertNotNull(nodes);
assertEquals(1, nodes.size());
// get document node
node = nodes.get(0);
assertNotNull(node);
assertTrue(node instanceof HTMLDocument);
assertEquals("HTML", node.getPlatform());
assertFalse(node.isUsed());
nodes = guiModel.getChildren(node);
assertNotNull(nodes);
assertEquals(1, nodes.size());
// get html node
node = nodes.get(0);
assertNotNull(node);
assertTrue(node instanceof HTMLPageElement);
assertEquals("HTML", node.getPlatform());
assertFalse(node.isUsed());
nodes = guiModel.getChildren(node);
assertNotNull(nodes);
assertEquals(1, nodes.size()); // only one child as the head tag should have been ignored
// get body node
node = nodes.get(0);
assertNotNull(node);
assertTrue(node instanceof HTMLPageElement);
assertEquals("HTML", node.getPlatform());
assertTrue(node.isUsed());
nodes = guiModel.getChildren(node);
assertNotNull(nodes);
assertEquals(0, nodes.size());
// check the sequences
Collection> sequences = parser.getSequences();
assertNotNull(sequences);
Iterator> iterator = sequences.iterator();
assertTrue(iterator.hasNext());
List sequence = iterator.next();
assertFalse(iterator.hasNext());
assertNotNull(sequence);
assertEquals(1, sequence.size());
assertEvent(sequence.get(0), 12345, MouseClick.class, node, 194, 7);
}
/**
*
*/
@Test
public void testSeveralMessagesInOneSession() throws Exception {
String clientId = "123";
String message =
"{" +
" \"message\": {" +
" \"clientInfos\": {" +
" \"clientId\":\"" + clientId + "\"," +
" \"userAgent\":\"Agent\"," +
" \"title\":\"Title\"," +
" \"url\":\"http://host/path\"" +
" }," +
" \"guiModel\": {" +
" \"tagName\":\"html\"," +
" \"index\":\"0\"," +
" \"children\":" +
" [ {" +
" \"tagName\":\"head\"," +
" \"index\":\"0\"," +
" }," +
" {" +
" \"tagName\":\"body\"," +
" \"htmlId\":\"gsr\"," +
" \"children\":" +
" [ {" +
" \"tagName\":\"input_button\"," +
" \"htmlId\":\"input1\"," +
" }," +
" {" +
" \"tagName\":\"input_button\"," +
" \"htmlId\":\"input2\"," +
" }," +
" {" +
" \"tagName\":\"input_button\"," +
" \"htmlId\":\"input3\"," +
" }," +
" {" +
" \"tagName\":\"input_button\"," +
" \"htmlId\":\"input4\"," +
" }," +
" {" +
" \"tagName\":\"input_button\"," +
" \"htmlId\":\"input5\"," +
" }," +
" {" +
" \"tagName\":\"input_button\"," +
" \"htmlId\":\"input6\"," +
" }," +
" {" +
" \"tagName\":\"input_button\"," +
" \"htmlId\":\"input7\"," +
" }," +
" {" +
" \"tagName\":\"input_button\"," +
" \"htmlId\":\"input8\"," +
" }," +
" {" +
" \"tagName\":\"input_button\"," +
" \"htmlId\":\"input9\"," +
" }," +
" {" +
" \"tagName\":\"input_button\"," +
" \"htmlId\":\"input10\"," +
" }," +
" ]" +
" }" +
" ]" +
" }," +
" \"events\":" +
" [ {" +
" \"time\":\"1\"," +
" \"path\":\"/html[0]/body(htmlId=gsr)/input_button(htmlId=input1)\"," +
" \"eventType\":\"onclick\"," +
" \"coordinates\": [\"194\", \"7\"]" +
" }," +
" {" +
" \"time\":\"2\"," +
" \"path\":\"/html[0]/body(htmlId=gsr)/input_button(htmlId=input2)\"," +
" \"eventType\":\"ondblclick\"," +
" \"coordinates\": [\"194\", \"7\"]" +
" }," +
" {" +
" \"time\":\"3\"," +
" \"path\":\"/html[0]/body(htmlId=gsr)/input_button(htmlId=input3)\"," +
" \"eventType\":\"onfocus\"" +
" }," +
" {" +
" \"time\":\"4\"," +
" \"path\":\"/html[0]/body(htmlId=gsr)/input_button(htmlId=input4)\"," +
" \"eventType\":\"onclick\"," +
" \"coordinates\": [\"125\", \"14\"]" +
" }," +
" {" +
" \"time\":\"5\"," +
" \"path\":\"/html[0]/body(htmlId=gsr)/input_button(htmlId=input5)\"," +
" \"eventType\":\"onfocus\"" +
" }," +
" {" +
" \"time\":\"6\"," +
" \"path\":\"/html[0]/body(htmlId=gsr)/input_button(htmlId=input6)\"," +
" \"eventType\":\"onfocus\"" +
" }," +
" {" +
" \"time\":\"7\"," +
" \"path\":\"/html[0]/body(htmlId=gsr)/input_button(htmlId=input7)\"," +
" \"eventType\":\"onfocus\"" +
" }," +
" {" +
" \"time\":\"8\"," +
" \"path\":\"/html[0]/body(htmlId=gsr)/input_button(htmlId=input8)\"," +
" \"eventType\":\"onclick\"," +
" \"coordinates\": [\"255\", \"4\"]" +
" }," +
" {" +
" \"time\":\"9\"," +
" \"path\":\"/html[0]/body(htmlId=gsr)\"," +
" \"eventType\":\"onscroll\"," +
" \"scrollPosition\": [\"23\", \"567\"]" +
" }," +
" {" +
" \"time\":\"10\"," +
" \"path\":\"/html[0]/body(htmlId=gsr)/input_button(htmlId=input10)\"," +
" \"eventType\":\"onclick\"," +
" \"coordinates\": [\"516\", \"154\"]" +
" }" +
" ]" +
" }" +
"}";
sendMessageAndAssertResponse(message);
htmlMonitor.stop();
htmlMonitor = null;
File logFile = new File(LOG_FILE_DIR + File.separator + clientId + File.separator +
"htmlmonitor_" + clientId + "_000.log");
assertTrue(logFile.exists());
HTMLLogParser parser = new HTMLLogParser();
parser.parseFile(logFile);
// check the GUI model
GUIModel guiModel = parser.getGuiModel();
assertNotNull(guiModel);
List nodes = guiModel.getRootElements();
assertNotNull(nodes);
assertEquals(1, nodes.size());
// get server node
IGUIElement node = nodes.get(0);
assertNotNull(node);
assertTrue(node instanceof HTMLServer);
assertEquals("HTML", node.getPlatform());
assertFalse(node.isUsed());
nodes = guiModel.getChildren(node);
assertNotNull(nodes);
assertEquals(1, nodes.size());
// get document node
node = nodes.get(0);
assertNotNull(node);
assertTrue(node instanceof HTMLDocument);
assertEquals("HTML", node.getPlatform());
assertFalse(node.isUsed());
nodes = guiModel.getChildren(node);
assertNotNull(nodes);
assertEquals(1, nodes.size());
// get html node
node = nodes.get(0);
assertNotNull(node);
assertTrue(node instanceof HTMLPageElement);
assertEquals("HTML", node.getPlatform());
assertFalse(node.isUsed());
nodes = guiModel.getChildren(node);
assertNotNull(nodes);
assertEquals(1, nodes.size()); // only one child as the head tag should have been ignored
// get body node
IGUIElement body = nodes.get(0);
assertNotNull(body);
assertTrue(body instanceof HTMLPageElement);
assertEquals("HTML", body.getPlatform());
assertTrue(body.isUsed());
nodes = guiModel.getChildren(body);
assertNotNull(nodes);
assertEquals(10, nodes.size());
// get input nodes
for (int i = 0; i < nodes.size(); i++) {
node = nodes.get(i);
assertNotNull(node);
assertTrue(node instanceof HTMLPageElement);
assertEquals("HTML", node.getPlatform());
if (i != 8) {
assertTrue(node.isUsed());
}
else {
assertFalse(node.isUsed());
}
assertNotNull(guiModel.getChildren(node));
assertEquals(0, guiModel.getChildren(node).size());
}
// check the sequences
Collection> sequences = parser.getSequences();
assertNotNull(sequences);
Iterator> iterator = sequences.iterator();
assertTrue(iterator.hasNext());
List sequence = iterator.next();
assertFalse(iterator.hasNext());
assertNotNull(sequence);
assertEquals(10, sequence.size());
assertEvent(sequence.get(0), 1, MouseClick.class, nodes.get(0), 194, 7);
assertEvent(sequence.get(1), 2, MouseDoubleClick.class, nodes.get(1), 194, 7);
assertEvent(sequence.get(2), 3, KeyboardFocusChange.class, nodes.get(2), 0, 0);
assertEvent(sequence.get(3), 4, MouseClick.class, nodes.get(3), 125, 14);
assertEvent(sequence.get(4), 5, KeyboardFocusChange.class, nodes.get(4), 0, 0);
assertEvent(sequence.get(5), 6, KeyboardFocusChange.class, nodes.get(5), 0, 0);
assertEvent(sequence.get(6), 7, KeyboardFocusChange.class, nodes.get(6), 0, 0);
assertEvent(sequence.get(7), 8, MouseClick.class, nodes.get(7), 255, 4);
assertEvent(sequence.get(8), 9, Scroll.class, body, 0, 0);
assertEvent(sequence.get(9), 10, MouseClick.class, nodes.get(9), 516, 154);
}
/**
*
*/
@Test
public void testSeveralSessions() throws Exception {
String clientId = "123";
String message =
"{" +
" \"message\": {" +
" \"clientInfos\": {" +
" \"clientId\":\"" + clientId + "\"," +
" \"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)\"," +
" \"eventType\":\"onclick\"" +
" \"coordinates\": [\"194\", \"7\"]" +
" }" +
" ]" +
" }" +
"}";
sendMessageAndAssertResponse(message);
int numberOfSessions = 10;
for (int i = 0; i < numberOfSessions; i++) {
htmlMonitor.stop();
htmlMonitor = new HtmlMonitor(new String[] { LOG_FILE_DIR, Integer.toString(PORT) });
htmlMonitor.init();
htmlMonitor.start();
sendMessageAndAssertResponse(message);
}
htmlMonitor.stop();
htmlMonitor = null;
HTMLLogParser parser = new HTMLLogParser();
// assert 9 already rotated log files
for (int i = 0; i < numberOfSessions; i++) {
File logFile = new File(LOG_FILE_DIR + File.separator + clientId + File.separator +
"htmlmonitor_" + clientId + "_00" + i + ".log");
assertTrue(logFile.exists());
parser.parseFile(logFile);
}
// check the GUI model
GUIModel guiModel = parser.getGuiModel();
assertNotNull(guiModel);
List nodes = guiModel.getRootElements();
assertNotNull(nodes);
assertEquals(1, nodes.size());
// get server node
IGUIElement node = nodes.get(0);
assertNotNull(node);
assertTrue(node instanceof HTMLServer);
assertEquals("HTML", node.getPlatform());
assertFalse(node.isUsed());
nodes = guiModel.getChildren(node);
assertNotNull(nodes);
assertEquals(1, nodes.size());
// get document node
node = nodes.get(0);
assertNotNull(node);
assertTrue(node instanceof HTMLDocument);
assertEquals("HTML", node.getPlatform());
assertFalse(node.isUsed());
nodes = guiModel.getChildren(node);
assertNotNull(nodes);
assertEquals(1, nodes.size());
// get html node
node = nodes.get(0);
assertNotNull(node);
assertTrue(node instanceof HTMLPageElement);
assertEquals("HTML", node.getPlatform());
assertFalse(node.isUsed());
nodes = guiModel.getChildren(node);
assertNotNull(nodes);
assertEquals(1, nodes.size()); // only one child as the head tag should have been ignored
// get body node
node = nodes.get(0);
assertNotNull(node);
assertTrue(node instanceof HTMLPageElement);
assertEquals("HTML", node.getPlatform());
assertTrue(node.isUsed());
nodes = guiModel.getChildren(node);
assertNotNull(nodes);
assertEquals(0, nodes.size());
// check the sequences
Collection> sequences = parser.getSequences();
assertNotNull(sequences);
assertEquals(numberOfSessions, sequences.size());
Iterator> iterator = sequences.iterator();
while (iterator.hasNext()) {
List sequence = iterator.next();
assertNotNull(sequence);
assertEquals(1, sequence.size());
assertEvent(sequence.get(0), 12345, MouseClick.class, node, 194, 7);
}
}
/**
*
* TODO: comment
*
*
* @param message
*/
private void sendMessageAndAssertResponse(String message) throws Exception {
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("http://localhost:" + PORT + "/");
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));
}
finally {
httpPost.releaseConnection();
}
}
/**
*
*/
private void assertEvent(Event event,
int timestamp,
Class extends IEventType> eventType,
IGUIElement eventTarget,
int xCoordinate,
int yCoordinate)
{
assertEquals(timestamp, event.getTimestamp());
assertTrue(eventType.isInstance(event.getType()));
assertEquals(eventTarget, event.getTarget());
if (event.getType() instanceof MouseButtonInteraction) {
assertEquals(xCoordinate, ((MouseButtonInteraction) event.getType()).getX());
assertEquals(yCoordinate, ((MouseButtonInteraction) event.getType()).getY());
}
}
/**
*
* TODO: comment
*
*
* @param file
*/
private void deleteFiles(File file) {
if (file.exists()) {
if (file.isDirectory()) {
for (File child : file.listFiles()) {
deleteFiles(child);
}
}
try {
file.delete();
}
catch (Exception e) {
// ignore and delete as much as possible
}
}
}
}