mirror of
https://github.com/pion/webrtc.git
synced 2025-10-21 22:29:25 +08:00
@@ -24,8 +24,8 @@ type Header struct {
|
||||
// some additional padding octets at the end which are not part of
|
||||
// the control information but are included in the length field.
|
||||
Padding bool
|
||||
// The number of reception report blocks contained in this packet.
|
||||
ReportCount uint8
|
||||
// The number of reception reports or sources contained in this packet (depending on the Type)
|
||||
Count uint8
|
||||
// The RTCP packet type for this packet
|
||||
Type uint8
|
||||
// The length of this RTCP packet in 32-bit words minus one,
|
||||
@@ -35,7 +35,7 @@ type Header struct {
|
||||
|
||||
var (
|
||||
errInvalidVersion = errors.New("invalid version")
|
||||
errInvalidReportCount = errors.New("invalid report count")
|
||||
errInvalidCount = errors.New("invalid count header")
|
||||
errInvalidTotalLost = errors.New("invalid total lost count")
|
||||
errPacketTooShort = errors.New("packet too short")
|
||||
)
|
||||
@@ -46,8 +46,8 @@ const (
|
||||
versionMask = 0x3
|
||||
paddingShift = 5
|
||||
paddingMask = 0x1
|
||||
reportCountShift = 0
|
||||
reportCountMask = 0x1f
|
||||
countShift = 0
|
||||
countMask = 0x1f
|
||||
)
|
||||
|
||||
// Marshal encodes the Header in binary
|
||||
@@ -70,10 +70,10 @@ func (h Header) Marshal() ([]byte, error) {
|
||||
rawPacket[0] |= 1 << paddingShift
|
||||
}
|
||||
|
||||
if h.ReportCount > 31 {
|
||||
return nil, errInvalidReportCount
|
||||
if h.Count > 31 {
|
||||
return nil, errInvalidCount
|
||||
}
|
||||
rawPacket[0] |= h.ReportCount << reportCountShift
|
||||
rawPacket[0] |= h.Count << countShift
|
||||
|
||||
rawPacket[1] = h.Type
|
||||
|
||||
@@ -98,7 +98,7 @@ func (h *Header) Unmarshal(rawPacket []byte) error {
|
||||
|
||||
h.Version = rawPacket[0] >> versionShift & versionMask
|
||||
h.Padding = (rawPacket[0] >> paddingShift & paddingMask) > 0
|
||||
h.ReportCount = rawPacket[0] >> reportCountShift & reportCountMask
|
||||
h.Count = rawPacket[0] >> countShift & countMask
|
||||
|
||||
h.Type = rawPacket[1]
|
||||
|
||||
|
@@ -48,7 +48,7 @@ func TestHeaderRoundTrip(t *testing.T) {
|
||||
Header: Header{
|
||||
Version: 2,
|
||||
Padding: true,
|
||||
ReportCount: 31,
|
||||
Count: 31,
|
||||
Type: TypeSenderReport,
|
||||
Length: 4,
|
||||
},
|
||||
@@ -58,7 +58,7 @@ func TestHeaderRoundTrip(t *testing.T) {
|
||||
Header: Header{
|
||||
Version: 1,
|
||||
Padding: false,
|
||||
ReportCount: 28,
|
||||
Count: 28,
|
||||
Type: TypeReceiverReport,
|
||||
Length: 65535,
|
||||
},
|
||||
@@ -71,11 +71,11 @@ func TestHeaderRoundTrip(t *testing.T) {
|
||||
WantError: errInvalidVersion,
|
||||
},
|
||||
{
|
||||
Name: "invalid report count",
|
||||
Name: "invalid count",
|
||||
Header: Header{
|
||||
ReportCount: 40,
|
||||
Count: 40,
|
||||
},
|
||||
WantError: errInvalidReportCount,
|
||||
WantError: errInvalidCount,
|
||||
},
|
||||
} {
|
||||
data, err := test.Header.Marshal()
|
||||
|
264
pkg/rtcp/source_description.go
Normal file
264
pkg/rtcp/source_description.go
Normal file
@@ -0,0 +1,264 @@
|
||||
package rtcp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// RTP SDES item types registered with IANA. See: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-5
|
||||
const (
|
||||
SDESEnd = iota // end of SDES list RFC 3550, 6.5
|
||||
SDESCNAME // canonical name RFC 3550, 6.5.1
|
||||
SDESName // user name RFC 3550, 6.5.2
|
||||
SDESEmail // user's electronic mail address RFC 3550, 6.5.3
|
||||
SDESPhone // user's phone number RFC 3550, 6.5.4
|
||||
SDESLocation // geographic user location RFC 3550, 6.5.5
|
||||
SDESTool // name of application or tool RFC 3550, 6.5.6
|
||||
SDESNote // notice about the source RFC 3550, 6.5.7
|
||||
SDESPrivate // private extensions RFC 3550, 6.5.8 (not implemented)
|
||||
)
|
||||
|
||||
var (
|
||||
errSDESTextTooLong = errors.New("session description must be < 255 octets long")
|
||||
errSDESMissingType = errors.New("session description item missing type")
|
||||
)
|
||||
|
||||
const (
|
||||
sdesSourceLen = 4
|
||||
sdesTypeLen = 1
|
||||
sdesTypeOffset = 0
|
||||
sdesOctetCountLen = 1
|
||||
sdesOctetCountOffset = 1
|
||||
sdesMaxOctetCount = (1 << 8) - 1
|
||||
sdesTextOffset = 2
|
||||
)
|
||||
|
||||
// A SourceDescription (SDES) packet describes the sources in an RTP stream.
|
||||
type SourceDescription struct {
|
||||
Chunks []SourceDescriptionChunk
|
||||
}
|
||||
|
||||
// Marshal encodes the SourceDescription in binary
|
||||
func (s SourceDescription) Marshal() ([]byte, error) {
|
||||
/*
|
||||
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
* chunk | SSRC/CSRC_1 |
|
||||
* 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | SDES items |
|
||||
* | ... |
|
||||
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
* chunk | SSRC/CSRC_2 |
|
||||
* 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | SDES items |
|
||||
* | ... |
|
||||
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
*/
|
||||
|
||||
rawPacket := make([]byte, 0)
|
||||
for _, c := range s.Chunks {
|
||||
data, err := c.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawPacket = append(rawPacket, data...)
|
||||
}
|
||||
|
||||
return rawPacket, nil
|
||||
}
|
||||
|
||||
// Unmarshal decodes the SourceDescription from binary
|
||||
func (s *SourceDescription) Unmarshal(rawPacket []byte) error {
|
||||
/*
|
||||
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
* chunk | SSRC/CSRC_1 |
|
||||
* 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | SDES items |
|
||||
* | ... |
|
||||
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
* chunk | SSRC/CSRC_2 |
|
||||
* 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | SDES items |
|
||||
* | ... |
|
||||
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
*/
|
||||
|
||||
for i := 0; i < len(rawPacket); {
|
||||
var chunk SourceDescriptionChunk
|
||||
if err := chunk.Unmarshal(rawPacket[i:]); err != nil {
|
||||
return err
|
||||
}
|
||||
s.Chunks = append(s.Chunks, chunk)
|
||||
|
||||
i += chunk.len()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// A SourceDescriptionChunk contains items describing a single RTP source
|
||||
type SourceDescriptionChunk struct {
|
||||
// The source (ssrc) or contributing source (csrc) identifier this packet describes
|
||||
Source uint32
|
||||
Items []SourceDescriptionItem
|
||||
}
|
||||
|
||||
// Marshal encodes the SourceDescriptionChunk in binary
|
||||
func (s SourceDescriptionChunk) Marshal() ([]byte, error) {
|
||||
/*
|
||||
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
* | SSRC/CSRC_1 |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | SDES items |
|
||||
* | ... |
|
||||
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
*/
|
||||
|
||||
rawPacket := make([]byte, sdesSourceLen)
|
||||
binary.BigEndian.PutUint32(rawPacket, s.Source)
|
||||
|
||||
for _, it := range s.Items {
|
||||
data, err := it.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawPacket = append(rawPacket, data...)
|
||||
}
|
||||
|
||||
// The list of items in each chunk MUST be terminated by one or more null octets
|
||||
rawPacket = append(rawPacket, SDESEnd)
|
||||
|
||||
// additional null octets MUST be included if needed to pad until the next 32-bit boundary
|
||||
if size := len(rawPacket); size%4 != 0 {
|
||||
padding := make([]byte, 4-size%4)
|
||||
rawPacket = append(rawPacket, padding...)
|
||||
}
|
||||
|
||||
return rawPacket, nil
|
||||
}
|
||||
|
||||
// Unmarshal decodes the SourceDescriptionChunk from binary
|
||||
func (s *SourceDescriptionChunk) Unmarshal(rawPacket []byte) error {
|
||||
/*
|
||||
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
* | SSRC/CSRC_1 |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | SDES items |
|
||||
* | ... |
|
||||
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
*/
|
||||
|
||||
if len(rawPacket) < (sdesSourceLen + sdesTypeLen) {
|
||||
return errPacketTooShort
|
||||
}
|
||||
|
||||
s.Source = binary.BigEndian.Uint32(rawPacket)
|
||||
|
||||
for i := 4; i < len(rawPacket); {
|
||||
if pktType := rawPacket[i]; pktType == SDESEnd {
|
||||
return nil
|
||||
}
|
||||
|
||||
var it SourceDescriptionItem
|
||||
if err := it.Unmarshal(rawPacket[i:]); err != nil {
|
||||
return err
|
||||
}
|
||||
s.Items = append(s.Items, it)
|
||||
i += it.len()
|
||||
}
|
||||
|
||||
return errPacketTooShort
|
||||
}
|
||||
|
||||
func (s SourceDescriptionChunk) len() int {
|
||||
len := sdesSourceLen
|
||||
for _, it := range s.Items {
|
||||
len += it.len()
|
||||
}
|
||||
len += sdesTypeLen // for terminating null octet
|
||||
|
||||
// align to 32-bit boundary
|
||||
if len%4 != 0 {
|
||||
len += 4 - (len % 4)
|
||||
}
|
||||
|
||||
return len
|
||||
}
|
||||
|
||||
// A SourceDescriptionItem is a part of a SourceDescription that describes a stream.
|
||||
type SourceDescriptionItem struct {
|
||||
// The type identifier for this item. eg, SDESCNAME for canonical name description.
|
||||
//
|
||||
// Type zero or SDESEnd is interpreted as the end of an item list and cannot be used.
|
||||
Type uint8
|
||||
// Text is a unicode text blob associated with the item. Its meaning varies based on the item's Type.
|
||||
Text string
|
||||
}
|
||||
|
||||
func (s SourceDescriptionItem) len() int {
|
||||
/*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | CNAME=1 | length | user and domain name ...
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
return sdesTypeLen + sdesOctetCountLen + len([]byte(s.Text))
|
||||
}
|
||||
|
||||
// Marshal encodes the SourceDescriptionItem in binary
|
||||
func (s SourceDescriptionItem) Marshal() ([]byte, error) {
|
||||
/*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | CNAME=1 | length | user and domain name ...
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
if s.Type == SDESEnd {
|
||||
return nil, errSDESMissingType
|
||||
}
|
||||
|
||||
rawPacket := make([]byte, sdesTypeLen+sdesOctetCountLen)
|
||||
|
||||
rawPacket[sdesTypeOffset] = s.Type
|
||||
|
||||
txtBytes := []byte(s.Text)
|
||||
octetCount := len(txtBytes)
|
||||
if octetCount > sdesMaxOctetCount {
|
||||
return nil, errSDESTextTooLong
|
||||
}
|
||||
rawPacket[sdesOctetCountOffset] = uint8(octetCount)
|
||||
|
||||
rawPacket = append(rawPacket, txtBytes...)
|
||||
|
||||
return rawPacket, nil
|
||||
}
|
||||
|
||||
// Unmarshal decodes the SourceDescriptionItem from binary
|
||||
func (s *SourceDescriptionItem) Unmarshal(rawPacket []byte) error {
|
||||
/*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | CNAME=1 | length | user and domain name ...
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
if len(rawPacket) < (sdesTypeLen + sdesOctetCountLen) {
|
||||
return errPacketTooShort
|
||||
}
|
||||
|
||||
s.Type = rawPacket[sdesTypeOffset]
|
||||
|
||||
octetCount := int(rawPacket[sdesOctetCountOffset])
|
||||
if sdesTextOffset+octetCount > len(rawPacket) {
|
||||
return errPacketTooShort
|
||||
}
|
||||
|
||||
txtBytes := rawPacket[sdesTextOffset : sdesTextOffset+octetCount]
|
||||
s.Text = string(txtBytes)
|
||||
|
||||
return nil
|
||||
}
|
299
pkg/rtcp/source_description_test.go
Normal file
299
pkg/rtcp/source_description_test.go
Normal file
@@ -0,0 +1,299 @@
|
||||
package rtcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSourceDescriptionUnmarshal(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
Name string
|
||||
Data []byte
|
||||
Want SourceDescription
|
||||
WantError error
|
||||
}{
|
||||
{
|
||||
Name: "nil",
|
||||
Data: nil,
|
||||
Want: SourceDescription{
|
||||
Chunks: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "missing type",
|
||||
Data: []byte{
|
||||
// ssrc=0x00000000
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
WantError: errPacketTooShort,
|
||||
},
|
||||
{
|
||||
Name: "bad cname length",
|
||||
Data: []byte{
|
||||
// ssrc=0x00000000
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
// CNAME, len = 1
|
||||
0x01, 0x01,
|
||||
},
|
||||
WantError: errPacketTooShort,
|
||||
},
|
||||
{
|
||||
Name: "short cname",
|
||||
Data: []byte{
|
||||
// ssrc=0x00000000
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
// CNAME, Missing length
|
||||
0x01,
|
||||
},
|
||||
WantError: errPacketTooShort,
|
||||
},
|
||||
{
|
||||
Name: "no end",
|
||||
Data: []byte{
|
||||
// ssrc=0x00000000
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
// CNAME, len=1, content=A
|
||||
0x01, 0x02, 0x41,
|
||||
// Missing END
|
||||
},
|
||||
WantError: errPacketTooShort,
|
||||
},
|
||||
{
|
||||
Name: "bad octet count",
|
||||
Data: []byte{0, 0, 0, 0, 1, 1},
|
||||
WantError: errPacketTooShort,
|
||||
},
|
||||
{
|
||||
Name: "zero item chunk",
|
||||
Data: []byte{
|
||||
// ssrc=0x01020304
|
||||
0x01, 0x02, 0x03, 0x04,
|
||||
// END + padding
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
},
|
||||
Want: SourceDescription{
|
||||
Chunks: []SourceDescriptionChunk{{
|
||||
Source: 0x01020304,
|
||||
Items: nil,
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "empty string",
|
||||
Data: []byte{
|
||||
// ssrc=0x01020304
|
||||
0x01, 0x02, 0x03, 0x04,
|
||||
// CNAME, len=0
|
||||
0x01, 0x00,
|
||||
// END + padding
|
||||
0x00, 0x00,
|
||||
},
|
||||
Want: SourceDescription{
|
||||
Chunks: []SourceDescriptionChunk{{
|
||||
Source: 0x01020304,
|
||||
Items: []SourceDescriptionItem{
|
||||
{
|
||||
Type: SDESCNAME,
|
||||
Text: "",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "two items",
|
||||
Data: []byte{
|
||||
// ssrc=0x10000000
|
||||
0x10, 0x00, 0x00, 0x00,
|
||||
// CNAME, len=1, content=A
|
||||
0x01, 0x01, 0x41,
|
||||
// PHONE, len=1, content=B
|
||||
0x04, 0x01, 0x42,
|
||||
// END + padding
|
||||
0x00, 0x00,
|
||||
},
|
||||
Want: SourceDescription{
|
||||
Chunks: []SourceDescriptionChunk{
|
||||
{
|
||||
Source: 0x10000000,
|
||||
Items: []SourceDescriptionItem{
|
||||
{
|
||||
Type: SDESCNAME,
|
||||
Text: "A",
|
||||
},
|
||||
{
|
||||
Type: SDESPhone,
|
||||
Text: "B",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "two chunks",
|
||||
Data: []byte{
|
||||
// ssrc=0x01020304
|
||||
0x01, 0x02, 0x03, 0x04,
|
||||
// Chunk 1
|
||||
// CNAME, len=1, content=A
|
||||
0x01, 0x01, 0x41,
|
||||
// END
|
||||
0x00,
|
||||
// Chunk 2
|
||||
// SSRC 0x05060708
|
||||
0x05, 0x06, 0x07, 0x08,
|
||||
// CNAME, len=3, content=BCD
|
||||
0x01, 0x03, 0x42, 0x43, 0x44,
|
||||
// END
|
||||
0x00, 0x00, 0x00,
|
||||
},
|
||||
Want: SourceDescription{
|
||||
Chunks: []SourceDescriptionChunk{
|
||||
{
|
||||
Source: 0x01020304,
|
||||
Items: []SourceDescriptionItem{
|
||||
{
|
||||
Type: SDESCNAME,
|
||||
Text: "A",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Source: 0x05060708,
|
||||
Items: []SourceDescriptionItem{
|
||||
{
|
||||
Type: SDESCNAME,
|
||||
Text: "BCD",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
var sdes SourceDescription
|
||||
err := sdes.Unmarshal(test.Data)
|
||||
if got, want := err, test.WantError; got != want {
|
||||
t.Fatalf("Unmarshal %q: err = %v, want %v", test.Name, got, want)
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if got, want := sdes, test.Want; !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("Unmarshal %q: got %#v, want %#v", test.Name, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceDescriptionRoundTrip(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
Name string
|
||||
Desc SourceDescription
|
||||
WantError error
|
||||
}{
|
||||
{
|
||||
Name: "valid",
|
||||
Desc: SourceDescription{
|
||||
Chunks: []SourceDescriptionChunk{
|
||||
{
|
||||
Source: 1,
|
||||
Items: []SourceDescriptionItem{
|
||||
{
|
||||
Type: SDESCNAME,
|
||||
Text: "test@example.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Source: 2,
|
||||
Items: []SourceDescriptionItem{
|
||||
{
|
||||
Type: SDESNote,
|
||||
Text: "some note",
|
||||
},
|
||||
{
|
||||
Type: SDESNote,
|
||||
Text: "another note",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "item without type",
|
||||
Desc: SourceDescription{
|
||||
Chunks: []SourceDescriptionChunk{{
|
||||
Source: 1,
|
||||
Items: []SourceDescriptionItem{{
|
||||
Text: "test@example.com",
|
||||
}},
|
||||
}},
|
||||
},
|
||||
WantError: errSDESMissingType,
|
||||
},
|
||||
{
|
||||
Name: "zero items",
|
||||
Desc: SourceDescription{
|
||||
Chunks: []SourceDescriptionChunk{{
|
||||
Source: 1,
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "email item",
|
||||
Desc: SourceDescription{
|
||||
Chunks: []SourceDescriptionChunk{{
|
||||
Source: 1,
|
||||
Items: []SourceDescriptionItem{{
|
||||
Type: SDESEmail,
|
||||
Text: "test@example.com",
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "empty text",
|
||||
Desc: SourceDescription{
|
||||
Chunks: []SourceDescriptionChunk{{
|
||||
Source: 1,
|
||||
Items: []SourceDescriptionItem{{
|
||||
Type: SDESCNAME,
|
||||
Text: "",
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "text too long",
|
||||
Desc: SourceDescription{
|
||||
Chunks: []SourceDescriptionChunk{{
|
||||
Items: []SourceDescriptionItem{{
|
||||
Type: SDESCNAME,
|
||||
Text: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
}},
|
||||
}},
|
||||
},
|
||||
WantError: errSDESTextTooLong,
|
||||
},
|
||||
} {
|
||||
data, err := test.Desc.Marshal()
|
||||
if got, want := err, test.WantError; got != want {
|
||||
t.Fatalf("Marshal %q: err = %v, want %v", test.Name, got, want)
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var decoded SourceDescription
|
||||
if err := decoded.Unmarshal(data); err != nil {
|
||||
t.Fatalf("Unmarshal %q: %v", test.Name, err)
|
||||
}
|
||||
|
||||
if got, want := decoded, test.Desc; !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("%q sdes round trip: got %#v, want %#v", test.Name, got, want)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user