Skip to content
go-libfossil Artifact [390747f070]
Login

Artifact [390747f070]

Artifact 390747f070c01760e0595a5f5f07b8b7154fa93efa88168147d049660c185ea9:


package manifest

import (
	"fmt"
	"testing"

	"github.com/danmestas/go-libfossil/internal/cone"
	"github.com/danmestas/go-libfossil/internal/content"
	"github.com/danmestas/go-libfossil/internal/deck"
	libfossil "github.com/danmestas/go-libfossil/internal/fsltype"
	"github.com/danmestas/go-libfossil/internal/hash"
	"github.com/danmestas/go-libfossil/internal/repo"
)

func sparseBoundaryCandidates(t *testing.T) []deck.FileCard {
	t.Helper()
	var candidates []deck.FileCard
	for i := 0; i < 256 && len(candidates) < 18; i++ {
		name := fmt.Sprintf("f%04d", i)
		if zeroBits(name) == 0 {
			candidates = append(candidates, deck.FileCard{Name: name, UUID: hash.SHA3([]byte(name))})
		}
	}
	if len(candidates) != 18 {
		t.Fatal("bounded search found too few non-boundary names")
	}
	return candidates
}

func elideWriterNode(t *testing.T, r *repo.Repo, uuid string) {
	t.Helper()
	for _, sql := range []string{
		"UPDATE blob SET size=-1, content=NULL WHERE uuid=?",
		"INSERT INTO phantom(rid) SELECT rid FROM blob WHERE uuid=?",
	} {
		if _, err := r.DB().Exec(sql, uuid); err != nil {
			t.Fatal(err)
		}
	}
	if _, ok := content.AvailableByUUID(r.DB(), uuid); ok {
		t.Fatal("out-of-cone submanifest was not actually removed")
	}
}

func sparseBoundaryParent(t *testing.T, candidates, oldFiles []deck.FileCard, oldTree Tree) (
	*repo.Repo, libfossil.FslID, *deck.Deck, *cone.Cone,
) {
	t.Helper()
	r := sha3Repo(t)
	setSettings(t, r, SettingEnableSubmanifests, "on", SettingSubmanifestK, "1")
	var files []File
	for _, f := range oldFiles {
		files = append(files, File{Name: f.Name, Content: []byte(f.Name)})
	}
	rid, parent := commit(t, r, CheckinOpts{Files: files})
	if len(parent.S) != len(oldTree.S) {
		t.Fatalf("parent S count %d, want %d", len(parent.S), len(oldTree.S))
	}
	var included []string
	for _, f := range candidates {
		if deck.Compare(f.Name, oldTree.S[1].Name) < 0 {
			included = append(included, f.Name)
		}
	}
	c, err := cone.New(included, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := content.SetStoreCone(r.DB(), c); err != nil {
		t.Fatal(err)
	}
	for i := 1; i < len(oldTree.S); i++ {
		if parent.S[i] != oldTree.S[i] {
			t.Fatalf("stored parent differs from canonical tree at %d", i)
		}
		elideWriterNode(t, r, parent.S[i].UUID)
	}
	_, hi := partitionLimits(1)
	if len(included) != hi+1 {
		t.Fatalf("preserving old sibling hashes leaves %d in-cone entries, want MAX+1=%d", len(included), hi+1)
	}
	return r, rid, parent, c
}

func assertBoundaryPropagation(t *testing.T, oldTree, newTree Tree) {
	t.Helper()
	for i := 1; i < len(oldTree.S); i++ {
		for _, next := range newTree.S {
			if next.UUID == oldTree.S[i].UUID {
				t.Fatalf("old untouched subtree %s unexpectedly survived the canonical insertion", oldTree.S[i].Name)
			}
		}
		oldLeaf, err := deck.Parse(oldTree.Subs[i])
		if err != nil {
			t.Fatal(err)
		}
		newLeaf, err := deck.Parse(newTree.Subs[i])
		if err != nil {
			t.Fatal(err)
		}
		oldSize, newSize := 8, 8
		if i == 2 {
			oldSize, newSize = 1, 2
		}
		if len(oldLeaf.F) != oldSize || len(newLeaf.F) != newSize {
			t.Fatalf("leaf %d sizes old=%d new=%d, want %d/%d", i, len(oldLeaf.F), len(newLeaf.F), oldSize, newSize)
		}
		t.Logf("untouched old range %s: %d entries, hash %s; canonical replacement starts %s: %d entries, hash %s",
			oldTree.S[i].Name, len(oldLeaf.F), oldTree.S[i].UUID,
			newTree.S[i].Name, len(newLeaf.F), newTree.S[i].UUID)
	}
}

// MAX cuts depend on the number of preceding entries, not just the next
// filename. An insertion can change a disjoint subtree's canonical bytes.
func TestSparseWriterCanonicalBoundaryPropagation(t *testing.T) {
	candidates := sparseBoundaryCandidates(t)
	inserted := candidates[1]
	t.Logf("insert %s into first old range", inserted.Name)
	oldFiles := append([]deck.FileCard{candidates[0]}, candidates[2:]...)
	oldTree := mustBuildTree(t, oldFiles, 1)
	newTree := mustBuildTree(t, candidates, 1)
	if len(oldTree.S) != 3 || len(newTree.S) != 3 {
		t.Fatalf("S counts old=%d new=%d, want three leaves", len(oldTree.S), len(newTree.S))
	}
	if deck.Compare(inserted.Name, oldTree.S[1].Name) >= 0 {
		t.Fatal("insertion is not confined to the first old subtree")
	}
	r, rid, parent, c := sparseBoundaryParent(t, candidates, oldFiles, oldTree)
	assertBoundaryPropagation(t, oldTree, newTree)
	visible, err := ListFilesFiltered(r, rid, c)
	if err != nil || len(visible) != 8 {
		t.Fatalf("in-cone enumeration after eliding S nodes = %d files, %v; want 8, nil", len(visible), err)
	}
	if _, err := ListFiles(r, rid); err == nil {
		t.Fatal("full enumeration unexpectedly succeeded without out-of-cone S nodes")
	} else if uuid, ok := MissingSubmanifest(err); !ok || uuid != parent.S[1].UUID {
		t.Fatalf("full enumeration error = %v; want missing %s", err, parent.S[1].UUID)
	}
}