blob: d1f1eff68f8228a04e74185f92d9190d09741535 [file] [log] [blame]
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package wilco
import (
"context"
"time"
"chromiumos/tast/ctxutil"
"chromiumos/tast/local/rtc"
"chromiumos/tast/local/upstart"
"chromiumos/tast/testing"
)
func init() {
testing.AddTest(&testing.Test{
Func: ECRTC,
Desc: "Checks that the EC RTC on Wilco devices is readable, writable, and updates itself",
Contacts: []string{
"campello@chromium.org", // Test maintainer.
"chromeos-wilco@google.com", // Possesses some more domain-specific knowledge.
"chromeos-kernel-test@google.com",
},
SoftwareDeps: []string{"wilco"},
Timeout: 30 * time.Second,
Attr: []string{"group:mainline"},
})
}
// ECRTC tests the RTC contained within the EC on Wilco devices. As a
// first check it reads the current time. Then, for a more detailed check,
// it sets the time to a fake time, sleeps for a bit, and reads the
// time again. The RTC better have updated itself. The test attempts to
// reset the RTC back to time.Now() after failure or completion.
func ECRTC(ctx context.Context, s *testing.State) {
// If the main body of the test times out, we still want to reserve a few
// seconds to allow for our cleanup code to run.
cleanupCtx := ctx
mainCtx, cancel := ctxutil.Shorten(cleanupCtx, 5*time.Second)
defer cancel()
const (
numRTCRetries = 5
sleepTime = 3 * time.Second
// There is an upstart job that continually keeps the EC RTC in sync with
// local time. We need to disable it during the test.
upstartJobName = "wilco_sync_ec_rtc"
tolerance = 2 * time.Second
)
wilcoECRTC := rtc.RTC{DevName: "rtc1", LocalTime: true, NoAdjfile: true}
readECRTC := func() time.Time {
for i := 1; ; i++ {
t, err := wilcoECRTC.Read(mainCtx)
if err == nil {
return t
}
s.Logf("Failed to read EC RTC (trial %d/%d): %v", i, numRTCRetries, err)
if i >= numRTCRetries {
s.Fatal("Failed to read EC RTC: ", err)
return time.Time{}
}
}
}
writeECRTC := func(ctx context.Context, t time.Time) {
for i := 1; ; i++ {
err := wilcoECRTC.Write(ctx, t)
if err == nil {
return
}
s.Logf("Failed to write EC RTC (trial %d/%d): %v", i, numRTCRetries, err)
if i >= numRTCRetries {
s.Fatal("Failed to write EC RTC: ", err)
return
}
}
}
// Validity check before we do more complicated testing.
readECRTC()
// Stop the upstart job that keeps the EC RTC in sync with local time.
if err := upstart.StopJob(mainCtx, upstartJobName); err != nil {
s.Fatal("Failed to stop sync RTC upstart job: ", err)
}
defer func() {
if err := upstart.EnsureJobRunning(cleanupCtx, upstartJobName); err != nil {
s.Fatal("Failed to restart sync RTC upstart job: ", err)
}
}()
// Ensure (as best we can) that the DUT is back in it's original state after the test.
defer func() {
writeECRTC(cleanupCtx, time.Now())
}()
// Set the RTC to a fake time for consistency.
startTime := time.Date(2001, time.January, 1, 12, 0, 0, 0, time.Now().Location())
// Set the RTC, sleep a bit, and the RTC better have updated itself.
writeECRTC(mainCtx, startTime)
realStartTime := time.Now()
testing.Sleep(mainCtx, sleepTime)
elapsed := readECRTC().Sub(startTime)
realElapsed := time.Now().Sub(realStartTime)
if elapsed < realElapsed-tolerance || elapsed > realElapsed+tolerance {
s.Fatalf("RTC did not update properly: got %v; want in [%v, %v]", elapsed, realElapsed-tolerance, realElapsed+tolerance)
}
}