Skip to content

Commit

Permalink
encoder.go: 100% test coverage achieved
Browse files Browse the repository at this point in the history
Note, encoder.go was slightly refactored to remove unreachable (and so,
untestable) line of code
  • Loading branch information
alexpevzner committed Nov 18, 2023
1 parent 9b199eb commit d7e4457
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 11 deletions.
13 changes: 4 additions & 9 deletions encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,16 +150,11 @@ func (me *messageEncoder) encodeName(name string) error {
func (me *messageEncoder) encodeValue(tag Tag, v Value) error {
// Check Value type vs the Tag
tagType := tag.Type()
switch tagType {
case TypeInvalid:
return fmt.Errorf("Tag %s cannot be used for value", tag)
case TypeVoid:
if tagType == TypeVoid {
v = Void{} // Ignore supplied value
default:
if tagType != v.Type() {
return fmt.Errorf("Tag %s: %s value required, %s present",
tag, tagType, v.Type())
}
} else if tagType != v.Type() {
return fmt.Errorf("Tag %s: %s value required, %s present",
tag, tagType, v.Type())
}

// Encode the value
Expand Down
170 changes: 168 additions & 2 deletions goipp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,52 @@ package goipp

import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"reflect"
"strings"
"testing"
"time"
)

// errWriter implements io.Writer interface
//
// it accepts some first bytes and after that always returns an error
type errWriter struct{ skip int }

var _ = io.Writer(&errWriter{})

func (ewr *errWriter) Write(data []byte) (int, error) {
if len(data) <= ewr.skip {
ewr.skip -= len(data)
return len(data), nil
}

n := ewr.skip
ewr.skip = 0

return n, errors.New("I/O error")
}

// errValue implements Value interface and returns
// error from its encode() and decode() methods
type errValue struct{}

var _ = Value(errValue{})

func (errValue) String() string { return "" }
func (errValue) Type() Type { return TypeInteger }

func (errValue) encode() ([]byte, error) {
return nil, errors.New("encode error")
}

func (errValue) decode([]byte) (Value, error) {
return nil, errors.New("decode error")
}

// Check that err == nil
func assertNoError(t *testing.T, err error) {
if err != nil {
Expand All @@ -29,6 +68,18 @@ func assertWithError(t *testing.T, err error) {
}
}

// Check that err != nil and contains expected test
func assertErrorIs(t *testing.T, err error, s string) {
if err == nil {
t.Errorf("Error expected")
return
}

if !strings.HasPrefix(err.Error(), s) {
t.Errorf("Error is %q, expected %q", err, s)
}
}

// Check that value type is as specified
func assertValueType(t *testing.T, val Value, typ Type) {
if val.Type() != typ {
Expand Down Expand Up @@ -254,8 +305,9 @@ func TestVersion(t *testing.T) {
}
}

// Test Message Encode, then Decode
func TestEncodeDecode(t *testing.T) {
// testEncodeDecodeMessage creates a quite complex message
// for Encode/Decode test
func testEncodeDecodeMessage() *Message {
m1 := &Message{
Version: DefaultVersion,
Code: 0x1234,
Expand Down Expand Up @@ -322,6 +374,12 @@ func TestEncodeDecode(t *testing.T) {
TextWithLang{"привет", "ru"}))

//m1.Print(os.Stdout, false)
return m1
}

// Test Message Encode, then Decode
func TestEncodeDecode(t *testing.T) {
m1 := testEncodeDecodeMessage()

data, err := m1.EncodeBytes()
assertNoError(t, err)
Expand All @@ -331,6 +389,114 @@ func TestEncodeDecode(t *testing.T) {
assertNoError(t, err)
}

// Test encode errors
func TestEncodeErrors(t *testing.T) {
// Attribute without name
m := NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a := MakeAttribute("attr", TagInteger, Integer(123))
a.Name = ""
m.Operation.Add(a)
err := m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Attribute without name")

// Attribute without value
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagInteger, Integer(123))
a.Values = nil
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Attribute without value")

// Attribute name exceeds...
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagInteger, Integer(123))
a.Name = strings.Repeat("x", 32767)
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertNoError(t, err)

m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagInteger, Integer(123))
a.Name = strings.Repeat("x", 32767+1)
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Attribute name exceeds 32767 bytes")

// Attribute value exceeds...
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagText, String(strings.Repeat("x", 32767)))
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertNoError(t, err)

m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagText, String(strings.Repeat("x", 32767+1)))
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Attribute value exceeds 32767 bytes")

// Tag XXX cannot be used with value
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagJobGroup, Integer(123))
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Tag job-attributes-tag cannot be used with value")

m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagMemberName, Integer(123))
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Tag memberAttrName cannot be used with value")

m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagEndCollection, Integer(123))
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Tag endCollection cannot be used with value")

// Collection member without name
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagBeginCollection, Collection{
MakeAttribute("", TagInteger, Integer(123)),
})
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Collection member without name")

// Tag XXX: YYY value required, ZZZ present
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagText, Integer(123))
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Tag textWithoutLanguage: String value required, Integer present")

// I/O error
m = testEncodeDecodeMessage()

data, err := m.EncodeBytes()
assertNoError(t, err)

for skip := 0; skip < len(data); skip++ {
err = m.Encode(&errWriter{skip})
assertErrorIs(t, err, "I/O error")
}

// encode error
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagInteger, errValue{})
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "encode error")

m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagBeginCollection, Collection{
MakeAttribute("attr", TagInteger, errValue{}),
})
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "encode error")
}

// Test message decoding
func testDecode(t *testing.T, data []byte, opt DecoderOptions,
mustFail, mustEncode bool) {
Expand Down

0 comments on commit d7e4457

Please sign in to comment.