blob: 8f64f56e217e97180083735ad175ee3eeae401b1 [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 Weksteena6351ca2020-09-29 09:53:21 +020021 "strings"
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +020022 "testing"
23
24 "android/soong/android"
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +020025)
26
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +020027// testProjectJson run the generation of rust-project.json. It returns the raw
28// content of the generated file.
Thiébaud Weksteen0a75e522020-10-07 14:30:03 +020029func testProjectJson(t *testing.T, bp string) []byte {
Paul Duffin2c4ca8d2021-03-07 19:18:38 +000030 result := prepareForRustTest.
31 Extend(android.FixtureMergeEnv(map[string]string{"SOONG_GEN_RUST_PROJECT": "1"})).
32 RunTestWithBp(t, bp)
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +020033
34 // The JSON file is generated via WriteFileToOutputDir. Therefore, it
35 // won't appear in the Output of the TestingSingleton. Manually verify
36 // it exists.
Paul Duffin2c4ca8d2021-03-07 19:18:38 +000037 content, err := ioutil.ReadFile(filepath.Join(result.Config.BuildDir(), rustProjectJsonFileName))
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +020038 if err != nil {
39 t.Errorf("rust-project.json has not been generated")
40 }
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +020041 return content
42}
43
44// validateJsonCrates validates that content follows the basic structure of
45// rust-project.json. It returns the crates attribute if the validation
46// succeeded.
47// It uses an empty interface instead of relying on a defined structure to
48// avoid a strong dependency on our implementation.
49func validateJsonCrates(t *testing.T, rawContent []byte) []interface{} {
50 var content interface{}
51 err := json.Unmarshal(rawContent, &content)
52 if err != nil {
53 t.Errorf("Unable to parse the rust-project.json as JSON: %v", err)
54 }
55 root, ok := content.(map[string]interface{})
56 if !ok {
57 t.Errorf("Unexpected JSON format: %v", content)
58 }
59 if _, ok = root["crates"]; !ok {
60 t.Errorf("No crates attribute in rust-project.json: %v", root)
61 }
62 crates, ok := root["crates"].([]interface{})
63 if !ok {
64 t.Errorf("Unexpected crates format: %v", root["crates"])
65 }
66 return crates
67}
68
Thiébaud Weksteen064f6e92020-12-03 20:58:32 +010069// validateCrate ensures that a crate can be parsed as a map.
70func validateCrate(t *testing.T, crate interface{}) map[string]interface{} {
71 c, ok := crate.(map[string]interface{})
72 if !ok {
73 t.Fatalf("Unexpected type for crate: %v", c)
74 }
75 return c
76}
77
78// validateDependencies parses the dependencies for a crate. It returns a list
79// of the dependencies name.
80func validateDependencies(t *testing.T, crate map[string]interface{}) []string {
81 var dependencies []string
82 deps, ok := crate["deps"].([]interface{})
83 if !ok {
84 t.Errorf("Unexpected format for deps: %v", crate["deps"])
85 }
86 for _, dep := range deps {
87 d, ok := dep.(map[string]interface{})
88 if !ok {
89 t.Errorf("Unexpected format for dependency: %v", dep)
90 }
91 name, ok := d["name"].(string)
92 if !ok {
93 t.Errorf("Dependency is missing the name key: %v", d)
94 }
95 dependencies = append(dependencies, name)
96 }
97 return dependencies
98}
99
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +0200100func TestProjectJsonDep(t *testing.T) {
101 bp := `
102 rust_library {
103 name: "liba",
104 srcs: ["a/src/lib.rs"],
105 crate_name: "a"
106 }
107 rust_library {
108 name: "libb",
109 srcs: ["b/src/lib.rs"],
110 crate_name: "b",
111 rlibs: ["liba"],
112 }
Thiébaud Weksteen0a75e522020-10-07 14:30:03 +0200113 `
114 jsonContent := testProjectJson(t, bp)
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +0200115 validateJsonCrates(t, jsonContent)
116}
117
Thiébaud Weksteen064f6e92020-12-03 20:58:32 +0100118func TestProjectJsonBinary(t *testing.T) {
119 bp := `
120 rust_binary {
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100121 name: "libz",
122 srcs: ["z/src/lib.rs"],
123 crate_name: "z"
Thiébaud Weksteen064f6e92020-12-03 20:58:32 +0100124 }
125 `
126 jsonContent := testProjectJson(t, bp)
127 crates := validateJsonCrates(t, jsonContent)
128 for _, c := range crates {
129 crate := validateCrate(t, c)
130 rootModule, ok := crate["root_module"].(string)
131 if !ok {
132 t.Fatalf("Unexpected type for root_module: %v", crate["root_module"])
133 }
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100134 if rootModule == "z/src/lib.rs" {
Thiébaud Weksteen064f6e92020-12-03 20:58:32 +0100135 return
136 }
137 }
138 t.Errorf("Entry for binary %q not found: %s", "a", jsonContent)
139}
140
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +0200141func TestProjectJsonBindGen(t *testing.T) {
142 bp := `
143 rust_library {
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100144 name: "libd",
145 srcs: ["d/src/lib.rs"],
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200146 rlibs: ["libbindings1"],
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100147 crate_name: "d"
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +0200148 }
149 rust_bindgen {
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200150 name: "libbindings1",
151 crate_name: "bindings1",
152 source_stem: "bindings1",
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +0200153 host_supported: true,
154 wrapper_src: "src/any.h",
155 }
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200156 rust_library_host {
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100157 name: "libe",
158 srcs: ["e/src/lib.rs"],
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200159 rustlibs: ["libbindings2"],
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100160 crate_name: "e"
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200161 }
162 rust_bindgen_host {
163 name: "libbindings2",
164 crate_name: "bindings2",
165 source_stem: "bindings2",
166 wrapper_src: "src/any.h",
167 }
Thiébaud Weksteen0a75e522020-10-07 14:30:03 +0200168 `
169 jsonContent := testProjectJson(t, bp)
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200170 crates := validateJsonCrates(t, jsonContent)
171 for _, c := range crates {
Thiébaud Weksteen064f6e92020-12-03 20:58:32 +0100172 crate := validateCrate(t, c)
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200173 rootModule, ok := crate["root_module"].(string)
174 if !ok {
175 t.Fatalf("Unexpected type for root_module: %v", crate["root_module"])
176 }
177 if strings.Contains(rootModule, "libbindings1") && !strings.Contains(rootModule, "android_arm64") {
Thiébaud Weksteen4f6c7662020-09-30 13:33:41 +0200178 t.Errorf("The source path for libbindings1 does not contain android_arm64, got %v", rootModule)
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200179 }
Thiébaud Weksteen4f6c7662020-09-30 13:33:41 +0200180 if strings.Contains(rootModule, "libbindings2") && !strings.Contains(rootModule, android.BuildOs.String()) {
181 t.Errorf("The source path for libbindings2 does not contain the BuildOs, got %v; want %v",
182 rootModule, android.BuildOs.String())
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200183 }
Thiébaud Weksteen3c5905b2020-11-25 16:09:32 +0100184 // Check that libbindings1 does not depend on itself.
185 if strings.Contains(rootModule, "libbindings1") {
Thiébaud Weksteen064f6e92020-12-03 20:58:32 +0100186 for _, depName := range validateDependencies(t, crate) {
187 if depName == "bindings1" {
Thiébaud Weksteen3c5905b2020-11-25 16:09:32 +0100188 t.Errorf("libbindings1 depends on itself")
189 }
190 }
191 }
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100192 if strings.Contains(rootModule, "d/src/lib.rs") {
Thiébaud Weksteenee6a89b2021-02-25 16:30:57 +0100193 // Check that libd depends on libbindings1
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100194 found := false
195 for _, depName := range validateDependencies(t, crate) {
196 if depName == "bindings1" {
197 found = true
198 break
199 }
200 }
201 if !found {
Thiébaud Weksteenee6a89b2021-02-25 16:30:57 +0100202 t.Errorf("libd does not depend on libbindings1: %v", crate)
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100203 }
Thiébaud Weksteenee6a89b2021-02-25 16:30:57 +0100204 // Check that OUT_DIR is populated.
205 env, ok := crate["env"].(map[string]interface{})
206 if !ok {
207 t.Errorf("libd does not have its environment variables set: %v", crate)
208 }
209 if _, ok = env["OUT_DIR"]; !ok {
210 t.Errorf("libd does not have its OUT_DIR set: %v", env)
211 }
212
Thiébaud Weksteenfa5feae2020-12-07 13:40:19 +0100213 }
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200214 }
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +0200215}
Thiébaud Weksteen2f628ba2020-08-05 14:27:32 +0200216
217func TestProjectJsonMultiVersion(t *testing.T) {
218 bp := `
219 rust_library {
220 name: "liba1",
221 srcs: ["a1/src/lib.rs"],
222 crate_name: "a"
223 }
224 rust_library {
225 name: "liba2",
226 srcs: ["a2/src/lib.rs"],
227 crate_name: "a",
228 }
229 rust_library {
230 name: "libb",
231 srcs: ["b/src/lib.rs"],
232 crate_name: "b",
233 rustlibs: ["liba1", "liba2"],
234 }
Thiébaud Weksteen0a75e522020-10-07 14:30:03 +0200235 `
236 jsonContent := testProjectJson(t, bp)
Thiébaud Weksteen2f628ba2020-08-05 14:27:32 +0200237 crates := validateJsonCrates(t, jsonContent)
Thiébaud Weksteen064f6e92020-12-03 20:58:32 +0100238 for _, c := range crates {
239 crate := validateCrate(t, c)
240 rootModule, ok := crate["root_module"].(string)
241 if !ok {
242 t.Fatalf("Unexpected type for root_module: %v", crate["root_module"])
243 }
244 // Make sure that b has 2 different dependencies.
245 if rootModule == "b/src/lib.rs" {
Thiébaud Weksteen2f628ba2020-08-05 14:27:32 +0200246 aCount := 0
Thiébaud Weksteen064f6e92020-12-03 20:58:32 +0100247 deps := validateDependencies(t, crate)
248 for _, depName := range deps {
249 if depName == "a" {
Thiébaud Weksteen2f628ba2020-08-05 14:27:32 +0200250 aCount++
251 }
252 }
253 if aCount != 2 {
254 t.Errorf("Unexpected number of liba dependencies want %v, got %v: %v", 2, aCount, deps)
255 }
256 return
257 }
258 }
259 t.Errorf("libb crate has not been found: %v", crates)
260}