Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/build/build/soong/rust/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 9 kB image not shown  

Quelle  project_json_test.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// 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"
 "os"
 "path/filepath"
 "slices"
 "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 := os.ReadFile(filepath.Join(result.Config.SoongOutDir(), 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) []any {
 var content any
 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]any)
 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"].([]any)
 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 any) map[string]any {
 c, ok := crate.(map[string]any)
 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]any) []string {
 var dependencies []string
 deps, ok := crate["deps"].([]any)
 if !ok {
  t.Errorf("Unexpected format for deps: %v", crate["deps"])
 }
 for _, dep := range deps {
  d, ok := dep.(map[string]any)
  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) {
 t.Parallel()
 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 TestProjectJsonProcMacroDep(t *testing.T) {
 t.Parallel()
 bp := `
 rust_proc_macro {
  name: "libproc_macro",
  srcs: ["a/src/lib.rs"],
  crate_name: "proc_macro"
 }
 rust_library {
  name: "librust",
  srcs: ["b/src/lib.rs"],
  crate_name: "rust",
  proc_macros: ["libproc_macro"],
 }
 `
 jsonContent := testProjectJson(t, bp)
 crates := validateJsonCrates(t, jsonContent)
 libproc_macro_count := 0
 librust_count := 0
 for _, c := range crates {
  crate := validateCrate(t, c)
  procMacro, ok := crate["is_proc_macro"].(bool)
  if !ok {
   t.Fatalf("Unexpected type for is_proc_macro: %v", crate["is_proc_macro"])
  }

  name, ok := crate["display_name"].(string)
  if !ok {
   t.Fatalf("Unexpected type for display_name: %v", crate["display_name"])
  }

  switch name {
  case "libproc_macro":
   libproc_macro_count += 1
   if !procMacro {
    t.Fatalf("'libproc_macro' is marked with is_proc_macro=false")
   }
  case "librust":
   librust_count += 1
   if procMacro {
    t.Fatalf("'librust' is not a proc macro crate, but is marked with is_proc_macro=true")
   }
  }
 }

 if libproc_macro_count != 1 || librust_count != 1 {
  t.Fatalf("Unexpected crate counts: libproc_macro_count: %v, librust_count: %v",
   libproc_macro_count, librust_count)
 }
}

func TestProjectJsonFeature(t *testing.T) {
 t.Parallel()
 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"].([]any)
  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) {
 t.Parallel()
 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) {
 t.Parallel()
 buildOS := android.TestConfig(t.TempDir(), nil, "", nil).BuildOS

 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, buildOS.String()) {
   t.Errorf("The source path for libbindings2 does not contain the BuildOs, got %v; want %v",
    rootModule, 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 := slices.Contains(validateDependencies(t, crate), "bindings1")
   if !found {
    t.Errorf("libd does not depend on libbindings1: %v", crate)
   }
   // Check that OUT_DIR is populated.
   env, ok := crate["env"].(map[string]any)
   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) {
 t.Parallel()
 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)
}

func TestProjectJsonTarget(t *testing.T) {
 t.Parallel()
 bp := `
 rust_library {
  name: "liba",
  srcs: ["a/src/lib.rs"],
  crate_name: "a"
 }
 `
 jsonContent := testProjectJson(t, bp)
 crates := validateJsonCrates(t, jsonContent)
 for _, c := range crates {
  crate := validateCrate(t, c)
  target, ok := crate["target"].(string)
  if !ok {
   t.Fatalf("Unexpected type for target: %v", crate["target"])
  }
  expectedTarget := "aarch64-linux-android"
  if target != expectedTarget {
   t.Errorf("Incorrect target: got %v; want %v", target, expectedTarget)
  }
 }
}

[Dauer der Verarbeitung: 0.1 Sekunden, vorverarbeitet 2026-06-28]