Commit ec5e0674 authored by ISWB Prasetya's avatar ISWB Prasetya
Browse files

Improving the way CUT with only private constructors is treated.

parent 5cf2285c
......@@ -33,6 +33,13 @@ import Sequenic.T3.Pool;
import Sequenic.T3.SimpleClassLoader;
import Sequenic.T3.Sequence.Datatype.*;
/**
* This provides top-level APIs to invoke G2 generator. Use the static method:
*
* generateSuites(CUTname,config,timebudget) ... the budget is in ms.
*
*/
public class G2 {
/**
......@@ -97,9 +104,10 @@ public class G2 {
static public class TimeBudgetException extends Exception { }
/**
* Create an instance of G2 test suites generator. CUT has to be a concrete class
* (it should not be an interface nor an abstract class).
* @throws Exception
* Create an instance of G2 test suites generator. Typically, the CUT is a concrete
* class (it is not an interface or an abstract class). If an interface or an abstract
* class is given as the CUT, it may still contain concrete static methods, which we
* can test; so in these cases we will target them.
*/
protected G2(String CUTname, G2Config config) throws Exception {
this.config = config ;
......@@ -127,28 +135,38 @@ public class G2 {
else t = new SingleTarget(config,g2sg,m) ;
worklist.addTarget(t);
}
g2sg.scope.configureForADTtesting();
for (Constructor co : g2sg.scope.constructors) {
SingleTarget t ;
if (config.refinementHeuristic.equals("evo")) t = new EvoSingleTarget(config,g2sg,co) ;
else t = new SingleTarget(config,g2sg,co) ;
//worklist.addTarget(t);
}
for (Method m : g2sg.scope.methods) {
if (Modifier.isStatic(m.getModifiers())) continue ;
SingleTarget t ;
if (config.refinementHeuristic.equals("evo")) t = new EvoSingleTarget(config,g2sg,m) ;
else t = new SingleTarget(config,g2sg,m) ;
worklist.addTarget(t);
if (!g2sg.scope.CUT.isInterface() && ! Modifier.isAbstract(g2sg.scope.CUT.getModifiers())) {
// set targets for ADT testing; only when the CUT is concrete:
g2sg.scope.configureForADTtesting();
for (Constructor co : g2sg.scope.constructors) {
SingleTarget t ;
if (config.refinementHeuristic.equals("evo")) t = new EvoSingleTarget(config,g2sg,co) ;
else t = new SingleTarget(config,g2sg,co) ;
//worklist.addTarget(t);
}
for (Method m : g2sg.scope.methods) {
if (Modifier.isStatic(m.getModifiers())) continue ;
SingleTarget t ;
if (config.refinementHeuristic.equals("evo")) t = new EvoSingleTarget(config,g2sg,m) ;
else t = new SingleTarget(config,g2sg,m) ;
worklist.addTarget(t);
}
}
t3log.info("Creating " + printTargets()) ;
}
private String printTargets() {
StringBuilder s = new StringBuilder() ;
s.append("#worklist=" + worklist.waiting.size() + ", targets: ") ;
s.append("G2 generator targeting " + g2sg.scope.CUT.getName()) ;
s.append(", #worklist=" + worklist.waiting.size() + ", targets: ") ;
for (SingleTarget target : worklist.waiting) {
target.maxNumberOfRefinement = config.maxNumberOfRefinements_ofEachTarget ;
target.minimumCovTobeHappy = config.minimumCovTobeHappy_ofEachTarget ;
s.append("\n " + target.getName() + "(" + target.getNumOfParams() + ")") ;
}
t3log.info("Creating G2 targeting " + g2sg.scope.CUT.getName() + ", " + s.toString());
return s.toString() ;
}
public void startTimeBudget(long timebudget) {
......@@ -165,6 +183,11 @@ public class G2 {
* The whole process will keep going, until it runs out time budget.
*/
protected void generateAndRefine() throws TimeBudgetException, InterruptedException {
if (worklist.isEmpty()) {
t3log.info("" + g2sg.scope.CUT.getName() + " induces no target, so NO TEST SUITE is generated.") ;
return ;
}
// DON't do shut down here!! --> JacocoInstrumenter.shutDownJacocoLogger();
double budget = Math.max(1,timebudgetTracker.getBudget()) ;
double remaining = timebudgetTracker.check() ;
......@@ -181,15 +204,25 @@ public class G2 {
int prefixRefinementCount = 0 ;
int prefixStagnationCount = 0 ;
while (!worklist.isEmpty()) {
if (worklist.targets.size() == 0) {
// the first time, this will generate the prefixes, upon the next iterations,
if (worklist.targets.size() == 0 && worklist.hasADTtarget()) {
// The first time, this will generate the prefixes, upon the next iterations,
// this will refine the prefixes each time the targetlist is emptied.
// Also, only generate prefixes if we have ADT target left.
if (prefixStagnationCount < 3) {
prefixRefinementCount++ ;
Integer K = config.numberOfPrefixes ;
if (K==null) K = 50 ;
if (g2sg.getPrefixes() != null) K = Math.min(K,10) ;
int numOfAddedPrefixes = g2sg.incrementallyGeneratePrefixes(K,config.maxPrefixLength,config.maxObjectDepth) ;
Integer numOfAddedPrefixes = g2sg.incrementallyGeneratePrefixes(K,config.maxPrefixLength,config.maxObjectDepth) ;
if (numOfAddedPrefixes==null) {
// cannot generate prefixes because CUT has no constructor/factory,
// we should now remove all ADT targets because we can't solve them anyway
worklist.removeADTtargets();
t3log.info("Since " + g2sg.scope.CUT.getName() + " can't be instantiated, we now DROP all ADT targets.") ;
// go back to the loop-head again:
continue ;
}
if (numOfAddedPrefixes == 0) prefixStagnationCount++ ;
t3log.info("=== Generating/refining prefixes of "
......@@ -227,14 +260,28 @@ public class G2 {
/**
* Use this factory method to construct instances of G2 to generate test
* suites for a given CUT.
* If the CUT is not concrete (it is an interface or abstract) then
* the method tries to find a concrete implementation of the CUT and target
* this implementation instead. If there are multiple implementations, one
* will be selected randomly.
* There are three special cases:
*
* If the CUT has no inner class, then only a single instance of G2 will be
* generated. If it has static inner classes, one instance of G2 will be
* recursively constructed for each such inner class.
* (1) CUT is an interface. It may have static methods, which are then concrete.
* G2 will then target these methods.
*
* (2) CUT is an abstract class. Like Interface, it may gace static methods, which
* must be concrete. G2 will in any case target these methods.
*
* An abstract class may also have some concrete constructors and instance methods.
* These methods cannot be tested directly since Java prevents direct creation of
* instances of an abstract class. We will have to test the CUT indirectly through
* a concrete subclass. Ideally, the tester should provide a proxy subclass that
* will simply pass calls to implemented methods of the CUT, but for now G2 will just
* search for a random implementation of the CUT and targets this implementation instead.
*
* This will create a separate instance of G2, targeting the implementation.
*
* TODO: a tool to generate such a proxy class.
*
* (3) If the CUT has inner classes, and if G2 is configured to chase them, it will
* target these inner classes as well. Fresh instances of G2 will be created for each
* inner class.
*/
static public List<G2> mkG2(String CUTname, G2Config config) {
ImplementationMap imap = new ImplementationMap(new String[0]) ;
......@@ -263,49 +310,44 @@ public class G2 {
ImplementationMap imap,
ClassLoader loader)
{
List<G2> g2s = new LinkedList<G2>() ;
try {
Class CUT = loader.loadClass(CUTname) ;
boolean abstractCUT = Modifier.isAbstract(CUT.getModifiers()) ;
if (CUT.isInterface() || abstractCUT) {
// if CUT is an interface or abstract, find an implementation:
Class imp = imap.getRndImp(CUT) ;
if (imp!=null) {
t3log.info(CUT.getName() + " is abstract or interface; trying to target a concrete implementation instead, namely:" + imp.getName());
g2s.add(new G2(imp.getName(),config)) ;
}
else t3log.warning(CUT.getName() + " is abstract or interface; but failed to find an implementation to target!");
if (abstractCUT) {
//Method[] publicmethods = CUT.getMethods() ;
Method[] CUTmethods = CUT.getDeclaredMethods() ;
for (Method m : CUTmethods) {
int m_ = m.getModifiers() ;
if (!Modifier.isAbstract(m_) && Modifier.isStatic(m_)) {
t3log.info(CUT.getName() + " is abstract, but it has at least one concrete and static method; so we target the class too.");
g2s.add(new G2(CUTname,config)) ;
break ;
}
}
}
List<G2> g2s = new LinkedList<G2>() ;
try {
// First create G2 for the CUT itself. Even if it is non-concrete it may contains concrete
// static classes that we should test
g2s.add(new G2(CUTname,config)) ;
// If CUT is abstract, it may have concrete methods. To test these we need to create
// instances of the CUT. However, an abstract class cannot be instantiated, so we need
// to find a concrete implementation, and test the CUT indirectly through this concrete
// implementation:
Class CUT = loader.loadClass(CUTname) ;
if (!CUT.isInterface() && Modifier.isAbstract(CUT.getModifiers())) {
// if CUT is abstract, find an implementation:
Class imp = imap.getRndImp(CUT) ;
if (imp!=null) {
t3log.info(CUT.getName() + " is abstract; we will test indirectly through a concrete implementation, namely:" + imp.getName());
G2 g2indirect = new G2(imp.getName(),config) ;
g2s.add(g2indirect) ;
}
else t3log.warning(CUT.getName() + " is abstract; but failed to find an implementation to target!");
}
else g2s.add(new G2(CUTname,config)) ;
// we additionally constructs instances of G2 for every static inner
// class of the CUT
if (config.targetStaticInnerClassesToo) {
// in some rare case CUT.getDeclaredClasses() may throw java.lang.IllegalAccessError, cannot access superclass...
// can't figure out why; probably classloader issue
for (Class innerC : CUT.getDeclaredClasses()) {
// non static inner classes and inherited inner classes are NOT targeted:
// NOTE: well, include them nonetheless...
// if (! Modifier.isStatic(innerC.getModifiers())) continue ;
// if (innerC.getDeclaringClass() != CUT) continue ;
// t3log.info("Found an inner static class " + innerC.getName());
g2s.addAll(mkG2worker(innerC.getName(),config,imap,loader)) ;
}
// if configured to do so, we additionally constructs instances of G2 for every static inner
// class of the CUT
if (config.targetStaticInnerClassesToo) {
// in some rare case CUT.getDeclaredClasses() may throw java.lang.IllegalAccessError, cannot access superclass...
// can't figure out why; probably classloader issue
for (Class innerC : CUT.getDeclaredClasses()) {
// non static inner classes and inherited inner classes are NOT targeted:
// NOTE: well, include them nonetheless...
// if (! Modifier.isStatic(innerC.getModifiers())) continue ;
// if (innerC.getDeclaringClass() != CUT) continue ;
// t3log.info("Found an inner static class " + innerC.getName());
g2s.addAll(mkG2worker(innerC.getName(),config,imap,loader)) ;
}
}
}
catch (Throwable t) {
......@@ -335,7 +377,7 @@ public class G2 {
return ;
}
if (g2s.length>1) {
String s = "G2 will target CUT and its internal classes: " ;
String s = "G2 will target these classes: " ;
for (int k=0; k<g2s.length; k++) {
if (k>0) s += ", " ;
s += g2s[k].g2sg.scope.CUT.getName() ;
......
......@@ -147,21 +147,7 @@ public class G2SuiteGen {
// setting up implementation-map and testing scope:
imap = new ImplementationMap(new String[0]) ;
scope = new TestingScope(imap,CUT) ;
// Forcefully adding private constructors to the scope... hackish.
scope.configureForADTtesting() ;
if (true /* scope.constructors.isEmpty() && scope.creatorMethods.isEmpty() */) {
for (Constructor co : CUT.getDeclaredConstructors()) {
int mod = co.getModifiers() ;
if (Modifier.isPrivate(mod) && ! Modifier.isAbstract(mod)) {
scope.forceConstructors.add(co) ;
}
}
if (!scope.forceConstructors.isEmpty()) {
t3log.info("Forcefully adding private constructors into the scope of " + CUT.getName()) ;
}
}
// read various static information provided in file(s):
if (staticInfoDir != null) {
try {
......@@ -484,10 +470,38 @@ public class G2SuiteGen {
*
* If maximum length is reached and we reach stagnation, we reset the legth
* to 1.
*
* If the testing scope does not contain any constructor/factory to create an instance of
* the SUT the method will however returns null.
*/
private int refine(int N) {
private Integer refine(int N) {
// prepare the scope
scope.configureForADTtesting();
// If we have no constructor nor creation method, forcefully add declared
// non-visible constructors:
if(scope.constructors.isEmpty() && scope.creatorMethods().isEmpty()) {
boolean added = false ;
for (Constructor co : scope.CUT.getDeclaredConstructors()) {
int mod = co.getModifiers() ;
if (! Modifier.isAbstract(mod)) {
added = true ; scope.constructors.add(co) ;
}
}
if(added) {
Logger.getLogger(CONSTANTS.T3loggerName).info("Forcefully adding non-visible constructors into the scope of " + scope.CUT.getName()) ;
}
else {
// if forcing fails, then we simply have no means to create an instance of
// the CUT, hence also no means to produce prefixes ;
Logger.getLogger(CONSTANTS.T3loggerName).info("Cannot find a constructor/factory to instantiate "
+ scope.CUT.getName()
+ " to create prefixes. No prefixes will be generated.") ;
return null ;
}
}
if (staticInfo != null) {
resetSeededConstants() ;
addSeededSPrimitives(staticInfo.getAllConstansts()) ;
......@@ -558,9 +572,12 @@ public class G2SuiteGen {
/**
* This will generate up to N prefixes iteratively. It returns the number
* of prefixes it manages to generate.
*
* If the testing scope does not contain any constructor/factory to create an instance of
* the SUT the method will however returns null.
*/
public int incrementallyGeneratePrefixes(int N, int maxlength, int maxdepth) {
System.err.println(">>> invoking incrementallyGeneratePrefixes...") ;
public Integer incrementallyGeneratePrefixes(int N, int maxlength, int maxdepth) {
// t3log.info(">>> invoking incrementallyGeneratePrefixes...") ;
if (currentPrefixes == null) {
currentPrefixes = new Prefixes(maxlength,maxdepth) ;
}
......@@ -568,14 +585,16 @@ public class G2SuiteGen {
currentPrefixes.maxlength = maxlength ;
currentPrefixes.ObjStructureMaxDepth = maxdepth ;
}
int added = currentPrefixes.refine(N) ;
Integer added = currentPrefixes.refine(N) ;
if(added == null) {
return null ;
}
if(added == 0) {
System.err.println(">>> incrementallyGeneratePrefixes DONE; FAIL to add any.") ;
t3log.info("FAIL to add any new prefix.") ;
}
else {
System.err.println(">>> incrementallyGeneratePrefixes DONE. Adding " + added
+ " prefixes. New size: " + currentPrefixes.size()) ;
t3log.info("Adding " + added + "; #prefixes now =" + currentPrefixes.size()) ;
}
return added ;
}
......
......@@ -124,13 +124,13 @@ public class SingleTarget {
if (target instanceof Method) {
Method m = (Method) target ;
if (isADT) {
g2sg.scope.configureForADTtesting();
// g2sg.scope.configureForADTtesting(); this will be done in the generate below
SUITE prefixes = g2sg.getPrefixes() ;
int multiplier = 2 ;
return g2sg.generateSuiteForASingleMethod(prefixes,m,multiplier) ;
}
else {
g2sg.scope.configureForNonADTTesting();
// g2sg.scope.configureForNonADTTesting(); this will be done in the generate below
Integer N = config.singeMethodTestSuiteSize ;
if (N == null) {
N = Math.max(50,power(4,m.getParameterCount())) ;
......@@ -139,7 +139,7 @@ public class SingleTarget {
}
}
// else target is a constructor
g2sg.scope.configureForADTtesting();
// g2sg.scope.configureForADTtesting(); ; this will be done in the generate below
Constructor co = (Constructor) target ;
Integer N = config.singeMethodTestSuiteSize ;
if (N == null) {
......
......@@ -48,7 +48,7 @@ public class Test_G2_forTriangle {
static public void main(String[] args) throws Throwable {
//test_one_bareG2() ;
//test_two_bareG2() ;
//genWithG2() ;
replayG2() ;
genWithG2() ;
//replayG2() ;
}
}
......@@ -17,6 +17,7 @@
*/
package Sequenic.T3.DerivativeSuiteGens.Gen2;
import java.lang.reflect.*;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
......@@ -80,6 +81,18 @@ public class Worklist {
public boolean isEmpty() { return targets.isEmpty() && waiting.isEmpty() ; }
public boolean hasADTtarget() {
return targets.stream().anyMatch(T -> T.isADT) || waiting.stream().anyMatch(T -> T.isADT) ;
}
/**
* Remove all ADT targets (e.g. because we can't generate prefixes).
*/
public void removeADTtargets() {
targets.removeIf(T -> T.isADT) ;
waiting.removeIf(T -> T.isADT) ;
}
/**
* To innitialize the worklist, before using it.
*/
......
......@@ -9,4 +9,6 @@ public interface IStudent {
}
public int tuitionFee() ;
public static int foo(int x) { return x+1 ; }
}
......@@ -22,5 +22,7 @@ public abstract class Vehicle {
}
abstract public int slowdown() ;
public static int foo(int x) { return x+1 ; }
}
......@@ -8,9 +8,13 @@ public class Triangle1 implements Serializable {
private float y = 0 ;
private float z = 0 ;
public Triangle1(){
System.out.println("*** Triangle()") ;
}
public void setX(float x) {
System.out.println("* setX " + x) ;
if (x<=0) throw new IllegalArgumentException() ;
......
......@@ -28,6 +28,7 @@ import java.lang.reflect.*;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.logging.Logger;
/**
* Representing the constructors, methods, and fields that are to be included in testing.
......@@ -56,10 +57,6 @@ public class TestingScope {
public boolean testingFromTheSamePackagePespective = true ;
public boolean includePrivateAndDefaultMembers = false ;
// HACK... force certain constructors to be included, e.g. private ones:
public List<Constructor> forceConstructors = new LinkedList<Constructor>() ;
public List<Constructor> constructors = new LinkedList<Constructor>() ;
public List<Method> creatorMethods = new LinkedList<Method>() ;
public List<Method> methods = new LinkedList<Method>();
......@@ -119,8 +116,7 @@ public class TestingScope {
clear() ;
// get the constructors in scope
constructors = getAccessibleConstructors() ;
// Hack:
constructors.addAll(forceConstructors) ;
// get the methods in scope
List<Method> allMethods = getAccessibleMethods() ;
for (Method M : allMethods) {
......@@ -147,7 +143,7 @@ public class TestingScope {
}
catch(Throwable t){ }
for (Method M : candidates) {
if (M.getDeclaringClass() == CUT) {
if (M.getDeclaringClass() == CUT) { // this is a bit wierd.. but maybe if the actual target is an inner class and the CUT is configure to be the enclosing class...
if (! Modifier.isStatic(M.getModifiers())) continue ;
if (M.getReturnType() != CUT) continue ;
//if (hasParameterOfThisType(M,CUT)) continue ;
......@@ -172,10 +168,8 @@ public class TestingScope {
/**
* Configure the scope for non-ADT-based testing. By default we will only include
* static methods and fields in the scope. In principle we can also test an
* abstract class in a non-ADT way, but we should then only include non-asbtract
* methods.
* Configure the scope for non-ADT-based testing.
* NOTE: static fields will be excluded.
*/
public void configureForNonADTTesting() {
clear();
......@@ -351,6 +345,7 @@ public class TestingScope {
/**
* Return all methods of C that are visible and accessible.
* NOTE: abstract methods are excluded.
*/
public List<Method> getAccessibleMethods() {
......@@ -360,6 +355,8 @@ public class TestingScope {
for (Method M : CUT.getDeclaredMethods()) candidates.add(M) ;
for (Class D : Reflection.getALLSuperClasses(CUT)) {
for (Method M : D.getDeclaredMethods()) {
// we won't include static method defined by superclass:
if (Modifier.isStatic(M.getModifiers())) continue ;
// if a method with the same name and type is already in the candidates, then M
// is overriden; so, not visible from the client:
boolean overriden = false ;
......@@ -383,7 +380,7 @@ public class TestingScope {
for (Method M : candidates) {
int mod = M.getModifiers() ;
// if (Modifier.isAbstract(mod)) continue ;
if (Modifier.isAbstract(mod)) continue ; // don't put abstract methods in the scope
String name = M.getName() ;
if (name.equals(CONSTANTS.classinv_name)
|| name.startsWith("<") // exclude <init> and <clinit>
......@@ -426,6 +423,12 @@ public class TestingScope {
}
else return (Modifier.isPublic(modifier)) ;
}
/**
* Returns the set of accessible constructors on the CUT. If CUT is abstract, no
* constructor is returned, since an abstract class cannot be instantiated through
* its constructor (even if it declares one).
*/
public List<Constructor> getAccessibleConstructors() {
......
......@@ -83,7 +83,10 @@ public class cpscannerCmd {
*/
private static void mainWorker(String[] args) throws Exception {
if (args.length < 2) throw new IllegalArgumentException();
if (args.length == 2) MkInheritenceMap(args[0],args[1]) ;
if (args.length == 2) {
MkInheritenceMap(args[0],args[1]) ;
return ;
}
String CUT = args[0] ;
int k = Integer.parseInt(args[2]) ;
......
......@@ -13,7 +13,7 @@ Make use of various 3rd party library; see its libs.
* stdingenCmd (command-line tool): to generate a concrete java-class that
implements a given abstract class. It generates a source file, then compiles
it. Complicated :) Note that in-memoty generated class cannot be use during
it. Complicated :) Note that in-memory generated class cannot be used during
the replay of a test suite! Hence the need to generate source code.
* configure (jar library): to auto configure T3
......
......@@ -127,10 +127,14 @@ public class standinGenCmd {
String standInName = absclass.getSimpleName() + "_StandIn" ;
//String srcpath = "./src" ;
String filename = srcpath + "/" + standInName + ".java" ;
File file = new File(filename);
File file = new File(filename);
String code = mkStandInCode(standInName,absclass) ;
System.out.println(code);
// if (file.exists()) return ; well just overwrite
/*
save(file, mkStandInCode(standInName,absclass)) ;
System.err.println("** a stand-in " + filename + " is created.") ;
......@@ -147,6 +151,7 @@ public class standinGenCmd {
}
System.err.println("** a stand-in " + filename + " is compiled.") ;
*/
}
/**
......@@ -155,8 +160,8 @@ public class standinGenCmd {
* arg[2] : javac command to compile, e.g. javac -cp bla
*/
public static void main(String[] args) throws Exception {
generateStandIn(args[0],args[1],args[2]) ;
// generateStandIn("Sequenic.T3.Examples.Abstractx.Vehicle",".","c:/apps/Java/jdk1.8u31/bin/javac -cp ./bin;../T3_v0/bin") ;
//generateStandIn(args[0],args[1],args[2]) ;
generateStandIn("Sequenic.T3.Examples.InterfaceAndAbstract.Vehicle","~/tmp/t3/standins","javac -cp ./bin:../t3/bin") ;
// generateStandIn("Sequenic.T3.Examples.Abstractx.Vehicle",".","c:/apps/Java/jdk1.8u31/bin/javac -version") ;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment