// (c) 2019-2020, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowball import ( "math/rand" "strings" "testing" "github.com/prometheus/client_golang/prometheus" "github.com/ava-labs/gecko/ids" ) func TestTreeParams(t *testing.T) { ParamsTest(t, TreeFactory{}) } func TestSnowballSingleton(t *testing.T) { params := Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 3, BetaRogue: 5, } tree := Tree{} tree.Initialize(params, Red) if tree.Finalized() { t.Fatalf("Snowball is finalized too soon") } oneRed := ids.Bag{} oneRed.Add(Red) tree.RecordPoll(oneRed) if tree.Finalized() { t.Fatalf("Snowball is finalized too soon") } tree.RecordPoll(oneRed) if tree.Finalized() { t.Fatalf("Snowball is finalized too soon") } tree.RecordPoll(oneRed) if !tree.Finalized() { t.Fatalf("Snowball should be finalized") } else if !Red.Equals(tree.Preference()) { t.Fatalf("After only voting red, something else was decided") } tree.Add(Blue) oneBlue := ids.Bag{} oneBlue.Add(Blue) tree.RecordPoll(oneBlue) if !tree.Finalized() { t.Fatalf("Snowball should be finalized") } else if !Red.Equals(tree.Preference()) { t.Fatalf("After only voting red, something else was decided") } } func TestSnowballRecordUnsuccessfulPoll(t *testing.T) { params := Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 3, BetaRogue: 5, } tree := Tree{} tree.Initialize(params, Red) if tree.Finalized() { t.Fatalf("Snowball is finalized too soon") } oneRed := ids.Bag{} oneRed.Add(Red) tree.RecordPoll(oneRed) tree.RecordUnsuccessfulPoll() tree.RecordPoll(oneRed) if tree.Finalized() { t.Fatalf("Snowball is finalized too soon") } tree.RecordPoll(oneRed) if tree.Finalized() { t.Fatalf("Snowball is finalized too soon") } tree.RecordPoll(oneRed) if !tree.Finalized() { t.Fatalf("Snowball should be finalized") } else if !Red.Equals(tree.Preference()) { t.Fatalf("After only voting red, something else was decided") } } func TestSnowballBinary(t *testing.T) { params := Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 2, } tree := Tree{} tree.Initialize(params, Red) tree.Add(Blue) if pref := tree.Preference(); !Red.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", Red, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } oneBlue := ids.Bag{} oneBlue.Add(Blue) tree.RecordPoll(oneBlue) if pref := tree.Preference(); !Blue.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", Blue, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } oneRed := ids.Bag{} oneRed.Add(Red) tree.RecordPoll(oneRed) if pref := tree.Preference(); !Blue.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", Blue, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } tree.RecordPoll(oneBlue) if pref := tree.Preference(); !Blue.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", Blue, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } tree.RecordPoll(oneBlue) if pref := tree.Preference(); !Blue.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", Blue, pref) } else if !tree.Finalized() { t.Fatalf("Didn't finalized correctly") } } func TestSnowballLastBinary(t *testing.T) { zero := ids.Empty one := ids.NewID([32]byte{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, }) params := Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 2, BetaRogue: 2, } tree := Tree{} tree.Initialize(params, zero) tree.Add(one) expected := "SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [0, 255)\n" + " SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 255" if str := tree.String(); expected != str { t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str) } else if pref := tree.Preference(); !zero.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", zero, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } oneBag := ids.Bag{} oneBag.Add(one) tree.RecordPoll(oneBag) if pref := tree.Preference(); !one.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", one, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } tree.RecordPoll(oneBag) if pref := tree.Preference(); !one.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", one, pref) } else if !tree.Finalized() { t.Fatalf("Finalized too late") } } func TestSnowballTrinary(t *testing.T) { params := Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 2, } tree := Tree{} tree.Initialize(params, Green) tree.Add(Red) tree.Add(Blue) // * // / \ // R * // / \ // G B if pref := tree.Preference(); !Green.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", Green, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } redBag := ids.Bag{} redBag.Add(Red) tree.RecordPoll(redBag) if pref := tree.Preference(); !Red.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", Red, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } blueBag := ids.Bag{} blueBag.Add(Blue) tree.RecordPoll(blueBag) if pref := tree.Preference(); !Red.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", Red, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } greenBag := ids.Bag{} greenBag.Add(Green) tree.RecordPoll(greenBag) // Here is a case where voting for a color makes a different color become // the preferred color. This is intended behavior. if pref := tree.Preference(); !Blue.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", Blue, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } tree.RecordPoll(redBag) if pref := tree.Preference(); !Blue.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", Blue, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } tree.RecordPoll(greenBag) if pref := tree.Preference(); !Green.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", Green, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } } func TestSnowballCloseTrinary(t *testing.T) { yellow := ids.NewID([32]byte{0x01}) cyan := ids.NewID([32]byte{0x02}) magenta := ids.NewID([32]byte{0x03}) params := Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 2, } tree := Tree{} tree.Initialize(params, yellow) tree.Add(cyan) tree.Add(magenta) if pref := tree.Preference(); !yellow.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", yellow, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } yellowBag := ids.Bag{} yellowBag.Add(yellow) tree.RecordPoll(yellowBag) if pref := tree.Preference(); !yellow.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", yellow, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } magentaBag := ids.Bag{} magentaBag.Add(magenta) tree.RecordPoll(magentaBag) if pref := tree.Preference(); !yellow.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", yellow, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } cyanBag := ids.Bag{} cyanBag.Add(cyan) tree.RecordPoll(cyanBag) if pref := tree.Preference(); !yellow.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", yellow, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } tree.RecordPoll(cyanBag) if pref := tree.Preference(); !yellow.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", yellow, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } } func TestSnowball5Colors(t *testing.T) { numColors := 5 params := Parameters{ Metrics: prometheus.NewRegistry(), K: 5, Alpha: 5, BetaVirtuous: 20, BetaRogue: 30, } colors := []ids.ID{} for i := 0; i < numColors; i++ { colors = append(colors, ids.Empty.Prefix(uint64(i))) } tree0 := Tree{} tree0.Initialize(params, colors[4]) tree0.Add(colors[0]) tree0.Add(colors[1]) tree0.Add(colors[2]) tree0.Add(colors[3]) tree1 := Tree{} tree1.Initialize(params, colors[3]) tree1.Add(colors[0]) tree1.Add(colors[1]) tree1.Add(colors[2]) tree1.Add(colors[4]) s1 := tree0.String() s2 := tree1.String() if strings.Count(s1, " ") != strings.Count(s2, " ") { t.Fatalf("Mis-matched initial values:\n\n%s\n\n%s", s1, s2) } } func TestSnowballFineGrained(t *testing.T) { c0000 := ids.NewID([32]byte{0x00}) c1000 := ids.NewID([32]byte{0x01}) c1100 := ids.NewID([32]byte{0x03}) c0010 := ids.NewID([32]byte{0x04}) params := Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 2, } tree := Tree{} tree.Initialize(params, c0000) { expected := "SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [0, 256)" if str := tree.String(); expected != str { t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str) } else if pref := tree.Preference(); !c0000.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } } tree.Add(c1100) { expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 0\n" + " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 256)\n" + " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 256)" if str := tree.String(); expected != str { t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str) } else if pref := tree.Preference(); !c0000.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } } tree.Add(c1000) { expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 0\n" + " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 256)\n" + " SB(Preference = 1, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 1))) Bit = 1\n" + " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [2, 256)\n" + " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [2, 256)" if str := tree.String(); expected != str { t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str) } else if pref := tree.Preference(); !c0000.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } } tree.Add(c0010) { expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 0\n" + " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 2)\n" + " SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 2\n" + " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [3, 256)\n" + " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [3, 256)\n" + " SB(Preference = 1, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 1))) Bit = 1\n" + " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [2, 256)\n" + " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [2, 256)" if str := tree.String(); expected != str { t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str) } else if pref := tree.Preference(); !c0000.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } } c0000Bag := ids.Bag{} c0000Bag.Add(c0000) tree.RecordPoll(c0000Bag) { expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF(Confidence = 1, Finalized = false, SL(Preference = 0))) Bit = 0\n" + " SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF(Confidence = 1, Finalized = false, SL(Preference = 0))) Bit = 2\n" + " SB(NumSuccessfulPolls = 1, SF(Confidence = 1, Finalized = true)) Bits = [3, 256)\n" + " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [3, 256)\n" + " SB(Preference = 1, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 1))) Bit = 1\n" + " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [2, 256)\n" + " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [2, 256)" if str := tree.String(); expected != str { t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str) } else if pref := tree.Preference(); !c0000.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } } c0010Bag := ids.Bag{} c0010Bag.Add(c0010) tree.RecordPoll(c0010Bag) { expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 1, SF(Confidence = 1, Finalized = false, SL(Preference = 1))) Bit = 2\n" + " SB(NumSuccessfulPolls = 1, SF(Confidence = 1, Finalized = true)) Bits = [3, 256)\n" + " SB(NumSuccessfulPolls = 1, SF(Confidence = 1, Finalized = true)) Bits = [3, 256)" if str := tree.String(); expected != str { t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str) } else if pref := tree.Preference(); !c0000.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } } tree.RecordPoll(c0010Bag) { expected := "SB(NumSuccessfulPolls = 2, SF(Confidence = 2, Finalized = true)) Bits = [3, 256)" if str := tree.String(); expected != str { t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str) } else if pref := tree.Preference(); !c0010.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", c0010, pref) } else if !tree.Finalized() { t.Fatalf("Finalized too late") } } } func TestSnowballDoubleAdd(t *testing.T) { params := Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 3, BetaRogue: 5, } tree := Tree{} tree.Initialize(params, Red) tree.Add(Red) { expected := "SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [0, 256)" if str := tree.String(); expected != str { t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str) } else if pref := tree.Preference(); !Red.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", Red, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } } } func TestSnowballConsistent(t *testing.T) { numColors := 50 numNodes := 100 params := Parameters{ Metrics: prometheus.NewRegistry(), K: 20, Alpha: 15, BetaVirtuous: 20, BetaRogue: 30, } seed := int64(0) rand.Seed(seed) n := Network{} n.Initialize(params, numColors) for i := 0; i < numNodes; i++ { n.AddNode(&Tree{}) } for !n.Finalized() && !n.Disagreement() { n.Round() } if !n.Agreement() { t.Fatalf("Network agreed on inconsistent values") } } func TestSnowballFilterBinaryChildren(t *testing.T) { c0000 := ids.NewID([32]byte{0b00000000}) c1000 := ids.NewID([32]byte{0b00000001}) c0100 := ids.NewID([32]byte{0b00000010}) c0010 := ids.NewID([32]byte{0b00000100}) params := Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 2, } tree := Tree{} tree.Initialize(params, c0000) { expected := "SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [0, 256)" if pref := tree.Preference(); !c0000.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } else if str := tree.String(); expected != str { t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str) } } tree.Add(c1000) { expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 0\n"+ " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 256)\n"+ " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 256)" if pref := tree.Preference(); !c0000.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } else if str := tree.String(); expected != str { t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str) } } tree.Add(c0010) { expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 0\n"+ " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 2)\n"+ " SB(Preference = 0, NumSuccessfulPolls[0] = 0, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 2\n"+ " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [3, 256)\n"+ " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [3, 256)\n"+ " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 256)" if pref := tree.Preference(); !c0000.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } else if str := tree.String(); expected != str { t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str) } } c0000Bag := ids.Bag{} c0000Bag.Add(c0000) tree.RecordPoll(c0000Bag) { expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF(Confidence = 1, Finalized = false, SL(Preference = 0))) Bit = 0\n"+ " SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF(Confidence = 1, Finalized = false, SL(Preference = 0))) Bit = 2\n"+ " SB(NumSuccessfulPolls = 1, SF(Confidence = 1, Finalized = true)) Bits = [3, 256)\n"+ " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [3, 256)\n"+ " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 256)" if pref := tree.Preference(); !c0000.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } else if str := tree.String(); expected != str { t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str) } } tree.Add(c0100) { expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF(Confidence = 1, Finalized = false, SL(Preference = 0))) Bit = 0\n"+ " SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF(Confidence = 1, Finalized = false, SL(Preference = 0))) Bit = 2\n"+ " SB(NumSuccessfulPolls = 1, SF(Confidence = 1, Finalized = true)) Bits = [3, 256)\n"+ " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [3, 256)\n"+ " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [1, 256)" if pref := tree.Preference(); !c0000.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } else if str := tree.String(); expected != str { t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str) } } c0100Bag := ids.Bag{} c0100Bag.Add(c0100) tree.RecordPoll(c0100Bag) { expected := "SB(Preference = 0, NumSuccessfulPolls[0] = 1, NumSuccessfulPolls[1] = 0, SF(Confidence = 0, Finalized = false, SL(Preference = 0))) Bit = 2\n"+ " SB(NumSuccessfulPolls = 1, SF(Confidence = 1, Finalized = true)) Bits = [3, 256)\n"+ " SB(NumSuccessfulPolls = 0, SF(Confidence = 0, Finalized = false)) Bits = [3, 256)" if pref := tree.Preference(); !c0000.Equals(pref) { t.Fatalf("Wrong preference. Expected %s got %s", c0000, pref) } else if tree.Finalized() { t.Fatalf("Finalized too early") } else if str := tree.String(); expected != str { t.Fatalf("Wrong string. Expected:\n%s\ngot:\n%s", expected, str) } } }