Add GoToClownfish/ToGo conversion routines.

Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/55d40ac6
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/55d40ac6
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/55d40ac6

Branch: refs/heads/master
Commit: 55d40ac67b7a076050c15e652d51f4a5dfc937a6
Parents: 56e4ab9
Author: Marvin Humphrey <[email protected]>
Authored: Fri Jul 24 14:23:44 2015 -0700
Committer: Marvin Humphrey <[email protected]>
Committed: Fri Jul 31 11:05:42 2015 -0700

----------------------------------------------------------------------
 runtime/go/clownfish/clownfish.go      | 383 +++++++++++++++++++++++++++-
 runtime/go/clownfish/clownfish_test.go | 236 ++++++++++++++++-
 2 files changed, 611 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/55d40ac6/runtime/go/clownfish/clownfish.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish.go 
b/runtime/go/clownfish/clownfish.go
index 0fb7e32..21eb567 100644
--- a/runtime/go/clownfish/clownfish.go
+++ b/runtime/go/clownfish/clownfish.go
@@ -18,6 +18,8 @@ package clownfish
 
 /*
 
+#include <limits.h>
+
 #include "charmony.h"
 
 #include "Clownfish/Obj.h"
@@ -26,7 +28,9 @@ package clownfish
 #include "Clownfish/String.h"
 #include "Clownfish/Blob.h"
 #include "Clownfish/Hash.h"
+#include "Clownfish/HashIterator.h"
 #include "Clownfish/Vector.h"
+#include "Clownfish/Num.h"
 #include "Clownfish/Boolean.h"
 #include "Clownfish/Util/Memory.h"
 #include "Clownfish/Method.h"
@@ -63,6 +67,7 @@ import "C"
 import "runtime"
 import "unsafe"
 import "fmt"
+import "math"
 import "sync"
 
 const (
@@ -139,7 +144,315 @@ func (o *ObjIMP) TOPTR() uintptr {
        return o.ref
 }
 
+func certifyCF(value interface{}, class *C.cfish_Class) {
+       cfObj, ok := value.(Obj)
+       if ok {
+               if 
C.cfish_Obj_is_a((*C.cfish_Obj)(unsafe.Pointer(cfObj.TOPTR())), class) {
+                       return
+               }
+       }
+       className := StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(class)))
+       panic(NewErr(fmt.Sprintf("Can't convert a %T to %s", value, className)))
+}
+
+// Convert a Go type into an incremented Clownfish object.  If the supplied
+// object is a Clownfish object wrapped in a Go struct, extract the Clownfish
+// object and incref it before returning its address.
+func GoToClownfish(value interface{}, class unsafe.Pointer, nullable bool) 
unsafe.Pointer {
+       klass := (*C.cfish_Class)(class)
+
+       // Check for nil values.
+       if value == nil {
+               if nullable {
+                       return nil
+               } else if class != nil {
+                       className := 
StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(klass)))
+                       panic(NewErr("Cannot be nil, must be a valid " + 
className))
+               } else {
+                       panic(NewErr("Cannot be nil"))
+               }
+       }
+
+       // Default to accepting any type.
+       if klass == nil {
+               klass = C.CFISH_OBJ
+       }
+
+       // Convert the value according to its type if possible.
+       var converted unsafe.Pointer
+       switch v := value.(type) {
+       case string:
+               if klass == C.CFISH_STRING || klass == C.CFISH_OBJ {
+                       converted = GoToString(value)
+               }
+       case []byte:
+               if klass == C.CFISH_BLOB || klass == C.CFISH_OBJ {
+                       converted = GoToBlob(value)
+               }
+       case int:
+               if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+                       converted = GoToInteger(value)
+               }
+       case uint:
+               if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+                       converted = GoToInteger(value)
+               }
+       case uintptr:
+               if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+                       converted = GoToInteger(value)
+               }
+       case int64:
+               if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+                       converted = GoToInteger(value)
+               }
+       case int32:
+               if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+                       converted = GoToInteger(value)
+               }
+       case int16:
+               if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+                       converted = GoToInteger(value)
+               }
+       case int8:
+               if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+                       converted = GoToInteger(value)
+               }
+       case uint64:
+               if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+                       converted = GoToInteger(value)
+               }
+       case uint32:
+               if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+                       converted = GoToInteger(value)
+               }
+       case uint16:
+               if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+                       converted = GoToInteger(value)
+               }
+       case uint8:
+               if klass == C.CFISH_INTEGER || klass == C.CFISH_OBJ {
+                       converted = GoToInteger(value)
+               }
+       case float32:
+               if klass == C.CFISH_FLOAT || klass == C.CFISH_OBJ {
+                       converted = GoToFloat(value)
+               }
+       case float64:
+               if klass == C.CFISH_FLOAT || klass == C.CFISH_OBJ {
+                       converted = GoToFloat(value)
+               }
+       case []interface{}:
+               if klass == C.CFISH_VECTOR || klass == C.CFISH_OBJ {
+                       converted = GoToVector(value)
+               }
+       case map[string]interface{}:
+               if klass == C.CFISH_HASH || klass == C.CFISH_OBJ {
+                       converted = GoToHash(value)
+               }
+       case Obj:
+               converted = 
unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+       }
+
+       // Confirm that we got what we were looking for and return.
+       if converted != nil {
+               if C.cfish_Obj_is_a((*C.cfish_Obj)(converted), klass) {
+                       return unsafe.Pointer(C.cfish_incref(converted))
+               }
+       }
+
+       // Report a conversion error.
+       className := StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(klass)))
+       panic(NewErr(fmt.Sprintf("Can't convert a %T to %s", value, className)))
+}
+
+func GoToString(value interface{}) unsafe.Pointer {
+       switch v := value.(type) {
+       case string:
+               size := len(v)
+               str := C.CString(v)
+               return unsafe.Pointer(C.cfish_Str_new_steal_utf8(str, 
C.size_t(size)))
+       case Obj:
+               certifyCF(v, C.CFISH_STRING)
+               return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+       default:
+               mess := fmt.Sprintf("Can't convert %T to clownfish.String", v)
+               panic(NewErr(mess))
+       }
+}
+
+func GoToBlob(value interface{}) unsafe.Pointer {
+       switch v := value.(type) {
+       case []byte:
+               size := C.size_t(len(v))
+               var buf *C.char = nil
+               if size > 0 {
+                       buf = ((*C.char)(unsafe.Pointer(&v[0])))
+               }
+               return unsafe.Pointer(C.cfish_Blob_new(buf, size))
+       case Obj:
+               certifyCF(v, C.CFISH_BLOB)
+               return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+       default:
+               mess := fmt.Sprintf("Can't convert %T to clownfish.Blob", v)
+               panic(NewErr(mess))
+       }
+}
+
+func GoToInteger(value interface{}) unsafe.Pointer {
+       switch v := value.(type) {
+       case int:
+               return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+       case uint:
+               if v > math.MaxInt64 {
+                       mess := fmt.Sprintf("uint value too large: %v", v)
+                       panic(NewErr(mess))
+               }
+               return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+       case uintptr:
+               if v > math.MaxInt64 {
+                       mess := fmt.Sprintf("uintptr value too large: %v", v)
+                       panic(NewErr(mess))
+               }
+               return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+       case uint64:
+               if v > math.MaxInt64 {
+                       mess := fmt.Sprintf("uint64 value too large: %v", v)
+                       panic(NewErr(mess))
+               }
+               return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+       case uint32:
+               return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+       case uint16:
+               return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+       case uint8:
+               return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+       case int64:
+               return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+       case int32:
+               return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+       case int16:
+               return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+       case int8:
+               return unsafe.Pointer(C.cfish_Int_new(C.int64_t(v)))
+       case Obj:
+               certifyCF(v, C.CFISH_INTEGER)
+               return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+       default:
+               mess := fmt.Sprintf("Can't convert %T to clownfish.Integer", v)
+               panic(NewErr(mess))
+       }
+}
+
+func GoToFloat(value interface{}) unsafe.Pointer {
+       switch v := value.(type) {
+       case float32:
+               return unsafe.Pointer(C.cfish_Float_new(C.double(v)))
+       case float64:
+               return unsafe.Pointer(C.cfish_Float_new(C.double(v)))
+       case Obj:
+               certifyCF(v, C.CFISH_FLOAT)
+               return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+       default:
+               mess := fmt.Sprintf("Can't convert %T to clownfish.Float", v)
+               panic(NewErr(mess))
+       }
+}
+
+func GoToBoolean(value interface{}) unsafe.Pointer {
+       switch v := value.(type) {
+       case bool:
+               if v {
+                       return 
unsafe.Pointer(C.cfish_incref(unsafe.Pointer(C.CFISH_TRUE)))
+               } else {
+                       return 
unsafe.Pointer(C.cfish_incref(unsafe.Pointer(C.CFISH_FALSE)))
+               }
+       case Obj:
+               certifyCF(v, C.CFISH_BOOLEAN)
+               return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+       default:
+               mess := fmt.Sprintf("Can't convert %T to clownfish.Boolean", v)
+               panic(NewErr(mess))
+       }
+}
+
+func GoToVector(value interface{}) unsafe.Pointer {
+       switch v := value.(type) {
+       case []interface{}:
+               size := len(v)
+               vec := C.cfish_Vec_new(C.size_t(size))
+               for i := 0; i < size; i++ {
+                       elem := GoToClownfish(v[i], nil, true)
+                       C.CFISH_Vec_Store(vec, C.size_t(i), 
(*C.cfish_Obj)(elem))
+               }
+               return unsafe.Pointer(vec)
+       case Obj:
+               certifyCF(v, C.CFISH_VECTOR)
+               return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+       default:
+               mess := fmt.Sprintf("Can't convert %T to clownfish.Vector", v)
+               panic(NewErr(mess))
+       }
+}
+
+func GoToHash(value interface{}) unsafe.Pointer {
+       switch v := value.(type) {
+       case map[string]interface{}:
+               size := len(v)
+               hash := C.cfish_Hash_new(C.size_t(size))
+               for key, val := range v {
+                       newVal := GoToClownfish(val, nil, true)
+                       keySize := len(key)
+                       keyStr := C.CString(key)
+                   cfKey := C.cfish_Str_new_steal_utf8(keyStr, 
C.size_t(keySize))
+                       defer C.cfish_dec_refcount(unsafe.Pointer(cfKey))
+                       C.CFISH_Hash_Store(hash, cfKey, (*C.cfish_Obj)(newVal))
+               }
+               return unsafe.Pointer(hash)
+       case Obj:
+               certifyCF(v, C.CFISH_HASH)
+               return unsafe.Pointer(C.cfish_incref(unsafe.Pointer(v.TOPTR())))
+       default:
+               mess := fmt.Sprintf("Can't convert %T to clownfish.Hash", v)
+               panic(NewErr(mess))
+       }
+}
+
+func ToGo(ptr unsafe.Pointer) interface{} {
+       if ptr == nil {
+               return nil
+       }
+       class := C.cfish_Obj_get_class((*C.cfish_Obj)(ptr))
+       if class == C.CFISH_STRING {
+               return CFStringToGo(ptr)
+       } else if class == C.CFISH_BLOB {
+               return BlobToGo(ptr)
+       } else if class == C.CFISH_VECTOR {
+               return VectorToGo(ptr)
+       } else if class == C.CFISH_HASH {
+               return HashToGo(ptr)
+       } else if class == C.CFISH_BOOLEAN {
+               if ptr == unsafe.Pointer(C.CFISH_TRUE) {
+                       return true
+               } else {
+                       return false
+               }
+       } else if class == C.CFISH_INTEGER {
+               val := C.CFISH_Int_Get_Value((*C.cfish_Integer)(ptr))
+               return int64(val)
+       } else if class == C.CFISH_FLOAT {
+               val := C.CFISH_Float_Get_Value((*C.cfish_Float)(ptr))
+               return float64(val)
+       } else {
+               // Don't convert to a native Go type, but wrap in a Go struct.
+               return WRAPAny(ptr)
+       }
+}
+
 func CFStringToGo(ptr unsafe.Pointer) string {
+       return StringToGo(ptr)
+}
+
+func StringToGo(ptr unsafe.Pointer) string {
        cfString := (*C.cfish_String)(ptr)
        if cfString == nil {
                return ""
@@ -149,13 +462,77 @@ func CFStringToGo(ptr unsafe.Pointer) string {
                defer C.cfish_dec_refcount(unsafe.Pointer(cfString))
        }
        data := C.CFISH_Str_Get_Ptr8(cfString)
-       size := C.int(C.CFISH_Str_Get_Size(cfString))
-       return C.GoStringN(data, size)
+       size := C.CFISH_Str_Get_Size(cfString)
+       if size > C.size_t(C.INT_MAX) {
+               panic(fmt.Sprintf("Overflow: %d > %d", size, C.INT_MAX))
+       }
+       return C.GoStringN(data, C.int(size))
+}
+
+func BlobToGo(ptr unsafe.Pointer) []byte {
+       blob := (*C.cfish_Blob)(ptr)
+       if blob == nil {
+               return nil
+       }
+       class := C.cfish_Obj_get_class((*C.cfish_Obj)(ptr))
+       if class != C.CFISH_BLOB {
+               mess := "Not a Blob: " + 
StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(class)))
+               panic(NewErr(mess))
+       }
+       data := C.CFISH_Blob_Get_Buf(blob)
+       size := C.CFISH_Blob_Get_Size(blob)
+       if size > C.size_t(C.INT_MAX) {
+               panic(fmt.Sprintf("Overflow: %d > %d", size, C.INT_MAX))
+       }
+       return C.GoBytes(unsafe.Pointer(data), C.int(size))
+}
+
+func VectorToGo(ptr unsafe.Pointer) []interface{} {
+       vec := (*C.cfish_Vector)(ptr)
+       if vec == nil {
+               return nil
+       }
+       class := C.cfish_Obj_get_class((*C.cfish_Obj)(ptr))
+       if class != C.CFISH_VECTOR {
+               mess := "Not a Vector: " + 
StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(class)))
+               panic(NewErr(mess))
+       }
+       size := C.CFISH_Vec_Get_Size(vec)
+       if size > C.size_t(maxInt) {
+               panic(fmt.Sprintf("Overflow: %d > %d", size, maxInt))
+       }
+       slice := make([]interface{}, int(size))
+       for i := 0; i < int(size); i++ {
+               slice[i] = ToGo(unsafe.Pointer(C.CFISH_Vec_Fetch(vec, 
C.size_t(i))))
+       }
+       return slice
+}
+
+func HashToGo(ptr unsafe.Pointer) map[string]interface{} {
+       hash := (*C.cfish_Hash)(ptr)
+       if hash == nil {
+               return nil
+       }
+       class := C.cfish_Obj_get_class((*C.cfish_Obj)(ptr))
+       if class != C.CFISH_HASH {
+               mess := "Not a Hash: " + 
StringToGo(unsafe.Pointer(C.CFISH_Class_Get_Name(class)))
+               panic(NewErr(mess))
+       }
+       size := C.CFISH_Hash_Get_Size(hash)
+       m := make(map[string]interface{}, int(size))
+       iter := C.cfish_HashIter_new(hash)
+       defer C.cfish_dec_refcount(unsafe.Pointer(iter))
+       for C.CFISH_HashIter_Next(iter) {
+               key := C.CFISH_HashIter_Get_Key(iter)
+               val := C.CFISH_HashIter_Get_Value(iter)
+               m[StringToGo(unsafe.Pointer(key))] = ToGo(unsafe.Pointer(val))
+       }
+       return m
 }
 
 func (e *ErrIMP) Error() string {
        mess := C.CFISH_Err_Get_Mess((*C.cfish_Err)(unsafe.Pointer(e.ref)))
-       return CFStringToGo(unsafe.Pointer(mess))
+       return StringToGo(unsafe.Pointer(mess))
 }
 
 //export GoCfish_PanicErr_internal

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/55d40ac6/runtime/go/clownfish/clownfish_test.go
----------------------------------------------------------------------
diff --git a/runtime/go/clownfish/clownfish_test.go 
b/runtime/go/clownfish/clownfish_test.go
index 6103f24..6b31527 100644
--- a/runtime/go/clownfish/clownfish_test.go
+++ b/runtime/go/clownfish/clownfish_test.go
@@ -18,11 +18,237 @@ package clownfish
 
 import "testing"
 import "unsafe"
+import "reflect"
+import "math"
 
-func TestStuff(t *testing.T) {
-       cfString := NewString("foo")
-       goString := CFStringToGo(unsafe.Pointer(cfString.TOPTR()))
-       if goString != "foo" {
-               t.Error("Round-tripping strings failed")
+func deepCheck(t *testing.T, got, expected interface{}) {
+       if !reflect.DeepEqual(got, expected) {
+               t.Errorf("Expected %v got %v", expected, got)
        }
 }
+
+func TestStringToGo(t *testing.T) {
+       strings := []string{"foo", "", "z\u0000z"}
+       for _, val := range strings {
+               got := StringToGo(unsafe.Pointer(NewString(val).TOPTR()))
+               deepCheck(t, got, val)
+       }
+}
+
+func TestBlobToGo(t *testing.T) {
+       strings := []string{"foo", "", "z\u0000z"}
+       for _, str := range strings {
+               expected := []byte(str)
+               got := BlobToGo(unsafe.Pointer(NewBlob(expected).TOPTR()))
+               deepCheck(t, got, expected)
+       }
+}
+
+func TestIntegerToGo(t *testing.T) {
+       values := []int64{math.MaxInt64, math.MinInt64, 0, 1, -1}
+       for _, val := range values {
+               got := ToGo(unsafe.Pointer(NewInteger(val).TOPTR()))
+               deepCheck(t, got, val)
+       }
+}
+
+func TestFloatToGo(t *testing.T) {
+       values := []float64{math.MaxFloat64, math.SmallestNonzeroFloat64,
+               0.0, -0.0, 0.5, -0.5, math.Inf(1), math.Inf(-1)}
+       for _, val := range values {
+               got := ToGo(unsafe.Pointer(NewFloat(val).TOPTR()))
+               deepCheck(t, got, val)
+       }
+       notNum := ToGo(unsafe.Pointer(NewFloat(math.NaN()).TOPTR()))
+       if !math.IsNaN(notNum.(float64)) {
+               t.Error("Didn't convert NaN cleanly")
+       }
+}
+
+func TestBoolToGo(t *testing.T) {
+       values := []bool{true, false}
+       for _, val := range values {
+               got := ToGo(unsafe.Pointer(NewBoolean(val).TOPTR()))
+               deepCheck(t, got, val)
+       }
+}
+
+func TestVectorToGo(t *testing.T) {
+       vec := NewVector(3)
+       vec.Push(NewString("foo"))
+       vec.Push(NewInteger(42))
+       //vec.Push(nil)
+       inner := NewVector(1)
+       inner.Push(NewString("bar"))
+       vec.Push(inner)
+       expected := []interface{}{
+               "foo",
+               int64(42),
+               //nil,
+               []interface{}{"bar"},
+       }
+       got := VectorToGo(unsafe.Pointer(vec.TOPTR()))
+       deepCheck(t, got, expected)
+}
+
+func TestHashToGo(t *testing.T) {
+       hash := NewHash(0)
+       hash.Store("str", NewString("foo"))
+       hash.Store("bool", NewBoolean(false))
+       hash.Store("float", NewFloat(-0.5))
+       hash.Store("int", NewInteger(42))
+       //hash.Store("nil", nil)
+       vec := NewVector(1)
+       vec.Push(NewString("bar"))
+       hash.Store("vector", vec)
+       got := HashToGo(unsafe.Pointer(hash.TOPTR()))
+
+       expected := map[string]interface{}{
+               "str":   "foo",
+               "bool":  false,
+               "float": -0.5,
+               "int":   int64(42),
+               //"nil": nil,
+               "vector": []interface{}{"bar"},
+       }
+       deepCheck(t, got, expected)
+}
+
+func TestNilToGo(t *testing.T) {
+       got := ToGo(unsafe.Pointer(uintptr(0)))
+       if got != nil {
+               t.Errorf("Expected nil, got %v", got)
+       }
+}
+
+func TestGoToNil(t *testing.T) {
+       if GoToClownfish(nil, nil, true) != nil {
+               t.Error("Convert nullable nil successfully")
+       }
+}
+
+func TestGoToNilNotNullable(t *testing.T) {
+       defer func() { recover() }()
+       GoToClownfish(nil, nil, false)                   // should panic
+       t.Error("Non-nullable nil should trigger error") // should be 
unreachable
+}
+
+func TestGoToString(t *testing.T) {
+       strings := []string{"foo", "", "z\u0000z"}
+       for _, val := range strings {
+               got := WRAPAny(GoToString(val))
+               if _, ok := got.(String); !ok {
+                       t.Errorf("Not a String, but a %T", got)
+               }
+               if ToGo(unsafe.Pointer(got.TOPTR())).(string) != val {
+                       t.Error("Round trip failed")
+               }
+       }
+}
+
+func TestGoToBlob(t *testing.T) {
+       strings := []string{"foo", "", "z\u0000z"}
+       for _, str := range strings {
+               val := []byte(str)
+               got := WRAPAny(GoToBlob(val))
+               if _, ok := got.(Blob); !ok {
+                       t.Errorf("Not a Blob, but a %T", got)
+               }
+               if !reflect.DeepEqual(ToGo(unsafe.Pointer(got.TOPTR())), val) {
+                       t.Error("Round trip failed")
+               }
+       }
+}
+
+func checkIntConv(t *testing.T, got Obj) {
+       if _, ok := got.(Integer); !ok {
+               t.Errorf("Not an Integer, but a %T", got)
+       }
+}
+
+func TestGoToInteger(t *testing.T) {
+       checkIntConv(t, WRAPAny(GoToClownfish(int(42), nil, true)))
+       checkIntConv(t, WRAPAny(GoToClownfish(uint(42), nil, true)))
+       checkIntConv(t, WRAPAny(GoToClownfish(int64(42), nil, true)))
+       checkIntConv(t, WRAPAny(GoToClownfish(int32(42), nil, true)))
+       checkIntConv(t, WRAPAny(GoToClownfish(int16(42), nil, true)))
+       checkIntConv(t, WRAPAny(GoToClownfish(int8(42), nil, true)))
+       checkIntConv(t, WRAPAny(GoToClownfish(uint64(42), nil, true)))
+       checkIntConv(t, WRAPAny(GoToClownfish(uint32(42), nil, true)))
+       checkIntConv(t, WRAPAny(GoToClownfish(uint16(42), nil, true)))
+       checkIntConv(t, WRAPAny(GoToClownfish(uint8(42), nil, true)))
+       checkIntConv(t, WRAPAny(GoToClownfish(byte(42), nil, true)))
+       checkIntConv(t, WRAPAny(GoToClownfish(rune(42), nil, true)))
+}
+
+func TestGoToIntegerRangeError(t *testing.T) {
+       defer func() { recover() }()
+       GoToClownfish(uint64(math.MaxUint64), nil, true)
+       t.Error("Truncation didn't cause error")
+}
+
+func TestGoToFloat(t *testing.T) {
+       var got Obj
+
+       values := []float64{math.MaxFloat64, math.SmallestNonzeroFloat64,
+               0.0, -0.0, 0.5, -0.5, math.Inf(1), math.Inf(-1)}
+       for _, val := range values {
+               got := WRAPAny(GoToFloat(val))
+               if _, ok := got.(Float); !ok {
+                       t.Errorf("Not a Float, but a %T", got)
+               }
+               if !reflect.DeepEqual(ToGo(unsafe.Pointer(got.TOPTR())), val) {
+                       t.Error("Round trip failed")
+               }
+       }
+
+       // NaN
+       got = WRAPAny(GoToFloat(math.NaN()))
+       if !math.IsNaN(ToGo(unsafe.Pointer(got.TOPTR())).(float64)) {
+               t.Error("Didn't convert NaN cleanly")
+       }
+
+       // float32
+       expected := float32(0.5)
+       got = WRAPAny(GoToClownfish(expected, nil, false))
+       deepCheck(t, got.(Float).GetValue(), float64(expected))
+}
+
+func TestGoToBoolean(t *testing.T) {
+       values := []bool{true, false}
+       for _, val := range values {
+               got := WRAPAny(GoToBoolean(val))
+               if _, ok := got.(Boolean); !ok {
+                       t.Errorf("Not a Boolean, but a %T", got)
+               }
+               if !reflect.DeepEqual(ToGo(unsafe.Pointer(got.TOPTR())), val) {
+                       t.Error("Round trip failed")
+               }
+       }
+}
+
+func TestGoToHash(t *testing.T) {
+       expected := map[string]interface{}{
+               "foo": int64(1),
+               "bar": []interface{}{},
+       }
+       got := WRAPAny(GoToHash(expected))
+       if _, ok := got.(Hash); !ok {
+               t.Errorf("Not a Hash, but a %T", got)
+       }
+       deepCheck(t, ToGo(unsafe.Pointer(got.TOPTR())), expected)
+}
+
+func TestGoToVector(t *testing.T) {
+       expected := []interface{}{
+               "foo",
+               "bar",
+               []interface{}{},
+               int64(-1),
+       }
+       got := WRAPAny(GoToVector(expected))
+       if _, ok := got.(Vector); !ok {
+               t.Errorf("Not a Vector, but a %T", got)
+       }
+       deepCheck(t, ToGo(unsafe.Pointer(got.TOPTR())), expected)
+}

Reply via email to