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  }