1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "java/JavaClassGenerator.h"
18
19#include <sstream>
20#include <string>
21
22#include "test/Test.h"
23#include "util/Util.h"
24
25using android::StringPiece;
26
27namespace aapt {
28
29TEST(JavaClassGeneratorTest, FailWhenEntryIsJavaKeyword) {
30  std::unique_ptr<ResourceTable> table =
31      test::ResourceTableBuilder()
32          .SetPackageId("android", 0x01)
33          .AddSimple("android:id/class", ResourceId(0x01020000))
34          .Build();
35
36  std::unique_ptr<IAaptContext> context =
37      test::ContextBuilder()
38          .AddSymbolSource(
39              util::make_unique<ResourceTableSymbolSource>(table.get()))
40          .SetNameManglerPolicy(NameManglerPolicy{"android"})
41          .Build();
42  JavaClassGenerator generator(context.get(), table.get(), {});
43
44  std::stringstream out;
45  EXPECT_FALSE(generator.Generate("android", &out));
46}
47
48TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
49  std::unique_ptr<ResourceTable> table =
50      test::ResourceTableBuilder()
51          .SetPackageId("android", 0x01)
52          .AddSimple("android:id/hey-man", ResourceId(0x01020000))
53          .AddValue("android:attr/cool.attr", ResourceId(0x01010000),
54                    test::AttributeBuilder(false).Build())
55          .AddValue(
56              "android:styleable/hey.dude", ResourceId(0x01030000),
57              test::StyleableBuilder()
58                  .AddItem("android:attr/cool.attr", ResourceId(0x01010000))
59                  .Build())
60          .Build();
61
62  std::unique_ptr<IAaptContext> context =
63      test::ContextBuilder()
64          .AddSymbolSource(
65              util::make_unique<ResourceTableSymbolSource>(table.get()))
66          .SetNameManglerPolicy(NameManglerPolicy{"android"})
67          .Build();
68  JavaClassGenerator generator(context.get(), table.get(), {});
69
70  std::stringstream out;
71  EXPECT_TRUE(generator.Generate("android", &out));
72
73  std::string output = out.str();
74
75  EXPECT_NE(std::string::npos,
76            output.find("public static final int hey_man=0x01020000;"));
77
78  EXPECT_NE(std::string::npos,
79            output.find("public static final int[] hey_dude={"));
80
81  EXPECT_NE(std::string::npos,
82            output.find("public static final int hey_dude_cool_attr=0;"));
83}
84
85TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
86  std::unique_ptr<ResourceTable> table =
87      test::ResourceTableBuilder()
88          .SetPackageId("android", 0x01)
89          .AddSimple("android:id/one", ResourceId(0x01020000))
90          .AddSimple("android:id/com.foo$two", ResourceId(0x01020001))
91          .Build();
92
93  std::unique_ptr<IAaptContext> context =
94      test::ContextBuilder()
95          .AddSymbolSource(
96              util::make_unique<ResourceTableSymbolSource>(table.get()))
97          .SetNameManglerPolicy(NameManglerPolicy{"android"})
98          .Build();
99  JavaClassGenerator generator(context.get(), table.get(), {});
100  std::stringstream out;
101  ASSERT_TRUE(generator.Generate("android", "com.android.internal", &out));
102
103  std::string output = out.str();
104  EXPECT_NE(std::string::npos, output.find("package com.android.internal;"));
105  EXPECT_NE(std::string::npos,
106            output.find("public static final int one=0x01020000;"));
107  EXPECT_EQ(std::string::npos, output.find("two"));
108  EXPECT_EQ(std::string::npos, output.find("com_foo$two"));
109}
110
111TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
112  std::unique_ptr<ResourceTable> table =
113      test::ResourceTableBuilder()
114          .SetPackageId("android", 0x01)
115          .AddSimple("android:attr/two", ResourceId(0x01010001))
116          .AddSimple("android:^attr-private/one", ResourceId(0x01010000))
117          .Build();
118
119  std::unique_ptr<IAaptContext> context =
120      test::ContextBuilder()
121          .AddSymbolSource(
122              util::make_unique<ResourceTableSymbolSource>(table.get()))
123          .SetNameManglerPolicy(NameManglerPolicy{"android"})
124          .Build();
125  JavaClassGenerator generator(context.get(), table.get(), {});
126  std::stringstream out;
127  ASSERT_TRUE(generator.Generate("android", &out));
128
129  std::string output = out.str();
130  EXPECT_NE(std::string::npos, output.find("public static final class attr"));
131  EXPECT_EQ(std::string::npos,
132            output.find("public static final class ^attr-private"));
133}
134
135TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
136  StdErrDiagnostics diag;
137  std::unique_ptr<ResourceTable> table =
138      test::ResourceTableBuilder()
139          .SetPackageId("android", 0x01)
140          .AddSimple("android:id/one", ResourceId(0x01020000))
141          .AddSimple("android:id/two", ResourceId(0x01020001))
142          .AddSimple("android:id/three", ResourceId(0x01020002))
143          .SetSymbolState("android:id/one", ResourceId(0x01020000),
144                          SymbolState::kPublic)
145          .SetSymbolState("android:id/two", ResourceId(0x01020001),
146                          SymbolState::kPrivate)
147          .Build();
148
149  std::unique_ptr<IAaptContext> context =
150      test::ContextBuilder()
151          .AddSymbolSource(
152              util::make_unique<ResourceTableSymbolSource>(table.get()))
153          .SetNameManglerPolicy(NameManglerPolicy{"android"})
154          .Build();
155
156  JavaClassGeneratorOptions options;
157  options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
158  {
159    JavaClassGenerator generator(context.get(), table.get(), options);
160    std::stringstream out;
161    ASSERT_TRUE(generator.Generate("android", &out));
162    std::string output = out.str();
163    EXPECT_NE(std::string::npos,
164              output.find("public static final int one=0x01020000;"));
165    EXPECT_EQ(std::string::npos, output.find("two"));
166    EXPECT_EQ(std::string::npos, output.find("three"));
167  }
168
169  options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
170  {
171    JavaClassGenerator generator(context.get(), table.get(), options);
172    std::stringstream out;
173    ASSERT_TRUE(generator.Generate("android", &out));
174    std::string output = out.str();
175    EXPECT_NE(std::string::npos,
176              output.find("public static final int one=0x01020000;"));
177    EXPECT_NE(std::string::npos,
178              output.find("public static final int two=0x01020001;"));
179    EXPECT_EQ(std::string::npos, output.find("three"));
180  }
181
182  options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
183  {
184    JavaClassGenerator generator(context.get(), table.get(), options);
185    std::stringstream out;
186    ASSERT_TRUE(generator.Generate("android", &out));
187    std::string output = out.str();
188    EXPECT_NE(std::string::npos,
189              output.find("public static final int one=0x01020000;"));
190    EXPECT_NE(std::string::npos,
191              output.find("public static final int two=0x01020001;"));
192    EXPECT_NE(std::string::npos,
193              output.find("public static final int three=0x01020002;"));
194  }
195}
196
197/*
198 * TODO(adamlesinski): Re-enable this once we get merging working again.
199 * TEST(JavaClassGeneratorTest, EmitPackageMangledSymbols) {
200    ASSERT_TRUE(addResource(ResourceName{ {}, ResourceType::kId, u"foo" },
201                            ResourceId{ 0x01, 0x02, 0x0000 }));
202    ResourceTable table;
203    table.setPackage(u"com.lib");
204    ASSERT_TRUE(table.addResource(ResourceName{ {}, ResourceType::kId, u"test"
205}, {},
206                                  Source{ "lib.xml", 33 },
207util::make_unique<Id>()));
208    ASSERT_TRUE(mTable->merge(std::move(table)));
209
210    Linker linker(mTable,
211                  std::make_shared<MockResolver>(mTable, std::map<ResourceName,
212ResourceId>()),
213                  {});
214    ASSERT_TRUE(linker.linkAndValidate());
215
216    JavaClassGenerator generator(mTable, {});
217
218    std::stringstream out;
219    EXPECT_TRUE(generator.generate(mTable->getPackage(), out));
220    std::string output = out.str();
221    EXPECT_NE(std::string::npos, output.find("int foo ="));
222    EXPECT_EQ(std::string::npos, output.find("int test ="));
223
224    out.str("");
225    EXPECT_TRUE(generator.generate(u"com.lib", out));
226    output = out.str();
227    EXPECT_NE(std::string::npos, output.find("int test ="));
228    EXPECT_EQ(std::string::npos, output.find("int foo ="));
229}*/
230
231TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
232  std::unique_ptr<ResourceTable> table =
233      test::ResourceTableBuilder()
234          .SetPackageId("android", 0x01)
235          .SetPackageId("com.lib", 0x02)
236          .AddValue("android:attr/bar", ResourceId(0x01010000),
237                    test::AttributeBuilder(false).Build())
238          .AddValue("com.lib:attr/bar", ResourceId(0x02010000),
239                    test::AttributeBuilder(false).Build())
240          .AddValue("android:styleable/foo", ResourceId(0x01030000),
241                    test::StyleableBuilder()
242                        .AddItem("android:attr/bar", ResourceId(0x01010000))
243                        .AddItem("com.lib:attr/bar", ResourceId(0x02010000))
244                        .Build())
245          .Build();
246
247  std::unique_ptr<IAaptContext> context =
248      test::ContextBuilder()
249          .AddSymbolSource(
250              util::make_unique<ResourceTableSymbolSource>(table.get()))
251          .SetNameManglerPolicy(NameManglerPolicy{"android"})
252          .Build();
253  JavaClassGenerator generator(context.get(), table.get(), {});
254
255  std::stringstream out;
256  EXPECT_TRUE(generator.Generate("android", &out));
257
258  std::string output = out.str();
259  EXPECT_NE(std::string::npos, output.find("int foo_bar="));
260  EXPECT_NE(std::string::npos, output.find("int foo_com_lib_bar="));
261}
262
263TEST(JavaClassGeneratorTest, CommentsForSimpleResourcesArePresent) {
264  std::unique_ptr<ResourceTable> table =
265      test::ResourceTableBuilder()
266          .SetPackageId("android", 0x01)
267          .AddSimple("android:id/foo", ResourceId(0x01010000))
268          .Build();
269  test::GetValue<Id>(table.get(), "android:id/foo")
270      ->SetComment(std::string("This is a comment\n@deprecated"));
271
272  std::unique_ptr<IAaptContext> context =
273      test::ContextBuilder()
274          .AddSymbolSource(
275              util::make_unique<ResourceTableSymbolSource>(table.get()))
276          .SetNameManglerPolicy(NameManglerPolicy{"android"})
277          .Build();
278  JavaClassGenerator generator(context.get(), table.get(), {});
279  std::stringstream out;
280  ASSERT_TRUE(generator.Generate("android", &out));
281  std::string actual = out.str();
282
283  const char* expectedText =
284      R"EOF(/**
285     * This is a comment
286     * @deprecated
287     */
288    @Deprecated
289    public static final int foo=0x01010000;)EOF";
290
291  EXPECT_NE(std::string::npos, actual.find(expectedText));
292}
293
294TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) {}
295
296TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent) {
297  Attribute attr(false);
298  attr.SetComment(StringPiece("This is an attribute"));
299
300  Styleable styleable;
301  styleable.entries.push_back(
302      Reference(test::ParseNameOrDie("android:attr/one")));
303  styleable.SetComment(StringPiece("This is a styleable"));
304
305  std::unique_ptr<ResourceTable> table =
306      test::ResourceTableBuilder()
307          .SetPackageId("android", 0x01)
308          .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
309          .AddValue("android:styleable/Container",
310                    std::unique_ptr<Styleable>(styleable.Clone(nullptr)))
311          .Build();
312
313  std::unique_ptr<IAaptContext> context =
314      test::ContextBuilder()
315          .AddSymbolSource(
316              util::make_unique<ResourceTableSymbolSource>(table.get()))
317          .SetNameManglerPolicy(NameManglerPolicy{"android"})
318          .Build();
319  JavaClassGeneratorOptions options;
320  options.use_final = false;
321  JavaClassGenerator generator(context.get(), table.get(), options);
322  std::stringstream out;
323  ASSERT_TRUE(generator.Generate("android", &out));
324  std::string actual = out.str();
325
326  EXPECT_NE(std::string::npos, actual.find("attr name android:one"));
327  EXPECT_NE(std::string::npos, actual.find("attr description"));
328  EXPECT_NE(std::string::npos, actual.find(attr.GetComment().data()));
329  EXPECT_NE(std::string::npos, actual.find(styleable.GetComment().data()));
330}
331
332TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
333  Attribute attr(false);
334  attr.SetComment(StringPiece("removed"));
335
336  std::unique_ptr<ResourceTable> table =
337      test::ResourceTableBuilder()
338          .SetPackageId("android", 0x01)
339          .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
340          .Build();
341
342  std::unique_ptr<IAaptContext> context =
343      test::ContextBuilder()
344          .AddSymbolSource(
345              util::make_unique<ResourceTableSymbolSource>(table.get()))
346          .SetNameManglerPolicy(NameManglerPolicy{"android"})
347          .Build();
348  JavaClassGeneratorOptions options;
349  options.use_final = false;
350  JavaClassGenerator generator(context.get(), table.get(), options);
351  std::stringstream out;
352  ASSERT_TRUE(generator.Generate("android", &out));
353  std::string actual = out.str();
354
355  EXPECT_EQ(std::string::npos, actual.find("@attr name android:one"));
356  EXPECT_EQ(std::string::npos, actual.find("@attr description"));
357
358  // We should find @removed only in the attribute javadoc and not anywhere else
359  // (i.e. the class
360  // javadoc).
361  const size_t pos = actual.find("removed");
362  EXPECT_NE(std::string::npos, pos);
363  EXPECT_EQ(std::string::npos, actual.find("removed", pos + 1));
364}
365
366TEST(JavaClassGeneratorTest, GenerateOnResourcesLoadedCallbackForSharedLibrary) {
367  std::unique_ptr<ResourceTable> table =
368      test::ResourceTableBuilder()
369          .SetPackageId("android", 0x00)
370          .AddValue("android:attr/foo", ResourceId(0x00010000), util::make_unique<Attribute>(false))
371          .AddValue("android:id/foo", ResourceId(0x00020000), util::make_unique<Id>())
372          .AddValue(
373              "android:style/foo", ResourceId(0x00030000),
374              test::StyleBuilder()
375                  .AddItem("android:attr/foo", ResourceId(0x00010000), util::make_unique<Id>())
376                  .Build())
377          .Build();
378
379  std::unique_ptr<IAaptContext> context =
380      test::ContextBuilder().SetPackageId(0x00).SetCompilationPackage("android").Build();
381
382  JavaClassGeneratorOptions options;
383  options.use_final = false;
384  options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{
385      {"com.foo", "com.boo"},
386  };
387  JavaClassGenerator generator(context.get(), table.get(), options);
388
389  std::stringstream out;
390  ASSERT_TRUE(generator.Generate("android", &out));
391
392  std::string actual = out.str();
393
394  EXPECT_NE(std::string::npos, actual.find("void onResourcesLoaded"));
395  EXPECT_NE(std::string::npos, actual.find("com.foo.R.onResourcesLoaded"));
396  EXPECT_NE(std::string::npos, actual.find("com.boo.R.onResourcesLoaded"));
397}
398
399}  // namespace aapt
400