blob: faa89b15f8d91f347d72d38cb6831f5d29f218d1 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* 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
*/
package com.android.compat.annotation;
import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.tools.StandardLocation.CLASS_OUTPUT;
import com.google.testing.compile.JavaFileObjects;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.Compiler;
import com.google.testing.compile.CompilationSubject;
import com.google.common.collect.ObjectArrays;
import com.google.common.io.ByteSource;
import javax.tools.JavaFileObject;
import org.junit.Test;
public class ChangeIdProcessorTest {
// Hard coding the annotation definitions to avoid dependency on libcore, where they're defined.
private static final JavaFileObject[] mAnnotations = {
JavaFileObjects.forSourceLines("android.compat.annotation.ChangeId",
"package android.compat.annotation;",
"import static java.lang.annotation.ElementType.FIELD;",
"import static java.lang.annotation.ElementType.PARAMETER;",
"import static java.lang.annotation.RetentionPolicy.CLASS;",
"import java.lang.annotation.Retention;",
"import java.lang.annotation.Target;",
"@Retention(CLASS)",
"@Target({FIELD, PARAMETER})",
"public @interface ChangeId {",
"}"),
JavaFileObjects.forSourceLines("android.compat.annotation.Disabled",
"package android.compat.annotation;",
"import static java.lang.annotation.ElementType.FIELD;",
"import static java.lang.annotation.RetentionPolicy.CLASS;",
"import java.lang.annotation.Retention;",
"import java.lang.annotation.Target;",
"@Retention(CLASS)",
"@Target({FIELD})",
"public @interface Disabled {",
"}"),
JavaFileObjects.forSourceLines("android.compat.annotation.EnabledAfter",
"package android.compat.annotation;",
"import static java.lang.annotation.ElementType.FIELD;",
"import static java.lang.annotation.RetentionPolicy.CLASS;",
"import java.lang.annotation.Retention;",
"import java.lang.annotation.Target;",
"@Retention(CLASS)",
"@Target({FIELD})",
"public @interface EnabledAfter {",
"int targetSdkVersion();",
"}")
};
private static final String HEADER =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>";
@Test
public void testSuccessfulCompilation() {
JavaFileObject[] source = {
JavaFileObjects.forSourceLines(
"libcore.util.Compat",
"package libcore.util;",
"import android.compat.annotation.ChangeId;",
"import android.compat.annotation.EnabledAfter;",
"import android.compat.annotation.Disabled;",
"public class Compat {",
" @EnabledAfter(targetSdkVersion=29)",
" @ChangeId",
" static final long MY_CHANGE_ID = 123456789l;",
" @ChangeId",
" @Disabled",
" public static final long ANOTHER_CHANGE = 23456700l;",
"}")
};
String expectedFile = HEADER + "<config>" +
"<compat-change enableAfterTargetSdk=\"29\" id=\"123456789\" "
+ "name=\"MY_CHANGE_ID\"/>" +
"<compat-change disabled=\"true\" id=\"23456700\" name=\"ANOTHER_CHANGE\"/>" +
"</config>";
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
.compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).succeeded();
CompilationSubject.assertThat(compilation).generatedFile(CLASS_OUTPUT, "compat",
"compat_config.xml").hasContents(ByteSource.wrap(expectedFile.getBytes(UTF_8)));
}
@Test
public void testBothDisabledAndEnabledAfter() {
JavaFileObject[] source = {
JavaFileObjects.forSourceLines(
"libcore.util.Compat",
"package libcore.util;",
"import android.compat.annotation.ChangeId;",
"import android.compat.annotation.EnabledAfter;",
"import android.compat.annotation.Disabled;",
"public class Compat {",
" @EnabledAfter(targetSdkVersion=29)",
" @Disabled",
" @ChangeId",
" static final long MY_CHANGE_ID = 123456789l;",
"}")
};
String expectedFile = HEADER + "<config>" +
"<compat-change disabled=\"true\" enableAfterTargetSdk=\"29\" id=\"123456789\" "
+ "name=\"MY_CHANGE_ID\"/>" +
"</config>";
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
.compile(ObjectArrays.concat(mAnnotations, source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).hadErrorContaining(
"ChangeId cannot be annotated with both @Disabled and @EnabledAfter.");
}
@Test
public void testIgnoredParams() {
JavaFileObject[] source = {
JavaFileObjects.forSourceLines(
"android.compat.Compatibility",
"package android.compat;",
"import android.compat.annotation.ChangeId;",
"public final class Compatibility {",
" public static void reportChange(@ChangeId long changeId) {}",
" public static boolean isChangeEnabled(@ChangeId long changeId) {",
" return true;",
" }",
"}")
};
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
.compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).succeeded();
}
@Test
public void testOtherClassParams() {
JavaFileObject[] source = {
JavaFileObjects.forSourceLines(
"android.compat.OtherClass",
"package android.compat;",
"import android.compat.annotation.ChangeId;",
"public final class OtherClass {",
" public static void reportChange(@ChangeId long changeId) {}",
"}")
};
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
.compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).hadErrorContaining(
"Non field element changeId annotated with @ChangeId. Got type PARAMETER, "
+ "expected FIELD.");
}
@Test
public void testOtherMethodParams() {
JavaFileObject[] source = {
JavaFileObjects.forSourceLines(
"android.compat.Compatibility",
"package android.compat;",
"import android.compat.annotation.ChangeId;",
"public final class Compatibility {",
" public static void otherMethod(@ChangeId long changeId) {}",
"}")
};
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
.compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).hadErrorContaining(
"Non field element changeId annotated with @ChangeId. Got type PARAMETER, "
+ "expected FIELD.");
}
@Test
public void testNonFinal() {
JavaFileObject[] source = {
JavaFileObjects.forSourceLines(
"libcore.util.Compat",
"package libcore.util;",
"import android.compat.annotation.ChangeId;",
"import android.compat.annotation.EnabledAfter;",
"public class Compat {",
" @EnabledAfter(targetSdkVersion=29)",
" @ChangeId",
" static long MY_CHANGE_ID = 123456789l;",
"}")
};
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
.compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).hadErrorContaining(
"Non constant/final variable MY_CHANGE_ID (or non constant value) annotated with "
+ "@ChangeId.");
}
@Test
public void testNonLong() {
JavaFileObject[] source = {
JavaFileObjects.forSourceLines(
"libcore.util.Compat",
"package libcore.util;",
"import android.compat.annotation.ChangeId;",
"import android.compat.annotation.EnabledAfter;",
"public class Compat {",
" @EnabledAfter(targetSdkVersion=29)",
" @ChangeId",
" static final int MY_CHANGE_ID = 12345;",
"}")
};
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
.compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).hadErrorContaining(
"Variables annotated with @ChangeId should be of type long.");
}
@Test
public void testNonStatic() {
JavaFileObject[] source = {
JavaFileObjects.forSourceLines(
"libcore.util.Compat",
"package libcore.util;",
"import android.compat.annotation.ChangeId;",
"import android.compat.annotation.EnabledAfter;",
"public class Compat {",
" @EnabledAfter(targetSdkVersion=29)",
" @ChangeId",
" final long MY_CHANGE_ID = 123456789l;",
"}")
};
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
.compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).hadErrorContaining(
"Non static variable MY_CHANGE_ID annotated with @ChangeId.");
}
}