Skip to content
quarry Artifact [28a807b5cb]
Login

Artifact [28a807b5cb]

Artifact 28a807b5cb6bd2453b8d0a82b808e925339cdba5b1f86b998b85632d99b81614:


package ci

import (
	"slices"
	"testing"
)

// askedRepo records which versions its pipeline files were read at.
type askedRepo struct {
	*fakeRepo
	asked []string
}

func (a *askedRepo) Files(v string) ([]string, error) {
	a.asked = append(a.asked, v)
	return a.fakeRepo.Files(v)
}

func (a *askedRepo) Read(p, v string) ([]byte, error) {
	a.asked = append(a.asked, v)
	return a.fakeRepo.Read(p, v)
}

// A PAIR RUN STARTS EVERY PIPELINE ONCE, FROM THE MERGED TREE'S
// DEFINITIONS, AND STOPS ONLY OLDER PAIR RUNS.
//
// A pair is not a check-in, so its definitions are the merged tree's
// (RFC 0009 §once), never read from the branch tip.
// The landing-only pipeline runs too: a pair exists only for a proposed
// branch. Asking again starts nothing. A pair against a newer target
// supersedes the older pair's runs, and never the branch tip's own.
func TestAPairRunStartsOnceFromTheBranchTip(t *testing.T) {
	srv, started, cancelled := cancellingEngine(t)
	e := New(srv.URL)
	st := &fakeStore{mark: map[string]string{"alpha": "old"}}
	repo := &askedRepo{fakeRepo: landingRepo()}
	repo.landings["feat"] = true

	repo.news = []Checkin{{Hash: "b1", Branch: "feat"}}
	if err := Trigger(e, st, "alpha", repo, "http://x/r/alpha"); err != nil {
		t.Fatal(err)
	}
	repo.news = nil
	started()
	repo.asked = nil

	if _, err := RunPairAt(e, st, "alpha", repo, "http://x/r/alpha", "feat", "b1", "t1"); err != nil {
		t.Fatal(err)
	}
	if got := started(); !slices.Equal(got, []string{"pair:b1:t1 ci/quarry.json", "pair:b1:t1 ci/race.json"}) {
		t.Errorf("FAIL: the pair started %v; want both pipelines at pair:b1:t1", got)
	}
	if len(repo.asked) != 0 {
		t.Errorf("FAIL: the pair read pipeline files at %v; want the merged tree's", repo.asked)
	}
	if got := cancelled(); len(got) != 0 {
		t.Errorf("FAIL: starting a pair cancelled %v; the branch tip's runs are still its verdict", got)
	}

	if _, err := RunPairAt(e, st, "alpha", repo, "http://x/r/alpha", "feat", "b1", "t1"); err != nil {
		t.Fatal(err)
	}
	if got := started(); len(got) != 0 {
		t.Errorf("FAIL: asking for the same pair again started %v", got)
	}

	if _, err := RunPairAt(e, st, "alpha", repo, "http://x/r/alpha", "feat", "b1", "t2"); err != nil {
		t.Fatal(err)
	}
	started()
	if got := cancelled(); !slices.Equal(got, []string{"pair:b1:t1 ci/quarry.json", "pair:b1:t1 ci/race.json"}) {
		t.Errorf("FAIL: a pair against a newer target cancelled %v; want the older pair's runs only", got)
	}
	t.Log("ok: a pair runs once, from the merged tree's definitions, and supersedes only older pairs")
}

// A branch tip with no pipelines has nothing to run for its merge, and
// says so, so the sweep need not list its files again.
func TestAPairWithNoPipelinesSaysItRanNothing(t *testing.T) {
	srv, started := startedEngine(t)
	e := New(srv.URL)
	repo := &fakeRepo{files: []string{"README.md"}, landings: map[string]bool{}}
	ran, err := RunPairAt(e, &fakeStore{}, "alpha", repo, "http://x/r/alpha", "feat", "b1", "t1")
	if err != nil || ran {
		t.Fatalf("FAIL: a tip with no pipelines answered ran=%v err=%v", ran, err)
	}
	if got := started(); len(got) != 0 {
		t.Fatalf("FAIL: started %v", got)
	}
	t.Log("ok: nothing to run, and it says so")
}

// A Re-run of a merge starts every pipeline again, where asking for the
// merge's missing runs starts nothing a second time.
func TestAReRunOfAMergeStartsEverythingAgain(t *testing.T) {
	srv, started := startedEngine(t)
	e := New(srv.URL)
	st := &fakeStore{}
	repo := landingRepo()
	if _, err := RunPairAt(e, st, "alpha", repo, "http://x/r/alpha", "feat", "b1", "t1"); err != nil {
		t.Fatal(err)
	}
	started()
	if err := RerunPairAt(e, st, "alpha", repo, "http://x/r/alpha", "feat", "b1", "t1"); err != nil {
		t.Fatal(err)
	}
	if got := started(); !slices.Equal(got, []string{"pair:b1:t1 ci/quarry.json", "pair:b1:t1 ci/race.json"}) {
		t.Fatalf("FAIL: re-running the merge started %v, want both pipelines again", got)
	}
	t.Log("ok: a re-run of a merge starts everything again")
}