/*
 * Copyright (c) 2012 Mateusz Parzonka, Eric Bodden
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Mateusz Parzonka - initial API and implementation
 */
package prm4j.indexing.realtime;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

import prm4j.AbstractTest;
import prm4j.api.fsm.FSM;
import prm4j.api.fsm.FSMSpec;
import prm4j.indexing.BaseMonitor;
import prm4j.indexing.StatefulMonitor;
import prm4j.indexing.staticdata.StaticDataConverter;
import prm4j.spec.FiniteParametricProperty;
import prm4j.spec.FiniteSpec;

public class GarbageCollectionTest extends AbstractTest {

    protected StaticDataConverter converter;
    protected DefaultBindingStore bindingStore;
    protected DefaultNodeStore nodeStore;
    protected BaseMonitor prototypeMonitor;
    protected DefaultParametricMonitor pm;

    public void createDefaultParametricMonitorWithAwareComponents(FSM fsm, int cleaningInterval) {
	FiniteSpec finiteSpec = new FSMSpec(fsm);
	converter = new StaticDataConverter(new FiniteParametricProperty(finiteSpec));
	bindingStore = new DefaultBindingStore(finiteSpec.getFullParameterSet(), 1);
	nodeStore = new DefaultNodeStore(converter.getMetaTree());
	prototypeMonitor = new StatefulMonitor(finiteSpec.getInitialState());
	pm = new DefaultParametricMonitor(bindingStore, nodeStore, prototypeMonitor, converter.getEventContext());
    }

    @Test
    public void aliveBinding_nodeIsPersistent() throws Exception {

	FSM_obj_obj fsm = new FSM_obj_obj();
	createDefaultParametricMonitorWithAwareComponents(fsm.fsm, 1);

	// exercise
	Object object = new Object();
	LowLevelBinding[] bindings = bindingStore.getBindings(array(object));
	Node node = nodeStore.getOrCreateNode(bindings);

	// verify
	assertEquals(node, nodeStore.getNode(bindings));
    }

    @Test
    public void aliveBinding_oneNodeIsStoredInRootnode() throws Exception {

	FSM_obj_obj fsm = new FSM_obj_obj();
	createDefaultParametricMonitorWithAwareComponents(fsm.fsm, 1);

	// exercise
	Object object = new Object();
	LowLevelBinding[] bindings = bindingStore.getBindings(array(object));
	nodeStore.getOrCreateNode(bindings);

	// verify
	assertEquals(1, nodeStore.getRootNode().size());
    }

    @Test
    public void aliveBinding_bindingIsPersistent() throws Exception {

	FSM_obj_obj fsm = new FSM_obj_obj();
	createDefaultParametricMonitorWithAwareComponents(fsm.fsm, 1);

	// exercise
	Object object = new Object();
	LowLevelBinding[] bindings = bindingStore.getBindings(array(object));
	nodeStore.getOrCreateNode(bindings);

	// verify
	assertEquals(1, bindingStore.size());
    }

    @Test
    public void expiredBinding_bindingIsRemovedFromBindingStore() throws Exception {

	FSM_obj_obj fsm = new FSM_obj_obj();
	createDefaultParametricMonitorWithAwareComponents(fsm.fsm, 1);

	// exercise
	Object object = new Object();
	LowLevelBinding[] bindings = bindingStore.getBindings(array(object));
	nodeStore.getOrCreateNode(bindings);
	object = null;
	runGarbageCollectorAFewTimes();
	bindingStore.getBindings(array(new Object())); // object is collected, second object is added

	// verify
	assertEquals(1, bindingStore.size()); // second object persists
    }

    @Test
    public void expiredBinding_bindingIsRemovedFromNodeStore() throws Exception {

	FSM_obj_obj fsm = new FSM_obj_obj();
	createDefaultParametricMonitorWithAwareComponents(fsm.fsm, 1);

	// exercise
	Object object = new Object();
	LowLevelBinding[] bindings = bindingStore.getBindings(array(object));
	nodeStore.getOrCreateNode(bindings);
	assertEquals(1, nodeStore.getRootNode().size()); // node is stored

	object = null;
	runGarbageCollectorAFewTimes();
	bindingStore.getBindings(array(new Object())); // object is collected, second object is added

	// verify
	assertEquals(0, nodeStore.getRootNode().size()); // node was removed
    }

}

@@ -1339,17 +1339,32 @@
 rSet(), 
-1
+cleaningInterval
 );%0A%09node
@@ -4057,10 +4057,1259 @@
 %0A    %7D%0A%0A
+    @Test%0A    public void fiveExpiredBindings_bindingsAreRemovedFromNodeStore() throws Exception %7B%0A%0A%09FSM_obj_obj fsm = new FSM_obj_obj();%0A%09createDefaultParametricMonitorWithAwareComponents(fsm.fsm, 5);%0A%0A%09// exercise%0A%09nodeStore.getOrCreateNode(bindingStore.getBindings(array(new Object())));%0A%09assertEquals(1, nodeStore.getRootNode().size()); // node is stored%0A%0A%09nodeStore.getOrCreateNode(bindingStore.getBindings(array(new Object())));%0A%09assertEquals(2, nodeStore.getRootNode().size()); // node is stored%0A%0A%09nodeStore.getOrCreateNode(bindingStore.getBindings(array(new Object())));%0A%09assertEquals(3, nodeStore.getRootNode().size()); // node is stored%0A%09runGarbageCollectorAFewTimes(); // should do nothing%0A%0A%09nodeStore.getOrCreateNode(bindingStore.getBindings(array(new Object())));%0A%09assertEquals(4, nodeStore.getRootNode().size()); // node is stored%0A%0A%09nodeStore.getOrCreateNode(bindingStore.getBindings(array(new Object())));%0A%09assertEquals(5, nodeStore.getRootNode().size()); // node is stored%0A%09runGarbageCollectorAFewTimes();%0A%0A%09nodeStore.getOrCreateNode(bindingStore.getBindings(array(new Object()))); // triggers cleanup in nodeStore%0A%0A%09// verify%0A%09assertEquals(1, nodeStore.getRootNode().size()); // first 5 nodes are removed, last node persists%0A    %7D%0A%0A
 %7D%0A

Add test ensuring node cleanup; Fix bug in test setup