technology from back to front

Archive for July, 2011

Rolling your own control structures with lambdas

Squeak Smalltalk ships with an, ahem, mildly controversial feature: a case statement. Case statements usually evoke “but that’s not OO!” from people, usually with good reason: a complicated case statement only gets less understandable as it evolves, while a State implementation’s complexity remains more or less constant. (You can concentrate on only the bit you care about; a nightmare case statement requires you to know about a great deal more code.)

And yet. And yet, I managed to extend Andreas Raab’s DNSClient library[1] to parse SRV and NAPTR records by adding a single statement each, thanks to a case statement.

So with my hat firmly in the pragmatist corner (as opposed to the ideologically pure corner, for a change), let’s use our new unification library, and build a pattern matching (really, a unifying) case statement.

Read more…

by
Frank Shearar
on
31/07/11

Testing go programs with gocheck

The Go programming language comes with a simple built in test framework testing – this is usable and functional but is lacking in features that you might find in other languages test frameworks. A more fully featured testing framework called gocheck has been developed by Gustavo Niemeyer, this blog post walks through developing an extremely simple go program using gocheck.

The code we will try and produce is a package for editing two dimensional images such as icons. To start with lets write a minimal implementation of the library and a test. We will use a struct that holds a two dimensional array of integers and the size of the image and add a New function so that we can hide the underlying array from consumers of the library.

package image

type Image struct {
  M, N int
  content [][]int
}

func New(M, N int) *Image {
  c := make([][]int, M, M)
  for i := 0; i < M; i++ {
    c[i] = make([]int, N, N)
  }
  return &Image{N, M, c}
}

For the first test I would really like not to be able to create images with negative sizes and would like the library to panic if that happens. gocheck allows panics to be tested for so this is our first basic test.

package image

import (
  . "launchpad.net/gocheck"
  "testing"
  "os"
)

// This plumbs gocheck into testing
func Test(t *testing.T) {
  TestingT(t)
}

// This is a fixure used by a suite of tests
type S struct{}
var _ = Suite(&S{})

// This is a test
func (s *S) TestNew(c *C) {
  c.Assert(func(){New(-1, -2)}, Panics, os.NewError("Bang"))
}

Now I run the test!

make test
gotest
rm -f _test/test.a
8g  -o _gotest_.8 image.go  image_test.go
rm -f _test/test.a
gopack grc _test/test.a _gotest_.8

----------------------------------------------------------------------
PANIC: image_test.go:16: test.S.TestNew

... Panic: runtime error: makeslice: len out of range (PC=0x8056ABA)

/home/tim/Tools/go/src/pkg/runtime/proc.c:1041
  in runtime.panic
/home/tim/Tools/go/src/pkg/runtime/runtime.c:116
  in runtime.panicstring
/home/tim/Tools/go/src/pkg/runtime/slice.c:22
  in runtime.makeslice
image.go:9
  in test.New
image_test.go:17
  in test.S.TestNew
OOPS: 0 passed, 1 PANICKED
--- FAIL: image.Test (0.01 seconds)
FAIL
gotest: "./8.out" failed: exit status 1
make: *** [test] Error 2

It has failed, a panic occurred but not the panic I expected, so lets modify the New function to produce a specific panic if negative sizes are passed in.

const newError = "An image dimension cannot be negative (M = %d  N = %d)"

func New(M, N int) *Image {
  if M < 0 || N < 0 {
    panic(fmt.Errorf(newError, M, N))
  }

  c := make([][]int, M, M)
  for i := 0; i < M; i++ {
    c[i] = make([]int, N, N)
  }
  return &Image{N, M, c}
}

Running the tests again produces a pass.

make test
gotest
rm -f _test/test.a
8g  -o _gotest_.8 image.go  image_test.go
rm -f _test/test.a
gopack grc _test/test.a _gotest_.8
OK: 1 passed
PASS

Now lets provide a function to allow values to be read from the image, to add a small wrinkle let us make the origin of the image be at (1, 1) as opposed to (0, 0). Additionally in go style let us return an error with the result in a similar way to go maps. We use two auxiliary methods and a function to check that coordinates are valid.

func check(val, limit int) bool {
  return val > 0 && val <= limit
}

func (i Image) checkX(x int) bool {
  return check(x, i.M)
}

func (i Image) checkY(y int) bool {
  return check(y, i.N)
}

func (i Image) ValueAt(x, y int) (value int, ok bool)   {
  if i.checkX(x) && i.checkY(y) {
    return i.content[x - 1][y - 1], true
  }
  return 0, false
}

The accompanying test looks like this:

func (s *S) TestNewShouldPanicWithNegatives(c *C) {
  c.Assert(func() { New(-1, -2) }, Panics, fmt.Errorf(newError, -1, -2))
}

func (s *S) TestNew(c *C) {
  c.Assert(New(1, 2), NotNil)
}


func (s *S) TestValueAt(c *C) {
  i := New(1, 2)
  v, ok := i.ValueAt(-1, 2)
  c.Assert(v, Equals, 0)
  c.Assert(ok, Equals, false)

  v, ok = i.ValueAt(1, 1)
  c.Assert(v, Equals, 0)
  c.Assert(ok, Equals, true)
}

Running this gives us another pass.

make test
gotest
rm -f _test/test.a
8g  -o _gotest_.8 image.go  image_test.go
rm -f _test/test.a
gopack grc _test/test.a _gotest_.8
OK: 3 passed
PASS

Now lets add a function to put data into the image pixel, by pixel, this function also returns a value to indicate whether the pixel value was set, if you attempt to set a pixel outside of the image it will return false.

func (i* Image) SetValueAt(x, y, v int) (ok bool) {
  if i.checkX(x) && i.checkY(y) {
    i.content[x - 1][y - 1] = v
    return true
  }
  return false
}

With an accompanying test:

func (s *S) TestSetValueAt(c *C) {
  i := New(1, 2)
  c.Assert(i.SetValueAt(-1, -2, 3), Equals, false)
  c.Assert(i.SetValueAt(1, 2, 3), Equals, true)

  v, ok := i.ValueAt(1, 2)
  c.Assert(v, Equals, 3)
  c.Assert(ok, Equals, true)
}

Which will of course pass.

make test
gotest
rm -f _test/test.a
8g  -o _gotest_.8 image.go  image_test.go
rm -f _test/test.a
gopack grc _test/test.a _gotest_.8

----------------------------------------------------------------------
FAIL: image_test.go:36: test.S.TestSetValueAt

image_test.go:39:
    c.Assert(i.SetValueAt(1, 2, 3), Equals, true)
... obtained bool = false
... expected bool = true

OOPS: 3 passed, 1 FAILED
--- FAIL: image.Test (0.01 seconds)
FAIL
gotest: "./8.out" failed: exit status 1
make: *** [test] Error 2

And I have been overconfident, aarghhhhh! why doesn’t it pass? Much pondering later I realised that I was having problems with my alphabet in my New function since I swap N for M when I construct the struct. The correct New function looks like this:

func New(M, N int) *Image {
  if M < 0 || N < 0 {
    panic(fmt.Errorf(newError, M, N))
  }

  c := make([][]int, M, M)
  for i := 0; i < M; i++ {
    c[i] = make([]int, N, N)
  }
  return &Image{M, N, c}
}

Running the tests again:

make test
gotest
rm -f _test/test.a
8g  -o _gotest_.8 image.go  image_test.go
rm -f _test/test.a
gopack grc _test/test.a _gotest_.8
OK: 4 passed
PASS

Now I refactor my test to removes some of the duplication and put some common code into the fixture.

type S struct{
  i *Image
}

func (s *S) SetUpTest(c *C) {
  s.i = New(1, 2)
}

func (s *S) TearDownTest(c *C)  {
  s.i = nil
}

func (s *S) TestValueAt(c *C) {
  v, ok := s.i.ValueAt(-1, 2)
  c.Assert(v, Equals, 0)
  c.Assert(ok, Equals, false)

  v, ok = s.i.ValueAt(1, 1)
  c.Assert(v, Equals, 0)
  c.Assert(ok, Equals, true)
}

func (s *S) TestSetValueAt(c *C) {
  c.Assert(s.i.SetValueAt(-1, -2, 3), Equals, false)
  c.Assert(s.i.SetValueAt(1, 2, 3), Equals, true)

  v, ok := s.i.ValueAt(1, 2)
  c.Assert(v, Equals, 3)
  c.Assert(ok, Equals, true)
}

Now it would be handy to actually see the image so lets make it to conform to the Stringer interface required by the various Print* functions in the fmt package. First the test:

func (s *S) TestString(c *C) {
  var stringer fmt.Stringer
  c.Assert(s.i, Implements, &stringer)
}

Which fails:

make test
gotest
rm -f _test/test.a
8g  -o _gotest_.8 image.go  image_test.go
rm -f _test/test.a
gopack grc _test/test.a _gotest_.8

----------------------------------------------------------------------
FAIL: image_test.go:54: test.S.TestString

image_test.go:56:
    c.Assert(s.i, Implements, &stringer)
... obtained *image.Image = &image.Image{M:1, N:2, content:[][]int{[]int{0, 0}}}
... ifaceptr *fmt.Stringer = (*fmt.Stringer)(0x500017a8)

OOPS: 4 passed, 1 FAILED
--- FAIL: image.Test (0.01 seconds)
FAIL
gotest: "./8.out" failed: exit status 1
make: *** [test] Error 2

I add a simple String function:

func (i Image) String() string {
  return "Image"
}

And the tests pass:

make test
gotest
rm -f _test/test.a
8g  -o _gotest_.8 image.go  image_test.go
rm -f _test/test.a
gopack grc _test/test.a _gotest_.8
OK: 5 passed
PASS

So we have some basic functionality and it is all tested. Next time I will extend this example and write a custom checker for gocheck to help out.

by
tim
on

Un Petit Haskell

It’s time to visit another Smalltalk PEG parser. We’ve seen OMeta2, and now it’s time for a rather different approach to parsing. Lukas Renggli wrote PetitParser, a parser library based on parser combinators.
Read more…

by
Frank Shearar
on
19/07/11

Search

Categories

You are currently browsing the LShift Ltd. blog archives for July, 2011.

Feeds

Archives

2000-14 LShift Ltd, 1st Floor, Hoxton Point, 6 Rufus Street, London, N1 6PE, UK+44 (0)20 7729 7060   Contact us