summaryrefslogtreecommitdiff
path: root/rust/project_json_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'rust/project_json_test.go')
-rw-r--r--rust/project_json_test.go297
1 files changed, 297 insertions, 0 deletions
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
new file mode 100644
index 000000000..09d30dbde
--- /dev/null
+++ b/rust/project_json_test.go
@@ -0,0 +1,297 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "path/filepath"
+ "sort"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+// testProjectJson run the generation of rust-project.json. It returns the raw
+// content of the generated file.
+func testProjectJson(t *testing.T, bp string) []byte {
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ android.FixtureMergeEnv(map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}),
+ ).RunTestWithBp(t, bp)
+
+ // The JSON file is generated via WriteFileToOutputDir. Therefore, it
+ // won't appear in the Output of the TestingSingleton. Manually verify
+ // it exists.
+ content, err := ioutil.ReadFile(filepath.Join(result.Config.BuildDir(), rustProjectJsonFileName))
+ if err != nil {
+ t.Errorf("rust-project.json has not been generated")
+ }
+ return content
+}
+
+// validateJsonCrates validates that content follows the basic structure of
+// rust-project.json. It returns the crates attribute if the validation
+// succeeded.
+// It uses an empty interface instead of relying on a defined structure to
+// avoid a strong dependency on our implementation.
+func validateJsonCrates(t *testing.T, rawContent []byte) []interface{} {
+ var content interface{}
+ err := json.Unmarshal(rawContent, &content)
+ if err != nil {
+ t.Errorf("Unable to parse the rust-project.json as JSON: %v", err)
+ }
+ root, ok := content.(map[string]interface{})
+ if !ok {
+ t.Errorf("Unexpected JSON format: %v", content)
+ }
+ if _, ok = root["crates"]; !ok {
+ t.Errorf("No crates attribute in rust-project.json: %v", root)
+ }
+ crates, ok := root["crates"].([]interface{})
+ if !ok {
+ t.Errorf("Unexpected crates format: %v", root["crates"])
+ }
+ return crates
+}
+
+// validateCrate ensures that a crate can be parsed as a map.
+func validateCrate(t *testing.T, crate interface{}) map[string]interface{} {
+ c, ok := crate.(map[string]interface{})
+ if !ok {
+ t.Fatalf("Unexpected type for crate: %v", c)
+ }
+ return c
+}
+
+// validateDependencies parses the dependencies for a crate. It returns a list
+// of the dependencies name.
+func validateDependencies(t *testing.T, crate map[string]interface{}) []string {
+ var dependencies []string
+ deps, ok := crate["deps"].([]interface{})
+ if !ok {
+ t.Errorf("Unexpected format for deps: %v", crate["deps"])
+ }
+ for _, dep := range deps {
+ d, ok := dep.(map[string]interface{})
+ if !ok {
+ t.Errorf("Unexpected format for dependency: %v", dep)
+ }
+ name, ok := d["name"].(string)
+ if !ok {
+ t.Errorf("Dependency is missing the name key: %v", d)
+ }
+ dependencies = append(dependencies, name)
+ }
+ return dependencies
+}
+
+func TestProjectJsonDep(t *testing.T) {
+ bp := `
+ rust_library {
+ name: "liba",
+ srcs: ["a/src/lib.rs"],
+ crate_name: "a"
+ }
+ rust_library {
+ name: "libb",
+ srcs: ["b/src/lib.rs"],
+ crate_name: "b",
+ rlibs: ["liba"],
+ }
+ `
+ jsonContent := testProjectJson(t, bp)
+ validateJsonCrates(t, jsonContent)
+}
+
+func TestProjectJsonFeature(t *testing.T) {
+ bp := `
+ rust_library {
+ name: "liba",
+ srcs: ["a/src/lib.rs"],
+ crate_name: "a",
+ features: ["f1", "f2"]
+ }
+ `
+ jsonContent := testProjectJson(t, bp)
+ crates := validateJsonCrates(t, jsonContent)
+ for _, c := range crates {
+ crate := validateCrate(t, c)
+ cfgs, ok := crate["cfg"].([]interface{})
+ if !ok {
+ t.Fatalf("Unexpected type for cfgs: %v", crate)
+ }
+ expectedCfgs := []string{"feature=\"f1\"", "feature=\"f2\""}
+ foundCfgs := []string{}
+ for _, cfg := range cfgs {
+ cfg, ok := cfg.(string)
+ if !ok {
+ t.Fatalf("Unexpected type for cfg: %v", cfg)
+ }
+ foundCfgs = append(foundCfgs, cfg)
+ }
+ sort.Strings(foundCfgs)
+ for i, foundCfg := range foundCfgs {
+ if foundCfg != expectedCfgs[i] {
+ t.Errorf("Incorrect features: got %v; want %v", foundCfg, expectedCfgs[i])
+ }
+ }
+ }
+}
+
+func TestProjectJsonBinary(t *testing.T) {
+ bp := `
+ rust_binary {
+ name: "libz",
+ srcs: ["z/src/lib.rs"],
+ crate_name: "z"
+ }
+ `
+ jsonContent := testProjectJson(t, bp)
+ crates := validateJsonCrates(t, jsonContent)
+ for _, c := range crates {
+ crate := validateCrate(t, c)
+ rootModule, ok := crate["root_module"].(string)
+ if !ok {
+ t.Fatalf("Unexpected type for root_module: %v", crate["root_module"])
+ }
+ if rootModule == "z/src/lib.rs" {
+ return
+ }
+ }
+ t.Errorf("Entry for binary %q not found: %s", "a", jsonContent)
+}
+
+func TestProjectJsonBindGen(t *testing.T) {
+ bp := `
+ rust_library {
+ name: "libd",
+ srcs: ["d/src/lib.rs"],
+ rlibs: ["libbindings1"],
+ crate_name: "d"
+ }
+ rust_bindgen {
+ name: "libbindings1",
+ crate_name: "bindings1",
+ source_stem: "bindings1",
+ host_supported: true,
+ wrapper_src: "src/any.h",
+ }
+ rust_library_host {
+ name: "libe",
+ srcs: ["e/src/lib.rs"],
+ rustlibs: ["libbindings2"],
+ crate_name: "e"
+ }
+ rust_bindgen_host {
+ name: "libbindings2",
+ crate_name: "bindings2",
+ source_stem: "bindings2",
+ wrapper_src: "src/any.h",
+ }
+ `
+ jsonContent := testProjectJson(t, bp)
+ crates := validateJsonCrates(t, jsonContent)
+ for _, c := range crates {
+ crate := validateCrate(t, c)
+ rootModule, ok := crate["root_module"].(string)
+ if !ok {
+ t.Fatalf("Unexpected type for root_module: %v", crate["root_module"])
+ }
+ if strings.Contains(rootModule, "libbindings1") && !strings.Contains(rootModule, "android_arm64") {
+ t.Errorf("The source path for libbindings1 does not contain android_arm64, got %v", rootModule)
+ }
+ if strings.Contains(rootModule, "libbindings2") && !strings.Contains(rootModule, android.BuildOs.String()) {
+ t.Errorf("The source path for libbindings2 does not contain the BuildOs, got %v; want %v",
+ rootModule, android.BuildOs.String())
+ }
+ // Check that libbindings1 does not depend on itself.
+ if strings.Contains(rootModule, "libbindings1") {
+ for _, depName := range validateDependencies(t, crate) {
+ if depName == "bindings1" {
+ t.Errorf("libbindings1 depends on itself")
+ }
+ }
+ }
+ if strings.Contains(rootModule, "d/src/lib.rs") {
+ // Check that libd depends on libbindings1
+ found := false
+ for _, depName := range validateDependencies(t, crate) {
+ if depName == "bindings1" {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("libd does not depend on libbindings1: %v", crate)
+ }
+ // Check that OUT_DIR is populated.
+ env, ok := crate["env"].(map[string]interface{})
+ if !ok {
+ t.Errorf("libd does not have its environment variables set: %v", crate)
+ }
+ if _, ok = env["OUT_DIR"]; !ok {
+ t.Errorf("libd does not have its OUT_DIR set: %v", env)
+ }
+
+ }
+ }
+}
+
+func TestProjectJsonMultiVersion(t *testing.T) {
+ bp := `
+ rust_library {
+ name: "liba1",
+ srcs: ["a1/src/lib.rs"],
+ crate_name: "a"
+ }
+ rust_library {
+ name: "liba2",
+ srcs: ["a2/src/lib.rs"],
+ crate_name: "a",
+ }
+ rust_library {
+ name: "libb",
+ srcs: ["b/src/lib.rs"],
+ crate_name: "b",
+ rustlibs: ["liba1", "liba2"],
+ }
+ `
+ jsonContent := testProjectJson(t, bp)
+ crates := validateJsonCrates(t, jsonContent)
+ for _, c := range crates {
+ crate := validateCrate(t, c)
+ rootModule, ok := crate["root_module"].(string)
+ if !ok {
+ t.Fatalf("Unexpected type for root_module: %v", crate["root_module"])
+ }
+ // Make sure that b has 2 different dependencies.
+ if rootModule == "b/src/lib.rs" {
+ aCount := 0
+ deps := validateDependencies(t, crate)
+ for _, depName := range deps {
+ if depName == "a" {
+ aCount++
+ }
+ }
+ if aCount != 2 {
+ t.Errorf("Unexpected number of liba dependencies want %v, got %v: %v", 2, aCount, deps)
+ }
+ return
+ }
+ }
+ t.Errorf("libb crate has not been found: %v", crates)
+}