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

org.test4j.mock.processor.filer.MocksFiler Maven / Gradle / Ivy

package org.test4j.mock.processor.filer;

import com.squareup.javapoet.*;
import org.test4j.mock.faking.fluent.MocksApply;
import org.test4j.mock.processor.MocksProcessor;
import org.test4j.mock.processor.filer.attr.ClassNames;
import org.test4j.mock.processor.filer.file.MockClassFiler;
import org.test4j.mock.processor.filer.file.MockTypeFiler;
import org.test4j.mock.processor.filer.file.MockUpFiler;

import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import java.util.*;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.function.Consumer;

import static org.test4j.mock.processor.MocksProcessor.error;

/**
 * MocksFiler
 *
 * @author wudarui
 */
public class MocksFiler {
    private static final Set Has_Mocks = new ConcurrentSkipListSet<>();
    private final ClassName fullName;
    private final String mocksClass;
    private final Map mocks = new HashMap<>();

    public MocksFiler(String fullName, String suffix, Set classNames) {
        this.fullName = ClassNames.getClassName(fullName);
        this.mocksClass = this.fullName.simpleName() + suffix;
        this.addToMockClass(classNames);
    }

    private void addToMockClass(Collection classNames) {
        for (ClassName className : classNames) {
            this.addMockFile(className);
        }
    }

    private void addMockFile(ClassName className) {
        if (className == null) {
            return;
        }
        ClassName mockClassName = ClassNames.mockup(className);
        this.mocks.put(className.toString(), mockClassName);
    }

    public void writeFiler() {
        TypeSpec.Builder builder = TypeSpec.classBuilder(mocksClass)
            .addModifiers(Modifier.PUBLIC)
            .superclass(MocksApply.class);

        this.build(builder);

        JavaFile.Builder javaBuilder = JavaFile.builder(this.fullName.packageName(), builder.build());
        MocksProcessor.writeFiler(javaBuilder.build());
    }

    /**
     * key: toFaked.simpleName(), value: 同名类名出现的次数
     */
    private final Map counts = new HashMap<>();

    private int count(ClassName toFaked) {
        String name = toFaked.simpleName();
        Integer count = counts.get(name);
        count = count == null ? 0 : count + 1;
        counts.put(name, count);
        return count;
    }

    private void build(TypeSpec.Builder spec) {
        spec.addField(this.f_mocks(mocksClass))
            .addMethod(this.m_mocks(mocksClass));

        for (Map.Entry entry : this.mocks.entrySet()) {
            ClassName toFaked = ClassNames.getClassName(entry.getKey());
            int count = count(toFaked);
            if (count > 0) {
                toFaked = ClassNames.getClassName(entry.getKey() + "__" + count);
            }

            spec.addMethod(this.m_mockUp1(toFaked, entry.getValue()));
            spec.addMethod(this.m_mockUp2(toFaked, entry.getValue()));
            spec.addField(this.f_mockUpField(toFaked, entry.getValue()));
        }
        while (!this.mocks.isEmpty()) {
            Iterator> it = this.mocks.entrySet().iterator();
            Map.Entry entry = it.next();
            it.remove();
            MockUpFiler mockUpFiler = this.generateMockUp(entry.getKey());
            if (mockUpFiler == null) {
                continue;
            }
            mockUpFiler.writeFiler();
            this.addMockFile(mockUpFiler.getSuperClass());
        }
    }

    private static final String Mock1_JavaDoc = "" +
        " mock {@link $T} 多个方法, 示例代码\n" +
        " 
\n" +
        " mocks.$L(fake->{\n" +
        "      fake.toMockMethod1.restReturn(\"返回值1\",\"返回值2\");\n" +
        "      fake.toMockMethod2.restReturn(\"返回值1\",\"返回值2\");\n" +
        " });\n" +
        " 
\n\n" + " @param faker {@link $LMockUp} 构造\n" + " @param targets 指定, 表示对特定$L实例进行mock;否则,表示对所有$L实例进行mock"; private static final String Mock2_JavaDoc = "" + " mock {@link $T} 单个方法, 示例代码\n" + "
\n" +
        " mocks.$L().toMockMethod1.restReturn(\"返回值1\",\"返回值2\");\n" +
        " 
\n\n" + " @param targets 指定, 表示对特定$L实例进行mock;否则,表示对所有$L实例进行mock"; private MethodSpec m_mockUp1(ClassName toFaked, ClassName mockUp) { String name = toFaked.simpleName(); return MethodSpec.methodBuilder(toFaked.simpleName()) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addJavadoc(Mock1_JavaDoc, toFaked, name, name, name, name) .addParameter(ParameterizedTypeName.get(ClassName.get(Consumer.class), mockUp), "faker") .addParameter(ArrayTypeName.of(Object.class), "targets") .varargs(true) .returns(TypeVariableName.get(mocksClass)) .addStatement("return super.apply(faker, new $T(targets))", mockUp) .build(); } private MethodSpec m_mockUp2(ClassName toFaked, ClassName mockUp) { String name = toFaked.simpleName(); return MethodSpec.methodBuilder(name) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addJavadoc(Mock2_JavaDoc, toFaked, name, name, name) .addParameter(ArrayTypeName.of(Object.class), "targets") .varargs(true) .returns(mockUp) .addStatement("return new $T(targets)", mockUp) .build(); } private FieldSpec f_mockUpField(ClassName toFaked, ClassName mockUp) { String name = toFaked.simpleName(); return FieldSpec.builder(mockUp, name, Modifier.FINAL, Modifier.PUBLIC) .addJavadoc("@see #$L(Object...), 无参模式 ", name) .initializer("$L()", name) .build(); } /** * 构造public static final XyzMocks mocks = new XyzMocks() 单例变量 * * @param mocksClass mock class * @return ignore */ private FieldSpec f_mocks(String mocksClass) { return FieldSpec.builder(ClassNames.getClassName(mocksClass), "mocks", Modifier.FINAL, Modifier.STATIC, Modifier.PUBLIC) .addJavadoc("" + "全局变量, 方便在测试方法中引用$L实例\n" + "每个测试执行完毕, 本实例中定义的mock行为会被清除\n" + "如果想定义全局mock行为, 使用 mocks()方法引用", mocksClass) .initializer("mocks()") .build(); } private MethodSpec m_mocks(String mocksClass) { return MethodSpec.methodBuilder("mocks") .returns(ClassNames.getClassName(mocksClass)) .addModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PUBLIC) .addJavadoc("$L全局变量, 方便引用", mocksClass) .addJavadoc("每个测试执行完毕, mock行为会被清除") .addStatement("return new $L()", mocksClass) .build(); } private MockUpFiler generateMockUp(String className) { if (Has_Mocks.contains(className)) { return null; } Has_Mocks.add(className); try { return new MockClassFiler(Class.forName(className)); } catch (ClassNotFoundException e) { TypeElement typeElement = MocksProcessor.getTypeElement(className); if (typeElement == null) { error("ClassNotFoundException", e); } else { return new MockTypeFiler(typeElement); } } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy