Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
Tobias Stamann committed Jan 26, 2024
2 parents 00862fb + a0146f4 commit d79ceaa
Show file tree
Hide file tree
Showing 31 changed files with 352 additions and 23 deletions.
2 changes: 1 addition & 1 deletion coverage/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<parent>
<groupId>io.toolisticon.cute</groupId>
<artifactId>cute-parent</artifactId>
<version>1.0.0_RC1</version>
<version>1.0.0_RC2</version>
</parent>

<name>coverage</name>
Expand Down
2 changes: 1 addition & 1 deletion cute/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>io.toolisticon.cute</groupId>
<artifactId>cute-parent</artifactId>
<version>1.0.0_RC1</version>
<version>1.0.0_RC2</version>
</parent>

<name>cute</name>
Expand Down
27 changes: 27 additions & 0 deletions cute/src/main/java/io/toolisticon/cute/CompileTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,20 @@ public void executeTest() {
// Check messages
checkMessages(compilationResult.getDiagnostics());

// Check compiled classes
for (GeneratedClassesTest generatedClassesTest : this.compileTestConfiguration.getGeneratedClassesTest()) {
CuteClassLoader cuteClassLoader = new CuteClassLoaderImpl(compilationResult.getCompileTestFileManager());

try {
generatedClassesTest.doTests(cuteClassLoader);
} catch (AssertionError e) {
throw e;
} catch (Exception e) {
throw new FailingAssertionException(Constants.Messages.MESSAGE_GOT_UNEXPECTED_EXCEPTION_DURING_CLASS_TEST_ERROR.produceMessage(generatedClassesTest, e.getMessage()));
}
}

// Check generated JavaFileObjects
for (CuteApi.GeneratedJavaFileObjectCheckBB generatedJavaFileObjectCheck : this.compileTestConfiguration.javaFileObjectChecks()) {
if (CuteApi.FileObjectCheckType.EXISTS.equals(generatedJavaFileObjectCheck.getCheckType())) {
if (!compilationResult.getCompileTestFileManager().existsExpectedJavaFileObject(generatedJavaFileObjectCheck.getLocation(), generatedJavaFileObjectCheck.getClassName(), generatedJavaFileObjectCheck.getKind())) {
Expand Down Expand Up @@ -109,6 +122,20 @@ public void executeTest() {

}

// Check with test
if (generatedJavaFileObjectCheck.getGeneratedClassesTest() != null) {
CuteClassLoader cuteClassLoader = new CuteClassLoaderImpl(compilationResult.getCompileTestFileManager());

try {
generatedJavaFileObjectCheck.getGeneratedClassesTest().doTests(cuteClassLoader.getClass(generatedJavaFileObjectCheck.getClassName()), cuteClassLoader);
} catch (AssertionError e) {
throw e;
} catch (Exception e) {
throw new FailingAssertionException(Constants.Messages.MESSAGE_GOT_UNEXPECTED_EXCEPTION_DURING_CLASS_TEST_ERROR.produceMessage(getJavaFileObjectInfoString(generatedJavaFileObjectCheck), e.getMessage()));
}
}


} catch (IOException e) {
// ignore
}
Expand Down
2 changes: 2 additions & 0 deletions cute/src/main/java/io/toolisticon/cute/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ public String getMessagePattern(){

public final static Message MESSAGE_TECHNICAL_ERROR = new Message("TECHNICAL ERROR : %s");

public final static Message MESSAGE_GOT_UNEXPECTED_EXCEPTION_DURING_CLASS_TEST_ERROR = new Message("Unexpected Exception happened during test of generated class %s : %s");


// IllegalArgumentException Messages
public final static Message IAE_PASSED_PARAMETER_MUST_NOT_BE_NULL = new Message("Passed %s must not be null");
Expand Down
37 changes: 34 additions & 3 deletions cute/src/main/java/io/toolisticon/cute/CuteApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ public interface CompilerTestBB {
@FluentApiBackingBeanField("fileObjectChecks")
List<GeneratedFileObjectCheckBB> fileObjectChecks();

@FluentApiBackingBeanField("generatedClassesTest")
List<GeneratedClassesTest> getGeneratedClassesTest();

default long countErrorMessageChecks() {
long count = 0;

Expand Down Expand Up @@ -195,6 +198,9 @@ public interface GeneratedJavaFileObjectCheckBB {

@FluentApiBackingBeanField("generatedFileObjectMatcher")
GeneratedFileObjectMatcher getGeneratedFileObjectMatcher();

@FluentApiBackingBeanField("generatedClassesTest")
GeneratedClassesTestForSpecificClass getGeneratedClassesTest();
}

@FluentApiBackingBean
Expand Down Expand Up @@ -819,11 +825,10 @@ public interface BlackBoxTestOutcomeInterface {
CompilerTestExpectAndThatInterface compilationFails();



}

@FluentApiInterface(CompilerTestBB.class)
public interface UnitTestOutcomeInterface extends BlackBoxTestOutcomeInterface{
public interface UnitTestOutcomeInterface extends BlackBoxTestOutcomeInterface {

/**
* Expect an Exception to be thrown
Expand All @@ -837,7 +842,6 @@ public interface UnitTestOutcomeInterface extends BlackBoxTestOutcomeInterface{
}



@FluentApiInterface(CompilerTestBB.class)
public interface CompilerTestExpectAndThatInterface {

Expand All @@ -859,6 +863,18 @@ public interface CompilerTestExpectAndThatInterface {
@FluentApiInterface(CompilerTestBB.class)
public interface CompilerTestExpectThatInterface {

/**
* Sometimes it can become handy to even test the generated code.
* This method can used to do those tests. Compiled classes are provided via the {@link GeneratedClassesTest} interface.
* Be aware that the binary class names must be used to get classes ( '$' delimiter for inner types,...)
* Test rely heavily on reflection api.
* So please consider integration test projects for testing generated code if your code doesn't implement a precompiled interface.
* @param generatedClassesTest the test to execute
* @return the next interface
*/
CompilerTestExpectAndThatInterface compiledClassesTestsSucceeds(@FluentApiBackingBeanMapping(value = "generatedClassesTest", action = MappingAction.ADD) GeneratedClassesTest generatedClassesTest);


/**
* Adds check that generated class exists or doesn't exist.
*
Expand Down Expand Up @@ -1067,6 +1083,21 @@ default CompilerTestExpectAndThatInterface matches(ExpectedFileObjectMatcherKind
@FluentApiParentBackingBeanMapping(value = "javaFileObjectChecks", action = MappingAction.ADD)
CompilerTestExpectAndThatInterface matches(@FluentApiBackingBeanMapping(value = "generatedFileObjectMatcher") GeneratedFileObjectMatcher generatedJavaFileObjectCheck);


/**
* Sometimes it can become handy to even test the generated code.
* This method can used to do those tests. Compiled classes are provided via the {@link GeneratedClassesTest} interface.
* Be aware that the binary class names must be used to get classes ( '$' delimiter for inner types,...)
* Test rely heavily on reflection api.
* So please consider integration test projects for testing generated code if your code doesn't implement a precompiled interface.
* @param generatedClassesTest the test to execute
* @return the next interface
*/
@FluentApiImplicitValue(id = "checkType", value = "EXISTS")
@FluentApiParentBackingBeanMapping(value = "javaFileObjectChecks", action = MappingAction.ADD)
CompilerTestExpectAndThatInterface testedSuccessfullyBy(@FluentApiBackingBeanMapping(value = "generatedClassesTest") GeneratedClassesTestForSpecificClass generatedClassesTest);


}

@FluentApiInterface(GeneratedFileObjectCheckBB.class)
Expand Down
7 changes: 7 additions & 0 deletions cute/src/main/java/io/toolisticon/cute/CuteClassLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.toolisticon.cute;

public interface CuteClassLoader {

<TYPE> Class<TYPE> getClass(String binaryClassName) throws ClassNotFoundException;

}
86 changes: 86 additions & 0 deletions cute/src/main/java/io/toolisticon/cute/CuteClassLoaderImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package io.toolisticon.cute;

import javax.tools.JavaFileObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

class CuteClassLoaderImpl extends ClassLoader implements CuteClassLoader {

final CompileTestFileManager compileTestFileManager;

final Map<String, Class<?>> classMap = new HashMap<>();

public CuteClassLoaderImpl(CompileTestFileManager compileTestFileManager) {
this.compileTestFileManager = compileTestFileManager;

// get classes
List<JavaFileObject> javaFileObjectList = compileTestFileManager.getGeneratedJavaFileObjects().stream()
.filter(e -> e.getKind() == JavaFileObject.Kind.CLASS)
.collect(Collectors.toList());

for (JavaFileObject javaFileObject : javaFileObjectList) {
try {
byte[] byteArray = readAllBytes(javaFileObject.openInputStream());
String className = convertJavaFileObjectNameToBinaryClassName(javaFileObject);
classMap.put(className, defineClass(className, byteArray, 0, byteArray.length));
} catch (IOException e) {
// Ignore for the moment
}
}

}

private static byte[] readAllBytes(InputStream inputStream) throws IOException {
final int bufLen = 4 * 0x400; // 4KB
byte[] buf = new byte[bufLen];
int readLen;
IOException exception = null;

try {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
while ((readLen = inputStream.read(buf, 0, bufLen)) != -1)
outputStream.write(buf, 0, readLen);

return outputStream.toByteArray();
}
} catch (IOException e) {
exception = e;
throw e;
} finally {
if (exception == null) inputStream.close();
else try {
inputStream.close();
} catch (IOException e) {
exception.addSuppressed(e);
}
}
}

static String convertJavaFileObjectNameToBinaryClassName(JavaFileObject javaFileObject) {
Pattern pattern = Pattern.compile("^[/](.*)[.]class$");
Matcher matcher = pattern.matcher(javaFileObject.getName());
if (matcher.matches()) {
return matcher.group(1).replaceAll("[/]",".");
}
throw new IllegalStateException("Got invalid name : " + javaFileObject.getName());
}

@Override
public <TYPE> Class<TYPE> getClass(String binaryClassName) throws ClassNotFoundException {


Class<TYPE> clazz = (Class<TYPE>) this.classMap.get(binaryClassName);

if(clazz == null){
throw new ClassNotFoundException(binaryClassName);
}
return clazz;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.toolisticon.cute;

public interface GeneratedClassesTest {

void doTests(CuteClassLoader cuteClassLoader) throws Exception;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.toolisticon.cute;

public interface GeneratedClassesTestForSpecificClass {

void doTests(Class<?> clazz, CuteClassLoader cuteClassLoader) throws Exception;

}
116 changes: 116 additions & 0 deletions cute/src/test/java/io/toolisticon/cute/CuteTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.toolisticon.cute;

import io.toolisticon.cute.common.SimpleTestProcessor1;
import io.toolisticon.cute.testcases.SimpleTestInterface;
import io.toolisticon.fluapigen.validation.api.ValidatorException;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
Expand Down Expand Up @@ -969,5 +970,120 @@ public void blackBoxTest_executeTest_WithoutChecks_FailedCompilation() {
throw new AssertionError("Should have got Assertion error that compilation was expected to be successful but failed");
}

@Test()
public void blackBoxTest_justCompileCodeAndDoClassTests() {
Cute.blackBoxTest().given().processors()
.andSourceFiles("/TestClass.java")
.whenCompiled()
.thenExpectThat()
.compilationSucceeds()
.andThat().generatedClass("io.toolisticon.cute.TestClass").testedSuccessfullyBy(new GeneratedClassesTestForSpecificClass() {
@Override
public void doTests(Class<?> clazz,CuteClassLoader cuteClassLoader) throws Exception{
MatcherAssert.assertThat(clazz.getCanonicalName(),Matchers.is("io.toolisticon.cute.TestClass"));

Object instance = clazz.getConstructor().newInstance();
MatcherAssert.assertThat(instance, Matchers.notNullValue());
}
})
.executeTest();
}

@Test()
public void blackBoxTest_justCompileCodeAndDoClassTests2() {
Cute.blackBoxTest().given().processors()
.andSourceFiles("/TestClassWithInnerClasses.java")
.whenCompiled()
.thenExpectThat()
.compilationSucceeds()
.andThat().generatedClass("io.toolisticon.cute.TestClassWithInnerClasses").testedSuccessfullyBy(new GeneratedClassesTestForSpecificClass() {
@Override
public void doTests(Class<?>clazz, CuteClassLoader cuteClassLoader) throws Exception{
MatcherAssert.assertThat(clazz.getCanonicalName(),Matchers.is("io.toolisticon.cute.TestClassWithInnerClasses"));

Class<?> innerClazz = cuteClassLoader.getClass("io.toolisticon.cute.TestClassWithInnerClasses$InnerClass");
MatcherAssert.assertThat(innerClazz.getCanonicalName(),Matchers.is("io.toolisticon.cute.TestClassWithInnerClasses.InnerClass"));

Class<?> staticInnerClazz = cuteClassLoader.getClass("io.toolisticon.cute.TestClassWithInnerClasses$StaticInnerClass");
MatcherAssert.assertThat(staticInnerClazz.getCanonicalName(),Matchers.is("io.toolisticon.cute.TestClassWithInnerClasses.StaticInnerClass"));

Class<?> innerInterface = cuteClassLoader.getClass("io.toolisticon.cute.TestClassWithInnerClasses$InnerInterface");
MatcherAssert.assertThat(innerInterface.getCanonicalName(),Matchers.is("io.toolisticon.cute.TestClassWithInnerClasses.InnerInterface"));

Object instance = clazz.getConstructor().newInstance();
MatcherAssert.assertThat(instance, Matchers.notNullValue());

}
})
.executeTest();
}

@Test()
public void blackBoxTest_justCompileCodeAndDoClassTest3() {
Cute.blackBoxTest().given().processors()
.andSourceFiles("/TestClassWithInnerClasses.java")
.whenCompiled()
.thenExpectThat()
.compilationSucceeds()
.andThat().compiledClassesTestsSucceeds(new GeneratedClassesTest() {
@Override
public void doTests(CuteClassLoader cuteClassLoader) throws Exception{
Class<?> clazz = cuteClassLoader.getClass("io.toolisticon.cute.TestClassWithInnerClasses");
MatcherAssert.assertThat(clazz.getCanonicalName(),Matchers.is("io.toolisticon.cute.TestClassWithInnerClasses"));

Class<?> innerClazz = cuteClassLoader.getClass("io.toolisticon.cute.TestClassWithInnerClasses$InnerClass");
MatcherAssert.assertThat(innerClazz.getCanonicalName(),Matchers.is("io.toolisticon.cute.TestClassWithInnerClasses.InnerClass"));

Class<?> staticInnerClazz = cuteClassLoader.getClass("io.toolisticon.cute.TestClassWithInnerClasses$StaticInnerClass");
MatcherAssert.assertThat(staticInnerClazz.getCanonicalName(),Matchers.is("io.toolisticon.cute.TestClassWithInnerClasses.StaticInnerClass"));

Class<?> innerInterface = cuteClassLoader.getClass("io.toolisticon.cute.TestClassWithInnerClasses$InnerInterface");
MatcherAssert.assertThat(innerInterface.getCanonicalName(),Matchers.is("io.toolisticon.cute.TestClassWithInnerClasses.InnerInterface"));

Object instance = clazz.getConstructor().newInstance();
MatcherAssert.assertThat(instance, Matchers.notNullValue());

}
})
.executeTest();
}

@Test()
public void blackBoxTest_justCompileCodeAndDoClassTest4() {
Cute.blackBoxTest().given().processors()
.andSourceFiles("/TestClassWithInnerClasses.java")
.whenCompiled()
.thenExpectThat()
.compilationSucceeds()
.andThat().generatedClass("io.toolisticon.cute.TestClassWithInnerClasses$InnerClass").testedSuccessfullyBy(new GeneratedClassesTestForSpecificClass() {
@Override
public void doTests( Class<?> innerClazz, CuteClassLoader cuteClassLoader) throws Exception{

MatcherAssert.assertThat(innerClazz.getCanonicalName(),Matchers.is("io.toolisticon.cute.TestClassWithInnerClasses.InnerClass"));


}
})
.executeTest();
}

@Test()
public void blackBoxTest_justCompileCodeAndDoClassTestWithImplementedInterface() {
Cute.blackBoxTest().given().noProcessors()
.andSourceFiles("/TestClassWithImplementedInterface.java")
.whenCompiled()
.thenExpectThat()
.compilationSucceeds()
.andThat().generatedClass("io.toolisticon.cute.TestClassWithImplementedInterface").testedSuccessfullyBy(new GeneratedClassesTestForSpecificClass() {
@Override
public void doTests(Class<?> clazz, CuteClassLoader cuteClassLoader) throws Exception{

SimpleTestInterface unit = (SimpleTestInterface) clazz.getConstructor().newInstance();
MatcherAssert.assertThat(unit.saySomething(), Matchers.is("WHATS UP?"));

}
})
.executeTest();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.toolisticon.cute.testcases;

public interface SimpleTestInterface {
String saySomething();
}
Loading

0 comments on commit d79ceaa

Please sign in to comment.