evcc-io/core/circuit/circuit_test.go

177 lines
4.4 KiB
Go

package circuit
import (
"testing"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
)
type circuitTest struct {
// current values for parent, circuit 1, circuit 2
p, c1, c2 float64
// old/new demand values and allowed result
old, new, res float64
}
func circuitTests() []circuitTest {
return []circuitTest{
// no load
{0, 0, 0, 0, 0, 0}, // =
{0, 0, 0, 0, 1, 1}, // +
{0, 0, 0, 0, 2, 1}, // +
{0, 0, 0, 1, 1, 1}, // =
// circuit 1 loaded
{0, 1, 0, 0, 0, 0}, // =
{0, 1, 0, 0, 1, 0}, // +
{0, 1, 0, 0, 2, 0}, // +
{0, 1, 0, 1, 1, 1}, // =
{0, 1, 0, 2, 1, 1}, // -
// circuit 1 overloaded
{0, 2, 0, 0, 0, 0}, // =
{0, 2, 0, 0, 1, 0}, // +
{0, 2, 0, 1, 1, 0}, // =
{0, 2, 0, 2, 2, 1}, // =
{0, 2, 0, 2, 3, 1}, // +
{0, 2, 0, 2, 1, 1}, // -
{0, 1.1, 0, 2, 1, 1}, // -
{0, 1.1, 0, 1, 0, 0}, // -
// parent loaded
{1, 0, 0, 0, 0, 0}, // =
{1, 0, 0, 0, 1, 0}, // +
{1, 0, 0, 0, 2, 0}, // +
{1, 0, 0, 1, 1, 1}, // =
{1, 0, 0, 2, 1, 1}, // -
// parent overloaded
{2, 0, 0, 0, 0, 0}, // =
{2, 0, 0, 0, 1, 0}, // +
{2, 0, 0, 1, 1, 0}, // =
{2, 0, 0, 2, 2, 1}, // =
{2, 0, 0, 2, 3, 1}, // +
{2, 0, 0, 2, 1, 1}, // -
{1.1, 0, 0, 2, 1, 1}, // -
{1.1, 0, 0, 1, 0, 0}, // -
// negative load
{-1, -1, 0, 0, 2, 2}, // +
}
}
func TestCircuitPower(t *testing.T) {
log := util.NewLogger("foo")
circ := func(t *testing.T, ctrl *gomock.Controller, maxP float64) (*Circuit, *api.MockMeter) {
m := api.NewMockMeter(ctrl)
c, err := New(log, "foo", 0, maxP, m, 0)
require.NoError(t, err)
return c, m
}
for _, tc := range circuitTests() {
ctrl := gomock.NewController(t)
pc, pm := circ(t, ctrl, 1)
c1, cm1 := circ(t, ctrl, 1)
c2, cm2 := circ(t, ctrl, 1)
c1.setParent(pc)
c2.setParent(pc)
// update meters
pm.EXPECT().CurrentPower().Return(tc.p, nil)
cm1.EXPECT().CurrentPower().Return(tc.c1, nil)
cm2.EXPECT().CurrentPower().Return(tc.c2, nil)
require.NoError(t, pc.Update(nil))
assert.Equal(t, tc.res, c1.ValidatePower(tc.old, tc.new), tc)
ctrl.Finish()
}
}
func TestCircuitCurrents(t *testing.T) {
log := util.NewLogger("foo")
type combined struct {
*api.MockMeter
*api.MockPhaseCurrents
}
circ := func(t *testing.T, ctrl *gomock.Controller, maxC float64) (*Circuit, combined) {
m := combined{
api.NewMockMeter(ctrl),
api.NewMockPhaseCurrents(ctrl),
}
c, err := New(log, "foo", maxC, 0, m, 0)
require.NoError(t, err)
return c, m
}
for _, tc := range circuitTests() {
ctrl := gomock.NewController(t)
pc, pm := circ(t, ctrl, 1)
c1, cm1 := circ(t, ctrl, 1)
c2, cm2 := circ(t, ctrl, 1)
c1.setParent(pc)
c2.setParent(pc)
// update meters
pm.MockMeter.EXPECT().CurrentPower().AnyTimes().Return(0.0, nil)
cm1.MockMeter.EXPECT().CurrentPower().AnyTimes().Return(0.0, nil)
cm2.MockMeter.EXPECT().CurrentPower().AnyTimes().Return(0.0, nil)
pm.MockPhaseCurrents.EXPECT().Currents().Return(tc.p, tc.p, tc.p, nil)
cm1.MockPhaseCurrents.EXPECT().Currents().Return(tc.c1, tc.c1, tc.c1, nil)
cm2.MockPhaseCurrents.EXPECT().Currents().Return(tc.c2, tc.c2, tc.c2, nil)
require.NoError(t, pc.Update(nil))
assert.Equal(t, tc.res, c1.ValidateCurrent(tc.old, tc.new), tc)
ctrl.Finish()
}
}
// TestHEMSConsumptionClamp verifies that ValidatePower clamps against the
// HEMS consumption limit when one is registered on the root circuit.
func TestHEMSConsumptionClamp(t *testing.T) {
log := util.NewLogger("foo")
for _, tc := range []struct {
name string
maxPower float64
hemsLimit float64
request float64
want float64
}{
{"no hems, no max", 0, 0, 9000, 9000},
{"no hems, max set", 5000, 0, 9000, 5000},
{"hems below max", 5000, 4200, 9000, 4200},
{"hems above max", 5000, 9000, 9000, 5000},
// when the user has no circuit max, the HEMS limit is not enforced
// — strictly preserve user intent (no clamp without an explicit user-side cap).
{"hems but no user max", 0, 4200, 9000, 9000},
} {
t.Run(tc.name, func(t *testing.T) {
c, err := New(log, "root", 0, tc.maxPower, nil, 0)
require.NoError(t, err)
if tc.hemsLimit > 0 {
ctrl := gomock.NewController(t)
hems := api.NewMockHEMS(ctrl)
hems.EXPECT().MaxConsumptionPower().Return(tc.hemsLimit).AnyTimes()
c.hems = hems
}
assert.Equal(t, tc.want, c.ValidatePower(0, tc.request))
})
}
}