dcrd/dcrutil/amount_test.go
Dave Collins bd9629caeb
dcrutil: Consolidate tests into package.
Putting the test code in the same package makes it easier for forks
since they don't have to change the import paths as much and it also
gets rid of the need for internal_test.go to bridge.
2019-03-16 20:41:23 -05:00

351 lines
6.8 KiB
Go

// Copyright (c) 2013, 2014 The btcsuite developers
// Copyright (c) 2015-2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package dcrutil
import (
"math"
"reflect"
"sort"
"testing"
)
func TestAmountCreation(t *testing.T) {
tests := []struct {
name string
amount float64
valid bool
expected Amount
}{
// Positive tests.
{
name: "zero",
amount: 0,
valid: true,
expected: 0,
},
{
name: "max producable",
amount: 21e6,
valid: true,
expected: MaxAmount,
},
{
name: "min producable",
amount: -21e6,
valid: true,
expected: -MaxAmount,
},
{
name: "exceeds max producable",
amount: 21e6 + 1e-8,
valid: true,
expected: MaxAmount + 1,
},
{
name: "exceeds min producable",
amount: -21e6 - 1e-8,
valid: true,
expected: -MaxAmount - 1,
},
{
name: "one hundred",
amount: 100,
valid: true,
expected: 100 * AtomsPerCoin,
},
{
name: "fraction",
amount: 0.01234567,
valid: true,
expected: 1234567,
},
{
name: "rounding up",
amount: 54.999999999999943157,
valid: true,
expected: 55 * AtomsPerCoin,
},
{
name: "rounding down",
amount: 55.000000000000056843,
valid: true,
expected: 55 * AtomsPerCoin,
},
// Negative tests.
{
name: "not-a-number",
amount: math.NaN(),
valid: false,
},
{
name: "-infinity",
amount: math.Inf(-1),
valid: false,
},
{
name: "+infinity",
amount: math.Inf(1),
valid: false,
},
}
for _, test := range tests {
a, err := NewAmount(test.amount)
switch {
case test.valid && err != nil:
t.Errorf("%v: Positive test Amount creation failed with: %v", test.name, err)
continue
case !test.valid && err == nil:
t.Errorf("%v: Negative test Amount creation succeeded (value %v) when should fail", test.name, a)
continue
}
if a != test.expected {
t.Errorf("%v: Created amount %v does not match expected %v", test.name, a, test.expected)
continue
}
}
}
func TestAmountUnitConversions(t *testing.T) {
tests := []struct {
name string
amount Amount
unit AmountUnit
converted float64
s string
}{
{
name: "MDCR",
amount: MaxAmount,
unit: AmountMegaCoin,
converted: 21,
s: "21 MDCR",
},
{
name: "kDCR",
amount: 44433322211100,
unit: AmountKiloCoin,
converted: 444.33322211100,
s: "444.333222111 kDCR",
},
{
name: "Coin",
amount: 44433322211100,
unit: AmountCoin,
converted: 444333.22211100,
s: "444333.222111 DCR",
},
{
name: "mDCR",
amount: 44433322211100,
unit: AmountMilliCoin,
converted: 444333222.11100,
s: "444333222.111 mDCR",
},
{
name: "μDCR",
amount: 44433322211100,
unit: AmountMicroCoin,
converted: 444333222111.00,
s: "444333222111 μDCR",
},
{
name: "atom",
amount: 44433322211100,
unit: AmountAtom,
converted: 44433322211100,
s: "44433322211100 Atom",
},
{
name: "non-standard unit",
amount: 44433322211100,
unit: AmountUnit(-1),
converted: 4443332.2211100,
s: "4443332.22111 1e-1 DCR",
},
}
for _, test := range tests {
f := test.amount.ToUnit(test.unit)
if f != test.converted {
t.Errorf("%v: converted value %v does not match expected %v", test.name, f, test.converted)
continue
}
s := test.amount.Format(test.unit)
if s != test.s {
t.Errorf("%v: format '%v' does not match expected '%v'", test.name, s, test.s)
continue
}
// Verify that Amount.ToCoin works as advertised.
f1 := test.amount.ToUnit(AmountCoin)
f2 := test.amount.ToCoin()
if f1 != f2 {
t.Errorf("%v: ToCoin does not match ToUnit(AmountCoin): %v != %v", test.name, f1, f2)
}
// Verify that Amount.String works as advertised.
s1 := test.amount.Format(AmountCoin)
s2 := test.amount.String()
if s1 != s2 {
t.Errorf("%v: String does not match Format(AmountCoin): %v != %v", test.name, s1, s2)
}
}
}
func TestAmountMulF64(t *testing.T) {
tests := []struct {
name string
amt Amount
mul float64
res Amount
}{
{
name: "Multiply 0.1 DCR by 2",
amt: 100e5, // 0.1 DCR
mul: 2,
res: 200e5, // 0.2 DCR
},
{
name: "Multiply 0.2 DCR by 0.02",
amt: 200e5, // 0.2 DCR
mul: 1.02,
res: 204e5, // 0.204 DCR
},
{
name: "Multiply 0.1 DCR by -2",
amt: 100e5, // 0.1 DCR
mul: -2,
res: -200e5, // -0.2 DCR
},
{
name: "Multiply 0.2 DCR by -0.02",
amt: 200e5, // 0.2 DCR
mul: -1.02,
res: -204e5, // -0.204 DCR
},
{
name: "Multiply -0.1 DCR by 2",
amt: -100e5, // -0.1 DCR
mul: 2,
res: -200e5, // -0.2 DCR
},
{
name: "Multiply -0.2 DCR by 0.02",
amt: -200e5, // -0.2 DCR
mul: 1.02,
res: -204e5, // -0.204 DCR
},
{
name: "Multiply -0.1 DCR by -2",
amt: -100e5, // -0.1 DCR
mul: -2,
res: 200e5, // 0.2 DCR
},
{
name: "Multiply -0.2 DCR by -0.02",
amt: -200e5, // -0.2 DCR
mul: -1.02,
res: 204e5, // 0.204 DCR
},
{
name: "Round down",
amt: 49, // 49 Atoms
mul: 0.01,
res: 0,
},
{
name: "Round up",
amt: 50, // 50 Atoms
mul: 0.01,
res: 1, // 1 Atom
},
{
name: "Multiply by 0.",
amt: 1e8, // 1 DCR
mul: 0,
res: 0, // 0 DCR
},
{
name: "Multiply 1 by 0.5.",
amt: 1, // 1 Atom
mul: 0.5,
res: 1, // 1 Atom
},
{
name: "Multiply 100 by 66%.",
amt: 100, // 100 Atoms
mul: 0.66,
res: 66, // 66 Atoms
},
{
name: "Multiply 100 by 66.6%.",
amt: 100, // 100 Atoms
mul: 0.666,
res: 67, // 67 Atoms
},
{
name: "Multiply 100 by 2/3.",
amt: 100, // 100 Atoms
mul: 2.0 / 3,
res: 67, // 67 Atoms
},
}
for _, test := range tests {
a := test.amt.MulF64(test.mul)
if a != test.res {
t.Errorf("%v: expected %v got %v", test.name, test.res, a)
}
}
}
func TestAmountSorter(t *testing.T) {
tests := []struct {
name string
as []Amount
want []Amount
}{
{
name: "Sort zero length slice of Amounts",
as: []Amount{},
want: []Amount{},
},
{
name: "Sort 1-element slice of Amounts",
as: []Amount{7},
want: []Amount{7},
},
{
name: "Sort 2-element slice of Amounts",
as: []Amount{7, 5},
want: []Amount{5, 7},
},
{
name: "Sort 6-element slice of Amounts",
as: []Amount{0, 9e8, 4e6, 4e6, 3, 9e12},
want: []Amount{0, 3, 4e6, 4e6, 9e8, 9e12},
},
}
for i, test := range tests {
result := make([]Amount, len(test.as))
copy(result, test.as)
sort.Sort(AmountSorter(result))
if !reflect.DeepEqual(result, test.want) {
t.Errorf("AmountSorter #%d got %v want %v", i, result,
test.want)
continue
}
}
}