Adding basic iconv unit tests and an untested (yet) writer implementation
This commit is contained in:
parent
5ea739d3eb
commit
8c9fe240c5
116
iconv_test.go
Normal file
116
iconv_test.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package iconv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type iconvTest struct {
|
||||||
|
description string
|
||||||
|
input string
|
||||||
|
inputEncoding string
|
||||||
|
output string
|
||||||
|
outputEncoding string
|
||||||
|
bytesRead int
|
||||||
|
bytesWritten int
|
||||||
|
err Error
|
||||||
|
}
|
||||||
|
|
||||||
|
var iconvTests = []iconvTest {
|
||||||
|
iconvTest{
|
||||||
|
"simple utf-8 to latin1 conversion success",
|
||||||
|
"Hello World!", "utf-8",
|
||||||
|
"Hello World!", "latin1",
|
||||||
|
12, 12, nil,
|
||||||
|
},
|
||||||
|
iconvTest{
|
||||||
|
"invalid source encoding causes EINVAL",
|
||||||
|
"", "doesnotexist",
|
||||||
|
"", "utf-8",
|
||||||
|
0, 0, EINVAL,
|
||||||
|
},
|
||||||
|
iconvTest{
|
||||||
|
"invalid destination encoding causes EINVAL",
|
||||||
|
"", "utf-8",
|
||||||
|
"", "doesnotexist",
|
||||||
|
0, 0, EINVAL,
|
||||||
|
},
|
||||||
|
iconvTest{
|
||||||
|
"invalid input sequence causes EILSEQ",
|
||||||
|
"\xFF", "utf-8",
|
||||||
|
"", "latin1",
|
||||||
|
0, 0, EILSEQ,
|
||||||
|
},
|
||||||
|
iconvTest{
|
||||||
|
"invalid input causes partial output and EILSEQ",
|
||||||
|
"Hello\xFF", "utf-8",
|
||||||
|
"Hello", "latin1",
|
||||||
|
5, 5, EILSEQ,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertString(t *testing.T) {
|
||||||
|
for _, test := range iconvTests {
|
||||||
|
// perform the conversion
|
||||||
|
output, err := ConvertString(test.input, test.inputEncoding, test.outputEncoding)
|
||||||
|
|
||||||
|
// check that output and err match
|
||||||
|
if output != test.output {
|
||||||
|
t.Errorf("test \"%s\" failed, output did not match expected", test.description)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that err is same as expected
|
||||||
|
if err != test.err {
|
||||||
|
if test.err != nil {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test \"%s\" failed, got %s when expecting %s", test.description, err, test.err)
|
||||||
|
} else {
|
||||||
|
t.Errorf("test \"%s\" failed, got nil when expecting %s", test.description, test.err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("test \"%s\" failed, got unexpected error: %s", test.description, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvert(t *testing.T) {
|
||||||
|
for _, test := range iconvTests {
|
||||||
|
// setup input buffer
|
||||||
|
input := []byte(test.input)
|
||||||
|
|
||||||
|
// setup a buffer as large as the expected bytesWritten
|
||||||
|
output := make([]byte, 50)
|
||||||
|
|
||||||
|
// peform the conversion
|
||||||
|
bytesRead, bytesWritten, err := Convert(input, output, test.inputEncoding, test.outputEncoding)
|
||||||
|
|
||||||
|
// check that bytesRead is same as expected
|
||||||
|
if bytesRead != test.bytesRead {
|
||||||
|
t.Errorf("test \"%s\" failed, bytesRead did not match expected", test.description)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that bytesWritten is same as expected
|
||||||
|
if bytesWritten != test.bytesWritten {
|
||||||
|
t.Errorf("test \"%s\" failed, bytesWritten did not match expected", test.description)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check output bytes against expected - simplest to convert output to
|
||||||
|
// string and then do an equality check which is actually a byte wise operation
|
||||||
|
if string(output[:bytesWritten]) != test.output {
|
||||||
|
t.Errorf("test \"%s\" failed, output did not match expected", test.description)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that err is same as expected
|
||||||
|
if err != test.err {
|
||||||
|
if test.err != nil {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test \"%s\" failed, got %s when expecting %s", test.description, err, test.err)
|
||||||
|
} else {
|
||||||
|
t.Errorf("test \"%s\" failed, got nil when expecting %s", test.description, test.err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("test \"%s\" failed, got unexpected error: %s", test.description, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
85
writer.go
Normal file
85
writer.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package iconv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Writer struct {
|
||||||
|
destination io.Writer
|
||||||
|
converter *Converter
|
||||||
|
buffer []byte
|
||||||
|
readPos, writePos int
|
||||||
|
err os.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWriter(destination io.Writer, fromEncoding string, toEncoding string) (*Writer, os.Error) {
|
||||||
|
// create a converter
|
||||||
|
converter, err := NewConverter(fromEncoding, toEncoding)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return NewWriterFromConverter(destination, converter), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the error
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWriterFromConverter(destination io.Writer, converter *Converter) (writer *Writer) {
|
||||||
|
writer = new(Writer)
|
||||||
|
|
||||||
|
// copy elements
|
||||||
|
writer.destination = destination
|
||||||
|
writer.converter = converter
|
||||||
|
|
||||||
|
// create 8K buffers
|
||||||
|
writer.buffer = make([]byte, 8 * 1024)
|
||||||
|
|
||||||
|
return writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Writer) emptyBuffer() {
|
||||||
|
// write new data out of buffer
|
||||||
|
bytesWritten, err := this.destination.Write(this.buffer[this.readPos:this.writePos])
|
||||||
|
|
||||||
|
// update read position
|
||||||
|
this.readPos += bytesWritten
|
||||||
|
|
||||||
|
// slide existing data to beginning
|
||||||
|
if this.readPos > 0 {
|
||||||
|
// copy current bytes - is this guaranteed safe?
|
||||||
|
copy(this.buffer, this.buffer[this.readPos:this.writePos])
|
||||||
|
|
||||||
|
// adjust positions
|
||||||
|
this.writePos -= this.readPos
|
||||||
|
this.readPos = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// track any reader error / EOF
|
||||||
|
if err != nil {
|
||||||
|
this.err = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// implement the io.Writer interface
|
||||||
|
func (this *Writer) Write(p []byte) (n int, err os.Error) {
|
||||||
|
// write data into our internal buffer
|
||||||
|
bytesRead, bytesWritten, err := this.converter.Convert(p, this.buffer[this.writePos:])
|
||||||
|
|
||||||
|
// update bytes written for return
|
||||||
|
n += bytesRead
|
||||||
|
this.writePos += bytesWritten
|
||||||
|
|
||||||
|
// checks for when we have a full buffer
|
||||||
|
for this.writePos > 0 {
|
||||||
|
// if we have an error, just return it
|
||||||
|
if this.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// else empty the buffer
|
||||||
|
this.emptyBuffer()
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user