go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cipkg/base/workflow/packages_test.go (about) 1 // Copyright 2023 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package workflow 16 17 import ( 18 "context" 19 "errors" 20 "runtime" 21 "sync" 22 "sync/atomic" 23 "testing" 24 25 "go.chromium.org/luci/cipkg/base/actions" 26 "go.chromium.org/luci/cipkg/base/generators" 27 "go.chromium.org/luci/cipkg/core" 28 29 . "github.com/smartystreets/goconvey/convey" 30 ) 31 32 func TestLocalPackageManager(t *testing.T) { 33 Convey("Test LocalPackageManager", t, func() { 34 pm, err := NewLocalPackageManager(t.TempDir()) 35 So(err, ShouldBeNil) 36 37 Convey("new package - ok", func() { 38 h := pm.Get("something") 39 So(h, ShouldNotBeNil) 40 err = h.IncRef() 41 So(errors.Is(err, core.ErrPackageNotExist), ShouldBeTrue) 42 43 built := false 44 err = h.Build(func() error { 45 built = true 46 return nil 47 }) 48 So(built, ShouldBeTrue) 49 So(err, ShouldBeNil) 50 51 err = h.IncRef() 52 So(err, ShouldBeNil) 53 ok, err := h.TryRemove() // We can't remove a package in use. 54 So(err, ShouldBeNil) 55 So(ok, ShouldBeFalse) 56 57 err = h.DecRef() 58 So(err, ShouldBeNil) 59 ok, err = h.TryRemove() 60 So(err, ShouldBeNil) 61 So(ok, ShouldBeTrue) 62 63 err = h.IncRef() 64 So(errors.Is(err, core.ErrPackageNotExist), ShouldBeTrue) 65 }) 66 67 Convey("new package - build error", func() { 68 h := pm.Get("something") 69 So(h, ShouldNotBeNil) 70 err = h.IncRef() 71 So(errors.Is(err, core.ErrPackageNotExist), ShouldBeTrue) 72 73 someErr := errors.New("some err") 74 built := false 75 err = h.Build(func() error { 76 built = true 77 return someErr 78 }) 79 So(built, ShouldBeTrue) 80 So(errors.Is(err, someErr), ShouldBeTrue) 81 }) 82 83 Convey("lock", func(c C) { 84 var wg sync.WaitGroup 85 wg.Add(2) 86 87 signal := make(chan struct{}) 88 89 go func() { 90 h := pm.Get("something") 91 err := h.Build(func() error { 92 signal <- struct{}{} // holding exclusive lock 93 <-signal // waiting signal to release 94 return nil 95 }) 96 c.So(err, ShouldBeNil) 97 wg.Done() 98 }() 99 100 <-signal 101 var built atomic.Bool 102 go func() { 103 h := pm.Get("something") 104 err := h.IncRef() // blocked by build 105 // exclusive lock released 106 c.So(err, ShouldBeNil) 107 c.So(built.Load(), ShouldBeTrue) 108 err = h.DecRef() 109 c.So(err, ShouldBeNil) 110 wg.Done() 111 }() 112 113 // We can't ensure `close` happened after IncRef. So this will be flaky 114 // if something wrong here. 115 runtime.Gosched() // Let IncRef execute. 116 built.Store(true) 117 close(signal) 118 wg.Wait() 119 }) 120 121 Convey("prune", func() { 122 h := pm.Get("something") 123 So(h, ShouldNotBeNil) 124 err = h.IncRef() 125 So(errors.Is(err, core.ErrPackageNotExist), ShouldBeTrue) 126 127 built := false 128 err = h.Build(func() error { 129 built = true 130 return nil 131 }) 132 So(built, ShouldBeTrue) 133 So(err, ShouldBeNil) 134 135 ctx := context.Background() 136 err = h.IncRef() 137 pm.Prune(ctx, 0, -1) 138 ok, err := h.TryRemove() // We can't remove a package in use. 139 So(err, ShouldBeNil) 140 So(ok, ShouldBeFalse) 141 142 err = h.DecRef() 143 So(err, ShouldBeNil) 144 pm.Prune(ctx, 0, -1) // Pruned 145 err = h.IncRef() 146 So(errors.Is(err, core.ErrPackageNotExist), ShouldBeTrue) 147 }) 148 }) 149 } 150 151 func TestRefRecursiveRuntime(t *testing.T) { 152 Convey("Test RefRecursiveRuntime", t, func() { 153 ctx := context.Background() 154 155 pm, err := NewLocalPackageManager(t.TempDir()) 156 So(err, ShouldBeNil) 157 158 b := NewBuilder(generators.Platforms{}, pm, actions.NewActionProcessor()) 159 b.SetExecutor(func(context.Context, *ExecutionConfig, *core.Derivation) error { return nil }) 160 first, err := b.Build(ctx, "", &Generator{ 161 Name: "first", 162 Dependencies: []generators.Dependency{ 163 {Generator: &Generator{Name: "second"}, Type: generators.DepsBuildHost, Runtime: true}, 164 }, 165 }) 166 So(err, ShouldBeNil) 167 168 Convey("ok", func() { 169 So(func() { MustIncRefRecursiveRuntime(first) }, ShouldNotPanic) 170 So(func() { MustDecRefRecursiveRuntime(first) }, ShouldNotPanic) 171 }) 172 Convey("panic inc", func() { 173 So(func() { MustIncRefRecursiveRuntime(first) }, ShouldNotPanic) 174 So(func() { MustIncRefRecursiveRuntime(first) }, ShouldPanic) 175 So(func() { MustDecRefRecursiveRuntime(first) }, ShouldNotPanic) 176 }) 177 }) 178 179 }