featurebase/pql/decimal_test.go
Seebs e84d2d2a59 refactor number parsing a bit
There are subtle inconsistencies, like "01" being a valid decimal but not
a valid integer, which vaguely bug me. Cleaning this up, and the corresponding
parser logic.

A number can't have leading spaces because the grammar doesn't
put spaces in them in the first place, so stop accepting them in the
number syntax. This should never have any impact on anything,
it's just simpler.

Update a couple of test cases to reflect this -- no longer testing
that trailing spaces are okay, now testing that they're not, for
instance.
2020-10-19 13:37:21 -05:00

262 lines
10 KiB
Go

// Copyright 2017 Pilosa Corp.
//
// Licensed 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 pql_test
import (
"encoding/json"
"reflect"
"strings"
"testing"
"github.com/pilosa/pilosa/v2/pql"
)
// Ensure call can be converted into a string.
func TestDecimal(t *testing.T) {
t.Run("Parse", func(t *testing.T) {
tests := []struct {
s string
exp pql.Decimal
expErr string
}{
{"0", pql.Decimal{0, 0}, ""},
{"-0", pql.Decimal{0, 0}, ""},
{"0.0", pql.Decimal{0, 0}, ""},
{"0.", pql.Decimal{0, 0}, ""},
{"-0.00", pql.Decimal{0, 0}, ""},
{"123.4567", pql.Decimal{1234567, 4}, ""},
{"123.456700", pql.Decimal{1234567, 4}, ""},
{"00123.4567", pql.Decimal{1234567, 4}, ""},
{"+123.4567", pql.Decimal{1234567, 4}, ""},
{"-123.4567", pql.Decimal{-1234567, 4}, ""},
{"-00123.4567", pql.Decimal{-1234567, 4}, ""},
{"-12.25", pql.Decimal{-1225, 2}, ""},
{"123", pql.Decimal{123, 0}, ""},
{"-12300", pql.Decimal{-123, -2}, ""},
{"+012300", pql.Decimal{123, -2}, ""},
{"12300", pql.Decimal{123, -2}, ""},
{"12300.", pql.Decimal{123, -2}, ""},
{"12300.0", pql.Decimal{123, -2}, ""},
{"123.0", pql.Decimal{123, 0}, ""},
{".123", pql.Decimal{123, 3}, ""},
{"0.123", pql.Decimal{123, 3}, ""},
{"0.001230", pql.Decimal{123, 5}, ""},
{"-0.001230", pql.Decimal{-123, 5}, ""},
// int64 edges.
{".000009223372036854775807", pql.Decimal{9223372036854775807, 24}, ""},
{"-.000009223372036854775808", pql.Decimal{-9223372036854775808, 24}, ""},
{"92233720368547.75807", pql.Decimal{9223372036854775807, 5}, ""},
{"-92233720368547.75807", pql.Decimal{-9223372036854775807, 5}, ""},
{"9223372036854775807000", pql.Decimal{9223372036854775807, -3}, ""},
{"-9223372036854775807000", pql.Decimal{-9223372036854775807, -3}, ""},
// precision adjustment
{"2.666666666666666667", pql.Decimal{2666666666666666667, 18}, ""},
{"2.6666666666666666667", pql.Decimal{2666666666666666666, 18}, ""},
{"2.6666666666666666666667", pql.Decimal{2666666666666666666, 18}, ""},
{"-9.223372036854775808", pql.Decimal{-9223372036854775808, 18}, ""},
{"-9.223372036854775809", pql.Decimal{-922337203685477580, 17}, ""},
{"9.223372036854775807", pql.Decimal{9223372036854775807, 18}, ""},
{"9.223372036854775808", pql.Decimal{922337203685477580, 17}, ""},
// Error cases.
{"", pql.Decimal{}, "decimal string is empty"},
{"-", pql.Decimal{}, "decimal string is empty"},
{"*0.123", pql.Decimal{}, "invalid syntax"},
{"abc", pql.Decimal{}, "invalid syntax"},
{"0.12.3", pql.Decimal{}, "invalid decimal string"},
{"--12300", pql.Decimal{}, "invalid syntax"},
{" 123.4567 ", pql.Decimal{}, "invalid syntax"},
{" 123.4567", pql.Decimal{}, "invalid syntax"},
{"123.4567 ", pql.Decimal{}, "invalid syntax"},
{"0.a", pql.Decimal{}, "invalid syntax"},
// These are no longer error cases since we introduced precision adjustment.
//{"922337203685477580.9", pql.Decimal{}, "value out of range"},
//{"-922337203685477580.9", pql.Decimal{}, "value out of range"},
{"9223372036854775808000", pql.Decimal{}, "value out of range"},
{"-9223372036854775809000", pql.Decimal{}, "value out of range"},
}
for i, test := range tests {
dec, err := pql.ParseDecimal(test.s)
if test.expErr != "" {
if err == nil || !strings.Contains(err.Error(), test.expErr) {
t.Fatalf("test %d parsing string `%s`: expected error to contain: %s, but got: %v", i, test.s, test.expErr, err)
}
} else if err != nil {
t.Fatalf("test %d parsing string `%s`: %s", i, test.s, err)
} else if dec != test.exp {
t.Fatalf("test %d parsing string `%s`: expected: %v, but got: %v", i, test.s, test.exp, dec)
}
}
})
t.Run("ToInt64", func(t *testing.T) {
tests := []struct {
dec pql.Decimal
scale int64
exp int64
}{
{pql.Decimal{0, 0}, 0, 0}, // 0 : 0
{pql.Decimal{0, 0}, 1, 0}, // 0 : 0.0
{pql.Decimal{0, 0}, -1, 0}, // 0 : 0
{pql.Decimal{1234567, 4}, 5, 12345670}, // 123.4567 : 123.45670
{pql.Decimal{1234567, 4}, 4, 1234567}, // 123.4567 : 123.4567
{pql.Decimal{1234567, 4}, 3, 123456}, // 123.4567 : 123.456
{pql.Decimal{-1234567, 4}, 5, -12345670}, // -123.4567 : -123.45670
{pql.Decimal{-1234567, 4}, 4, -1234567}, // -123.4567 : -123.4567
{pql.Decimal{-1234567, 4}, 3, -123456}, // -123.4567 : -123.456
{pql.Decimal{123, -2}, 5, 1230000000}, // 12300 : 12300.00000
{pql.Decimal{123, -2}, -1, 1230}, // 12300 : 1230
{pql.Decimal{123, 1}, -1, 1}, // 12.3 : 1
{pql.Decimal{123, 1}, -2, 0}, // 12.3 : 0
}
for i, test := range tests {
v := test.dec.ToInt64(test.scale)
if v != test.exp {
t.Fatalf("test %d expected: %d, but got: %d", i, test.exp, v)
}
}
})
t.Run("String", func(t *testing.T) {
tests := []struct {
s string
exp string
}{
{"123.4567", "123.4567"},
{"123.456700", "123.4567"},
{"00123.4567", "123.4567"},
{"+123.4567", "123.4567"},
{"-123.4567", "-123.4567"},
{"-00123.4567", "-123.4567"},
{"-12.25", "-12.25"},
{"123", "123"},
{"-12300", "-12300"},
{"+012300", "12300"},
{"12300", "12300"},
{"12300.", "12300"},
{"12300.0", "12300"},
{"123.0", "123"},
{"0.123", "0.123"},
{"0.001230", "0.00123"},
{"+0.001230", "0.00123"},
{"-0.001230", "-0.00123"},
}
for i, test := range tests {
dec, err := pql.ParseDecimal(test.s)
if err != nil {
t.Fatalf("parsing string `%s`: %s", test.s, err)
}
if str := dec.String(); str != test.exp {
t.Fatalf("test %d expected: %s, but got: %s", i, test.exp, str)
}
}
})
t.Run("Comparisons", func(t *testing.T) {
tests := []struct {
d1 pql.Decimal
d2 pql.Decimal
expLT bool
expLTE bool
expGT bool
expGTE bool
expEQ bool
}{
{pql.NewDecimal(0, 0), pql.NewDecimal(0, 0), false, true, false, true, true},
{pql.NewDecimal(0, 0), pql.NewDecimal(10, 0), true, true, false, false, false},
{pql.NewDecimal(10, 0), pql.NewDecimal(0, 0), false, false, true, true, false},
{pql.NewDecimal(123456, 3), pql.NewDecimal(123456, 3), false, true, false, true, true},
{pql.NewDecimal(123456, 3), pql.NewDecimal(123456, 4), false, false, true, true, false},
{pql.NewDecimal(123456, 4), pql.NewDecimal(123456, 3), true, true, false, false, false},
{pql.NewDecimal(1233456, 4), pql.NewDecimal(123456, 3), true, true, false, false, false},
{pql.NewDecimal(0, 0), pql.NewDecimal(-10, 0), false, false, true, true, false},
{pql.NewDecimal(-10, 0), pql.NewDecimal(0, 0), true, true, false, false, false},
{pql.NewDecimal(-123456, 3), pql.NewDecimal(-123456, 3), false, true, false, true, true},
{pql.NewDecimal(-123456, 3), pql.NewDecimal(-123456, 4), true, true, false, false, false},
{pql.NewDecimal(-123456, 4), pql.NewDecimal(-123456, 3), false, false, true, true, false},
{pql.NewDecimal(-1233456, 4), pql.NewDecimal(-123456, 3), false, false, true, true, false},
{pql.NewDecimal(10, 0), pql.NewDecimal(-10, 0), false, false, true, true, false},
{pql.NewDecimal(-10, 0), pql.NewDecimal(10, 0), true, true, false, false, false},
{pql.NewDecimal(-123456, 3), pql.NewDecimal(123456, 3), true, true, false, false, false},
{pql.NewDecimal(123456, 3), pql.NewDecimal(-123456, 3), false, false, true, true, false},
{pql.NewDecimal(-123456, 3), pql.NewDecimal(123456, 4), true, true, false, false, false},
{pql.NewDecimal(123456, 3), pql.NewDecimal(-123456, 4), false, false, true, true, false},
{pql.NewDecimal(-123456, 4), pql.NewDecimal(123456, 3), true, true, false, false, false},
{pql.NewDecimal(123456, 4), pql.NewDecimal(-123456, 3), false, false, true, true, false},
{pql.NewDecimal(-1233456, 4), pql.NewDecimal(123456, 3), true, true, false, false, false},
{pql.NewDecimal(1233456, 4), pql.NewDecimal(-123456, 3), false, false, true, true, false},
{pql.NewDecimal(9223372036854775807, 0), pql.NewDecimal(9223372036854775807, 0), false, true, false, true, true},
{pql.NewDecimal(9223372036854775807, 2), pql.NewDecimal(9223372036854775807, 0), true, true, false, false, false},
{pql.NewDecimal(9223372036854775807, 19), pql.NewDecimal(9223372036854775807, 0), true, true, false, false, false},
{pql.NewDecimal(-9223372036854775808, 0), pql.NewDecimal(-9223372036854775808, 0), false, true, false, true, true},
{pql.NewDecimal(-9223372036854775808, 0), pql.NewDecimal(-9223372036854775807, 0), true, true, false, false, false},
{pql.NewDecimal(-9223372036854775808, 2), pql.NewDecimal(-9223372036854775808, 0), false, false, true, true, false},
{pql.NewDecimal(-9223372036854775808, 19), pql.NewDecimal(-9223372036854775807, 0), false, false, true, true, false},
}
for i, test := range tests {
if got := test.d1.LessThan(test.d2); got != test.expLT {
t.Fatalf("test LT %d expected %s < %s to be %v, but got: %v", i, test.d1, test.d2, test.expLT, got)
}
if got := test.d1.LessThanOrEqualTo(test.d2); got != test.expLTE {
t.Fatalf("test LTE %d expected %s <= %s to be %v, but got: %v", i, test.d1, test.d2, test.expLTE, got)
}
if got := test.d1.GreaterThan(test.d2); got != test.expGT {
t.Fatalf("test GT %d expected %s > %s to be %v, but got: %v", i, test.d1, test.d2, test.expGT, got)
}
if got := test.d1.GreaterThanOrEqualTo(test.d2); got != test.expGTE {
t.Fatalf("test GTE %d expected %s >= %s to be %v, but got: %v", i, test.d1, test.d2, test.expGTE, got)
}
if got := test.d1.EqualTo(test.d2); got != test.expEQ {
t.Fatalf("test EQ %d expected %s == %s to be %v, but got: %v", i, test.d1, test.d2, test.expEQ, got)
}
}
})
t.Run("JSON", func(t *testing.T) {
t.Run("Unmarshal", func(t *testing.T) {
tests := []struct {
json string
exp pql.Decimal
}{
{"1234.56", pql.NewDecimal(123456, 2)},
}
for i, test := range tests {
b := []byte(test.json)
dec := &pql.Decimal{}
if err := json.Unmarshal(b, &dec); err != nil {
panic(err)
}
if !reflect.DeepEqual(*dec, test.exp) {
t.Fatalf("test %d expected: %T, but got: %T", i, test.exp, dec)
}
}
})
})
}