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() } } func TestWrapCycleDetection(t *testing.T) { log := util.NewLogger("foo") pc, _ := New(log, "root", 0, 0, nil, 0) lpc, _ := New(log, "lpc", 0, 0, nil, 0) require.NoError(t, lpc.setParent(pc)) require.Error(t, pc.Wrap(lpc)) }