Skip to content

Commit

Permalink
Added unit test to compile add generated class.
Browse files Browse the repository at this point in the history
  • Loading branch information
li-ukumar committed Sep 7, 2023
1 parent 4640b55 commit f6843e5
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 2 deletions.
2 changes: 2 additions & 0 deletions avro-builder/tests/tests-allavro/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ dependencies {
exclude group: "org.slf4j"
}

testImplementation project(":test-common")

avro15 "org.apache.avro:avro:1.5.4"
avro15 "org.apache.avro:avro-compiler:1.5.4"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@
import com.linkedin.avroutil1.compatibility.RandomRecordGenerator;
import com.linkedin.avroutil1.compatibility.RecordGenerationConfig;
import com.linkedin.avroutil1.compatibility.StringConverterUtil;
import com.linkedin.avroutil1.testcommon.CommonCompilerHelper;
import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -1829,6 +1833,29 @@ public void testNewBuilder() throws Exception {
compareIndexedRecords(instance, builder.build());
}

@DataProvider
private Object[][] testGeneratedCompilationProvider(){
String projectPath = System.getProperty("user.dir");
List<String> list = Arrays.stream(new File(projectPath + "/../").list(new FilenameFilter() {
@Override
public boolean accept(File current, String name) {
return name.matches("codegen-[\\w\\-]+") && new File(current, name).isDirectory();
}
})).map(dir -> projectPath + "/../" + dir + "/build/generated/sources/avro").collect(Collectors.toList());

Object[][] res = new Object[list.size()][1];

for(int i = 0; i< list.size(); i++) {
res[i][0] = list.get(i);
}
return res;
}

@Test(dataProvider = "testGeneratedCompilationProvider")
public void testGeneratedCompilation(String genPath) throws Exception {
CommonCompilerHelper.assertCompiles(Paths.get(genPath));
}

@BeforeClass
public void setup() {
System.setProperty("org.apache.avro.specific.use_custom_coders", "true");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import com.linkedin.avroutil1.model.AvroRecordSchema;
import com.linkedin.avroutil1.parser.avsc.AvscParseResult;
import com.linkedin.avroutil1.parser.avsc.AvscParser;
import com.linkedin.avroutil1.testcommon.CommonCompilerHelper;
import com.linkedin.avroutil1.testcommon.TestUtil;
import com.linkedin.avroutil1.testutil.CompilerHelper;
import java.io.IOException;
import javax.tools.JavaFileObject;
import org.testng.Assert;
Expand Down Expand Up @@ -56,7 +56,7 @@ public void testSchema(String path, boolean checkCompiles) throws Exception {
generator.generateSpecificClass(schema, SpecificRecordGenerationConfig.BROAD_COMPATIBILITY);

if (checkCompiles) {
CompilerHelper.assertCompiles(javaFileObject);
CommonCompilerHelper.assertCompiles(javaFileObject);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@

/**
* utility class for dealing with the java compiler in unit tests
* @deprecated Use test-common -> CommonCompilerHelper
*/
@Deprecated
public class CompilerHelper {

public static void assertCompiles(JavaFileObject sourceFile) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*
* Copyright 2023 LinkedIn Corp.
* Licensed under the BSD 2-Clause License (the "License").
* See License in the project root for license information.
*/

package com.linkedin.avroutil1.testcommon;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import org.apache.commons.io.IOUtils;
import org.testng.Assert;


/**
* utility class for dealing with the java compiler in unit tests
*/
public class CommonCompilerHelper {

public static void assertCompiles(JavaFileObject sourceFile) throws Exception {
List<JavaFileObject> fileObjects = Arrays.asList(sourceFile);
assertCompiles(fileObjects);
}

public static void assertCompiles(Path sourceRoot) throws Exception {
List<JavaFileObject> fileObjects = listSourceFiles(sourceRoot);
assertCompiles(fileObjects);
}

public static void assertCompiles(List<JavaFileObject> fileObjects) throws Exception {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
ClassFileManager manager = new ClassFileManager(compiler.getStandardFileManager(null, null, null));
CompilationListener listener = new CompilationListener();
JavaCompiler.CompilationTask compilationTask = compiler.getTask(null, manager, listener, null, null, fileObjects);
Boolean success = compilationTask.call();

if (!Boolean.TRUE.equals(success) || !listener.errors.isEmpty()) {
Assert.fail("failed to compile code: " + listener.errors);
}
}

private static List<JavaFileObject> listSourceFiles(Path root) throws IOException {
List<JavaFileObject> fileObjects = new ArrayList<>();
//noinspection Convert2Diamond because java 8 isnt smart enough to figure this out
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (attrs.isRegularFile()) {
fileObjects.add(new JavaSourceFile(file));
}
return FileVisitResult.CONTINUE;
}
});
return fileObjects;
}

private static String summarize(Diagnostic<? extends JavaFileObject> diagnostic) {
StringBuilder sb = new StringBuilder();
sb.append(diagnostic.getKind()).append(": ");
JavaFileObject sourceObject = diagnostic.getSource();
if (sourceObject != null) {
sb.append("file ").append(sourceObject.toString()).append(" ");
}
String message = diagnostic.getMessage(Locale.ROOT);
if (message != null && !message.isEmpty()) {
sb.append(message).append(" ");
}
long line = diagnostic.getLineNumber();
long column = diagnostic.getColumnNumber();
if (line != -1 || column != -1) {
sb.append("at line ").append(line).append(" column ").append(column);
}
return sb.toString().trim();
}

private static class ClassFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
private final Map<String, JavaClassObject> classObjects = new ConcurrentHashMap<>();

ClassFileManager(StandardJavaFileManager m) {
super(m);
}

@Override
public JavaFileObject getJavaFileForOutput(
Location location,
String className,
JavaFileObject.Kind kind,
FileObject sibling
) throws IOException {
if (kind != JavaFileObject.Kind.CLASS) {
throw new UnsupportedOperationException("unhandled kind " + kind);
}
return classObjects.computeIfAbsent(className, s -> new JavaClassObject((JavaFileObject) sibling, className));
}

}

private static class JavaSourceFile extends SimpleJavaFileObject {
private final Path file;

JavaSourceFile(Path file) {
super(file.toUri(), Kind.SOURCE);
this.file = file;
}

@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return IOUtils.toString(file.toUri(), StandardCharsets.UTF_8);
}

@Override
public String toString() {
return file.getFileName().toString();
}
}

private static class JavaClassObject extends SimpleJavaFileObject {
private final JavaFileObject sourceFile;
private final String fqcn;
private volatile ByteArrayOutputStream outputStream;

public JavaClassObject(JavaFileObject sourceFile, String fqcn) {
super(URI.create("mem:///" + fqcn.replace('.', '/')), Kind.CLASS);
this.sourceFile = sourceFile;
this.fqcn = fqcn;
}

@Override
public synchronized OutputStream openOutputStream() {
if (outputStream == null) {
outputStream = new ByteArrayOutputStream();
}
return outputStream;
}
}

private static class CompilationListener implements DiagnosticListener<JavaFileObject> {
private final List<String> warnings = new ArrayList<>();
private final List<String> errors = new ArrayList<>();

@Override
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
Diagnostic.Kind kind = diagnostic.getKind();
String desc = summarize(diagnostic);
switch (kind) {
case NOTE:
case WARNING:
case MANDATORY_WARNING:
warnings.add(desc);
break;
case ERROR:
case OTHER:
errors.add(desc);
break;
default:
throw new IllegalStateException("unhandled " + kind);
}
}
}
}

0 comments on commit f6843e5

Please sign in to comment.