mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 16:08:26 -05:00
Boundary Attestation and Balances Helper functions (#1115)
This commit is contained in:
@@ -13,8 +13,8 @@ import (
|
||||
// included in the chain during the epoch.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// [a for a in state.latest_attestations if state.slot - EPOCH_LENGTH <=
|
||||
// a.data.slot < state.slot]
|
||||
// [a for a in state.latest_attestations
|
||||
// if state.slot - EPOCH_LENGTH <= a.data.slot < state.slot]
|
||||
func Attestations(state *pb.BeaconState) []*pb.PendingAttestationRecord {
|
||||
epochLength := params.BeaconConfig().EpochLength
|
||||
var thisEpochAttestations []*pb.PendingAttestationRecord
|
||||
@@ -29,7 +29,7 @@ func Attestations(state *pb.BeaconState) []*pb.PendingAttestationRecord {
|
||||
earliestSlot = state.Slot - epochLength
|
||||
}
|
||||
|
||||
if earliestSlot <= attestation.GetData().Slot && attestation.GetData().Slot < state.Slot {
|
||||
if earliestSlot <= attestation.GetData().GetSlot() && attestation.GetData().GetSlot() < state.Slot {
|
||||
thisEpochAttestations = append(thisEpochAttestations, attestation)
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ func Attestations(state *pb.BeaconState) []*pb.PendingAttestationRecord {
|
||||
}
|
||||
|
||||
// BoundaryAttestations returns the pending attestations from
|
||||
// the epoch boundary block.
|
||||
// the epoch's boundary block.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// [a for a in this_epoch_attestations if a.data.epoch_boundary_root ==
|
||||
@@ -66,3 +66,79 @@ func BoundaryAttestations(
|
||||
}
|
||||
return boundaryAttestations, nil
|
||||
}
|
||||
|
||||
// PrevAttestations returns the attestations of the previous epoch
|
||||
// (state.slot - 2 * EPOCH_LENGTH...state.slot - EPOCH_LENGTH).
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// [a for a in state.latest_attestations
|
||||
// if state.slot - 2 * EPOCH_LENGTH <= a.slot < state.slot - EPOCH_LENGTH]
|
||||
func PrevAttestations(state *pb.BeaconState) []*pb.PendingAttestationRecord {
|
||||
epochLength := params.BeaconConfig().EpochLength
|
||||
var prevEpochAttestations []*pb.PendingAttestationRecord
|
||||
var earliestSlot uint64
|
||||
|
||||
for _, attestation := range state.LatestAttestations {
|
||||
|
||||
// If the state slot is less than 2 * epochLength, then the earliestSlot would
|
||||
// result in a negative number. Therefore we should default to
|
||||
// earliestSlot = 0 in this case.
|
||||
if state.Slot > 2*epochLength {
|
||||
earliestSlot = state.Slot - 2*epochLength
|
||||
}
|
||||
|
||||
if earliestSlot <= attestation.GetData().GetSlot() &&
|
||||
attestation.GetData().GetSlot() < state.Slot-epochLength {
|
||||
prevEpochAttestations = append(prevEpochAttestations, attestation)
|
||||
}
|
||||
}
|
||||
return prevEpochAttestations
|
||||
}
|
||||
|
||||
// PrevJustifiedAttestations returns the justified attestations
|
||||
// of the previous 2 epochs.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// [a for a in this_epoch_attestations + previous_epoch_attestations
|
||||
// if a.justified_slot == state.previous_justified_slot]
|
||||
func PrevJustifiedAttestations(
|
||||
state *pb.BeaconState,
|
||||
thisEpochAttestations []*pb.PendingAttestationRecord,
|
||||
prevEpochAttestations []*pb.PendingAttestationRecord,
|
||||
) []*pb.PendingAttestationRecord {
|
||||
var prevJustifiedAttestations []*pb.PendingAttestationRecord
|
||||
epochAttestations := append(thisEpochAttestations, prevEpochAttestations...)
|
||||
|
||||
for _, attestation := range epochAttestations {
|
||||
if attestation.GetData().GetJustifiedSlot() == state.PreviousJustifiedSlot {
|
||||
prevJustifiedAttestations = append(prevJustifiedAttestations, attestation)
|
||||
}
|
||||
}
|
||||
return prevJustifiedAttestations
|
||||
}
|
||||
|
||||
// PrevHeadAttestations returns the pending attestations from
|
||||
// the canonical beacon chain.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// [a for a in previous_epoch_attestations
|
||||
// if a.beacon_block_root == get_block_root(state, a.slot)]
|
||||
func PrevHeadAttestations(
|
||||
state *pb.BeaconState,
|
||||
prevEpochAttestations []*pb.PendingAttestationRecord,
|
||||
) ([]*pb.PendingAttestationRecord, error) {
|
||||
var headAttestations []*pb.PendingAttestationRecord
|
||||
|
||||
for _, attestation := range prevEpochAttestations {
|
||||
canonicalBlockRoot, err := types.BlockRoot(state, attestation.GetData().GetSlot())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attestationData := attestation.GetData()
|
||||
if bytes.Equal(attestationData.BeaconBlockRootHash32, canonicalBlockRoot) {
|
||||
headAttestations = append(headAttestations, attestation)
|
||||
}
|
||||
}
|
||||
return headAttestations, nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
func TestEpochAttestations_ok(t *testing.T) {
|
||||
func TestEpochAttestations(t *testing.T) {
|
||||
if params.BeaconConfig().EpochLength != 64 {
|
||||
t.Errorf("EpochLength should be 64 for these tests to pass")
|
||||
}
|
||||
@@ -51,11 +51,11 @@ func TestEpochAttestations_ok(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
state.Slot = tt.stateSlot
|
||||
|
||||
if Attestations(state)[0].Data.Slot != tt.firstAttestationSlot {
|
||||
if Attestations(state)[0].GetData().GetSlot() != tt.firstAttestationSlot {
|
||||
t.Errorf(
|
||||
"Result slot was an unexpected value. Wanted %d, got %d",
|
||||
tt.firstAttestationSlot,
|
||||
Attestations(state)[0].Data.Slot,
|
||||
Attestations(state)[0].GetData().GetSlot(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -95,12 +95,149 @@ func TestEpochBoundaryAttestations(t *testing.T) {
|
||||
t.Fatalf("EpochBoundaryAttestations failed: %v", err)
|
||||
}
|
||||
|
||||
if epochBoundaryAttestation[0].GetData().JustifiedSlot != 0 {
|
||||
t.Errorf("Wanted justified slot 0 for epoch boundary attestation, got: %d", epochBoundaryAttestation[0].Data.JustifiedSlot)
|
||||
if epochBoundaryAttestation[0].GetData().GetJustifiedSlot() != 0 {
|
||||
t.Errorf("Wanted justified slot 0 for epoch boundary attestation, got: %d", epochBoundaryAttestation[0].GetData().GetJustifiedSlot())
|
||||
}
|
||||
|
||||
if !bytes.Equal(epochBoundaryAttestation[0].GetData().JustifiedBlockRootHash32, []byte{0}) {
|
||||
if !bytes.Equal(epochBoundaryAttestation[0].GetData().GetJustifiedBlockRootHash32(), []byte{0}) {
|
||||
t.Errorf("Wanted justified block hash [0] for epoch boundary attestation, got: %v",
|
||||
epochBoundaryAttestation[0].Data.JustifiedBlockRootHash32)
|
||||
epochBoundaryAttestation[0].GetData().GetJustifiedBlockRootHash32())
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrevEpochAttestations(t *testing.T) {
|
||||
if params.BeaconConfig().EpochLength != 64 {
|
||||
t.Errorf("EpochLength should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
var pendingAttestations []*pb.PendingAttestationRecord
|
||||
for i := uint64(0); i < params.BeaconConfig().EpochLength*4; i++ {
|
||||
pendingAttestations = append(pendingAttestations, &pb.PendingAttestationRecord{
|
||||
Data: &pb.AttestationData{
|
||||
Slot: i,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
state := &pb.BeaconState{LatestAttestations: pendingAttestations}
|
||||
|
||||
tests := []struct {
|
||||
stateSlot uint64
|
||||
firstAttestationSlot uint64
|
||||
}{
|
||||
{
|
||||
stateSlot: 10,
|
||||
firstAttestationSlot: 0,
|
||||
},
|
||||
{
|
||||
stateSlot: 127,
|
||||
firstAttestationSlot: 0,
|
||||
},
|
||||
{
|
||||
stateSlot: 383,
|
||||
firstAttestationSlot: 383 - 2*params.BeaconConfig().EpochLength,
|
||||
},
|
||||
{
|
||||
stateSlot: 129,
|
||||
firstAttestationSlot: 129 - 2*params.BeaconConfig().EpochLength,
|
||||
},
|
||||
{
|
||||
stateSlot: 256,
|
||||
firstAttestationSlot: 256 - 2*params.BeaconConfig().EpochLength,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
state.Slot = tt.stateSlot
|
||||
|
||||
if PrevAttestations(state)[0].GetData().GetSlot() != tt.firstAttestationSlot {
|
||||
t.Errorf(
|
||||
"Result slot was an unexpected value. Wanted %d, got %d",
|
||||
tt.firstAttestationSlot,
|
||||
Attestations(state)[0].GetData().GetSlot(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrevJustifiedAttestations(t *testing.T) {
|
||||
prevEpochAttestations := []*pb.PendingAttestationRecord{
|
||||
{Data: &pb.AttestationData{JustifiedSlot: 0}},
|
||||
{Data: &pb.AttestationData{JustifiedSlot: 2}},
|
||||
{Data: &pb.AttestationData{JustifiedSlot: 5}},
|
||||
{Data: &pb.AttestationData{Shard: 2, JustifiedSlot: 100}},
|
||||
{Data: &pb.AttestationData{Shard: 3, JustifiedSlot: 100}},
|
||||
{Data: &pb.AttestationData{JustifiedSlot: 999}},
|
||||
}
|
||||
|
||||
thisEpochAttestations := []*pb.PendingAttestationRecord{
|
||||
{Data: &pb.AttestationData{JustifiedSlot: 0}},
|
||||
{Data: &pb.AttestationData{JustifiedSlot: 10}},
|
||||
{Data: &pb.AttestationData{JustifiedSlot: 15}},
|
||||
{Data: &pb.AttestationData{Shard: 0, JustifiedSlot: 100}},
|
||||
{Data: &pb.AttestationData{Shard: 1, JustifiedSlot: 100}},
|
||||
{Data: &pb.AttestationData{JustifiedSlot: 888}},
|
||||
}
|
||||
|
||||
state := &pb.BeaconState{PreviousJustifiedSlot: 100}
|
||||
|
||||
prevJustifiedAttestations := PrevJustifiedAttestations(state, thisEpochAttestations, prevEpochAttestations)
|
||||
|
||||
for i, attestation := range prevJustifiedAttestations {
|
||||
if attestation.GetData().Shard != uint64(i) {
|
||||
t.Errorf("Wanted shard %d, got %d", i, attestation.GetData().Shard)
|
||||
}
|
||||
if attestation.GetData().GetJustifiedSlot() != 100 {
|
||||
t.Errorf("Wanted justified slot 100, got %d", attestation.GetData().GetJustifiedSlot())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeadAttestations_Ok(t *testing.T) {
|
||||
if params.BeaconConfig().EpochLength != 64 {
|
||||
t.Errorf("EpochLength should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
prevAttestations := []*pb.PendingAttestationRecord{
|
||||
{Data: &pb.AttestationData{Slot: 1, BeaconBlockRootHash32: []byte{'A'}}},
|
||||
{Data: &pb.AttestationData{Slot: 2, BeaconBlockRootHash32: []byte{'B'}}},
|
||||
{Data: &pb.AttestationData{Slot: 3, BeaconBlockRootHash32: []byte{'C'}}},
|
||||
{Data: &pb.AttestationData{Slot: 4, BeaconBlockRootHash32: []byte{'D'}}},
|
||||
}
|
||||
|
||||
state := &pb.BeaconState{Slot: 5, LatestBlockRootHash32S: [][]byte{{'A'}, {'X'}, {'C'}, {'Y'}}}
|
||||
|
||||
headAttestations, err := PrevHeadAttestations(state, prevAttestations)
|
||||
if err != nil {
|
||||
t.Fatalf("PrevHeadAttestations failed with %v", err)
|
||||
}
|
||||
|
||||
if headAttestations[0].GetData().GetSlot() != 1 {
|
||||
t.Errorf("headAttestations[0] wanted slot 1, got slot %d", headAttestations[0].GetData().GetSlot())
|
||||
}
|
||||
if headAttestations[1].GetData().GetSlot() != 3 {
|
||||
t.Errorf("headAttestations[1] wanted slot 3, got slot %d", headAttestations[1].GetData().GetSlot())
|
||||
}
|
||||
if !bytes.Equal([]byte{'A'}, headAttestations[0].GetData().GetBeaconBlockRootHash32()) {
|
||||
t.Errorf("headAttestations[0] wanted hash [A], got slot %v",
|
||||
headAttestations[0].GetData().GetBeaconBlockRootHash32())
|
||||
}
|
||||
if !bytes.Equal([]byte{'C'}, headAttestations[1].GetData().GetBeaconBlockRootHash32()) {
|
||||
t.Errorf("headAttestations[1] wanted hash [C], got slot %v",
|
||||
headAttestations[1].GetData().GetBeaconBlockRootHash32())
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeadAttestations_NotOk(t *testing.T) {
|
||||
if params.BeaconConfig().EpochLength != 64 {
|
||||
t.Errorf("EpochLength should be 64 for these tests to pass")
|
||||
}
|
||||
|
||||
prevAttestations := []*pb.PendingAttestationRecord{{Data: &pb.AttestationData{Slot: 1}}}
|
||||
|
||||
state := &pb.BeaconState{Slot: 0}
|
||||
|
||||
if _, err := PrevHeadAttestations(state, prevAttestations); err == nil {
|
||||
t.Fatal("PrevHeadAttestations should have failed with invalid range")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ go_library(
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/mathutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/slices:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/bitutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/slices"
|
||||
)
|
||||
|
||||
const bitsInByte = 8
|
||||
@@ -421,6 +422,65 @@ func EffectiveBalance(validator *pb.ValidatorRecord) uint64 {
|
||||
return validator.Balance
|
||||
}
|
||||
|
||||
// Attesters returns the validator records using validator indices.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Let this_epoch_boundary_attesters = [state.validator_registry[i]
|
||||
// for indices in this_epoch_boundary_attester_indices for i in indices].
|
||||
func Attesters(state *pb.BeaconState, attesterIndices []uint32) []*pb.ValidatorRecord {
|
||||
|
||||
var boundaryAttesters []*pb.ValidatorRecord
|
||||
for _, attesterIndex := range attesterIndices {
|
||||
boundaryAttesters = append(boundaryAttesters, state.ValidatorRegistry[attesterIndex])
|
||||
}
|
||||
|
||||
return boundaryAttesters
|
||||
}
|
||||
|
||||
// ValidatorIndices returns all the validator indices from the input attestations
|
||||
// and state.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Let this_epoch_boundary_attester_indices be the union of the validator
|
||||
// index sets given by [get_attestation_participants(state, a.data, a.participation_bitfield)
|
||||
// for a in this_epoch_boundary_attestations]
|
||||
func ValidatorIndices(
|
||||
state *pb.BeaconState,
|
||||
boundaryAttestations []*pb.PendingAttestationRecord,
|
||||
) ([]uint32, error) {
|
||||
|
||||
var attesterIndicesIntersection []uint32
|
||||
for _, boundaryAttestation := range boundaryAttestations {
|
||||
attesterIndices, err := AttestationParticipants(
|
||||
state,
|
||||
boundaryAttestation.Data,
|
||||
boundaryAttestation.ParticipationBitfield)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attesterIndicesIntersection = slices.Union(attesterIndicesIntersection, attesterIndices)
|
||||
}
|
||||
|
||||
return attesterIndicesIntersection, nil
|
||||
}
|
||||
|
||||
// AttestingBalance returns the combined balances from the input validator
|
||||
// records.
|
||||
//
|
||||
// Spec pseudocode definition:
|
||||
// Let this_epoch_boundary_attesting_balance =
|
||||
// sum([get_effective_balance(v) for v in this_epoch_boundary_attesters])
|
||||
func AttestingBalance(boundaryAttesters []*pb.ValidatorRecord) uint64 {
|
||||
|
||||
var boundaryAttestingBalance uint64
|
||||
for _, boundaryAttester := range boundaryAttesters {
|
||||
boundaryAttestingBalance += EffectiveBalance(boundaryAttester)
|
||||
}
|
||||
|
||||
return boundaryAttestingBalance
|
||||
}
|
||||
|
||||
// minEmptyValidator returns the lowest validator index which the status is withdrawn.
|
||||
func minEmptyValidator(validators []*pb.ValidatorRecord) int {
|
||||
for i := 0; i < len(validators); i++ {
|
||||
|
||||
@@ -646,3 +646,82 @@ func TestGetActiveValidatorRecord(t *testing.T) {
|
||||
t.Errorf("Active validators don't match. Wanted: %v, Got: %v", outputValidators, validators)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoundaryAttestingBalance(t *testing.T) {
|
||||
attesters := []*pb.ValidatorRecord{
|
||||
{Balance: 25 * 1e9},
|
||||
{Balance: 26 * 1e9},
|
||||
{Balance: 32 * 1e9},
|
||||
{Balance: 33 * 1e9},
|
||||
{Balance: 100 * 1e9},
|
||||
}
|
||||
attestedBalances := AttestingBalance(attesters)
|
||||
|
||||
// 25 + 26 + 32 + 32 + 32 = 147
|
||||
if attestedBalances != 147*1e9 {
|
||||
t.Errorf("Incorrect attested balances. Wanted: %f, got: %d", 147*1e9, attestedBalances)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoundaryAttesters(t *testing.T) {
|
||||
var validators []*pb.ValidatorRecord
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
validators = append(validators, &pb.ValidatorRecord{Pubkey: []byte{byte(i)}})
|
||||
}
|
||||
|
||||
state := &pb.BeaconState{ValidatorRegistry: validators}
|
||||
|
||||
boundaryAttesters := Attesters(state, []uint32{5, 2, 87, 42, 99, 0})
|
||||
|
||||
expectedBoundaryAttesters := []*pb.ValidatorRecord{
|
||||
{Pubkey: []byte{byte(5)}},
|
||||
{Pubkey: []byte{byte(2)}},
|
||||
{Pubkey: []byte{byte(87)}},
|
||||
{Pubkey: []byte{byte(42)}},
|
||||
{Pubkey: []byte{byte(99)}},
|
||||
{Pubkey: []byte{byte(0)}},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedBoundaryAttesters, boundaryAttesters) {
|
||||
t.Errorf("Incorrect boundary attesters. Wanted: %v, got: %v", expectedBoundaryAttesters, boundaryAttesters)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoundaryAttesterIndices(t *testing.T) {
|
||||
if params.BeaconConfig().EpochLength != 64 {
|
||||
t.Errorf("EpochLength should be 64 for these tests to pass")
|
||||
}
|
||||
var committeeIndices []uint32
|
||||
for i := uint32(0); i < 8; i++ {
|
||||
committeeIndices = append(committeeIndices, i)
|
||||
}
|
||||
var shardAndCommittees []*pb.ShardAndCommitteeArray
|
||||
for i := uint64(0); i < params.BeaconConfig().EpochLength*2; i++ {
|
||||
shardAndCommittees = append(shardAndCommittees, &pb.ShardAndCommitteeArray{
|
||||
ArrayShardAndCommittee: []*pb.ShardAndCommittee{
|
||||
{Shard: 100, Committee: committeeIndices},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
state := &pb.BeaconState{
|
||||
ShardAndCommitteesAtSlots: shardAndCommittees,
|
||||
Slot: 5,
|
||||
}
|
||||
|
||||
boundaryAttestations := []*pb.PendingAttestationRecord{
|
||||
{Data: &pb.AttestationData{Slot: 2, Shard: 100}, ParticipationBitfield: []byte{'F'}}, // returns indices 1,5,6
|
||||
{Data: &pb.AttestationData{Slot: 2, Shard: 100}, ParticipationBitfield: []byte{3}}, // returns indices 6,7
|
||||
{Data: &pb.AttestationData{Slot: 2, Shard: 100}, ParticipationBitfield: []byte{'A'}}, // returns indices 1,7
|
||||
}
|
||||
|
||||
attesterIndices, err := ValidatorIndices(state, boundaryAttestations)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to run BoundaryAttesterIndices: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(attesterIndices, []uint32{1, 5, 6, 7}) {
|
||||
t.Errorf("Incorrect boundary attester indices. Wanted: %v, got: %v", []uint32{1, 5, 6, 7}, attesterIndices)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,3 +18,23 @@ func Intersection(a []uint32, b []uint32) []uint32 {
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// Union of two uint32 slices with time
|
||||
// complexity of approximately O(n) leveraging a map to
|
||||
// check for element existence off by a constant factor
|
||||
// of underlying map efficiency.
|
||||
func Union(a []uint32, b []uint32) []uint32 {
|
||||
set := make([]uint32, 0)
|
||||
m := make(map[uint32]bool)
|
||||
|
||||
for i := 0; i < len(a); i++ {
|
||||
m[a[i]] = true
|
||||
set = append(set, a[i])
|
||||
}
|
||||
for i := 0; i < len(b); i++ {
|
||||
if _, found := m[b[i]]; !found {
|
||||
set = append(set, b[i])
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
@@ -27,3 +27,25 @@ func TestIntersection(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnion(t *testing.T) {
|
||||
testCases := []struct {
|
||||
setA []uint32
|
||||
setB []uint32
|
||||
out []uint32
|
||||
}{
|
||||
{[]uint32{2, 3, 5}, []uint32{4, 6}, []uint32{2, 3, 5, 4, 6}},
|
||||
{[]uint32{2, 3, 5}, []uint32{3, 5}, []uint32{2, 3, 5}},
|
||||
{[]uint32{2, 3, 5}, []uint32{2, 3, 5}, []uint32{2, 3, 5}},
|
||||
{[]uint32{2, 3, 5}, []uint32{}, []uint32{2, 3, 5}},
|
||||
{[]uint32{}, []uint32{2, 3, 5}, []uint32{2, 3, 5}},
|
||||
{[]uint32{}, []uint32{}, []uint32{}},
|
||||
{[]uint32{1}, []uint32{1}, []uint32{1}},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
result := Union(tt.setA, tt.setB)
|
||||
if !reflect.DeepEqual(result, tt.out) {
|
||||
t.Errorf("got %d, want %d", result, tt.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user