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