blob: 6d7052ebef0521a225b5f90cce68e600c4ace046 [file] [log] [blame]
Colin Cross0ef08162019-05-01 15:50:51 -07001// Copyright 2019 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package java
16
17import (
18 "fmt"
19 "io"
20 "strings"
21
22 "android/soong/android"
23)
24
25func init() {
26 android.RegisterModuleType("android_robolectric_test", RobolectricTestFactory)
27}
28
29var robolectricDefaultLibs = []string{
30 "robolectric_android-all-stub",
31 "Robolectric_all-target",
32 "mockito-robolectric-prebuilt",
33 "truth-prebuilt",
34}
35
Colin Cross3ec27ec2019-05-01 15:54:05 -070036var (
37 roboCoverageLibsTag = dependencyTag{name: "roboSrcs"}
38)
39
Colin Cross0ef08162019-05-01 15:50:51 -070040type robolectricProperties struct {
41 // The name of the android_app module that the tests will run against.
42 Instrumentation_for *string
43
Colin Cross3ec27ec2019-05-01 15:54:05 -070044 // Additional libraries for which coverage data should be generated
45 Coverage_libs []string
46
Colin Cross0ef08162019-05-01 15:50:51 -070047 Test_options struct {
48 // Timeout in seconds when running the tests.
49 Timeout *string
50 }
51}
52
53type robolectricTest struct {
54 Library
55
56 robolectricProperties robolectricProperties
57
58 libs []string
Colin Cross3ec27ec2019-05-01 15:54:05 -070059
60 roboSrcJar android.Path
Colin Cross0ef08162019-05-01 15:50:51 -070061}
62
63func (r *robolectricTest) DepsMutator(ctx android.BottomUpMutatorContext) {
64 r.Library.DepsMutator(ctx)
65
66 if r.robolectricProperties.Instrumentation_for != nil {
67 ctx.AddVariationDependencies(nil, instrumentationForTag, String(r.robolectricProperties.Instrumentation_for))
68 } else {
69 ctx.PropertyErrorf("instrumentation_for", "missing required instrumented module")
70 }
71
72 ctx.AddVariationDependencies(nil, libTag, robolectricDefaultLibs...)
Colin Cross3ec27ec2019-05-01 15:54:05 -070073
74 ctx.AddVariationDependencies(nil, roboCoverageLibsTag, r.robolectricProperties.Coverage_libs...)
Colin Cross0ef08162019-05-01 15:50:51 -070075}
76
77func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
Colin Cross3ec27ec2019-05-01 15:54:05 -070078 roboTestConfig := android.PathForModuleGen(ctx, "robolectric").
79 Join(ctx, "com/android/tools/test_config.properties")
80
81 // TODO: this inserts paths to built files into the test, it should really be inserting the contents.
82 instrumented := ctx.GetDirectDepsWithTag(instrumentationForTag)
83
84 if len(instrumented) != 1 {
85 panic(fmt.Errorf("expected exactly 1 instrumented dependency, got %d", len(instrumented)))
86 }
87
88 instrumentedApp, ok := instrumented[0].(*AndroidApp)
89 if !ok {
90 ctx.PropertyErrorf("instrumentation_for", "dependency must be an android_app")
91 }
92
93 generateRoboTestConfig(ctx, roboTestConfig, instrumentedApp)
94 r.extraResources = android.Paths{roboTestConfig}
95
Colin Cross0ef08162019-05-01 15:50:51 -070096 r.Library.GenerateAndroidBuildActions(ctx)
97
Colin Cross3ec27ec2019-05-01 15:54:05 -070098 roboSrcJar := android.PathForModuleGen(ctx, "robolectric", ctx.ModuleName()+".srcjar")
99 r.generateRoboSrcJar(ctx, roboSrcJar, instrumentedApp)
100 r.roboSrcJar = roboSrcJar
101
Colin Cross0ef08162019-05-01 15:50:51 -0700102 for _, dep := range ctx.GetDirectDepsWithTag(libTag) {
103 r.libs = append(r.libs, ctx.OtherModuleName(dep))
104 }
105}
106
Colin Cross3ec27ec2019-05-01 15:54:05 -0700107func generateRoboTestConfig(ctx android.ModuleContext, outputFile android.WritablePath, instrumentedApp *AndroidApp) {
108 manifest := instrumentedApp.mergedManifestFile
109 resourceApk := instrumentedApp.outputFile
110
111 rule := android.NewRuleBuilder()
112
113 rule.Command().Text("rm -f").Output(outputFile)
114 rule.Command().
115 Textf(`echo "android_merged_manifest=%s" >>`, manifest.String()).Output(outputFile).Text("&&").
116 Textf(`echo "android_resource_apk=%s" >>`, resourceApk.String()).Output(outputFile).
117 // Make it depend on the files to which it points so the test file's timestamp is updated whenever the
118 // contents change
119 Implicit(manifest).
120 Implicit(resourceApk)
121
122 rule.Build(pctx, ctx, "generate_test_config", "generate test_config.properties")
123}
124
125func (r *robolectricTest) generateRoboSrcJar(ctx android.ModuleContext, outputFile android.WritablePath,
126 instrumentedApp *AndroidApp) {
127
128 srcJarArgs := copyOf(instrumentedApp.srcJarArgs)
129 srcJarDeps := append(android.Paths(nil), instrumentedApp.srcJarDeps...)
130
131 for _, m := range ctx.GetDirectDepsWithTag(roboCoverageLibsTag) {
132 if dep, ok := m.(Dependency); ok {
133 depSrcJarArgs, depSrcJarDeps := dep.SrcJarArgs()
134 srcJarArgs = append(srcJarArgs, depSrcJarArgs...)
135 srcJarDeps = append(srcJarDeps, depSrcJarDeps...)
136 }
137 }
138
139 TransformResourcesToJar(ctx, outputFile, srcJarArgs, srcJarDeps)
140}
141
Colin Cross0ef08162019-05-01 15:50:51 -0700142func (r *robolectricTest) AndroidMk() android.AndroidMkData {
143 data := r.Library.AndroidMk()
144
145 data.Custom = func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
146 android.WriteAndroidMkData(w, data)
147
148 fmt.Fprintln(w, "")
149 fmt.Fprintln(w, "include $(CLEAR_VARS)")
150 fmt.Fprintln(w, "LOCAL_MODULE := Run"+name)
151 fmt.Fprintln(w, "LOCAL_JAVA_LIBRARIES :=", name)
152 fmt.Fprintln(w, "LOCAL_JAVA_LIBRARIES += ", strings.Join(r.libs, " "))
153 fmt.Fprintln(w, "LOCAL_TEST_PACKAGE :=", String(r.robolectricProperties.Instrumentation_for))
Colin Cross3ec27ec2019-05-01 15:54:05 -0700154 fmt.Fprintln(w, "LOCAL_INSTRUMENT_SRCJARS :=", r.roboSrcJar.String())
Colin Cross0ef08162019-05-01 15:50:51 -0700155 if t := r.robolectricProperties.Test_options.Timeout; t != nil {
156 fmt.Fprintln(w, "LOCAL_ROBOTEST_TIMEOUT :=", *t)
157 }
158 fmt.Fprintln(w, "-include external/robolectric-shadows/run_robotests.mk")
159 }
160
161 return data
162}
163
164// An android_robolectric_test module compiles tests against the Robolectric framework that can run on the local host
165// instead of on a device. It also generates a rule with the name of the module prefixed with "Run" that can be
166// used to run the tests. Running the tests with build rule will eventually be deprecated and replaced with atest.
167func RobolectricTestFactory() android.Module {
168 module := &robolectricTest{}
169
170 module.AddProperties(
171 &module.Module.properties,
172 &module.Module.protoProperties,
173 &module.robolectricProperties)
174
175 module.Module.dexpreopter.isTest = true
176
177 InitJavaModule(module, android.DeviceSupported)
178 return module
179}