diff options
Diffstat (limited to 'rust/project_json_test.go')
-rw-r--r-- | rust/project_json_test.go | 297 |
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) +} |