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