golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/http2/frame_test.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package http2
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"io"
    11  	"reflect"
    12  	"strings"
    13  	"testing"
    14  	"unsafe"
    15  
    16  	"golang.org/x/net/http2/hpack"
    17  )
    18  
    19  func testFramer() (*Framer, *bytes.Buffer) {
    20  	buf := new(bytes.Buffer)
    21  	return NewFramer(buf, buf), buf
    22  }
    23  
    24  func TestFrameSizes(t *testing.T) {
    25  	// Catch people rearranging the FrameHeader fields.
    26  	if got, want := int(unsafe.Sizeof(FrameHeader{})), 12; got != want {
    27  		t.Errorf("FrameHeader size = %d; want %d", got, want)
    28  	}
    29  }
    30  
    31  func TestFrameTypeString(t *testing.T) {
    32  	tests := []struct {
    33  		ft   FrameType
    34  		want string
    35  	}{
    36  		{FrameData, "DATA"},
    37  		{FramePing, "PING"},
    38  		{FrameGoAway, "GOAWAY"},
    39  		{0xf, "UNKNOWN_FRAME_TYPE_15"},
    40  	}
    41  
    42  	for i, tt := range tests {
    43  		got := tt.ft.String()
    44  		if got != tt.want {
    45  			t.Errorf("%d. String(FrameType %d) = %q; want %q", i, int(tt.ft), got, tt.want)
    46  		}
    47  	}
    48  }
    49  
    50  func TestWriteRST(t *testing.T) {
    51  	fr, buf := testFramer()
    52  	var streamID uint32 = 1<<24 + 2<<16 + 3<<8 + 4
    53  	var errCode uint32 = 7<<24 + 6<<16 + 5<<8 + 4
    54  	fr.WriteRSTStream(streamID, ErrCode(errCode))
    55  	const wantEnc = "\x00\x00\x04\x03\x00\x01\x02\x03\x04\x07\x06\x05\x04"
    56  	if buf.String() != wantEnc {
    57  		t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc)
    58  	}
    59  	f, err := fr.ReadFrame()
    60  	if err != nil {
    61  		t.Fatal(err)
    62  	}
    63  	want := &RSTStreamFrame{
    64  		FrameHeader: FrameHeader{
    65  			valid:    true,
    66  			Type:     0x3,
    67  			Flags:    0x0,
    68  			Length:   0x4,
    69  			StreamID: 0x1020304,
    70  		},
    71  		ErrCode: 0x7060504,
    72  	}
    73  	if !reflect.DeepEqual(f, want) {
    74  		t.Errorf("parsed back %#v; want %#v", f, want)
    75  	}
    76  }
    77  
    78  func TestWriteData(t *testing.T) {
    79  	fr, buf := testFramer()
    80  	var streamID uint32 = 1<<24 + 2<<16 + 3<<8 + 4
    81  	data := []byte("ABC")
    82  	fr.WriteData(streamID, true, data)
    83  	const wantEnc = "\x00\x00\x03\x00\x01\x01\x02\x03\x04ABC"
    84  	if buf.String() != wantEnc {
    85  		t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc)
    86  	}
    87  	f, err := fr.ReadFrame()
    88  	if err != nil {
    89  		t.Fatal(err)
    90  	}
    91  	df, ok := f.(*DataFrame)
    92  	if !ok {
    93  		t.Fatalf("got %T; want *DataFrame", f)
    94  	}
    95  	if !bytes.Equal(df.Data(), data) {
    96  		t.Errorf("got %q; want %q", df.Data(), data)
    97  	}
    98  	if f.Header().Flags&1 == 0 {
    99  		t.Errorf("didn't see END_STREAM flag")
   100  	}
   101  }
   102  
   103  func TestWriteDataPadded(t *testing.T) {
   104  	tests := [...]struct {
   105  		streamID   uint32
   106  		endStream  bool
   107  		data       []byte
   108  		pad        []byte
   109  		wantHeader FrameHeader
   110  	}{
   111  		// Unpadded:
   112  		0: {
   113  			streamID:  1,
   114  			endStream: true,
   115  			data:      []byte("foo"),
   116  			pad:       nil,
   117  			wantHeader: FrameHeader{
   118  				Type:     FrameData,
   119  				Flags:    FlagDataEndStream,
   120  				Length:   3,
   121  				StreamID: 1,
   122  			},
   123  		},
   124  
   125  		// Padded bit set, but no padding:
   126  		1: {
   127  			streamID:  1,
   128  			endStream: true,
   129  			data:      []byte("foo"),
   130  			pad:       []byte{},
   131  			wantHeader: FrameHeader{
   132  				Type:     FrameData,
   133  				Flags:    FlagDataEndStream | FlagDataPadded,
   134  				Length:   4,
   135  				StreamID: 1,
   136  			},
   137  		},
   138  
   139  		// Padded bit set, with padding:
   140  		2: {
   141  			streamID:  1,
   142  			endStream: false,
   143  			data:      []byte("foo"),
   144  			pad:       []byte{0, 0, 0},
   145  			wantHeader: FrameHeader{
   146  				Type:     FrameData,
   147  				Flags:    FlagDataPadded,
   148  				Length:   7,
   149  				StreamID: 1,
   150  			},
   151  		},
   152  	}
   153  	for i, tt := range tests {
   154  		fr, _ := testFramer()
   155  		fr.WriteDataPadded(tt.streamID, tt.endStream, tt.data, tt.pad)
   156  		f, err := fr.ReadFrame()
   157  		if err != nil {
   158  			t.Errorf("%d. ReadFrame: %v", i, err)
   159  			continue
   160  		}
   161  		got := f.Header()
   162  		tt.wantHeader.valid = true
   163  		if !got.Equal(tt.wantHeader) {
   164  			t.Errorf("%d. read %+v; want %+v", i, got, tt.wantHeader)
   165  			continue
   166  		}
   167  		df := f.(*DataFrame)
   168  		if !bytes.Equal(df.Data(), tt.data) {
   169  			t.Errorf("%d. got %q; want %q", i, df.Data(), tt.data)
   170  		}
   171  	}
   172  }
   173  
   174  func (fh FrameHeader) Equal(b FrameHeader) bool {
   175  	return fh.valid == b.valid &&
   176  		fh.Type == b.Type &&
   177  		fh.Flags == b.Flags &&
   178  		fh.Length == b.Length &&
   179  		fh.StreamID == b.StreamID
   180  }
   181  
   182  func TestWriteHeaders(t *testing.T) {
   183  	tests := []struct {
   184  		name      string
   185  		p         HeadersFrameParam
   186  		wantEnc   string
   187  		wantFrame *HeadersFrame
   188  	}{
   189  		{
   190  			"basic",
   191  			HeadersFrameParam{
   192  				StreamID:      42,
   193  				BlockFragment: []byte("abc"),
   194  				Priority:      PriorityParam{},
   195  			},
   196  			"\x00\x00\x03\x01\x00\x00\x00\x00*abc",
   197  			&HeadersFrame{
   198  				FrameHeader: FrameHeader{
   199  					valid:    true,
   200  					StreamID: 42,
   201  					Type:     FrameHeaders,
   202  					Length:   uint32(len("abc")),
   203  				},
   204  				Priority:      PriorityParam{},
   205  				headerFragBuf: []byte("abc"),
   206  			},
   207  		},
   208  		{
   209  			"basic + end flags",
   210  			HeadersFrameParam{
   211  				StreamID:      42,
   212  				BlockFragment: []byte("abc"),
   213  				EndStream:     true,
   214  				EndHeaders:    true,
   215  				Priority:      PriorityParam{},
   216  			},
   217  			"\x00\x00\x03\x01\x05\x00\x00\x00*abc",
   218  			&HeadersFrame{
   219  				FrameHeader: FrameHeader{
   220  					valid:    true,
   221  					StreamID: 42,
   222  					Type:     FrameHeaders,
   223  					Flags:    FlagHeadersEndStream | FlagHeadersEndHeaders,
   224  					Length:   uint32(len("abc")),
   225  				},
   226  				Priority:      PriorityParam{},
   227  				headerFragBuf: []byte("abc"),
   228  			},
   229  		},
   230  		{
   231  			"with padding",
   232  			HeadersFrameParam{
   233  				StreamID:      42,
   234  				BlockFragment: []byte("abc"),
   235  				EndStream:     true,
   236  				EndHeaders:    true,
   237  				PadLength:     5,
   238  				Priority:      PriorityParam{},
   239  			},
   240  			"\x00\x00\t\x01\r\x00\x00\x00*\x05abc\x00\x00\x00\x00\x00",
   241  			&HeadersFrame{
   242  				FrameHeader: FrameHeader{
   243  					valid:    true,
   244  					StreamID: 42,
   245  					Type:     FrameHeaders,
   246  					Flags:    FlagHeadersEndStream | FlagHeadersEndHeaders | FlagHeadersPadded,
   247  					Length:   uint32(1 + len("abc") + 5), // pad length + contents + padding
   248  				},
   249  				Priority:      PriorityParam{},
   250  				headerFragBuf: []byte("abc"),
   251  			},
   252  		},
   253  		{
   254  			"with priority",
   255  			HeadersFrameParam{
   256  				StreamID:      42,
   257  				BlockFragment: []byte("abc"),
   258  				EndStream:     true,
   259  				EndHeaders:    true,
   260  				PadLength:     2,
   261  				Priority: PriorityParam{
   262  					StreamDep: 15,
   263  					Exclusive: true,
   264  					Weight:    127,
   265  				},
   266  			},
   267  			"\x00\x00\v\x01-\x00\x00\x00*\x02\x80\x00\x00\x0f\u007fabc\x00\x00",
   268  			&HeadersFrame{
   269  				FrameHeader: FrameHeader{
   270  					valid:    true,
   271  					StreamID: 42,
   272  					Type:     FrameHeaders,
   273  					Flags:    FlagHeadersEndStream | FlagHeadersEndHeaders | FlagHeadersPadded | FlagHeadersPriority,
   274  					Length:   uint32(1 + 5 + len("abc") + 2), // pad length + priority + contents + padding
   275  				},
   276  				Priority: PriorityParam{
   277  					StreamDep: 15,
   278  					Exclusive: true,
   279  					Weight:    127,
   280  				},
   281  				headerFragBuf: []byte("abc"),
   282  			},
   283  		},
   284  		{
   285  			"with priority stream dep zero", // golang.org/issue/15444
   286  			HeadersFrameParam{
   287  				StreamID:      42,
   288  				BlockFragment: []byte("abc"),
   289  				EndStream:     true,
   290  				EndHeaders:    true,
   291  				PadLength:     2,
   292  				Priority: PriorityParam{
   293  					StreamDep: 0,
   294  					Exclusive: true,
   295  					Weight:    127,
   296  				},
   297  			},
   298  			"\x00\x00\v\x01-\x00\x00\x00*\x02\x80\x00\x00\x00\u007fabc\x00\x00",
   299  			&HeadersFrame{
   300  				FrameHeader: FrameHeader{
   301  					valid:    true,
   302  					StreamID: 42,
   303  					Type:     FrameHeaders,
   304  					Flags:    FlagHeadersEndStream | FlagHeadersEndHeaders | FlagHeadersPadded | FlagHeadersPriority,
   305  					Length:   uint32(1 + 5 + len("abc") + 2), // pad length + priority + contents + padding
   306  				},
   307  				Priority: PriorityParam{
   308  					StreamDep: 0,
   309  					Exclusive: true,
   310  					Weight:    127,
   311  				},
   312  				headerFragBuf: []byte("abc"),
   313  			},
   314  		},
   315  		{
   316  			"zero length",
   317  			HeadersFrameParam{
   318  				StreamID: 42,
   319  				Priority: PriorityParam{},
   320  			},
   321  			"\x00\x00\x00\x01\x00\x00\x00\x00*",
   322  			&HeadersFrame{
   323  				FrameHeader: FrameHeader{
   324  					valid:    true,
   325  					StreamID: 42,
   326  					Type:     FrameHeaders,
   327  					Length:   0,
   328  				},
   329  				Priority: PriorityParam{},
   330  			},
   331  		},
   332  	}
   333  	for _, tt := range tests {
   334  		fr, buf := testFramer()
   335  		if err := fr.WriteHeaders(tt.p); err != nil {
   336  			t.Errorf("test %q: %v", tt.name, err)
   337  			continue
   338  		}
   339  		if buf.String() != tt.wantEnc {
   340  			t.Errorf("test %q: encoded %q; want %q", tt.name, buf.Bytes(), tt.wantEnc)
   341  		}
   342  		f, err := fr.ReadFrame()
   343  		if err != nil {
   344  			t.Errorf("test %q: failed to read the frame back: %v", tt.name, err)
   345  			continue
   346  		}
   347  		if !reflect.DeepEqual(f, tt.wantFrame) {
   348  			t.Errorf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tt.name, f, tt.wantFrame)
   349  		}
   350  	}
   351  }
   352  
   353  func TestWriteInvalidStreamDep(t *testing.T) {
   354  	fr, _ := testFramer()
   355  	err := fr.WriteHeaders(HeadersFrameParam{
   356  		StreamID: 42,
   357  		Priority: PriorityParam{
   358  			StreamDep: 1 << 31,
   359  		},
   360  	})
   361  	if err != errDepStreamID {
   362  		t.Errorf("header error = %v; want %q", err, errDepStreamID)
   363  	}
   364  
   365  	err = fr.WritePriority(2, PriorityParam{StreamDep: 1 << 31})
   366  	if err != errDepStreamID {
   367  		t.Errorf("priority error = %v; want %q", err, errDepStreamID)
   368  	}
   369  }
   370  
   371  func TestWriteContinuation(t *testing.T) {
   372  	const streamID = 42
   373  	tests := []struct {
   374  		name string
   375  		end  bool
   376  		frag []byte
   377  
   378  		wantFrame *ContinuationFrame
   379  	}{
   380  		{
   381  			"not end",
   382  			false,
   383  			[]byte("abc"),
   384  			&ContinuationFrame{
   385  				FrameHeader: FrameHeader{
   386  					valid:    true,
   387  					StreamID: streamID,
   388  					Type:     FrameContinuation,
   389  					Length:   uint32(len("abc")),
   390  				},
   391  				headerFragBuf: []byte("abc"),
   392  			},
   393  		},
   394  		{
   395  			"end",
   396  			true,
   397  			[]byte("def"),
   398  			&ContinuationFrame{
   399  				FrameHeader: FrameHeader{
   400  					valid:    true,
   401  					StreamID: streamID,
   402  					Type:     FrameContinuation,
   403  					Flags:    FlagContinuationEndHeaders,
   404  					Length:   uint32(len("def")),
   405  				},
   406  				headerFragBuf: []byte("def"),
   407  			},
   408  		},
   409  	}
   410  	for _, tt := range tests {
   411  		fr, _ := testFramer()
   412  		if err := fr.WriteContinuation(streamID, tt.end, tt.frag); err != nil {
   413  			t.Errorf("test %q: %v", tt.name, err)
   414  			continue
   415  		}
   416  		fr.AllowIllegalReads = true
   417  		f, err := fr.ReadFrame()
   418  		if err != nil {
   419  			t.Errorf("test %q: failed to read the frame back: %v", tt.name, err)
   420  			continue
   421  		}
   422  		if !reflect.DeepEqual(f, tt.wantFrame) {
   423  			t.Errorf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tt.name, f, tt.wantFrame)
   424  		}
   425  	}
   426  }
   427  
   428  func TestWritePriority(t *testing.T) {
   429  	const streamID = 42
   430  	tests := []struct {
   431  		name      string
   432  		priority  PriorityParam
   433  		wantFrame *PriorityFrame
   434  	}{
   435  		{
   436  			"not exclusive",
   437  			PriorityParam{
   438  				StreamDep: 2,
   439  				Exclusive: false,
   440  				Weight:    127,
   441  			},
   442  			&PriorityFrame{
   443  				FrameHeader{
   444  					valid:    true,
   445  					StreamID: streamID,
   446  					Type:     FramePriority,
   447  					Length:   5,
   448  				},
   449  				PriorityParam{
   450  					StreamDep: 2,
   451  					Exclusive: false,
   452  					Weight:    127,
   453  				},
   454  			},
   455  		},
   456  
   457  		{
   458  			"exclusive",
   459  			PriorityParam{
   460  				StreamDep: 3,
   461  				Exclusive: true,
   462  				Weight:    77,
   463  			},
   464  			&PriorityFrame{
   465  				FrameHeader{
   466  					valid:    true,
   467  					StreamID: streamID,
   468  					Type:     FramePriority,
   469  					Length:   5,
   470  				},
   471  				PriorityParam{
   472  					StreamDep: 3,
   473  					Exclusive: true,
   474  					Weight:    77,
   475  				},
   476  			},
   477  		},
   478  	}
   479  	for _, tt := range tests {
   480  		fr, _ := testFramer()
   481  		if err := fr.WritePriority(streamID, tt.priority); err != nil {
   482  			t.Errorf("test %q: %v", tt.name, err)
   483  			continue
   484  		}
   485  		f, err := fr.ReadFrame()
   486  		if err != nil {
   487  			t.Errorf("test %q: failed to read the frame back: %v", tt.name, err)
   488  			continue
   489  		}
   490  		if !reflect.DeepEqual(f, tt.wantFrame) {
   491  			t.Errorf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tt.name, f, tt.wantFrame)
   492  		}
   493  	}
   494  }
   495  
   496  func TestWriteSettings(t *testing.T) {
   497  	fr, buf := testFramer()
   498  	settings := []Setting{{1, 2}, {3, 4}}
   499  	fr.WriteSettings(settings...)
   500  	const wantEnc = "\x00\x00\f\x04\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x03\x00\x00\x00\x04"
   501  	if buf.String() != wantEnc {
   502  		t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc)
   503  	}
   504  	f, err := fr.ReadFrame()
   505  	if err != nil {
   506  		t.Fatal(err)
   507  	}
   508  	sf, ok := f.(*SettingsFrame)
   509  	if !ok {
   510  		t.Fatalf("Got a %T; want a SettingsFrame", f)
   511  	}
   512  	var got []Setting
   513  	sf.ForeachSetting(func(s Setting) error {
   514  		got = append(got, s)
   515  		valBack, ok := sf.Value(s.ID)
   516  		if !ok || valBack != s.Val {
   517  			t.Errorf("Value(%d) = %v, %v; want %v, true", s.ID, valBack, ok, s.Val)
   518  		}
   519  		return nil
   520  	})
   521  	if !reflect.DeepEqual(settings, got) {
   522  		t.Errorf("Read settings %+v != written settings %+v", got, settings)
   523  	}
   524  }
   525  
   526  func TestWriteSettingsAck(t *testing.T) {
   527  	fr, buf := testFramer()
   528  	fr.WriteSettingsAck()
   529  	const wantEnc = "\x00\x00\x00\x04\x01\x00\x00\x00\x00"
   530  	if buf.String() != wantEnc {
   531  		t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc)
   532  	}
   533  }
   534  
   535  func TestWriteWindowUpdate(t *testing.T) {
   536  	fr, buf := testFramer()
   537  	const streamID = 1<<24 + 2<<16 + 3<<8 + 4
   538  	const incr = 7<<24 + 6<<16 + 5<<8 + 4
   539  	if err := fr.WriteWindowUpdate(streamID, incr); err != nil {
   540  		t.Fatal(err)
   541  	}
   542  	const wantEnc = "\x00\x00\x04\x08\x00\x01\x02\x03\x04\x07\x06\x05\x04"
   543  	if buf.String() != wantEnc {
   544  		t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc)
   545  	}
   546  	f, err := fr.ReadFrame()
   547  	if err != nil {
   548  		t.Fatal(err)
   549  	}
   550  	want := &WindowUpdateFrame{
   551  		FrameHeader: FrameHeader{
   552  			valid:    true,
   553  			Type:     0x8,
   554  			Flags:    0x0,
   555  			Length:   0x4,
   556  			StreamID: 0x1020304,
   557  		},
   558  		Increment: 0x7060504,
   559  	}
   560  	if !reflect.DeepEqual(f, want) {
   561  		t.Errorf("parsed back %#v; want %#v", f, want)
   562  	}
   563  }
   564  
   565  func TestWritePing(t *testing.T)    { testWritePing(t, false) }
   566  func TestWritePingAck(t *testing.T) { testWritePing(t, true) }
   567  
   568  func testWritePing(t *testing.T, ack bool) {
   569  	fr, buf := testFramer()
   570  	if err := fr.WritePing(ack, [8]byte{1, 2, 3, 4, 5, 6, 7, 8}); err != nil {
   571  		t.Fatal(err)
   572  	}
   573  	var wantFlags Flags
   574  	if ack {
   575  		wantFlags = FlagPingAck
   576  	}
   577  	var wantEnc = "\x00\x00\x08\x06" + string(wantFlags) + "\x00\x00\x00\x00" + "\x01\x02\x03\x04\x05\x06\x07\x08"
   578  	if buf.String() != wantEnc {
   579  		t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc)
   580  	}
   581  
   582  	f, err := fr.ReadFrame()
   583  	if err != nil {
   584  		t.Fatal(err)
   585  	}
   586  	want := &PingFrame{
   587  		FrameHeader: FrameHeader{
   588  			valid:    true,
   589  			Type:     0x6,
   590  			Flags:    wantFlags,
   591  			Length:   0x8,
   592  			StreamID: 0,
   593  		},
   594  		Data: [8]byte{1, 2, 3, 4, 5, 6, 7, 8},
   595  	}
   596  	if !reflect.DeepEqual(f, want) {
   597  		t.Errorf("parsed back %#v; want %#v", f, want)
   598  	}
   599  }
   600  
   601  func TestReadFrameHeader(t *testing.T) {
   602  	tests := []struct {
   603  		in   string
   604  		want FrameHeader
   605  	}{
   606  		{in: "\x00\x00\x00" + "\x00" + "\x00" + "\x00\x00\x00\x00", want: FrameHeader{}},
   607  		{in: "\x01\x02\x03" + "\x04" + "\x05" + "\x06\x07\x08\x09", want: FrameHeader{
   608  			Length: 66051, Type: 4, Flags: 5, StreamID: 101124105,
   609  		}},
   610  		// Ignore high bit:
   611  		{in: "\xff\xff\xff" + "\xff" + "\xff" + "\xff\xff\xff\xff", want: FrameHeader{
   612  			Length: 16777215, Type: 255, Flags: 255, StreamID: 2147483647}},
   613  		{in: "\xff\xff\xff" + "\xff" + "\xff" + "\x7f\xff\xff\xff", want: FrameHeader{
   614  			Length: 16777215, Type: 255, Flags: 255, StreamID: 2147483647}},
   615  	}
   616  	for i, tt := range tests {
   617  		got, err := readFrameHeader(make([]byte, 9), strings.NewReader(tt.in))
   618  		if err != nil {
   619  			t.Errorf("%d. readFrameHeader(%q) = %v", i, tt.in, err)
   620  			continue
   621  		}
   622  		tt.want.valid = true
   623  		if !got.Equal(tt.want) {
   624  			t.Errorf("%d. readFrameHeader(%q) = %+v; want %+v", i, tt.in, got, tt.want)
   625  		}
   626  	}
   627  }
   628  
   629  func TestReadWriteFrameHeader(t *testing.T) {
   630  	tests := []struct {
   631  		len      uint32
   632  		typ      FrameType
   633  		flags    Flags
   634  		streamID uint32
   635  	}{
   636  		{len: 0, typ: 255, flags: 1, streamID: 0},
   637  		{len: 0, typ: 255, flags: 1, streamID: 1},
   638  		{len: 0, typ: 255, flags: 1, streamID: 255},
   639  		{len: 0, typ: 255, flags: 1, streamID: 256},
   640  		{len: 0, typ: 255, flags: 1, streamID: 65535},
   641  		{len: 0, typ: 255, flags: 1, streamID: 65536},
   642  
   643  		{len: 0, typ: 1, flags: 255, streamID: 1},
   644  		{len: 255, typ: 1, flags: 255, streamID: 1},
   645  		{len: 256, typ: 1, flags: 255, streamID: 1},
   646  		{len: 65535, typ: 1, flags: 255, streamID: 1},
   647  		{len: 65536, typ: 1, flags: 255, streamID: 1},
   648  		{len: 16777215, typ: 1, flags: 255, streamID: 1},
   649  	}
   650  	for _, tt := range tests {
   651  		fr, buf := testFramer()
   652  		fr.startWrite(tt.typ, tt.flags, tt.streamID)
   653  		fr.writeBytes(make([]byte, tt.len))
   654  		fr.endWrite()
   655  		fh, err := ReadFrameHeader(buf)
   656  		if err != nil {
   657  			t.Errorf("ReadFrameHeader(%+v) = %v", tt, err)
   658  			continue
   659  		}
   660  		if fh.Type != tt.typ || fh.Flags != tt.flags || fh.Length != tt.len || fh.StreamID != tt.streamID {
   661  			t.Errorf("ReadFrameHeader(%+v) = %+v; mismatch", tt, fh)
   662  		}
   663  	}
   664  
   665  }
   666  
   667  func TestWriteTooLargeFrame(t *testing.T) {
   668  	fr, _ := testFramer()
   669  	fr.startWrite(0, 1, 1)
   670  	fr.writeBytes(make([]byte, 1<<24))
   671  	err := fr.endWrite()
   672  	if err != ErrFrameTooLarge {
   673  		t.Errorf("endWrite = %v; want errFrameTooLarge", err)
   674  	}
   675  }
   676  
   677  func TestWriteGoAway(t *testing.T) {
   678  	const debug = "foo"
   679  	fr, buf := testFramer()
   680  	if err := fr.WriteGoAway(0x01020304, 0x05060708, []byte(debug)); err != nil {
   681  		t.Fatal(err)
   682  	}
   683  	const wantEnc = "\x00\x00\v\a\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08" + debug
   684  	if buf.String() != wantEnc {
   685  		t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc)
   686  	}
   687  	f, err := fr.ReadFrame()
   688  	if err != nil {
   689  		t.Fatal(err)
   690  	}
   691  	want := &GoAwayFrame{
   692  		FrameHeader: FrameHeader{
   693  			valid:    true,
   694  			Type:     0x7,
   695  			Flags:    0,
   696  			Length:   uint32(4 + 4 + len(debug)),
   697  			StreamID: 0,
   698  		},
   699  		LastStreamID: 0x01020304,
   700  		ErrCode:      0x05060708,
   701  		debugData:    []byte(debug),
   702  	}
   703  	if !reflect.DeepEqual(f, want) {
   704  		t.Fatalf("parsed back:\n%#v\nwant:\n%#v", f, want)
   705  	}
   706  	if got := string(f.(*GoAwayFrame).DebugData()); got != debug {
   707  		t.Errorf("debug data = %q; want %q", got, debug)
   708  	}
   709  }
   710  
   711  func TestWritePushPromise(t *testing.T) {
   712  	pp := PushPromiseParam{
   713  		StreamID:      42,
   714  		PromiseID:     42,
   715  		BlockFragment: []byte("abc"),
   716  	}
   717  	fr, buf := testFramer()
   718  	if err := fr.WritePushPromise(pp); err != nil {
   719  		t.Fatal(err)
   720  	}
   721  	const wantEnc = "\x00\x00\x07\x05\x00\x00\x00\x00*\x00\x00\x00*abc"
   722  	if buf.String() != wantEnc {
   723  		t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc)
   724  	}
   725  	f, err := fr.ReadFrame()
   726  	if err != nil {
   727  		t.Fatal(err)
   728  	}
   729  	_, ok := f.(*PushPromiseFrame)
   730  	if !ok {
   731  		t.Fatalf("got %T; want *PushPromiseFrame", f)
   732  	}
   733  	want := &PushPromiseFrame{
   734  		FrameHeader: FrameHeader{
   735  			valid:    true,
   736  			Type:     0x5,
   737  			Flags:    0x0,
   738  			Length:   0x7,
   739  			StreamID: 42,
   740  		},
   741  		PromiseID:     42,
   742  		headerFragBuf: []byte("abc"),
   743  	}
   744  	if !reflect.DeepEqual(f, want) {
   745  		t.Fatalf("parsed back:\n%#v\nwant:\n%#v", f, want)
   746  	}
   747  }
   748  
   749  // test checkFrameOrder and that HEADERS and CONTINUATION frames can't be intermingled.
   750  func TestReadFrameOrder(t *testing.T) {
   751  	head := func(f *Framer, id uint32, end bool) {
   752  		f.WriteHeaders(HeadersFrameParam{
   753  			StreamID:      id,
   754  			BlockFragment: []byte("foo"), // unused, but non-empty
   755  			EndHeaders:    end,
   756  		})
   757  	}
   758  	cont := func(f *Framer, id uint32, end bool) {
   759  		f.WriteContinuation(id, end, []byte("foo"))
   760  	}
   761  
   762  	tests := [...]struct {
   763  		name    string
   764  		w       func(*Framer)
   765  		atLeast int
   766  		wantErr string
   767  	}{
   768  		0: {
   769  			w: func(f *Framer) {
   770  				head(f, 1, true)
   771  			},
   772  		},
   773  		1: {
   774  			w: func(f *Framer) {
   775  				head(f, 1, true)
   776  				head(f, 2, true)
   777  			},
   778  		},
   779  		2: {
   780  			wantErr: "got HEADERS for stream 2; expected CONTINUATION following HEADERS for stream 1",
   781  			w: func(f *Framer) {
   782  				head(f, 1, false)
   783  				head(f, 2, true)
   784  			},
   785  		},
   786  		3: {
   787  			wantErr: "got DATA for stream 1; expected CONTINUATION following HEADERS for stream 1",
   788  			w: func(f *Framer) {
   789  				head(f, 1, false)
   790  			},
   791  		},
   792  		4: {
   793  			w: func(f *Framer) {
   794  				head(f, 1, false)
   795  				cont(f, 1, true)
   796  				head(f, 2, true)
   797  			},
   798  		},
   799  		5: {
   800  			wantErr: "got CONTINUATION for stream 2; expected stream 1",
   801  			w: func(f *Framer) {
   802  				head(f, 1, false)
   803  				cont(f, 2, true)
   804  				head(f, 2, true)
   805  			},
   806  		},
   807  		6: {
   808  			wantErr: "unexpected CONTINUATION for stream 1",
   809  			w: func(f *Framer) {
   810  				cont(f, 1, true)
   811  			},
   812  		},
   813  		7: {
   814  			wantErr: "unexpected CONTINUATION for stream 1",
   815  			w: func(f *Framer) {
   816  				cont(f, 1, false)
   817  			},
   818  		},
   819  		8: {
   820  			wantErr: "HEADERS frame with stream ID 0",
   821  			w: func(f *Framer) {
   822  				head(f, 0, true)
   823  			},
   824  		},
   825  		9: {
   826  			wantErr: "CONTINUATION frame with stream ID 0",
   827  			w: func(f *Framer) {
   828  				cont(f, 0, true)
   829  			},
   830  		},
   831  		10: {
   832  			wantErr: "unexpected CONTINUATION for stream 1",
   833  			atLeast: 5,
   834  			w: func(f *Framer) {
   835  				head(f, 1, false)
   836  				cont(f, 1, false)
   837  				cont(f, 1, false)
   838  				cont(f, 1, false)
   839  				cont(f, 1, true)
   840  				cont(f, 1, false)
   841  			},
   842  		},
   843  	}
   844  	for i, tt := range tests {
   845  		buf := new(bytes.Buffer)
   846  		f := NewFramer(buf, buf)
   847  		f.AllowIllegalWrites = true
   848  		tt.w(f)
   849  		f.WriteData(1, true, nil) // to test transition away from last step
   850  
   851  		var err error
   852  		n := 0
   853  		var log bytes.Buffer
   854  		for {
   855  			var got Frame
   856  			got, err = f.ReadFrame()
   857  			fmt.Fprintf(&log, "  read %v, %v\n", got, err)
   858  			if err != nil {
   859  				break
   860  			}
   861  			n++
   862  		}
   863  		if err == io.EOF {
   864  			err = nil
   865  		}
   866  		ok := tt.wantErr == ""
   867  		if ok && err != nil {
   868  			t.Errorf("%d. after %d good frames, ReadFrame = %v; want success\n%s", i, n, err, log.Bytes())
   869  			continue
   870  		}
   871  		if !ok && err != ConnectionError(ErrCodeProtocol) {
   872  			t.Errorf("%d. after %d good frames, ReadFrame = %v; want ConnectionError(ErrCodeProtocol)\n%s", i, n, err, log.Bytes())
   873  			continue
   874  		}
   875  		if !((f.errDetail == nil && tt.wantErr == "") || (fmt.Sprint(f.errDetail) == tt.wantErr)) {
   876  			t.Errorf("%d. framer eror = %q; want %q\n%s", i, f.errDetail, tt.wantErr, log.Bytes())
   877  		}
   878  		if n < tt.atLeast {
   879  			t.Errorf("%d. framer only read %d frames; want at least %d\n%s", i, n, tt.atLeast, log.Bytes())
   880  		}
   881  	}
   882  }
   883  
   884  func TestMetaFrameHeader(t *testing.T) {
   885  	write := func(f *Framer, frags ...[]byte) {
   886  		for i, frag := range frags {
   887  			end := (i == len(frags)-1)
   888  			if i == 0 {
   889  				f.WriteHeaders(HeadersFrameParam{
   890  					StreamID:      1,
   891  					BlockFragment: frag,
   892  					EndHeaders:    end,
   893  				})
   894  			} else {
   895  				f.WriteContinuation(1, end, frag)
   896  			}
   897  		}
   898  	}
   899  
   900  	want := func(flags Flags, length uint32, pairs ...string) *MetaHeadersFrame {
   901  		mh := &MetaHeadersFrame{
   902  			HeadersFrame: &HeadersFrame{
   903  				FrameHeader: FrameHeader{
   904  					Type:     FrameHeaders,
   905  					Flags:    flags,
   906  					Length:   length,
   907  					StreamID: 1,
   908  				},
   909  			},
   910  			Fields: []hpack.HeaderField(nil),
   911  		}
   912  		for len(pairs) > 0 {
   913  			mh.Fields = append(mh.Fields, hpack.HeaderField{
   914  				Name:  pairs[0],
   915  				Value: pairs[1],
   916  			})
   917  			pairs = pairs[2:]
   918  		}
   919  		return mh
   920  	}
   921  	truncated := func(mh *MetaHeadersFrame) *MetaHeadersFrame {
   922  		mh.Truncated = true
   923  		return mh
   924  	}
   925  
   926  	const noFlags Flags = 0
   927  
   928  	oneKBString := strings.Repeat("a", 1<<10)
   929  
   930  	tests := [...]struct {
   931  		name              string
   932  		w                 func(*Framer)
   933  		want              interface{} // *MetaHeaderFrame or error
   934  		wantErrReason     string
   935  		maxHeaderListSize uint32
   936  	}{
   937  		0: {
   938  			name: "single_headers",
   939  			w: func(f *Framer) {
   940  				var he hpackEncoder
   941  				all := he.encodeHeaderRaw(t, ":method", "GET", ":path", "/")
   942  				write(f, all)
   943  			},
   944  			want: want(FlagHeadersEndHeaders, 2, ":method", "GET", ":path", "/"),
   945  		},
   946  		1: {
   947  			name: "with_continuation",
   948  			w: func(f *Framer) {
   949  				var he hpackEncoder
   950  				all := he.encodeHeaderRaw(t, ":method", "GET", ":path", "/", "foo", "bar")
   951  				write(f, all[:1], all[1:])
   952  			},
   953  			want: want(noFlags, 1, ":method", "GET", ":path", "/", "foo", "bar"),
   954  		},
   955  		2: {
   956  			name: "with_two_continuation",
   957  			w: func(f *Framer) {
   958  				var he hpackEncoder
   959  				all := he.encodeHeaderRaw(t, ":method", "GET", ":path", "/", "foo", "bar")
   960  				write(f, all[:2], all[2:4], all[4:])
   961  			},
   962  			want: want(noFlags, 2, ":method", "GET", ":path", "/", "foo", "bar"),
   963  		},
   964  		3: {
   965  			name: "big_string_okay",
   966  			w: func(f *Framer) {
   967  				var he hpackEncoder
   968  				all := he.encodeHeaderRaw(t, ":method", "GET", ":path", "/", "foo", oneKBString)
   969  				write(f, all[:2], all[2:])
   970  			},
   971  			want: want(noFlags, 2, ":method", "GET", ":path", "/", "foo", oneKBString),
   972  		},
   973  		4: {
   974  			name: "big_string_error",
   975  			w: func(f *Framer) {
   976  				var he hpackEncoder
   977  				all := he.encodeHeaderRaw(t, ":method", "GET", ":path", "/", "foo", oneKBString)
   978  				write(f, all[:2], all[2:])
   979  			},
   980  			maxHeaderListSize: (1 << 10) / 2,
   981  			want:              ConnectionError(ErrCodeCompression),
   982  		},
   983  		5: {
   984  			name: "max_header_list_truncated",
   985  			w: func(f *Framer) {
   986  				var he hpackEncoder
   987  				var pairs = []string{":method", "GET", ":path", "/"}
   988  				for i := 0; i < 100; i++ {
   989  					pairs = append(pairs, "foo", "bar")
   990  				}
   991  				all := he.encodeHeaderRaw(t, pairs...)
   992  				write(f, all[:2], all[2:])
   993  			},
   994  			maxHeaderListSize: (1 << 10) / 2,
   995  			want: truncated(want(noFlags, 2,
   996  				":method", "GET",
   997  				":path", "/",
   998  				"foo", "bar",
   999  				"foo", "bar",
  1000  				"foo", "bar",
  1001  				"foo", "bar",
  1002  				"foo", "bar",
  1003  				"foo", "bar",
  1004  				"foo", "bar",
  1005  				"foo", "bar",
  1006  				"foo", "bar",
  1007  				"foo", "bar",
  1008  				"foo", "bar", // 11
  1009  			)),
  1010  		},
  1011  		6: {
  1012  			name: "pseudo_order",
  1013  			w: func(f *Framer) {
  1014  				write(f, encodeHeaderRaw(t,
  1015  					":method", "GET",
  1016  					"foo", "bar",
  1017  					":path", "/", // bogus
  1018  				))
  1019  			},
  1020  			want:          streamError(1, ErrCodeProtocol),
  1021  			wantErrReason: "pseudo header field after regular",
  1022  		},
  1023  		7: {
  1024  			name: "pseudo_unknown",
  1025  			w: func(f *Framer) {
  1026  				write(f, encodeHeaderRaw(t,
  1027  					":unknown", "foo", // bogus
  1028  					"foo", "bar",
  1029  				))
  1030  			},
  1031  			want:          streamError(1, ErrCodeProtocol),
  1032  			wantErrReason: "invalid pseudo-header \":unknown\"",
  1033  		},
  1034  		8: {
  1035  			name: "pseudo_mix_request_response",
  1036  			w: func(f *Framer) {
  1037  				write(f, encodeHeaderRaw(t,
  1038  					":method", "GET",
  1039  					":status", "100",
  1040  				))
  1041  			},
  1042  			want:          streamError(1, ErrCodeProtocol),
  1043  			wantErrReason: "mix of request and response pseudo headers",
  1044  		},
  1045  		9: {
  1046  			name: "pseudo_dup",
  1047  			w: func(f *Framer) {
  1048  				write(f, encodeHeaderRaw(t,
  1049  					":method", "GET",
  1050  					":method", "POST",
  1051  				))
  1052  			},
  1053  			want:          streamError(1, ErrCodeProtocol),
  1054  			wantErrReason: "duplicate pseudo-header \":method\"",
  1055  		},
  1056  		10: {
  1057  			name: "trailer_okay_no_pseudo",
  1058  			w:    func(f *Framer) { write(f, encodeHeaderRaw(t, "foo", "bar")) },
  1059  			want: want(FlagHeadersEndHeaders, 8, "foo", "bar"),
  1060  		},
  1061  		11: {
  1062  			name:          "invalid_field_name",
  1063  			w:             func(f *Framer) { write(f, encodeHeaderRaw(t, "CapitalBad", "x")) },
  1064  			want:          streamError(1, ErrCodeProtocol),
  1065  			wantErrReason: "invalid header field name \"CapitalBad\"",
  1066  		},
  1067  		12: {
  1068  			name:          "invalid_field_value",
  1069  			w:             func(f *Framer) { write(f, encodeHeaderRaw(t, "key", "bad_null\x00")) },
  1070  			want:          streamError(1, ErrCodeProtocol),
  1071  			wantErrReason: `invalid header field value for "key"`,
  1072  		},
  1073  	}
  1074  	for i, tt := range tests {
  1075  		buf := new(bytes.Buffer)
  1076  		f := NewFramer(buf, buf)
  1077  		f.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil)
  1078  		f.MaxHeaderListSize = tt.maxHeaderListSize
  1079  		tt.w(f)
  1080  
  1081  		name := tt.name
  1082  		if name == "" {
  1083  			name = fmt.Sprintf("test index %d", i)
  1084  		}
  1085  
  1086  		var got interface{}
  1087  		var err error
  1088  		got, err = f.ReadFrame()
  1089  		if err != nil {
  1090  			got = err
  1091  
  1092  			// Ignore the StreamError.Cause field, if it matches the wantErrReason.
  1093  			// The test table above predates the Cause field.
  1094  			if se, ok := err.(StreamError); ok && se.Cause != nil && se.Cause.Error() == tt.wantErrReason {
  1095  				se.Cause = nil
  1096  				got = se
  1097  			}
  1098  		}
  1099  		if !reflect.DeepEqual(got, tt.want) {
  1100  			if mhg, ok := got.(*MetaHeadersFrame); ok {
  1101  				if mhw, ok := tt.want.(*MetaHeadersFrame); ok {
  1102  					hg := mhg.HeadersFrame
  1103  					hw := mhw.HeadersFrame
  1104  					if hg != nil && hw != nil && !reflect.DeepEqual(*hg, *hw) {
  1105  						t.Errorf("%s: headers differ:\n got: %+v\nwant: %+v\n", name, *hg, *hw)
  1106  					}
  1107  				}
  1108  			}
  1109  			str := func(v interface{}) string {
  1110  				if _, ok := v.(error); ok {
  1111  					return fmt.Sprintf("error %v", v)
  1112  				} else {
  1113  					return fmt.Sprintf("value %#v", v)
  1114  				}
  1115  			}
  1116  			t.Errorf("%s:\n got: %v\nwant: %s", name, str(got), str(tt.want))
  1117  		}
  1118  		if tt.wantErrReason != "" && tt.wantErrReason != fmt.Sprint(f.errDetail) {
  1119  			t.Errorf("%s: got error reason %q; want %q", name, f.errDetail, tt.wantErrReason)
  1120  		}
  1121  	}
  1122  }
  1123  
  1124  func TestSetReuseFrames(t *testing.T) {
  1125  	fr, buf := testFramer()
  1126  	fr.SetReuseFrames()
  1127  
  1128  	// Check that DataFrames are reused. Note that
  1129  	// SetReuseFrames only currently implements reuse of DataFrames.
  1130  	firstDf := readAndVerifyDataFrame("ABC", 3, fr, buf, t)
  1131  
  1132  	for i := 0; i < 10; i++ {
  1133  		df := readAndVerifyDataFrame("XYZ", 3, fr, buf, t)
  1134  		if df != firstDf {
  1135  			t.Errorf("Expected Framer to return references to the same DataFrame. Have %v and %v", &df, &firstDf)
  1136  		}
  1137  	}
  1138  
  1139  	for i := 0; i < 10; i++ {
  1140  		df := readAndVerifyDataFrame("", 0, fr, buf, t)
  1141  		if df != firstDf {
  1142  			t.Errorf("Expected Framer to return references to the same DataFrame. Have %v and %v", &df, &firstDf)
  1143  		}
  1144  	}
  1145  
  1146  	for i := 0; i < 10; i++ {
  1147  		df := readAndVerifyDataFrame("HHH", 3, fr, buf, t)
  1148  		if df != firstDf {
  1149  			t.Errorf("Expected Framer to return references to the same DataFrame. Have %v and %v", &df, &firstDf)
  1150  		}
  1151  	}
  1152  }
  1153  
  1154  func TestSetReuseFramesMoreThanOnce(t *testing.T) {
  1155  	fr, buf := testFramer()
  1156  	fr.SetReuseFrames()
  1157  
  1158  	firstDf := readAndVerifyDataFrame("ABC", 3, fr, buf, t)
  1159  	fr.SetReuseFrames()
  1160  
  1161  	for i := 0; i < 10; i++ {
  1162  		df := readAndVerifyDataFrame("XYZ", 3, fr, buf, t)
  1163  		// SetReuseFrames should be idempotent
  1164  		fr.SetReuseFrames()
  1165  		if df != firstDf {
  1166  			t.Errorf("Expected Framer to return references to the same DataFrame. Have %v and %v", &df, &firstDf)
  1167  		}
  1168  	}
  1169  }
  1170  
  1171  func TestNoSetReuseFrames(t *testing.T) {
  1172  	fr, buf := testFramer()
  1173  	const numNewDataFrames = 10
  1174  	dfSoFar := make([]interface{}, numNewDataFrames)
  1175  
  1176  	// Check that DataFrames are not reused if SetReuseFrames wasn't called.
  1177  	// SetReuseFrames only currently implements reuse of DataFrames.
  1178  	for i := 0; i < numNewDataFrames; i++ {
  1179  		df := readAndVerifyDataFrame("XYZ", 3, fr, buf, t)
  1180  		for _, item := range dfSoFar {
  1181  			if df == item {
  1182  				t.Errorf("Expected Framer to return new DataFrames since SetNoReuseFrames not set.")
  1183  			}
  1184  		}
  1185  		dfSoFar[i] = df
  1186  	}
  1187  }
  1188  
  1189  func readAndVerifyDataFrame(data string, length byte, fr *Framer, buf *bytes.Buffer, t *testing.T) *DataFrame {
  1190  	var streamID uint32 = 1<<24 + 2<<16 + 3<<8 + 4
  1191  	fr.WriteData(streamID, true, []byte(data))
  1192  	wantEnc := "\x00\x00" + string(length) + "\x00\x01\x01\x02\x03\x04" + data
  1193  	if buf.String() != wantEnc {
  1194  		t.Errorf("encoded as %q; want %q", buf.Bytes(), wantEnc)
  1195  	}
  1196  	f, err := fr.ReadFrame()
  1197  	if err != nil {
  1198  		t.Fatal(err)
  1199  	}
  1200  	df, ok := f.(*DataFrame)
  1201  	if !ok {
  1202  		t.Fatalf("got %T; want *DataFrame", f)
  1203  	}
  1204  	if !bytes.Equal(df.Data(), []byte(data)) {
  1205  		t.Errorf("got %q; want %q", df.Data(), []byte(data))
  1206  	}
  1207  	if f.Header().Flags&1 == 0 {
  1208  		t.Errorf("didn't see END_STREAM flag")
  1209  	}
  1210  	return df
  1211  }
  1212  
  1213  func encodeHeaderRaw(t *testing.T, pairs ...string) []byte {
  1214  	var he hpackEncoder
  1215  	return he.encodeHeaderRaw(t, pairs...)
  1216  }
  1217  
  1218  func TestSettingsDuplicates(t *testing.T) {
  1219  	tests := []struct {
  1220  		settings []Setting
  1221  		want     bool
  1222  	}{
  1223  		{nil, false},
  1224  		{[]Setting{{ID: 1}}, false},
  1225  		{[]Setting{{ID: 1}, {ID: 2}}, false},
  1226  		{[]Setting{{ID: 1}, {ID: 2}}, false},
  1227  		{[]Setting{{ID: 1}, {ID: 2}, {ID: 3}}, false},
  1228  		{[]Setting{{ID: 1}, {ID: 2}, {ID: 3}}, false},
  1229  		{[]Setting{{ID: 1}, {ID: 2}, {ID: 3}, {ID: 4}}, false},
  1230  
  1231  		{[]Setting{{ID: 1}, {ID: 2}, {ID: 3}, {ID: 2}}, true},
  1232  		{[]Setting{{ID: 4}, {ID: 2}, {ID: 3}, {ID: 4}}, true},
  1233  
  1234  		{[]Setting{
  1235  			{ID: 1}, {ID: 2}, {ID: 3}, {ID: 4},
  1236  			{ID: 5}, {ID: 6}, {ID: 7}, {ID: 8},
  1237  			{ID: 9}, {ID: 10}, {ID: 11}, {ID: 12},
  1238  		}, false},
  1239  
  1240  		{[]Setting{
  1241  			{ID: 1}, {ID: 2}, {ID: 3}, {ID: 4},
  1242  			{ID: 5}, {ID: 6}, {ID: 7}, {ID: 8},
  1243  			{ID: 9}, {ID: 10}, {ID: 11}, {ID: 11},
  1244  		}, true},
  1245  	}
  1246  	for i, tt := range tests {
  1247  		fr, _ := testFramer()
  1248  		fr.WriteSettings(tt.settings...)
  1249  		f, err := fr.ReadFrame()
  1250  		if err != nil {
  1251  			t.Fatalf("%d. ReadFrame: %v", i, err)
  1252  		}
  1253  		sf := f.(*SettingsFrame)
  1254  		got := sf.HasDuplicates()
  1255  		if got != tt.want {
  1256  			t.Errorf("%d. HasDuplicates = %v; want %v", i, got, tt.want)
  1257  		}
  1258  	}
  1259  
  1260  }