Commit 31130e60 authored by uprime815's avatar uprime815
Browse files

initial upload of t3

pulled from t3 svn repos
parent 257c80b2
t3
==
An automated unit testing tool for Java classes.
This diff is collapsed.
---------------------------------------------------------------------
T2 LICENSE
---------------------------------------------------------------------
This copyright and license statements apply to the entire T2 software.
Copyright 2007 Wishnu Prasetya.
T2 is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License (GPL) version 3, as
published by the Free Software Foundation.
T2 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.
A copy of the GNU General Public License (GPL) can be found in the
file gpl_license.txt, which is included in this software. If it is
missing, see http://www.gnu.org/licenses.
---------------------------------------------------------------------
\ No newline at end of file
t3core: automated unit-testing tool for Java classes. This
is the back-end tool.
t3i: extended features over t3core, e.g. DSL to specify custom
object generators, DSL to query generated test suite, oracle
inference. t3i is intended to be run from Groovy.
<html>
<head>
<link rel="stylesheet" type="text/css" href="./docs/docs.css" />
</head>
<body>
<h1>T3 Readme</h1>
At the top-level T3 has two tools: generator, and replay.
The generator will need Java 8. The replay tool can be compiled with Java 7.
Both tools require a common-T3 part, which can be compiled in older
Java.
<h2>All-Java8 Build (standard)</h2>
This builds the whole T3 with Java8. With this scheme,
running T3's tools requires Java8.
<ol>
<li>We build with ant. Before running ant, set JAVA_HOME to the jdk of Java8.
In Linux/cygwin Bash: export JAVA_HOME=/cygdrive/c/apps/Java/jdk1.8
<li>run "ant jarT3" to build.
</ol>
<h3>To Run</h3>
Generator: java -cp T3.jar Sequenic.T3.T3Cmd [option]* targetclass
<p>Replay: java -co T3.jar Sequenic.T3.ReplayCmd [option]* suitefile
<p>For both, --help will list available options.
<h2>Mixed Scheme</h2>
This builds the generator with and for Java 8, and the replay tool with Java 7.
<ol>
<li>Build the common-T3 with Java 7:
<ol>
<li>We build with ant. Before running ant, set JAVA_HOME to the jdk of Java8.
In Linux/cygwin Bash: export JAVA_HOME=/cygdrive/c/apps/Java/jdk1.7
<li>run "ant jarCommon" to build
</ol>
<p>
<li>Build T3 tools.
<ol>
<li> Set JAVA_HOME to the jdk of Java8.
In Linux/cygwin Bash: export JAVA_HOME=/cygdrive/c/apps/Java/jdk1.8
<li>run "ant jarT3" to build
</ol>
</ol>
<h3>To Run</h3>
Generator: java -cp T3.jar Sequenic.T3.T3Cmd [option]* targetclass
<p>Replay with Java 8: java -cp T3.jar Sequenic.T3.ReplayCmd [option]* suitefile
<p>Replay with older Java 7: java -cp CommonT3.jar Sequenic.T3.ReplayCmd [option]* suitefile
<p>For all, --help will list available options.
</body>
</html>
\ No newline at end of file
/*
* Copyright 2013 Wishnu Prasetya.
*
* This file is part of T3.
* T3 is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License (GPL) as published by the
* Free Software Foundation; either version 3 of the License, or any
* later version.
*
* T3 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.
*
* A copy of the GNU General Public License can be found in T3 distribution.
* If it is missing, see http://www.gnu.org/licenses.
*/
package Sequenic.T3;
public class CONSTANTS {
public static final String T3loggerName = "T3" ;
public static final String auxField_prefix = "AUX__" ;
public static final String classinv_name = "classinv__" ;
}
/*
* Copyright 2013 Wishnu Prasetya.
*
* This file is part of T3.
* T3 is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License (GPL) as published by the
* Free Software Foundation; either version 3 of the License, or any
* later version.
*
* T3 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.
*
* A copy of the GNU General Public License can be found in T3 distribution.
* If it is missing, see http://www.gnu.org/licenses.
*/
package Sequenic.T3;
/**
* To represent a violation to a class invariant.
*/
public class ClassInvariantError extends AssertionError {
private static final long serialVersionUID = 1L;
public ClassInvariantError(String info) {
super(info) ;
}
public ClassInvariantError(String info, Throwable cause) {
super(info,cause) ;
}
public ClassInvariantError(Throwable cause) {
super(cause) ;
}
}
/*
* Copyright 2014 Wishnu Prasetya.
*
* This file is part of T3.
* T3 is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License (GPL) as published by the
* Free Software Foundation; either version 3 of the License, or any
* later version.
*
* T3 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.
*
* A copy of the GNU General Public License can be found in T3 distribution.
* If it is missing, see http://www.gnu.org/licenses.
*/
package Sequenic.T3;
import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.Logger;
/**
* Used to hold various configuration parameters for T3. The use of this Config
* class is shared by multiple T3's API classes, but keep in mind that such an
* API class may not utilize all configuration parameters available in this
* Config class.
*/
public class Config {
public Class CUT ;
public boolean assumeClientInTheSamePackage = true ;
public int maxLevelOfObjectsNesting = 4 ;
public int maxCollectionSize = 3 ;
public int maxNumberOfStepRetry = 30 ;
public int maxNumberOfSeqRetry = 5 ;
public int maxPrefixLength = 8 ;
public int maxSuffixLength = 3 ;
/**
* If true will try to maximize the prefix to the specified maximum length.
* Default it true.
*/
public boolean maximizePrefix = true ;
public double suiteSizeMultiplierPerGoal = 4.0 ;
public double fieldUpdateProbability = 0.1 ;
/**
* If true we will only keep sequences that violates assertions, or throws an exception,
* one that is caused by a violation to a pre-condition. Other sequences are dropped.
* Default is false.
*/
public boolean keepOnlyRedTraces = false ;
public boolean dropDuplicates = false ;
/**
* The resulting suite will be split into this number of sub-suites.
*/
public int splitSuite = 1 ;
/**
* An output stream to print out sequence-executions (if requested).
*/
public OutputStream sequencePrintOut = System.out ;
/**
* An output stream to print out report and statistics.
*/
public OutputStream reportOut = System.out ;
public int reportVerbosity = 1 ;
/**
* If true, the CUT is assumed to be correct, and we want to generate
* a suite to be used for regression testing. Oralces will be injected
* into the sequences; e.g. stating that methods should return exactly
* the same values as they do now. If exceptions are thrown, then they
* are converted into oracles, stating that the same exceptions should
* be thrown by replay (negative tests).
*
* The default is false.
*/
public boolean regressionMode = false ;
/**
* If regressionMode and this flag are true, this will automatically
* inject oracles into the generated test-sequence. Default is true.
*/
public boolean injectOracles = true ;
/**
* The number of processor cores. The default is 1.
*/
public int numberOfCores = 1 ;
/**
* A list of paths to roots of the class files that are to be scanned for building
* an interface map.
*/
public String[] dirsToClasses = new String[0] ;
public void setDirsToClasses(String... dirs) {
dirsToClasses = dirs ;
}
/**
* If true, when replaying a suite, all sequences will be run; else the execution will
* stop at the first step, in the first execution that throws an exception.
* This exception will be wrapped in a Violation, and then (re-)thrown.
*/
public boolean replayRunAll = false ;
/**
* If true, then replays that throw exception (or Oracle
* Error in the regression-mode) will be shown/reported into the given output stream.
*/
public boolean replayShowExcExecution = true ;
public int replayShowLength = 10 ;
public int replayShowDepth = 3 ;
/**
* Create a config, with default setting.
*/
public Config() { ; }
protected void reportWrite(String s){
try {
reportOut.write(s.getBytes());
}
catch(Exception e) {
Logger.getLogger(CONSTANTS.T3loggerName).warning("Fail to write to a report-output-stream.");
}
}
protected void reportWriteln(String s){
reportWrite(s + "\n") ;
}
public void print() {
printGeneral() ;
printGeneratorRelated() ;
printReplayRelated() ;
}
public void printGeneral() {
reportWriteln("-----") ;
reportWriteln("Configuration:") ;
reportWriteln("** General parameters:") ;
reportWriteln(" #cores : " + numberOfCores) ;
}
public void printGeneratorRelated() {
reportWriteln("** Generator related parameters: ") ;
reportWrite (" Dirs to class-files to be scanned : ") ;
if (dirsToClasses.length==0) reportWriteln("-") ;
else {
for (int k=0; k<dirsToClasses.length; k++) {
if (k>0) reportWrite(", ") ;
reportWrite(dirsToClasses[k]) ;
}
reportWriteln("") ;
}
reportWriteln(" CUT : " + CUT) ;
if (assumeClientInTheSamePackage) reportWriteln(" Testing CUT from the same package's perspective") ;
else reportWriteln(" Testing CUT from a different package's perspective") ;
reportWriteln(" Max. number of step-retries : " + maxNumberOfStepRetry) ;
reportWriteln(" Max. number of sequence-retries : " + maxNumberOfSeqRetry) ;
reportWriteln("** T3-default-generator's specific parameters:") ;
reportWriteln(" Regression-mode : " + regressionMode) ;
reportWriteln(" Injecting oracles : " + injectOracles) ;
reportWriteln(" Max. collection size : " + maxCollectionSize) ;
reportWriteln(" Max. level of object nesting : " + maxLevelOfObjectsNesting) ;
reportWriteln(" Max. prefix length : " + maxPrefixLength) ;
reportWriteln(" Max. suffix length : " + maxSuffixLength) ;
reportWriteln(" Variable length prefix : " + !maximizePrefix) ;
reportWriteln(" Field-update probablity as a step : " + fieldUpdateProbability) ;
reportWriteln(" Suite size multiplier for each goal : " + suiteSizeMultiplierPerGoal) ;
reportWriteln(" Keeping only exception-throwing sequences : " + keepOnlyRedTraces) ;
reportWriteln(" Dropping duplicates : " + dropDuplicates) ;
reportWriteln(" Suite is to be split in : " + splitSuite + " subsuites") ;
}
public void printReplayRelated() {
reportWriteln("** Replay related parameters: ") ;
reportWriteln(" Replaying all sequences: "+ replayRunAll) ;
reportWriteln(" Regression-mode : " + regressionMode) ;
reportWriteln(" Show exception-throwning execution: "+ replayShowExcExecution) ;
reportWriteln(" Max. length of shown suffix: " + replayShowLength) ;
reportWriteln(" Max. depth of objects shown: " + replayShowDepth) ;
}
}
package Sequenic.T3.Info;
import static Sequenic.T3.Reflection.Reflection.getALLSuperClasses;
import java.io.OutputStream;
import java.lang.reflect.*;
import java.util.*;
import Sequenic.T3.CONSTANTS;
import Sequenic.T3.Reflection.Reflection;
import Sequenic.T3.utils.Pair;
public class ObjectCoverage {
public Class CUT ;
/**
* All non-static, non-abstract, and non-final fields of CUT
*/
public LinkedList<Field> fields ;
/**
* A set of integers, encoding all possible pairs of abstracted values
* over the fields of CUT.
*/
public Set<Integer> allpairs ;
public Set<Integer> coveredPairs = new HashSet<Integer>() ;
public ObjectCoverage(Class CUT) {
this.CUT = CUT ;
fields = getAllNonStaticFields(CUT) ;
allpairs = this.allPairsToCover() ;
}
static private LinkedList<Field> getAllNonStaticFields(Class C) {
LinkedList<Field> fields = new LinkedList<Field>();
List<Class> ancestors = new LinkedList<Class>() ;
ancestors.add(C) ;
ancestors.addAll(getALLSuperClasses(C)) ;
for (Class D : ancestors) {
Field[] fieldsOfD = D.getDeclaredFields() ;
for (Field f : fieldsOfD) {
int mf = f.getModifiers() ;
if (Modifier.isStatic(mf) || Modifier.isAbstract(mf) || Modifier.isFinal(mf)) continue ;
if (f.getName().startsWith(CONSTANTS.auxField_prefix)) continue ;
fields.add(f) ;
}
}
return fields ;
}
private int[] getProfile() {
LinkedList<Integer> profile = new LinkedList<Integer>() ;
for (Field f : fields) {
Class fclass = f.getClass() ;
if (fclass==Character.class || fclass==Character.TYPE) {
profile.addLast(1) ; continue ;
}
if (fclass==Boolean.class || fclass==Boolean.TYPE) {
profile.addLast(1) ; continue ;
}
if (Reflection.isPrimitiveLike(CUT)
|| fclass==String.class
|| fclass.isArray()
|| Collection.class.isAssignableFrom(CUT)
) {
profile.addLast(2) ; continue ;
}
profile.addLast(1) ;
}
int[] profile_ = new int[profile.size()] ;
for (int i=0; i<profile_.length; i++) profile_[i] = profile.get(i) ;
return profile_ ;
}
static private int calculateCombination(int x, int valx, int y, int valy) {
return (x+1)*1000 + valx*100 + (y+1)*10 + valy ;
}
private Set<Integer> allPairsToCover() {
Set<Integer> allpairs = new HashSet<Integer>() ;
int[] profile = getProfile() ;
int N = profile.length ;
for(int x = 0; x<N; x++) {
for (int y=x+1; y<N; y++) {
for(int valx = 0 ; valx <= profile[x]; valx++) {
for(int valy=0; valy <= profile[y]; valy++) {
allpairs.add(calculateCombination(x,valx,y,valy)) ;
//System.out.println("*>> to cover: " + calculateCombination(x,valx,y,valy)) ;
}
}
}
}
return allpairs ;
}
/**
* Get the abstract value of the field f in an object o.
*/
private int getAbstractValue(Field f, Object o) {
Class fclass = f.getClass() ;
Object val = null ;
try {
f.setAccessible(true) ;
val = f.get(o) ;
}
catch(Exception e) {
// if we can't get the value of the field, assume it is 0
return 0 ;
}
if (val == null) return 0 ;
if (fclass==Character.class || fclass==Character.TYPE) return 1 ;
if (fclass==Boolean.class || fclass==Boolean.TYPE) {
if (((boolean) val)==false) return 0 ;
else return 1 ;
}
if (Reflection.isPrimitiveLike(fclass)) {
if (((double) val)==0) return 0 ;
if (((double) val) > 0) return 1 ;
return 2 ;
}
if (fclass.isArray()) {
if (Array.getLength(o) == 0) return 2 ;
return 1 ;
}
if (Collection.class.isAssignableFrom(fclass)) {
if (((Collection) o).size() > 0) return 2 ;
return 1 ;
}
// for other types of the field, if it is not null then map it to 1
return 1 ;
}
/**
* Clear the list of covered pairs.
*/
public void reset() {
coveredPairs.clear();
}
/**
* Add the pairs covered by the given object to the set of covered pairs.
*/
public void addCoveredPairs(Object o) {
int N = fields.size() ;
int[] vector = new int[N] ;
int i = 0 ;
for (Field f : fields) {
vector[i] = getAbstractValue(f,o) ;
i++ ;
}
for(int x = 0; x<N; x++) {
for (int y=x+1; y<N; y++) {
coveredPairs.add(calculateCombination(x,vector[x],y,vector[y])) ;
//System.out.println("*>> adding " + calculateCombination(x,vector[x],y,vector[y])) ;
}
}
}
private void write(OutputStream out, String s) throws Exception {
out.write(s.getBytes());
}
private void writeln(OutputStream out, String s) throws Exception {
write(out, s + "\n") ;
}
private String coverage(int covered, int tobeCovered) {
String s = "" + covered + "/" + tobeCovered ;
if (tobeCovered==0) s += " (100%)" ;
else {
double p = Math.round(10000 * covered/tobeCovered)/100.0 ;
s += " (" + p + "%)" ;
}
return s ;
}
public void printReport(OutputStream out) throws Exception {
writeln(out, "** Field-pairs' abstract values coverage : " + coverage(coveredPairs.size(),allpairs.size())) ;
}
public static void main(String[] args) {
}
}
/*
* Copyright 2013 Wishnu Prasetya.
*
* This file is part of T3.
* T3 is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License (GPL) as published by the
* Free Software Foundation; either version 3 of the License, or any
* later version.
*
* T3 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.
*
* A copy of the GNU General Public License can be found in T3 distribution.
* If it is missing, see http://www.gnu.org/licenses.
*/
package Sequenic.T3.JavaType;
import Sequenic.T3.Reflection.Reflection;