blob: ac7e570fa3052b9b15a9077358c2316c1f0ea34b [file] [log] [blame]
# Copyright 2013 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""This is a buzzer test.
Description
-----------
This test checks the functionality of the buzzer of a ChromeOS device.
Test Procedure
--------------
1. Press space and DUT will start buzzing.
2. Press the number of buzz heard to pass the test or press 'r' to play again.
Dependency
----------
None.
Examples
--------
To test buzzer with default parameters on puff, add this in test list::
{
"pytest_name": "buzzer"
"args": {
"gpio_index": "382"
}
}
If you want to change the mute duration between two beeps and the duration of a
beep and test buzzer on brask::
{
"pytest_name": "buzzer",
"args": {
"gpio_index": "166"
"beep_duration_secs": 0.8,
"mute_duration_secs": 1.5
}
}
"""
import datetime
import random
import time
from cros.factory.device import device_utils
from cros.factory.test.i18n import _
from cros.factory.test import test_case
from cros.factory.test import test_ui
from cros.factory.utils.arg_utils import Arg
_MAX_BEEP_TIMES = 5
# Use the same frequency as dev boot beep
# pylint: disable=line-too-long
# Reference: https://chromium.googlesource.com/chromiumos/platform/depthcharge/+/41b87c02a7facc8ba47c3f9ac6ad16d4fcfbc2b8/src/drivers/sound/gpio_edge_buzzer.c#29
# pylint: enable=line-too-long
_BEEP_FREQUENCY = 2700
_SECOND_TO_NANOSECONDS = 10**9
class BuzzerTestArgs:
beep_duration_secs: float
mute_duration_secs: float
gpio_index: str
class BuzzerTest(test_case.TestCase):
"""Tests buzzer."""
related_components = tuple()
ARGS = [
# Common arguments
Arg('beep_duration_secs', float, 'How long for one beep', 0.3),
Arg('mute_duration_secs', float, 'Mute duration between two beeps', 0.5),
Arg('gpio_index', str, 'Index for gpio file depending on the board'),
]
args: BuzzerTestArgs
ui: test_ui.StandardUI
def setUp(self):
self.dut = device_utils.CreateDUTInterface()
self._pass_digit = random.randint(1, _MAX_BEEP_TIMES)
self.ui.ToggleTemplateClass('font-large', True)
def runTest(self):
max_total_duration = _MAX_BEEP_TIMES * (
self.args.beep_duration_secs + self.args.mute_duration_secs)
self.ui.SetState(_('How many beeps do you hear? <br>Press space to start.'))
self.ui.WaitKeysOnce(test_ui.SPACE_KEY)
self.ui.SetState(
_('How many beeps do you hear? <br>'
'Press the number you hear to pass the test.<br>'
"Press 'r' to play again."))
while True:
start_time = time.time()
for unused_i in range(self._pass_digit):
self.BeepOnce(self.args.beep_duration_secs)
self.Sleep(self.args.mute_duration_secs)
# Try to make the test always run for about same duration, to avoid
# cheating by looking at when the buttons appear.
self.Sleep(max_total_duration - (time.time() - start_time))
all_keys = [str(num + 1) for num in range(_MAX_BEEP_TIMES)] + ['R']
key = self.ui.WaitKeysOnce(all_keys)
if key != 'R':
self.assertEqual(self._pass_digit, int(key), 'Wrong number to press.')
return
def WaitingLoop(self, target_time):
time_nsecs = time.time() * _SECOND_TO_NANOSECONDS
while time_nsecs < target_time:
time_nsecs = time.time() * _SECOND_TO_NANOSECONDS
def BeepOnce(self, beep_duration):
t1 = datetime.datetime.now()
beep_sec = datetime.timedelta(seconds=beep_duration)
index = self.args.gpio_index
self.dut.WriteSpecialFile('/sys/class/gpio/export', index)
self.dut.WriteSpecialFile(f'/sys/class/gpio/gpio{index}/direction', 'out')
i = 0
half_period_nsecs = _SECOND_TO_NANOSECONDS / _BEEP_FREQUENCY / 2
start_time_nsecs = time.time() * _SECOND_TO_NANOSECONDS
next_flip_time_nsecs = start_time_nsecs + half_period_nsecs
while t1 + beep_sec > datetime.datetime.now():
self.dut.WriteSpecialFile(f'/sys/class/gpio/gpio{index}/value', str(i))
i = i ^ 1
self.WaitingLoop(next_flip_time_nsecs)
next_flip_time_nsecs += half_period_nsecs
self.dut.WriteSpecialFile('/sys/class/gpio/unexport', index)