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

Quelle  microfactory_test.go   Sprache: unbekannt

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

// Copyright 2017 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 microfactory

import (
 "flag"
 "io/ioutil"
 "os"
 "path/filepath"
 "reflect"
 "runtime"
 "testing"
 "time"
)

func TestSimplePackagePathMap(t *testing.T) {
 t.Parallel()

 pkgMap := pkgPathMappingVar{&Config{}}
 flags := flag.NewFlagSet("", flag.ContinueOnError)
 flags.Var(&pkgMap, "m", "")
 err := flags.Parse([]string{
  "-m", "android/soong=build/soong/",
  "-m", "github.com/google/blueprint/=build/blueprint",
 })
 if err != nil {
  t.Fatal(err)
 }

 compare := func(got, want interface{}) {
  if !reflect.DeepEqual(got, want) {
   t.Errorf("Unexpected values in .pkgs:\nwant: %v\n got: %v",
    want, got)
  }
 }

 wantPkgs := []string{"android/soong", "github.com/google/blueprint"}
 compare(pkgMap.pkgs, wantPkgs)
 compare(pkgMap.paths[wantPkgs[0]], "build/soong")
 compare(pkgMap.paths[wantPkgs[1]], "build/blueprint")

 got, ok, err := pkgMap.Path("android/soong/ui/test")
 if err != nil {
  t.Error("Unexpected error in pkgMap.Path(soong):", err)
 } else if !ok {
  t.Error("Expected a result from pkgMap.Path(soong)")
 } else {
  compare(got, "build/soong/ui/test")
 }

 got, ok, err = pkgMap.Path("github.com/google/blueprint")
 if err != nil {
  t.Error("Unexpected error in pkgMap.Path(blueprint):", err)
 } else if !ok {
  t.Error("Expected a result from pkgMap.Path(blueprint)")
 } else {
  compare(got, "build/blueprint")
 }
}

func TestBadPackagePathMap(t *testing.T) {
 t.Parallel()

 pkgMap := pkgPathMappingVar{&Config{}}
 if _, _, err := pkgMap.Path("testing"); err == nil {
  t.Error("Expected error if no maps are specified")
 }
 if err := pkgMap.Set(""); err == nil {
  t.Error("Expected error with blank argument, but none returned")
 }
 if err := pkgMap.Set("a=a"); err != nil {
  t.Errorf("Unexpected error: %v", err)
 }
 if err := pkgMap.Set("a=b"); err == nil {
  t.Error("Expected error with duplicate package prefix, but none returned")
 }
 if _, ok, err := pkgMap.Path("testing"); err != nil {
  t.Errorf("Unexpected error: %v", err)
 } else if ok {
  t.Error("Expected testing to be consider in the stdlib")
 }
}

// TestSingleBuild ensures that just a basic build works.
func TestSingleBuild(t *testing.T) {
 t.Parallel()

 setupDir(t, func(config *Config, dir string, loadPkg loadPkgFunc) {
  // The output binary
  out := filepath.Join(dir, "out", "test")

  pkg := loadPkg()

  if err := pkg.Compile(config, filepath.Join(dir, "out")); err != nil {
   t.Fatal("Got error when compiling:", err)
  }

  if err := pkg.Link(config, out); err != nil {
   t.Fatal("Got error when linking:", err)
  }

  if _, err := os.Stat(out); err != nil {
   t.Error("Cannot stat output:", err)
  }
 })
}

// testBuildAgain triggers two builds, running the modify function in between
// each build. It verifies that the second build did or did not actually need
// to rebuild anything based on the shouldRebuild argument.
func testBuildAgain(t *testing.T,
 shouldRecompile, shouldRelink bool,
 modify func(config *Config, dir string, loadPkg loadPkgFunc),
 after func(pkg *GoPackage)) {

 t.Parallel()

 setupDir(t, func(config *Config, dir string, loadPkg loadPkgFunc) {
  // The output binary
  out := filepath.Join(dir, "out", "test")

  pkg := loadPkg()

  if err := pkg.Compile(config, filepath.Join(dir, "out")); err != nil {
   t.Fatal("Got error when compiling:", err)
  }

  if err := pkg.Link(config, out); err != nil {
   t.Fatal("Got error when linking:", err)
  }

  var firstTime time.Time
  if stat, err := os.Stat(out); err == nil {
   firstTime = stat.ModTime()
  } else {
   t.Fatal("Failed to stat output file:", err)
  }

  // mtime on HFS+ (the filesystem on darwin) are stored with 1
  // second granularity, so the timestamp checks will fail unless
  // we wait at least a second. Sleeping 1.1s to be safe.
  if runtime.GOOS == "darwin" {
   time.Sleep(1100 * time.Millisecond)
  }

  modify(config, dir, loadPkg)

  pkg = loadPkg()

  if err := pkg.Compile(config, filepath.Join(dir, "out")); err != nil {
   t.Fatal("Got error when compiling:", err)
  }
  if shouldRecompile {
   if !pkg.rebuilt {
    t.Fatal("Package should have recompiled, but was not recompiled.")
   }
  } else {
   if pkg.rebuilt {
    t.Fatal("Package should not have needed to be recompiled, but was recompiled.")
   }
  }

  if err := pkg.Link(config, out); err != nil {
   t.Fatal("Got error while linking:", err)
  }
  if shouldRelink {
   if !pkg.rebuilt {
    t.Error("Package should have relinked, but was not relinked.")
   }
  } else {
   if pkg.rebuilt {
    t.Error("Package should not have needed to be relinked, but was relinked.")
   }
  }

  if stat, err := os.Stat(out); err == nil {
   if shouldRelink {
    if stat.ModTime() == firstTime {
     t.Error("Output timestamp should be different, but both were", firstTime)
    }
   } else {
    if stat.ModTime() != firstTime {
     t.Error("Output timestamp should be the same.")
     t.Error(" first:", firstTime)
     t.Error("second:", stat.ModTime())
    }
   }
  } else {
   t.Fatal("Failed to stat output file:", err)
  }

  after(pkg)
 })
}

// TestRebuildAfterNoChanges ensures that we don't rebuild if nothing
// changes
func TestRebuildAfterNoChanges(t *testing.T) {
 testBuildAgain(t, false, false, func(config *Config, dir string, loadPkg loadPkgFunc) {}, func(pkg *GoPackage) {})
}

// TestRebuildAfterTimestamp ensures that we don't rebuild because
// timestamps of important files have changed. We should only rebuild if the
// content hashes are different.
func TestRebuildAfterTimestampChange(t *testing.T) {
 testBuildAgain(t, false, false, func(config *Config, dir string, loadPkg loadPkgFunc) {
  // Ensure that we've spent some amount of time asleep
  time.Sleep(100 * time.Millisecond)

  newTime := time.Now().Local()
  os.Chtimes(filepath.Join(dir, "test.fact"), newTime, newTime)
  os.Chtimes(filepath.Join(dir, "main/main.go"), newTime, newTime)
  os.Chtimes(filepath.Join(dir, "a/a.go"), newTime, newTime)
  os.Chtimes(filepath.Join(dir, "a/b.go"), newTime, newTime)
  os.Chtimes(filepath.Join(dir, "b/a.go"), newTime, newTime)
 }, func(pkg *GoPackage) {})
}

// TestRebuildAfterGoChange ensures that we rebuild after a content change
// to a package's go file.
func TestRebuildAfterGoChange(t *testing.T) {
 testBuildAgain(t, true, true, func(config *Config, dir string, loadPkg loadPkgFunc) {
  if err := ioutil.WriteFile(filepath.Join(dir, "a", "a.go"), []byte(go_a_a+"\n"), 0666); err != nil {
   t.Fatal("Error writing a/a.go:", err)
  }
 }, func(pkg *GoPackage) {
  if !pkg.directDeps[0].rebuilt {
   t.Fatal("android/soong/a should have rebuilt")
  }
  if !pkg.directDeps[1].rebuilt {
   t.Fatal("android/soong/b should have rebuilt")
  }
 })
}

// TestRebuildAfterMainChange ensures that we don't rebuild any dependencies
// if only the main package's go files are touched.
func TestRebuildAfterMainChange(t *testing.T) {
 testBuildAgain(t, true, true, func(config *Config, dir string, loadPkg loadPkgFunc) {
  if err := ioutil.WriteFile(filepath.Join(dir, "main", "main.go"), []byte(go_main_main+"\n"), 0666); err != nil {
   t.Fatal("Error writing main/main.go:", err)
  }
 }, func(pkg *GoPackage) {
  if pkg.directDeps[0].rebuilt {
   t.Fatal("android/soong/a should not have rebuilt")
  }
  if pkg.directDeps[1].rebuilt {
   t.Fatal("android/soong/b should not have rebuilt")
  }
 })
}

// TestRebuildAfterRemoveOut ensures that we rebuild if the output file is
// missing, even if everything else doesn't need rebuilding.
func TestRebuildAfterRemoveOut(t *testing.T) {
 testBuildAgain(t, false, true, func(config *Config, dir string, loadPkg loadPkgFunc) {
  if err := os.Remove(filepath.Join(dir, "out", "test")); err != nil {
   t.Fatal("Failed to remove output:", err)
  }
 }, func(pkg *GoPackage) {})
}

// TestRebuildAfterPartialBuild ensures that even if the build was interrupted
// between the recompile and relink stages, we'll still relink when we run again.
func TestRebuildAfterPartialBuild(t *testing.T) {
 testBuildAgain(t, false, true, func(config *Config, dir string, loadPkg loadPkgFunc) {
  if err := ioutil.WriteFile(filepath.Join(dir, "main", "main.go"), []byte(go_main_main+"\n"), 0666); err != nil {
   t.Fatal("Error writing main/main.go:", err)
  }

  pkg := loadPkg()

  if err := pkg.Compile(config, filepath.Join(dir, "out")); err != nil {
   t.Fatal("Got error when compiling:", err)
  }
  if !pkg.rebuilt {
   t.Fatal("Package should have recompiled, but was not recompiled.")
  }
 }, func(pkg *GoPackage) {})
}

// BenchmarkInitialBuild computes how long a clean build takes (for tiny test
// inputs).
func BenchmarkInitialBuild(b *testing.B) {
 for i := 0; i < b.N; i++ {
  setupDir(b, func(config *Config, dir string, loadPkg loadPkgFunc) {
   pkg := loadPkg()
   if err := pkg.Compile(config, filepath.Join(dir, "out")); err != nil {
    b.Fatal("Got error when compiling:", err)
   }

   if err := pkg.Link(config, filepath.Join(dir, "out", "test")); err != nil {
    b.Fatal("Got error when linking:", err)
   }
  })
 }
}

// BenchmarkMinIncrementalBuild computes how long an incremental build that
// doesn't actually need to build anything takes.
func BenchmarkMinIncrementalBuild(b *testing.B) {
 setupDir(b, func(config *Config, dir string, loadPkg loadPkgFunc) {
  pkg := loadPkg()

  if err := pkg.Compile(config, filepath.Join(dir, "out")); err != nil {
   b.Fatal("Got error when compiling:", err)
  }

  if err := pkg.Link(config, filepath.Join(dir, "out", "test")); err != nil {
   b.Fatal("Got error when linking:", err)
  }

  b.ResetTimer()

  for i := 0; i < b.N; i++ {
   pkg := loadPkg()

   if err := pkg.Compile(config, filepath.Join(dir, "out")); err != nil {
    b.Fatal("Got error when compiling:", err)
   }

   if err := pkg.Link(config, filepath.Join(dir, "out", "test")); err != nil {
    b.Fatal("Got error when linking:", err)
   }

   if pkg.rebuilt {
    b.Fatal("Should not have rebuilt anything")
   }
  }
 })
}

///////////////////////////////////////////////////////
// Templates used to create fake compilable packages //
///////////////////////////////////////////////////////

const go_main_main = `
package main
import (
 "fmt"
 "android/soong/a"
 "android/soong/b"
)
func main() {
 fmt.Println(a.Stdout, b.Stdout)
}
`

const go_a_a = `
package a
import "os"
var Stdout = os.Stdout
`

const go_a_b = `
package a
`

const go_b_a = `
package b
import "android/soong/a"
var Stdout = a.Stdout
`

type T interface {
 Fatal(args ...interface{})
 Fatalf(format string, args ...interface{})
}

type loadPkgFunc func() *GoPackage

func setupDir(t T, test func(config *Config, dir string, loadPkg loadPkgFunc)) {
 dir, err := ioutil.TempDir("", "test")
 if err != nil {
  t.Fatalf("Error creating temporary directory: %#v", err)
 }
 defer os.RemoveAll(dir)

 writeFile := func(name, contents string) {
  if err := ioutil.WriteFile(filepath.Join(dir, name), []byte(contents), 0666); err != nil {
   t.Fatalf("Error writing %q: %#v", name, err)
  }
 }
 mkdir := func(name string) {
  if err := os.Mkdir(filepath.Join(dir, name), 0777); err != nil {
   t.Fatalf("Error creating %q directory: %#v", name, err)
  }
 }
 mkdir("main")
 mkdir("a")
 mkdir("b")
 writeFile("main/main.go", go_main_main)
 writeFile("a/a.go", go_a_a)
 writeFile("a/b.go", go_a_b)
 writeFile("b/a.go", go_b_a)

 config := &Config{}
 config.Map("android/soong", dir)

 loadPkg := func() *GoPackage {
  pkg := &GoPackage{
   Name: "main",
  }
  if err := pkg.FindDeps(config, filepath.Join(dir, "main")); err != nil {
   t.Fatalf("Error finding deps: %v", err)
  }
  return pkg
 }

 test(config, dir, loadPkg)
}

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