Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  context_test.go   Sprache: unbekannt

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

// Copyright 2014 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 blueprint

import (
 "bytes"
 "errors"
 "fmt"
 "hash/fnv"
 "reflect"
 "slices"
 "strconv"
 "strings"
 "sync"
 "testing"
 "time"

 "github.com/google/blueprint/gobtools"
 "github.com/google/blueprint/parser"
 "github.com/google/blueprint/proptools"
 "github.com/google/blueprint/uniquelist"
)

type Walker interface {
 Walk() bool
}

func walkDependencyGraph(ctx *Context, topModule *moduleInfo, allowDuplicates bool) (string, string) {
 var outputDown string
 var outputUp string
 ctx.walkDeps(topModule, allowDuplicates,
  func(dep depInfo, parent *moduleInfo) bool {
   outputDown += ctx.ModuleName(dep.module.logicModule)
   if tag, ok := dep.tag.(walkerDepsTag); ok {
    if !tag.follow {
     return false
    }
   }
   if dep.module.logicModule.(Walker).Walk() {
    return true
   }

   return false
  },
  func(dep depInfo, parent *moduleInfo) {
   outputUp += ctx.ModuleName(dep.module.logicModule)
  })
 return outputDown, outputUp
}

type depsProvider interface {
 Deps() []string
 IgnoreDeps() []string
}

type IncrementalTestInfo struct {
 Value string
}

var IncrementalTestProviderKey = NewProvider[IncrementalTestInfo]()

func init() {
 IncrementalTestProviderGobRegId = gobtools.RegisterType(func() gobtools.CustomDec { return new(IncrementalTestInfo) })
}

func (r IncrementalTestInfo) Encode(ctx gobtools.EncContext, buf *bytes.Buffer) error {
 var err error

 if err = gobtools.EncodeString(buf, r.Value); err != nil {
  return err
 }
 return err
}

func (r *IncrementalTestInfo) Decode(ctx gobtools.EncContext, buf *bytes.Reader) error {
 var err error

 err = gobtools.DecodeString(buf, &r.Value)
 if err != nil {
  return err
 }

 return err
}

func (r IncrementalTestInfo) CustomHash(hasher *proptools.Hasher) error {
 hasher.HashType(reflect.TypeOf(r))
 hasher.WriteString(r.Value)
 return nil
}

var IncrementalTestProviderGobRegId int16

func (r IncrementalTestInfo) GetTypeId() int16 {
 return IncrementalTestProviderGobRegId
}

type baseTestModule struct {
 ModuleBase
 SimpleName
 properties struct {
  Deps             []string
  Ignored_deps     []string
  Outputs          []string
  Order_only       []string
  Extra_outputs    []string
  Extra_order_only []string
  Srcs             []string
  Exclude_srcs     []string
 }
 GenerateBuildActionsCalled bool
}

func (b *baseTestModule) Deps() []string {
 return b.properties.Deps
}

func (b *baseTestModule) IgnoreDeps() []string {
 return b.properties.Ignored_deps
}

var pctx PackageContext

func init() {
 pctx = NewPackageContext("android/blueprint")
}
func (b *baseTestModule) GenerateBuildActions(ctx ModuleContext) {
 b.GenerateBuildActionsCalled = true
 ctx.Build(pctx, BuildParams{
  Rule:      Phony,
  Outputs:   b.properties.Outputs,
  OrderOnly: b.properties.Order_only,
 })
 if len(b.properties.Extra_outputs) > 0 {
  ctx.Build(pctx, BuildParams{
   Rule:      Phony,
   Outputs:   b.properties.Extra_outputs,
   OrderOnly: b.properties.Extra_order_only,
  })
 }
 for _, src := range b.properties.Srcs {
  ctx.GlobWithDeps(src, b.properties.Exclude_srcs)
 }
 ctx.VisitDirectDeps(func(module Module) {
  OtherModuleProvider(ctx, module, IncrementalTestProviderKey)
 })
 SetProvider(ctx, IncrementalTestProviderKey, IncrementalTestInfo{
  Value: ctx.ModuleName(),
 })
}

type fooModule struct {
 baseTestModule
}

func newFooModule() (Module, []interface{}) {
 m := &fooModule{}
 return m, []interface{}{&m.baseTestModule.properties, &m.SimpleName.Properties}
}

func (f *fooModule) Walk() bool {
 return true
}

type barModule struct {
 baseTestModule
}

func newBarModule() (Module, []interface{}) {
 m := &barModule{}
 return m, []interface{}{&m.baseTestModule.properties, &m.SimpleName.Properties}
}

func (b *barModule) Walk() bool {
 return false
}

type walkerDepsTag struct {
 BaseDependencyTag
 // True if the dependency should be followed, false otherwise.
 follow bool
}

func depsMutator(mctx BottomUpMutatorContext) {
 if m, ok := mctx.Module().(depsProvider); ok {
  mctx.AddDependency(mctx.Module(), walkerDepsTag{follow: false}, m.IgnoreDeps()...)
  mctx.AddDependency(mctx.Module(), walkerDepsTag{follow: true}, m.Deps()...)
 }
}

func TestContextParse(t *testing.T) {
 ctx := NewContext()
 ctx.RegisterModuleType("foo_module", newFooModule)
 ctx.RegisterModuleType("bar_module", newBarModule)

 r := bytes.NewBufferString(`
  foo_module {
         name: "MyFooModule",
   deps: ["MyBarModule"],
  }

  bar_module {
         name: "MyBarModule",
  }
 `)

 _, _, errs := ctx.parseOne(".", "Blueprint", r, parser.NewScope(nil), nil)
 if len(errs) > 0 {
  t.Errorf("unexpected parse errors:")
  for _, err := range errs {
   t.Errorf("  %s", err)
  }
  t.FailNow()
 }

 _, errs = ctx.ResolveDependencies(nil)
 if len(errs) > 0 {
  t.Errorf("unexpected dep errors:")
  for _, err := range errs {
   t.Errorf("  %s", err)
  }
  t.FailNow()
 }
}

// > |===B---D       - represents a non-walkable edge
// > A               = represents a walkable edge
// > |===C===E---G
// >     |       |   A should not be visited because it's the root node.
// >     |===F===|   B, D and E should not be walked.
func TestWalkDeps(t *testing.T) {
 ctx := NewContext()
 ctx.MockFileSystem(map[string][]byte{
  "Android.bp": []byte(`
   foo_module {
       name: "A",
       deps: ["B", "C"],
   }

   bar_module {
       name: "B",
       deps: ["D"],
   }

   foo_module {
       name: "C",
       deps: ["E", "F"],
   }

   foo_module {
       name: "D",
   }

   bar_module {
       name: "E",
       deps: ["G"],
   }

   foo_module {
       name: "F",
       deps: ["G"],
   }

   foo_module {
       name: "G",
   }
  `),
 })

 ctx.RegisterModuleType("foo_module", newFooModule)
 ctx.RegisterModuleType("bar_module", newBarModule)
 ctx.RegisterBottomUpMutator("deps", depsMutator)
 _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
 if len(errs) > 0 {
  t.Errorf("unexpected parse errors:")
  for _, err := range errs {
   t.Errorf("  %s", err)
  }
  t.FailNow()
 }

 _, errs = ctx.ResolveDependencies(nil)
 if len(errs) > 0 {
  t.Errorf("unexpected dep errors:")
  for _, err := range errs {
   t.Errorf("  %s", err)
  }
  t.FailNow()
 }

 topModule := ctx.moduleGroupFromName("A", nil).modules.firstModule()
 outputDown, outputUp := walkDependencyGraph(ctx, topModule, false)
 if outputDown != "BCEFG" {
  t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: BCEFG", outputDown)
 }
 if outputUp != "BEGFC" {
  t.Errorf("unexpected walkDeps behaviour: %s\nup should be: BEGFC", outputUp)
 }
}

// > |===B---D           - represents a non-walkable edge
// > A                   = represents a walkable edge
// > |===C===E===\       A should not be visited because it's the root node.
// >     |       |       B, D should not be walked.
// >     |===F===G===H   G should be visited multiple times
// >         \===/       H should only be visited once
func TestWalkDepsDuplicates(t *testing.T) {
 ctx := NewContext()
 ctx.MockFileSystem(map[string][]byte{
  "Android.bp": []byte(`
   foo_module {
       name: "A",
       deps: ["B", "C"],
   }

   bar_module {
       name: "B",
       deps: ["D"],
   }

   foo_module {
       name: "C",
       deps: ["E", "F"],
   }

   foo_module {
       name: "D",
   }

   foo_module {
       name: "E",
       deps: ["G"],
   }

   foo_module {
       name: "F",
       deps: ["G", "G"],
   }

   foo_module {
       name: "G",
    deps: ["H"],
   }

   foo_module {
       name: "H",
   }
  `),
 })

 ctx.RegisterModuleType("foo_module", newFooModule)
 ctx.RegisterModuleType("bar_module", newBarModule)
 ctx.RegisterBottomUpMutator("deps", depsMutator)
 _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
 if len(errs) > 0 {
  t.Errorf("unexpected parse errors:")
  for _, err := range errs {
   t.Errorf("  %s", err)
  }
  t.FailNow()
 }

 _, errs = ctx.ResolveDependencies(nil)
 if len(errs) > 0 {
  t.Errorf("unexpected dep errors:")
  for _, err := range errs {
   t.Errorf("  %s", err)
  }
  t.FailNow()
 }

 topModule := ctx.moduleGroupFromName("A", nil).modules.firstModule()
 outputDown, outputUp := walkDependencyGraph(ctx, topModule, true)
 if outputDown != "BCEGHFGG" {
  t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: BCEGHFGG", outputDown)
 }
 if outputUp != "BHGEGGFC" {
  t.Errorf("unexpected walkDeps behaviour: %s\nup should be: BHGEGGFC", outputUp)
 }
}

// >                     - represents a non-walkable edge
// > A                   = represents a walkable edge
// > |===B-------\       A should not be visited because it's the root node.
// >     |       |       B -> D should not be walked.
// >     |===C===D===E   B -> C -> D -> E should be walked
func TestWalkDepsDuplicates_IgnoreFirstPath(t *testing.T) {
 ctx := NewContext()
 ctx.MockFileSystem(map[string][]byte{
  "Android.bp": []byte(`
   foo_module {
       name: "A",
       deps: ["B"],
   }

   foo_module {
       name: "B",
       deps: ["C"],
       ignored_deps: ["D"],
   }

   foo_module {
       name: "C",
       deps: ["D"],
   }

   foo_module {
       name: "D",
       deps: ["E"],
   }

   foo_module {
       name: "E",
   }
  `),
 })

 ctx.RegisterModuleType("foo_module", newFooModule)
 ctx.RegisterModuleType("bar_module", newBarModule)
 ctx.RegisterBottomUpMutator("deps", depsMutator)
 _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
 if len(errs) > 0 {
  t.Errorf("unexpected parse errors:")
  for _, err := range errs {
   t.Errorf("  %s", err)
  }
  t.FailNow()
 }

 _, errs = ctx.ResolveDependencies(nil)
 if len(errs) > 0 {
  t.Errorf("unexpected dep errors:")
  for _, err := range errs {
   t.Errorf("  %s", err)
  }
  t.FailNow()
 }

 topModule := ctx.moduleGroupFromName("A", nil).modules.firstModule()
 outputDown, outputUp := walkDependencyGraph(ctx, topModule, true)
 expectedDown := "BDCDE"
 if outputDown != expectedDown {
  t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: %s", outputDown, expectedDown)
 }
 expectedUp := "DEDCB"
 if outputUp != expectedUp {
  t.Errorf("unexpected walkDeps behaviour: %s\nup should be: %s", outputUp, expectedUp)
 }
}

func TestCreateModule(t *testing.T) {
 ctx := newContext()
 ctx.MockFileSystem(map[string][]byte{
  "Android.bp": []byte(`
   foo_module {
       name: "A",
       deps: ["B", "C"],
   }
  `),
 })

 ctx.RegisterBottomUpMutator("create", createTestMutator).UsesCreateModule()
 ctx.RegisterBottomUpMutator("deps", depsMutator)

 ctx.RegisterModuleType("foo_module", newFooModule)
 ctx.RegisterModuleType("bar_module", newBarModule)
 _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
 if len(errs) > 0 {
  t.Errorf("unexpected parse errors:")
  for _, err := range errs {
   t.Errorf("  %s", err)
  }
  t.FailNow()
 }

 _, errs = ctx.ResolveDependencies(nil)
 if len(errs) > 0 {
  t.Errorf("unexpected dep errors:")
  for _, err := range errs {
   t.Errorf("  %s", err)
  }
  t.FailNow()
 }

 a := ctx.moduleGroupFromName("A", nil).modules.firstModule().logicModule.(*fooModule)
 b := ctx.moduleGroupFromName("B", nil).modules.firstModule().logicModule.(*barModule)
 c := ctx.moduleGroupFromName("C", nil).modules.firstModule().logicModule.(*barModule)
 d := ctx.moduleGroupFromName("D", nil).modules.firstModule().logicModule.(*fooModule)

 checkDeps := func(m Module, expected string) {
  var deps []string
  ctx.VisitDirectDeps(m, func(m Module) {
   deps = append(deps, ctx.ModuleName(m))
  })
  got := strings.Join(deps, ",")
  if got != expected {
   t.Errorf("unexpected %q dependencies, got %q expected %q",
    ctx.ModuleName(m), got, expected)
  }
 }

 checkDeps(a, "B,C")
 checkDeps(b, "D")
 checkDeps(c, "D")
 checkDeps(d, "")
}

func createTestMutator(ctx BottomUpMutatorContext) {
 type props struct {
  Name string
  Deps []string
 }

 ctx.CreateModule(newBarModule, "new_bar", &props{
  Name: "B",
  Deps: []string{"D"},
 })

 ctx.CreateModule(newBarModule, "new_bar", &props{
  Name: "C",
  Deps: []string{"D"},
 })

 ctx.CreateModule(newFooModule, "new_foo", &props{
  Name: "D",
 })
}

func TestWalkFileOrder(t *testing.T) {
 // Run the test once to see how long it normally takes
 start := time.Now()
 doTestWalkFileOrder(t, time.Duration(0))
 duration := time.Since(start)

 // Run the test again, but put enough of a sleep into each visitor to detect ordering
 // problems if they exist
 doTestWalkFileOrder(t, duration)
}

// test that WalkBlueprintsFiles calls asyncVisitor in the right order
func doTestWalkFileOrder(t *testing.T, sleepDuration time.Duration) {
 // setup mock context
 ctx := newContext()
 mockFiles := map[string][]byte{
  "Android.bp": []byte(`
   sample_module {
       name: "a",
   }
  `),
  "dir1/Android.bp": []byte(`
   sample_module {
       name: "b",
   }
  `),
  "dir1/dir2/Android.bp": []byte(`
   sample_module {
       name: "c",
   }
  `),
 }
 ctx.MockFileSystem(mockFiles)

 // prepare to monitor the visit order
 visitOrder := []string{}
 visitLock := sync.Mutex{}
 correctVisitOrder := []string{"Android.bp", "dir1/Android.bp", "dir1/dir2/Android.bp"}

 // sleep longer when processing the earlier files
 chooseSleepDuration := func(fileName string) (duration time.Duration) {
  duration = time.Duration(0)
  for i := len(correctVisitOrder) - 1; i >= 0; i-- {
   if fileName == correctVisitOrder[i] {
    return duration
   }
   duration = duration + sleepDuration
  }
  panic("unrecognized file name " + fileName)
 }

 visitor := func(file *parser.File) {
  time.Sleep(chooseSleepDuration(file.Name))
  visitLock.Lock()
  defer visitLock.Unlock()
  visitOrder = append(visitOrder, file.Name)
 }
 keys := []string{"Android.bp", "dir1/Android.bp", "dir1/dir2/Android.bp"}

 // visit the blueprints files
 ctx.WalkBlueprintsFiles(".", keys, visitor)

 // check the order
 if !reflect.DeepEqual(visitOrder, correctVisitOrder) {
  t.Errorf("Incorrect visit order; expected %v, got %v", correctVisitOrder, visitOrder)
 }
}

// test that WalkBlueprintsFiles reports syntax errors
func TestWalkingWithSyntaxError(t *testing.T) {
 // setup mock context
 ctx := newContext()
 mockFiles := map[string][]byte{
  "Android.bp": []byte(`
   sample_module {
       name: "a" "b",
   }
  `),
  "dir1/Android.bp": []byte(`
   sample_module {
       name: "b",
  `),
  "dir1/dir2/Android.bp": []byte(`
   sample_module {
       name: "c",
   }
  `),
 }
 ctx.MockFileSystem(mockFiles)

 keys := []string{"Android.bp", "dir1/Android.bp", "dir1/dir2/Android.bp"}

 // visit the blueprints files
 _, errs := ctx.WalkBlueprintsFiles(".", keys, func(file *parser.File) {})

 expectedErrs := []error{
  errors.New(`Android.bp:3:18: expected "}", found String`),
  errors.New(`dir1/Android.bp:4:3: expected "}", found EOF`),
 }
 if fmt.Sprintf("%s", expectedErrs) != fmt.Sprintf("%s", errs) {
  t.Errorf("Incorrect errors; expected:\n%s\ngot:\n%s", expectedErrs, errs)
 }

}

func TestParseFailsForModuleWithoutName(t *testing.T) {
 ctx := NewContext()
 ctx.MockFileSystem(map[string][]byte{
  "Android.bp": []byte(`
   foo_module {
       name: "A",
   }

   bar_module {
       deps: ["A"],
   }
  `),
 })
 ctx.RegisterModuleType("foo_module", newFooModule)
 ctx.RegisterModuleType("bar_module", newBarModule)

 _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)

 expectedErrs := []error{
  errors.New(`Android.bp:6:4: property 'name' is missing from a module`),
 }
 if fmt.Sprintf("%s", expectedErrs) != fmt.Sprintf("%s", errs) {
  t.Errorf("Incorrect errors; expected:\n%s\ngot:\n%s", expectedErrs, errs)
 }
}

func Test_findVariant(t *testing.T) {
 module := &moduleInfo{
  variant: variant{
   name: "normal_local",
   variations: variationMap{
    map[string]string{
     "normal": "normal",
    },
   },
  },
 }

 makeDependencyGroup := func(in ...*moduleInfo) *moduleGroup {
  group := &moduleGroup{
   name: "dep",
  }
  for _, m := range in {
   m.group = group
   group.modules = append(group.modules, m)
  }
  return group
 }

 tests := []struct {
  name         string
  possibleDeps *moduleGroup
  variations   []Variation
  far          bool
  reverse      bool
  want         string
 }{
  {
   name: "AddVariationDependencies(nil)",
   // A dependency that matches the non-local variations of the module
   possibleDeps: makeDependencyGroup(
    &moduleInfo{
     variant: variant{
      name: "normal",
      variations: variationMap{
       map[string]string{
        "normal": "normal",
       },
      },
     },
    },
   ),
   variations: nil,
   far:        false,
   reverse:    false,
   want:       "normal",
  },
  {
   name: "AddVariationDependencies(a)",
   // A dependency with local variations
   possibleDeps: makeDependencyGroup(
    &moduleInfo{
     variant: variant{
      name: "normal_a",
      variations: variationMap{
       map[string]string{
        "normal": "normal",
        "a":      "a",
       },
      },
     },
    },
   ),
   variations: []Variation{{"a", "a"}},
   far:        false,
   reverse:    false,
   want:       "normal_a",
  },
  {
   name: "AddFarVariationDependencies(far)",
   // A dependency with far variations
   possibleDeps: makeDependencyGroup(
    &moduleInfo{
     variant: variant{
      name:       "",
      variations: variationMap{},
     },
    },
    &moduleInfo{
     variant: variant{
      name: "far",
      variations: variationMap{
       map[string]string{
        "far": "far",
       },
      },
     },
    },
   ),
   variations: []Variation{{"far", "far"}},
   far:        true,
   reverse:    false,
   want:       "far",
  },
 }
 for _, tt := range tests {
  t.Run(tt.name, func(t *testing.T) {
   ctx := NewContext()
   got, _, errs := ctx.findVariant(nil, module, nil, tt.possibleDeps, tt.variations, tt.far, tt.reverse, -1)
   if errs != nil {
    t.Fatal(errs)
   }
   if g, w := got == nil, tt.want == "nil"; g != w {
    t.Fatalf("findVariant() got = %v, want %v", got, tt.want)
   }
   if got != nil {
    if g, w := got.String(), fmt.Sprintf("module %q variant %q", "dep", tt.want); g != w {
     t.Errorf("findVariant() got = %v, want %v", g, w)
    }
   }
  })
 }
}

func Test_parallelVisit(t *testing.T) {
 addDep := func(from, to *moduleInfo) {
  from.directDeps = append(from.directDeps, depInfo{to, nil})
  from.forwardDeps = append(from.forwardDeps, to)
  to.reverseDeps = append(to.reverseDeps, from)
 }

 create := func(name string) *moduleInfo {
  m := &moduleInfo{
   group: &moduleGroup{
    name: name,
   },
  }
  m.group.modules = moduleList{m}
  return m
 }
 moduleA := create("A")
 moduleB := create("B")
 moduleC := create("C")
 moduleD := create("D")
 moduleE := create("E")
 moduleF := create("F")
 moduleG := create("G")

 moduleH := create("H")
 moduleI := create("I")
 moduleJ := create("J")

 // A depends on B, B depends on C.
 addDep(moduleA, moduleB)
 addDep(moduleB, moduleC)

 // Nothing depends on D through G, and they don't depend on anything.

 // H depends on I, and I and J depend on each other.
 addDep(moduleH, moduleI)
 addDep(moduleI, moduleJ)
 addDep(moduleJ, moduleI)

 t.Run("no modules", func(t *testing.T) {
  errs := parallelVisit(slices.Values([]*moduleInfo(nil)), bottomUpVisitorImpl{}, 1,
   func(module *moduleInfo, pause pauseFunc) bool {
    panic("unexpected call to visitor")
   })
  if errs != nil {
   t.Errorf("expected no errors, got %q", errs)
  }
 })
 t.Run("bottom up", func(t *testing.T) {
  order := ""
  errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC}), bottomUpVisitorImpl{}, 1,
   func(module *moduleInfo, pause pauseFunc) bool {
    order += module.group.name
    return false
   })
  if errs != nil {
   t.Errorf("expected no errors, got %q", errs)
  }
  if g, w := order, "CBA"; g != w {
   t.Errorf("expected order %q, got %q", w, g)
  }
 })
 t.Run("pause", func(t *testing.T) {
  order := ""
  errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC, moduleD}), bottomUpVisitorImpl{}, 1,
   func(module *moduleInfo, pause pauseFunc) bool {
    if module == moduleC {
     // Pause module C on module D
     pause(moduleD)
    }
    order += module.group.name
    return false
   })
  if errs != nil {
   t.Errorf("expected no errors, got %q", errs)
  }
  if g, w := order, "DCBA"; g != w {
   t.Errorf("expected order %q, got %q", w, g)
  }
 })
 t.Run("cancel", func(t *testing.T) {
  order := ""
  errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC}), bottomUpVisitorImpl{}, 1,
   func(module *moduleInfo, pause pauseFunc) bool {
    order += module.group.name
    // Cancel in module B
    return module == moduleB
   })
  if errs != nil {
   t.Errorf("expected no errors, got %q", errs)
  }
  if g, w := order, "CB"; g != w {
   t.Errorf("expected order %q, got %q", w, g)
  }
 })
 t.Run("pause and cancel", func(t *testing.T) {
  order := ""
  errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC, moduleD}), bottomUpVisitorImpl{}, 1,
   func(module *moduleInfo, pause pauseFunc) bool {
    if module == moduleC {
     // Pause module C on module D
     pause(moduleD)
    }
    order += module.group.name
    // Cancel in module D
    return module == moduleD
   })
  if errs != nil {
   t.Errorf("expected no errors, got %q", errs)
  }
  if g, w := order, "D"; g != w {
   t.Errorf("expected order %q, got %q", w, g)
  }
 })
 t.Run("parallel", func(t *testing.T) {
  order := ""
  errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC}), bottomUpVisitorImpl{}, 3,
   func(module *moduleInfo, pause pauseFunc) bool {
    order += module.group.name
    return false
   })
  if errs != nil {
   t.Errorf("expected no errors, got %q", errs)
  }
  if g, w := order, "CBA"; g != w {
   t.Errorf("expected order %q, got %q", w, g)
  }
 })
 t.Run("pause existing", func(t *testing.T) {
  order := ""
  errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC}), bottomUpVisitorImpl{}, 3,
   func(module *moduleInfo, pause pauseFunc) bool {
    if module == moduleA {
     // Pause module A on module B (an existing dependency)
     pause(moduleB)
    }
    order += module.group.name
    return false
   })
  if errs != nil {
   t.Errorf("expected no errors, got %q", errs)
  }
  if g, w := order, "CBA"; g != w {
   t.Errorf("expected order %q, got %q", w, g)
  }
 })
 t.Run("cycle", func(t *testing.T) {
  errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC}), bottomUpVisitorImpl{}, 3,
   func(module *moduleInfo, pause pauseFunc) bool {
    if module == moduleC {
     // Pause module C on module A (a dependency cycle)
     pause(moduleA)
    }
    return false
   })
  want := []string{
   `encountered dependency cycle`,
   `module "C" depends on module "A"`,
   `module "A" depends on module "B"`,
   `module "B" depends on module "C"`,
  }
  for i := range want {
   if len(errs) <= i {
    t.Errorf("missing error %s", want[i])
   } else if !strings.Contains(errs[i].Error(), want[i]) {
    t.Errorf("expected error %s, got %s", want[i], errs[i])
   }
  }
  if len(errs) > len(want) {
   for _, err := range errs[len(want):] {
    t.Errorf("unexpected error %s", err.Error())
   }
  }
 })
 t.Run("pause cycle", func(t *testing.T) {
  errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC, moduleD}), bottomUpVisitorImpl{}, 3,
   func(module *moduleInfo, pause pauseFunc) bool {
    if module == moduleC {
     // Pause module C on module D
     pause(moduleD)
    }
    if module == moduleD {
     // Pause module D on module C (a pause cycle)
     pause(moduleC)
    }
    return false
   })
  want := []string{
   `encountered dependency cycle`,
   `module "D" depends on module "C"`,
   `module "C" depends on module "D"`,
  }
  for i := range want {
   if len(errs) <= i {
    t.Errorf("missing error %s", want[i])
   } else if !strings.Contains(errs[i].Error(), want[i]) {
    t.Errorf("expected error %s, got %s", want[i], errs[i])
   }
  }
  if len(errs) > len(want) {
   for _, err := range errs[len(want):] {
    t.Errorf("unexpected error %s", err.Error())
   }
  }
 })
 t.Run("pause cycle with deps", func(t *testing.T) {
  pauseDeps := map[*moduleInfo]*moduleInfo{
   // F and G form a pause cycle
   moduleF: moduleG,
   moduleG: moduleF,
   // D depends on E which depends on the pause cycle, making E the first alphabetical
   // entry in pauseMap, which is not part of the cycle.
   moduleD: moduleE,
   moduleE: moduleF,
  }
  errs := parallelVisit(slices.Values([]*moduleInfo{moduleD, moduleE, moduleF, moduleG}), bottomUpVisitorImpl{}, 4,
   func(module *moduleInfo, pause pauseFunc) bool {
    if dep, ok := pauseDeps[module]; ok {
     pause(dep)
    }
    return false
   })
  want := []string{
   `encountered dependency cycle`,
   `module "G" depends on module "F"`,
   `module "F" depends on module "G"`,
  }
  for i := range want {
   if len(errs) <= i {
    t.Errorf("missing error %s", want[i])
   } else if !strings.Contains(errs[i].Error(), want[i]) {
    t.Errorf("expected error %s, got %s", want[i], errs[i])
   }
  }
  if len(errs) > len(want) {
   for _, err := range errs[len(want):] {
    t.Errorf("unexpected error %s", err.Error())
   }
  }
 })
 t.Run("existing cycle", func(t *testing.T) {
  errs := parallelVisit(slices.Values([]*moduleInfo{moduleG, moduleH, moduleI, moduleJ}), bottomUpVisitorImpl{}, 3,
   func(module *moduleInfo, pause pauseFunc) bool {
    if module == moduleG {
     // Pause module G on module I.  This verifies the fix for b/433694465, where
     // having a module paused on a cycle of existing dependencies could drop
     // the cycle error.
     pause(moduleI)
    }
    return false
   })
  want := []string{
   `encountered dependency cycle`,
   `module "J" depends on module "I"`,
   `module "I" depends on module "J"`,
  }
  for i := range want {
   if len(errs) <= i {
    t.Errorf("missing error %s", want[i])
   } else if !strings.Contains(errs[i].Error(), want[i]) {
    t.Errorf("expected error %s, got %s", want[i], errs[i])
   }
  }
  if len(errs) > len(want) {
   for _, err := range errs[len(want):] {
    t.Errorf("unexpected error %s", err.Error())
   }
  }
 })
}

func TestDeduplicateOrderOnlyDeps(t *testing.T) {
 b := func(output string, inputs []string, orderOnlyDeps []string) *buildDef {
  return &buildDef{
   OutputStrings:    []string{output},
   InputStrings:     inputs,
   OrderOnlyStrings: uniquelist.Make(orderOnlyDeps),
  }
 }

 type testcase struct {
  bp             string
  expectedPhonys []*buildDef
  conversions    map[string][]string
 }
 fnvHash := func(s string) string {
  hash := fnv.New64a()
  hash.Write([]byte(s))
  return strconv.FormatUint(hash.Sum64(), 16)
 }
 testCases := []testcase{{
  bp: `
   foo_module {
     name: "A",
     outputs: ["A"],
     order_only: ["d"],
   }
   foo_module {
     name: "B",
     outputs: ["B"],
     order_only: ["d"],
   }
  `,
  expectedPhonys: []*buildDef{
   b("dedup-"+fnvHash("d"), []string{"d"}, nil),
  },
  conversions: map[string][]string{
   "A": []string{"dedup-" + fnvHash("d")},
   "B": []string{"dedup-" + fnvHash("d")},
  },
 }, {
  bp: `
   foo_module {
     name: "A",
     outputs: ["A"],
     order_only: ["a"],
   }
   foo_module {
     name: "B",
     outputs: ["B"],
     order_only: ["b"],
   }
  `,
 }, {
  bp: `
   foo_module {
     name: "A",
     outputs: ["A"],
     order_only: ["a"],
   }
   foo_module {
     name: "B",
     outputs: ["B"],
     order_only: ["b"],
   }
   foo_module {
     name: "C",
     outputs: ["C"],
     order_only: ["a"],
   }
  `,
  expectedPhonys: []*buildDef{b("dedup-"+fnvHash("a"), []string{"a"}, nil)},
  conversions: map[string][]string{
   "A": []string{"dedup-" + fnvHash("a")},
   "B": []string{"b"},
   "C": []string{"dedup-" + fnvHash("a")},
  },
 }, {
  bp: `
   foo_module {
     name: "A",
     outputs: ["A"],
     order_only: ["a", "b"],
     extra_outputs: ["B"],
     extra_order_only: ["a", "b"],
   }
   foo_module {
     name: "C",
     outputs: ["C"],
     order_only: ["a", "c"],
     extra_outputs: ["D"],
     extra_order_only: ["a", "c"],
   }
  `,
  expectedPhonys: []*buildDef{
   b("dedup-"+fnvHash("ab"), []string{"a", "b"}, nil),
   b("dedup-"+fnvHash("ac"), []string{"a", "c"}, nil)},
  conversions: map[string][]string{
   "A": []string{"dedup-" + fnvHash("ab")},
   "B": []string{"dedup-" + fnvHash("ab")},
   "C": []string{"dedup-" + fnvHash("ac")},
   "D": []string{"dedup-" + fnvHash("ac")},
  },
 }}
 for index, tc := range testCases {
  t.Run(fmt.Sprintf("TestCase-%d", index), func(t *testing.T) {
   ctx := bpSetup(t, tc.bp)
   _, errs := ctx.PrepareBuildActions(nil)
   if len(errs) > 0 {
    t.Errorf("unexpected errors calling generateModuleBuildActions:")
    for _, err := range errs {
     t.Errorf("  %s", err)
    }
    t.FailNow()
   }
   var modules []*moduleInfo
   for module := range ctx.iterateAllVariants() {
    modules = append(modules, module)
   }
   actualPhonys := ctx.deduplicateOrderOnlyDeps(modules)
   if len(actualPhonys.variables) != 0 {
    t.Errorf("No variables expected but found %v", actualPhonys.variables)
   }
   if len(actualPhonys.rules) != 0 {
    t.Errorf("No rules expected but found %v", actualPhonys.rules)
   }
   if e, a := len(tc.expectedPhonys), len(actualPhonys.buildDefs); e != a {
    t.Errorf("Expected %d build statements but got %d", e, a)
   }
   for i := 0; i < len(tc.expectedPhonys); i++ {
    a := actualPhonys.buildDefs[i]
    e := tc.expectedPhonys[i]
    if !reflect.DeepEqual(e.Outputs, a.Outputs) {
     t.Errorf("phonys expected %v but actualPhonys %v", e.Outputs, a.Outputs)
    }
    if !reflect.DeepEqual(e.Inputs, a.Inputs) {
     t.Errorf("phonys expected %v but actualPhonys %v", e.Inputs, a.Inputs)
    }
   }
   find := func(k string) *buildDef {
    for _, m := range modules {
     for _, b := range m.actionDefs.buildDefs {
      if reflect.DeepEqual(b.OutputStrings, []string{k}) {
       return b
      }
     }
    }
    return nil
   }
   for k, conversion := range tc.conversions {
    actual := find(k)
    if actual == nil {
     t.Errorf("Couldn't find %s", k)
    }
    if !reflect.DeepEqual(actual.OrderOnlyStrings.ToSlice(), conversion) {
     t.Errorf("expected %s.OrderOnly = %v but got %v", k, conversion, actual.OrderOnly)
    }
   }
  })
 }
}

func TestSourceRootDirAllowed(t *testing.T) {
 type pathCase struct {
  path           string
  decidingPrefix string
  allowed        bool
 }
 testcases := []struct {
  desc      string
  rootDirs  []string
  pathCases []pathCase
 }{
  {
   desc: "simple case",
   rootDirs: []string{
    "a",
    "b/c/d",
    "-c",
    "-d/c/a",
    "c/some_single_file",
   },
   pathCases: []pathCase{
    {
     path:           "a",
     decidingPrefix: "a",
     allowed:        true,
    },
    {
     path:           "a/b/c",
     decidingPrefix: "a",
     allowed:        true,
    },
    {
     path:           "b",
     decidingPrefix: "",
     allowed:        true,
    },
    {
     path:           "b/c/d/a",
     decidingPrefix: "b/c/d",
     allowed:        true,
    },
    {
     path:           "c",
     decidingPrefix: "c",
     allowed:        false,
    },
    {
     path:           "c/a/b",
     decidingPrefix: "c",
     allowed:        false,
    },
    {
     path:           "c/some_single_file",
     decidingPrefix: "c/some_single_file",
     allowed:        true,
    },
    {
     path:           "d/c/a/abc",
     decidingPrefix: "d/c/a",
     allowed:        false,
    },
   },
  },
  {
   desc: "root directory order matters",
   rootDirs: []string{
    "-a",
    "a/c/some_allowed_file",
    "a/b/d/some_allowed_file",
    "a/b",
    "a/c",
    "-a/b/d",
   },
   pathCases: []pathCase{
    {
     path:           "a",
     decidingPrefix: "a",
     allowed:        false,
    },
    {
     path:           "a/some_disallowed_file",
     decidingPrefix: "a",
     allowed:        false,
    },
    {
     path:           "a/c/some_allowed_file",
     decidingPrefix: "a/c/some_allowed_file",
     allowed:        true,
    },
    {
     path:           "a/b/d/some_allowed_file",
     decidingPrefix: "a/b/d/some_allowed_file",
     allowed:        true,
    },
    {
     path:           "a/b/c",
     decidingPrefix: "a/b",
     allowed:        true,
    },
    {
     path:           "a/b/c/some_allowed_file",
     decidingPrefix: "a/b",
     allowed:        true,
    },
    {
     path:           "a/b/d",
     decidingPrefix: "a/b/d",
     allowed:        false,
    },
   },
  },
 }
 for _, tc := range testcases {
  dirs := SourceRootDirs{}
  dirs.Add(tc.rootDirs...)
  for _, pc := range tc.pathCases {
   t.Run(fmt.Sprintf("%s: %s", tc.desc, pc.path), func(t *testing.T) {
    allowed, decidingPrefix := dirs.SourceRootDirAllowed(pc.path)
    if allowed != pc.allowed {
     if pc.allowed {
      t.Errorf("expected path %q to be allowed, but was not; root allowlist: %q", pc.path, tc.rootDirs)
     } else {
      t.Errorf("path %q was allowed unexpectedly; root allowlist: %q", pc.path, tc.rootDirs)
     }
    }
    if decidingPrefix != pc.decidingPrefix {
     t.Errorf("expected decidingPrefix to be %q, but got %q", pc.decidingPrefix, decidingPrefix)
    }
   })
  }
 }
}

func TestSourceRootDirs(t *testing.T) {
 root_foo_bp := `
 foo_module {
  name: "foo",
  deps: ["foo_dir1", "foo_dir_ignored_special_case"],
 }
 `
 dir1_foo_bp := `
 foo_module {
  name: "foo_dir1",
  deps: ["foo_dir_ignored"],
 }
 `
 dir_ignored_foo_bp := `
 foo_module {
  name: "foo_dir_ignored",
 }
 `
 dir_ignored_special_case_foo_bp := `
 foo_module {
  name: "foo_dir_ignored_special_case",
 }
 `
 mockFs := map[string][]byte{
  "Android.bp":                          []byte(root_foo_bp),
  "dir1/Android.bp":                     []byte(dir1_foo_bp),
  "dir_ignored/Android.bp":              []byte(dir_ignored_foo_bp),
  "dir_ignored/special_case/Android.bp": []byte(dir_ignored_special_case_foo_bp),
 }
 fileList := []string{}
 for f := range mockFs {
  fileList = append(fileList, f)
 }
 testCases := []struct {
  sourceRootDirs       []string
  expectedModuleDefs   []string
  unexpectedModuleDefs []string
  expectedErrs         []string
 }{
  {
   sourceRootDirs: []string{},
   expectedModuleDefs: []string{
    "foo",
    "foo_dir1",
    "foo_dir_ignored",
    "foo_dir_ignored_special_case",
   },
  },
  {
   sourceRootDirs: []string{"-", ""},
   unexpectedModuleDefs: []string{
    "foo",
    "foo_dir1",
    "foo_dir_ignored",
    "foo_dir_ignored_special_case",
   },
  },
  {
   sourceRootDirs: []string{"-"},
   unexpectedModuleDefs: []string{
    "foo",
    "foo_dir1",
    "foo_dir_ignored",
    "foo_dir_ignored_special_case",
   },
  },
  {
   sourceRootDirs: []string{"dir1"},
   expectedModuleDefs: []string{
    "foo",
    "foo_dir1",
    "foo_dir_ignored",
    "foo_dir_ignored_special_case",
   },
  },
  {
   sourceRootDirs: []string{"-dir1"},
   expectedModuleDefs: []string{
    "foo",
    "foo_dir_ignored",
    "foo_dir_ignored_special_case",
   },
   unexpectedModuleDefs: []string{
    "foo_dir1",
   },
   expectedErrs: []string{
    `Android.bp:2:2: module "foo" depends on skipped module "foo_dir1"; "foo_dir1" was defined in files(s) [dir1/Android.bp], but was skipped for reason(s) ["dir1/Android.bp" is a descendant of "dir1", and that path prefix was not included in PRODUCT_SOURCE_ROOT_DIRS]`,
   },
  },
  {
   sourceRootDirs: []string{"-", "dir1"},
   expectedModuleDefs: []string{
    "foo_dir1",
   },
   unexpectedModuleDefs: []string{
    "foo",
    "foo_dir_ignored",
    "foo_dir_ignored_special_case",
   },
   expectedErrs: []string{
    `dir1/Android.bp:2:2: module "foo_dir1" depends on skipped module "foo_dir_ignored"; "foo_dir_ignored" was defined in files(s) [dir_ignored/Android.bp], but was skipped for reason(s) ["dir_ignored/Android.bp" is a descendant of "", and that path prefix was not included in PRODUCT_SOURCE_ROOT_DIRS]`,
   },
  },
  {
   sourceRootDirs: []string{"-", "dir1", "dir_ignored/special_case/Android.bp"},
   expectedModuleDefs: []string{
    "foo_dir1",
    "foo_dir_ignored_special_case",
   },
   unexpectedModuleDefs: []string{
    "foo",
    "foo_dir_ignored",
   },
   expectedErrs: []string{
    "dir1/Android.bp:2:2: module \"foo_dir1\" depends on skipped module \"foo_dir_ignored\"; \"foo_dir_ignored\" was defined in files(s) [dir_ignored/Android.bp], but was skipped for reason(s) [\"dir_ignored/Android.bp\" is a descendant of \"\", and that path prefix was not included in PRODUCT_SOURCE_ROOT_DIRS]",
   },
  },
 }
 for _, tc := range testCases {
  t.Run(fmt.Sprintf(`source root dirs are %q`, tc.sourceRootDirs), func(t *testing.T) {
   ctx := NewContext()
   ctx.MockFileSystem(mockFs)
   ctx.RegisterModuleType("foo_module", newFooModule)
   ctx.RegisterBottomUpMutator("deps", depsMutator)
   ctx.AddSourceRootDirs(tc.sourceRootDirs...)
   ctx.ParseFileList(".", fileList, nil)
   _, actualErrs := ctx.ResolveDependencies(nil)

   stringErrs := []string(nil)
   for _, err := range actualErrs {
    stringErrs = append(stringErrs, err.Error())
   }
   if !reflect.DeepEqual(tc.expectedErrs, stringErrs) {
    t.Errorf("expected to find errors %v; got %v", tc.expectedErrs, stringErrs)
   }
   for _, modName := range tc.expectedModuleDefs {
    allMods := ctx.moduleGroupFromName(modName, nil)
    if allMods == nil || len(allMods.modules) != 1 {
     mods := moduleList{}
     if allMods != nil {
      mods = allMods.modules
     }
     t.Errorf("expected to find one definition for module %q, but got %v", modName, mods)
    }
   }

   for _, modName := range tc.unexpectedModuleDefs {
    allMods := ctx.moduleGroupFromName(modName, nil)
    if allMods != nil {
     t.Errorf("expected to find no definitions for module %q, but got %v", modName, allMods.modules)
    }
   }
  })
 }
}

func TestDisallowedMutatorMethods(t *testing.T) {
 testCases := []struct {
  name              string
  mutatorHandleFunc func(MutatorHandle)
  mutatorFunc       func(BottomUpMutatorContext)
  expectedPanic     string
 }{
  {
   name:              "rename",
   mutatorHandleFunc: func(handle MutatorHandle) { handle.UsesRename() },
   mutatorFunc:       func(ctx BottomUpMutatorContext) { ctx.Rename("qux") },
   expectedPanic:     "method Rename called from mutator that was not marked UsesRename",
  },
  {
   name:              "replace_dependencies",
   mutatorHandleFunc: func(handle MutatorHandle) { handle.UsesReplaceDependencies() },
   mutatorFunc:       func(ctx BottomUpMutatorContext) { ctx.ReplaceDependencies("bar") },
   expectedPanic:     "method ReplaceDependenciesIf called from mutator that was not marked UsesReplaceDependencies",
  },
  {
   name:              "replace_dependencies_if",
   mutatorHandleFunc: func(handle MutatorHandle) { handle.UsesReplaceDependencies() },
   mutatorFunc: func(ctx BottomUpMutatorContext) {
    ctx.ReplaceDependenciesIf("bar", func(from Module, tag DependencyTag, to Module) bool { return false })
   },
   expectedPanic: "method ReplaceDependenciesIf called from mutator that was not marked UsesReplaceDependencies",
  },
  {
   name:              "reverse_dependencies",
   mutatorHandleFunc: func(handle MutatorHandle) { handle.UsesReverseDependencies() },
   mutatorFunc:       func(ctx BottomUpMutatorContext) { ctx.AddReverseDependency(ctx.Module(), nil, "baz") },
   expectedPanic:     "method AddReverseDependency called from mutator that was not marked UsesReverseDependencies",
  },
  {
   name:              "create_module",
   mutatorHandleFunc: func(handle MutatorHandle) { handle.UsesCreateModule() },
   mutatorFunc: func(ctx BottomUpMutatorContext) {
    ctx.CreateModule(newFooModule, "create_module",
     &struct{ Name string }{Name: "quz"})
   },
   expectedPanic: "method CreateModule called from mutator that was not marked UsesCreateModule",
  },
 }

 runTest := func(mutatorHandleFunc func(MutatorHandle), mutatorFunc func(ctx BottomUpMutatorContext), expectedPanic string) {
  ctx := NewContext()

  ctx.MockFileSystem(map[string][]byte{
   "Android.bp": []byte(`
   foo_module {
    name: "foo",
   }

   foo_module {
    name: "bar",
    deps: ["foo"],
   }

   foo_module {
    name: "baz",
   }
  `)})

  ctx.RegisterModuleType("foo_module", newFooModule)
  ctx.RegisterBottomUpMutator("deps", depsMutator)
  handle := ctx.RegisterBottomUpMutator("mutator", func(ctx BottomUpMutatorContext) {
   if ctx.ModuleName() == "foo" {
    mutatorFunc(ctx)
   }
  })
  mutatorHandleFunc(handle)

  _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
  if len(errs) > 0 {
   t.Errorf("unexpected parse errors:")
   for _, err := range errs {
    t.Errorf("  %s", err)
   }
   t.FailNow()
  }

  _, errs = ctx.ResolveDependencies(nil)
  if expectedPanic != "" {
   if len(errs) == 0 {
    t.Errorf("missing expected error %q", expectedPanic)
   } else if !strings.Contains(errs[0].Error(), expectedPanic) {
    t.Errorf("missing expected error %q in %q", expectedPanic, errs[0].Error())
   }
  } else if len(errs) > 0 {
   t.Errorf("unexpected dep errors:")
   for _, err := range errs {
    t.Errorf("  %s", err)
   }
   t.FailNow()
  }
 }

 noopMutatorHandleFunc := func(MutatorHandle) {}

 for _, testCase := range testCases {
  t.Run(testCase.name, func(t *testing.T) {
   t.Run("allowed", func(t *testing.T) {
    // Test that the method doesn't panic when the handle function is called.
    runTest(testCase.mutatorHandleFunc, testCase.mutatorFunc, "")
   })
   t.Run("disallowed", func(t *testing.T) {
    // Test that the method does panic with the expected error when the
    // handle function is not called.
    runTest(noopMutatorHandleFunc, testCase.mutatorFunc, testCase.expectedPanic)
   })
  })
 }

}

func Benchmark_parallelVisit(b *testing.B) {
 b.ReportAllocs()
 create := func(name string) *moduleInfo {
  m := &moduleInfo{
   group: &moduleGroup{
    name: name,
   },
  }
  m.group.modules = moduleList{m}
  return m
 }

 addDep := func(from, to *moduleInfo) {
  from.directDeps = append(from.directDeps, depInfo{to, nil})
  from.forwardDeps = append(from.forwardDeps, to)
  to.reverseDeps = append(to.reverseDeps, from)
 }
 _ = addDep

 var modules []*moduleInfo

 for i := range b.N {
  modules = append(modules, create(strconv.Itoa(i)))
  if i != 0 {
   //addDep(modules[len(modules)-1], modules[len(modules)-2])
  }
 }

 b.ResetTimer()
 errs := parallelVisit(slices.Values(modules), bottomUpVisitorImpl{}, 1000,
  func(module *moduleInfo, pause pauseFunc) bool {
   //fmt.Println(module.group.name)
   return false
  })
 if errs != nil {
  b.Errorf("expected no errors, got %q", errs)
 }
}

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

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik