github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/datacodec/double_test.go (about)

     1  // Copyright 2021 DataStax
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package datacodec
    16  
    17  import (
    18  	"fmt"
    19  	"math"
    20  	"math/big"
    21  	"testing"
    22  
    23  	"github.com/stretchr/testify/assert"
    24  
    25  	"github.com/datastax/go-cassandra-native-protocol/datatype"
    26  	"github.com/datastax/go-cassandra-native-protocol/primitive"
    27  )
    28  
    29  var (
    30  	doubleZeroBytes       = encodeUint64(0x0000000000000000)
    31  	doubleOneBytes        = encodeUint64(0x3ff0000000000000)
    32  	doubleMinusOneBytes   = encodeUint64(0xbff0000000000000)
    33  	doubleMaxFloat64Bytes = encodeUint64(0x7fefffffffffffff)
    34  )
    35  
    36  func Test_doubleCodec_DataType(t *testing.T) {
    37  	assert.Equal(t, datatype.Double, Double.DataType())
    38  }
    39  
    40  func Test_doubleCodec_Encode(t *testing.T) {
    41  	for _, version := range primitive.SupportedProtocolVersions() {
    42  		t.Run(version.String(), func(t *testing.T) {
    43  			tests := []struct {
    44  				name     string
    45  				source   interface{}
    46  				expected []byte
    47  				err      string
    48  			}{
    49  				{"nil", nil, nil, ""},
    50  				{"nil pointer", float64NilPtr(), nil, ""},
    51  				{"non nil", 1.0, doubleOneBytes, ""},
    52  				{"conversion failed", int32(42), nil, fmt.Sprintf("cannot encode int32 as CQL double with %v: cannot convert from int32 to float64: conversion not supported", version)},
    53  			}
    54  			for _, tt := range tests {
    55  				t.Run(tt.name, func(t *testing.T) {
    56  					actual, err := Double.Encode(tt.source, version)
    57  					assert.Equal(t, tt.expected, actual)
    58  					assertErrorMessage(t, tt.err, err)
    59  				})
    60  			}
    61  		})
    62  	}
    63  }
    64  
    65  func Test_doubleCodec_Decode(t *testing.T) {
    66  	for _, version := range primitive.SupportedProtocolVersions() {
    67  		t.Run(version.String(), func(t *testing.T) {
    68  			tests := []struct {
    69  				name     string
    70  				source   []byte
    71  				dest     interface{}
    72  				expected interface{}
    73  				wasNull  bool
    74  				err      string
    75  			}{
    76  				{"null", nil, new(float64), new(float64), true, ""},
    77  				{"non null", doubleOneBytes, new(float64), float64Ptr(1), false, ""},
    78  				{"non null interface", doubleOneBytes, new(interface{}), interfacePtr(1.0), false, ""},
    79  				{"read failed", []byte{1}, new(float64), new(float64), false, fmt.Sprintf("cannot decode CQL double as *float64 with %v: cannot read float64: expected 8 bytes but got: 1", version)},
    80  				{"conversion failed", doubleOneBytes, new(int64), new(int64), false, fmt.Sprintf("cannot decode CQL double as *int64 with %v: cannot convert from float64 to *int64: conversion not supported", version)},
    81  			}
    82  			for _, tt := range tests {
    83  				t.Run(tt.name, func(t *testing.T) {
    84  					wasNull, err := Double.Decode(tt.source, tt.dest, version)
    85  					assert.Equal(t, tt.expected, tt.dest)
    86  					assert.Equal(t, tt.wasNull, wasNull)
    87  					assertErrorMessage(t, tt.err, err)
    88  				})
    89  			}
    90  		})
    91  	}
    92  }
    93  
    94  func Test_convertToFloat64(t *testing.T) {
    95  	tests := []struct {
    96  		name     string
    97  		input    interface{}
    98  		expected float64
    99  		wasNil   bool
   100  		err      string
   101  	}{
   102  		{"from float64", float64(1), 1, false, ""},
   103  		{"from *float64 non nil", float64Ptr(1), 1, false, ""},
   104  		{"from *float64 nil", float64NilPtr(), 0, true, ""},
   105  		{"from float32", float32(1), 1, false, ""},
   106  		{"from *float32 non nil", float32Ptr(1), 1, false, ""},
   107  		{"from *float32 nil", float32NilPtr(), 0, true, ""},
   108  		{"from *big.Float non nil", big.NewFloat(1), 1, false, ""},
   109  		{"from *big.Float out of range", new(big.Float).SetUint64(math.MaxUint64), 0, false, "cannot convert from *big.Float to float64: value out of range: 1.8446744073709551615e+19"},
   110  		{"from *big.Float nil", bigFloatNilPtr(), 0, true, ""},
   111  		{"from untyped nil", nil, 0, true, ""},
   112  		{"from unsupported value type", 42, 0, false, "cannot convert from int to float64: conversion not supported"},
   113  		{"from unsupported pointer type", int32Ptr(42), 0, false, "cannot convert from *int32 to float64: conversion not supported"},
   114  	}
   115  	for _, tt := range tests {
   116  		t.Run(tt.name, func(t *testing.T) {
   117  			dest, wasNil, err := convertToFloat64(tt.input)
   118  			assert.Equal(t, tt.expected, dest)
   119  			assert.Equal(t, tt.wasNil, wasNil)
   120  			assertErrorMessage(t, tt.err, err)
   121  		})
   122  	}
   123  }
   124  
   125  func Test_convertFromFloat64(t *testing.T) {
   126  	tests := []struct {
   127  		name     string
   128  		val      float64
   129  		wasNull  bool
   130  		dest     interface{}
   131  		expected interface{}
   132  		err      string
   133  	}{
   134  		{"to *interface{} nil dest", 1, false, interfaceNilPtr(), interfaceNilPtr(), "cannot convert from float64 to *interface {}: destination is nil"},
   135  		{"to *interface{} nil source", 0, true, new(interface{}), new(interface{}), ""},
   136  		{"to *interface{} non nil", 1, false, new(interface{}), interfacePtr(float64(1)), ""},
   137  		{"to *float64 nil dest", 1, false, float64NilPtr(), float64NilPtr(), "cannot convert from float64 to *float64: destination is nil"},
   138  		{"to *float64 nil source", 0, true, new(float64), float64Ptr(0), ""},
   139  		{"to *float64 non nil", 1, false, new(float64), float64Ptr(1), ""},
   140  		{"to *float32 nil dest", 1, false, float32NilPtr(), float32NilPtr(), "cannot convert from float64 to *float32: destination is nil"},
   141  		{"to *float32 nil source", 0, true, new(float32), float32Ptr(0), ""},
   142  		{"to *float32 non nil", 1, false, new(float32), float32Ptr(1), ""},
   143  		{"to *float32 out of range pos", math.MaxFloat64, false, new(float32), new(float32), "cannot convert from float64 to *float32: value out of range: 1.7976931348623157e+308"},
   144  		{"to *big.Float nil dest", 1, false, bigFloatNilPtr(), bigFloatNilPtr(), "cannot convert from float64 to *big.Float: destination is nil"},
   145  		{"to *big.Float nil source", 0, true, new(big.Float), new(big.Float), ""},
   146  		{"to *big.Float non nil", 1, false, big.NewFloat(1), big.NewFloat(1), ""},
   147  		{"to untyped nil", 1, false, nil, nil, "cannot convert from float64 to <nil>: destination is nil"},
   148  		{"to non pointer", 1, false, int64(0), int64(0), "cannot convert from float64 to int64: destination is not pointer"},
   149  		{"to unsupported pointer type", 1, false, new(int64), new(int64), "cannot convert from float64 to *int64: conversion not supported"},
   150  	}
   151  	for _, tt := range tests {
   152  		t.Run(tt.name, func(t *testing.T) {
   153  			err := convertFromFloat64(tt.val, tt.wasNull, tt.dest)
   154  			assert.Equal(t, tt.expected, tt.dest)
   155  			assertErrorMessage(t, tt.err, err)
   156  		})
   157  	}
   158  }
   159  
   160  func Test_writeFloat64(t *testing.T) {
   161  	tests := []struct {
   162  		name     string
   163  		val      float64
   164  		expected []byte
   165  	}{
   166  		{"zero", 0, doubleZeroBytes},
   167  		{"1", 1, doubleOneBytes},
   168  		{"-1", -1, doubleMinusOneBytes},
   169  		{"simple pos", 123.4, encodeUint64(0x405ed9999999999a)},
   170  		{"simple neg", -123.4, encodeUint64(0xc05ed9999999999a)},
   171  		{"max", math.MaxFloat64, doubleMaxFloat64Bytes},
   172  	}
   173  	for _, tt := range tests {
   174  		t.Run(tt.name, func(t *testing.T) {
   175  			actual := writeFloat64(tt.val)
   176  			assert.Equal(t, tt.expected, actual)
   177  		})
   178  	}
   179  }
   180  
   181  func Test_readFloat64(t *testing.T) {
   182  	tests := []struct {
   183  		name     string
   184  		source   []byte
   185  		expected float64
   186  		wasNull  bool
   187  		err      string
   188  	}{
   189  		{"nil", nil, 0, true, ""},
   190  		{"empty", []byte{}, 0, true, ""},
   191  		{"wrong length", []byte{1}, 0, false, "cannot read float64: expected 8 bytes but got: 1"},
   192  		{"zero", doubleZeroBytes, 0, false, ""},
   193  		{"1", doubleOneBytes, 1, false, ""},
   194  		{"-1", doubleMinusOneBytes, -1, false, ""},
   195  		{"simple pos", encodeUint64(0x405ed9999999999a), 123.4, false, ""},
   196  		{"simple neg", encodeUint64(0xc05ed9999999999a), -123.4, false, ""},
   197  		{"max", doubleMaxFloat64Bytes, math.MaxFloat64, false, ""},
   198  	}
   199  	for _, tt := range tests {
   200  		t.Run(tt.name, func(t *testing.T) {
   201  			actual, wasNull, err := readFloat64(tt.source)
   202  			assert.Equal(t, tt.expected, actual)
   203  			assert.Equal(t, tt.wasNull, wasNull)
   204  			assertErrorMessage(t, tt.err, err)
   205  		})
   206  	}
   207  }