170 lines
3.8 KiB
Go
170 lines
3.8 KiB
Go
package wrapper
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/benbjohnson/clock"
|
|
"github.com/evcc-io/evcc/api"
|
|
"github.com/evcc-io/evcc/util"
|
|
"go.uber.org/mock/gomock"
|
|
)
|
|
|
|
func TestNoMeter(t *testing.T) {
|
|
cr := NewChargeRater(util.NewLogger("foo"), nil)
|
|
clck := clock.NewMock()
|
|
cr.clck = clck
|
|
|
|
cr.StartCharge(false)
|
|
clck.Add(time.Hour)
|
|
|
|
if f, err := cr.ChargedEnergy(); f != 0 || err != nil {
|
|
t.Errorf("energy: %.1f %v", f, err)
|
|
}
|
|
|
|
cr.StartCharge(true)
|
|
|
|
// 1kWh
|
|
clck.Add(time.Hour)
|
|
cr.SetChargePower(1e3)
|
|
cr.SetChargePower(1e3) // should be ignored as time is identical
|
|
|
|
// 0kWh
|
|
clck.Add(time.Hour)
|
|
cr.SetChargePower(0)
|
|
|
|
cr.StopCharge()
|
|
|
|
// 1kWh - not counted
|
|
clck.Add(time.Hour)
|
|
cr.SetChargePower(1e3)
|
|
|
|
if f, err := cr.ChargedEnergy(); f != 1 || err != nil {
|
|
t.Errorf("energy: %.1f %v", f, err)
|
|
}
|
|
|
|
// continue
|
|
cr.StartCharge(true)
|
|
|
|
// 1kWh
|
|
clck.Add(2 * time.Hour)
|
|
cr.SetChargePower(1e3)
|
|
cr.StopCharge()
|
|
|
|
if f, err := cr.ChargedEnergy(); f != 3 || err != nil {
|
|
t.Errorf("energy: %.1f %v", f, err)
|
|
}
|
|
}
|
|
|
|
func TestWrappedMeter(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
mm := api.NewMockMeter(ctrl)
|
|
me := api.NewMockMeterEnergy(ctrl)
|
|
|
|
type EnergyDecorator struct {
|
|
api.Meter
|
|
api.MeterEnergy
|
|
}
|
|
|
|
cm := &EnergyDecorator{Meter: mm, MeterEnergy: me}
|
|
|
|
cr := NewChargeRater(util.NewLogger("foo"), cm)
|
|
clck := clock.NewMock()
|
|
cr.clck = clck
|
|
|
|
me.EXPECT().TotalEnergy().Return(2.0, nil)
|
|
|
|
cr.StartCharge(false)
|
|
|
|
// ignored with meter present
|
|
clck.Add(time.Hour)
|
|
cr.SetChargePower(1e3)
|
|
clck.Add(time.Hour)
|
|
cr.SetChargePower(0)
|
|
|
|
me.EXPECT().TotalEnergy().Return(3.0, nil)
|
|
|
|
cr.StopCharge()
|
|
|
|
if f, err := cr.ChargedEnergy(); f != 1 || err != nil {
|
|
t.Errorf("energy: %.1f %v", f, err)
|
|
}
|
|
|
|
// ignored with meter present
|
|
clck.Add(time.Hour)
|
|
cr.SetChargePower(1e3)
|
|
|
|
// continue
|
|
me.EXPECT().TotalEnergy().Return(10.0, nil)
|
|
|
|
cr.StartCharge(true)
|
|
clck.Add(time.Hour) // actual timing ignored as energy comes from meter
|
|
|
|
me.EXPECT().TotalEnergy().Return(12.0, nil)
|
|
|
|
cr.StopCharge()
|
|
|
|
if f, err := cr.ChargedEnergy(); f != 3 || err != nil {
|
|
t.Errorf("energy: %.1f %v", f, err)
|
|
}
|
|
}
|
|
|
|
// TestDeferredBaseline covers the OCPP transaction-recovery case: the meter is
|
|
// not yet readable when StartCharge fires, so the baseline must be latched on
|
|
// the first successful TotalEnergy() read instead of defaulting to zero
|
|
// (which would cause the lifetime register to be reported as session energy).
|
|
func TestDeferredBaseline(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
mm := api.NewMockMeter(ctrl)
|
|
me := api.NewMockMeterEnergy(ctrl)
|
|
|
|
type EnergyDecorator struct {
|
|
api.Meter
|
|
api.MeterEnergy
|
|
}
|
|
|
|
cm := &EnergyDecorator{Meter: mm, MeterEnergy: me}
|
|
|
|
cr := NewChargeRater(util.NewLogger("foo"), cm)
|
|
clck := clock.NewMock()
|
|
cr.clck = clck
|
|
|
|
// meter not yet available at StartCharge — recovered transaction before first MeterValues
|
|
me.EXPECT().TotalEnergy().Return(0.0, errors.New("not available"))
|
|
|
|
cr.StartCharge(true)
|
|
|
|
// first read also fails — must surface the error, not a bogus delta
|
|
me.EXPECT().TotalEnergy().Return(0.0, errors.New("not available"))
|
|
|
|
if _, err := cr.ChargedEnergy(); err == nil {
|
|
t.Errorf("expected error while meter unavailable")
|
|
}
|
|
|
|
// first successful read latches the baseline (lifetime register, e.g. 939 kWh)
|
|
me.EXPECT().TotalEnergy().Return(939.080, nil)
|
|
|
|
if f, err := cr.ChargedEnergy(); f != 0 || err != nil {
|
|
t.Errorf("expected 0 on baseline-latch read, got %.3f %v", f, err)
|
|
}
|
|
|
|
// subsequent reads return delta against the latched baseline
|
|
me.EXPECT().TotalEnergy().Return(942.080, nil)
|
|
|
|
if f, err := cr.ChargedEnergy(); f != 3 || err != nil {
|
|
t.Errorf("expected 3kWh delta, got %.3f %v", f, err)
|
|
}
|
|
|
|
me.EXPECT().TotalEnergy().Return(944.080, nil)
|
|
|
|
cr.StopCharge()
|
|
|
|
if f, err := cr.ChargedEnergy(); f != 5 || err != nil {
|
|
t.Errorf("final energy: %.1f %v", f, err)
|
|
}
|
|
}
|