All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.binis.codegen.test.BaseTest Maven / Gradle / Ivy

There is a newer version: 1.2.22
Show newest version
package net.binis.codegen.test;

/*-
 * #%L
 * code-generator
 * %%
 * Copyright (C) 2021 Binis Belev
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.binis.codegen.CodeGen;
import net.binis.codegen.generation.core.Generator;
import net.binis.codegen.generation.core.Helpers;
import net.binis.codegen.generation.core.interfaces.PrototypeDescription;
import net.binis.codegen.javaparser.CodeGenPrettyPrinter;
import net.binis.codegen.tools.Reflection;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;

import javax.tools.*;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;

import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static net.binis.codegen.generation.core.CompileHelper.createFileManager;
import static net.binis.codegen.generation.core.CompileHelper.getCompilationUnits;
import static net.binis.codegen.generation.core.Helpers.*;
import static net.binis.codegen.tools.Tools.ifNull;
import static net.binis.codegen.tools.Tools.with;
import static org.junit.Assert.*;

@Slf4j
public abstract class BaseTest {

    protected JavaParser parser = new JavaParser();

    protected String getAsString(CompilationUnit file) {
        var printer = new CodeGenPrettyPrinter();

        sortImports(file);
        if (file.getType(0).isClassOrInterfaceDeclaration()) {
            sortClass(file.getType(0).asClassOrInterfaceDeclaration());
        }

        return printer.print(file);
    }

    protected void generate() {
        var parsed = new ArrayList<>(lookup.parsed());
        for (var entry : parsed) {
            ifNull(entry.getFiles(), () ->
                    Generator.generateCodeForClass(entry.getDeclaration().findCompilationUnit().get(), entry));
        }

        lookup.calcPrototypeMaps();

        with(lookup.parsed().stream().filter(PrototypeDescription::isValid).sorted(Helpers::sortForEnrich).collect(Collectors.toList()), list -> {
            list.forEach(Helpers::handleEnrichers);
            list.forEach(Helpers::finalizeEnrichers);
            list.forEach(Helpers::postProcessEnrichers);
        });
    }

    protected void cleanUp() {
        Helpers.cleanUp();
    }

    protected void load(List> list, String resource) {
        var source = resourceAsString(resource);

        var parse = parser.parse(source);
        assertTrue(parse.toString(), parse.isSuccessful());
        if (nonNull(list)) {
            list.add(Pair.of(parse.getResult().get().findFirst(ClassOrInterfaceDeclaration.class).get().getFullyQualifiedName().get(), source));
        }
        parse.getResult().ifPresent(u ->
                u.getTypes().forEach(t ->
                        CodeGen.handleType(parser, t, resource)));
    }

    protected void loadExecute(List> list, String resource) {
        var source = resourceAsString(resource);

        if (nonNull(list)) {
            list.add(Pair.of("net.binis.codegen.Execute", source));
        }

    }

    protected void compare(CompilationUnit unit, String resource) {
        if (nonNull(resource)) {
            assertEquals(resource, resourceAsString(resource), getAsString(unit));
        }
    }

    protected void testSingleExecute(String prototype, String resClass, String resInterface, String resExecute) {
        testSingleExecute(prototype, resClass, resInterface, null, 1, resExecute);
    }

    protected void testSingleExecute(String prototype, String resClass, String resInterface, int expected, String resExecute) {
        testSingleExecute(prototype, resClass, resInterface, null, expected, resExecute);
    }


    protected void testSingle(String prototype, String resClass, String resInterface) {
        testSingle(prototype, resClass, resInterface, null, 1);
    }

    protected void testSingle(String prototype, String resClass, String resInterface, String pathToSave) {
        testSingle(prototype, resClass, resInterface, pathToSave, 1);
    }

    protected void testSingle(String prototype, String resClass, String resInterface, int expected) {
        testSingle(prototype, resClass, resInterface, null, expected);
    }

    protected void testSingle(String prototype, String resClass, String resInterface, String pathToSave, int expected) {
        testSingleExecute(prototype, resClass, resInterface, pathToSave, expected, null);
    }

    protected void testSingleExecute(String prototype, String resClass, String resInterface, String pathToSave, int expected, String resExecute) {
        var list = newList();
        load(list, prototype);
        assertTrue(compile(new TestClassLoader(), list, null));
        generate();

        assertEquals(expected, lookup.parsed().size());

        list = newList();
        for (var parsed : lookup.generated()) {
            if (isNull(parsed.getCompiled()) && (!parsed.isNested() || isNull(parsed.getParentClassName()))) {
                if (nonNull(pathToSave)) {
                    save(parsed.getProperties().getClassName(), parsed.getFiles().get(0), pathToSave);
                    save(parsed.getProperties().getInterfaceName(), parsed.getFiles().get(1), pathToSave);
                }

                compare(parsed.getFiles().get(1), resInterface);
                compare(parsed.getFiles().get(0), resClass);
            }

            if (!parsed.isNested() || isNull(parsed.getParentClassName())) {
                if (!classExists(parsed.getInterfaceFullName())) {
                    list.add(Pair.of(parsed.getInterfaceFullName(), getAsString(parsed.getFiles().get(1))));
                }
                if (!classExists(parsed.getParsedFullName())) {
                    list.add(Pair.of(parsed.getParsedFullName(), getAsString(parsed.getFiles().get(0))));
                }
            }

        }
        var loader = new TestClassLoader();

        assertTrue(compile(loader, list, resExecute));


    }

    protected void testMulti(List> files) {
        testMulti(files, null);
    }

    protected void testMulti(List> files, int expected) {
        testMultiExecute(files, expected, null, null);
    }

    protected void testMulti(List> files, String pathToSave) {
        testMultiExecute(files, files.size(), pathToSave, null);
    }

    protected void testMultiExecute(List> files, String resExecute) {
        testMultiExecute(files, files.size(), null, resExecute);
    }

    protected void testMultiExecute(List> files, int expected, String pathToSave, String resExecute) {
        var list = newList();
        files.forEach(t ->
                load(list, t.getLeft()));
        assertTrue(compile(new TestClassLoader(), list, null));
        generate();

        assertEquals(expected, lookup.parsed().size());


        var compileList = new ArrayList>();
        files.forEach(f ->
                lookup.findGeneratedByFileName(f.getLeft()).forEach(parsed -> {
                    compare(parsed.getFiles().get(1), f.getRight());
                    compare(parsed.getFiles().get(0), f.getMiddle());

                    if (nonNull(pathToSave)) {
                        if (isNull(parsed.getMixIn())) {
                            save(parsed.getProperties().getClassName(), parsed.getFiles().get(0), pathToSave);
                        }
                        save(parsed.getProperties().getInterfaceName(), parsed.getFiles().get(1), pathToSave);
                    }

                    if (isNull(parsed.getMixIn())) {
                        if (!parsed.isNested() || isNull(parsed.getParentClassName())) {
                            compileList.add(Pair.of(parsed.getInterfaceFullName(), getAsString(parsed.getFiles().get(1))));
                            compileList.add(Pair.of(parsed.getParsedFullName(), getAsString(parsed.getFiles().get(0))));
                        }
                    } else {
                        for (var i = 0; i < compileList.size(); i++) {
                            if (compileList.get(i).getKey().equals(parsed.getMixIn().getParsedFullName())) {
                                compileList.add(i, Pair.of(parsed.getInterfaceFullName(), getAsString(parsed.getFiles().get(1))));
                                break;
                            }
                        }
                    }
                }));

        var loader = new TestClassLoader();
        assertTrue(compile(loader, compileList, resExecute));
    }

    @SneakyThrows
    protected void save(String name, CompilationUnit unit, String pathToSave) {
        var fileName = pathToSave + "/" + name + ".java";
        BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
        writer.write(getAsString(unit));
        writer.close();
    }

    protected void testSingleWithBase(String basePrototype, String baseClassName, String prototype, String className, String baseClass, String baseInterface, String resClass, String resInterface) {
        var src = newList();
        load(src, basePrototype);
        load(src, prototype);
        assertTrue(compile(new TestClassLoader(), src, null));
        generate();

        assertEquals(2, lookup.parsed().size());

        var list = newList();

        with(lookup.findGenerated(baseClassName), parsed -> {
            compare(parsed.getFiles().get(0), baseClass);
            compare(parsed.getFiles().get(1), baseInterface);

            list.add(Pair.of(parsed.getInterfaceFullName(), getAsString(parsed.getFiles().get(1))));
            list.add(Pair.of(parsed.getParsedFullName(), getAsString(parsed.getFiles().get(0))));
        });

        with(lookup.findGenerated(className), parsed -> {
            compare(parsed.getFiles().get(0), resClass);
            compare(parsed.getFiles().get(1), resInterface);

            list.add(Pair.of(parsed.getInterfaceFullName(), getAsString(parsed.getFiles().get(1))));
            list.add(Pair.of(parsed.getParsedFullName(), getAsString(parsed.getFiles().get(0))));
        });

        assertTrue(compile(new TestClassLoader(), list, null));
    }

    protected void testSingleWithMixIn(String basePrototype, String baseClassName, String prototype, String className, String baseClass, String baseInterface, String mixInInterface) {
        var src = newList();
        load(src, basePrototype);
        load(src, prototype);
        assertTrue(compile(new TestClassLoader(), src, null));
        generate();

        assertEquals(2, lookup.parsed().size());

        var list = newList();

        with(lookup.findGenerated(baseClassName), parsed ->
                with(lookup.findGenerated(className), parsedMixIn -> {
                    compare(parsed.getFiles().get(1), baseInterface);
                    compare(parsedMixIn.getFiles().get(1), mixInInterface);
                    compare(parsed.getFiles().get(0), baseClass);

                    list.add(Pair.of(parsed.getInterfaceFullName(), getAsString(parsed.getFiles().get(1))));
                    list.add(Pair.of(parsedMixIn.getInterfaceFullName(), getAsString(parsedMixIn.getFiles().get(1))));
                    list.add(Pair.of(parsed.getParsedFullName(), getAsString(parsed.getFiles().get(0))));
                }));

        assertTrue(compile(new TestClassLoader(), list, null));
    }

    @SneakyThrows
    protected boolean compile(TestClassLoader loader, List> files, String resExecute) {
        if (nonNull(resExecute)) {
            loadExecute(files, resExecute);
        }

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        DiagnosticCollector diagnostics =
                new DiagnosticCollector<>();

        var objects = files.stream().collect(Collectors.toMap(Pair::getKey, f -> new JavaByteObject(f.getKey())));

        StandardJavaFileManager standardFileManager =
                compiler.getStandardFileManager(diagnostics, null, null);

        JavaFileManager fileManager = createFileManager(standardFileManager, objects);

        JavaCompiler.CompilationTask task = compiler.getTask(null,
                fileManager, diagnostics, null, null, getCompilationUnits(files));

        if (!task.call()) {
            diagnostics.getDiagnostics().forEach(System.out::println);
            fileManager.close();
            return false;
        }
        fileManager.close();

        files.forEach(f ->
                with(objects.get(f.getKey()), o ->
                        loader.define(f.getKey(), o)));

        if (nonNull(resExecute)) {
            var cls = loader.findClass("net.binis.codegen.Execute");
            assertNotNull("Executor class not found!", cls);
            assertNotNull("Executor doesn't inherit TestExecutor!", cls.getSuperclass());
            assertEquals("Executor doesn't inherit TestExecutor!", TestExecutor.class, cls.getSuperclass());
            defineObjects(loader, objects);
            Reflection.withLoader(loader, () ->
                    assertTrue("Test execution failed!", TestExecutor.test((Class) cls)));
        }

        return true;
    }

    private void defineObjects(TestClassLoader loader, Map objects) {
        defineObjects(loader, objects, objects.size());
    }

    private void defineObjects(TestClassLoader loader, Map objects, int tries) {
        var error = false;

        for (var entry : objects.entrySet()) {
            if (isNull(loader.findClass(entry.getKey()))) {
                try {
                    loader.define(entry.getKey(), entry.getValue());
                } catch (NoClassDefFoundError ex) {
                    error = true;
                }
            }
        }

        if (error && tries > 0) {
            defineObjects(loader, objects, tries - 1);
        }
    }

    @SneakyThrows
    private String resourceAsString(String resource) {
        try {
            return new String(Files.readAllBytes(Paths.get(getClass().getClassLoader().getResource(resource).toURI())));
        } catch (Exception e) {
            log.error("Unable to load resource: {}!", resource);
            throw e;
        }
    }

    protected List> newList() {
        return new ArrayList<>();
    }

    protected UnaryOperator testSourcesLookup() {
        return s -> {
            try {
                return Files.readString(Path.of("./src/test/java/" + s.replace('.', '/') + ".java"));
            } catch (Exception e) {
                return null;
            }
        };
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy