Skip to content

Commit

Permalink
Change errors to include details
Browse files Browse the repository at this point in the history
  • Loading branch information
sorairolake committed Apr 6, 2024
1 parent 64ae7f6 commit 32b0337
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 91 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ All notable changes to this project will be documented in this file.
The format is based on https://keepachangelog.com/[Keep a Changelog], and this
project adheres to https://semver.org/[Semantic Versioning].

== {compare-url}/v0.2.0\...HEAD[Unreleased]

=== Changed

* Change errors to include details ({pull-request-url}/8[#8])

== {compare-url}/v0.1.0\...v0.2.0[0.2.0] - 2024-04-05

=== Added
Expand Down
123 changes: 83 additions & 40 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,86 @@ package lzip

import "errors"

var (
// ErrInvalidMagic represents an error due to the magic number was
// invalid.
ErrInvalidMagic = errors.New("lzip: invalid magic number")

// ErrUnsupportedVersion represents an error due to the version number
// stored in the header indicated the lzip format which is not
// supported by this package.
ErrUnsupportedVersion = errors.New("lzip: unsupported version number")

// ErrUnknownVersion represents an error due to the version number
// stored in the header was not recognized by this package.
ErrUnknownVersion = errors.New("lzip: unknown version number")

// ErrDictSizeTooSmall represents an error due to the dictionary size
// was smaller than 4 KiB.
ErrDictSizeTooSmall = errors.New("lzip: dictionary size is too small")

// ErrDictSizeTooLarge represents an error due to the dictionary size
// was larger than 512 MiB.
ErrDictSizeTooLarge = errors.New("lzip: dictionary size is too large")

// ErrInvalidCRC represents an error due to a CRC of the original
// uncompressed data mismatched.
ErrInvalidCRC = errors.New("lzip: CRC mismatch")

// ErrInvalidDataSize represents an error due to the size of the
// original uncompressed data stored in the trailer and the actual size
// of it mismatched.
ErrInvalidDataSize = errors.New("lzip: data size mismatch")

// ErrInvalidMemberSize represents an error due to the total size of
// the member stored in the trailer and the actual total size of it
// mismatched.
ErrInvalidMemberSize = errors.New("lzip: member size mismatch")

// ErrDictSizeTooLarge represents an error due to the member size was
// larger than 2 PiB.
ErrMemberSizeTooLarge = errors.New("lzip: member size is too large")
)
// ErrInvalidMagic represents an error due to the magic number was invalid.
var ErrInvalidMagic = errors.New("lzip: invalid magic number")

// UnsupportedVersionError represents an error due to the version number stored
// in the header indicated the lzip format which is not supported by this
// package.
type UnsupportedVersionError struct {
Version uint8
}

func (e *UnsupportedVersionError) Error() string {
return "lzip: unsupported version number"
}

// UnknownVersionError represents an error due to the version number stored in
// the header was not recognized by this package.
type UnknownVersionError struct {
Version uint8
}

func (e *UnknownVersionError) Error() string {
return "lzip: unknown version number"
}

// DictSizeTooSmallError represents an error due to the dictionary size was
// smaller than 4 KiB.
type DictSizeTooSmallError struct {
DictSize uint32
}

func (e *DictSizeTooSmallError) Error() string {
return "lzip: dictionary size is too small"
}

// DictSizeTooLargeError represents an error due to the dictionary size was
// larger than 512 MiB.
type DictSizeTooLargeError struct {
DictSize uint32
}

func (e *DictSizeTooLargeError) Error() string {
return "lzip: dictionary size is too large"
}

// InvalidCRCError represents an error due to a CRC of the original
// uncompressed data mismatched.
type InvalidCRCError struct {
CRC uint32
}

func (e *InvalidCRCError) Error() string {
return "lzip: CRC mismatch"
}

// InvalidDataSizeError represents an error due to the size of the original
// uncompressed data stored in the trailer and the actual size of it mismatched.
type InvalidDataSizeError struct {
DataSize uint64
}

func (e *InvalidDataSizeError) Error() string {
return "lzip: data size mismatch"
}

// InvalidMemberSizeError represents an error due to the total size of the
// member stored in the trailer and the actual total size of it mismatched.
type InvalidMemberSizeError struct {
MemberSize uint64
}

func (e *InvalidMemberSizeError) Error() string {
return "lzip: member size mismatch"
}

// MemberSizeTooLargeError represents an error due to the member size was
// larger than 2 PiB.
type MemberSizeTooLargeError struct {
MemberSize uint64
}

func (e *MemberSizeTooLargeError) Error() string {
return "lzip: member size is too large"
}
65 changes: 49 additions & 16 deletions error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package lzip_test

import (
"math"
"testing"

"github.com/sorairolake/lzip-go"
Expand All @@ -21,90 +22,122 @@ func TestErrInvalidMagic(t *testing.T) {
}
}

func TestErrUnsupportedVersion(t *testing.T) {
func TestUnsupportedVersionError(t *testing.T) {
t.Parallel()

err := lzip.ErrUnsupportedVersion
err := lzip.UnsupportedVersionError{0}
expected := "lzip: unsupported version number"

if err.Error() != expected {
t.Error("unexpected error message")
}

if v := err.Version; v != 0 {
t.Errorf("expected unsupported version number `%v`, got `%v`", 0, v)
}
}

func TestErrUnknownVersion(t *testing.T) {
func TestUnknownVersionError(t *testing.T) {
t.Parallel()

err := lzip.ErrUnknownVersion
err := lzip.UnknownVersionError{math.MaxUint8}
expected := "lzip: unknown version number"

if err.Error() != expected {
t.Error("unexpected error message")
}

if v := err.Version; v != math.MaxUint8 {
t.Errorf("expected unknown version number `%v`, got `%v`", math.MaxUint8, v)
}
}

func TestErrDictSizeTooSmall(t *testing.T) {
func TestDictSizeTooSmallError(t *testing.T) {
t.Parallel()

err := lzip.ErrDictSizeTooSmall
err := lzip.DictSizeTooSmallError{lzip.MinDictSize - 1}
expected := "lzip: dictionary size is too small"

if err.Error() != expected {
t.Error("unexpected error message")
}

if size := err.DictSize; size != (lzip.MinDictSize - 1) {
t.Errorf("expected too small dictionary size `%v`, got `%v`", lzip.MinDictSize-1, size)
}
}

func TestErrDictSizeTooLarge(t *testing.T) {
func TestDictSizeTooLargeError(t *testing.T) {
t.Parallel()

err := lzip.ErrDictSizeTooLarge
err := lzip.DictSizeTooLargeError{lzip.MaxDictSize + 1}
expected := "lzip: dictionary size is too large"

if err.Error() != expected {
t.Error("unexpected error message")
}

if size := err.DictSize; size != (lzip.MaxDictSize + 1) {
t.Errorf("expected too large dictionary size `%v`, got `%v`", lzip.MaxDictSize+1, size)
}
}

func TestErrInvalidCRC(t *testing.T) {
func TestInvalidCRCError(t *testing.T) {
t.Parallel()

err := lzip.ErrInvalidCRC
err := lzip.InvalidCRCError{0}
expected := "lzip: CRC mismatch"

if err.Error() != expected {
t.Error("unexpected error message")
}

if crc := err.CRC; crc != 0 {
t.Errorf("expected invalid CRC `%v`, got `%v`", 0, crc)
}
}

func TestErrInvalidDataSize(t *testing.T) {
func TestInvalidDataSizeError(t *testing.T) {
t.Parallel()

err := lzip.ErrInvalidDataSize
err := lzip.InvalidDataSizeError{0}
expected := "lzip: data size mismatch"

if err.Error() != expected {
t.Error("unexpected error message")
}

if size := err.DataSize; size != 0 {
t.Errorf("expected invalid data size `%v`, got `%v`", 0, size)
}
}

func TestErrInvalidMemberSize(t *testing.T) {
func TestInvalidMemberSizeError(t *testing.T) {
t.Parallel()

err := lzip.ErrInvalidMemberSize
err := lzip.InvalidMemberSizeError{0}
expected := "lzip: member size mismatch"

if err.Error() != expected {
t.Error("unexpected error message")
}

if size := err.MemberSize; size != 0 {
t.Errorf("expected invalid member size `%v`, got `%v`", 0, size)
}
}

func TestErrMemberSizeTooLarge(t *testing.T) {
func TestMemberSizeTooLargeError(t *testing.T) {
t.Parallel()

err := lzip.ErrMemberSizeTooLarge
err := lzip.MemberSizeTooLargeError{lzip.MaxMemberSize + 1}
expected := "lzip: member size is too large"

if err.Error() != expected {
t.Error("unexpected error message")
}

if size := err.MemberSize; size != (lzip.MaxMemberSize + 1) {
t.Errorf("expected too large member size `%v`, got `%v`", lzip.MaxMemberSize+1, size)
}
}
18 changes: 9 additions & 9 deletions reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,20 @@ func NewReader(r io.Reader) (*Reader, error) {

switch v := header[4]; v {
case 0:
return nil, ErrUnsupportedVersion
return nil, &UnsupportedVersionError{v}
case 1:
default:
return nil, ErrUnknownVersion
return nil, &UnknownVersionError{v}
}

dictSize := uint32(1 << (header[5] & 0x1f))
dictSize -= (dictSize / 16) * uint32((header[5]>>5)&0x07)

switch {
case dictSize < MinDictSize:
return nil, ErrDictSizeTooSmall
return nil, &DictSizeTooSmallError{dictSize}
case dictSize > MaxDictSize:
return nil, ErrDictSizeTooLarge
return nil, &DictSizeTooLargeError{dictSize}
}

rb, err := io.ReadAll(r)
Expand All @@ -66,8 +66,8 @@ func NewReader(r io.Reader) (*Reader, error) {
copy(lzmaHeader[5:], rb[len(rb)-16:len(rb)-8])

z.trailer.memberSize = uint64(headerSize + len(rb))
if z.trailer.memberSize > MaxMemberSize {
return nil, ErrMemberSizeTooLarge
if memberSize := z.trailer.memberSize; memberSize > MaxMemberSize {
return nil, &MemberSizeTooLargeError{memberSize}
}

rb = slices.Concat(lzmaHeader[:], rb)
Expand Down Expand Up @@ -106,17 +106,17 @@ func (z *Reader) Read(p []byte) (n int, err error) {

crc := binary.LittleEndian.Uint32(trailer[:4])
if crc != z.trailer.crc {
return n, ErrInvalidCRC
return n, &InvalidCRCError{crc}
}

dataSize := binary.LittleEndian.Uint64(trailer[4:12])
if dataSize != z.trailer.dataSize {
return n, ErrInvalidDataSize
return n, &InvalidDataSizeError{dataSize}
}

memberSize := binary.LittleEndian.Uint64(trailer[12:])
if memberSize != z.trailer.memberSize {
return n, ErrInvalidMemberSize
return n, &InvalidMemberSizeError{memberSize}
}
}

Expand Down
32 changes: 28 additions & 4 deletions reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,20 @@ func TestReaderUnknownVersion(t *testing.T) {
t.Fatal(err)
}

if _, err := lzip.NewReader(file); !errors.Is(err, lzip.ErrUnknownVersion) {
t.Error("unexpected error type")
_, err = lzip.NewReader(file)
if err == nil {
t.Fatal("unexpected success")
}

var unknownVersionError *lzip.UnknownVersionError
if !errors.As(err, &unknownVersionError) {
t.Fatal("unexpected error type")
}

const expected = 2

if v := unknownVersionError.Version; v != expected {
t.Errorf("expected unrecognized version number `%v`, got `%v`", expected, v)
}
}

Expand All @@ -76,7 +88,19 @@ func TestReaderDictSizeTooSmall(t *testing.T) {
t.Fatal(err)
}

if _, err := lzip.NewReader(file); !errors.Is(err, lzip.ErrDictSizeTooSmall) {
t.Error("unexpected error type")
_, err = lzip.NewReader(file)
if err == nil {
t.Fatal("unexpected success")
}

var dictSizeTooSmallError *lzip.DictSizeTooSmallError
if !errors.As(err, &dictSizeTooSmallError) {
t.Fatal("unexpected error type")
}

const expected = 1 << 11

if size := dictSizeTooSmallError.DictSize; size != expected {
t.Errorf("expected too small dictionary size `%v`, got `%v`", expected, size)
}
}
Loading

0 comments on commit 32b0337

Please sign in to comment.