Package nz.ac.waikato.modeljunit

Source Code of nz.ac.waikato.modeljunit.Tester

/**
Copyright (C) 2007 Mark Utting
This file is part of the CZT project.

The CZT project contains free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

The CZT project is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with CZT; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package nz.ac.waikato.modeljunit;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Set;
import java.util.List;
import java.util.HashSet;
import java.util.Queue;
import java.util.LinkedList;
import java.util.Random;
import java.util.BitSet;

import nz.ac.waikato.modeljunit.GraphListener;
import nz.ac.waikato.modeljunit.coverage.CoverageMetric;
import nz.ac.waikato.modeljunit.timing.TimedFsmModel;
import nz.ac.waikato.modeljunit.timing.TimedModel;

/**
* An abstract superclass for all the test generation algorithms.
* Each subclass must implement a {@link #generate()} method that
* generates the next step in a test sequence.
* <p>
* Note that many test generation algorithms use randomness,
* so this class provides a setter and getter for a random number
* generator.  By default this is set to <code>new Random(FIXEDSEED)</code>,
* so that test generation is deterministic.
*
* @author marku
*
*/
public abstract class Tester
{
  public static final long FIXEDSEED = 123456789L;

  /**
   * The model from which tests will be generated.
   */
  protected Model model_;

  /**
   *  A Random number generator for use in test generation.
   */
  protected Random rand_ = new Random(FIXEDSEED);

  /**
   *  Create a test generator for the given model.
   * @param model  Must be non-null.
   */
  public Tester(Model model)
  {
    assert model != null;
    model_ = model;
  }

  /**
   * A convenience constructor that puts a Model wrapper around an FsmModel.
   * @param fsm  Must be non-null.
   */
  public Tester(FsmModel fsm)
  {
    if(fsm instanceof TimedFsmModel)
      model_ = new TimedModel((TimedFsmModel)fsm);
    else
      model_ = new Model(fsm);
  }
 


  /** The name of this test generation algorithm. */
  public abstract String getName();

  /** A brief description of this test generation algorithm. */
  public abstract String getDescription();

  /**
   * @return The model that is driving the test generation.
   */
  public Model getModel()
  {
    return model_;
  }

  /** Get the random number generator that is used for test generation. */
  public Random getRandom()
  {
    System.out.println("getRandom returns "+rand_);
    return rand_;
  }

  /** This allows you to specify a random number generator.
   *  The default is to use new Random(FIXEDSEED), so that test
   *  generation is repeatable (that is, each instance of this class
   *  will generate the same test sequences).
   *
   * @param rand  A non-null instance of Random.
   */
  public void setRandom(Random rand)
  {
    rand_ = rand;
  }

  /**
   *  A convenience method for adding known listeners and coverage metrics.
   *  This is equivalent to <code>getModel().addListener(name)</code>.
   *
   *  See the Factory class for the set of known names.
   *
   *  @param name The name of a known listener.
   *  @return     The listener that has been added (now or earlier).
   */
  public ModelListener addListener(String name)
  {
    return model_.addListener(name);
  }

  /** @deprecated Use addListener(listener) instead. */
  public void addListener(String name, ModelListener listen)
  {
    model_.addListener(listen);
  }

  /** This is equivalent to addListener(metric), but more convenient.
   * @param metric  A non-null coverage metric to add.
   * @return metric, or a previously-added metric with the same name (if any).
   */
  public CoverageMetric addCoverageMetric(CoverageMetric metric)
  {
    return (CoverageMetric) model_.addListener(metric);
  }

  /**
   *  A convenience method that adds a listener object.
   *  This is equivalent to <code>getModel().addListener(listener)</code>.
   * @param listener  Must be non-null.
   * @return     The listener that has been added (now or earlier).
   */
  public ModelListener addListener(ModelListener listener)
  {
    return model_.addListener(listener);
  }

  /** Prints the name and toString message from each coverage metric.
   *  They are printed in alphabetical order.
   */
  public void printCoverage()
  {
    List<String> names = new ArrayList<String>(model_.getListenerNames());
    Collections.sort(names);
    for (String name : names) {
      ModelListener listen = model_.getListener(name);
      if (listen instanceof CoverageMetric) {
        model_.printMessage(name+": "+listen.toString());
      }
    }
  }

  /** Performs a user-requested reset of the model. */
  public void reset()
  {
    model_.doReset()// a user-requested reset
  }

  /** Generate one more test step in the current sequence.
   *  This may reset and start a new test sequence if necessary.
   *
   *  @return the number of the action taken, or -1 if a reset was done.
   */
  public abstract int generate();

  /** Generate some test sequences, with the given total length.
   *  The default implementation of this just calls generate()
   *  length times.
   *
   * @param length
   */
  public void generate(int length)
  {
    for (int i=0; i<length; i++)
      generate();
  }

  /** Equivalent to buildGraph(10000). */
  public GraphListener buildGraph()
  {
    return buildGraph(10000);
  }

  /** Equivalent to buildGraph(MaxSteps,true). */
  public GraphListener buildGraph(int maxSteps)
  {
    return buildGraph(maxSteps, true);
  }

  /** Calls {@code generate()} repeatedly until the graph seems to be complete.
   *  <p>
   *  Note that this method uses a fresh random number generator
   *  (with FIXEDSEED) to avoid modifying the random number
   *  generator {@link #getRandom()} that is used for test generation.
   *  </p>
   @param maxSteps An upper bound on the number of calls to generate,
   *              to avoid eternal exploration of large graphs.
   *  @param clear If this is true, the TODO and DONE flags on each
   *            transition of the graph are cleared after the graph is built.
   *            This is recommended, so that algorithms like GreedyTester
   *            get a fresh view of the graph.
   *  @see GraphListener.isComplete()
   */
  public GraphListener buildGraph(int maxSteps, boolean clear)
  {

    if(false) {

    Random old = rand_;
    rand_ = new Random(FIXEDSEED);
    // make sure there is a graph listener
    GraphListener graph = (GraphListener) model_.addListener("graph");
    boolean wasTesting = model_.setTesting(false);
    model_.doReset("Buildgraph");
    do {
      generate(10);
      maxSteps -= 10;
    }
    while (graph.numTodo() > 0 && maxSteps > 0);

    int todo = graph.numTodo();
    if (todo > 0) {
      model_.printWarning("buildgraph stopped with "
          + graph.getGraph().numEdges() + " transitions and "
          + graph.getGraph().numVertices() + " states, but "
          + todo + " unexplored branches.");
    }
    model_.setTesting(wasTesting);
    model_.doReset("Buildgraph");
    if (clear) {
      graph.clearDoneTodo();
    }

    // restore the original random number generator.
    rand_ = old;
    return graph;
    }
   
    return buildGraphBreadthFirst(150, clear);
  }

  /** Generate a graph using a breadth-first approach with optimisations.
   *  <p>
   *  Note that this method uses a fresh random number generator
   *  (with FIXEDSEED) to avoid modifying the random number
   *  generator {@link #getRandom()} that is used for test generation.
   *  </p>
   <p>
   *  The approach taken by this method is to maintain two queues:
   *  <ul>
   <li>a high priority queue, consisting of actions and guards that have
   *      not previously been encountered, and</li>
   <li>a low priority queue, consisting of transitions that have not
   *      previously been taken, and high-priority transitions beyond
   *      depth {@code maxDepth}.</li>
   </ul>
   </p>
   <p>Note that this function is incomplete and will be available in a
   *     subsequent release.</p>
   *
   *  @param maxDepth An upper bound on the depth to explore,
   *            to avoid eternal exploration of large graphs.
   *  @param clear If this is true, the TODO and DONE flags on each
   *            transition of the graph are cleared after the graph is built.
   *            This is recommended, so that algorithms like GreedyTester
   *            get a fresh view of the graph.
   *  @see GraphListener.isComplete()
   */
  public GraphListener buildGraphBreadthFirst(int maxDepth, boolean clear)
  {
    System.out.println("Called new buildgraph");
    Random old = rand_;
    rand_ = new Random(FIXEDSEED);
    GraphListener graph = (GraphListener) model_.addListener("graph");
    boolean wasTesting = model_.setTesting(false);

    // Build graph logic
    Set<String> seenActions = new HashSet<String>();
    Set<Object> seenStates = new HashSet<Object>();
    Set<List<Integer>> visitedPaths = new HashSet<List<Integer>>();
    Queue<List<Integer>> highPriority = new LinkedList<List<Integer>>();
    Queue<List<Integer>> lowPriority = new LinkedList<List<Integer>>();

    BitSet enabled = model_.enabledGuards();
 
    //seenStates.put(model_.getState());

    // Initially populate the queues and then proceed
    for(int i = 0; i<enabled.cardinality(); i++) {
           List<Integer> path = new LinkedList<Integer>();
           if(enabled.get(i))
              path.add(i);
           highPriority.offer(path);
           System.out.println("Populated buildgraph");
    }

    while(true) {
       System.out.println("Called new buildgraph main loop");
       List<Integer> path = highPriority.poll();
       if(path == null) path = lowPriority.poll();
       if(path == null) break; // No work left to do - exit
       model_.doReset("Buildgraph");

       // Exit condition
       if(path.size() > maxDepth) break;

       for(Integer p : path) {
          String actionName = model_.getActionName(p);
          model_.doAction(p);
          seenActions.add(actionName);
         // seenStates.add(model_.getState());

          BitSet nenabled = model_.enabledGuards();
          for(int i = 0; i<nenabled.cardinality(); i++) {
             if(nenabled.get(i)) {
                if(!seenActions.contains(model_.getActionName(i))) {
                   // High priority - not seen before
                   List<Integer> newPath = (List<Integer>) ((LinkedList<Integer>) path).clone();
                   newPath.add(i);
                   highPriority.offer(newPath);
                } else {
                   // Low priority
                   List<Integer> newPath = (List<Integer>) ((LinkedList<Integer>) path).clone();
                   newPath.add(i);
                   //TODO: Need to make this a proper equality test - will fail at the moment
                   // because it is checking references.  Should be equivalent to checking
                   // if we have taken the current path + the action under consideration before.
                   if(!visitedPaths.contains(newPath)) {
                      lowPriority.offer(newPath);
                   }
                }

             }
          }
       }

       visitedPaths.add(path);
    }
    model_.setTesting(wasTesting);
    model_.doReset("Buildgraph");
    if (clear) {
      graph.clearDoneTodo();
    }

    // restore the original random number generator.
    rand_ = old;
    return graph;
  }
}
TOP

Related Classes of nz.ac.waikato.modeljunit.Tester

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.