diff --git a/internal/controller/controller_test.go b/internal/controller/controller_test.go index 1dab39a..d6dc394 100644 --- a/internal/controller/controller_test.go +++ b/internal/controller/controller_test.go @@ -1,6 +1,7 @@ package controller import ( + "errors" "sort" "testing" "time" @@ -58,6 +59,7 @@ func (c MockCurve) Evaluate() (value int, err error) { type MockFan struct { ID string + ControlMode fans.ControlMode PWM int MinPWM int RPM int @@ -121,11 +123,12 @@ func (fan *MockFan) AttachFanCurveData(curveData *map[int]float64) (err error) { } func (fan MockFan) GetPwmEnabled() (int, error) { - panic("implement me") + return int(fan.ControlMode), nil } func (fan *MockFan) SetPwmEnabled(value fans.ControlMode) (err error) { - panic("implement me") + fan.ControlMode = value + return nil } func (fan MockFan) IsPwmAuto() (bool, error) { @@ -201,20 +204,31 @@ var ( } ) -type mockPersistence struct{} +type mockPersistence struct { + hasPwmMap bool + hasSavedPwmData bool +} func (p mockPersistence) Init() (err error) { return nil } func (p mockPersistence) SaveFanPwmData(fan fans.Fan) (err error) { return nil } func (p mockPersistence) LoadFanPwmData(fan fans.Fan) (map[int]float64, error) { - fanCurveDataMap := map[int]float64{} - return fanCurveDataMap, nil + if p.hasSavedPwmData { + fanCurveDataMap := map[int]float64{} + return fanCurveDataMap, nil + } else { + return nil, errors.New("no pwm data found") + } } func (p mockPersistence) DeleteFanPwmData(fan fans.Fan) (err error) { return nil } func (p mockPersistence) LoadFanPwmMap(fanId string) (map[int]int, error) { - pwmMap := map[int]int{} - return pwmMap, nil + if p.hasPwmMap { + pwmMap := map[int]int{} + return pwmMap, nil + } else { + return nil, errors.New("no pwm map found") + } } func (p mockPersistence) SaveFanPwmMap(fanId string, pwmMap map[int]int) (err error) { return nil } func (p mockPersistence) DeleteFanPwmMap(fanId string) (err error) { return nil } @@ -370,10 +384,11 @@ func TestCalculateTargetSpeedNeverStop(t *testing.T) { fans.FanMap[fan.GetId()] = fan controller := PidFanController{ - persistence: mockPersistence{}, fan: fan, - curve: curve, - updateRate: time.Duration(100), - pwmMap: createOneToOnePwmMap(), + persistence: mockPersistence{}, + fan: fan, + curve: curve, + updateRate: time.Duration(100), + pwmMap: createOneToOnePwmMap(), } controller.updateDistinctPwmValues() @@ -456,10 +471,11 @@ func TestFanController_UpdateFanSpeed_FanCurveGaps(t *testing.T) { } controller := PidFanController{ - persistence: mockPersistence{}, fan: fan, - curve: curve, - updateRate: time.Duration(100), - pwmMap: pwmMap, + persistence: mockPersistence{}, + fan: fan, + curve: curve, + updateRate: time.Duration(100), + pwmMap: pwmMap, } controller.updateDistinctPwmValues() @@ -472,3 +488,62 @@ func TestFanController_UpdateFanSpeed_FanCurveGaps(t *testing.T) { closestTarget := controller.findClosestDistinctTarget(targetPwm) assert.Equal(t, 58, closestTarget) } + +func TestFanController_ComputePwmMap_FullRange(t *testing.T) { + // GIVEN + avgTmp := 40000.0 + + s := MockSensor{ + ID: "sensor", + Name: "sensor", + MovingAvg: avgTmp, + } + sensors.SensorMap[s.GetId()] = &s + + curveValue := 5 + curve := &MockCurve{ + ID: "curve", + Value: curveValue, + } + curves.SpeedCurveMap[curve.GetId()] = curve + + fan := &MockFan{ + ID: "fan", + PWM: 0, + RPM: 100, + MinPWM: 50, + curveId: curve.GetId(), + shouldNeverStop: true, + speedCurve: &DutyCycleFan, + } + fans.FanMap[fan.GetId()] = fan + + var keys []int + for pwm := range DutyCycleFan { + keys = append(keys, pwm) + } + sort.Ints(keys) + + expectedPwmMap := map[int]int{} + for i := 0; i <= 255; i++ { + expectedPwmMap[i] = i + } + + controller := PidFanController{ + persistence: mockPersistence{ + hasPwmMap: false, + }, + fan: fan, + curve: curve, + updateRate: time.Duration(100), + pwmMap: map[int]int{}, + } + controller.updateDistinctPwmValues() + + // WHEN + err := controller.computePwmMap() + + // THEN + assert.NoError(t, err) + assert.Equal(t, expectedPwmMap, controller.pwmMap) +} diff --git a/internal/fans/file.go b/internal/fans/file.go index 653eeba..1c0e825 100644 --- a/internal/fans/file.go +++ b/internal/fans/file.go @@ -109,6 +109,7 @@ func (fan *FileFan) SetPwm(pwm int) (err error) { err = util.WriteIntToFile(pwm, filePath) if err != nil { ui.Error("Unable to write to file: %v", fan.Config.File.Path) + return err } return nil } diff --git a/internal/fans/file_test.go b/internal/fans/file_test.go new file mode 100644 index 0000000..76191ba --- /dev/null +++ b/internal/fans/file_test.go @@ -0,0 +1,382 @@ +package fans + +import ( + "github.com/markusressel/fan2go/internal/configuration" + "github.com/markusressel/fan2go/internal/util" + "github.com/stretchr/testify/assert" + "os" + "testing" +) + +func TestFileFan_NewFan(t *testing.T) { + // GIVEN + id := "test" + config := configuration.FanConfig{ + ID: id, + File: &configuration.FileFanConfig{ + Path: "../../test/file_fan_pwm", + RpmPath: "../../test/file_fan_rpm", + }, + } + + // WHEN + fan, err := NewFan(config) + + // THEN + assert.NoError(t, err) + assert.NotNil(t, fan) +} + +func TestFileFan_GetId(t *testing.T) { + // GIVEN + id := "test" + config := configuration.FanConfig{ + ID: id, + File: &configuration.FileFanConfig{ + Path: "../../test/file_fan_pwm", + RpmPath: "../../test/file_fan_rpm", + }, + } + fan, _ := NewFan(config) + + // WHEN + result := fan.GetId() + + assert.Equal(t, id, result) +} + +func TestFileFan_GetStartPwm(t *testing.T) { + // GIVEN + config := configuration.FanConfig{ + File: &configuration.FileFanConfig{ + Path: "../../test/file_fan_pwm", + RpmPath: "../../test/file_fan_rpm", + }, + } + fan, _ := NewFan(config) + + // WHEN + result := fan.GetStartPwm() + + // THEN + assert.Equal(t, 1, result) +} + +func TestFileFan_SetStartPwm(t *testing.T) { + // GIVEN + config := configuration.FanConfig{ + File: &configuration.FileFanConfig{ + Path: "../../test/file_fan_pwm", + RpmPath: "../../test/file_fan_rpm", + }, + } + + fan, _ := NewFan(config) + + // WHEN + fan.SetStartPwm(100, false) + + // THEN + // NOTE: file fan does not support setting start pwm + assert.Equal(t, 1, fan.GetStartPwm()) +} + +func TestFileFan_GetMinPwm(t *testing.T) { + // GIVEN + config := configuration.FanConfig{ + File: &configuration.FileFanConfig{ + Path: "../../test/file_fan_pwm", + RpmPath: "../../test/file_fan_rpm", + }, + } + + fan, _ := NewFan(config) + + // WHEN + result := fan.GetMinPwm() + + // THEN + assert.Equal(t, 0, result) +} + +func TestFileFan_SetMinPwm(t *testing.T) { + // GIVEN + config := configuration.FanConfig{ + File: &configuration.FileFanConfig{ + Path: "../../test/file_fan_pwm", + RpmPath: "../../test/file_fan_rpm", + }, + } + + fan, _ := NewFan(config) + + // WHEN + fan.SetMinPwm(100, false) + + // THEN + // NOTE: file fan does not support setting start pwm + assert.Equal(t, 0, fan.GetMinPwm()) +} + +func TestFileFan_GetMaxPwm(t *testing.T) { + // GIVEN + config := configuration.FanConfig{ + File: &configuration.FileFanConfig{ + Path: "../../test/file_fan_pwm", + RpmPath: "../../test/file_fan_rpm", + }, + } + + fan, _ := NewFan(config) + + // WHEN + result := fan.GetMaxPwm() + + // THEN + assert.Equal(t, 255, result) +} + +func TestFileFan_SetMaxPwm(t *testing.T) { + // GIVEN + config := configuration.FanConfig{ + File: &configuration.FileFanConfig{ + Path: "../../test/file_fan_pwm", + RpmPath: "../../test/file_fan_rpm", + }, + } + + fan, _ := NewFan(config) + + // WHEN + fan.SetMaxPwm(100, false) + + // THEN + // NOTE: file fan does not support setting max pwm + assert.Equal(t, 255, fan.GetMaxPwm()) +} + +func TestFileFan_GetRpm(t *testing.T) { + // GIVEN + config := configuration.FanConfig{ + File: &configuration.FileFanConfig{ + Path: "../../test/file_fan_pwm", + RpmPath: "../../test/file_fan_rpm", + }, + } + + fan, _ := NewFan(config) + + // WHEN + result, err := fan.GetRpm() + + // THEN + assert.NoError(t, err) + assert.Equal(t, 2150, result) +} + +func TestFileFan_GetRpm_InvalidPath(t *testing.T) { + // GIVEN + config := configuration.FanConfig{ + File: &configuration.FileFanConfig{ + Path: "../../test/non_existent_file", + RpmPath: "../../test/non_existent_file", + }, + } + + fan, _ := NewFan(config) + + // WHEN + result, err := fan.GetRpm() + + // THEN + assert.Error(t, err) + assert.Equal(t, 0, result) +} + +func TestFileFan_SetRpmAvg(t *testing.T) { + // GIVEN + config := configuration.FanConfig{ + File: &configuration.FileFanConfig{ + Path: "../../test/non_existent_file", + RpmPath: "../../test/non_existent_file", + }, + } + + fan, _ := NewFan(config) + + rpmAvg := 1000.5 + + // WHEN + fan.SetRpmAvg(rpmAvg) + + // THEN + assert.Equal(t, float64(int(rpmAvg)), fan.GetRpmAvg()) +} + +func TestFileFan_GetRpmAvg(t *testing.T) { + // GIVEN + config := configuration.FanConfig{ + File: &configuration.FileFanConfig{ + Path: "../../test/non_existent_file", + RpmPath: "../../test/non_existent_file", + }, + } + + fan, _ := NewFan(config) + + rpmAvg := 1234.5 + fan.SetRpmAvg(rpmAvg) + + // WHEN + result := fan.GetRpmAvg() + + // THEN + assert.Equal(t, float64(int(rpmAvg)), result) +} + +func TestFileFan_GetPwm(t *testing.T) { + // GIVEN + config := configuration.FanConfig{ + File: &configuration.FileFanConfig{ + Path: "../../test/file_fan_pwm", + RpmPath: "../../test/file_fan_rpm", + }, + } + + fan, _ := NewFan(config) + + // WHEN + result, err := fan.GetPwm() + + // THEN + assert.NoError(t, err) + assert.Equal(t, 152, result) +} + +func TestFileFan_GetPwm_InvalidPath(t *testing.T) { + // GIVEN + config := configuration.FanConfig{ + File: &configuration.FileFanConfig{ + Path: "../../test/non_existent_file", + RpmPath: "../../test/non_existent_file", + }, + } + + fan, _ := NewFan(config) + + // WHEN + result, err := fan.GetPwm() + + // THEN + assert.Error(t, err) + assert.Equal(t, 0, result) +} + +func TestFileFan_SetPwm(t *testing.T) { + // GIVEN + defer os.Remove("./file_fan_pwm") + + config := configuration.FanConfig{ + File: &configuration.FileFanConfig{ + Path: "./file_fan_pwm", + RpmPath: "../../test/file_fan_rpm", + }, + } + + fan, _ := NewFan(config) + targetPwm := 100 + + // WHEN + err := fan.SetPwm(targetPwm) + + // THEN + assert.NoError(t, err) + + result, err := fan.GetPwm() + + assert.NoError(t, err) + assert.Equal(t, targetPwm, result) +} + +func TestFileFan_SetPwm_InvalidPath(t *testing.T) { + // GIVEN + config := configuration.FanConfig{ + File: &configuration.FileFanConfig{ + Path: "../..////", + RpmPath: "../../test/non_existent_file", + }, + } + + fan, _ := NewFan(config) + + // WHEN + err := fan.SetPwm(100) + + // THEN + assert.Error(t, err) +} + +func TestFileFan_GetFanCurveData(t *testing.T) { + // GIVEN + config := configuration.FanConfig{ + File: &configuration.FileFanConfig{ + Path: "../../test/file_fan_pwm", + RpmPath: "../../test/file_fan_rpm", + }, + } + + fan, _ := NewFan(config) + + expectedFanCurve := util.InterpolateLinearly( + &map[int]float64{ + 0: 0.0, + 255: 255.0, + }, + 0, 255, + ) + + // WHEN + result := fan.GetFanCurveData() + + // THEN + assert.Equal(t, expectedFanCurve, *result) +} + +func TestFileFan_GetCurveId(t *testing.T) { + // GIVEN + curveId := "curveId" + config := configuration.FanConfig{ + Curve: curveId, + File: &configuration.FileFanConfig{ + Path: "../../test/file_fan_pwm", + RpmPath: "../../test/file_fan_rpm", + }, + } + + fan, _ := NewFan(config) + + // WHEN + result := fan.GetCurveId() + + // THEN + assert.Equal(t, curveId, result) +} + +func TestFileFan_ShouldNeverStop(t *testing.T) { + // GIVEN + config := configuration.FanConfig{ + NeverStop: true, + File: &configuration.FileFanConfig{ + Path: "../../test/file_fan_pwm", + RpmPath: "../../test/file_fan_rpm", + }, + } + + fan, _ := NewFan(config) + + // WHEN + result := fan.ShouldNeverStop() + + // THEN + assert.Equal(t, true, result) +} diff --git a/internal/persistence/persistence_test.go b/internal/persistence/persistence_test.go index 976f289..4f26e09 100644 --- a/internal/persistence/persistence_test.go +++ b/internal/persistence/persistence_test.go @@ -1,6 +1,7 @@ package persistence import ( + "os" "testing" "github.com/markusressel/fan2go/internal/configuration" @@ -10,7 +11,7 @@ import ( ) const ( - dbTestingPath = "./test.db" + dbTestingPath = "../testing/test.db" ) var ( @@ -26,6 +27,26 @@ var ( } ) +func TestMain(m *testing.M) { + beforeEach() + code := m.Run() + afterEach() + os.Exit(code) +} + +func beforeEach() { + err := NewPersistence(dbTestingPath).Init() + if err != nil { + panic(err) + } +} + +func afterEach() { + defer func() { + _ = os.Remove(dbTestingPath) + }() +} + func TestPersistence_DeleteFanPwmData(t *testing.T) { // GIVEN p := NewPersistence(dbTestingPath) diff --git a/internal/util/file.go b/internal/util/file.go index 8a48188..5060153 100644 --- a/internal/util/file.go +++ b/internal/util/file.go @@ -55,7 +55,7 @@ func ReadIntFromFile(path string) (value int, err error) { } text := string(data) if len(text) <= 0 { - return 0, fmt.Errorf("file is empty: %s", path) + return -1, fmt.Errorf("file is empty: %s", path) } text = strings.TrimSpace(text) value, err = strconv.Atoi(text) diff --git a/internal/util/file_test.go b/internal/util/file_test.go index cad6988..b261bc9 100644 --- a/internal/util/file_test.go +++ b/internal/util/file_test.go @@ -22,8 +22,12 @@ func TestFileHasPermissionsUserIsRoot(t *testing.T) { err = os.Chmod(filePath, filePerm) assert.NoError(t, err) - defer file.Close() - defer os.Remove(filePath) + defer func(file *os.File) { + _ = file.Close() + }(file) + defer func(name string) { + _ = os.Remove(name) + }(filePath) // WHEN result, err := CheckFilePermissionsForExecution(filePath) @@ -49,8 +53,12 @@ func TestFileHasPermissionsGroupIsRootAndHasWrite(t *testing.T) { err = os.Chmod(filePath, filePerm) assert.NoError(t, err) - defer file.Close() - defer os.Remove(filePath) + defer func(file *os.File) { + _ = file.Close() + }(file) + defer func(name string) { + _ = os.Remove(name) + }(filePath) // WHEN result, err := CheckFilePermissionsForExecution(filePath) @@ -76,8 +84,12 @@ func TestFileHasPermissionsGroupOtherThanRootHasWritePermission(t *testing.T) { err = os.Chmod(filePath, filePerm) assert.NoError(t, err) - defer file.Close() - defer os.Remove(filePath) + defer func(file *os.File) { + _ = file.Close() + }(file) + defer func(name string) { + _ = os.Remove(name) + }(filePath) // WHEN result, err := CheckFilePermissionsForExecution(filePath) @@ -103,8 +115,12 @@ func TestFileHasPermissionsOtherHasWritePermission(t *testing.T) { err = os.Chmod(filePath, filePerm) assert.NoError(t, err) - defer file.Close() - defer os.Remove(filePath) + defer func(file *os.File) { + _ = file.Close() + }(file) + defer func(name string) { + _ = os.Remove(name) + }(filePath) // WHEN result, err := CheckFilePermissionsForExecution(filePath) @@ -113,3 +129,77 @@ func TestFileHasPermissionsOtherHasWritePermission(t *testing.T) { assert.Equal(t, false, result) assert.Error(t, err) } + +func TestReadIntFromFile_Success(t *testing.T) { + // GIVEN + filePath := "../../test/file_fan_rpm" + + // WHEN + result, err := ReadIntFromFile(filePath) + + // THEN + assert.Equal(t, 2150, result) + assert.NoError(t, err) +} + +func TestReadIntFromFile_FileNotFound(t *testing.T) { + // GIVEN + filePath := "../../not exists" + + // WHEN + result, err := ReadIntFromFile(filePath) + + // THEN + assert.Equal(t, -1, result) + assert.Error(t, err) +} + +func TestReadIntFromFile_FileEmpty(t *testing.T) { + // GIVEN + filePath := "./empty_file" + _, _ = os.Create(filePath) + defer func(name string) { + _ = os.Remove(name) + }(filePath) + + // WHEN + result, err := ReadIntFromFile(filePath) + + // THEN + assert.Equal(t, -1, result) + assert.Error(t, err) +} + +func TestWriteIntToFile_Success(t *testing.T) { + // GIVEN + filePath := "./testfile" + defer func(name string) { + _ = os.Remove(name) + }(filePath) + value := 123 + + // WHEN + err := WriteIntToFile(value, filePath) + + // THEN + assert.NoError(t, err) + + // WHEN + result, err := ReadIntFromFile(filePath) + + // THEN + assert.Equal(t, value, result) + assert.NoError(t, err) +} + +func TestWriteIntToFile_InvalidPath(t *testing.T) { + // GIVEN + filePath := ".////" + value := 123 + + // WHEN + err := WriteIntToFile(value, filePath) + + // THEN + assert.Error(t, err) +} diff --git a/internal/util/math.go b/internal/util/math.go index 4a197cb..605c7a8 100644 --- a/internal/util/math.go +++ b/internal/util/math.go @@ -42,12 +42,12 @@ func HexString(hex string) string { return fmt.Sprintf("%X", value) } -// Ratio calculates the ration that target has in comparison to rangeMin and rangeMax +// Ratio calculates the ratio that target has in comparison to rangeMin and rangeMax // Make sure that: // rangeMin <= target <= rangeMax // rangeMax - rangeMin != 0 func Ratio(target float64, rangeMin float64, rangeMax float64) float64 { - return (target - rangeMin) / (rangeMax - rangeMin) + return ((target - rangeMin) / (rangeMax - rangeMin) * 100) / 100 } // UpdateSimpleMovingAvg calculates the new moving average, based on an existing average and buffer size @@ -98,7 +98,7 @@ func CalculateInterpolatedCurveValue(steps map[int]float64, interpolationType st nextY := steps[nextX] ratio := Ratio(input, float64(currentX), float64(nextX)) - interpolation := currentY + ratio*(nextY-currentY) + interpolation := float64(float32(currentY + (ratio * (nextY - currentY)))) return interpolation } } @@ -128,7 +128,7 @@ func FindClosest(target int, arr []int) int { mid = (i + j) / 2 if arr[mid] == target { - return arr[mid] + break } /* If target is less than array element, diff --git a/internal/util/math_test.go b/internal/util/math_test.go index 7dae1fb..e23ae79 100644 --- a/internal/util/math_test.go +++ b/internal/util/math_test.go @@ -57,6 +57,16 @@ func TestFindClosest(t *testing.T) { // THEN assert.Equal(t, 10, closest) + // WHEN + closest = FindClosest(11, options) + // THEN + assert.Equal(t, 10, closest) + + // WHEN + closest = FindClosest(50, options) + // THEN + assert.Equal(t, 50, closest) + // WHEN closest = FindClosest(54, options) // THEN @@ -64,13 +74,78 @@ func TestFindClosest(t *testing.T) { // WHEN closest = FindClosest(55, options) - // THEN assert.Equal(t, 60, closest) + // WHEN + closest = FindClosest(75, options) + // THEN + assert.Equal(t, 80, closest) + // WHEN closest = FindClosest(100, options) // THEN assert.Equal(t, 90, closest) } + +func TestCoerce(t *testing.T) { + // GIVEN + min := 0.0 + max := 10.0 + + // WHEN + resultMin := Coerce(-10, min, max) + // THEN + assert.Equal(t, min, resultMin) + + // WHEN + resultValueLow := Coerce(0, min, max) + // THEN + assert.Equal(t, resultValueLow, resultValueLow) + + // WHEN + resultValueHigh := Coerce(10, min, max) + + // THEN + assert.Equal(t, resultValueHigh, resultValueHigh) + + // WHEN + resultMax := Coerce(20, min, max) + // THEN + assert.Equal(t, max, resultMax) +} + +func TestUpdateSimpleMovingAvg(t *testing.T) { + // GIVEN + avg := 0.0 + n := 2 + newValue := 10.0 + + // WHEN + result := UpdateSimpleMovingAvg(avg, n, newValue) + + // THEN + assert.Equal(t, 5.0, result) +} + +func TestInterpolateLinearly(t *testing.T) { + // GIVEN + data := map[int]float64{ + 0: 0.0, + 100: 100.0, + } + start := 0 + stop := 100 + + expectedResult := map[int]float64{} + for i := 0; i <= 100; i++ { + expectedResult[i] = float64(i) + } + + // WHEN + result := InterpolateLinearly(&data, start, stop) + + // THEN + assert.Equal(t, expectedResult, result) +} diff --git a/internal/util/pid_test.go b/internal/util/pid_test.go new file mode 100644 index 0000000..a55e1f2 --- /dev/null +++ b/internal/util/pid_test.go @@ -0,0 +1,90 @@ +package util + +import ( + "github.com/stretchr/testify/assert" + "testing" + "time" +) + +func TestNewPidLoop(t *testing.T) { + // GIVEN + p, i, d := 1.0, 2.0, 3.0 + + // WHEN + pidLoop := NewPidLoop(p, i, d) + + // THEN + assert.Equal(t, p, pidLoop.p) + assert.Equal(t, i, pidLoop.i) + assert.Equal(t, d, pidLoop.d) +} + +func TestPidLoop_Advance(t *testing.T) { + // GIVEN + p, i, d := 1.0, 2.0, 3.0 + pidLoop := NewPidLoop(p, i, d) + + // WHEN + output := pidLoop.Loop(10.0, 5.0) + // THEN + assert.Equal(t, 0.0, output) + + // WHEN + output = pidLoop.Loop(10.0, 5.0) + // THEN + assert.Equal(t, 5, int(output)) +} + +func TestPidLoop_P(t *testing.T) { + // GIVEN + p, i, d := 0.01, 0.0, 0.0 + pidLoop := NewPidLoop(p, i, d) + + // WHEN + output := pidLoop.Loop(10.0, 5.0) + // THEN + assert.Equal(t, 0.0, output) + + time.Sleep(1 * time.Second) + + // WHEN + output = pidLoop.Loop(10.0, 5.0) + // THEN + assert.Equal(t, 0.05, output) +} + +func TestPidLoop_I(t *testing.T) { + // GIVEN + p, i, d := 0.0, 0.01, 0.0 + pidLoop := NewPidLoop(p, i, d) + + // WHEN + output := pidLoop.Loop(10.0, 5.0) + // THEN + assert.Equal(t, 0.0, output) + + time.Sleep(1 * time.Second) + + // WHEN + output = pidLoop.Loop(10.0, 5.0) + // THEN + assert.InDelta(t, 0.05, output, 0.01) +} + +func TestPidLoop_D(t *testing.T) { + // GIVEN + p, i, d := 0.0, 0.0, 0.01 + pidLoop := NewPidLoop(p, i, d) + + // WHEN + output := pidLoop.Loop(10.0, 5.0) + // THEN + assert.Equal(t, 0.0, output) + + time.Sleep(1 * time.Second) + + // WHEN + output = pidLoop.Loop(10.0, 8.0) + // THEN + assert.InDelta(t, -0.03, output, 0.01) +} diff --git a/internal/util/slice_test.go b/internal/util/slice_test.go index 3c2cbbf..9d48e22 100644 --- a/internal/util/slice_test.go +++ b/internal/util/slice_test.go @@ -34,3 +34,37 @@ func TestContainsString_Invalid(t *testing.T) { // THEN assert.False(t, result) } + +func TestSortSlice(t *testing.T) { + // GIVEN + list := []float64{ + 3.0, + 1.0, + 2.0, + } + + // WHEN + sortSlice(list) + + // THEN + assert.Equal(t, 1.0, list[0]) + assert.Equal(t, 2.0, list[1]) + assert.Equal(t, 3.0, list[2]) +} + +func TestSortedKeys(t *testing.T) { + // GIVEN + m := map[int]string{ + 3: "three", + 1: "one", + 2: "two", + } + + // WHEN + result := SortedKeys(m) + + // THEN + assert.Equal(t, 1, result[0]) + assert.Equal(t, 2, result[1]) + assert.Equal(t, 3, result[2]) +} diff --git a/internal/util/window_test.go b/internal/util/window_test.go new file mode 100644 index 0000000..982f31b --- /dev/null +++ b/internal/util/window_test.go @@ -0,0 +1,31 @@ +package util + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestGetWindowMax(t *testing.T) { + // GIVEN + window := CreateRollingWindow(3) + window.Append(1) + window.Append(2) + window.Append(3) + + // WHEN + maximumm := GetWindowMax(window) + + // THEN + assert.Equal(t, 3.0, maximumm) +} + +func TestFillWindow(t *testing.T) { + // GIVEN + window := CreateRollingWindow(10) + + // WHEN + FillWindow(window, 3, 10) + + // THEN + assert.Equal(t, 10.0, GetWindowMax(window)) +}