Skip to content
Snippets Groups Projects
path_attributes_test.go 36.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		input       *PathAttribute
    		expected    []byte
    		expectedLen uint8
    	}{
    		{
    			name: "Test #1",
    			input: &PathAttribute{
    				TypeCode: NextHopAttr,
    
    				Value:    strAddr("100.110.120.130"),
    
    			expected:    []byte{64, 3, 4, 100, 110, 120, 130},
    
    			expectedLen: 7,
    		},
    	}
    
    	for _, test := range tests {
    		buf := bytes.NewBuffer(nil)
    		n := test.input.serializeNextHop(buf)
    		if n != test.expectedLen {
    			t.Errorf("Unexpected length for test %q: %d", test.name, n)
    			continue
    		}
    
    		assert.Equal(t, test.expected, buf.Bytes())
    	}
    }
    
    func TestSerializeMED(t *testing.T) {
    	tests := []struct {
    		name        string
    		input       *PathAttribute
    		expected    []byte
    		expectedLen uint8
    	}{
    		{
    			name: "Test #1",
    			input: &PathAttribute{
    				TypeCode: MEDAttr,
    				Value:    uint32(1000),
    			},
    			expected: []byte{
    				128,          // Attribute flags
    				4,            // Type
    				4,            // Length
    				0, 0, 3, 232, // Value = 1000
    			},
    			expectedLen: 7,
    		},
    	}
    
    	for _, test := range tests {
    		buf := bytes.NewBuffer(nil)
    		n := test.input.serializeMED(buf)
    		if n != test.expectedLen {
    			t.Errorf("Unexpected length for test %q: %d", test.name, n)
    			continue
    		}
    
    		assert.Equal(t, test.expected, buf.Bytes())
    	}
    }
    
    func TestSerializeLocalPref(t *testing.T) {
    	tests := []struct {
    		name        string
    		input       *PathAttribute
    		expected    []byte
    		expectedLen uint8
    	}{
    		{
    			name: "Test #1",
    			input: &PathAttribute{
    				TypeCode: LocalPrefAttr,
    				Value:    uint32(1000),
    			},
    			expected: []byte{
    				64,           // Attribute flags
    				5,            // Type
    				4,            // Length
    				0, 0, 3, 232, // Value = 1000
    			},
    			expectedLen: 7,
    		},
    	}
    
    	for _, test := range tests {
    		buf := bytes.NewBuffer(nil)
    		n := test.input.serializeLocalpref(buf)
    		if n != test.expectedLen {
    			t.Errorf("Unexpected length for test %q: %d", test.name, n)
    			continue
    		}
    
    		assert.Equal(t, test.expected, buf.Bytes())
    	}
    }
    
    func TestSerializeAtomicAggregate(t *testing.T) {
    	tests := []struct {
    		name        string
    		input       *PathAttribute
    		expected    []byte
    		expectedLen uint8
    	}{
    		{
    			name: "Test #1",
    			input: &PathAttribute{
    				TypeCode: AtomicAggrAttr,
    			},
    			expected: []byte{
    				64, // Attribute flags
    				6,  // Type
    				0,  // Length
    			},
    			expectedLen: 3,
    		},
    	}
    
    	for _, test := range tests {
    		buf := bytes.NewBuffer(nil)
    		n := test.input.serializeAtomicAggregate(buf)
    		if n != test.expectedLen {
    			t.Errorf("Unexpected length for test %q: %d", test.name, n)
    			continue
    		}
    
    		assert.Equal(t, test.expected, buf.Bytes())
    	}
    }
    
    func TestSerializeAggregator(t *testing.T) {
    	tests := []struct {
    		name        string
    		input       *PathAttribute
    		expected    []byte
    		expectedLen uint8
    	}{
    		{
    			name: "Test #1",
    			input: &PathAttribute{
    				TypeCode: AggregatorAttr,
    				Value:    uint16(174),
    			},
    			expected: []byte{
    				192,    // Attribute flags
    				7,      // Type
    				2,      // Length
    				0, 174, // Value = 174
    			},
    			expectedLen: 5,
    		},
    	}
    
    	for _, test := range tests {
    		buf := bytes.NewBuffer(nil)
    		n := test.input.serializeAggregator(buf)
    		if n != test.expectedLen {
    			t.Errorf("Unexpected length for test %q: %d", test.name, n)
    			continue
    		}
    
    		assert.Equal(t, test.expected, buf.Bytes())
    	}
    }
    
    func TestSerializeASPath(t *testing.T) {
    	tests := []struct {
    		name        string
    		input       *PathAttribute
    		expected    []byte
    		expectedLen uint8
    
    		use32BitASN bool
    
    	}{
    		{
    			name: "Test #1",
    			input: &PathAttribute{
    				TypeCode: ASPathAttr,
    				Value: ASPath{
    					{
    						Type: 2, // Sequence
    						ASNs: []uint32{
    							100, 200, 210,
    						},
    					},
    				},
    			},
    			expected: []byte{
    				64,     // Attribute flags
    				2,      // Type
    
    Oliver Herms's avatar
    Oliver Herms committed
    				8,      // Length
    
    				2,      // AS_SEQUENCE
    				3,      // ASN count
    				0, 100, // ASN 100
    				0, 200, // ASN 200
    				0, 210, // ASN 210
    			},
    			expectedLen: 10,
    		},
    
    		{
    			name: "32bit ASN",
    			input: &PathAttribute{
    				TypeCode: ASPathAttr,
    				Value: ASPath{
    					{
    						Type: 2, // Sequence
    						ASNs: []uint32{
    							100, 200, 210,
    						},
    					},
    				},
    			},
    			expected: []byte{
    				64,           // Attribute flags
    				2,            // Type
    				14,           // Length
    				2,            // AS_SEQUENCE
    				3,            // ASN count
    				0, 0, 0, 100, // ASN 100
    				0, 0, 0, 200, // ASN 200
    				0, 0, 0, 210, // ASN 210
    			},
    			expectedLen: 16,
    			use32BitASN: true,
    		},
    
    	t.Parallel()
    
    
    	for _, test := range tests {
    
    		t.Run(test.name, func(t *testing.T) {
    			buf := bytes.NewBuffer(nil)
    
    				Supports4OctetASN: test.use32BitASN,
    			}
    			n := test.input.serializeASPath(buf, opt)
    			if n != test.expectedLen {
    				t.Fatalf("Unexpected length for test %q: %d", test.name, n)
    			}
    
    			assert.Equal(t, test.expected, buf.Bytes())
    		})
    
    func TestSerializeLargeCommunities(t *testing.T) {
    	tests := []struct {
    		name        string
    		input       *PathAttribute
    		expected    []byte
    		expectedLen uint8
    	}{
    		{
    			name: "2 large communities",
    			input: &PathAttribute{
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    				TypeCode: LargeCommunitiesAttr,
    
    				Value: []LargeCommunity{
    					{
    						GlobalAdministrator: 1,
    						DataPart1:           2,
    						DataPart2:           3,
    					},
    					{
    						GlobalAdministrator: 4,
    						DataPart1:           5,
    						DataPart2:           6,
    					},
    				},
    			},
    			expected: []byte{
    				0xe0,                                                                   // Attribute flags
    				32,                                                                     // Type
    				24,                                                                     // Length
    				0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, // Communities (1, 2, 3), (4, 5, 6)
    			},
    			expectedLen: 24,
    		},
    		{
    			name: "empty list of communities",
    			input: &PathAttribute{
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    				TypeCode: LargeCommunitiesAttr,
    
    				Value:    []LargeCommunity{},
    			},
    			expected:    []byte{},
    			expectedLen: 0,
    		},
    	}
    
    	for _, test := range tests {
    		t.Run(test.name, func(te *testing.T) {
    			buf := bytes.NewBuffer([]byte{})
    			n := test.input.serializeLargeCommunities(buf)
    			if n != test.expectedLen {
    				t.Fatalf("Unexpected length for test %q: %d", test.name, n)
    			}
    
    			assert.Equal(t, test.expected, buf.Bytes())
    		})
    	}
    }
    
    
    func TestSerializeCommunities(t *testing.T) {
    	tests := []struct {
    		name        string
    		input       *PathAttribute
    		expected    []byte
    		expectedLen uint8
    	}{
    		{
    			name: "2 communities",
    			input: &PathAttribute{
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    				TypeCode: LargeCommunitiesAttr,
    
    				Value: []uint32{
    					131080, 16778241,
    				},
    			},
    			expected: []byte{
    				0xe0,                   // Attribute flags
    				8,                      // Type
    				8,                      // Length
    				0, 2, 0, 8, 1, 0, 4, 1, // Communities (2,8), (256,1025)
    			},
    			expectedLen: 8,
    		},
    		{
    			name: "empty list of communities",
    			input: &PathAttribute{
    				TypeCode: CommunitiesAttr,
    				Value:    []uint32{},
    			},
    			expected:    []byte{},
    			expectedLen: 0,
    		},
    	}
    
    	for _, test := range tests {
    		t.Run(test.name, func(te *testing.T) {
    			buf := bytes.NewBuffer([]byte{})
    			n := test.input.serializeCommunities(buf)
    			if n != test.expectedLen {
    				t.Fatalf("Unexpected length for test %q: %d", test.name, n)
    			}
    
    			assert.Equal(t, test.expected, buf.Bytes())
    		})
    	}
    }
    
    
    func TestSerializeUnknownAttribute(t *testing.T) {
    	tests := []struct {
    		name        string
    		input       *PathAttribute
    		expected    []byte
    		expectedLen uint8
    	}{
    		{
    			name: "Arbritary attribute",
    			input: &PathAttribute{
    				TypeCode:   200,
    				Value:      []byte{1, 2, 3, 4},
    				Transitive: true,
    			},
    			expected: []byte{
    				64,         // Attribute flags
    				200,        // Type
    				4,          // Length
    				1, 2, 3, 4, // Payload
    			},
    			expectedLen: 6,
    		},
    	}
    
    	for _, test := range tests {
    		t.Run(test.name, func(t *testing.T) {
    			buf := bytes.NewBuffer(nil)
    			n := test.input.serializeUnknownAttribute(buf)
    
    			assert.Equal(t, test.expectedLen, n)
    			assert.Equal(t, test.expected, buf.Bytes())
    		})
    	}
    }
    
    
    func TestSerialize(t *testing.T) {
    	tests := []struct {
    		name     string
    		msg      *BGPUpdate
    		expected []byte
    		wantFail bool
    	}{
    		{
    			name: "Withdraw only",
    			msg: &BGPUpdate{
    				WithdrawnRoutes: &NLRI{
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    					IP:     strAddr("100.110.120.0"),
    
    					Pfxlen: 24,
    				},
    			},
    			expected: []byte{
    				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    				0, 27, // Length
    				2,    // Msg Type
    				0, 4, // Withdrawn Routes Length
    				24, 100, 110, 120, // NLRI
    				0, 0, // Total Path Attribute Length
    			},
    		},
    		{
    			name: "NLRI only",
    			msg: &BGPUpdate{
    				NLRI: &NLRI{
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    					IP:     strAddr("100.110.128.0"),
    
    					Pfxlen: 17,
    				},
    			},
    			expected: []byte{
    				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    				0, 27, // Length
    				2,    // Msg Type
    				0, 0, // Withdrawn Routes Length
    				0, 0, // Total Path Attribute Length
    				17, 100, 110, 128, // NLRI
    			},
    		},
    		{
    			name: "Path Attributes only",
    			msg: &BGPUpdate{
    				PathAttributes: &PathAttribute{
    					Optional:   true,
    					Transitive: true,
    					TypeCode:   OriginAttr,
    					Value:      uint8(0), // IGP
    				},
    			},
    			expected: []byte{
    				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    				0, 27, // Length
    				2,    // Msg Type
    				0, 0, // Withdrawn Routes Length
    				0, 4, // Total Path Attribute Length
    				64, // Attr. Flags
    				1,  // Attr. Type Code
    				1,  // Length
    				0,  // Value
    			},
    		},
    		{
    			name: "Full test",
    			msg: &BGPUpdate{
    				WithdrawnRoutes: &NLRI{
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    					IP:     strAddr("10.0.0.0"),
    
    					Pfxlen: 8,
    					Next: &NLRI{
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    						IP:     strAddr("192.168.0.0"),
    
    						Pfxlen: 16,
    					},
    				},
    				PathAttributes: &PathAttribute{
    					TypeCode: OriginAttr,
    					Value:    uint8(0),
    					Next: &PathAttribute{
    						TypeCode: ASPathAttr,
    						Value: ASPath{
    							{
    								Type: 2,
    								ASNs: []uint32{100, 155, 200},
    							},
    							{
    								Type: 1,
    								ASNs: []uint32{10, 20},
    							},
    						},
    						Next: &PathAttribute{
    							TypeCode: NextHopAttr,
    
    							Value:    strAddr("10.20.30.40"),
    
    							Next: &PathAttribute{
    								TypeCode: MEDAttr,
    								Value:    uint32(100),
    								Next: &PathAttribute{
    									TypeCode: LocalPrefAttr,
    									Value:    uint32(500),
    									Next: &PathAttribute{
    										TypeCode: AtomicAggrAttr,
    										Next: &PathAttribute{
    											TypeCode: AggregatorAttr,
    											Value:    uint16(200),
    										},
    									},
    								},
    							},
    						},
    					},
    				},
    				NLRI: &NLRI{
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    					IP:     strAddr("8.8.8.0"),
    
    					Pfxlen: 24,
    					Next: &NLRI{
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    						IP:     strAddr("185.65.240.0"),
    
    						Pfxlen: 22,
    					},
    				},
    			},
    			expected: []byte{
    				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    
    				2, // Msg Type
    
    				// Withdraws
    				0, 5, // Withdrawn Routes Length
    				8, 10, // Withdraw 10/8
    				16, 192, 168, // Withdraw 192.168/16
    
    
    				0, 50, // Total Path Attribute Length
    
    
    				// ORIGIN
    				64, // Attr. Flags
    				1,  // Attr. Type Code
    				1,  // Length
    				0,  // Value
    				// ASPath
    				64,                     // Attr. Flags
    				2,                      // Attr. Type Code
    
    				2,                      // Path Segment Type = AS_SEQUENCE
    				3,                      // Path Segment Length
    				0, 100, 0, 155, 0, 200, // ASNs
    				1,            // Path Segment Type = AS_SET
    				2,            // Path Segment Type = AS_SET
    				0, 10, 0, 20, // ASNs
    				// Next Hop
    				64,             // Attr. Flags
    				3,              // Attr. Type Code
    				4,              // Length
    				10, 20, 30, 40, // Next Hop Address
    				// MED
    				128,          // Attr. Flags
    				4,            // Attr Type Code
    				4,            // Length
    				0, 0, 0, 100, // MED = 100
    				// LocalPref
    				64,           // Attr. Flags
    				5,            // Attr. Type Code
    				4,            // Length
    				0, 0, 1, 244, // Localpref
    				// Atomic Aggregate
    				64, // Attr. Flags
    				6,  // Attr. Type Code
    				0,  // Length
    				// Aggregator
    				192,    // Attr. Flags
    				7,      // Attr. Type Code
    				2,      // Length
    				0, 200, // Aggregator ASN = 200
    
    				// NLRI
    				24, 8, 8, 8, // 8.8.8.0/24
    				22, 185, 65, 240, // 185.65.240.0/22
    			},
    		},
    	}
    
    	for _, test := range tests {
    
    		opt := &Options{}
    
    		res, err := test.msg.SerializeUpdate(opt)
    
    		if err != nil {
    			if test.wantFail {
    				continue
    			}
    
    			t.Errorf("Unexpected failure for test %q: %v", test.name, err)
    			continue
    		}
    
    		if test.wantFail {
    			t.Errorf("Unexpected success for test %q", test.name)
    			continue
    		}
    
    		assert.Equalf(t, test.expected, res, "%s", test.name)
    	}
    }
    
    func TestSerializeAddPath(t *testing.T) {
    	tests := []struct {
    		name     string
    		msg      *BGPUpdateAddPath
    		expected []byte
    		wantFail bool
    	}{
    		{
    			name: "Withdraw only",
    			msg: &BGPUpdateAddPath{
    				WithdrawnRoutes: &NLRIAddPath{
    					PathIdentifier: 257,
    					IP:             strAddr("100.110.120.0"),
    					Pfxlen:         24,
    				},
    			},
    			expected: []byte{
    				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    				0, 31, // Length
    				2,    // Msg Type
    				0, 8, // Withdrawn Routes Length
    				0, 0, 1, 1, // Path Identifier
    				24, 100, 110, 120, // NLRI
    				0, 0, // Total Path Attribute Length
    			},
    		},
    		{
    			name: "NLRI only",
    			msg: &BGPUpdateAddPath{
    				NLRI: &NLRIAddPath{
    					PathIdentifier: 257,
    					IP:             strAddr("100.110.128.0"),
    					Pfxlen:         17,
    				},
    			},
    			expected: []byte{
    				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    				0, 31, // Length
    				2,    // Msg Type
    				0, 0, // Withdrawn Routes Length
    				0, 0, // Total Path Attribute Length
    				0, 0, 1, 1, // Path Identifier
    				17, 100, 110, 128, // NLRI
    			},
    		},
    		{
    			name: "Path Attributes only",
    			msg: &BGPUpdateAddPath{
    				PathAttributes: &PathAttribute{
    					Optional:   true,
    					Transitive: true,
    					TypeCode:   OriginAttr,
    					Value:      uint8(0), // IGP
    				},
    			},
    			expected: []byte{
    				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    				0, 27, // Length
    				2,    // Msg Type
    				0, 0, // Withdrawn Routes Length
    				0, 4, // Total Path Attribute Length
    				64, // Attr. Flags
    				1,  // Attr. Type Code
    				1,  // Length
    				0,  // Value
    			},
    		},
    		{
    			name: "Full test",
    			msg: &BGPUpdateAddPath{
    				WithdrawnRoutes: &NLRIAddPath{
    					IP:     strAddr("10.0.0.0"),
    					Pfxlen: 8,
    					Next: &NLRIAddPath{
    						IP:     strAddr("192.168.0.0"),
    						Pfxlen: 16,
    					},
    				},
    				PathAttributes: &PathAttribute{
    					TypeCode: OriginAttr,
    					Value:    uint8(0),
    					Next: &PathAttribute{
    						TypeCode: ASPathAttr,
    						Value: ASPath{
    							{
    								Type: 2,
    								ASNs: []uint32{100, 155, 200},
    							},
    							{
    								Type: 1,
    								ASNs: []uint32{10, 20},
    							},
    						},
    						Next: &PathAttribute{
    							TypeCode: NextHopAttr,
    							Value:    strAddr("10.20.30.40"),
    							Next: &PathAttribute{
    								TypeCode: MEDAttr,
    								Value:    uint32(100),
    								Next: &PathAttribute{
    									TypeCode: LocalPrefAttr,
    									Value:    uint32(500),
    									Next: &PathAttribute{
    										TypeCode: AtomicAggrAttr,
    										Next: &PathAttribute{
    											TypeCode: AggregatorAttr,
    											Value:    uint16(200),
    										},
    									},
    								},
    							},
    						},
    					},
    				},
    				NLRI: &NLRIAddPath{
    					IP:     strAddr("8.8.8.0"),
    					Pfxlen: 24,
    					Next: &NLRIAddPath{
    						IP:     strAddr("185.65.240.0"),
    						Pfxlen: 22,
    					},
    				},
    			},
    			expected: []byte{
    				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    				0, 102, // Length
    				2, // Msg Type
    
    				// Withdraws
    				0, 13, // Withdrawn Routes Length
    				0, 0, 0, 0, // Path Identifier
    				8, 10, // Withdraw 10/8
    				0, 0, 0, 0, // Path Identifier
    				16, 192, 168, // Withdraw 192.168/16
    
    				0, 50, // Total Path Attribute Length
    
    				// ORIGIN
    				64, // Attr. Flags
    				1,  // Attr. Type Code
    				1,  // Length
    				0,  // Value
    				// ASPath
    				64,                     // Attr. Flags
    				2,                      // Attr. Type Code
    				14,                     // Attr. Length
    				2,                      // Path Segment Type = AS_SEQUENCE
    				3,                      // Path Segment Length
    				0, 100, 0, 155, 0, 200, // ASNs
    				1,            // Path Segment Type = AS_SET
    				2,            // Path Segment Type = AS_SET
    				0, 10, 0, 20, // ASNs
    				// Next Hop
    				64,             // Attr. Flags
    				3,              // Attr. Type Code
    				4,              // Length
    				10, 20, 30, 40, // Next Hop Address
    				// MED
    				128,          // Attr. Flags
    				4,            // Attr Type Code
    				4,            // Length
    				0, 0, 0, 100, // MED = 100
    				// LocalPref
    				64,           // Attr. Flags
    				5,            // Attr. Type Code
    				4,            // Length
    				0, 0, 1, 244, // Localpref
    				// Atomic Aggregate
    				64, // Attr. Flags
    				6,  // Attr. Type Code
    				0,  // Length
    				// Aggregator
    				192,    // Attr. Flags
    				7,      // Attr. Type Code
    				2,      // Length
    				0, 200, // Aggregator ASN = 200
    
    				// NLRI
    				0, 0, 0, 0, // Path Identifier
    				24, 8, 8, 8, // 8.8.8.0/24
    				0, 0, 0, 0, // Path Identifier
    				22, 185, 65, 240, // 185.65.240.0/22
    			},
    		},
    	}
    
    	for _, test := range tests {
    
    		opt := &Options{}
    
    		res, err := test.msg.SerializeUpdate(opt)
    
    		if err != nil {
    			if test.wantFail {
    				continue
    			}
    
    			t.Errorf("Unexpected failure for test %q: %v", test.name, err)
    			continue
    		}
    
    		if test.wantFail {
    			t.Errorf("Unexpected success for test %q", test.name)
    			continue
    		}
    
    
    		assert.Equalf(t, test.expected, res, "%s", test.name)
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    func TestFourBytesToUint32(t *testing.T) {
    	tests := []struct {
    		name     string
    		input    [4]byte
    		expected uint32
    	}{
    		{
    			name:     "Test #1",
    			input:    [4]byte{0, 0, 0, 200},
    			expected: 200,
    		},
    		{
    			name:     "Test #2",
    			input:    [4]byte{1, 0, 0, 200},
    			expected: 16777416,
    		},
    	}
    
    	for _, test := range tests {
    		res := fourBytesToUint32(test.input)
    		if res != test.expected {
    			t.Errorf("Unexpected result for test %q: Got: %d Want: %d", test.name, res, test.expected)
    		}
    	}
    }