1/*
2 * Copyright 2014, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32package org.jf.smalidea;
33
34import com.intellij.psi.*;
35import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
36import org.jf.smalidea.psi.impl.SmaliClass;
37import org.jf.smalidea.psi.impl.SmaliFile;
38import org.jf.smalidea.psi.impl.SmaliLiteral;
39import org.jf.smalidea.psi.impl.SmaliMethod;
40import org.junit.Assert;
41
42public class SmaliAnnotationTest extends LightCodeInsightFixtureTestCase {
43    // TODO: test default values
44
45    public void testClassAnnotation() {
46        myFixture.addFileToProject("my/TestAnnotation.smali",
47                ".class public interface abstract annotation Lmy/TestAnnotation;\n" +
48                ".super Ljava/lang/Object;\n" +
49                ".implements Ljava/lang/annotation/Annotation;\n" +
50                "\n" +
51                ".method public abstract testBooleanValue()Z\n" +
52                ".end method\n" +
53                "\n" +
54                ".method public abstract testStringArrayValue()[Ljava/lang/String;\n" +
55                ".end method\n" +
56                "\n" +
57                ".method public abstract testStringValue()Ljava/lang/String;\n" +
58                ".end method");
59
60        myFixture.addFileToProject("my/TestAnnotation2.smali",
61                ".class public interface abstract annotation Lmy/TestAnnotation2;\n" +
62                ".super Ljava/lang/Object;\n" +
63                ".implements Ljava/lang/annotation/Annotation;\n");
64
65        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
66                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
67                "\n" +
68                ".annotation runtime Lmy/TestAnnotation;\n" +
69                "    testBooleanValue = true\n" +
70                "    testStringValue = \"blah\"\n" +
71                "    testStringArrayValue = {\n" +
72                "        \"blah1\",\n" +
73                "        \"blah2\"\n" +
74                "    }\n" +
75                ".end annotation\n" +
76                "\n" +
77                ".annotation runtime Lmy/TestAnnotation2;\n" +
78                ".end annotation");
79
80        SmaliClass smaliClass = file.getPsiClass();
81        Assert.assertEquals("my.pkg.blah", smaliClass.getQualifiedName());
82
83        doTest(smaliClass);
84    }
85
86    public void testFieldAnnotation() {
87        myFixture.addFileToProject("my/TestAnnotation.smali",
88                ".class public interface abstract annotation Lmy/TestAnnotation;\n" +
89                ".super Ljava/lang/Object;\n" +
90                ".implements Ljava/lang/annotation/Annotation;\n" +
91                "\n" +
92                ".method public abstract testBooleanValue()Z\n" +
93                ".end method\n" +
94                "\n" +
95                ".method public abstract testStringArrayValue()[Ljava/lang/String;\n" +
96                ".end method\n" +
97                "\n" +
98                ".method public abstract testStringValue()Ljava/lang/String;\n" +
99                ".end method");
100
101        myFixture.addFileToProject("my/TestAnnotation2.smali",
102                ".class public interface abstract annotation Lmy/TestAnnotation2;\n" +
103                ".super Ljava/lang/Object;\n" +
104                ".implements Ljava/lang/annotation/Annotation;\n");
105
106        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
107                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
108                "\n" +
109                ".field public myField:I\n" +
110                "    .annotation runtime Lmy/TestAnnotation;\n" +
111                "        testBooleanValue = true\n" +
112                "        testStringValue = \"blah\"\n" +
113                "        testStringArrayValue = {\n" +
114                "            \"blah1\",\n" +
115                "            \"blah2\"\n" +
116                "        }\n" +
117                "    .end annotation\n" +
118                "    .annotation runtime Lmy/TestAnnotation2;\n" +
119                "    .end annotation\n" +
120                ".end field");
121
122        SmaliClass smaliClass = file.getPsiClass();
123        Assert.assertEquals("my.pkg.blah", smaliClass.getQualifiedName());
124
125        PsiField field = smaliClass.findFieldByName("myField", false);
126        doTest((PsiAnnotationOwner)field);
127    }
128
129    public void testMethodAnnotation() {
130        myFixture.addFileToProject("my/TestAnnotation.smali",
131                ".class public interface abstract annotation Lmy/TestAnnotation;\n" +
132                ".super Ljava/lang/Object;\n" +
133                ".implements Ljava/lang/annotation/Annotation;\n" +
134                "\n" +
135                ".method public abstract testBooleanValue()Z\n" +
136                ".end method\n" +
137                "\n" +
138                ".method public abstract testStringArrayValue()[Ljava/lang/String;\n" +
139                ".end method\n" +
140                "\n" +
141                ".method public abstract testStringValue()Ljava/lang/String;\n" +
142                ".end method");
143
144        myFixture.addFileToProject("my/TestAnnotation2.smali",
145                ".class public interface abstract annotation Lmy/TestAnnotation2;\n" +
146                ".super Ljava/lang/Object;\n" +
147                ".implements Ljava/lang/annotation/Annotation;\n");
148
149        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
150                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
151                "\n" +
152                ".method public myMethod()V\n" +
153                "    .annotation runtime Lmy/TestAnnotation;\n" +
154                "        testBooleanValue = true\n" +
155                "        testStringValue = \"blah\"\n" +
156                "        testStringArrayValue = {\n" +
157                "            \"blah1\",\n" +
158                "            \"blah2\"\n" +
159                "        }\n" +
160                "    .end annotation\n" +
161                "    .annotation runtime Lmy/TestAnnotation2;\n" +
162                "    .end annotation\n" +
163                ".end method");
164
165        SmaliClass smaliClass = file.getPsiClass();
166        Assert.assertEquals("my.pkg.blah", smaliClass.getQualifiedName());
167
168        SmaliMethod method = smaliClass.getMethods()[0];
169        doTest(method);
170    }
171
172    public void doTest(PsiAnnotationOwner annotationOwner) {
173        Assert.assertEquals(2, annotationOwner.getAnnotations().length);
174
175        Assert.assertEquals("my.TestAnnotation", annotationOwner.getAnnotations()[0].getQualifiedName());
176        PsiJavaCodeReferenceElement annotationNameRef = annotationOwner.getAnnotations()[0].getNameReferenceElement();
177        Assert.assertNotNull(annotationNameRef);
178        SmaliClass smaliAnnotationClass = (SmaliClass)annotationNameRef.resolve();
179        Assert.assertNotNull(smaliAnnotationClass);
180        Assert.assertEquals("my.TestAnnotation", smaliAnnotationClass.getQualifiedName());
181
182        Assert.assertEquals("my.TestAnnotation2", annotationOwner.getAnnotations()[1].getQualifiedName());
183        annotationNameRef = annotationOwner.getAnnotations()[1].getNameReferenceElement();
184        Assert.assertNotNull(annotationNameRef);
185        smaliAnnotationClass = (SmaliClass)annotationNameRef.resolve();
186        Assert.assertNotNull(smaliAnnotationClass);
187        Assert.assertEquals("my.TestAnnotation2", smaliAnnotationClass.getQualifiedName());
188
189        PsiAnnotation smaliAnnotation = annotationOwner.findAnnotation("my.TestAnnotation");
190        Assert.assertNotNull(smaliAnnotation);
191        Assert.assertEquals("my.TestAnnotation", smaliAnnotation.getQualifiedName());
192        PsiAnnotationOwner owner = smaliAnnotation.getOwner();
193        Assert.assertNotNull(owner);
194        Assert.assertSame(annotationOwner, owner);
195        annotationNameRef = smaliAnnotation.getNameReferenceElement();
196        Assert.assertNotNull(annotationNameRef);
197        smaliAnnotationClass = (SmaliClass)annotationNameRef.resolve();
198        Assert.assertNotNull(smaliAnnotationClass);
199        Assert.assertEquals("my.TestAnnotation", smaliAnnotationClass.getQualifiedName());
200
201        PsiAnnotationParameterList parameterList = smaliAnnotation.getParameterList();
202        Assert.assertNotNull(parameterList);
203        Assert.assertEquals(3, parameterList.getAttributes().length);
204        Assert.assertEquals("testBooleanValue", parameterList.getAttributes()[0].getName());
205        PsiAnnotationMemberValue value = parameterList.getAttributes()[0].getValue();
206        Assert.assertNotNull(value);
207        // TODO: test the values rather than the text
208        Assert.assertEquals("true", value.getText());
209        Assert.assertEquals("testStringValue", parameterList.getAttributes()[1].getName());
210        value = parameterList.getAttributes()[1].getValue();
211        Assert.assertNotNull(value);
212        Assert.assertEquals("\"blah\"", value.getText());
213        Assert.assertEquals("testStringArrayValue", parameterList.getAttributes()[2].getName());
214        value = parameterList.getAttributes()[2].getValue();
215        Assert.assertNotNull(value);
216        // TODO: test the individual values, once the array literal stuff is implemented
217
218        value = smaliAnnotation.findAttributeValue("testBooleanValue");
219        Assert.assertNotNull(value);
220        Assert.assertEquals("true", value.getText());
221
222        value = smaliAnnotation.findAttributeValue("testStringValue");
223        Assert.assertNotNull(value);
224        Assert.assertEquals("\"blah\"", value.getText());
225
226        value = smaliAnnotation.findAttributeValue("testStringArrayValue");
227        Assert.assertNotNull(value);
228
229        // TODO: test findAttributeValue vs findDeclaredAttributeValue for default values
230
231        smaliAnnotation = annotationOwner.findAnnotation("my.TestAnnotation2");
232        Assert.assertNotNull(smaliAnnotation);
233        Assert.assertEquals("my.TestAnnotation2", smaliAnnotation.getQualifiedName());
234        owner = smaliAnnotation.getOwner();
235        Assert.assertNotNull(owner);
236        Assert.assertSame(annotationOwner, owner);
237        annotationNameRef = smaliAnnotation.getNameReferenceElement();
238        Assert.assertNotNull(annotationNameRef);
239        smaliAnnotationClass = (SmaliClass)annotationNameRef.resolve();
240        Assert.assertNotNull(smaliAnnotationClass);
241        Assert.assertEquals("my.TestAnnotation2", smaliAnnotationClass.getQualifiedName());
242
243        parameterList = smaliAnnotation.getParameterList();
244        Assert.assertNotNull(parameterList);
245        Assert.assertEquals(0, parameterList.getAttributes().length);
246    }
247
248    public void testDefaultValue() {
249        SmaliFile file = (SmaliFile)myFixture.addFileToProject("AnnotationWithDefaultValue.smali", "" +
250                ".class public abstract interface annotation LAnnotationWithValues;\n" +
251                ".super Ljava/lang/Object;\n" +
252                ".implements Ljava/lang/annotation/Annotation;\n" +
253                "\n" +
254                ".method public abstract intValue()I\n" +
255                ".end method\n" +
256                "\n" +
257                ".annotation system Ldalvik/annotation/AnnotationDefault;\n" +
258                "    value = .subannotation LAnnotationWithValues;\n" +
259                "                intValue = 4\n" +
260                "            .end subannotation\n" +
261                ".end annotation\n" +
262                "\n");
263
264        SmaliClass smaliClass = file.getPsiClass();
265        Assert.assertNotNull(smaliClass);
266        SmaliMethod method = smaliClass.getMethods()[0];
267        Assert.assertEquals("intValue", method.getName());
268
269        PsiAnnotationMemberValue defaultValue = method.getDefaultValue();
270        Assert.assertTrue(defaultValue instanceof SmaliLiteral);
271        Assert.assertEquals(4, ((SmaliLiteral)defaultValue).getIntegralValue());
272    }
273}
274