/*
* (C) Copyright 2005 Arnaud Bailly (arnaud.oqube@gmail.com),
* Yves Roos (yroos@lifl.fr) and others.
*
* 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 rationals.ioautomata.testing;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;
import rationals.ioautomata.IOAlphabetType;
import rationals.ioautomata.IOAutomaton;
import rationals.ioautomata.IOStateMachine;
import rationals.ioautomata.IOTransition;
import rationals.transformations.TransformationsToolBox;
/**
* A tester implementing the algorithm from Pyhala and Heljanko.
*
* @author nono
* @version $Id: PyhalaIOAutomataTester.java 2 2006-08-24 14:41:48Z oqube $
*/
public class PyhalaIOAutomataTester extends AbstractIOTester {
/* set of covered transitions */
private Set cover = new HashSet();
/* number of transitions in spec */
private int numtrans;
/* current coverage achieved */
private double coverage;
/* random number generator */
private Random rand = new Random();
// Algorithm parameters
private final int maxNbRun;
private final double minCoverage;
private final int resetInterval;
private double probGreedy;
private IOAutomaton spec;
private IOStateMachine impl;
protected final double probTerminate;
protected final double probReset;
protected final double probInput;
public PyhalaIOAutomataTester(int maxrun, double mincover,
int resetinterval, double probgreedy, double probTerminate,
double probReset, double probInput) {
this.maxNbRun = maxrun;
this.minCoverage = mincover;
this.resetInterval = resetinterval;
if (probgreedy < 0 || probgreedy > 1)
throw new IllegalArgumentException(
"Probability of selecting greedy move must be between 0.0 and 1.0");
this.probGreedy = probgreedy;
if (probTerminate + probReset + probInput >= 1)
throw new IllegalArgumentException(
"Sum of probabilities for random move cannot excess 1");
this.probTerminate = probTerminate;
this.probReset = probReset;
this.probInput = probInput;
}
/*
* (non-Javadoc)
*
* @see rationals.ioautomata.testing.IOAutomataTester#test(rationals.ioautomata.IOStateMachine,
* rationals.ioautomata.IOAutomaton)
*/
public void test(IOStateMachine impl, IOAutomaton spec)
throws IOTestFailure {
cover = new HashSet();
numtrans = spec.delta().size();
coverage = 0;
this.spec = spec;
this.impl = impl;
List trace = new ArrayList();
Set /* < List < Object > > */traces = new HashSet();
Set state = TransformationsToolBox
.epsilonClosure(spec.initials(), spec);
while (true) {
Move mv = testMove(trace, traces, state);
if (mv == Move.terminate) {
return;
} else if (mv == Move.reset) {
impl.reset();
traces.add(trace);
trace = new ArrayList();
state = TransformationsToolBox.epsilonClosure(spec.initials(),
spec);
continue;
} else if (mv == Move.out) {
/* get output from impl */
Object o = impl.output();
/* transform as output for spec */
IOTransition.IOLetter lt = new IOTransition.IOLetter(o,
IOAlphabetType.OUTPUT);
/* find next state */
Set next = spec.step(state, lt);
if (!next.isEmpty()) {
trace.add(lt);
state = next;
} else {
/* failure */
IOTestFailure fail = new IOTestFailure("unexpected output");
fail.setFailureTrace(trace);
throw fail;
}
} else {
/* send input to impl */
impl.input(mv.move);
IOTransition.IOLetter lt = new IOTransition.IOLetter(mv.move,
IOAlphabetType.INPUT);
state = spec.step(state, lt);
trace.add(lt);
}
}
}
/**
* @param state
* @return
*/
private Move randomTestMove(Set state) {
double rnd = rand.nextDouble();
if (rnd < probTerminate)
return Move.terminate;
else if (rnd < probReset + probTerminate)
return Move.reset;
else {
Set st = TransformationsToolBox.epsilonClosure(state, spec);
Set trs = spec.delta(st);
filterInputs(trs);
if (trs.size() == 0 || rnd > probReset + probTerminate + probInput)
return Move.out;
/* select an input randomly */
int n = rand.nextInt(trs.size());
IOTransition tr = null;
for (Iterator i = trs.iterator(); n >= 0; n--)
tr = (IOTransition) i.next();
return new Move(((IOTransition.IOLetter) tr.label()).label);
}
}
/**
* Implements HeuristicTestMove
*
* @param trace
* @param traces
* @param state
* @return
*/
private Move testMove(List trace, Set traces, Set state) {
if (traces.size() > maxNbRun || coverage > minCoverage)
return Move.terminate;
if (trace.size() > resetInterval)
return Move.reset;
if (rand.nextDouble() < probGreedy)
return greedyTestMove(state);
else
return randomTestMove(state);
}
/**
* Implementation of GreedyTestMove
*
* @param state
* @return
*/
private Move greedyTestMove(Set state) {
boolean inputUncovered = uncoveredInputs(state);
boolean outputUncovered = uncoveredOutputs(state);
int choice = 0;
/* select move according to coverage */
if (inputUncovered && outputUncovered) {
if (rand.nextDouble() < probReset + probTerminate + probInput) {
choice = 1; /* input */
} else
choice = 2; /* output */
} else if (inputUncovered && !outputUncovered)
choice = 1;
else if (!inputUncovered && outputUncovered)
choice = 2;
switch (choice) {
case 1: /* input */
return randomUncoveredInput(state);
case 2:
return Move.out;
default:
/* all transitions covered - try to find one by searching in graph */
return lookaheadTestMove(state);
}
}
/**
* Find an uncovered move.
*
* @param state
* @return
*/
private Move lookaheadTestMove(Set state) {
return null;
}
/**
* @param state
* @return
*/
private Move randomUncoveredInput(Set state) {
Set st = TransformationsToolBox.epsilonClosure(state, spec);
Set trs = spec.delta(st);
filterInputs(trs);
trs.removeAll(cover);
/* select an input randomly */
int n = rand.nextInt(trs.size());
IOTransition tr = null;
for (Iterator i = trs.iterator(); n >= 0; n--)
tr = (IOTransition) i.next();
return new Move(((IOTransition.IOLetter) tr.label()).label);
}
/**
* @param state
* @return
*/
private boolean uncoveredOutputs(Set state) {
Set trs = spec
.delta(TransformationsToolBox.epsilonClosure(state, spec));
filterOutputs(trs);
if (trs.isEmpty())
return false;
/* check there exists uncovered input transitions */
return !cover.containsAll(trs);
}
/**
* @param state
* @return
*/
private boolean uncoveredInputs(Set state) {
Set trs = spec
.delta(TransformationsToolBox.epsilonClosure(state, spec));
filterInputs(trs);
if (trs.isEmpty())
return false;
/* check there exists uncovered input transitions */
return !cover.containsAll(trs);
}
}