//   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.tasktrees.treeimpl;

import static org.junit.Assert.*;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.junit.Test;

import de.ugoe.cs.autoquest.eventcore.Event;
import de.ugoe.cs.autoquest.eventcore.IEventTarget;
import de.ugoe.cs.autoquest.eventcore.IEventType;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IIterationInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptional;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptionalInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelectionInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequenceInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskBuilder;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskFactory;
import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInfo;
import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskInfo;
import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskBuilder;
import de.ugoe.cs.autoquest.tasktrees.treeimpl.TaskFactory;
import de.ugoe.cs.autoquest.test.DummyGUIElement;
import de.ugoe.cs.autoquest.test.DummyInteraction;

/**
 *
 */
public class TaskModelTest {
    
    /** */
    private static final int MAX_TREE_DEPTH = 10;

    /** */
    private ITaskBuilder taskBuilder = new TaskBuilder();

    /** */
    private ITaskFactory taskFactory = new TaskFactory();

    /**
     *
     */
    @Test
    public void test_EventTask_01() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task = createNewEventTask(eventType, eventTarget);
        
        assertNotNull(task);
        assertNotNull(task.getId());
        assertTrue(task.equals(task));
        
        IEventTaskInstance instance = (IEventTaskInstance) task.getInstances().iterator().next();
        assertEquals(eventType, instance.getEvent().getType());
        assertEquals(eventTarget, instance.getEvent().getTarget());
    }

    /**
     *
     */
    @Test
    public void test_EventTask_02() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task1 = createNewEventTask(eventType, eventTarget);
        IEventTask task2 = createNewEventTask(eventType, eventTarget);
        
        // the tasks will not be equal as they should have a different id
        assertFalse(task1.equals(task2));
    }

    /**
     *
     */
    @Test
    public void test_EventTask_03() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task = createNewEventTask(eventType, eventTarget);
        ITaskInstance taskInstance1 = createNewTaskInstance(task);
        ITaskInstance taskInstance2 = createNewTaskInstance(task);
        
        assertFalse(taskInstance1.equals(taskInstance2));
    }

    /**
     *
     */
    @Test
    public void test_Sequence_01() throws Exception {
        ISequence task = taskFactory.createNewSequence();
        
        assertNotNull(task);
        assertNotNull(task.getId());
        assertNotNull(task.getChildren());
        assertEquals(0, task.getChildren().size());
        assertTrue(task.equals(task));
    }

    /**
     *
     */
    @Test
    public void test_Sequence_02() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask child = createNewEventTask(eventType, eventTarget);

        ISequence task = taskFactory.createNewSequence();
        
        taskBuilder.addChild(task, child);
        
        assertNotNull(task.getChildren());
        assertEquals(1, task.getChildren().size());
        assertEquals(child, task.getChildren().get(0));
    }

    /**
     *
     */
    @Test
    public void test_Sequence_03() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask child1 = createNewEventTask(eventType, eventTarget);
        IEventTask child2 = createNewEventTask(eventType, eventTarget);
        IEventTask child3 = createNewEventTask(eventType, eventTarget);
        IEventTask child4 = createNewEventTask(eventType, eventTarget);
        IEventTask child5 = createNewEventTask(eventType, eventTarget);

        ISequence task = taskFactory.createNewSequence();
        
        taskBuilder.addChild(task, child1);
        taskBuilder.addChild(task, child2);
        taskBuilder.addChild(task, child3);
        taskBuilder.addChild(task, child4);
        taskBuilder.addChild(task, child5);
        
        assertNotNull(task.getChildren());
        assertEquals(5, task.getChildren().size());
        assertEquals(child1, task.getChildren().get(0));
        assertEquals(child2, task.getChildren().get(1));
        assertEquals(child3, task.getChildren().get(2));
        assertEquals(child4, task.getChildren().get(3));
        assertEquals(child5, task.getChildren().get(4));
    }

    /**
     *
     */
    @Test
    public void test_Selection_01() throws Exception {
        ISelection task = taskFactory.createNewSelection();
        
        assertNotNull(task);
        assertNotNull(task.getId());
        assertNotNull(task.getChildren());
        assertEquals(0, task.getChildren().size());
        assertTrue(task.equals(task));
    }

    /**
     *
     */
    @Test
    public void test_Selection_02() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask child = createNewEventTask(eventType, eventTarget);

        ISelection task = taskFactory.createNewSelection();
        
        taskBuilder.addChild(task, child);
        
        assertNotNull(task.getChildren());
        assertEquals(1, task.getChildren().size());
        assertEquals(child, task.getChildren().get(0));
    }

    /**
     *
     */
    @Test
    public void test_Selection_03() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask child1 = createNewEventTask(eventType, eventTarget);
        IEventTask child2 = createNewEventTask(eventType, eventTarget);
        IEventTask child3 = createNewEventTask(eventType, eventTarget);
        IEventTask child4 = createNewEventTask(eventType, eventTarget);
        IEventTask child5 = createNewEventTask(eventType, eventTarget);

        ISelection task = taskFactory.createNewSelection();
        
        taskBuilder.addChild(task, child1);
        taskBuilder.addChild(task, child2);
        taskBuilder.addChild(task, child3);
        taskBuilder.addChild(task, child4);
        taskBuilder.addChild(task, child5);
        
        assertNotNull(task.getChildren());
        assertEquals(5, task.getChildren().size());
        assertEquals(child1, task.getChildren().get(0));
        assertEquals(child2, task.getChildren().get(1));
        assertEquals(child3, task.getChildren().get(2));
        assertEquals(child4, task.getChildren().get(3));
        assertEquals(child5, task.getChildren().get(4));
    }

    /**
     *
     */
    @Test
    public void test_Iteration_01() throws Exception {
        IIteration task = taskFactory.createNewIteration();
        
        assertNotNull(task);
        assertNotNull(task.getId());
        assertNull(task.getMarkedTask());
        assertTrue(task.equals(task));
    }

    /**
     *
     */
    @Test
    public void test_Iteration_02() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask child = createNewEventTask(eventType, eventTarget);

        IIteration task = taskFactory.createNewIteration();
        
        taskBuilder.setMarkedTask(task, child);
        
        assertEquals(child, task.getMarkedTask());
    }

    /**
     *
     */
    @Test
    public void test_Iteration_03() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask child1 = createNewEventTask(eventType, eventTarget);
        IEventTask child2 = createNewEventTask(eventType, eventTarget);

        IIteration task = taskFactory.createNewIteration();
        
        taskBuilder.setMarkedTask(task, child1);
        taskBuilder.setMarkedTask(task, child2);
        
        assertEquals(child2, task.getMarkedTask());
    }

    /**
     *
     */
    @Test
    public void test_Optional_01() throws Exception {
        IOptional task = taskFactory.createNewOptional();
        
        assertNotNull(task);
        assertNotNull(task.getId());
        assertNull(task.getMarkedTask());
        assertTrue(task.equals(task));
    }

    /**
     *
     */
    @Test
    public void test_Optional_02() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask child = createNewEventTask(eventType, eventTarget);

        IOptional task = taskFactory.createNewOptional();
        
        taskBuilder.setMarkedTask(task, child);
        
        assertEquals(child, task.getMarkedTask());
    }

    /**
     *
     */
    @Test
    public void test_Optional_03() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask child1 = createNewEventTask(eventType, eventTarget);
        IEventTask child2 = createNewEventTask(eventType, eventTarget);

        IOptional task = taskFactory.createNewOptional();
        
        taskBuilder.setMarkedTask(task, child1);
        taskBuilder.setMarkedTask(task, child2);
        
        assertEquals(child2, task.getMarkedTask());
    }

    /**
     *
     */
    @Test
    public void test_EventTaskInstance_01() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task = createNewEventTask(eventType, eventTarget);
        
        IEventTaskInstance taskInstance = createNewTaskInstance(task);
        
        assertNotNull(taskInstance);
        assertEquals(task, taskInstance.getTask());
        assertTrue(taskInstance.equals(taskInstance));
        assertFalse(taskInstance.equals(task));
    }

    /**
     *
     */
    @Test
    public void test_EventTaskInstance_02() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task = createNewEventTask(eventType, eventTarget);
        
        ITaskInstance taskInstance1 = createNewTaskInstance(task);
        ITaskInstance taskInstance2 = createNewTaskInstance(task);
        
        assertFalse(taskInstance1.equals(taskInstance2));
    }

    /**
     *
     */
    @Test()
    public void test_SequenceInstance_01() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task = createNewEventTask(eventType, eventTarget);
        
        ISequence sequence = taskFactory.createNewSequence();
        
        ITaskInstance taskInstance = createNewTaskInstance(task);
        ISequenceInstance sequenceInstance = createNewTaskInstance(sequence);
        
        taskBuilder.addChild(sequenceInstance, taskInstance);
    }

    /**
     *
     */
    @Test
    public void test_SequenceInstance_02() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task = createNewEventTask(eventType, eventTarget);
        
        ISequence sequence = taskFactory.createNewSequence();
        taskBuilder.addChild(sequence, task);
        
        ITaskInstance taskInstance = createNewTaskInstance(task);
        ISequenceInstance sequenceInstance = createNewTaskInstance(sequence);
        
        taskBuilder.addChild(sequenceInstance, taskInstance);
        
        assertEquals(1, sequenceInstance.size());
        assertEquals(taskInstance, sequenceInstance.get(0));
    }

    /**
     *
     */
    @Test
    public void test_SequenceInstance_03() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task1 = createNewEventTask(eventType, eventTarget);
        IEventTask task2 = createNewEventTask(eventType, eventTarget);
        IEventTask task3 = createNewEventTask(eventType, eventTarget);
        IEventTask task4 = createNewEventTask(eventType, eventTarget);
        IEventTask task5 = createNewEventTask(eventType, eventTarget);
        
        ISequence sequence = taskFactory.createNewSequence();
        taskBuilder.addChild(sequence, task1);
        taskBuilder.addChild(sequence, task2);
        taskBuilder.addChild(sequence, task3);
        taskBuilder.addChild(sequence, task4);
        taskBuilder.addChild(sequence, task5);
        
        ITaskInstance taskInstance1 = createNewTaskInstance(task1);
        ITaskInstance taskInstance2 = createNewTaskInstance(task2);
        ITaskInstance taskInstance3 = createNewTaskInstance(task3);
        ITaskInstance taskInstance4 = createNewTaskInstance(task4);
        ITaskInstance taskInstance5 = createNewTaskInstance(task5);
        ISequenceInstance sequenceInstance = createNewTaskInstance(sequence);
        
        taskBuilder.addChild(sequenceInstance, taskInstance1);
        taskBuilder.addChild(sequenceInstance, taskInstance2);
        taskBuilder.addChild(sequenceInstance, taskInstance3);
        taskBuilder.addChild(sequenceInstance, taskInstance4);
        taskBuilder.addChild(sequenceInstance, taskInstance5);
        
        assertEquals(5, sequenceInstance.size());
        assertEquals(taskInstance1, sequenceInstance.get(0));
        assertEquals(taskInstance2, sequenceInstance.get(1));
        assertEquals(taskInstance3, sequenceInstance.get(2));
        assertEquals(taskInstance4, sequenceInstance.get(3));
        assertEquals(taskInstance5, sequenceInstance.get(4));
    }

    /**
     *
     */
    @Test(expected=IllegalArgumentException.class)
    public void test_SelectionInstance_01() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task = createNewEventTask(eventType, eventTarget);
        
        ISelection selection = taskFactory.createNewSelection();
        
        ITaskInstance taskInstance = createNewTaskInstance(task);
        ISelectionInstance selectionInstance = createNewTaskInstance(selection);
        
        taskBuilder.setChild(selectionInstance, taskInstance);
    }

    /**
     *
     */
    @Test
    public void test_SelectionInstance_02() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task = createNewEventTask(eventType, eventTarget);
        
        ISelection selection = taskFactory.createNewSelection();
        taskBuilder.addChild(selection, task);
        
        ITaskInstance taskInstance = createNewTaskInstance(task);
        ISelectionInstance selectionInstance = createNewTaskInstance(selection);
        
        taskBuilder.setChild(selectionInstance, taskInstance);
        
        assertNotNull(selectionInstance.getChild());
        assertEquals(taskInstance, selectionInstance.getChild());
    }

    /**
     *
     */
    @Test
    public void test_SelectionInstance_03() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task1 = createNewEventTask(eventType, eventTarget);
        IEventTask task2 = createNewEventTask(eventType, eventTarget);
        IEventTask task3 = createNewEventTask(eventType, eventTarget);
        IEventTask task4 = createNewEventTask(eventType, eventTarget);
        IEventTask task5 = createNewEventTask(eventType, eventTarget);
        
        ISelection selection = taskFactory.createNewSelection();
        taskBuilder.addChild(selection, task1);
        taskBuilder.addChild(selection, task2);
        taskBuilder.addChild(selection, task3);
        taskBuilder.addChild(selection, task4);
        taskBuilder.addChild(selection, task5);
        
        ITaskInstance taskInstance1 = createNewTaskInstance(task1);
        ITaskInstance taskInstance2 = createNewTaskInstance(task2);
        ISelectionInstance selectionInstance = createNewTaskInstance(selection);
        
        taskBuilder.setChild(selectionInstance, taskInstance1);
        taskBuilder.setChild(selectionInstance, taskInstance2);
        
        assertEquals(taskInstance2, selectionInstance.getChild());
    }

    /**
     *
     */
    @Test()
    public void test_IterationInstance_01() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task = createNewEventTask(eventType, eventTarget);
        
        IIteration iteration = taskFactory.createNewIteration();
        
        ITaskInstance taskInstance = createNewTaskInstance(task);
        IIterationInstance iterationInstance = createNewTaskInstance(iteration);
        
        taskBuilder.addChild(iterationInstance, taskInstance);
    }

    /**
     *
     */
    @Test
    public void test_IterationInstance_02() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task = createNewEventTask(eventType, eventTarget);
        
        IIteration iteration = taskFactory.createNewIteration();
        taskBuilder.setMarkedTask(iteration, task);
        
        ITaskInstance taskInstance = createNewTaskInstance(task);
        IIterationInstance iterationInstance = createNewTaskInstance(iteration);
        
        taskBuilder.addChild(iterationInstance, taskInstance);
        
        assertEquals(1, iterationInstance.size());
        assertEquals(taskInstance, iterationInstance.get(0));
    }

    /**
     *
     */
    @Test(expected=IllegalArgumentException.class)
    public void test_IterationInstance_03() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task1 = createNewEventTask(eventType, eventTarget);
        IEventTask task2 = createNewEventTask(eventType, eventTarget);
        
        IIteration iteration = taskFactory.createNewIteration();
        taskBuilder.setMarkedTask(iteration, task1);
        taskBuilder.setMarkedTask(iteration, task2);
        
        ITaskInstance taskInstance1 = createNewTaskInstance(task1);
        IIterationInstance iterationInstance = createNewTaskInstance(iteration);
        
        taskBuilder.addChild(iterationInstance, taskInstance1);
    }

    /**
     *
     */
    @Test
    public void test_IterationInstance_04() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task1 = createNewEventTask(eventType, eventTarget);
        IEventTask task2 = createNewEventTask(eventType, eventTarget);
        
        IIteration iteration = taskFactory.createNewIteration();
        taskBuilder.setMarkedTask(iteration, task1);
        taskBuilder.setMarkedTask(iteration, task2);
        
        ITaskInstance taskInstance2 = createNewTaskInstance(task2);
        IIterationInstance iterationInstance = createNewTaskInstance(iteration);
        
        taskBuilder.addChild(iterationInstance, taskInstance2);
        
        assertEquals(1, iterationInstance.size());
        assertEquals(taskInstance2, iterationInstance.get(0));
    }

    /**
     *
     */
    @Test()
    public void test_OptionalInstance_01() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task = createNewEventTask(eventType, eventTarget);
        
        IOptional optional = taskFactory.createNewOptional();
        
        ITaskInstance taskInstance = createNewTaskInstance(task);
        IOptionalInstance optionalInstance = createNewTaskInstance(optional);
        
        taskBuilder.setChild(optionalInstance, taskInstance);
    }

    /**
     *
     */
    @Test
    public void test_OptionalInstance_02() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task = createNewEventTask(eventType, eventTarget);
        
        IOptional optional = taskFactory.createNewOptional();
        taskBuilder.setMarkedTask(optional, task);
        
        ITaskInstance taskInstance = createNewTaskInstance(task);
        IOptionalInstance optionalInstance = createNewTaskInstance(optional);
        
        taskBuilder.setChild(optionalInstance, taskInstance);
        
        assertNotNull(optionalInstance.getChild());
        assertEquals(taskInstance, optionalInstance.getChild());
    }

    /**
     *
     */
    @Test(expected=IllegalArgumentException.class)
    public void test_OptionalInstance_03() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task1 = createNewEventTask(eventType, eventTarget);
        IEventTask task2 = createNewEventTask(eventType, eventTarget);
        
        IOptional optional = taskFactory.createNewOptional();
        taskBuilder.setMarkedTask(optional, task1);
        taskBuilder.setMarkedTask(optional, task2);
        
        ITaskInstance taskInstance1 = createNewTaskInstance(task1);
        IOptionalInstance optionalInstance = createNewTaskInstance(optional);
        
        taskBuilder.setChild(optionalInstance, taskInstance1);
    }

    /**
     *
     */
    @Test
    public void test_OptionalInstance_04() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task1 = createNewEventTask(eventType, eventTarget);
        IEventTask task2 = createNewEventTask(eventType, eventTarget);
        
        IOptional optional = taskFactory.createNewOptional();
        taskBuilder.setMarkedTask(optional, task1);
        taskBuilder.setMarkedTask(optional, task2);
        
        ITaskInstance taskInstance2 = createNewTaskInstance(task2);
        IOptionalInstance optionalInstance = createNewTaskInstance(optional);
        
        taskBuilder.setChild(optionalInstance, taskInstance2);
        
        assertNotNull(optionalInstance.getChild());
        assertEquals(taskInstance2, optionalInstance.getChild());
    }

    /**
     *
     */
    @Test
    public void test_UserSession_01() throws Exception {
        IUserSession userSession = taskFactory.createUserSession();
        
        assertNotNull(userSession);
        assertEquals(0, userSession.size());
    }

    /**
     *
     */
    @Test
    public void test_UserSession_02() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task = createNewEventTask(eventType, eventTarget);
        
        IUserSession userSession = taskFactory.createUserSession();
        
        ITaskInstance taskInstance = createNewTaskInstance(task);
        
        taskBuilder.addExecutedTask(userSession, taskInstance);
        
        assertEquals(1, userSession.size());
        assertEquals(taskInstance, userSession.get(0));
    }

    /**
     *
     */
    @Test
    public void test_UserSession_03() throws Exception {
        IEventType eventType = new DummyInteraction("interaction", 1);
        IEventTarget eventTarget = new DummyGUIElement("elem");
        
        IEventTask task = createNewEventTask(eventType, eventTarget);
        ISequence sequence = taskFactory.createNewSequence();
        ISelection selection = taskFactory.createNewSelection();
        IIteration iteration = taskFactory.createNewIteration();
        IOptional optional = taskFactory.createNewOptional();
        
        taskBuilder.addChild(sequence, task);
        taskBuilder.addChild(selection, task);
        taskBuilder.setMarkedTask(iteration, task);
        taskBuilder.setMarkedTask(optional, task);
        
        ITaskInstance taskInstance = createNewTaskInstance(task);
        ISequenceInstance sequenceInstance = createNewTaskInstance(sequence);
        ISelectionInstance selectionInstance = createNewTaskInstance(selection);
        IIterationInstance iterationInstance = createNewTaskInstance(iteration);
        IOptionalInstance optionalInstance = createNewTaskInstance(optional);
        
        taskBuilder.addChild(sequenceInstance, createNewTaskInstance(task));
        taskBuilder.setChild(selectionInstance, createNewTaskInstance(task));
        taskBuilder.addChild(iterationInstance, createNewTaskInstance(task));
        taskBuilder.setChild(optionalInstance, createNewTaskInstance(task));
        
        IUserSession userSession = taskFactory.createUserSession();
        
        taskBuilder.addExecutedTask(userSession, taskInstance);
        taskBuilder.addExecutedTask(userSession, sequenceInstance);
        taskBuilder.addExecutedTask(userSession, selectionInstance);
        taskBuilder.addExecutedTask(userSession, iterationInstance);
        taskBuilder.addExecutedTask(userSession, optionalInstance);
        
        assertEquals(5, userSession.size());
        assertEquals(taskInstance, userSession.get(0));
        assertEquals(sequenceInstance, userSession.get(1));
        assertEquals(selectionInstance, userSession.get(2));
        assertEquals(iterationInstance, userSession.get(3));
        assertEquals(optionalInstance, userSession.get(4));
    }
    
    /**
     *
     */
    @Test
    public void testRandomTrees() throws Exception {
        int noOfTrees = 10;
        int noOfMaxChildren = 8;
        int maxDepth = MAX_TREE_DEPTH;

        for (int i = 0; i < noOfTrees; i++) {
            System.err.println("\niteration " + (i + 1) + ":");
            System.err.println("  creating tasks");
            Map<ITask, ITaskInfo> expectedTaskInfos = new HashMap<ITask, ITaskInfo>();
            ITask task = createTaskTree(noOfMaxChildren, maxDepth, expectedTaskInfos);
            if (!(task instanceof ISequence)) {
                ISequence sequence = taskFactory.createNewSequence();
                taskBuilder.addChild(sequence, task);
                task = sequence;
            }
            else {
                expectedTaskInfos.remove(task);
            }
            
            //new TaskTreeEncoder().encode(task, System.err);

            ISequenceInstance sequenceInstance =
                (ISequenceInstance) instantiateTask(task, noOfMaxChildren);
            
            System.err.println("  creating user session");
            
            IUserSession session = taskFactory.createUserSession();
            
            for (ITaskInstance child : sequenceInstance) {
                taskBuilder.addExecutedTask(session, child);
            }
            
            List<IUserSession> sessions = new LinkedList<IUserSession>();
            sessions.add(session);
            
            //new TaskTreeEncoder().encode(session, System.err);
            
            ITaskModel taskModel = taskFactory.createTaskModel(sessions);

            System.err.println("  validating task tree");
            Map<ITask, ITaskInfo> actualTaskInfos = new HashMap<ITask, ITaskInfo>();
            
            for (ITask currentTask : taskModel.getTasks()) {
                actualTaskInfos.put(currentTask, taskModel.getTaskInfo(currentTask));
            }
            
            assertMapsEqual(expectedTaskInfos, actualTaskInfos);
        }
    }

    /**
     *
     */
    private void assertMapsEqual(Map<ITask, ITaskInfo> map1,
                                 Map<ITask, ITaskInfo> map2)
    {
        try {
            if (map1 == null) {
                assertNull(map2);
                return;
            }

            assertEquals(map1.size(), map2.size());

            for (Map.Entry<ITask, ITaskInfo> entry : map1.entrySet()) {
                ITaskInfo value2 = map2.get(entry.getKey());
                assertNotNull(value2);
                assertEquals(entry.getValue().getTask(), value2.getTask());
                //assertEquals(entry.getValue().getNoOfOccurencesInTree(),
                //             value2.getNoOfOccurencesInTree());
            }
        }
        catch (AssertionError e) {
            dumpMap(map1);
            dumpMap(map2);
            
            if (map1.size() > map2.size()) {
                for (ITask task : map2.keySet()) {
                    map1.remove(task);
                }
                
                System.err.println("difference: ");
                dumpMap(map1);
            }
            else if (map2.size() > map1.size()) {
                for (ITask task : map1.keySet()) {
                    map2.remove(task);
                }
                
                System.err.println("difference: ");
                dumpMap(map2);
            }
            
            throw e;
        }
    }

    /**
     *
     */
    private void dumpMap(Map<ITask, ITaskInfo> map) {
        System.err.println();

        if (map == null) {
            System.err.println("map is null");
        }
        else {
            System.err.println("map:");
            for (Map.Entry<ITask, ITaskInfo> entry : map.entrySet()) {
                int lineLength = 2;
                System.err.print("  ");
                System.err.print(entry.getKey());
                lineLength += entry.getKey().toString().length();
                for (int i = lineLength; i < 70; i++) {
                    System.err.print(" ");
                }
                lineLength = 60;
                System.err.print(" : ");
                lineLength += 3;
                System.err.print(entry.getValue());
                lineLength += entry.getValue().toString().length();
                for (int i = lineLength; i < 140; i++) {
                    System.err.print(" ");
                }
                System.err.print(" : ");
                System.err.println(entry.getKey().getDescription());
            }
        }

        System.err.println();
    }

    /**
     *
     */
    private ITask createTaskTree(int                   maxNoOfChildren,
                                 int                   maxDepth,
                                 Map<ITask, ITaskInfo> taskInfos)
        throws Exception
    {

        ITask task;

        // integrating the maximum depth here assures, that either something between 0 and 8 will
        // be the type, or if the max depth decreases near 0 only event tasks will be created
        // to finish the tree creation
        int type = randomize(Math.min(10, maxDepth));

        switch (type)
        {
            case 0: {
                // System.err.print("creating new event task ");
                task = createNewEventTask(taskInfos);
                break;
            }
            case 1: {
                // System.err.print("reusing event task ");
                task = reuseEventTask(taskInfos);
                break;
            }
            case 2: {
                // System.err.println("creating new sequence {");
                task = createNewSequence(maxNoOfChildren, maxDepth, taskInfos);
                break;
            }
            case 3: {
                // System.err.println("reusing sequence {");
                task = reuseSequence(maxNoOfChildren, maxDepth, taskInfos);
                break;
            }
            case 4: {
                // System.err.println("creating new selection {");
                task = createNewSelection(maxNoOfChildren, maxDepth, taskInfos);
                break;
            }
            case 5: {
                // System.err.println("reusing selection {");
                task = reuseSelection(maxNoOfChildren, maxDepth, taskInfos);
                break;
            }
            case 6: {
                // System.err.println("creating new iteration {");
                task = createNewIteration(maxNoOfChildren, maxDepth, taskInfos);
                break;
            }
            case 7: {
                // System.err.println("reusing iteration {");
                task = reuseIteration(maxNoOfChildren, maxDepth, taskInfos);
                break;
            }
            case 8: {
                // System.err.println("creating new optional {");
                task = createNewOptional(maxNoOfChildren, maxDepth, taskInfos);
                break;
            }
            case 9: {
                // System.err.println("reusing optional {");
                task = reuseOptional(maxNoOfChildren, maxDepth, taskInfos);
                break;
            }
            default: {
                // System.err.print("creating new event task per default ");
                task = createNewEventTask(taskInfos);
            }
        }

        return task;
    }

    /**
     *
     */
    private ITask createNewEventTask(Map<ITask, ITaskInfo> taskInfos)
        throws Exception
    {
        Thread.sleep(2);
        long id = System.currentTimeMillis();
        IEventTask task = createNewEventTask
            (new DummyInteraction("interaction" + id, 1), new DummyGUIElement("elem" + id));

        taskInfos.put(task, new TaskInfo(task));

        return task;
    }

    /**
     *
     */
    private ITask reuseEventTask(Map<ITask, ITaskInfo> taskInfos)
        throws Exception
    {
        ITask eventTask = reuseTask(taskInfos, IEventTask.class);
        
        if (eventTask == null) {
            eventTask = createNewEventTask(taskInfos);
        }

        return eventTask;
    }

    /**
     *
     */
    private ITask createNewSequence(int                   maxNoOfChildren,
                                    int                   maxDepth,
                                    Map<ITask, ITaskInfo> taskInfos)
        throws Exception
    {
        ISequence sequence = taskFactory.createNewSequence();

        // ensure at the minimum 2 children
        int noOfChildren = randomize(2, maxNoOfChildren);

        for (int i = 0; i < noOfChildren; i++) {
            ITask child = createTaskTree(maxNoOfChildren, maxDepth - 1, taskInfos);
            
            //((Task) child).setDescription(sequence.toString());
            taskBuilder.addChild(sequence, child);
        }

        taskInfos.put(sequence, new TaskInfo(sequence));
        return sequence;
    }

    /**
     *
     */
    private ITask reuseSequence(int                   maxNoOfChildren,
                                int                   maxDepth,
                                Map<ITask, ITaskInfo> taskInfos)
        throws Exception
    {
        ITask sequence = reuseTask(taskInfos, ISequence.class);
        
        if (sequence == null) {
            sequence = createNewSequence(maxNoOfChildren, maxDepth, taskInfos);
        }

        return sequence;
    }

    /**
     *
     */
    private ITask createNewSelection(int                   maxNoOfChildren,
                                     int                   maxDepth,
                                     Map<ITask, ITaskInfo> taskInfos)
        throws Exception
    {
        ISelection selection = taskFactory.createNewSelection();

        // ensure at the minimum 1 child
        int noOfChildren = randomize(1, maxNoOfChildren);
        
        Map<ITask, ITaskInfo> childrenTaskInfos = new HashMap<ITask, ITaskInfo>();
        for (int i = 0; i < noOfChildren; i++) {
            ITask child;
            do {
                childrenTaskInfos.clear();
                child = createTaskTree(maxNoOfChildren, maxDepth - 1, childrenTaskInfos);
            }
            while (child instanceof ISelection);
            
            //((Task) child).setDescription(selection.toString());
            taskBuilder.addChild(selection, child);
            taskInfos.putAll(childrenTaskInfos);
        }

        taskInfos.put(selection, new TaskInfo(selection));
        return selection;
    }

    /**
     *
     */
    private ITask reuseSelection(int                   maxNoOfChildren,
                                 int                   maxDepth,
                                 Map<ITask, ITaskInfo> taskInfos)
        throws Exception
    {
        ITask selection = reuseTask(taskInfos, ISelection.class);
        
        if (selection == null) {
            selection = createNewSelection(maxNoOfChildren, maxDepth, taskInfos);
        }

        return selection;
    }

    /**
     * 
     */
    private ITask createNewIteration(int                   maxNoOfChildren,
                                     int                   maxDepth,
                                     Map<ITask, ITaskInfo> taskInfos)
        throws Exception
    {
        IIteration iteration = taskFactory.createNewIteration();

        Map<ITask, ITaskInfo> childrenTaskInfos = new HashMap<ITask, ITaskInfo>();
        ITask child;
        do {
            childrenTaskInfos.clear();
            child = createTaskTree(maxNoOfChildren, maxDepth - 1, childrenTaskInfos);
        }
        while ((child instanceof IOptional) || (child instanceof IIteration));
        
        //((Task) child).setDescription(iteration.toString());
        taskBuilder.setMarkedTask(iteration, child);
        taskInfos.putAll(childrenTaskInfos);

        taskInfos.put(iteration, new TaskInfo(iteration));
        return iteration;
    }

    /**
     *
     */
    private ITask reuseIteration(int                   maxNoOfChildren,
                                 int                   maxDepth,
                                 Map<ITask, ITaskInfo> taskInfos)
        throws Exception
    {
        ITask iteration = reuseTask(taskInfos, IIteration.class);
        
        if (iteration == null) {
            iteration = createNewIteration(maxNoOfChildren, maxDepth, taskInfos);
        }

        return iteration;
    }

    /**
     * 
     */
    private ITask createNewOptional(int                   maxNoOfChildren,
                                    int                   maxDepth,
                                    Map<ITask, ITaskInfo> taskInfos)
        throws Exception
    {
        IOptional optional = taskFactory.createNewOptional();

        Map<ITask, ITaskInfo> childrenTaskInfos = new HashMap<ITask, ITaskInfo>();
        ITask child;
        do {
            childrenTaskInfos.clear();
            child = createTaskTree(maxNoOfChildren, maxDepth - 1, childrenTaskInfos);
        }
        while (child instanceof IOptional);
        
        //((Task) child).setDescription(optional.toString());
        taskBuilder.setMarkedTask(optional, child);
        taskInfos.putAll(childrenTaskInfos);

        taskInfos.put(optional, new TaskInfo(optional));
        return optional;
    }

    /**
     *
     */
    private ITask reuseOptional(int                   maxNoOfChildren,
                                int                   maxDepth,
                                Map<ITask, ITaskInfo> taskInfos)
        throws Exception
    {
        ITask optional = reuseTask(taskInfos, IOptional.class);
        
        if (optional == null) {
            optional = createNewOptional(maxNoOfChildren, maxDepth, taskInfos);
        }

        return optional;
    }

    /**
     *
     */
    private ITask reuseTask(Map<ITask, ITaskInfo> taskInfos, Class<? extends ITask> type)
        throws Exception
    {
        int noOfTasks = 0;

        for (Map.Entry<ITask, ITaskInfo> entry : taskInfos.entrySet()) {
            if (type.isInstance(entry.getKey())) {
                noOfTasks++;
            }
        }

        if (noOfTasks > 0) {
            noOfTasks = randomize(noOfTasks);

            for (Map.Entry<ITask, ITaskInfo> entry : taskInfos.entrySet()) {
                if (type.isInstance(entry.getKey())) {
                    if (--noOfTasks <= 0) {
                        return entry.getKey();
                    }
                }
            }
        }
        else {
            return null;
        }

        throw new RuntimeException("this is an implementation error");
    }

    /**
     *
     */
    private ITaskInstance instantiateTask(ITask task, int maxIterationCount) throws Exception {
        ITaskInstance instance = createNewTaskInstance(task);

        if (task instanceof ISequence) {
            for (ITask child : ((ISequence) task).getChildren()) {
                taskBuilder.addChild
                    ((ISequenceInstance) instance, instantiateTask(child, maxIterationCount));
            }
        }
        else if (task instanceof ISelection) {
            List<ITask> children = ((ISelection) task).getChildren();
            int index = randomize(children.size());
            taskBuilder.setChild((ISelectionInstance) instance,
                                 instantiateTask(children.get(index),maxIterationCount));
        }
        else if (task instanceof IIteration) {
            int count = randomize(maxIterationCount);
            ITask child = ((IIteration) task).getMarkedTask();
            
            for (int i = 0; i < count; i++) {
                taskBuilder.addChild
                    ((IIterationInstance) instance, instantiateTask(child, maxIterationCount));
            }
        }
        else if (task instanceof IOptional) {
            ITask child = ((IOptional) task).getMarkedTask();
            
            if (randomize(1) == 0) {
                taskBuilder.setChild((IOptionalInstance) instance,
                                     instantiateTask(child, maxIterationCount));
            }
        }
        
        return instance;
    }

    /**
     *
     */
    private int randomize(int max) throws Exception {
        return randomize(0, max);
    }
    
    /**
     *
     */
    private int randomize(int min, int max) throws Exception {
        if (min > max) {
            throw new IllegalArgumentException("min must always be smaller or equal than max");
        }
        
        int deviation = max - min;
        int value = (int) (Math.random() * deviation);
        
        return value + min;
    }
    
    /**
     *
     */
    protected IEventTask createNewEventTask(IEventType eventType, IEventTarget eventTarget) {
        IEventTask eventTask = taskFactory.createNewEventTask(eventType + " --> " + eventTarget);
        taskFactory.createNewTaskInstance(eventTask, new Event(eventType, eventTarget));
        return eventTask;
    }

    /**
     *
     */
    private ITaskInstance createNewTaskInstance(ITask task) {
        if (task instanceof IEventTask) {
            return createNewTaskInstance((IEventTask) task);
        }
        else if (task instanceof ISequence) {
            return createNewTaskInstance((ISequence) task);
        }
        else if (task instanceof ISelection) {
            return createNewTaskInstance((ISelection) task);
        }
        else if (task instanceof IIteration) {
            return createNewTaskInstance((IIteration) task);
        }
        else if (task instanceof IOptional) {
            return createNewTaskInstance((IOptional) task);
        }
        
        throw new IllegalArgumentException("unknown type of task");
    }

    /**
     *
     */
    private IEventTaskInstance createNewTaskInstance(IEventTask task) {
        IEventTaskInstance existingInstance =
            (IEventTaskInstance) task.getInstances().iterator().next();
        return taskFactory.createNewTaskInstance(task, existingInstance.getEvent());
    }

    /**
     *
     */
    private ISequenceInstance createNewTaskInstance(ISequence task) {
        return taskFactory.createNewTaskInstance(task);
    }

    /**
     *
     */
    private ISelectionInstance createNewTaskInstance(ISelection task) {
        return taskFactory.createNewTaskInstance(task);
    }

    /**
     *
     */
    private IIterationInstance createNewTaskInstance(IIteration task) {
        return taskFactory.createNewTaskInstance(task);
    }

    /**
     *
     */
    private IOptionalInstance createNewTaskInstance(IOptional task) {
        return taskFactory.createNewTaskInstance(task);
    }

}
