github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/datacodec/float_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  	"testing"
    21  
    22  	"github.com/stretchr/testify/assert"
    23  
    24  	"github.com/datastax/go-cassandra-native-protocol/datatype"
    25  	"github.com/datastax/go-cassandra-native-protocol/primitive"
    26  )
    27  
    28  var (
    29  	floatZeroBytes       = encodeUint32(0x00000000)
    30  	floatOneBytes        = encodeUint32(0x3f800000)
    31  	floatMinusOneBytes   = encodeUint32(0xbf800000)
    32  	floatMaxFloat32Bytes = encodeUint32(0x7f7fffff)
    33  )
    34  
    35  func Test_floatCodec_DataType(t *testing.T) {
    36  	assert.Equal(t, datatype.Float, Float.DataType())
    37  }
    38  
    39  func Test_floatCodec_Encode(t *testing.T) {
    40  	for _, version := range primitive.SupportedProtocolVersions() {
    41  		t.Run(version.String(), func(t *testing.T) {
    42  			tests := []struct {
    43  				name     string
    44  				source   interface{}
    45  				expected []byte
    46  				err      string
    47  			}{
    48  				{"nil", nil, nil, ""},
    49  				{"nil pointer", float32NilPtr(), nil, ""},
    50  				{"non nil", 1.0, floatOneBytes, ""},
    51  				{"conversion failed", int32(42), nil, fmt.Sprintf("cannot encode int32 as CQL float with %v: cannot convert from int32 to float32: conversion not supported", version)},
    52  			}
    53  			for _, tt := range tests {
    54  				t.Run(tt.name, func(t *testing.T) {
    55  					actual, err := Float.Encode(tt.source, version)
    56  					assert.Equal(t, tt.expected, actual)
    57  					assertErrorMessage(t, tt.err, err)
    58  				})
    59  			}
    60  		})
    61  	}
    62  }
    63  
    64  func Test_floatCodec_Decode(t *testing.T) {
    65  	for _, version := range primitive.SupportedProtocolVersions() {
    66  		t.Run(version.String(), func(t *testing.T) {
    67  			tests := []struct {
    68  				name     string
    69  				source   []byte
    70  				dest     interface{}
    71  				expected interface{}
    72  				wasNull  bool
    73  				err      string
    74  			}{
    75  				{"null", nil, new(float32), new(float32), true, ""},
    76  				{"non null", floatOneBytes, new(float32), float32Ptr(1), false, ""},
    77  				{"non null interface", floatOneBytes, new(interface{}), interfacePtr(float32(1.0)), false, ""},
    78  				{"read failed", []byte{1}, new(float32), new(float32), false, fmt.Sprintf("cannot decode CQL float as *float32 with %v: cannot read float32: expected 4 bytes but got: 1", version)},
    79  				{"conversion failed", floatOneBytes, new(int64), new(int64), false, fmt.Sprintf("cannot decode CQL float as *int64 with %v: cannot convert from float32 to *int64: conversion not supported", version)},
    80  			}
    81  			for _, tt := range tests {
    82  				t.Run(tt.name, func(t *testing.T) {
    83  					wasNull, err := Float.Decode(tt.source, tt.dest, version)
    84  					assert.Equal(t, tt.expected, tt.dest)
    85  					assert.Equal(t, tt.wasNull, wasNull)
    86  					assertErrorMessage(t, tt.err, err)
    87  				})
    88  			}
    89  		})
    90  	}
    91  }
    92  
    93  func Test_convertToFloat32(t *testing.T) {
    94  	tests := []struct {
    95  		name     string
    96  		input    interface{}
    97  		expected float32
    98  		wasNil   bool
    99  		err      string
   100  	}{
   101  		{"from float64", float64(1), 1, false, ""},
   102  		{"from float64 out of range", math.MaxFloat64, 0, false, "cannot convert from float64 to float32: value out of range: 1.7976931348623157e+308"},
   103  		{"from *float64 non nil", float64Ptr(1), 1, false, ""},
   104  		{"from *float64 nil", float64NilPtr(), 0, true, ""},
   105  		{"from *float64 out of range", float64Ptr(math.MaxFloat64), 0, false, "cannot convert from *float64 to float32: value out of range: 1.7976931348623157e+308"},
   106  		{"from float32", float32(1), 1, false, ""},
   107  		{"from *float32 non nil", float32Ptr(1), 1, false, ""},
   108  		{"from *float32 nil", float32NilPtr(), 0, true, ""},
   109  		{"from untyped nil", nil, 0, true, ""},
   110  		{"from unsupported value type", 42, 0, false, "cannot convert from int to float32: conversion not supported"},
   111  		{"from unsupported pointer type", int32Ptr(42), 0, false, "cannot convert from *int32 to float32: conversion not supported"},
   112  	}
   113  	for _, tt := range tests {
   114  		t.Run(tt.name, func(t *testing.T) {
   115  			dest, wasNil, err := convertToFloat32(tt.input)
   116  			assert.Equal(t, tt.expected, dest)
   117  			assert.Equal(t, tt.wasNil, wasNil)
   118  			assertErrorMessage(t, tt.err, err)
   119  		})
   120  	}
   121  }
   122  
   123  func Test_convertFromFloat32(t *testing.T) {
   124  	tests := []struct {
   125  		name     string
   126  		val      float32
   127  		wasNull  bool
   128  		dest     interface{}
   129  		expected interface{}
   130  		err      string
   131  	}{
   132  		{"to *interface{} nil dest", 1, false, interfaceNilPtr(), interfaceNilPtr(), "cannot convert from float32 to *interface {}: destination is nil"},
   133  		{"to *interface{} nil source", 0, true, new(interface{}), new(interface{}), ""},
   134  		{"to *interface{} non nil", 1, false, new(interface{}), interfacePtr(float32(1)), ""},
   135  		{"to *float64 nil dest", 1, false, float64NilPtr(), float64NilPtr(), "cannot convert from float32 to *float64: destination is nil"},
   136  		{"to *float64 nil source", 0, true, new(float64), float64Ptr(0), ""},
   137  		{"to *float64 non nil", 1, false, new(float64), float64Ptr(1), ""},
   138  		{"to *float32 nil dest", 1, false, float32NilPtr(), float32NilPtr(), "cannot convert from float32 to *float32: destination is nil"},
   139  		{"to *float32 nil source", 0, true, new(float32), float32Ptr(0), ""},
   140  		{"to *float32 non nil", 1, false, new(float32), float32Ptr(1), ""},
   141  		{"to untyped nil", 1, false, nil, nil, "cannot convert from float32 to <nil>: destination is nil"},
   142  		{"to non pointer", 1, false, int64(0), int64(0), "cannot convert from float32 to int64: destination is not pointer"},
   143  		{"to unsupported pointer type", 1, false, new(int64), new(int64), "cannot convert from float32 to *int64: conversion not supported"},
   144  	}
   145  	for _, tt := range tests {
   146  		t.Run(tt.name, func(t *testing.T) {
   147  			err := convertFromFloat32(tt.val, tt.wasNull, tt.dest)
   148  			assert.Equal(t, tt.expected, tt.dest)
   149  			assertErrorMessage(t, tt.err, err)
   150  		})
   151  	}
   152  }
   153  
   154  func Test_writeFloat32(t *testing.T) {
   155  	tests := []struct {
   156  		name     string
   157  		val      float32
   158  		expected []byte
   159  	}{
   160  		{"zero", 0, floatZeroBytes},
   161  		{"1", 1, floatOneBytes},
   162  		{"-1", -1, floatMinusOneBytes},
   163  		{"simple pos", 123.4, encodeUint32(0x42f6cccd)},
   164  		{"simple neg", -123.4, encodeUint32(0xc2f6cccd)},
   165  		{"max", math.MaxFloat32, floatMaxFloat32Bytes},
   166  	}
   167  	for _, tt := range tests {
   168  		t.Run(tt.name, func(t *testing.T) {
   169  			actual := writeFloat32(tt.val)
   170  			assert.Equal(t, tt.expected, actual)
   171  		})
   172  	}
   173  }
   174  
   175  func Test_readFloat32(t *testing.T) {
   176  	tests := []struct {
   177  		name     string
   178  		source   []byte
   179  		expected float32
   180  		wasNull  bool
   181  		err      string
   182  	}{
   183  		{"nil", nil, 0, true, ""},
   184  		{"empty", []byte{}, 0, true, ""},
   185  		{"wrong length", []byte{1}, 0, false, "cannot read float32: expected 4 bytes but got: 1"},
   186  		{"zero", floatZeroBytes, 0, false, ""},
   187  		{"1", floatOneBytes, 1, false, ""},
   188  		{"-1", floatMinusOneBytes, -1, false, ""},
   189  		{"simple pos", encodeUint32(0x42f6cccd), 123.4, false, ""},
   190  		{"simple neg", encodeUint32(0xc2f6cccd), -123.4, false, ""},
   191  		{"max", floatMaxFloat32Bytes, math.MaxFloat32, false, ""},
   192  	}
   193  	for _, tt := range tests {
   194  		t.Run(tt.name, func(t *testing.T) {
   195  			actual, wasNull, err := readFloat32(tt.source)
   196  			assert.Equal(t, tt.expected, actual)
   197  			assert.Equal(t, tt.wasNull, wasNull)
   198  			assertErrorMessage(t, tt.err, err)
   199  		})
   200  	}
   201  }