blob: 7af463528392c527ddea831ecfb5821e85225c0f [file] [log] [blame]
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +02001// Copyright 2020 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 rust
16
17import (
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +020018 "encoding/json"
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +020019 "io/ioutil"
20 "path/filepath"
Thiébaud Weksteene8b0ee72021-03-25 09:26:07 +010021 "sort"
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +020022 "strings"
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +020023 "testing"
24
25 "android/soong/android"
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +020026)
27
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +020028// testProjectJson run the generation of rust-project.json. It returns the raw
29// content of the generated file.
Thiébaud Weksteen0a75e522020-10-07 14:30:03 +020030func testProjectJson(t *testing.T, bp string) []byte {
Paul Duffin2c4ca8d2021-03-07 19:18:38 +000031 result := prepareForRustTest.
32 Extend(android.FixtureMergeEnv(map[string]string{"SOONG_GEN_RUST_PROJECT": "1"})).
33 RunTestWithBp(t, bp)
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +020034
35 // The JSON file is generated via WriteFileToOutputDir. Therefore, it
36 // won't appear in the Output of the TestingSingleton. Manually verify
37 // it exists.
Paul Duffin2c4ca8d2021-03-07 19:18:38 +000038 content, err := ioutil.ReadFile(filepath.Join(result.Config.BuildDir(), rustProjectJsonFileName))
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +020039 if err != nil {
40 t.Errorf("rust-project.json has not been generated")
41 }
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +020042 return content
43}
44
45// validateJsonCrates validates that content follows the basic structure of
46// rust-project.json. It returns the crates attribute if the validation
47// succeeded.
48// It uses an empty interface instead of relying on a defined structure to
49// avoid a strong dependency on our implementation.
50func validateJsonCrates(t *testing.T, rawContent []byte) []interface{} {
51 var content interface{}
52 err := json.Unmarshal(rawContent, &content)
53 if err != nil {
54 t.Errorf("Unable to parse the rust-project.json as JSON: %v", err)
55 }
56 root, ok := content.(map[string]interface{})
57 if !ok {
58 t.Errorf("Unexpected JSON format: %v", content)
59 }
60 if _, ok = root["crates"]; !ok {
61 t.Errorf("No crates attribute in rust-project.json: %v", root)
62 }
63 crates, ok := root["crates"].([]interface{})
64 if !ok {
65 t.Errorf("Unexpected crates format: %v", root["crates"])
66 }
67 return crates
68}
69
Thiébaud Weksteen064f6e92020-12-03 20:58:32 +010070// validateCrate ensures that a crate can be parsed as a map.
71func validateCrate(t *testing.T, crate interface{}) map[string]interface{} {
72 c, ok := crate.(map[string]interface{})
73 if !ok {
74 t.Fatalf("Unexpected type for crate: %v", c)
75 }
76 return c
77}
78
79// validateDependencies parses the dependencies for a crate. It returns a list
80// of the dependencies name.
81func validateDependencies(t *testing.T, crate map[string]interface{}) []string {
82 var dependencies []string
83 deps, ok := crate["deps"].([]interface{})
84 if !ok {
85 t.Errorf("Unexpected format for deps: %v", crate["deps"])
86 }
87 for _, dep := range deps {
88 d, ok := dep.(map[string]interface{})
89 if !ok {
90 t.Errorf("Unexpected format for dependency: %v", dep)
91 }
92 name, ok := d["name"].(string)
93 if !ok {
94 t.Errorf("Dependency is missing the name key: %v", d)
95 }
96 dependencies = append(dependencies, name)
97 }
98 return dependencies
99}
100
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +0200101func TestProjectJsonDep(t *testing.T) {
102 bp := `
103 rust_library {
104 name: "liba",
105 srcs: ["a/src/lib.rs"],
106 crate_name: "a"
107 }
108 rust_library {
109 name: "libb",
110 srcs: ["b/src/lib.rs"],
111 crate_name: "b",
112 rlibs: ["liba"],
113 }
Thiébaud Weksteen0a75e522020-10-07 14:30:03 +0200114 `
115 jsonContent := testProjectJson(t, bp)
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +0200116 validateJsonCrates(t, jsonContent)
117}
118
Thiébaud Weksteene8b0ee72021-03-25 09:26:07 +0100119func TestProjectJsonFeature(t *testing.T) {
120 bp := `
121 rust_library {
122 name: "liba",
123 srcs: ["a/src/lib.rs"],
124 crate_name: "a",
125 features: ["f1", "f2"]
126 }
127 `
128 jsonContent := testProjectJson(t, bp)
129 crates := validateJsonCrates(t, jsonContent)
130 for _, c := range crates {
131 crate := validateCrate(t, c)
132 cfgs, ok := crate["cfg"].([]interface{})
133 if !ok {
134 t.Fatalf("Unexpected type for cfgs: %v", crate)
135 }
136 expectedCfgs := []string{"feature=\"f1\"", "feature=\"f2\""}
137 foundCfgs := []string{}
138 for _, cfg := range cfgs {
139 cfg, ok := cfg.(string)
140 if !ok {
141 t.Fatalf("Unexpected type for cfg: %v", cfg)
142 }
143 foundCfgs = append(foundCfgs, cfg)
144 }
145 sort.Strings(foundCfgs)
146 for i, foundCfg := range foundCfgs {
147 if foundCfg != expectedCfgs[i] {
148 t.Errorf("Incorrect features: got %v; want %v", foundCfg, expectedCfgs[i])
149 }
150 }
151 }
152}
153
Thiébaud Weksteen064f6e92020-12-03 20:58:32 +0100154func TestProjectJsonBinary(t *testing.T) {
155 bp := `
156 rust_binary {
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100157 name: "libz",
158 srcs: ["z/src/lib.rs"],
159 crate_name: "z"
Thiébaud Weksteen064f6e92020-12-03 20:58:32 +0100160 }
161 `
162 jsonContent := testProjectJson(t, bp)
163 crates := validateJsonCrates(t, jsonContent)
164 for _, c := range crates {
165 crate := validateCrate(t, c)
166 rootModule, ok := crate["root_module"].(string)
167 if !ok {
168 t.Fatalf("Unexpected type for root_module: %v", crate["root_module"])
169 }
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100170 if rootModule == "z/src/lib.rs" {
Thiébaud Weksteen064f6e92020-12-03 20:58:32 +0100171 return
172 }
173 }
174 t.Errorf("Entry for binary %q not found: %s", "a", jsonContent)
175}
176
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +0200177func TestProjectJsonBindGen(t *testing.T) {
178 bp := `
179 rust_library {
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100180 name: "libd",
181 srcs: ["d/src/lib.rs"],
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200182 rlibs: ["libbindings1"],
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100183 crate_name: "d"
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +0200184 }
185 rust_bindgen {
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200186 name: "libbindings1",
187 crate_name: "bindings1",
188 source_stem: "bindings1",
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +0200189 host_supported: true,
190 wrapper_src: "src/any.h",
191 }
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200192 rust_library_host {
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100193 name: "libe",
194 srcs: ["e/src/lib.rs"],
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200195 rustlibs: ["libbindings2"],
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100196 crate_name: "e"
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200197 }
198 rust_bindgen_host {
199 name: "libbindings2",
200 crate_name: "bindings2",
201 source_stem: "bindings2",
202 wrapper_src: "src/any.h",
203 }
Thiébaud Weksteen0a75e522020-10-07 14:30:03 +0200204 `
205 jsonContent := testProjectJson(t, bp)
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200206 crates := validateJsonCrates(t, jsonContent)
207 for _, c := range crates {
Thiébaud Weksteen064f6e92020-12-03 20:58:32 +0100208 crate := validateCrate(t, c)
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200209 rootModule, ok := crate["root_module"].(string)
210 if !ok {
211 t.Fatalf("Unexpected type for root_module: %v", crate["root_module"])
212 }
213 if strings.Contains(rootModule, "libbindings1") && !strings.Contains(rootModule, "android_arm64") {
Thiébaud Weksteen4f6c7662020-09-30 13:33:41 +0200214 t.Errorf("The source path for libbindings1 does not contain android_arm64, got %v", rootModule)
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200215 }
Thiébaud Weksteen4f6c7662020-09-30 13:33:41 +0200216 if strings.Contains(rootModule, "libbindings2") && !strings.Contains(rootModule, android.BuildOs.String()) {
217 t.Errorf("The source path for libbindings2 does not contain the BuildOs, got %v; want %v",
218 rootModule, android.BuildOs.String())
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200219 }
Thiébaud Weksteen3c5905b2020-11-25 16:09:32 +0100220 // Check that libbindings1 does not depend on itself.
221 if strings.Contains(rootModule, "libbindings1") {
Thiébaud Weksteen064f6e92020-12-03 20:58:32 +0100222 for _, depName := range validateDependencies(t, crate) {
223 if depName == "bindings1" {
Thiébaud Weksteen3c5905b2020-11-25 16:09:32 +0100224 t.Errorf("libbindings1 depends on itself")
225 }
226 }
227 }
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100228 if strings.Contains(rootModule, "d/src/lib.rs") {
Thiébaud Weksteenee6a89b2021-02-25 16:30:57 +0100229 // Check that libd depends on libbindings1
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100230 found := false
231 for _, depName := range validateDependencies(t, crate) {
232 if depName == "bindings1" {
233 found = true
234 break
235 }
236 }
237 if !found {
Thiébaud Weksteenee6a89b2021-02-25 16:30:57 +0100238 t.Errorf("libd does not depend on libbindings1: %v", crate)
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100239 }
Thiébaud Weksteenee6a89b2021-02-25 16:30:57 +0100240 // Check that OUT_DIR is populated.
241 env, ok := crate["env"].(map[string]interface{})
242 if !ok {
243 t.Errorf("libd does not have its environment variables set: %v", crate)
244 }
245 if _, ok = env["OUT_DIR"]; !ok {
246 t.Errorf("libd does not have its OUT_DIR set: %v", env)
247 }
248
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100249 }
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200250 }
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +0200251}
Thiébaud Weksteen2f628ba2020-08-05 14:27:32 +0200252
253func TestProjectJsonMultiVersion(t *testing.T) {
254 bp := `
255 rust_library {
256 name: "liba1",
257 srcs: ["a1/src/lib.rs"],
258 crate_name: "a"
259 }
260 rust_library {
261 name: "liba2",
262 srcs: ["a2/src/lib.rs"],
263 crate_name: "a",
264 }
265 rust_library {
266 name: "libb",
267 srcs: ["b/src/lib.rs"],
268 crate_name: "b",
269 rustlibs: ["liba1", "liba2"],
270 }
Thiébaud Weksteen0a75e522020-10-07 14:30:03 +0200271 `
272 jsonContent := testProjectJson(t, bp)
Thiébaud Weksteen2f628ba2020-08-05 14:27:32 +0200273 crates := validateJsonCrates(t, jsonContent)
Thiébaud Weksteen064f6e92020-12-03 20:58:32 +0100274 for _, c := range crates {
275 crate := validateCrate(t, c)
276 rootModule, ok := crate["root_module"].(string)
277 if !ok {
278 t.Fatalf("Unexpected type for root_module: %v", crate["root_module"])
279 }
280 // Make sure that b has 2 different dependencies.
281 if rootModule == "b/src/lib.rs" {
Thiébaud Weksteen2f628ba2020-08-05 14:27:32 +0200282 aCount := 0
Thiébaud Weksteen064f6e92020-12-03 20:58:32 +0100283 deps := validateDependencies(t, crate)
284 for _, depName := range deps {
285 if depName == "a" {
Thiébaud Weksteen2f628ba2020-08-05 14:27:32 +0200286 aCount++
287 }
288 }
289 if aCount != 2 {
290 t.Errorf("Unexpected number of liba dependencies want %v, got %v: %v", 2, aCount, deps)
291 }
292 return
293 }
294 }
295 t.Errorf("libb crate has not been found: %v", crates)
296}