package butterknife;
import butterknife.compiler.ButterKnifeProcessor;
import com.google.common.collect.ImmutableList;
import com.google.testing.compile.JavaFileObjects;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
import org.junit.Test;
import static com.google.common.truth.Truth.assertAbout;
import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
public class BindViewsTest {
@Test public void fieldVisibility() {
JavaFileObject source = JavaFileObjects.forSourceString("test.Test", ""
+ "package test;\n"
+ "import android.view.View;\n"
+ "import butterknife.BindViews;\n"
+ "import java.util.List;\n"
+ "public class Test {\n"
+ " @BindViews(1) public List<View> thing1;\n"
+ " @BindViews(2) List<View> thing2;\n"
+ " @BindViews(3) protected List<View> thing3;\n"
+ "}"
);
assertAbout(javaSource()).that(source)
.withCompilerOptions("-Xlint:-processing")
.processedWith(new ButterKnifeProcessor())
.compilesWithoutWarnings();
}
@Test public void bindingArray() {
JavaFileObject source = JavaFileObjects.forSourceString("test.Test", ""
+ "package test;\n"
+ "import android.view.View;\n"
+ "import butterknife.BindViews;\n"
+ "public class Test {\n"
+ " @BindViews({1, 2, 3}) View[] thing;\n"
+ "}"
);
JavaFileObject bindingSource = JavaFileObjects.forSourceString("test/Test_ViewBinding", ""
+ "package test;\n"
+ "import android.support.annotation.CallSuper;\n"
+ "import android.support.annotation.UiThread;\n"
+ "import android.view.View;\n"
+ "import butterknife.Unbinder;\n"
+ "import butterknife.internal.Utils;\n"
+ "import java.lang.IllegalStateException;\n"
+ "import java.lang.Override;\n"
+ "public class Test_ViewBinding implements Unbinder {\n"
+ " private Test target;\n"
+ " @UiThread\n"
+ " public Test_ViewBinding(Test target, View source) {\n"
+ " this.target = target;\n"
+ " target.thing = Utils.arrayOf(\n"
+ " Utils.findRequiredView(source, 1, \"field 'thing'\"), \n"
+ " Utils.findRequiredView(source, 2, \"field 'thing'\"), \n"
+ " Utils.findRequiredView(source, 3, \"field 'thing'\"));\n"
+ " }\n"
+ " @Override\n"
+ " @CallSuper\n"
+ " public void unbind() {\n"
+ " Test target = this.target;\n"
+ " if (target == null) throw new IllegalStateException(\"Bindings already cleared.\");\n"
+ " this.target = null;\n"
+ " target.thing = null;\n"
+ " }\n"
+ "}"
);
assertAbout(javaSource()).that(source)
.withCompilerOptions("-Xlint:-processing")
.processedWith(new ButterKnifeProcessor())
.compilesWithoutWarnings()
.and()
.generatesSources(bindingSource);
}
@Test public void bindingArrayWithGenerics() {
JavaFileObject source = JavaFileObjects.forSourceString("test.Test", ""
+ "package test;\n"
+ "import android.view.View;\n"
+ "import butterknife.BindViews;\n"
+ "public class Test<T extends View> {\n"
+ " @BindViews({1, 2, 3}) T[] thing;\n"
+ "}"
);
JavaFileObject bindingSource = JavaFileObjects.forSourceString("test/Test_ViewBinding", ""
+ "package test;\n"
+ "import android.support.annotation.CallSuper;\n"
+ "import android.support.annotation.UiThread;\n"
+ "import android.view.View;\n"
+ "import butterknife.Unbinder;\n"
+ "import butterknife.internal.Utils;\n"
+ "import java.lang.IllegalStateException;\n"
+ "import java.lang.Override;\n"
+ "public class Test_ViewBinding implements Unbinder {\n"
+ " private Test target;\n"
+ " @UiThread\n"
+ " public Test_ViewBinding(Test target, View source) {\n"
+ " this.target = target;\n"
+ " target.thing = Utils.arrayOf(\n"
+ " Utils.findRequiredView(source, 1, \"field 'thing'\"), \n"
+ " Utils.findRequiredView(source, 2, \"field 'thing'\"), \n"
+ " Utils.findRequiredView(source, 3, \"field 'thing'\"));\n"
+ " }\n"
+ " @Override\n"
+ " @CallSuper\n"
+ " public void unbind() {\n"
+ " Test target = this.target;\n"
+ " if (target == null) throw new IllegalStateException(\"Bindings already cleared.\");\n"
+ " this.target = null;\n"
+ " target.thing = null;\n"
+ " }\n"
+ "}"
);
assertAbout(javaSource()).that(source)
.withCompilerOptions("-Xlint:-processing")
.processedWith(new ButterKnifeProcessor())
// found raw type: test.Test
// missing type arguments for generic class test.Test<T>
.compilesWithoutError()
.and()
.generatesSources(bindingSource);
}
@Test public void bindingArrayWithCast() {
JavaFileObject source = JavaFileObjects.forSourceString("test.Test", ""
+ "package test;\n"
+ "import android.widget.TextView;\n"
+ "import butterknife.BindViews;\n"
+ "public class Test {\n"
+ " @BindViews({1, 2, 3}) TextView[] thing;\n"
+ "}"
);
JavaFileObject bindingSource = JavaFileObjects.forSourceString("test/Test_ViewBinding", ""
+ "package test;\n"
+ "import android.support.annotation.CallSuper;\n"
+ "import android.support.annotation.UiThread;\n"
+ "import android.view.View;\n"
+ "import android.widget.TextView;\n"
+ "import butterknife.Unbinder;\n"
+ "import butterknife.internal.Utils;\n"
+ "import java.lang.IllegalStateException;\n"
+ "import java.lang.Override;\n"
+ "public class Test_ViewBinding implements Unbinder {\n"
+ " private Test target;\n"
+ " @UiThread\n"
+ " public Test_ViewBinding(Test target, View source) {\n"
+ " this.target = target;\n"
+ " target.thing = Utils.arrayOf(\n"
+ " Utils.findRequiredViewAsType(source, 1, \"field 'thing'\", TextView.class), \n"
+ " Utils.findRequiredViewAsType(source, 2, \"field 'thing'\", TextView.class), \n"
+ " Utils.findRequiredViewAsType(source, 3, \"field 'thing'\", TextView.class));\n"
+ " }\n"
+ " @Override\n"
+ " @CallSuper\n"
+ " public void unbind() {\n"
+ " Test target = this.target;\n"
+ " if (target == null) throw new IllegalStateException(\"Bindings already cleared.\");\n"
+ " this.target = null;\n"
+ " target.thing = null;\n"
+ " }\n"
+ "}"
);
assertAbout(javaSource()).that(source)
.withCompilerOptions("-Xlint:-processing")
.processedWith(new ButterKnifeProcessor())
.compilesWithoutWarnings()
.and()
.generatesSources(bindingSource);
}
@Test public void bindingList() {
JavaFileObject source = JavaFileObjects.forSourceString("test.Test", ""
+ "package test;\n"
+ "import android.view.View;\n"
+ "import butterknife.BindViews;\n"
+ "import java.util.List;\n"
+ "public class Test {\n"
+ " @BindViews({1, 2, 3}) List<View> thing;\n"
+ "}"
);
JavaFileObject bindingSource = JavaFileObjects.forSourceString("test/Test_ViewBinding", ""
+ "package test;\n"
+ "import android.support.annotation.CallSuper;\n"
+ "import android.support.annotation.UiThread;\n"
+ "import android.view.View;\n"
+ "import butterknife.Unbinder;\n"
+ "import butterknife.internal.Utils;\n"
+ "import java.lang.IllegalStateException;\n"
+ "import java.lang.Override;\n"
+ "public class Test_ViewBinding implements Unbinder {\n"
+ " private Test target;\n"
+ " @UiThread\n"
+ " public Test_ViewBinding(Test target, View source) {\n"
+ " this.target = target;\n"
+ " target.thing = Utils.listOf(\n"
+ " Utils.findRequiredView(source, 1, \"field 'thing'\"), \n"
+ " Utils.findRequiredView(source, 2, \"field 'thing'\"), \n"
+ " Utils.findRequiredView(source, 3, \"field 'thing'\"));\n"
+ " }\n"
+ " @Override\n"
+ " @CallSuper\n"
+ " public void unbind() {\n"
+ " Test target = this.target;\n"
+ " if (target == null) throw new IllegalStateException(\"Bindings already cleared.\");\n"
+ " this.target = null;\n"
+ " target.thing = null;\n"
+ " }\n"
+ "}"
);
assertAbout(javaSource()).that(source)
.withCompilerOptions("-Xlint:-processing")
.processedWith(new ButterKnifeProcessor())
.compilesWithoutWarnings()
.and()
.generatesSources(bindingSource);
}
@Test public void bindingGeneratedView() {
JavaFileObject source = JavaFileObjects.forSourceString("test.Test", ""
+ "package test;\n"
+ "import butterknife.BindViews;\n"
+ "import java.util.List;\n"
+ "@PerformGeneration\n"
+ "public class Test {\n"
+ " @BindViews({1, 2}) List<GeneratedView> things;\n"
+ "}"
);
// w/o the GeneratingProcessor it can't find `class GeneratedView`
assertAbout(javaSources()).that(ImmutableList.of(source, TestGeneratingProcessor.ANNOTATION))
.processedWith(new ButterKnifeProcessor())
.failsToCompile()
.withErrorContaining("cannot find symbol");
// now the GeneratingProcessor should let it compile
assertAbout(javaSources()).that(ImmutableList.of(source, TestGeneratingProcessor.ANNOTATION))
.processedWith(new ButterKnifeProcessor(), new TestGeneratingProcessor("GeneratedView",
"package test;",
"import android.content.Context;",
"import android.view.View;",
"public class GeneratedView extends View {",
" public GeneratedView(Context context) {",
" super(context);",
" }",
"}"
))
.compilesWithoutError()
.withNoteContaining("@BindViews List or array with unresolved type (GeneratedView)").and()
.withNoteContaining("must elsewhere be generated as a View or interface").and()
.and()
.generatesFileNamed(StandardLocation.CLASS_OUTPUT, "test", "Test_ViewBinding.class");
}
@Test public void bindingListOfInterface() {
JavaFileObject source = JavaFileObjects.forSourceString("test.Test", ""
+ "package test;\n"
+ "import butterknife.BindViews;\n"
+ "import java.util.List;\n"
+ "public class Test {\n"
+ " interface TestInterface {}\n"
+ " @BindViews({1, 2, 3}) List<TestInterface> thing;\n"
+ "}"
);
JavaFileObject bindingSource = JavaFileObjects.forSourceString("test/Test_ViewBinding", ""
+ "package test;\n"
+ "import android.support.annotation.CallSuper;\n"
+ "import android.support.annotation.UiThread;\n"
+ "import android.view.View;\n"
+ "import butterknife.Unbinder;\n"
+ "import butterknife.internal.Utils;\n"
+ "import java.lang.IllegalStateException;\n"
+ "import java.lang.Override;\n"
+ "public class Test_ViewBinding implements Unbinder {\n"
+ " private Test target;\n"
+ " @UiThread\n"
+ " public Test_ViewBinding(Test target, View source) {\n"
+ " this.target = target;\n"
+ " target.thing = Utils.listOf(\n"
+ " Utils.findRequiredViewAsType(source, 1, \"field 'thing'\", Test.TestInterface.class), \n"
+ " Utils.findRequiredViewAsType(source, 2, \"field 'thing'\", Test.TestInterface.class), \n"
+ " Utils.findRequiredViewAsType(source, 3, \"field 'thing'\", Test.TestInterface.class));\n"
+ " }\n"
+ " @Override\n"
+ " @CallSuper\n"
+ " public void unbind() {\n"
+ " Test target = this.target;\n"
+ " if (target == null) throw new IllegalStateException(\"Bindings already cleared.\");\n"
+ " this.target = null;\n"
+ " target.thing = null;\n"
+ " }\n"
+ "}"
);
assertAbout(javaSource()).that(source)
.withCompilerOptions("-Xlint:-processing")
.processedWith(new ButterKnifeProcessor())
.compilesWithoutWarnings()
.and()
.generatesSources(bindingSource);
}
@Test public void bindingListWithGenerics() {
JavaFileObject source = JavaFileObjects.forSourceString("test.Test", ""
+ "package test;\n"
+ "import android.view.View;\n"
+ "import butterknife.BindViews;\n"
+ "import java.util.List;\n"
+ "public class Test<T extends View> {\n"
+ " @BindViews({1, 2, 3}) List<T> thing;\n"
+ "}"
);
JavaFileObject bindingSource = JavaFileObjects.forSourceString("test/Test_ViewBinding", ""
+ "package test;\n"
+ "import android.support.annotation.CallSuper;\n"
+ "import android.support.annotation.UiThread;\n"
+ "import android.view.View;\n"
+ "import butterknife.Unbinder;\n"
+ "import butterknife.internal.Utils;\n"
+ "import java.lang.IllegalStateException;\n"
+ "import java.lang.Override;\n"
+ "public class Test_ViewBinding implements Unbinder {\n"
+ " private Test target;\n"
+ " @UiThread\n"
+ " public Test_ViewBinding(Test target, View source) {\n"
+ " this.target = target;\n"
+ " target.thing = Utils.listOf(\n"
+ " Utils.findRequiredView(source, 1, \"field 'thing'\"), \n"
+ " Utils.findRequiredView(source, 2, \"field 'thing'\"), \n"
+ " Utils.findRequiredView(source, 3, \"field 'thing'\"));\n"
+ " }\n"
+ " @Override\n"
+ " @CallSuper\n"
+ " public void unbind() {\n"
+ " Test target = this.target;\n"
+ " if (target == null) throw new IllegalStateException(\"Bindings already cleared.\");\n"
+ " this.target = null;\n"
+ " target.thing = null;\n"
+ " }\n"
+ "}"
);
assertAbout(javaSource()).that(source)
.withCompilerOptions("-Xlint:-processing")
.processedWith(new ButterKnifeProcessor())
// found raw type: test.Test
// missing type arguments for generic class test.Test<T>
.compilesWithoutError()
.and()
.generatesSources(bindingSource);
}
@Test public void nullableList() {
JavaFileObject source = JavaFileObjects.forSourceString("test.Test", ""
+ "package test;\n"
+ "import android.view.View;\n"
+ "import butterknife.BindViews;\n"
+ "import java.util.List;\n"
+ "public class Test {\n"
+ " @interface Nullable {}\n"
+ " @Nullable @BindViews({1, 2, 3}) List<View> thing;\n"
+ "}"
);
JavaFileObject bindingSource = JavaFileObjects.forSourceString("test/Test_ViewBinding", ""
+ "package test;\n"
+ "import android.support.annotation.CallSuper;\n"
+ "import android.support.annotation.UiThread;\n"
+ "import android.view.View;\n"
+ "import butterknife.Unbinder;\n"
+ "import butterknife.internal.Utils;\n"
+ "import java.lang.IllegalStateException;\n"
+ "import java.lang.Override;\n"
+ "public class Test_ViewBinding implements Unbinder {\n"
+ " private Test target;\n"
+ " @UiThread\n"
+ " public Test_ViewBinding(Test target, View source) {\n"
+ " this.target = target;\n"
+ " target.thing = Utils.listOf(\n"
+ " source.findViewById(1), \n"
+ " source.findViewById(2), \n"
+ " source.findViewById(3));\n"
+ " }\n"
+ " @Override\n"
+ " @CallSuper\n"
+ " public void unbind() {\n"
+ " Test target = this.target;\n"
+ " if (target == null) throw new IllegalStateException(\"Bindings already cleared.\");\n"
+ " this.target = null;\n"
+ " target.thing = null;\n"
+ " }\n"
+ "}"
);
assertAbout(javaSource()).that(source)
.withCompilerOptions("-Xlint:-processing")
.processedWith(new ButterKnifeProcessor())
.compilesWithoutWarnings()
.and()
.generatesSources( bindingSource);
}
@Test public void failsIfNoIds() {
JavaFileObject source = JavaFileObjects.forSourceString("test.Test", ""
+ "package test;\n"
+ "import android.view.View;\n"
+ "import butterknife.BindViews;\n"
+ "import java.util.List;\n"
+ "public class Test {\n"
+ " @BindViews({}) List<View> thing;\n"
+ "}"
);
assertAbout(javaSource()).that(source)
.processedWith(new ButterKnifeProcessor())
.failsToCompile()
.withErrorContaining("@BindViews must specify at least one ID. (test.Test.thing)")
.in(source).onLine(6);
}
@Test public void failsIfNoGenericType() {
JavaFileObject source = JavaFileObjects.forSourceString("test.Test", ""
+ "package test;\n"
+ "import butterknife.BindViews;\n"
+ "import java.util.List;\n"
+ "public class Test {\n"
+ " @BindViews(1) List thing;\n"
+ "}"
);
assertAbout(javaSource()).that(source)
.processedWith(new ButterKnifeProcessor())
.failsToCompile()
.withErrorContaining("@BindViews List must have a generic component. (test.Test.thing)")
.in(source).onLine(5);
}
@Test public void failsIfUnsupportedCollection() {
JavaFileObject source = JavaFileObjects.forSourceString("test.Test", ""
+ "package test;\n"
+ "import android.view.View;\n"
+ "import butterknife.BindViews;\n"
+ "import java.util.Deque;\n"
+ "public class Test {\n"
+ " @BindViews(1) Deque<View> thing;\n"
+ "}"
);
assertAbout(javaSource()).that(source)
.processedWith(new ButterKnifeProcessor())
.failsToCompile()
.withErrorContaining("@BindViews must be a List or array. (test.Test.thing)")
.in(source).onLine(6);
}
@Test public void failsIfGenericNotView() {
JavaFileObject source = JavaFileObjects.forSourceString("test.Test", ""
+ "package test;\n"
+ "import butterknife.BindViews;\n"
+ "import java.util.List;\n"
+ "public class Test {\n"
+ " @BindViews(1) List<String> thing;\n"
+ "}"
);
assertAbout(javaSource()).that(source)
.processedWith(new ButterKnifeProcessor())
.failsToCompile()
.withErrorContaining(
"@BindViews List or array type must extend from View or be an interface. (test.Test.thing)")
.in(source).onLine(5);
}
@Test public void failsIfArrayNotView() {
JavaFileObject source = JavaFileObjects.forSourceString("test.Test", ""
+ "package test;\n"
+ "import butterknife.BindViews;\n"
+ "public class Test {\n"
+ " @BindViews(1) String[] thing;\n"
+ "}"
);
assertAbout(javaSource()).that(source)
.processedWith(new ButterKnifeProcessor())
.failsToCompile()
.withErrorContaining(
"@BindViews List or array type must extend from View or be an interface. (test.Test.thing)")
.in(source).onLine(4);
}
@Test public void failsIfContainsDuplicateIds() throws Exception {
JavaFileObject source = JavaFileObjects.forSourceString("test.Test", ""
+ "package test;\n"
+ "import android.view.View;\n"
+ "import butterknife.BindViews;\n"
+ "import java.util.List;\n"
+ "public class Test {\n"
+ " @BindViews({1, 1}) List<View> thing;\n"
+ "}"
);
assertAbout(javaSource()).that(source)
.processedWith(new ButterKnifeProcessor())
.failsToCompile()
.withErrorContaining("@BindViews annotation contains duplicate ID 1. (test.Test.thing)")
.in(source).onLine(6);
}
}