github.com/benchkram/bob@v0.0.0-20240314204020-b7a57f2f9be9/test/e2e/multilevelbuild/multilevelbuild_test.go (about) 1 package multilevelbuildtest 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "time" 11 12 "github.com/benchkram/bob/bob" 13 "github.com/benchkram/bob/bob/playbook" 14 "github.com/benchkram/bob/pkg/file" 15 "github.com/benchkram/errz" 16 17 . "github.com/onsi/ginkgo" 18 . "github.com/onsi/gomega" 19 ) 20 21 type binaryOutputFixture struct { 22 path string 23 output string 24 } 25 26 type requiresRebuildFixture struct { 27 taskname string 28 requiresRebuild bool 29 } 30 31 var _ = Describe("Test bob multilevel build", func() { 32 Context("in a fresh environment", func() { 33 34 It("initializes bob playground", func() { 35 Expect(bob.CreatePlayground(bob.PlaygroundOptions{Dir: dir})).NotTo(HaveOccurred()) 36 }) 37 38 It("runs build all", func() { 39 ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) 40 Expect(b.Build(ctx, bob.BuildAllTargetName)).NotTo(HaveOccurred()) 41 cancel() 42 }) 43 44 binaries := []binaryOutputFixture{ 45 { 46 path: filepath.Join(dir, "run"), 47 output: "Hello Playground v1\nByebye Playground v1\n", 48 }, 49 { 50 path: filepath.Join(dir, bob.SecondLevelDir, "runsecondlevel"), 51 output: "Hello Playground v2\nByebye Playground v2\n", 52 }, 53 { 54 path: filepath.Join(dir, bob.SecondLevelDir, bob.ThirdLevelDir, "runthirdlevel"), 55 output: "Hello Playground v3\nByebye Playground v3\n", 56 }, 57 } 58 59 It("checks that the built binaries exist", func() { 60 for _, b := range binaries { 61 Expect(file.Exists(b.path)).To(BeTrue(), fmt.Sprintf("%s doesn't exist", b.path)) 62 } 63 }) 64 65 It("checks that the built binaries produce the expected output", func() { 66 for _, b := range binaries { 67 cmd := exec.Command("./" + b.path) 68 var stdout, stderr bytes.Buffer 69 cmd.Stdout = &stdout 70 cmd.Stderr = &stderr 71 72 // The binarys are waiting for a ctrl-c 73 // to shutdown. 74 go func() { 75 time.Sleep(500 * time.Millisecond) 76 err := cmd.Process.Signal(os.Interrupt) 77 Expect(err).NotTo(HaveOccurred()) 78 }() 79 80 err := cmd.Run() 81 Expect(err).NotTo(HaveOccurred()) 82 83 Expect(b.output).To(Equal(stderr.String())) 84 } 85 }) 86 87 It("runs build multilinetouch", func() { 88 ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) 89 Expect(b.Build(ctx, "multilinetouch")).NotTo(HaveOccurred()) 90 cancel() 91 }) 92 93 It("checks that the touched files really exist", func() { 94 files := []string{ 95 "multilinefile1", 96 "multilinefile2", 97 "multilinefile3", 98 "multilinefile4", 99 "multilinefile5", 100 } 101 102 for _, f := range files { 103 Expect(file.Exists(f)).To(BeTrue(), fmt.Sprintf("%s doesn't exist", f)) 104 } 105 }) 106 107 It("checks that we do not require a rebuild of any of the levels", func() { 108 fixtures := []requiresRebuildFixture{ 109 { 110 taskname: bob.BuildAllTargetName, 111 requiresRebuild: false, 112 }, 113 { 114 taskname: "second-level/build2", 115 requiresRebuild: false, 116 }, 117 { 118 taskname: "second-level/third-level/build3", 119 requiresRebuild: false, 120 }, 121 } 122 123 requiresRebuildMustMatchFixtures(b, fixtures) 124 }) 125 126 It("changes a file of the second-level", func() { 127 f := filepath.Join(dir, bob.SecondLevelDir, "main2.go") 128 c, err := os.ReadFile(f) 129 Expect(err).NotTo(HaveOccurred()) 130 131 c = append(c, []byte("// some random comment so the file content is changed")...) 132 133 err = os.WriteFile(f, c, 0644) 134 Expect(err).NotTo(HaveOccurred()) 135 }) 136 137 It("checks that we now require a rebuild of the second- and first-level, but not the third-level", func() { 138 fixtures := []requiresRebuildFixture{ 139 { 140 taskname: bob.BuildAllTargetName, 141 requiresRebuild: true, 142 }, 143 { 144 taskname: "second-level/build2", 145 requiresRebuild: true, 146 }, 147 { 148 taskname: "second-level/third-level/build3", 149 requiresRebuild: false, 150 }, 151 } 152 153 requiresRebuildMustMatchFixtures(b, fixtures) 154 }) 155 156 It("runs build all again", func() { 157 ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) 158 Expect(b.Build(ctx, bob.BuildAllTargetName)).NotTo(HaveOccurred()) 159 cancel() 160 }) 161 162 It("checks that we do not require a rebuild of any of the levels", func() { 163 fixtures := []requiresRebuildFixture{ 164 { 165 taskname: bob.BuildAllTargetName, 166 requiresRebuild: false, 167 }, 168 { 169 taskname: "second-level/build2", 170 requiresRebuild: false, 171 }, 172 { 173 taskname: "second-level/third-level/build3", 174 requiresRebuild: false, 175 }, 176 } 177 178 requiresRebuildMustMatchFixtures(b, fixtures) 179 }) 180 181 It("changes a file of the third-level", func() { 182 f := filepath.Join(dir, bob.SecondLevelDir, bob.ThirdLevelDir, "main3.go") 183 c, err := os.ReadFile(f) 184 Expect(err).NotTo(HaveOccurred()) 185 186 c = append(c, []byte("// some random comment so the file content is changed")...) 187 188 err = os.WriteFile(f, c, 0644) 189 Expect(err).NotTo(HaveOccurred()) 190 }) 191 192 It("checks that we now require a rebuild of the third-, second- and first-level", func() { 193 fixtures := []requiresRebuildFixture{ 194 { 195 taskname: bob.BuildAllTargetName, 196 requiresRebuild: true, 197 }, 198 { 199 taskname: "second-level/build2", 200 requiresRebuild: true, 201 }, 202 { 203 taskname: "second-level/third-level/build3", 204 requiresRebuild: true, 205 }, 206 } 207 208 err := artifactsClean() 209 Expect(err).NotTo(HaveOccurred()) 210 requiresRebuildMustMatchFixtures(b, fixtures) 211 }) 212 }) 213 }) 214 215 func requiresRebuildMustMatchFixtures(b *bob.B, fixtures []requiresRebuildFixture) { 216 aggregate, err := b.Aggregate() 217 Expect(err).NotTo(HaveOccurred()) 218 219 err = b.Nix().BuildNixDependenciesInPipeline(aggregate, bob.BuildAllTargetName) 220 Expect(err).NotTo(HaveOccurred()) 221 222 pb, err := aggregate.Playbook(bob.BuildAllTargetName) 223 Expect(err).NotTo(HaveOccurred()) 224 225 err = pb.Build(context.Background()) 226 errz.Log(err) 227 Expect(err).NotTo(HaveOccurred()) 228 229 for _, f := range fixtures { 230 ts, err := pb.TaskStatus(f.taskname) 231 Expect(err).NotTo(HaveOccurred()) 232 requiresRebuild := ts.State() != playbook.StateNoRebuildRequired 233 234 Expect(f.requiresRebuild).To(Equal(requiresRebuild), fmt.Sprintf("task's %q rebuild requirement differ, got: %t, want: %t", f.taskname, requiresRebuild, f.requiresRebuild)) 235 } 236 }