// Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package bitfield import ( "bytes" "fmt" "io/ioutil" "testing" ) type myUint8 uint8 type test1 struct { // 28 bits foo uint16 `bitfield:",fob"` Bar int8 `bitfield:"5,baz"` Foo uint64 bar myUint8 `bitfield:"3"` Bool bool `bitfield:""` Baz int8 `bitfield:"3"` } type test2 struct { larger1 uint16 `bitfield:"32"` larger2 uint16 `bitfield:"32"` } type tooManyBits struct { u1 uint16 `bitfield:"12"` u2 uint16 `bitfield:"12"` u3 uint16 `bitfield:"12"` u4 uint16 `bitfield:"12"` u5 uint16 `bitfield:"12"` u6 uint16 `bitfield:"12"` } type just64 struct { foo uint64 `bitfield:""` } type toUint8 struct { foo bool `bitfield:""` } type toUint16 struct { foo int `bitfield:"9"` } type faultySize struct { foo uint64 `bitfield:"a"` } type faultyType struct { foo *int `bitfield:"5"` } var ( maxed = test1{ foo: 0xffff, Bar: 0x1f, Foo: 0xffff, bar: 0x7, Bool: true, Baz: 0x7, } alternate1 = test1{ foo: 0xffff, bar: 0x7, Baz: 0x7, } alternate2 = test1{ Bar: 0x1f, Bool: true, } overflow = test1{ Bar: 0x3f, } negative = test1{ Bar: -1, } ) func TestPack(t *testing.T) { testCases := []struct { desc string x interface{} nBits uint out uint64 ok bool }{ {"maxed out fields", maxed, 0, 0xfffffff0, true}, {"maxed using less bits", maxed, 28, 0x0fffffff, true}, {"alternate1", alternate1, 0, 0xffff0770, true}, {"alternate2", alternate2, 0, 0x0000f880, true}, {"just64", &just64{0x0f0f0f0f}, 00, 0xf0f0f0f, true}, {"just64", &just64{0x0f0f0f0f}, 64, 0xf0f0f0f, true}, {"just64", &just64{0xffffFFFF}, 64, 0xffffffff, true}, {"to uint8", &toUint8{true}, 0, 0x80, true}, {"to uint16", &toUint16{1}, 0, 0x0080, true}, // errors {"overflow", overflow, 0, 0, false}, {"too many bits", &tooManyBits{}, 0, 0, false}, {"fault size", &faultySize{}, 0, 0, false}, {"fault type", &faultyType{}, 0, 0, false}, {"negative", negative, 0, 0, false}, {"not enough bits", maxed, 27, 0, false}, } for _, tc := range testCases { t.Run(fmt.Sprintf("%T/%s", tc.x, tc.desc), func(t *testing.T) { v, err := Pack(tc.x, &Config{NumBits: tc.nBits}) if ok := err == nil; v != tc.out || ok != tc.ok { t.Errorf("got %#x, %v; want %#x, %v (%v)", v, ok, tc.out, tc.ok, err) } }) } } func TestRoundtrip(t *testing.T) { testCases := []struct { x test1 }{ {maxed}, {alternate1}, {alternate2}, } for _, tc := range testCases { t.Run("", func(t *testing.T) { v, err := Pack(tc.x, nil) if err != nil { t.Fatal(err) } want := tc.x want.Foo = 0 // not stored x := myInt(v) got := test1{ foo: x.fob(), Bar: x.baz(), bar: x.bar(), Bool: x.Bool(), Baz: x.Baz(), } if got != want { t.Errorf("\ngot %#v\nwant %#v (%#x)", got, want, v) } }) } } func TestGen(t *testing.T) { testCases := []struct { desc string x interface{} config *Config ok bool out string }{{ desc: "test1", x: &test1{}, ok: true, out: test1Gen, }, { desc: "test1 with options", x: &test1{}, config: &Config{Package: "bitfield", TypeName: "myInt"}, ok: true, out: mustRead("gen1_test.go"), }, { desc: "test1 with alternative bits", x: &test1{}, config: &Config{NumBits: 28, Package: "bitfield", TypeName: "myInt2"}, ok: true, out: mustRead("gen2_test.go"), }, { desc: "failure", x: &test1{}, config: &Config{NumBits: 27}, // Too few bits. ok: false, out: "", }} for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { w := &bytes.Buffer{} err := Gen(w, tc.x, tc.config) if ok := err == nil; ok != tc.ok { t.Fatalf("got %v; want %v (%v)", ok, tc.ok, err) } got := w.String() if got != tc.out { t.Errorf("got:\n%s\nwant:\n%s", got, tc.out) } }) } } const test1Gen = `type test1 uint32 func (t test1) fob() uint16 { return uint16((t >> 16) & 0xffff) } func (t test1) baz() int8 { return int8((t >> 11) & 0x1f) } func (t test1) bar() myUint8 { return myUint8((t >> 8) & 0x7) } func (t test1) Bool() bool { const bit = 1 << 7 return t&bit == bit } func (t test1) Baz() int8 { return int8((t >> 4) & 0x7) } ` func mustRead(filename string) string { b, err := ioutil.ReadFile(filename) if err != nil { panic(err) } return string(b) }