// todo: need header import org.aspectj.testing.Tester; /** * "Coverage" tests for * PR#476: * Member initializations are run after explicit * constructor calls ("this()" or "super()") when they should be run beforehand. *
Status: * 10 additional variants are defined, 5 of which fail, emitting 30 errors. *
background:
*
The effective order of operation during initialization should be:
*
fault model: * the compiler is inserting member initialization after the explicit * constructor call in the intermediate code. I.e., it produces: *
ThisCall() { * this("correctValue"); * { * this.initString = "INIT"; * this.initNull = null; * }* when it should produce: *
ThisCall() { * this("correctValue");* *
fix model: * Since member initialization must occur before this() call, * and this() must be first in the constructor, * I see no way to implement before advice on member initializers * using preprocessing to produce source code except to put them only * (and always) in the constructors without this() calls. * *
Variants tested in this coverage extension of the original test case: *
Member m = x; or Member m; { m = x; })
* with location variants.
Variants not (yet?) tested: *
super()
rather than this()
Untestable variants: *
super("result: " + member)
* or this("result: " + member)
.
* or this("result: " + super.member)
.
*
* $Id: MemberInitializationsAfterExplicitConstructorCallsCoverage.java,v 1.2 2001/08/03 22:38:49 isberg Exp $
*/
public class MemberInitializationsAfterExplicitConstructorCallsCoverage {
public static final String INPUT = "input";
public static final String INIT = "INIT";
public static void main(String[] args) {
test();
}
public static void test() {
boolean doPassingTests = true;
boolean doFailingTests = true;
//--------- proof that test code is correct
{ ThisCallTopSimple thisCall = new ThisCallTopSimple(1); thisCall.go(); }
//--------- passing test cases
//--- this duplicates original test case
// ThisCall thisCall;
// no constructor call to this
// thisCall = new ThisCall(INPUT);
// thisCall.go();
//--- new coverage tests - 5 tests, 6 errors each, 30 errors
if (doPassingTests) {
{ ThisCallTopSimple thisCall = new ThisCallTopSimple(INPUT); thisCall.go(); }
{ ThisCallTopBlock thisCall = new ThisCallTopBlock(INPUT); thisCall.go(); }
{ ThisCallBottomSimple thisCall = new ThisCallBottomSimple(INPUT); thisCall.go(); }
{ ThisCallBottomBlock thisCall = new ThisCallBottomBlock(INPUT); thisCall.go(); }
{ ThisCallMixed thisCall = new ThisCallMixed(INPUT); thisCall.go(); }
// all super cases pass
{ ThisCallChild thisCall = new ThisCallChild(); thisCall.go(); }
{ ThisCallChild thisCall = new ThisCallChild(2); thisCall.go(); }
{ ThisCallChild thisCall = new ThisCallChild(INPUT); thisCall.go(); }
// enclosed inner class initializer can access enclosing members
{ ThisCallEnclosing.ThisCallEnclosed thisCall
= (new ThisCallEnclosing("ignored")).new ThisCallEnclosed(); }
}
// { ThisCallChild thisCall = new ThisCallChild(); thisCall.go(); }
//--------- failing test cases
//--- duplicate original test case
// fails - constructor call to this
//thisCall = new ThisCall();
//thisCall.go();
//--- new coverage tests
if (doFailingTests) {
{ ThisCallTopSimple thisCall = new ThisCallTopSimple(); thisCall.go(); }
{ ThisCallTopBlock thisCall = new ThisCallTopBlock(); thisCall.go(); }
{ ThisCallBottomSimple thisCall = new ThisCallBottomSimple(); thisCall.go(); }
{ ThisCallBottomBlock thisCall = new ThisCallBottomBlock(); thisCall.go(); }
{ ThisCallMixed thisCall = new ThisCallMixed(); thisCall.go(); }
}
//--------- impossible test cases
//---- unable to test superclass initialization before instance
// { ThisCallChild thisCall = new ThisCallChild((long)1l); thisCall.go(); }
}
/** variant: location top, initializer simpleExpression */
static class ThisCallTopSimple {
/** type primitive, location top, initializer simpleExpression */
int initOne = 1;
/** type String, location top, initializer simpleExpression */
String initString = "INIT";
/** type Object, location top, initializer simpleExpression */
Object initNull = null;
/** type String, location top, initializer none */
String initNone;
/** no bug when calling this directly */
ThisCallTopSimple (String input) {
checkMembersHaveInitializedValues("constructor ThisCallTopSimple(\" + input + \")");
setValues(input);
checkMembersHaveSetValues("constructor ThisCallTopSimple.ThisCallTopSimple(\" + input + \")");
}
void setValues(String input) {
this.initString = input;
this.initNull = input;
this.initNone = input;
this.initOne = 2;
}
/** proof that test code is correct */
ThisCallTopSimple (int ignored) {
checkMembersHaveInitializedValues("constructor ThisCallTopSimple.ThisCallTopSimple(int)");
setValues(INPUT);
checkMembersHaveSetValues("constructor ThisCallTopSimple.ThisCallTopSimple(int)");
}
/** bug when calling this which calls ThisCall(String) */
ThisCallTopSimple () {
this(INPUT);
checkMembersHaveSetValues("constructor ThisCallTopSimple.ThisCallTopSimple()");
}
/** redundant check - same check at end of constructors */
void go() {
checkMembersHaveSetValues("method ThisCallTopSimple.go()");
}
/** the same method for all variants */
protected void checkMembersHaveInitializedValues(String label) {
Tester.checkEqual("INIT", initString, label + " initialized ");
Tester.checkEqual((Object) null, initNull, label + " initialized ");
Tester.checkEqual((Object) null, initNone, label + " initialized ");
Tester.checkEqual(1, initOne, label + " initialized ");
}
/** the same method for all variants */
protected void checkMembersHaveSetValues(String label) {
Tester.checkEqual(2, initOne, label + " set ");
Tester.checkEqual("input", initString, label + " set ");
Tester.checkEqual("input", initNone, label + " set ");
// Object uses strict/reference identity - input dependency
Tester.checkEqual(INPUT, initNull, label + " set ");
}
} // ThisCallTopSimple
/** variant: location top, initializer blockExpression */
static class ThisCallTopBlock {
/** top declarations */
/** type primitive, location top, initializer blockExpression */
int initOne;
/** type String, location top, initializer blockExpression */
String initString;
/** type Object, location top, initializer blockExpression */
Object initNull;
/** type String, location top, initializer none */
String initNone;
/** top initializer block */
{
initOne = 1;
initString = "INIT";
initNull = null;
}
/** no bug when calling this directly */
ThisCallTopBlock (String input) {
checkMembersHaveInitializedValues("constructor ThisCallTopBlock(\" + input + \")");
this.initString = input;
this.initNull = input;
this.initNone = input;
this.initOne = 2;
checkMembersHaveSetValues("constructor ThisCallTopSimple.ThisCall(\" + input + \")");
}
/** bug when calling this which calls ThisCallTopBlock(String) */
ThisCallTopBlock () {
this(INPUT);
checkMembersHaveSetValues("constructor ThisCallTopSimple.ThisCallTopBlock()");
}
/** redundant check - same check at end of constructors */
void go() {
checkMembersHaveSetValues("method ThisCallTopBlock.go()");
}
/** the same method for all variants */
protected void checkMembersHaveInitializedValues(String label) {
Tester.checkEqual("INIT", initString, label + " initialized ");
Tester.checkEqual((Object) null, initNull, label + " initialized ");
Tester.checkEqual((Object) null, initNone, label + " initialized ");
Tester.checkEqual(1, initOne, label + " initialized ");
}
/** the same method for all variants */
protected void checkMembersHaveSetValues(String label) {
Tester.checkEqual(2, initOne, label + " set ");
Tester.checkEqual("input", initString, label + " set ");
Tester.checkEqual("input", initNone, label + " set ");
// Object uses strict/reference identity - input dependency
Tester.checkEqual(INPUT, initNull, label + " set ");
}
} // ThisCallTopBlock
/** variant: location bottom, initializer simpleExpression */
static class ThisCallBottomSimple {
/** no bug when calling this directly */
ThisCallBottomSimple (String input) {
checkMembersHaveInitializedValues("constructor ThisCallBottomSimple(\" + input + \")");
this.initString = input;
this.initNull = input;
this.initNone = input;
this.initOne = 2;
checkMembersHaveSetValues("constructor ThisCallBottomSimple.ThisCallBottomSimple(\" + input + \")");
}
/** bug when calling this which calls ThisCallBottomSimple(String) */
ThisCallBottomSimple () {
this(INPUT);
checkMembersHaveSetValues("constructor ThisCallBottomSimple.ThisCallBottomSimple()");
}
/** redundant check - same check at end of constructors */
void go() {
checkMembersHaveSetValues("method ThisCallBottomSimple.go()");
}
/** the same method for all variants */
protected void checkMembersHaveInitializedValues(String label) {
Tester.checkEqual("INIT", initString, label + " initialized ");
Tester.checkEqual((Object) null, initNull, label + " initialized ");
Tester.checkEqual((Object) null, initNone, label + " initialized ");
Tester.checkEqual(1, initOne, label + " initialized ");
}
/** the same method for all variants */
protected void checkMembersHaveSetValues(String label) {
Tester.checkEqual(2, initOne, label + " set ");
Tester.checkEqual("input", initString, label + " set ");
Tester.checkEqual("input", initNone, label + " set ");
// Object uses strict/reference identity - input dependency
Tester.checkEqual(INPUT, initNull, label + " set ");
}
/** type primitive, location bottom, initializer simpleExpression */
int initOne = 1;
/** type String, location bottom, initializer simpleExpression */
String initString = "INIT";
/** type Object, location bottom, initializer simpleExpression */
Object initNull = null;
/** type String, location bottom, initializer none */
String initNone;
} // ThisCallBottomSimple
/** variant: location bottom, initializer blockExpression */
static class ThisCallBottomBlock {
/** no bug when calling this directly */
ThisCallBottomBlock (String input) {
checkMembersHaveInitializedValues("constructor ThisCallBottomBlock(\" + input + \")");
this.initString = input;
this.initNull = input;
this.initNone = input;
this.initOne = 2;
checkMembersHaveSetValues("constructor ThisCallBottomBlock.ThisCallBottomBlock(\" + input + \")");
}
/** bug when calling this which calls ThisCallBottomBlock(String) */
ThisCallBottomBlock () {
this(INPUT);
checkMembersHaveSetValues("constructor ThisCallBottemBlock.ThisCallBottomBlock()");
}
/** redundant check - same check at end of constructors */
void go() {
checkMembersHaveSetValues("method ThisCallBottomBlock.go()");
}
/** the same method for all variants */
protected void checkMembersHaveInitializedValues(String label) {
Tester.checkEqual("INIT", initString, label + " initialized ");
Tester.checkEqual((Object) null, initNull, label + " initialized ");
Tester.checkEqual((Object) null, initNone, label + " initialized ");
Tester.checkEqual(1, initOne, label + " initialized ");
}
/** the same method for all variants */
protected void checkMembersHaveSetValues(String label) {
Tester.checkEqual(2, initOne, label + " set ");
Tester.checkEqual("input", initString, label + " set ");
Tester.checkEqual("input", initNone, label + " set ");
// Object uses strict/reference identity - input dependency
Tester.checkEqual(INPUT, initNull, label + " set ");
}
/** bottom declarations */
/** type primitive, location bottom, initializer blockExpression */
int initOne;
/** type String, location bottom, initializer blockExpression */
String initString;
/** type Object, location bottom, initializer blockExpression */
Object initNull;
/** type String, location bottom, initializer none */
String initNone;
/** bottom initializer block */
{
initOne = 1;
initString = "INIT";
initNull = null;
}
} // ThisCallBottomBlock
/** variant: location mixed, initializer mixed */
static class ThisCallMixed {
/** type primitive, location top, initializer simpleExpression */
int initOne = 1;
/** type String, location top, initializer simpleExpression */
String initString;
/** no bug when calling this directly */
ThisCallMixed (String input) {
checkMembersHaveInitializedValues("constructor ThisCallMixed(\" + input + \")");
this.initString = input;
this.initNull = input;
this.initNone = input;
this.initOne = 2;
checkMembersHaveSetValues("constructor ThisCallMixed.ThisCallMixed(\" + input + \")");
}
/** bug when calling this which calls ThisCallMixed(String) */
ThisCallMixed () {
this(INPUT);
checkMembersHaveSetValues("constructor ThisCallMixed.ThisCallMixed()");
}
/** redundant check - same check at end of constructors */
void go() {
checkMembersHaveSetValues("method ThisCallMixed.go()");
}
/** the same method for all variants */
protected void checkMembersHaveInitializedValues(String label) {
Tester.checkEqual("INIT", initString, label + " initialized ");
Tester.checkEqual((Object) null, initNull, label + " initialized ");
Tester.checkEqual((Object) null, initNone, label + " initialized ");
Tester.checkEqual(1, initOne, label + " initialized ");
}
/** the same method for all variants */
protected void checkMembersHaveSetValues(String label) {
Tester.checkEqual(2, initOne, label + " set ");
Tester.checkEqual("input", initString, label + " set ");
Tester.checkEqual("input", initNone, label + " set ");
// Object uses strict/reference identity - input dependency
Tester.checkEqual(INPUT, initNull, label + " set ");
}
/** bottom declarations */
/** type String, location bottom, initializer none */
String initNone;
/** type Object, location bottom, initializer blockExpression */
Object initNull;
/** bottom (partial) initializer block */
{
initString = "INIT";
initNull = null;
}
} // ThisCallMixed
static class ThisCallChild extends ThisCallParent {
/** type primitive, location top, initializer simpleExpression */
int initOne = 1;
/** type String, location top, initializer simpleExpression */
String initString = "INIT";
/** type Object, location top, initializer simpleExpression */
Object initNull = null;
/** type String, location top, initializer none */
String initNone;
/** no bug when calling this directly */
ThisCallChild (String input) {
checkMembersHaveInitializedValues("constructor ThisCallChild(\" + input + \")");
setValues(input);
checkMembersHaveSetValues("constructor ThisCallChild.ThisCallChild((\" + input + \")");;
Tester.checkEqual(parentObject, INPUT, "ThisCallChild.ThisCallChild(int ignored)");
}
void setValues(String input) {
this.initString = input;
this.initNull = input;
this.initNone = input;
this.initOne = 2;
}
/**
* @param correctResult
* @param actual
* @param expected
* @param failedResult
* @param testerMessage the String to use for Tester on failure -
* Tester unused if null
* @return correctResult if expected.equals(actual), failedResult otherwise
*/
static private String checkObject(String correctResult
, Object expected
, Object actual
, String failedResult
, String testerMessage) {
if (null == expected) {
if (null == actual) {
return correctResult;
} // else failures fall through
} else if ((null != actual) && (expected.equals(actual))) {
return correctResult;
}
// failures
if (null != testerMessage) {
Tester.checkEqual(actual, expected, testerMessage);
}
return failedResult;
}
/** proof that test code is correct */
ThisCallChild (int ignored) {
checkMembersHaveInitializedValues("constructor ThisCallChild.ThisCallChild(int)");
setValues(INPUT);
checkMembersHaveSetValues("constructor ThisCallChild.ThisCallChild(int)");
Tester.checkEqual(parentObject, INPUT, "ThisCallChild.ThisCallChild(int ignored)");
}
/** no bug when calling this which calls ThisCall(String) */
ThisCallChild () {
super(INPUT);
checkMembersHaveInitializedValues("constructor ThisCallChild.ThisCallChild()");
setValues(INPUT);
checkMembersHaveSetValues("constructor ThisCallChild.ThisCallChild()");
Tester.checkEqual(parentObject, INPUT, "ThisCallChild.ThisCallChild()");
}
private static final String tccsuperlabel =
"ThisCallChild.ThisCallChild(long)/* parent initialization complete before child */";
/** unable to access superclass member state before explicitly invoking constructor */
ThisCallChild (long ignored) {
// this would do the check, but it is illegal
// this(checkObject(INPUT, INPUT, parentObject, tccsuperLabel + "_FAILED", tccsuperlabel));
// this(checkObject(INPUT, INPUT, this$.getParentObject(), tccsuperlabel + "_FAILED", tccsuperlabel));
checkMembersHaveInitializedValues("constructor ThisCallChild.ThisCallChild()");
setValues(INPUT);
checkMembersHaveSetValues("constructor ThisCallChild.ThisCallChild()");
Tester.checkEqual(parentObject, INPUT, "ThisCallChild.ThisCallChild()");
}
/** redundant check - same check at end of constructors */
void go() {
checkMembersHaveSetValues("method ThisCallChild.go()");
Tester.checkEqual(parentObject, INPUT, "ThisCallChild.go()");
}
/** the same method for all variants */
protected void checkMembersHaveInitializedValues(String label) {
Tester.checkEqual("INIT", initString, label + " initialized ");
Tester.checkEqual((Object) null, initNull, label + " initialized ");
Tester.checkEqual((Object) null, initNone, label + " initialized ");
Tester.checkEqual(1, initOne, label + " initialized ");
}
/** the same method for all variants */
protected void checkMembersHaveSetValues(String label) {
Tester.checkEqual(2, initOne, label + " set ");
Tester.checkEqual("input", initString, label + " set ");
Tester.checkEqual("input", initNone, label + " set ");
// Object uses strict/reference identity - input dependency
Tester.checkEqual(INPUT, initNull, label + " set ");
}
}
static class ThisCallParent {
/** not available to in child explicit constructor parameter expression */
protected Object parentObject = INIT;
/** not available to in child explicit constructor parameter expression */
protected Object getParentObject() { return parentObject; }
/** no bug here */
ThisCallParent() {
Tester.checkEqual(parentObject, INIT, "ThisCallParent.ThisCallParent()");
parentObject = INPUT;
}
/** no bug here */
ThisCallParent(String input) {
Tester.checkEqual(parentObject, INIT, "ThisCallParent.ThisCallParent(\"" + input + "\")");
parentObject = input;
}
}
} // MemberInitializationsAfterExplicitConstructorCallsCoverage
/** variant: location enclosing */
class ThisCallEnclosing {
public static final String INPUT = "input";
public static final String INIT = "INIT";
String initString = INIT;
String constructedString;
public ThisCallEnclosed getEnclosed() {
return new ThisCallEnclosed();
}
/** no bug when calling this directly */
ThisCallEnclosing (String ignored) {
constructedString = INPUT;
initString = INPUT;
}
public class ThisCallEnclosed {
boolean didCheck;
{
// check enclosing instance in initializer
Tester.checkEqual(INPUT, initString, "ThisCallEnclosed.