blob: e9927f09f308fd6993c9d53e76c437f2300b72ff [file] [log] [blame]
// 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 array_test
import (
"fmt"
"math"
"testing"
"github.com/apache/arrow/go/arrow/array"
"github.com/apache/arrow/go/arrow/float16"
"github.com/apache/arrow/go/arrow/internal/arrdata"
"github.com/apache/arrow/go/arrow/memory"
)
func TestArrayEqual(t *testing.T) {
for name, recs := range arrdata.Records {
t.Run(name, func(t *testing.T) {
rec := recs[0]
schema := rec.Schema()
for i, col := range rec.Columns() {
t.Run(schema.Field(i).Name, func(t *testing.T) {
arr := col
if !array.ArrayEqual(arr, arr) {
t.Fatalf("identical arrays should compare equal:\narray=%v", arr)
}
sub1 := array.NewSlice(arr, 1, int64(arr.Len()))
defer sub1.Release()
sub2 := array.NewSlice(arr, 0, int64(arr.Len()-1))
defer sub2.Release()
if array.ArrayEqual(sub1, sub2) {
t.Fatalf("non-identical arrays should not compare equal:\nsub1=%v\nsub2=%v\narrf=%v\n", sub1, sub2, arr)
}
})
}
})
}
}
func TestArraySliceEqual(t *testing.T) {
for name, recs := range arrdata.Records {
t.Run(name, func(t *testing.T) {
rec := recs[0]
schema := rec.Schema()
for i, col := range rec.Columns() {
t.Run(schema.Field(i).Name, func(t *testing.T) {
arr := col
if !array.ArraySliceEqual(
arr, 0, int64(arr.Len()),
arr, 0, int64(arr.Len()),
) {
t.Fatalf("identical slices should compare equal:\narray=%v", arr)
}
sub1 := array.NewSlice(arr, 1, int64(arr.Len()))
defer sub1.Release()
sub2 := array.NewSlice(arr, 0, int64(arr.Len()-1))
defer sub2.Release()
if array.ArraySliceEqual(sub1, 0, int64(sub1.Len()), sub2, 0, int64(sub2.Len())) {
t.Fatalf("non-identical slices should not compare equal:\nsub1=%v\nsub2=%v\narrf=%v\n", sub1, sub2, arr)
}
})
}
})
}
}
func TestArrayApproxEqual(t *testing.T) {
for name, recs := range arrdata.Records {
t.Run(name, func(t *testing.T) {
rec := recs[0]
schema := rec.Schema()
for i, col := range rec.Columns() {
t.Run(schema.Field(i).Name, func(t *testing.T) {
arr := col
if !array.ArrayApproxEqual(arr, arr) {
t.Fatalf("identical arrays should compare equal:\narray=%v", arr)
}
sub1 := array.NewSlice(arr, 1, int64(arr.Len()))
defer sub1.Release()
sub2 := array.NewSlice(arr, 0, int64(arr.Len()-1))
defer sub2.Release()
if array.ArrayApproxEqual(sub1, sub2) {
t.Fatalf("non-identical arrays should not compare equal:\nsub1=%v\nsub2=%v\narrf=%v\n", sub1, sub2, arr)
}
})
}
})
}
}
func TestArrayApproxEqualFloats(t *testing.T) {
f16sFrom := func(vs []float64) []float16.Num {
o := make([]float16.Num, len(vs))
for i, v := range vs {
o[i] = float16.New(float32(v))
}
return o
}
for _, tc := range []struct {
name string
a1 interface{}
a2 interface{}
opts []array.EqualOption
want bool
}{
{
name: "f16",
a1: f16sFrom([]float64{1, 2, 3, 4, 5, 6}),
a2: f16sFrom([]float64{1, 2, 3, 4, 5, 6}),
want: true,
},
{
name: "f16-no-tol",
a1: f16sFrom([]float64{1, 2, 3, 4, 5, 6}),
a2: f16sFrom([]float64{1, 2, 3, 4, 5, 7}),
want: false,
},
{
name: "f16-tol-ok",
a1: f16sFrom([]float64{1, 2, 3, 4, 5, 6}),
a2: f16sFrom([]float64{1, 2, 3, 4, 5, 7}),
opts: []array.EqualOption{array.WithAbsTolerance(1)},
want: true,
},
{
name: "f16-nan",
a1: f16sFrom([]float64{1, 2, 3, 4, 5, 6}),
a2: f16sFrom([]float64{1, 2, 3, 4, 5, math.NaN()}),
want: false,
},
{
name: "f16-nan-not",
a1: f16sFrom([]float64{1, 2, 3, 4, 5, 6}),
a2: f16sFrom([]float64{1, 2, 3, 4, 5, math.NaN()}),
opts: []array.EqualOption{array.WithNaNsEqual(true)},
want: false,
},
{
name: "f16-nan-ok",
a1: f16sFrom([]float64{1, 2, 3, 4, 5, math.NaN()}),
a2: f16sFrom([]float64{1, 2, 3, 4, 5, math.NaN()}),
opts: []array.EqualOption{array.WithNaNsEqual(true)},
want: true,
},
{
name: "f16-nan-no-tol",
a1: f16sFrom([]float64{1, 2, 3, 4, 5, math.NaN()}),
a2: f16sFrom([]float64{1, 2, 3, 4, 6, math.NaN()}),
opts: []array.EqualOption{array.WithNaNsEqual(true)},
want: false,
},
{
name: "f16-nan-tol",
a1: f16sFrom([]float64{1, 2, 3, 4, 5, math.NaN()}),
a2: f16sFrom([]float64{1, 2, 3, 4, 6, math.NaN()}),
opts: []array.EqualOption{array.WithNaNsEqual(true), array.WithAbsTolerance(1)},
want: true,
},
{
name: "f32",
a1: []float32{1, 2, 3, 4, 5, 6},
a2: []float32{1, 2, 3, 4, 5, 6},
want: true,
},
{
name: "f32-no-tol",
a1: []float32{1, 2, 3, 4, 5, 6},
a2: []float32{1, 2, 3, 4, 5, 7},
want: false,
},
{
name: "f32-tol-ok",
a1: []float32{1, 2, 3, 4, 5, 6},
a2: []float32{1, 2, 3, 4, 5, 7},
opts: []array.EqualOption{array.WithAbsTolerance(1)},
want: true,
},
{
name: "f32-nan",
a1: []float32{1, 2, 3, 4, 5, 6},
a2: []float32{1, 2, 3, 4, 5, float32(math.NaN())},
want: false,
},
{
name: "f32-nan-not",
a1: []float32{1, 2, 3, 4, 5, 6},
a2: []float32{1, 2, 3, 4, 5, float32(math.NaN())},
opts: []array.EqualOption{array.WithNaNsEqual(true)},
want: false,
},
{
name: "f32-nan-ok",
a1: []float32{1, 2, 3, 4, 5, float32(math.NaN())},
a2: []float32{1, 2, 3, 4, 5, float32(math.NaN())},
opts: []array.EqualOption{array.WithNaNsEqual(true)},
want: true,
},
{
name: "f32-nan-no-tol",
a1: []float32{1, 2, 3, 4, 5, float32(math.NaN())},
a2: []float32{1, 2, 3, 4, 6, float32(math.NaN())},
opts: []array.EqualOption{array.WithNaNsEqual(true)},
want: false,
},
{
name: "f32-nan-tol",
a1: []float32{1, 2, 3, 4, 5, float32(math.NaN())},
a2: []float32{1, 2, 3, 4, 6, float32(math.NaN())},
opts: []array.EqualOption{array.WithNaNsEqual(true), array.WithAbsTolerance(1)},
want: true,
},
{
name: "f64",
a1: []float64{1, 2, 3, 4, 5, 6},
a2: []float64{1, 2, 3, 4, 5, 6},
want: true,
},
{
name: "f64-no-tol",
a1: []float64{1, 2, 3, 4, 5, 6},
a2: []float64{1, 2, 3, 4, 5, 7},
want: false,
},
{
name: "f64-tol-ok",
a1: []float64{1, 2, 3, 4, 5, 6},
a2: []float64{1, 2, 3, 4, 5, 7},
opts: []array.EqualOption{array.WithAbsTolerance(1)},
want: true,
},
{
name: "f64-nan",
a1: []float64{1, 2, 3, 4, 5, 6},
a2: []float64{1, 2, 3, 4, 5, math.NaN()},
want: false,
},
{
name: "f64-nan-not",
a1: []float64{1, 2, 3, 4, 5, 6},
a2: []float64{1, 2, 3, 4, 5, math.NaN()},
opts: []array.EqualOption{array.WithNaNsEqual(true)},
want: false,
},
{
name: "f64-nan-ok",
a1: []float64{1, 2, 3, 4, 5, math.NaN()},
a2: []float64{1, 2, 3, 4, 5, math.NaN()},
opts: []array.EqualOption{array.WithNaNsEqual(true)},
want: true,
},
{
name: "f64-nan-no-tol",
a1: []float64{1, 2, 3, 4, 5, math.NaN()},
a2: []float64{1, 2, 3, 4, 6, math.NaN()},
opts: []array.EqualOption{array.WithNaNsEqual(true)},
want: false,
},
{
name: "f64-nan-tol",
a1: []float64{1, 2, 3, 4, 5, math.NaN()},
a2: []float64{1, 2, 3, 4, 6, math.NaN()},
opts: []array.EqualOption{array.WithNaNsEqual(true), array.WithAbsTolerance(1)},
want: true,
},
} {
t.Run(tc.name, func(t *testing.T) {
mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
defer mem.AssertSize(t, 0)
a1 := arrayOf(mem, tc.a1, nil)
defer a1.Release()
a2 := arrayOf(mem, tc.a2, nil)
defer a2.Release()
if got, want := array.ArrayApproxEqual(a1, a2, tc.opts...), tc.want; got != want {
t.Fatalf("invalid comparison: got=%v, want=%v\na1: %v\na2: %v\n", got, want, a1, a2)
}
})
}
}
func arrayOf(mem memory.Allocator, a interface{}, valids []bool) array.Interface {
if mem == nil {
mem = memory.NewGoAllocator()
}
switch a := a.(type) {
case []float16.Num:
bldr := array.NewFloat16Builder(mem)
defer bldr.Release()
bldr.AppendValues(a, valids)
return bldr.NewFloat16Array()
case []float32:
bldr := array.NewFloat32Builder(mem)
defer bldr.Release()
bldr.AppendValues(a, valids)
return bldr.NewFloat32Array()
case []float64:
bldr := array.NewFloat64Builder(mem)
defer bldr.Release()
bldr.AppendValues(a, valids)
return bldr.NewFloat64Array()
default:
panic(fmt.Errorf("arrdata: invalid data slice type %T", a))
}
}
func TestArrayEqualBaseArray(t *testing.T) {
mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
defer mem.AssertSize(t, 0)
b1 := array.NewBooleanBuilder(mem)
defer b1.Release()
b1.Append(true)
a1 := b1.NewBooleanArray()
defer a1.Release()
b2 := array.NewBooleanBuilder(mem)
defer b2.Release()
a2 := b2.NewBooleanArray()
defer a2.Release()
if array.ArrayEqual(a1, a2) {
t.Errorf("two arrays with different lengths must not be equal")
}
b3 := array.NewBooleanBuilder(mem)
defer b3.Release()
b3.AppendNull()
a3 := b3.NewBooleanArray()
defer a3.Release()
if array.ArrayEqual(a1, a3) {
t.Errorf("two arrays with different number of null values must not be equal")
}
b4 := array.NewInt32Builder(mem)
defer b4.Release()
b4.Append(0)
a4 := b4.NewInt32Array()
defer a4.Release()
if array.ArrayEqual(a1, a4) {
t.Errorf("two arrays with different types must not be equal")
}
b5 := array.NewBooleanBuilder(mem)
defer b5.Release()
b5.AppendNull()
b5.Append(true)
a5 := b5.NewBooleanArray()
defer a5.Release()
b1.AppendNull()
if array.ArrayEqual(a1, a5) {
t.Errorf("two arrays with different validity bitmaps must not be equal")
}
}
func TestArrayEqualNull(t *testing.T) {
mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
defer mem.AssertSize(t, 0)
null := array.NewNull(0)
defer null.Release()
if !array.ArrayEqual(null, null) {
t.Fatalf("identical arrays should compare equal")
}
n0 := array.NewNull(10)
defer n0.Release()
n1 := array.NewNull(10)
defer n1.Release()
if !array.ArrayEqual(n0, n0) {
t.Fatalf("identical arrays should compare equal")
}
if !array.ArrayEqual(n1, n1) {
t.Fatalf("identical arrays should compare equal")
}
if !array.ArrayEqual(n0, n1) || !array.ArrayEqual(n1, n0) {
t.Fatalf("n0 and n1 should compare equal")
}
sub07 := array.NewSlice(n0, 0, 7)
defer sub07.Release()
sub08 := array.NewSlice(n0, 0, 8)
defer sub08.Release()
sub19 := array.NewSlice(n0, 1, 9)
defer sub19.Release()
if !array.ArrayEqual(sub08, sub19) {
t.Fatalf("sub08 and sub19 should compare equal")
}
if array.ArrayEqual(sub08, sub07) {
t.Fatalf("sub08 and sub07 should not compare equal")
}
}
func TestArrayEqualMaskedArray(t *testing.T) {
mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
defer mem.AssertSize(t, 0)
ab := array.NewInt32Builder(mem)
defer ab.Release()
valids := []bool{false, false, false, false}
ab.AppendValues([]int32{1, 2, 0, 4}, valids)
a1 := ab.NewInt32Array()
defer a1.Release()
ab.AppendValues([]int32{1, 2, 3, 4}, valids)
a2 := ab.NewInt32Array()
defer a2.Release()
if !array.ArrayEqual(a1, a1) || !array.ArrayEqual(a2, a2) {
t.Errorf("an array must be equal to itself")
}
if !array.ArrayEqual(a1, a2) {
t.Errorf("%v must be equal to %v", a1, a2)
}
}
func TestArrayEqualDifferentMaskedValues(t *testing.T) {
// test 2 int32 arrays, with same nulls (but different masked values) compare equal.
mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
defer mem.AssertSize(t, 0)
ab := array.NewInt32Builder(mem)
defer ab.Release()
valids := []bool{true, true, false, true}
ab.AppendValues([]int32{1, 2, 0, 4}, valids)
a1 := ab.NewInt32Array()
defer a1.Release()
ab.AppendValues([]int32{1, 2, 3, 4}, valids)
a2 := ab.NewInt32Array()
defer a2.Release()
if !array.ArrayEqual(a1, a1) || !array.ArrayEqual(a2, a2) {
t.Errorf("an array must be equal to itself")
}
if !array.ArrayEqual(a1, a2) {
t.Errorf("%v must be equal to %v", a1, a2)
}
}
func TestRecordEqual(t *testing.T) {
for name, recs := range arrdata.Records {
t.Run(name, func(t *testing.T) {
rec0 := recs[0]
rec1 := recs[1]
if !array.RecordEqual(rec0, rec0) {
t.Fatalf("identical records should compare equal:\nrecord:\n%v", rec0)
}
if array.RecordEqual(rec0, rec1) {
t.Fatalf("non-identical records should not compare equal:\nrec0:\n%v\nrec1:\n%v", rec0, rec1)
}
sub00 := rec0.NewSlice(0, recs[0].NumRows()-1)
defer sub00.Release()
sub01 := rec0.NewSlice(1, recs[0].NumRows())
defer sub01.Release()
if array.RecordEqual(sub00, sub01) {
t.Fatalf("non-identical records should not compare equal:\nsub0:\n%v\nsub1:\n%v", sub00, sub01)
}
})
}
}
func TestRecordApproxEqual(t *testing.T) {
for name, recs := range arrdata.Records {
t.Run(name, func(t *testing.T) {
rec0 := recs[0]
rec1 := recs[1]
if !array.RecordApproxEqual(rec0, rec0) {
t.Fatalf("identical records should compare equal:\nrecord:\n%v", rec0)
}
if array.RecordApproxEqual(rec0, rec1) {
t.Fatalf("non-identical records should not compare equal:\nrec0:\n%v\nrec1:\n%v", rec0, rec1)
}
sub00 := rec0.NewSlice(0, recs[0].NumRows()-1)
defer sub00.Release()
sub01 := rec0.NewSlice(1, recs[0].NumRows())
defer sub01.Release()
if array.RecordApproxEqual(sub00, sub01) {
t.Fatalf("non-identical records should not compare equal:\nsub0:\n%v\nsub1:\n%v", sub00, sub01)
}
})
}
}