blob: 957a4f9c01085be3d709413d91405fa28bd1d49e [file]
// 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/rand"
"reflect"
"strings"
"testing"
"github.com/apache/arrow-go/v18/arrow"
"github.com/apache/arrow-go/v18/arrow/array"
"github.com/apache/arrow-go/v18/arrow/extensions"
"github.com/apache/arrow-go/v18/arrow/memory"
"github.com/apache/arrow-go/v18/internal/json"
)
type diffTestCase struct {
dataType arrow.DataType
baseJSON string
targetJSON string
wantInsert []bool
wantRunLength []int64
}
func (s *diffTestCase) check(t *testing.T) {
mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
defer mem.AssertSize(t, 0)
base, _, err := array.FromJSON(mem, s.dataType, strings.NewReader(s.baseJSON))
if err != nil {
t.Fatal(err)
}
defer base.Release()
target, _, err := array.FromJSON(mem, s.dataType, strings.NewReader(s.targetJSON))
if err != nil {
t.Fatal(err)
}
defer target.Release()
edits, err := array.Diff(base, target)
if err != nil {
t.Fatalf("got unexpected error %v", err)
}
gotInserts := make([]bool, len(edits))
gotRunLengths := make([]int64, len(edits))
for i, edit := range edits {
gotInserts[i] = edit.Insert
gotRunLengths[i] = edit.RunLength
}
if !reflect.DeepEqual(gotInserts, s.wantInsert) {
t.Errorf("Diff(\n base=%v, \ntarget=%v\n) got insert %v, want %v", base, target, gotInserts, s.wantInsert)
}
if !reflect.DeepEqual(gotRunLengths, s.wantRunLength) {
t.Errorf("Diff(\n base=%v, \ntarget=%v\n) got run length %v, want %v", base, target, gotRunLengths, s.wantRunLength)
}
}
func TestDiff_Trivial(t *testing.T) {
cases := []struct {
name string
base string
target string
wantInsert []bool
wantRunLength []int64
}{
{
name: "empty",
base: `[]`,
target: `[]`,
wantInsert: []bool{false},
wantRunLength: []int64{0},
},
{
name: "nulls",
base: `[null, null]`,
target: `[null, null, null, null]`,
wantInsert: []bool{false, true, true},
wantRunLength: []int64{2, 0, 0},
},
{
name: "equal",
base: `[1, 2, 3]`,
target: `[1, 2, 3]`,
wantInsert: []bool{false},
wantRunLength: []int64{3},
},
}
for _, tc := range cases {
d := diffTestCase{
dataType: arrow.PrimitiveTypes.Int32,
baseJSON: tc.base,
targetJSON: tc.target,
wantInsert: tc.wantInsert,
wantRunLength: tc.wantRunLength,
}
t.Run(tc.name, d.check)
}
}
func TestDiff_Basics(t *testing.T) {
cases := []struct {
name string
base string
target string
wantInsert []bool
wantRunLength []int64
}{
{
name: "insert one",
base: `[1, 2, null, 5]`,
target: `[1, 2, 3, null, 5]`,
wantInsert: []bool{false, true},
wantRunLength: []int64{2, 2},
},
{
name: "delete one",
base: `[1, 2, 3, null, 5]`,
target: `[1, 2, null, 5]`,
wantInsert: []bool{false, false},
wantRunLength: []int64{2, 2},
},
{
name: "change one",
base: `[1, 2, 3, null, 5]`,
target: `[1, 2, 23, null, 5]`,
wantInsert: []bool{false, false, true},
wantRunLength: []int64{2, 0, 2},
},
{
name: "null out one",
base: `[1, 2, 3, null, 5]`,
target: `[1, 2, null, null, 5]`,
wantInsert: []bool{false, false, true},
wantRunLength: []int64{2, 1, 1},
},
{
name: "append some",
base: `[1, 2, 3, null, 5]`,
target: `[1, 2, 3, null, 5, 6, 7, 8, 9]`,
wantInsert: []bool{false, true, true, true, true},
wantRunLength: []int64{5, 0, 0, 0, 0},
},
{
name: "prepend some",
base: `[1, 2, 3, null, 5]`,
target: `[6, 4, 2, 0, 1, 2, 3, null, 5]`,
wantInsert: []bool{false, true, true, true, true},
wantRunLength: []int64{0, 0, 0, 0, 5},
},
}
for _, tc := range cases {
d := diffTestCase{
dataType: arrow.PrimitiveTypes.Int32,
baseJSON: tc.base,
targetJSON: tc.target,
wantInsert: tc.wantInsert,
wantRunLength: tc.wantRunLength,
}
t.Run(tc.name, d.check)
}
}
func TestDiff_BasicsWithBooleans(t *testing.T) {
cases := []struct {
name string
base string
target string
wantInsert []bool
wantRunLength []int64
}{
{
name: "insert one",
base: `[true, true, true]`,
target: `[true, false, true, true]`,
wantInsert: []bool{false, true},
wantRunLength: []int64{1, 2},
},
{
name: "delete one",
base: `[true, false, true, true]`,
target: `[true, true, true]`,
wantInsert: []bool{false, false},
wantRunLength: []int64{1, 2},
},
{
name: "change one",
base: `[false, false, true]`,
target: `[true, false, true]`,
wantInsert: []bool{false, false, true},
wantRunLength: []int64{0, 0, 2},
},
{
name: "null out one",
base: `[true, false, true]`,
target: `[true, false, null]`,
wantInsert: []bool{false, false, true},
wantRunLength: []int64{2, 0, 0},
},
}
for _, tc := range cases {
d := diffTestCase{
dataType: &arrow.BooleanType{},
baseJSON: tc.base,
targetJSON: tc.target,
wantInsert: tc.wantInsert,
wantRunLength: tc.wantRunLength,
}
t.Run(tc.name, d.check)
}
}
func TestDiff_BasicsWithStrings(t *testing.T) {
cases := []struct {
name string
base string
target string
wantInsert []bool
wantRunLength []int64
}{
{
name: "insert one",
base: `["give", "a", "break"]`,
target: `["give", "me", "a", "break"]`,
wantInsert: []bool{false, true},
wantRunLength: []int64{1, 2},
},
{
name: "delete one",
base: `["give", "me", "a", "break"]`,
target: `["give", "a", "break"]`,
wantInsert: []bool{false, false},
wantRunLength: []int64{1, 2},
},
{
name: "change one",
base: `["give", "a", "break"]`,
target: `["gimme", "a", "break"]`,
wantInsert: []bool{false, false, true},
wantRunLength: []int64{0, 0, 2},
},
{
name: "null out one",
base: `["give", "a", "break"]`,
target: `["give", "a", null]`,
wantInsert: []bool{false, false, true},
wantRunLength: []int64{2, 0, 0},
},
}
for _, tc := range cases {
d := diffTestCase{
dataType: &arrow.StringType{},
baseJSON: tc.base,
targetJSON: tc.target,
wantInsert: tc.wantInsert,
wantRunLength: tc.wantRunLength,
}
t.Run(tc.name, d.check)
}
}
func TestDiff_BasicsWithLists(t *testing.T) {
cases := []struct {
name string
base string
target string
wantInsert []bool
wantRunLength []int64
}{
{
name: "insert one",
base: `[[2, 3, 1], [], [13]]`,
target: `[[2, 3, 1], [5, 9], [], [13]]`,
wantInsert: []bool{false, true},
wantRunLength: []int64{1, 2},
},
{
name: "delete one",
base: `[[2, 3, 1], [5, 9], [], [13]]`,
target: `[[2, 3, 1], [], [13]]`,
wantInsert: []bool{false, false},
wantRunLength: []int64{1, 2},
},
{
name: "change one",
base: `[[2, 3, 1], [], [13]]`,
target: `[[3, 3, 3], [], [13]]`,
wantInsert: []bool{false, false, true},
wantRunLength: []int64{0, 0, 2},
},
{
name: "null out one",
base: `[[2, 3, 1], [], [13]]`,
target: `[[2, 3, 1], [], null]`,
wantInsert: []bool{false, false, true},
wantRunLength: []int64{2, 0, 0},
},
}
for _, tc := range cases {
d := diffTestCase{
dataType: arrow.ListOf(arrow.PrimitiveTypes.Int32),
baseJSON: tc.base,
targetJSON: tc.target,
wantInsert: tc.wantInsert,
wantRunLength: tc.wantRunLength,
}
t.Run(tc.name, d.check)
}
}
func TestDiff_BasicsWithStructs(t *testing.T) {
cases := []struct {
name string
base string
target string
wantInsert []bool
wantRunLength []int64
}{
{
name: "insert one",
base: `[{"foo": "!", "bar": 3}, {}, {"bar": 13}]`,
target: `[{"foo": "!", "bar": 3}, {"foo": "?"}, {}, {"bar": 13}]`,
wantInsert: []bool{false, true},
wantRunLength: []int64{1, 2},
},
{
name: "delete one",
base: `[{"foo": "!", "bar": 3}, {"foo": "?"}, {}, {"bar": 13}]`,
target: `[{"foo": "!", "bar": 3}, {}, {"bar": 13}]`,
wantInsert: []bool{false, false},
wantRunLength: []int64{1, 2},
},
{
name: "change one",
base: `[{"foo": "!", "bar": 3}, {}, {"bar": 13}]`,
target: `[{"foo": "!", "bar": 2}, {}, {"bar": 13}]`,
wantInsert: []bool{false, false, true},
wantRunLength: []int64{0, 0, 2},
},
{
name: "null out one",
base: `[{"foo": "!", "bar": 3}, {}, {"bar": 13}]`,
target: `[{"foo": "!", "bar": 3}, {}, null]`,
wantInsert: []bool{false, false, true},
wantRunLength: []int64{2, 0, 0},
},
}
for _, tc := range cases {
f1 := arrow.Field{Name: "foo", Type: arrow.BinaryTypes.String, Nullable: true}
f2 := arrow.Field{Name: "bar", Type: arrow.PrimitiveTypes.Int32, Nullable: true}
d := diffTestCase{
dataType: arrow.StructOf(f1, f2),
baseJSON: tc.base,
targetJSON: tc.target,
wantInsert: tc.wantInsert,
wantRunLength: tc.wantRunLength,
}
t.Run(tc.name, d.check)
}
}
func TestDiff_Random(t *testing.T) {
rng := rand.New(rand.NewSource(0xdeadbeef))
for i := 0; i < 100; i++ {
t.Run(fmt.Sprintf("case-%d", i), func(t *testing.T) {
testRandomCase(t, rng)
})
}
}
func testRandomCase(t *testing.T, rng *rand.Rand) {
mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
defer mem.AssertSize(t, 0)
dataType := arrow.PrimitiveTypes.Int32
baseValues := make([]int32, rng.Intn(10))
for i := range baseValues {
baseValues[i] = rng.Int31()
}
baseJSON, err := json.Marshal(baseValues)
if err != nil {
t.Fatal(err)
}
targetValues := make([]int32, rng.Intn(10))
for i := range targetValues {
// create runs with some probability
if rng.Intn(2) == 0 && len(baseValues) > 0 {
targetValues[i] = baseValues[rng.Intn(len(baseValues))]
} else {
targetValues[i] = rng.Int31()
}
}
targetJSON, err := json.Marshal(targetValues)
if err != nil {
t.Fatal(err)
}
base, _, err := array.FromJSON(mem, dataType, strings.NewReader(string(baseJSON)))
if err != nil {
t.Fatal(err)
}
defer base.Release()
target, _, err := array.FromJSON(mem, dataType, strings.NewReader(string(targetJSON)))
if err != nil {
t.Fatal(err)
}
defer target.Release()
edits, err := array.Diff(base, target)
if err != nil {
t.Fatalf("got unexpected error %v", err)
}
validateEditScript(t, edits, base, target)
}
// validateEditScript checks that the edit script produces target when applied to base.
func validateEditScript(t *testing.T, edits array.Edits, base, target arrow.Array) {
if len(edits) == 0 {
t.Fatalf("edit script has run length of zero")
}
baseIndex := int64(0)
targetIndex := int64(0)
for i := 0; i < len(edits); i++ {
if i > 0 {
if edits[i].Insert {
targetIndex++
} else {
baseIndex++
}
}
for j := int64(0); j < edits[i].RunLength; j++ {
if !array.SliceEqual(base, baseIndex, baseIndex+1, target, targetIndex, targetIndex+1) {
t.Fatalf("edit script (%v) when applied to base %v does not produce target %v", edits, base, target)
}
baseIndex += 1
targetIndex += 1
}
}
if baseIndex != int64(base.Len()) || targetIndex != int64(target.Len()) {
t.Fatalf("edit script (%v) when applied to base %v does not produce target %v", edits, base, target)
}
}
type diffStringTestCase struct {
dataType arrow.DataType
name string
baseJSON string
targetJSON string
want string
}
func (s *diffStringTestCase) check(t *testing.T) {
mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
defer mem.AssertSize(t, 0)
base, _, err := array.FromJSON(mem, s.dataType, strings.NewReader(s.baseJSON))
if err != nil {
t.Fatal(err)
}
defer base.Release()
target, _, err := array.FromJSON(mem, s.dataType, strings.NewReader(s.targetJSON))
if err != nil {
t.Fatal(err)
}
defer target.Release()
edits, err := array.Diff(base, target)
if err != nil {
t.Fatalf("got unexpected error %v", err)
}
got := edits.UnifiedDiff(base, target)
if got != s.want {
t.Errorf("got:\n%v\n, want:\n%v", got, s.want)
}
}
func TestEdits_UnifiedDiff(t *testing.T) {
msPerDay := 24 * 60 * 60 * 1000
cases := []diffStringTestCase{
{
name: "no changes",
dataType: arrow.BinaryTypes.String,
baseJSON: `["give", "me", "a", "break"]`,
targetJSON: `["give", "me", "a", "break"]`,
want: ``,
},
{
name: "insert one",
dataType: arrow.BinaryTypes.String,
baseJSON: `["give", "a", "break"]`,
targetJSON: `["give", "me", "a", "break"]`,
want: `@@ -1, +1 @@
+"me"
`,
},
{
name: "delete one",
dataType: arrow.BinaryTypes.String,
baseJSON: `["give", "me", "a", "break"]`,
targetJSON: `["give", "a", "break"]`,
want: `@@ -1, +1 @@
-"me"
`,
},
{
name: "change one",
dataType: arrow.BinaryTypes.String,
baseJSON: `["give", "a", "break"]`,
targetJSON: `["gimme", "a", "break"]`,
want: `@@ -0, +0 @@
-"give"
+"gimme"
`,
},
{
name: "null out one",
dataType: arrow.BinaryTypes.String,
baseJSON: `["give", "a", "break"]`,
targetJSON: `["give", "a", null]`,
want: `@@ -2, +2 @@
-"break"
+null
`,
},
{
name: "strings with escaped chars",
dataType: arrow.BinaryTypes.String,
baseJSON: `["newline:\\n", "quote:'", "backslash:\\\\"]`,
targetJSON: `["newline:\\n", "tab:\\t", "quote:\\\"", "backslash:\\\\"]`,
want: `@@ -1, +1 @@
-"quote:'"
+"tab:\\t"
+"quote:\\\""
`,
},
{
name: "date32",
dataType: arrow.PrimitiveTypes.Date32,
baseJSON: `[0, 1, 2, 31, 4]`,
targetJSON: `[0, 1, 31, 2, 4]`,
want: `@@ -2, +2 @@
-1970-01-03
@@ -4, +3 @@
+1970-01-03
`,
},
{
name: "date64",
dataType: arrow.PrimitiveTypes.Date64,
baseJSON: fmt.Sprintf(`[%d, %d, %d, %d, %d]`, 0*msPerDay, 1*msPerDay, 2*msPerDay, 31*msPerDay, 4*msPerDay),
targetJSON: fmt.Sprintf(`[%d, %d, %d, %d, %d]`, 0*msPerDay, 1*msPerDay, 31*msPerDay, 2*msPerDay, 4*msPerDay),
want: `@@ -2, +2 @@
-1970-01-03
@@ -4, +3 @@
+1970-01-03
`,
},
{
name: "timestamp_s",
dataType: arrow.FixedWidthTypes.Timestamp_s,
baseJSON: fmt.Sprintf(`[0, 1, %d, 2, 4]`, 678+(5+60*(4+60*(3+24*int64(1))))),
targetJSON: fmt.Sprintf(`[0, 1, 2, %d, 4]`, 678+(5+60*(4+60*(3+24*int64(1))))),
want: `@@ -2, +2 @@
-1970-01-02 03:15:23 +0000 UTC
@@ -4, +3 @@
+1970-01-02 03:15:23 +0000 UTC
`,
},
{
name: "timestamp_ms",
dataType: arrow.FixedWidthTypes.Timestamp_ms,
baseJSON: fmt.Sprintf(`[0, 1, %d, 2, 4]`, 678+1000*(5+60*(4+60*(3+24*int64(1))))),
targetJSON: fmt.Sprintf(`[0, 1, 2, %d, 4]`, 678+1000*(5+60*(4+60*(3+24*int64(1))))),
want: `@@ -2, +2 @@
-1970-01-02 03:04:05.678 +0000 UTC
@@ -4, +3 @@
+1970-01-02 03:04:05.678 +0000 UTC
`,
},
{
name: "timestamp_us",
dataType: arrow.FixedWidthTypes.Timestamp_us,
baseJSON: fmt.Sprintf(`[0, 1, %d, 2, 4]`, 678+1000000*(5+60*(4+60*(3+24*int64(1))))),
targetJSON: fmt.Sprintf(`[0, 1, 2, %d, 4]`, 678+1000000*(5+60*(4+60*(3+24*int64(1))))),
want: `@@ -2, +2 @@
-1970-01-02 03:04:05.000678 +0000 UTC
@@ -4, +3 @@
+1970-01-02 03:04:05.000678 +0000 UTC
`,
},
{
name: "timestamp_ns",
dataType: arrow.FixedWidthTypes.Timestamp_ns,
baseJSON: fmt.Sprintf(`[0, 1, %d, 2, 4]`, 678+1000000000*(5+60*(4+60*(3+24*int64(1))))),
targetJSON: fmt.Sprintf(`[0, 1, 2, %d, 4]`, 678+1000000000*(5+60*(4+60*(3+24*int64(1))))),
want: `@@ -2, +2 @@
-1970-01-02 03:04:05.000000678 +0000 UTC
@@ -4, +3 @@
+1970-01-02 03:04:05.000000678 +0000 UTC
`,
},
{
name: "lists",
dataType: arrow.ListOf(arrow.PrimitiveTypes.Int32),
baseJSON: `[[2, 3, 1], [], [13], []]`,
targetJSON: `[[2, 3, 1], [5, 9], [], [13]]`,
want: `@@ -1, +1 @@
+[5,9]
@@ -3, +4 @@
-[]
`,
},
{
name: "maps",
dataType: arrow.MapOf(arrow.BinaryTypes.String, arrow.PrimitiveTypes.Int32),
baseJSON: `[
[{"key": "foo", "value": 2}, {"key": "bar", "value": 3}, {"key": "baz", "value": 1}],
[{"key": "quux", "value": 13}],
[]
]`,
targetJSON: `[
[{"key": "foo", "value": 2}, {"key": "bar", "value": 3}, {"key": "baz", "value": 1}],
[{"key": "ytho", "value": 11}],
[{"key": "quux", "value": 13}],
[]
]`,
want: `@@ -1, +1 @@
+[{"key":"ytho","value":11}]
`,
},
{
name: "structs",
dataType: arrow.StructOf(
[]arrow.Field{
{Name: "foo", Type: arrow.BinaryTypes.String, Nullable: true},
{Name: "bar", Type: arrow.PrimitiveTypes.Int32, Nullable: true},
}...,
),
baseJSON: `[{"foo": "!", "bar": 3}, {}, {"bar": 13}]`,
targetJSON: `[{"foo": null, "bar": 2}, {}, {"bar": 13}]`,
want: `@@ -0, +0 @@
-{"bar":3,"foo":"!"}
+{"bar":2,"foo":null}
`,
},
{
name: "unions",
dataType: arrow.UnionOf(arrow.SparseMode,
[]arrow.Field{
{Name: "foo", Type: arrow.BinaryTypes.String},
{Name: "bar", Type: arrow.PrimitiveTypes.Int32},
},
[]arrow.UnionTypeCode{2, 5},
),
baseJSON: `[[2, "!"], [5, 3], [5, 13]]`,
targetJSON: `[[2, "!"], [2, "3"], [5, 13]]`,
want: `@@ -1, +1 @@
-[5,3]
+[2,"3"]
`,
},
{
name: "string",
dataType: arrow.BinaryTypes.String,
baseJSON: `["h", "l", "l", "o", "o"]`,
targetJSON: `["h", "e", "l", "l", "o", "0"]`,
want: `@@ -1, +1 @@
+"e"
@@ -4, +5 @@
-"o"
+"0"
`,
},
{
name: "int8",
dataType: arrow.PrimitiveTypes.Int8,
baseJSON: `[0, 1, 2, 3, 5, 8, 11, 13, 17]`,
targetJSON: `[2, 3, 5, 7, 11, 13, 17, 19]`,
want: `@@ -0, +0 @@
-0
-1
@@ -5, +3 @@
-8
+7
@@ -9, +7 @@
+19
`,
},
{
name: "int16",
dataType: arrow.PrimitiveTypes.Int16,
baseJSON: `[0, 1, 2, 3, 5, 8, 11, 13, 17]`,
targetJSON: `[2, 3, 5, 7, 11, 13, 17, 19]`,
want: `@@ -0, +0 @@
-0
-1
@@ -5, +3 @@
-8
+7
@@ -9, +7 @@
+19
`,
},
{
name: "int32",
dataType: arrow.PrimitiveTypes.Int32,
baseJSON: `[0, 1, 2, 3, 5, 8, 11, 13, 17]`,
targetJSON: `[2, 3, 5, 7, 11, 13, 17, 19]`,
want: `@@ -0, +0 @@
-0
-1
@@ -5, +3 @@
-8
+7
@@ -9, +7 @@
+19
`,
},
{
name: "int64",
dataType: arrow.PrimitiveTypes.Int64,
baseJSON: `[0, 1, 2, 3, 5, 8, 11, 13, 17]`,
targetJSON: `[2, 3, 5, 7, 11, 13, 17, 19]`,
want: `@@ -0, +0 @@
-0
-1
@@ -5, +3 @@
-8
+7
@@ -9, +7 @@
+19
`,
},
{
name: "uint8",
dataType: arrow.PrimitiveTypes.Uint8,
baseJSON: `[0, 1, 2, 3, 5, 8, 11, 13, 17]`,
targetJSON: `[2, 3, 5, 7, 11, 13, 17, 19]`,
want: `@@ -0, +0 @@
-0
-1
@@ -5, +3 @@
-8
+7
@@ -9, +7 @@
+19
`,
},
{
name: "uint16",
dataType: arrow.PrimitiveTypes.Uint16,
baseJSON: `[0, 1, 2, 3, 5, 8, 11, 13, 17]`,
targetJSON: `[2, 3, 5, 7, 11, 13, 17, 19]`,
want: `@@ -0, +0 @@
-0
-1
@@ -5, +3 @@
-8
+7
@@ -9, +7 @@
+19
`,
},
{
name: "uint32",
dataType: arrow.PrimitiveTypes.Uint32,
baseJSON: `[0, 1, 2, 3, 5, 8, 11, 13, 17]`,
targetJSON: `[2, 3, 5, 7, 11, 13, 17, 19]`,
want: `@@ -0, +0 @@
-0
-1
@@ -5, +3 @@
-8
+7
@@ -9, +7 @@
+19
`,
},
{
name: "uint64",
dataType: arrow.PrimitiveTypes.Uint64,
baseJSON: `[0, 1, 2, 3, 5, 8, 11, 13, 17]`,
targetJSON: `[2, 3, 5, 7, 11, 13, 17, 19]`,
want: `@@ -0, +0 @@
-0
-1
@@ -5, +3 @@
-8
+7
@@ -9, +7 @@
+19
`,
},
{
name: "float32",
dataType: arrow.PrimitiveTypes.Float32,
baseJSON: `[0.1, 0.3, -0.5]`,
targetJSON: `[0.1, -0.5, 0.3]`,
want: `@@ -1, +1 @@
-0.300000
@@ -3, +2 @@
+0.300000
`,
},
{
name: "float64",
dataType: arrow.PrimitiveTypes.Float64,
baseJSON: `[0.1, 0.3, -0.5]`,
targetJSON: `[0.1, -0.5, 0.3]`,
want: `@@ -1, +1 @@
-0.300000
@@ -3, +2 @@
+0.300000
`,
},
{
name: "equal nulls",
dataType: arrow.PrimitiveTypes.Int32,
baseJSON: `[null, null]`,
targetJSON: `[null, null]`,
want: ``,
},
{
name: "nulls",
dataType: arrow.PrimitiveTypes.Int32,
baseJSON: `[1, null, null, null]`,
targetJSON: `[null, 1, null, 2]`,
want: `@@ -0, +0 @@
-1
@@ -2, +1 @@
-null
+1
@@ -4, +3 @@
+2
`,
},
{
name: "extensions",
dataType: extensions.NewUUIDType(),
baseJSON: `["00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000001"]`,
targetJSON: `["00000000-0000-0000-0000-000000000001", "00000000-0000-0000-0000-000000000002"]`,
want: `@@ -0, +0 @@
-"00000000-0000-0000-0000-000000000000"
@@ -2, +1 @@
+"00000000-0000-0000-0000-000000000002"
`,
},
}
for _, tc := range cases {
t.Run(tc.name, tc.check)
}
}