blob: 3a5c335b795a26b06edf59c9959ef76628ab784f [file]
// Licensed to 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. Apache Software Foundation (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 base
import (
"reflect"
"testing"
"time"
)
var (
defaultTime = time.Now()
)
func TestAdd(t *testing.T) {
tests := []struct {
name string
size int
dataOp func(window *TimeWindows[float64, float64])
result []float64
}{
{
name: "normal",
size: 3,
dataOp: func(window *TimeWindows[float64, float64]) {
addData(window, 1, 1)
addData(window, 2, 2)
addData(window, 3, 3)
},
result: []float64{1, 2, 3},
},
{
name: "out of count",
size: 3,
dataOp: func(window *TimeWindows[float64, float64]) {
addData(window, 1, 1)
addData(window, 2, 2)
addData(window, 3, 3)
addData(window, 4, 4)
},
result: []float64{2, 3, 4},
},
{
name: "add older data",
size: 3,
dataOp: func(window *TimeWindows[float64, float64]) {
addData(window, 1, 1)
addData(window, 2, 2)
addData(window, 3, 3)
addData(window, 4, 4)
addData(window, 2, 4)
addData(window, 1, 8)
addData(window, 0, 10)
},
result: []float64{4, 3, 4},
},
{
name: "add new data which bigger than windows count",
size: 3,
dataOp: func(window *TimeWindows[float64, float64]) {
addData(window, 1, 1)
addData(window, 4, 2)
addData(window, 5, 3)
addData(window, 7, 5)
},
result: []float64{3, 0, 5},
},
}
for _, tt := range tests {
windows := timeWindows(tt.size)
tt.dataOp(windows)
actualValue := getAllValues(windows)
if !reflect.DeepEqual(tt.result, actualValue) {
t.Fatalf("test [%s] failure, expceted: %v, actual: %v", tt.name, tt.result, actualValue)
}
}
}
func TestFlushData(t *testing.T) {
tests := []struct {
name string
size int
operations []interface{}
}{
{
name: "normal[most recent]",
size: 3,
operations: []interface{}{
appendDataOperation{1, 1},
mostRecentDataChecker{1, true},
mostRecentDataChecker{0, false},
appendDataOperation{2, 2},
appendDataOperation{3, 3},
mostRecentDataChecker{3, true},
mostRecentDataChecker{0, false},
},
},
{
name: "has older data[most recent]",
size: 3,
operations: []interface{}{
appendDataOperation{1, 1},
appendDataOperation{2, 2},
appendDataOperation{1, 3},
mostRecentDataChecker{2, true},
mostRecentDataChecker{0, false},
appendDataOperation{3, 3},
appendDataOperation{4, 4},
appendDataOperation{1, 9},
mostRecentDataChecker{4, true},
mostRecentDataChecker{0, false},
},
},
{
name: "normal[multiple recent]",
size: 3,
operations: []interface{}{
appendDataOperation{1, 1},
appendDataOperation{2, 2},
appendDataOperation{3, 3},
multipleRecentDataChecker{[]float64{3, 2, 1}, true},
multipleRecentDataChecker{nil, false},
appendDataOperation{4, 4},
multipleRecentDataChecker{[]float64{4}, true},
multipleRecentDataChecker{nil, false},
},
},
{
name: "has older data[multiple recent]",
size: 3,
operations: []interface{}{
appendDataOperation{1, 1},
appendDataOperation{2, 2},
appendDataOperation{4, 4},
multipleRecentDataChecker{[]float64{4, 2}, true},
multipleRecentDataChecker{nil, false},
appendDataOperation{1, 1},
multipleRecentDataChecker{nil, false},
},
},
}
for _, tt := range tests {
windows := timeWindows(tt.size)
for _, op := range tt.operations {
switch v := op.(type) {
case appendDataOperation:
addData(windows, v.second, v.value)
case mostRecentDataChecker:
val, hasData := windows.FlushMostRecentData()
if val != v.value || v.hasData != hasData {
t.Fatalf("test[%s] failure, excepted: %v-%t, actual: %v-%t", tt.name,
v.value, v.hasData, val, hasData)
}
case multipleRecentDataChecker:
val, hasData := windows.FlushMultipleRecentData()
if !reflect.DeepEqual(val, v.value) || v.hasData != hasData {
t.Fatalf("test[%s] failure, excepted: %v-%t, actual: %v-%t", tt.name,
v.value, v.hasData, val, hasData)
}
}
}
}
}
type appendDataOperation struct {
second int
value float64
}
type mostRecentDataChecker struct {
value float64
hasData bool
}
type multipleRecentDataChecker struct {
value []float64
hasData bool
}
func addData(win *TimeWindows[float64, float64], second int, val float64) {
result := defaultTime.Add(time.Second * time.Duration(second))
win.Add(result, val)
}
func timeWindows(count int) *TimeWindows[float64, float64] {
return NewTimeWindows([]*PolicyItem{{Period: count}}, func() WindowData[float64, float64] {
return NewLatestWindowData[float64]()
})
}
func getAllValues(win *TimeWindows[float64, float64]) []float64 {
result := make([]float64, 0)
for e := win.data.Front(); e != nil; e = e.Next() {
result = append(result, e.Value.(*windowDataWrapper[float64, float64]).Get())
}
return result
}