• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2019 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 package com.android.protolog.tool
18 
19 import com.android.protolog.tool.ProtoLogTool.PROTOLOG_IMPL_SRC_PATH
20 import com.android.protolog.tool.ProtoLogTool.injector
21 import com.google.common.truth.Truth
22 import java.io.ByteArrayInputStream
23 import java.io.ByteArrayOutputStream
24 import java.io.File
25 import java.io.FileNotFoundException
26 import java.io.OutputStream
27 import java.util.jar.JarInputStream
28 import java.util.regex.Pattern
29 import org.junit.Assert
30 import org.junit.Test
31 
32 class EndToEndTest {
33 
34     @Test
35     fun e2e_transform() {
36         val output = run(
37                 srcs = mapOf("frameworks/base/org/example/Example.java" to """
38                     package org.example;
39                     import com.android.internal.protolog.ProtoLog;
40                     import static com.android.internal.protolog.ProtoLogGroup.GROUP;
41 
42                     class Example {
43                         void method() {
44                             String argString = "hello";
45                             int argInt = 123;
46                             ProtoLog.d(GROUP, "Example: %s %d", argString, argInt);
47                         }
48                     }
49                 """.trimIndent()),
50                 logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"),
51                 commandOptions = CommandOptions(arrayOf("transform-protolog-calls",
52                         "--protolog-class", "com.android.internal.protolog.ProtoLog",
53                         "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup",
54                         "--loggroups-jar", "not_required.jar",
55                         "--viewer-config-file-path", "not_required.pb",
56                         "--output-srcjar", "out.srcjar",
57                         "frameworks/base/org/example/Example.java"))
58         )
59         val outSrcJar = assertLoadSrcJar(output, "out.srcjar")
60         Truth.assertThat(outSrcJar["frameworks/base/org/example/Example.java"])
61                 .containsMatch(Pattern.compile("\\{ String protoLogParam0 = " +
62                         "String\\.valueOf\\(argString\\); long protoLogParam1 = argInt; " +
63                         "com\\.android\\.internal\\.protolog.ProtoLogImpl_.*\\.d\\(" +
64                         "GROUP, -6872339441335321086L, 4, protoLogParam0, protoLogParam1" +
65                         "\\); \\}"))
66     }
67 
68     @Test
69     fun e2e_viewerConfig() {
70         val output = run(
71                 srcs = mapOf("frameworks/base/org/example/Example.java" to """
72                     package org.example;
73                     import com.android.internal.protolog.ProtoLog;
74                     import static com.android.internal.protolog.ProtoLogGroup.GROUP;
75 
76                     class Example {
77                         void method() {
78                             String argString = "hello";
79                             int argInt = 123;
80                             ProtoLog.d(GROUP, "Example: %s %d", argString, argInt);
81                         }
82                     }
83                 """.trimIndent()),
84                 logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"),
85                 commandOptions = CommandOptions(arrayOf("generate-viewer-config",
86                         "--protolog-class", "com.android.internal.protolog.ProtoLog",
87                         "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup",
88                         "--loggroups-jar", "not_required.jar",
89                         "--viewer-config-type", "json",
90                         "--viewer-config", "out.json",
91                         "frameworks/base/org/example/Example.java"))
92         )
93         val viewerConfigJson = assertLoadText(output, "out.json")
94         Truth.assertThat(viewerConfigJson).contains("""
95             "messages": {
96                 "-6872339441335321086": {
97                   "message": "Example: %s %d",
98                   "level": "DEBUG",
99                   "group": "GROUP",
100                   "at": "org\/example\/Example.java"
101                 }
102               }
103         """.trimIndent())
104     }
105 
106     @Test
107     fun e2e_transform_withErrors() {
108         val srcs = mapOf(
109             "frameworks/base/org/example/Example.java" to """
110                     package org.example;
111                     import com.android.internal.protolog.ProtoLog;
112                     import static com.android.internal.protolog.ProtoLogGroup.GROUP;
113 
114                     class Example {
115                         void method() {
116                             String argString = "hello";
117                             int argInt = 123;
118                             ProtoLog.d(GROUP, "Invalid format: %s %d %9 %", argString, argInt);
119                         }
120                     }
121                 """.trimIndent())
122         val output = run(
123             srcs = srcs,
124             logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"),
125             commandOptions = CommandOptions(arrayOf("transform-protolog-calls",
126                 "--protolog-class", "com.android.internal.protolog.ProtoLog",
127                 "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup",
128                 "--loggroups-jar", "not_required.jar",
129                 "--viewer-config-file-path", "not_required.pb",
130                 "--output-srcjar", "out.srcjar",
131                 "frameworks/base/org/example/Example.java"))
132         )
133         val outSrcJar = assertLoadSrcJar(output, "out.srcjar")
134         // No change to source code on failure to process
135         Truth.assertThat(outSrcJar["frameworks/base/org/example/Example.java"])
136             .contains(srcs["frameworks/base/org/example/Example.java"])
137 
138         Truth.assertThat(injector.processingErrors).hasSize(1)
139         Truth.assertThat(injector.processingErrors.first().message).contains("Invalid format")
140     }
141 
142     private fun assertLoadSrcJar(
143         outputs: Map<String, ByteArray>,
144         path: String
145     ): Map<String, String> {
146         val out = outputs[path] ?: fail("$path not in outputs (${outputs.keys})")
147 
148         val sources = mutableMapOf<String, String>()
149         JarInputStream(ByteArrayInputStream(out)).use { jarStream ->
150             var entry = jarStream.nextJarEntry
151             while (entry != null) {
152                 if (entry.name.endsWith(".java")) {
153                     sources[entry.name] = jarStream.reader().readText()
154                 }
155                 entry = jarStream.nextJarEntry
156             }
157         }
158         return sources
159     }
160 
161     private fun assertLoadText(outputs: Map<String, ByteArray>, path: String): String {
162         val out = outputs[path] ?: fail("$path not in outputs (${outputs.keys})")
163         return out.toString(Charsets.UTF_8)
164     }
165 
166     fun run(
167         srcs: Map<String, String>,
168         logGroup: LogGroup,
169         commandOptions: CommandOptions
170     ): Map<String, ByteArray> {
171         val outputs = mutableMapOf<String, ByteArrayOutputStream>()
172 
173         val srcs = srcs.toMutableMap()
174         srcs[PROTOLOG_IMPL_SRC_PATH] = """
175             package com.android.internal.protolog;
176 
177             import static com.android.internal.protolog.common.ProtoLogToolInjected.Value.LEGACY_OUTPUT_FILE_PATH;
178             import static com.android.internal.protolog.common.ProtoLogToolInjected.Value.LEGACY_VIEWER_CONFIG_PATH;
179             import static com.android.internal.protolog.common.ProtoLogToolInjected.Value.VIEWER_CONFIG_PATH;
180 
181             import com.android.internal.protolog.common.ProtoLogToolInjected;
182 
183             public class ProtoLogImpl {
184                 @ProtoLogToolInjected(VIEWER_CONFIG_PATH)
185                 private static String sViewerConfigPath;
186 
187                 @ProtoLogToolInjected(LEGACY_VIEWER_CONFIG_PATH)
188                 private static String sLegacyViewerConfigPath;
189 
190                 @ProtoLogToolInjected(LEGACY_OUTPUT_FILE_PATH)
191                 private static String sLegacyOutputFilePath;
192             }
193         """.trimIndent()
194 
195         ProtoLogTool.injector = object : ProtoLogTool.Injector {
196             override fun fileOutputStream(file: String): OutputStream =
197                     ByteArrayOutputStream().also { outputs[file] = it }
198 
199             override fun readText(file: File): String {
200                 for (src in srcs.entries) {
201                     val filePath = src.key
202                     if (file.path == filePath) {
203                         return src.value
204                     }
205                 }
206                 throw FileNotFoundException("$file not found in [${srcs.keys.joinToString()}].")
207             }
208 
209             override fun readLogGroups(jarPath: String, className: String) = mapOf(
210                     logGroup.name to logGroup)
211 
212             override fun reportProcessingError(ex: CodeProcessingException) {
213                 processingErrors.add(ex)
214             }
215 
216             override val processingErrors: MutableList<CodeProcessingException> = mutableListOf()
217         }
218 
219         ProtoLogTool.invoke(commandOptions)
220 
221         return outputs.mapValues { it.value.toByteArray() }
222     }
223 
224     fun fail(message: String): Nothing = Assert.fail(message) as Nothing
225 }
226