From db2fcaeef7deaf725bd72e9ee5f57c5359ee465b Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Tue, 4 Aug 2015 11:55:17 -0700 Subject: [PATCH 01/16] Don't convert return value of Clone(). Have Clone return an Obj rather than an interface{}, and override the default binding which would convert some Clownfish types to Go types. --- runtime/go/build.go | 1 + runtime/go/clownfish/clownfish.go | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/runtime/go/build.go b/runtime/go/build.go index 71a8c6b6..71c668a4 100644 --- a/runtime/go/build.go +++ b/runtime/go/build.go @@ -137,6 +137,7 @@ func runCFC() { func specMethods(parcel *cfc.Parcel) { objBinding := cfc.NewGoClass(parcel, "Clownfish::Obj") objBinding.SpecMethod("", "TOPTR() uintptr") + objBinding.SpecMethod("Clone", "Clone() Obj") objBinding.Register() errBinding := cfc.NewGoClass(parcel, "Clownfish::Err") diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go index d5e31900..35cb4705 100644 --- a/runtime/go/clownfish/clownfish.go +++ b/runtime/go/clownfish/clownfish.go @@ -160,6 +160,12 @@ func (o *ObjIMP) TOPTR() uintptr { return o.ref } +func (o *ObjIMP)Clone() Obj { + self := (*C.cfish_Obj)(unsafe.Pointer(o.TOPTR())) + dupe := C.CFISH_Obj_Clone(self) + return WRAPAny(unsafe.Pointer(dupe)).(Obj) +} + func certifyCF(value interface{}, class *C.cfish_Class) { cfObj, ok := value.(Obj) if ok { From 6f711206b49c8d8cae119cb37b632d38d138e157 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Tue, 4 Aug 2015 11:56:53 -0700 Subject: [PATCH 02/16] Add tests for Vector Go bindings. --- runtime/go/clownfish/vector_test.go | 190 ++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 runtime/go/clownfish/vector_test.go diff --git a/runtime/go/clownfish/vector_test.go b/runtime/go/clownfish/vector_test.go new file mode 100644 index 00000000..719b5d18 --- /dev/null +++ b/runtime/go/clownfish/vector_test.go @@ -0,0 +1,190 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package clownfish + +import "testing" +import "reflect" + +func TestVecPushPop(t *testing.T) { + vec := NewVector(1) + vec.Push("foo") + got := vec.Pop() + expected := "foo" + if !reflect.DeepEqual(got, expected) { + t.Errorf("Expected %v, got %v", expected, got) + } +} + +func TestVecPushAllSlice(t *testing.T) { + vec := NewVector(1) + strings := []interface{}{"foo", "bar", "baz"} + vec.PushAll(strings) + got := vec.Slice(0, 3) + if !reflect.DeepEqual(got, strings) { + t.Errorf("Expected %v, got %v", strings, got) + } +} + +func TestVecStoreFetch(t *testing.T) { + vec := NewVector(0) + vec.Store(2, "foo") + got := vec.Fetch(2) + if !reflect.DeepEqual(got, "foo") { + t.Errorf("Expected \"foo\", got %v", got) + } +} + +func TestVecInsert(t *testing.T) { + vec := NewVector(0) + vec.Push("foo") + vec.Push("bar") + vec.Insert(1, "between") + expected := []interface{}{"foo", "between", "bar"} + got := vec.Slice(0, 10) + if !reflect.DeepEqual(got, expected) { + t.Errorf("Expected %v, got %v", expected, got) + } +} + +func TestVecInsertAll(t *testing.T) { + vec := NewVector(0) + vec.Push("foo") + vec.Push("bar") + vec.InsertAll(1, []interface{}{"a", "b"}) + expected := []interface{}{"foo", "a", "b", "bar"} + got := vec.Slice(0, 10) + if !reflect.DeepEqual(got, expected) { + t.Errorf("Expected %v, got %v", expected, got) + } +} + +func TestVecDelete(t *testing.T) { + vec := NewVector(0) + vec.PushAll([]interface{}{"a", "b", "c"}) + deleted := vec.Delete(1) + if deleted != "b" { + t.Errorf("Delete() returned '%v' rather than the expected \"b\"") + } + got := vec.Slice(0, 10) + expected := []interface{}{"a", nil, "c"} + if !reflect.DeepEqual(got, expected) { + t.Errorf("Expected %v, got %v", expected, got) + } +} + +func TestVecExcise(t *testing.T) { + vec := NewVector(0) + vec.PushAll([]interface{}{"a", "b", "c"}) + vec.Excise(1, 1) + got := vec.Slice(0, 10) + expected := []interface{}{"a", "c"} + if !reflect.DeepEqual(got, expected) { + t.Errorf("Expected %v, got %v", expected, got) + } +} + +func TestVecSort(t *testing.T) { + vec := NewVector(0) + vec.PushAll([]interface{}{3, 1, 2}) + vec.Sort() + got := vec.Slice(0, 10) + expected := []interface{}{int64(1), int64(2), int64(3)} + if !reflect.DeepEqual(got, expected) { + t.Errorf("Expected %v, got %v", expected, got) + } +} + +func TestVecResize(t *testing.T) { + vec := NewVector(0) + vec.PushAll([]interface{}{"foo"}) + vec.Resize(3) + got := vec.Slice(0, 10) + expected := []interface{}{"foo", nil, nil} + if !reflect.DeepEqual(got, expected) { + t.Errorf("Expected %v, got %v", expected, got) + } +} + +func TestVecClone(t *testing.T) { + vec := NewVector(0) + charBuf := NewCharBuf(0) + vec.Push(charBuf) + clone := vec.Clone().(Vector) + if clone.TOPTR() == vec.TOPTR() { + t.Error("Clone returned self") + } + fetched := clone.Fetch(0).(CharBuf) + if fetched.TOPTR() != charBuf.TOPTR() { + t.Error("Clone should be shallow") + } +} + +func TestVecGetSize(t *testing.T) { + vec := NewVector(10) + size := vec.GetSize() + if size != 0 { + t.Errorf("Unexpected size: %v", size) + } + vec.Resize(5) + size = vec.GetSize() + if size != 5 { + t.Errorf("Unexpected size after resizing: %v", size) + } +} + +func TestVecGetCapacity(t *testing.T) { + vec := NewVector(10) + cap := vec.GetCapacity() + if cap != 10 { + t.Errorf("Unexpected capacity: %v", cap) + } +} + +func TestVecClear(t *testing.T) { + vec := NewVector(0) + vec.Push("foo") + vec.Clear() + got := vec.Slice(0, 10) + expected := []interface{}{} + if !reflect.DeepEqual(got, expected) { + t.Errorf("Expected %v, got %v", expected, got) + } +} + +func TestVecEquals(t *testing.T) { + vec := NewVector(0) + other := NewVector(0) + vec.Push("foo") + other.Push("foo") + if !vec.Equals(other) { + t.Error("Equals should succeed") + } + other.Push("bar") + if vec.Equals(other) { + t.Error("Equals should fail") + } + if !vec.Equals([]interface{}{"foo"}) { + t.Error("Equals should succeed against a slice") + } + hash := NewHash(0) + if vec.Equals(hash) { + t.Error("Equals should return false for another Clownfish type") + } + if vec.Equals(1) { + t.Error("Equals should return false for a different Go type.") + } +} From f6d4cfbda4b5ca306d0b6d3416cb680ce7b14601 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Tue, 4 Aug 2015 14:56:00 -0700 Subject: [PATCH 03/16] Change Go signature for Hash.Keys(). Return a slice of strings instead of a slice of empty-interface. --- runtime/go/build.go | 1 + runtime/go/clownfish/clownfish.go | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/runtime/go/build.go b/runtime/go/build.go index 71c668a4..dd6b1f3e 100644 --- a/runtime/go/build.go +++ b/runtime/go/build.go @@ -155,6 +155,7 @@ func specMethods(parcel *cfc.Parcel) { vecBinding.Register() hashBinding := cfc.NewGoClass(parcel, "Clownfish::Hash") + hashBinding.SpecMethod("Keys", "Keys() []string") hashBinding.SetSuppressCtor(true) hashBinding.Register() } diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go index 35cb4705..4da8c824 100644 --- a/runtime/go/clownfish/clownfish.go +++ b/runtime/go/clownfish/clownfish.go @@ -146,6 +146,18 @@ func NewHash(size int) Hash { return WRAPHash(unsafe.Pointer(cfObj)) } +func (h *HashIMP) Keys() []string { + self := (*C.cfish_Hash)(unsafe.Pointer(h.TOPTR())) + keysCF := C.CFISH_Hash_Keys(self) + numKeys := C.CFISH_Vec_Get_Size(keysCF) + keys := make([]string, 0, int(numKeys)) + for i := C.size_t(0); i < numKeys; i++ { + keys = append(keys, CFStringToGo(unsafe.Pointer(C.CFISH_Vec_Fetch(keysCF, i)))) + } + C.cfish_decref(unsafe.Pointer(keysCF)) + return keys +} + func (o *ObjIMP) INITOBJ(ptr unsafe.Pointer) { o.ref = uintptr(ptr) runtime.SetFinalizer(o, ClearRef) From ea6963b1cda260bbf4470d47d2f9c651b33dbd7b Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Tue, 4 Aug 2015 15:09:35 -0700 Subject: [PATCH 04/16] Add tests for Hash Go bindings. --- runtime/go/clownfish/hash_test.go | 136 ++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 runtime/go/clownfish/hash_test.go diff --git a/runtime/go/clownfish/hash_test.go b/runtime/go/clownfish/hash_test.go new file mode 100644 index 00000000..27161967 --- /dev/null +++ b/runtime/go/clownfish/hash_test.go @@ -0,0 +1,136 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package clownfish + +import "testing" +import "reflect" +import "sort" + +func TestHashStoreFetch(t *testing.T) { + hash := NewHash(0) + hash.Store("foo", "bar") + if got, ok := hash.Fetch("foo").(string); !ok || got != "bar" { + t.Errorf("Expected \"bar\", got %v", got) + } + t.Skip("Can't store nil values yet") + hash.Store("nada", nil) + if got := hash.Fetch("nada"); got != nil { + t.Errorf("Expected nil, got %v", got) + } +} + +func TestHashDelete(t *testing.T) { + hash := NewHash(0) + hash.Store("foo", "bar") + got := hash.Delete("foo") + if size := hash.GetSize(); size != 0 { + t.Errorf("Delete failed (size %d)", size) + } + if val, ok := got.(string); !ok || val != "bar" { + t.Errorf("Delete returned unexpected value: %v") + } +} + +func TestHashClear(t *testing.T) { + hash := NewHash(0) + hash.Store("foo", 1) + hash.Clear() + if size := hash.GetSize(); size != 0 { + t.Errorf("Clear failed (size %d)", size) + } +} + +func TestHashHasKey(t *testing.T) { + hash := NewHash(0) + hash.Store("foo", 1) + if !hash.HasKey("foo") { + t.Errorf("HasKey returns true on success") + } + if hash.HasKey("bar") { + t.Errorf("HasKey returns false when key not present") + } +} + +func TestHashKeys(t *testing.T) { + hash := NewHash(0) + hash.Store("a", 1) + hash.Store("b", 1) + keys := hash.Keys() + sort.Strings(keys) + expected := []string{"a", "b"} + if !reflect.DeepEqual(keys, expected) { + t.Errorf("Expected %v, got %v", expected, keys) + } +} + +func TestHashValues(t *testing.T) { + hash := NewHash(0) + hash.Store("foo", "a") + hash.Store("bar", "b") + got := hash.Values() + vals := make([]string, len(got)) + for i, val := range got { + vals[i] = val.(string) + } + sort.Strings(vals) + expected := []string{"a", "b"} + if !reflect.DeepEqual(vals, expected) { + t.Errorf("Expected %v, got %v", expected, vals) + } +} + +func TestGetCapacity(t *testing.T) { + hash := NewHash(1) + if cap := hash.GetCapacity(); cap <= 1 { + t.Errorf("Unexpected value for GetCapacity: %d", cap) + } +} + +func TestGetSize(t *testing.T) { + hash := NewHash(0) + if size := hash.GetSize(); size != 0 { + t.Errorf("Unexpected value for GetSize: %d", size) + } + hash.Store("meep", "moop") + if size := hash.GetSize(); size != 1 { + t.Errorf("Unexpected value for GetSize: %d", size) + } +} + +func TestHashEquals(t *testing.T) { + hash := NewHash(0) + other := NewHash(0) + hash.Store("a", "foo") + other.Store("a", "foo") + if !hash.Equals(other) { + t.Error("Equals should succeed") + } + other.Store("b", "bar") + if hash.Equals(other) { + t.Error("Equals should fail") + } + if !hash.Equals(map[string]interface{}{"a":"foo"}) { + t.Error("Equals should succeed against a Go map") + } + vec := NewVector(0) + if hash.Equals(vec) { + t.Error("Equals should return false for another Clownfish type") + } + if hash.Equals(1) { + t.Error("Equals should return false for a different Go type.") + } +} From 2f1681b10e95298faeca43db478fe3d7489e405a Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Tue, 4 Aug 2015 19:06:47 -0700 Subject: [PATCH 05/16] Better namespacing for Go String test funcs. --- runtime/go/clownfish/string_test.go | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/runtime/go/clownfish/string_test.go b/runtime/go/clownfish/string_test.go index 22d0267f..87427b25 100644 --- a/runtime/go/clownfish/string_test.go +++ b/runtime/go/clownfish/string_test.go @@ -18,7 +18,7 @@ package clownfish import "testing" -func TestCat(t *testing.T) { +func TestStringCat(t *testing.T) { s := NewString("foo") got := s.Cat("bar") if got != "foobar" { @@ -26,7 +26,7 @@ func TestCat(t *testing.T) { } } -func TestSwapChars(t *testing.T) { +func TestStringSwapChars(t *testing.T) { s := NewString("foo") got := s.SwapChars('o', 'u') if got != "fuu" { @@ -34,7 +34,7 @@ func TestSwapChars(t *testing.T) { } } -func TestStartsWithEndsWith(t *testing.T) { +func TestStringStartsWithEndsWith(t *testing.T) { s := NewString("foobar") if !s.StartsWith("foo") { t.Error("StartsWith yes") @@ -50,7 +50,7 @@ func TestStartsWithEndsWith(t *testing.T) { } } -func TestBaseXToI64(t *testing.T) { +func TestStringBaseXToI64(t *testing.T) { s := NewString("100000000") var got int64 = s.BaseXToI64(10) if got != 100000000 { @@ -71,7 +71,7 @@ func TestBaseXToI64(t *testing.T) { } } -func TestFind(t *testing.T) { +func TestStringFind(t *testing.T) { s := NewString("foobarbaz") var got int64 = s.Find("bar") if got != 3 { @@ -83,7 +83,7 @@ func TestFind(t *testing.T) { } } -func TestEquals(t *testing.T) { +func TestStringEquals(t *testing.T) { t.Skip("Skip Equals because Obj arg won't accept string") /* s := NewString("foo") @@ -96,7 +96,7 @@ func TestEquals(t *testing.T) { */ } -func TestCompareTo(t *testing.T) { +func TestStringCompareTo(t *testing.T) { t.Skip("Skip CompareTo() because Obj arg won't accept string") /* s := NewString("foo") @@ -124,7 +124,7 @@ func TestCompareTo(t *testing.T) { */ } -func TestLenAndGetSize(t *testing.T) { +func TestStringLenAndGetSize(t *testing.T) { s := NewString("\u263a") var len uintptr = s.Length() if len != 1 { @@ -136,7 +136,7 @@ func TestLenAndGetSize(t *testing.T) { } } -func TestClone(t *testing.T) { +func TestStringClone(t *testing.T) { t.Skip("Skip Clone() because it shouldn't return an Obj") s := NewString("foo") got := s.Clone() @@ -145,20 +145,20 @@ func TestClone(t *testing.T) { } } -func TestHashSum(t *testing.T) { +func TestStringHashSum(t *testing.T) { // Test compilation only. s := NewString("foo") var _ uintptr = s.HashSum() } -func TestToString(t *testing.T) { +func TestStringToString(t *testing.T) { s := NewString("foo") if s.ToString() != "foo" { t.Fail() } } -func TestTrim(t *testing.T) { +func TestStringTrim(t *testing.T) { s := NewString(" foo ") var got string = s.Trim() if got != "foo" { @@ -174,7 +174,7 @@ func TestTrim(t *testing.T) { } } -func TestCodePointAtFrom(t *testing.T) { +func TestStringCodePointAtFrom(t *testing.T) { s := NewString("foobar") var got rune = s.CodePointAt(3) if got != 'b' { @@ -186,7 +186,7 @@ func TestCodePointAtFrom(t *testing.T) { } } -func TestSubString(t *testing.T) { +func TestStringSubString(t *testing.T) { s := NewString("foobarbaz") var got string = s.SubString(3, 3) if got != "bar" { @@ -194,7 +194,7 @@ func TestSubString(t *testing.T) { } } -func TestTopTail(t *testing.T) { +func TestStringTopTail(t *testing.T) { s := NewString("foo") top := s.Top() got := top.Next() From 85743e3dc6379c8069ea4af00aeb2d6acc0bd133 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Tue, 4 Aug 2015 19:08:18 -0700 Subject: [PATCH 06/16] Enable tests for String methods which take Obj. Now that Obj is mapped to Go empty-interface, we can test against Go strings. --- runtime/go/clownfish/string_test.go | 64 +++++++++++++---------------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/runtime/go/clownfish/string_test.go b/runtime/go/clownfish/string_test.go index 87427b25..7cb7fadf 100644 --- a/runtime/go/clownfish/string_test.go +++ b/runtime/go/clownfish/string_test.go @@ -84,44 +84,38 @@ func TestStringFind(t *testing.T) { } func TestStringEquals(t *testing.T) { - t.Skip("Skip Equals because Obj arg won't accept string") - /* - s := NewString("foo") - if !s.Equals("foo") { - t.Error("Equals should succeed") - } - if s.Equals("bar") { - t.Error("Equals should fail") - } - */ + s := NewString("foo") + if !s.Equals("foo") { + t.Error("Equals should succeed") + } + if s.Equals("bar") { + t.Error("Equals should fail") + } } func TestStringCompareTo(t *testing.T) { - t.Skip("Skip CompareTo() because Obj arg won't accept string") - /* - s := NewString("foo") - if !(s.CompareTo("boo") > 0) { - t.Error("'foo' > 'boo'") - } - if !(s.CompareTo("foo") == 0) { - t.Error("'foo' == 'foo'") - } - if !(s.CompareTo("zoo") < 0) { - t.Error("'foo' < 'zoo'") - } - if !(s.CompareTo("fo") > 0) { - t.Error("'foo' > 'fo'") - } - if !(s.CompareTo("food") < 0) { - t.Error("'foo' < 'food'") - } - if !(s.CompareTo("foo\u0000") < 0) { - t.Error("'foo' < 'foo\\0'") - } - if !(s.CompareTo("") > 0) { - t.Error("'foo' > ''") - } - */ + s := NewString("foo") + if !(s.CompareTo("boo") > 0) { + t.Error("'foo' > 'boo'") + } + if !(s.CompareTo("foo") == 0) { + t.Error("'foo' == 'foo'") + } + if !(s.CompareTo("zoo") < 0) { + t.Error("'foo' < 'zoo'") + } + if !(s.CompareTo("fo") > 0) { + t.Error("'foo' > 'fo'") + } + if !(s.CompareTo("food") < 0) { + t.Error("'foo' < 'food'") + } + if !(s.CompareTo("foo\u0000") < 0) { + t.Error("'foo' < 'foo\\0'") + } + if !(s.CompareTo("") > 0) { + t.Error("'foo' > ''") + } } func TestStringLenAndGetSize(t *testing.T) { From a9ea5430b823bd626ce7b1f64f7d4f92addd9511 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Wed, 5 Aug 2015 17:15:26 -0700 Subject: [PATCH 07/16] Customize HashIterator Go ctor binding. --- runtime/go/build.go | 4 ++++ runtime/go/clownfish/clownfish.go | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/runtime/go/build.go b/runtime/go/build.go index dd6b1f3e..29f02bac 100644 --- a/runtime/go/build.go +++ b/runtime/go/build.go @@ -158,6 +158,10 @@ func specMethods(parcel *cfc.Parcel) { hashBinding.SpecMethod("Keys", "Keys() []string") hashBinding.SetSuppressCtor(true) hashBinding.Register() + + hashIterBinding := cfc.NewGoClass(parcel, "Clownfish::HashIterator") + hashIterBinding.SetSuppressCtor(true) + hashIterBinding.Register() } func prep() { diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go index 4da8c824..3087cc02 100644 --- a/runtime/go/clownfish/clownfish.go +++ b/runtime/go/clownfish/clownfish.go @@ -146,6 +146,12 @@ func NewHash(size int) Hash { return WRAPHash(unsafe.Pointer(cfObj)) } +func NewHashIterator(hash Hash) HashIterator { + hashCF := (*C.cfish_Hash)(unsafe.Pointer(hash.TOPTR())) + cfObj := C.cfish_HashIter_new(hashCF) + return WRAPHashIterator(unsafe.Pointer(cfObj)) +} + func (h *HashIMP) Keys() []string { self := (*C.cfish_Hash)(unsafe.Pointer(h.TOPTR())) keysCF := C.CFISH_Hash_Keys(self) From 0f60ed4b9902172e0f6b874fd24d6e303a4b4f00 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Wed, 5 Aug 2015 17:16:49 -0700 Subject: [PATCH 08/16] Add tests for HashIterator. --- runtime/go/clownfish/hash_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/runtime/go/clownfish/hash_test.go b/runtime/go/clownfish/hash_test.go index 27161967..992024fc 100644 --- a/runtime/go/clownfish/hash_test.go +++ b/runtime/go/clownfish/hash_test.go @@ -134,3 +134,21 @@ func TestHashEquals(t *testing.T) { t.Error("Equals should return false for a different Go type.") } } + +func TestHashIterator(t *testing.T) { + hash := NewHash(0) + hash.Store("a", "foo") + iter := NewHashIterator(hash) + if !iter.Next() { + t.Error("Next() should proceed") + } + if key := iter.GetKey(); key != "a" { + t.Error("Expected \"a\", got %v", key) + } + if val, ok := iter.GetValue().(string); !ok || val != "foo" { + t.Error("Expected \"a\", got %v", val) + } + if iter.Next() { + t.Error("Next() should return false when iteration complete") + } +} From d34ae6fdbbbbe205f72ed63515cec94354e2e7eb Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Wed, 5 Aug 2015 18:28:37 -0700 Subject: [PATCH 09/16] Go binding for Blob_Get_Buf. --- runtime/go/build.go | 4 ++++ runtime/go/clownfish/clownfish.go | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/runtime/go/build.go b/runtime/go/build.go index 29f02bac..d7bb7a04 100644 --- a/runtime/go/build.go +++ b/runtime/go/build.go @@ -150,6 +150,10 @@ func specMethods(parcel *cfc.Parcel) { stringBinding.SpecMethod("Swap_Chars", "SwapChars(rune, rune) string") stringBinding.Register() + blobBinding := cfc.NewGoClass(parcel, "Clownfish::Blob") + blobBinding.SpecMethod("", "GetBuf() uintptr") + blobBinding.Register() + vecBinding := cfc.NewGoClass(parcel, "Clownfish::Vector") vecBinding.SetSuppressCtor(true) vecBinding.Register() diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go index 3087cc02..65cde553 100644 --- a/runtime/go/clownfish/clownfish.go +++ b/runtime/go/clownfish/clownfish.go @@ -647,3 +647,8 @@ func NewBlob(content []byte) Blob { obj := C.cfish_Blob_new(buf, size) return WRAPBlob(unsafe.Pointer(obj)) } + +func (b *BlobIMP) GetBuf() uintptr { + self := (*C.cfish_Blob)(unsafe.Pointer(b.TOPTR())) + return uintptr(unsafe.Pointer(C.CFISH_Blob_Get_Buf(self))) +} From f7ff5b66a0916c70e611e87f753ec93106aa15c1 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Wed, 5 Aug 2015 18:29:14 -0700 Subject: [PATCH 10/16] Tests for Blob Go binding. --- runtime/go/clownfish/blob_test.go | 97 +++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 runtime/go/clownfish/blob_test.go diff --git a/runtime/go/clownfish/blob_test.go b/runtime/go/clownfish/blob_test.go new file mode 100644 index 00000000..1411d57d --- /dev/null +++ b/runtime/go/clownfish/blob_test.go @@ -0,0 +1,97 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package clownfish + +import "testing" +import "unsafe" +import "reflect" + +func TestBlobNewBlob(t *testing.T) { + content := []byte("foo") + blob := NewBlob(content) + converted := BlobToGo(unsafe.Pointer(blob.TOPTR())) + if !reflect.DeepEqual(converted, content) { + t.Errorf("Expected %v, got %v") + } +} + +func TestBlobGetBuf(t *testing.T) { + content := []byte("foo") + blob := NewBlob(content) + if buf := blob.GetBuf(); buf == 0 { + t.Error("GetBuf() not working as expected") + } +} + +func TestBlobGetSize(t *testing.T) { + content := []byte("foo") + blob := NewBlob(content) + if size := blob.GetSize(); size != 3 { + t.Error("Size should be 3, not %v", size) + } +} + +func TestBlobClone(t *testing.T) { + content := []byte("foo") + blob := NewBlob(content) + dupe := blob.Clone().(Blob) + if !blob.Equals(dupe) { + t.Errorf("Clone() should yield a dupe") + } +} + +func TestBlobCompareTo(t *testing.T) { + a := NewBlob([]byte("a")) + b := NewBlob([]byte("b")) + a2 := NewBlob([]byte("a")) + if got := a.CompareTo(b); got >= 0 { + t.Errorf("a CompareTo b: %v", got) + } + if got := b.CompareTo(a); got <= 0 { + t.Errorf("a CompareTo b: %v", got) + } + if got := a.CompareTo(a2); got != 0 { + t.Errorf("a CompareTo a: %v", got) + } +} + +func TestBlobEquals(t *testing.T) { + a := NewBlob([]byte("a")) + b := NewBlob([]byte("b")) + a2 := NewBlob([]byte("a")) + if a.Equals(b) { + t.Error("a should not Equal b") + } + if b.Equals(a) { + t.Error("b should not Equal a") + } + if !a.Equals(a2) { + t.Error("a should Equal a") + } + if !a.Equals([]byte("a")) { + t.Error("Comparison to duplicate Go []byte content") + } + if a.Equals([]byte("b")) { + t.Error("Comparison to different Go []byte content") + } + if a.Equals("a") { + t.Error("Comparison against Go string should fail") + } + if a.Equals(NewString("a")) { + t.Error("Comparison against String should fail") + } +} From 18ab461b513ae6dc958fc2beee449be6b5b9e695 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Wed, 5 Aug 2015 19:07:37 -0700 Subject: [PATCH 11/16] Correct Go test package name. --- runtime/go/clownfish/err_test.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/runtime/go/clownfish/err_test.go b/runtime/go/clownfish/err_test.go index 9bdbceb8..1457f7ca 100644 --- a/runtime/go/clownfish/err_test.go +++ b/runtime/go/clownfish/err_test.go @@ -14,29 +14,28 @@ * limitations under the License. */ -package clownfish_test +package clownfish -import "git-wip-us.apache.org/repos/asf/lucy-clownfish.git/runtime/go/clownfish" import "testing" import "errors" func TestTrapErr(t *testing.T) { - err := clownfish.TrapErr( - func() { panic(clownfish.NewErr("mistakes were made")) }, + err := TrapErr( + func() { panic(NewErr("mistakes were made")) }, ) if err == nil { - t.Error("Failed to trap clownfish.Err") + t.Error("Failed to trap Err") } } func TestTrapErr_no_trap_string(t *testing.T) { defer func() { recover() }() - clownfish.TrapErr(func() { panic("foo") }) + TrapErr(func() { panic("foo") }) t.Error("Trapped plain string") // shouldn't reach here } func TestTrapErr_no_trap_error(t *testing.T) { defer func() { recover() }() - clownfish.TrapErr(func() { panic(errors.New("foo")) }) + TrapErr(func() { panic(errors.New("foo")) }) t.Error("Trapped non-clownfish.Error error type") // shouldn't reach here } From 82023ccc6dc53778acc1c5e3c8114ffb864f9b41 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Wed, 5 Aug 2015 19:09:28 -0700 Subject: [PATCH 12/16] Test more Go method bindings for Err. --- runtime/go/clownfish/err_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/runtime/go/clownfish/err_test.go b/runtime/go/clownfish/err_test.go index 1457f7ca..a7abff5e 100644 --- a/runtime/go/clownfish/err_test.go +++ b/runtime/go/clownfish/err_test.go @@ -39,3 +39,28 @@ func TestTrapErr_no_trap_error(t *testing.T) { TrapErr(func() { panic(errors.New("foo")) }) t.Error("Trapped non-clownfish.Error error type") // shouldn't reach here } + +func TestErrGetMess(t *testing.T) { + err := NewErr("foo") + expected := "foo" + if got := err.GetMess(); got != expected { + t.Errorf("Expected %v, got %v", expected, got) + } +} + +func TestErrCatMess(t *testing.T) { + err := NewErr("foo") + err.CatMess("bar") + expected := "foobar" + if got := err.GetMess(); got != expected { + t.Errorf("Expected %v, got %v", expected, got) + } +} + +func TestErrToString(t *testing.T) { + err := NewErr("foo") + expected := "foo" + if got := err.ToString(); got != expected { + t.Errorf("Expected %v, got %v", expected, got) + } +} From 6a43f1e570f9bb4abe3cae712c2351df1eec0fea Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Wed, 5 Aug 2015 19:10:00 -0700 Subject: [PATCH 13/16] Tests for Integer Go bindings. --- runtime/go/clownfish/integer_test.go | 106 +++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 runtime/go/clownfish/integer_test.go diff --git a/runtime/go/clownfish/integer_test.go b/runtime/go/clownfish/integer_test.go new file mode 100644 index 00000000..2d12fb4e --- /dev/null +++ b/runtime/go/clownfish/integer_test.go @@ -0,0 +1,106 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package clownfish + +import "testing" + +func TestIntGetValue(t *testing.T) { + fortyTwo := NewInteger(42) + if got := fortyTwo.GetValue(); got != 42 { + t.Errorf("Expected 42, got %d", got) + } +} + +func TestIntToF64(t *testing.T) { + fortyTwo := NewInteger(42) + if got := fortyTwo.ToF64(); got != 42.0 { + t.Errorf("Expected 42.0, got %f", got) + } +} + +func TestIntToBool(t *testing.T) { + fortyTwo := NewInteger(42) + if got := fortyTwo.ToBool(); !got { + t.Errorf("Expected true, got %v", got) + } + zero := NewInteger(0) + if got := zero.ToBool(); got { + t.Errorf("Expected false, got %v", got) + } +} + +func TestIntToString(t *testing.T) { + fortyTwo := NewInteger(42) + if got := fortyTwo.ToString(); got != "42" { + t.Errorf("Expected \"42\", got %s", got) + } +} + +func TestIntClone(t *testing.T) { + fortyTwo := NewInteger(42) + dupe := fortyTwo.Clone().(Integer) + if got := dupe.GetValue(); got != 42 { + t.Errorf("Expected 42, got %d", got) + } +} + +func TestIntCompareTo(t *testing.T) { + fortyTwo := NewInteger(42) + if got := fortyTwo.CompareTo(43); got >= 0 { + t.Errorf("42 CompareTo 43: %v", got) + } + if got := fortyTwo.CompareTo(42); got != 0 { + t.Errorf("42 CompareTo 42: %v", got) + } + if got := fortyTwo.CompareTo(42.1); got >= 0 { + t.Errorf("42 CompareTo 42.1: %v", got) + } + if got := fortyTwo.CompareTo(41.9); got <= 0 { + t.Errorf("42 CompareTo 41.9: %v", got) + } +} + +func TestIntEquals(t *testing.T) { + fortyTwo := NewInteger(42) + fortyThree := NewInteger(43) + fortyTwoPointZero := NewFloat(42.0) + dupe := fortyTwo.Clone().(Integer) + if !fortyTwo.Equals(dupe) { + t.Error("Equal Clownfish Integer") + } + if !fortyTwo.Equals(42) { + t.Error("Equal Go integer") + } + if !fortyTwo.Equals(42.0) { + t.Error("Equal Go float64") + } + if !fortyTwo.Equals(fortyTwoPointZero) { + t.Error("Equal Clownfish Float") + } + if fortyTwo.Equals(fortyThree) { + t.Error("Non-equal Clownfish Integer") + } + if fortyTwo.Equals(43) { + t.Error("Non-equal Go integer") + } + if fortyTwo.Equals(42.1) { + t.Error("Non-equal Go float64") + } + if fortyTwo.Equals("foo") { + t.Error("Non-equal Go string") + } +} From a556c515f9d551e1f86d8788e14a197e132e7efc Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Wed, 5 Aug 2015 19:40:25 -0700 Subject: [PATCH 14/16] Tests for Float Go bindings. --- runtime/go/clownfish/float_test.go | 99 ++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 runtime/go/clownfish/float_test.go diff --git a/runtime/go/clownfish/float_test.go b/runtime/go/clownfish/float_test.go new file mode 100644 index 00000000..7c1f81aa --- /dev/null +++ b/runtime/go/clownfish/float_test.go @@ -0,0 +1,99 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package clownfish + +import "testing" + +func TestFloatGetValue(t *testing.T) { + num := NewFloat(2.5) + if got := num.GetValue(); got != 2.5 { + t.Errorf("Expected 2.5, got %f", got) + } +} + +func TestFloatToI64(t *testing.T) { + num := NewFloat(2.5) + if got := num.ToI64(); got != 2 { + t.Errorf("Expected 2, got %d", got) + } +} + +func TestFloatToBool(t *testing.T) { + num := NewFloat(0.1) + if got := num.ToBool(); !got { + t.Errorf("Expected true, got %v", got) + } + zero := NewFloat(0) + if got := zero.ToBool(); got { + t.Errorf("Expected false, got %v", got) + } +} + +func TestFloatToString(t *testing.T) { + num := NewFloat(2.5) + if got := num.ToString(); got != "2.5" { + t.Errorf("Expected \"2.5\", got %s", got) + } +} + +func TestFloatClone(t *testing.T) { + num := NewFloat(2.5) + dupe := num.Clone().(Float) + if got := dupe.GetValue(); got != 2.5 { + t.Errorf("Expected 2.5, got %f", got) + } +} + +func TestFloatCompareTo(t *testing.T) { + num := NewFloat(2.5) + if got := num.CompareTo(3.5); got >= 0 { + t.Errorf("2.5 CompareTo 3.5: %v", got) + } + if got := num.CompareTo(2.5); got != 0 { + t.Errorf("2.5 CompareTo 2.5: %v", got) + } + if got := num.CompareTo(2); got <= 0 { + t.Errorf("2.5 CompareTo 2: %v", got) + } + if got := num.CompareTo(-1.1); got <= 0 { + t.Errorf("2.5 CompareTo -1.1: %v", got) + } +} + +func TestFloatEquals(t *testing.T) { + num := NewFloat(2.5) + bigger := NewFloat(3.5) + dupe := num.Clone().(Float) + if !num.Equals(dupe) { + t.Error("Equal Clownfish Float") + } + if !num.Equals(2.5) { + t.Error("Equal Go float64") + } + if num.Equals(bigger) { + t.Error("Non-equal Clownfish Integer") + } + if num.Equals(43) { + t.Error("Non-equal Go integer") + } + if got := num.Equals(2.6); got { + t.Error("Non-equal Go float64") + } + if got := num.Equals("foo"); got { + t.Error("Non-equal Go string") + } +} From d947d8819be72c23b75213fe499aaf2371fc2c61 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Wed, 5 Aug 2015 20:07:10 -0700 Subject: [PATCH 15/16] Add missing case for converting Go `bool`. --- runtime/go/clownfish/clownfish.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go index 65cde553..34b6f5f4 100644 --- a/runtime/go/clownfish/clownfish.go +++ b/runtime/go/clownfish/clownfish.go @@ -281,6 +281,10 @@ func GoToClownfish(value interface{}, class unsafe.Pointer, nullable bool) unsaf if klass == C.CFISH_FLOAT || klass == C.CFISH_OBJ { converted = GoToFloat(value) } + case bool: + if klass == C.CFISH_BOOLEAN || klass == C.CFISH_OBJ { + converted = GoToBoolean(value) + } case []interface{}: if klass == C.CFISH_VECTOR || klass == C.CFISH_OBJ { converted = GoToVector(value) From 939b08ff6e76683d3826eefb5f357efa0ba00432 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Wed, 5 Aug 2015 20:06:49 -0700 Subject: [PATCH 16/16] Tests for Boolean Go bindings. --- runtime/go/clownfish/boolean_test.go | 105 +++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 runtime/go/clownfish/boolean_test.go diff --git a/runtime/go/clownfish/boolean_test.go b/runtime/go/clownfish/boolean_test.go new file mode 100644 index 00000000..a1419cb7 --- /dev/null +++ b/runtime/go/clownfish/boolean_test.go @@ -0,0 +1,105 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package clownfish + +import "testing" + +func TestBooleanGetValue(t *testing.T) { + myTrue := NewBoolean(true) + myFalse := NewBoolean(false) + if !myTrue.GetValue() { + t.Errorf("Expected true, got false") + } + if myFalse.GetValue() { + t.Errorf("Expected false, got true") + } +} + +func TestBooleanToF64(t *testing.T) { + myTrue := NewBoolean(true) + myFalse := NewBoolean(false) + if got := myTrue.ToF64(); got != 1.0 { + t.Errorf("Expected 1.0, got %v", got) + } + if got := myFalse.ToF64(); got != 0.0 { + t.Errorf("Expected 0.0, got %v", got) + } +} + +func TestBooleanToI64(t *testing.T) { + myTrue := NewBoolean(true) + myFalse := NewBoolean(false) + if got := myTrue.ToI64(); got != 1 { + t.Errorf("Expected 1, got %v", got) + } + if got := myFalse.ToI64(); got != 0 { + t.Errorf("Expected 0, got %v", got) + } +} + +func TestBooleanToBool(t *testing.T) { + myTrue := NewBoolean(true) + myFalse := NewBoolean(false) + if !myTrue.ToBool() { + t.Errorf("Expected true, got false") + } + if myFalse.ToBool() { + t.Errorf("Expected false, got true") + } +} + +func TestBooleanToString(t *testing.T) { + myTrue := NewBoolean(true) + myFalse := NewBoolean(false) + if got := myTrue.ToString(); got != "true" { + t.Errorf("Expected \"true\", got %v", got) + } + if got := myFalse.ToString(); got != "false" { + t.Errorf("Expected \"false\", got %v", got) + } +} + +func TestBooleanClone(t *testing.T) { + myTrue := NewBoolean(true) + myFalse := NewBoolean(false) + if myTrue.TOPTR() != myTrue.Clone().TOPTR() { + t.Errorf("Clone should wrap CFISH_TRUE") + } + if myFalse.TOPTR() != myFalse.Clone().TOPTR() { + t.Errorf("Clone should wrap CFISH_FALSE") + } +} + +func TestBooleanEquals(t *testing.T) { + myTrue := NewBoolean(true) + myFalse := NewBoolean(false) + if !myTrue.Equals(myTrue) { + t.Error("Equal Clownfish Boolean") + } + if !myTrue.Equals(true) { + t.Error("Equal Go bool (true)") + } + if !myFalse.Equals(false) { + t.Error("Equal Go bool (false)") + } + if myTrue.Equals(myFalse) { + t.Error("Non-equal Clownfish Boolean") + } + if myFalse.Equals(0) { + t.Error("Go 0 should not equal Clownfish Boolean false") + } +}