1 /*
2  * 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 org.junit.Assert
20 import org.junit.Assert.assertTrue
21 import org.junit.Test
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 
29 class EndToEndTest {
30 
31     @Test
32     fun e2e_transform() {
33         val output = run(
34                 src = "frameworks/base/org/example/Example.java" to """
35                     package org.example;
36                     import com.android.internal.protolog.common.ProtoLog;
37                     import static com.android.internal.protolog.ProtoLogGroup.GROUP;
38 
39                     class Example {
40                         void method() {
41                             String argString = "hello";
42                             int argInt = 123;
43                             ProtoLog.d(GROUP, "Example: %s %d", argString, argInt);
44                         }
45                     }
46                 """.trimIndent(),
47                 logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"),
48                 commandOptions = CommandOptions(arrayOf("transform-protolog-calls",
49                         "--protolog-class", "com.android.internal.protolog.common.ProtoLog",
50                         "--protolog-impl-class", "com.android.internal.protolog.ProtoLogImpl",
51                         "--protolog-cache-class",
52                         "com.android.server.wm.ProtoLogCache",
53                         "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup",
54                         "--loggroups-jar", "not_required.jar",
55                         "--output-srcjar", "out.srcjar",
56                         "frameworks/base/org/example/Example.java"))
57         )
58         val outSrcJar = assertLoadSrcJar(output, "out.srcjar")
59         assertTrue(" 2066303299," in outSrcJar["frameworks/base/org/example/Example.java"]!!)
60     }
61 
62     @Test
63     fun e2e_viewerConfig() {
64         val output = run(
65                 src = "frameworks/base/org/example/Example.java" to """
66                     package org.example;
67                     import com.android.internal.protolog.common.ProtoLog;
68                     import static com.android.internal.protolog.ProtoLogGroup.GROUP;
69 
70                     class Example {
71                         void method() {
72                             String argString = "hello";
73                             int argInt = 123;
74                             ProtoLog.d(GROUP, "Example: %s %d", argString, argInt);
75                         }
76                     }
77                 """.trimIndent(),
78                 logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"),
79                 commandOptions = CommandOptions(arrayOf("generate-viewer-config",
80                         "--protolog-class", "com.android.internal.protolog.common.ProtoLog",
81                         "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup",
82                         "--loggroups-jar", "not_required.jar",
83                         "--viewer-conf", "out.json",
84                         "frameworks/base/org/example/Example.java"))
85         )
86         val viewerConfigJson = assertLoadText(output, "out.json")
87         assertTrue("\"2066303299\"" in viewerConfigJson)
88     }
89 
90     private fun assertLoadSrcJar(
91         outputs: Map<String, ByteArray>,
92         path: String
93     ): Map<String, String> {
94         val out = outputs[path] ?: fail("$path not in outputs (${outputs.keys})")
95 
96         val sources = mutableMapOf<String, String>()
97         JarInputStream(ByteArrayInputStream(out)).use { jarStream ->
98             var entry = jarStream.nextJarEntry
99             while (entry != null) {
100                 if (entry.name.endsWith(".java")) {
101                     sources[entry.name] = jarStream.reader().readText()
102                 }
103                 entry = jarStream.nextJarEntry
104             }
105         }
106         return sources
107     }
108 
109     private fun assertLoadText(outputs: Map<String, ByteArray>, path: String): String {
110         val out = outputs[path] ?: fail("$path not in outputs (${outputs.keys})")
111         return out.toString(Charsets.UTF_8)
112     }
113 
114     fun run(
115         src: Pair<String, String>,
116         logGroup: LogGroup,
117         commandOptions: CommandOptions
118     ): Map<String, ByteArray> {
119         val outputs = mutableMapOf<String, ByteArrayOutputStream>()
120 
121         ProtoLogTool.injector = object : ProtoLogTool.Injector {
122             override fun fileOutputStream(file: String): OutputStream =
123                     ByteArrayOutputStream().also { outputs[file] = it }
124 
125             override fun readText(file: File): String {
126                 if (file.path == src.first) {
127                     return src.second
128                 }
129                 throw FileNotFoundException("expected: ${src.first}, but was $file")
130             }
131 
132             override fun readLogGroups(jarPath: String, className: String) = mapOf(
133                     logGroup.name to logGroup)
134 
135             override fun reportParseError(ex: ParsingException) = throw AssertionError(ex)
136         }
137 
138         ProtoLogTool.invoke(commandOptions)
139 
140         return outputs.mapValues { it.value.toByteArray() }
141     }
142 
143     fun fail(message: String): Nothing = Assert.fail(message) as Nothing
144 }
145