blob: 16699c19ad9128f2dc146ae80ba040b592c90706 [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 {
30 tctx := newTestRustCtx(t, bp)
31 tctx.env = map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}
32 tctx.generateConfig()
33 tctx.parse(t)
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.
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +020038 content, err := ioutil.ReadFile(filepath.Join(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
70func TestProjectJsonDep(t *testing.T) {
71 bp := `
72 rust_library {
73 name: "liba",
74 srcs: ["a/src/lib.rs"],
75 crate_name: "a"
76 }
77 rust_library {
78 name: "libb",
79 srcs: ["b/src/lib.rs"],
80 crate_name: "b",
81 rlibs: ["liba"],
82 }
Thiébaud Weksteen0a75e522020-10-07 14:30:03 +020083 `
84 jsonContent := testProjectJson(t, bp)
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +020085 validateJsonCrates(t, jsonContent)
86}
87
88func TestProjectJsonBindGen(t *testing.T) {
89 bp := `
90 rust_library {
91 name: "liba",
92 srcs: ["src/lib.rs"],
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +020093 rlibs: ["libbindings1"],
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +020094 crate_name: "a"
95 }
96 rust_bindgen {
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +020097 name: "libbindings1",
98 crate_name: "bindings1",
99 source_stem: "bindings1",
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +0200100 host_supported: true,
101 wrapper_src: "src/any.h",
102 }
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200103 rust_library_host {
104 name: "libb",
105 srcs: ["src/lib.rs"],
106 rustlibs: ["libbindings2"],
107 crate_name: "b"
108 }
109 rust_bindgen_host {
110 name: "libbindings2",
111 crate_name: "bindings2",
112 source_stem: "bindings2",
113 wrapper_src: "src/any.h",
114 }
Thiébaud Weksteen0a75e522020-10-07 14:30:03 +0200115 `
116 jsonContent := testProjectJson(t, bp)
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200117 crates := validateJsonCrates(t, jsonContent)
118 for _, c := range crates {
119 crate, ok := c.(map[string]interface{})
120 if !ok {
121 t.Fatalf("Unexpected type for crate: %v", c)
122 }
123 rootModule, ok := crate["root_module"].(string)
124 if !ok {
125 t.Fatalf("Unexpected type for root_module: %v", crate["root_module"])
126 }
127 if strings.Contains(rootModule, "libbindings1") && !strings.Contains(rootModule, "android_arm64") {
Thiébaud Weksteen4f6c7662020-09-30 13:33:41 +0200128 t.Errorf("The source path for libbindings1 does not contain android_arm64, got %v", rootModule)
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200129 }
Thiébaud Weksteen4f6c7662020-09-30 13:33:41 +0200130 if strings.Contains(rootModule, "libbindings2") && !strings.Contains(rootModule, android.BuildOs.String()) {
131 t.Errorf("The source path for libbindings2 does not contain the BuildOs, got %v; want %v",
132 rootModule, android.BuildOs.String())
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200133 }
Thiébaud Weksteen3c5905b2020-11-25 16:09:32 +0100134 // Check that libbindings1 does not depend on itself.
135 if strings.Contains(rootModule, "libbindings1") {
136 deps, ok := crate["deps"].([]interface{})
137 if !ok {
138 t.Errorf("Unexpected format for deps: %v", crate["deps"])
139 }
140 for _, dep := range deps {
141 d, ok := dep.(map[string]interface{})
142 if !ok {
143 t.Errorf("Unexpected format for dep: %v", dep)
144 }
145 if d["name"] == "bindings1" {
146 t.Errorf("libbindings1 depends on itself")
147 }
148 }
149 }
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200150 }
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +0200151}
Thiébaud Weksteen2f628ba2020-08-05 14:27:32 +0200152
153func TestProjectJsonMultiVersion(t *testing.T) {
154 bp := `
155 rust_library {
156 name: "liba1",
157 srcs: ["a1/src/lib.rs"],
158 crate_name: "a"
159 }
160 rust_library {
161 name: "liba2",
162 srcs: ["a2/src/lib.rs"],
163 crate_name: "a",
164 }
165 rust_library {
166 name: "libb",
167 srcs: ["b/src/lib.rs"],
168 crate_name: "b",
169 rustlibs: ["liba1", "liba2"],
170 }
Thiébaud Weksteen0a75e522020-10-07 14:30:03 +0200171 `
172 jsonContent := testProjectJson(t, bp)
Thiébaud Weksteen2f628ba2020-08-05 14:27:32 +0200173 crates := validateJsonCrates(t, jsonContent)
174 for _, crate := range crates {
175 c := crate.(map[string]interface{})
176 if c["root_module"] == "b/src/lib.rs" {
177 deps, ok := c["deps"].([]interface{})
178 if !ok {
179 t.Errorf("Unexpected format for deps: %v", c["deps"])
180 }
181 aCount := 0
182 for _, dep := range deps {
183 d, ok := dep.(map[string]interface{})
184 if !ok {
185 t.Errorf("Unexpected format for dep: %v", dep)
186 }
187 if d["name"] == "a" {
188 aCount++
189 }
190 }
191 if aCount != 2 {
192 t.Errorf("Unexpected number of liba dependencies want %v, got %v: %v", 2, aCount, deps)
193 }
194 return
195 }
196 }
197 t.Errorf("libb crate has not been found: %v", crates)
198}