blob: 11964f3453581dd741a84d080508399873007a27 [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"
25 "android/soong/cc"
26)
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.
30func testProjectJson(t *testing.T, bp string, fs map[string][]byte) []byte {
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +020031 cc.GatherRequiredFilesForTest(fs)
32
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +020033 env := map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +020034 config := android.TestArchConfig(buildDir, env, bp, fs)
35 ctx := CreateTestContext()
36 ctx.Register(config)
37 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
38 android.FailIfErrored(t, errs)
39 _, errs = ctx.PrepareBuildActions(config)
40 android.FailIfErrored(t, errs)
41
42 // The JSON file is generated via WriteFileToOutputDir. Therefore, it
43 // won't appear in the Output of the TestingSingleton. Manually verify
44 // it exists.
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +020045 content, err := ioutil.ReadFile(filepath.Join(buildDir, rustProjectJsonFileName))
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +020046 if err != nil {
47 t.Errorf("rust-project.json has not been generated")
48 }
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +020049 return content
50}
51
52// validateJsonCrates validates that content follows the basic structure of
53// rust-project.json. It returns the crates attribute if the validation
54// succeeded.
55// It uses an empty interface instead of relying on a defined structure to
56// avoid a strong dependency on our implementation.
57func validateJsonCrates(t *testing.T, rawContent []byte) []interface{} {
58 var content interface{}
59 err := json.Unmarshal(rawContent, &content)
60 if err != nil {
61 t.Errorf("Unable to parse the rust-project.json as JSON: %v", err)
62 }
63 root, ok := content.(map[string]interface{})
64 if !ok {
65 t.Errorf("Unexpected JSON format: %v", content)
66 }
67 if _, ok = root["crates"]; !ok {
68 t.Errorf("No crates attribute in rust-project.json: %v", root)
69 }
70 crates, ok := root["crates"].([]interface{})
71 if !ok {
72 t.Errorf("Unexpected crates format: %v", root["crates"])
73 }
74 return crates
75}
76
77func TestProjectJsonDep(t *testing.T) {
78 bp := `
79 rust_library {
80 name: "liba",
81 srcs: ["a/src/lib.rs"],
82 crate_name: "a"
83 }
84 rust_library {
85 name: "libb",
86 srcs: ["b/src/lib.rs"],
87 crate_name: "b",
88 rlibs: ["liba"],
89 }
90 ` + GatherRequiredDepsForTest()
91 fs := map[string][]byte{
92 "a/src/lib.rs": nil,
93 "b/src/lib.rs": nil,
94 }
95 jsonContent := testProjectJson(t, bp, fs)
96 validateJsonCrates(t, jsonContent)
97}
98
99func TestProjectJsonBindGen(t *testing.T) {
100 bp := `
101 rust_library {
102 name: "liba",
103 srcs: ["src/lib.rs"],
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200104 rlibs: ["libbindings1"],
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +0200105 crate_name: "a"
106 }
107 rust_bindgen {
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200108 name: "libbindings1",
109 crate_name: "bindings1",
110 source_stem: "bindings1",
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +0200111 host_supported: true,
112 wrapper_src: "src/any.h",
113 }
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200114 rust_library_host {
115 name: "libb",
116 srcs: ["src/lib.rs"],
117 rustlibs: ["libbindings2"],
118 crate_name: "b"
119 }
120 rust_bindgen_host {
121 name: "libbindings2",
122 crate_name: "bindings2",
123 source_stem: "bindings2",
124 wrapper_src: "src/any.h",
125 }
Thiébaud Weksteen83ee52f2020-08-05 09:29:23 +0200126 ` + GatherRequiredDepsForTest()
127 fs := map[string][]byte{
128 "src/lib.rs": nil,
129 }
130 jsonContent := testProjectJson(t, bp, fs)
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200131 crates := validateJsonCrates(t, jsonContent)
132 for _, c := range crates {
133 crate, ok := c.(map[string]interface{})
134 if !ok {
135 t.Fatalf("Unexpected type for crate: %v", c)
136 }
137 rootModule, ok := crate["root_module"].(string)
138 if !ok {
139 t.Fatalf("Unexpected type for root_module: %v", crate["root_module"])
140 }
141 if strings.Contains(rootModule, "libbindings1") && !strings.Contains(rootModule, "android_arm64") {
Thiébaud Weksteen4f6c7662020-09-30 13:33:41 +0200142 t.Errorf("The source path for libbindings1 does not contain android_arm64, got %v", rootModule)
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200143 }
Thiébaud Weksteen4f6c7662020-09-30 13:33:41 +0200144 if strings.Contains(rootModule, "libbindings2") && !strings.Contains(rootModule, android.BuildOs.String()) {
145 t.Errorf("The source path for libbindings2 does not contain the BuildOs, got %v; want %v",
146 rootModule, android.BuildOs.String())
Thiébaud Weksteena6351ca2020-09-29 09:53:21 +0200147 }
148 }
Thiébaud Weksteene4d12a02020-06-05 11:09:27 +0200149}
Thiébaud Weksteen2f628ba2020-08-05 14:27:32 +0200150
151func TestProjectJsonMultiVersion(t *testing.T) {
152 bp := `
153 rust_library {
154 name: "liba1",
155 srcs: ["a1/src/lib.rs"],
156 crate_name: "a"
157 }
158 rust_library {
159 name: "liba2",
160 srcs: ["a2/src/lib.rs"],
161 crate_name: "a",
162 }
163 rust_library {
164 name: "libb",
165 srcs: ["b/src/lib.rs"],
166 crate_name: "b",
167 rustlibs: ["liba1", "liba2"],
168 }
169 ` + GatherRequiredDepsForTest()
170 fs := map[string][]byte{
171 "a1/src/lib.rs": nil,
172 "a2/src/lib.rs": nil,
173 "b/src/lib.rs": nil,
174 }
175 jsonContent := testProjectJson(t, bp, fs)
176 crates := validateJsonCrates(t, jsonContent)
177 for _, crate := range crates {
178 c := crate.(map[string]interface{})
179 if c["root_module"] == "b/src/lib.rs" {
180 deps, ok := c["deps"].([]interface{})
181 if !ok {
182 t.Errorf("Unexpected format for deps: %v", c["deps"])
183 }
184 aCount := 0
185 for _, dep := range deps {
186 d, ok := dep.(map[string]interface{})
187 if !ok {
188 t.Errorf("Unexpected format for dep: %v", dep)
189 }
190 if d["name"] == "a" {
191 aCount++
192 }
193 }
194 if aCount != 2 {
195 t.Errorf("Unexpected number of liba dependencies want %v, got %v: %v", 2, aCount, deps)
196 }
197 return
198 }
199 }
200 t.Errorf("libb crate has not been found: %v", crates)
201}