blob: 8f0ae51503129c1b5860972b38c270e291975877 [file] [log] [blame]
# -*- coding: utf-8 -*-
# Copyright (c) 2010 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.
# DESCRIPTION : factory test of ambient light sensor. Test that ALS reacts to
# both darkening by covering w/ finger as well as brightening.
# Roughly speaking:
# indoor ambient lighting: 20-100
# sunlight direct: 30k-60k
# flashlight direct: 5k-10k
import gobject
import gtk
import logging
import math
import os
import sys
import time
from autotest_lib.client.bin import test
from autotest_lib.client.common_lib import error
from autotest_lib.client.common_lib import utils
from autotest_lib.client.cros import factory_setup_modules
from cros.factory.test import factory
from cros.factory.test import ui as ful
_LABEL_STATUS_SIZE = (140, 30)
_DEFAULT_SUBTEST_LIST = ['Light sensor dark',
'Light sensor exact',
'Light sensor light']
_DEFAULT_SUBTEST_CFG = {'Light sensor dark': {'below': 4},
'Light sensor exact': {'between': (10, 15)},
'Light sensor light': {'above': 200}}
_DEFAULT_SUBTEST_INSTRUCTION = {
'Light sensor dark': 'Cover light sensor with finger',
'Light sensor exact': 'Remove finger from light sensor',
'Light sensor light': 'Shine light sensor with flashlight'}
_DEFAULT_DEVICE_PATH='/sys/bus/iio/devices/iio:device0/'
class iio_generic():
'''
Object to interface to ambient light sensor over iio.
'''
PARAMS = {'rd': _DEFAULT_DEVICE_PATH + 'illuminance0_input',
'range_setting': _DEFAULT_DEVICE_PATH + 'range',
'init': '',
'min': 0,
'max': math.pow(2, 16),
# in seconds
'mindelay': 0.178,
}
def __init__(self, device_path, range_value):
self.buf = []
self.ambient = None
if device_path is not None:
self.PARAMS['rd'] = device_path + 'illuminance0_input'
self.PARAMS['range_setting'] = device_path + 'range'
if not os.path.isfile(self.PARAMS['rd']):
self.cfg()
if range_value is not None:
with open(self.PARAMS['range_setting'], 'w') as f:
f.write('%d\n' % range_value)
self.ambient = self.read('mean', delay=0, samples=10)
factory.log('ambient light sensor = %d' % self.ambient)
def cfg(self):
cmd = self.PARAMS['init']
utils.system(cmd)
time.sleep(1)
if not os.path.isfile(self.PARAMS['rd']):
raise error.TestError(cmd + ' did not create ' + self.PARAMS['rd'])
val = self.read('first', samples=1)
if val <= self.PARAMS['min'] or val >= self.PARAMS['max']:
raise error.TestError('Failed initial read\n')
def read(self, type, delay=None, samples=1):
'''
Read the light sensor and return value based on type
@parameter type - string describing type of value to return. Valid
strings are 'mean' | 'min' | 'max' | 'raw'
@parameter delay - delay between samples in seconds. 0 means as fast as
possible
@parameter samples - total samples to read. O means infinite
'''
cnt = 0
self.buf = []
if delay is None:
delay = self.PARAMS['mindelay']
while True:
fd = open(self.PARAMS['rd'])
ln = int(fd.readline().rstrip())
fd.close()
self.buf.append(ln)
cnt += 1
time.sleep(delay)
if cnt == samples:
break
if type is 'mean':
return sum(self.buf) / len(self.buf)
elif type is 'max':
return max(self.buf)
elif type is 'min':
return min(self.buf)
elif type is 'raw':
return self.buf
elif type is 'first':
return self.buf[0]
else:
error.ValueError('Illegal value %s for type' % type)
class factory_LightSensor(test.test):
version = 3
def next_subtest(self):
self._tested += 1
if self._tested >= len(self._subtest_list):
gtk.main_quit()
return False
self._active_subtest = self._subtest_list[self._tested]
self.update_status(self._active_subtest, ful.ACTIVE)
self._status_label[self._active_subtest].queue_draw()
self._deadline = time.time() + self._timeout_per_subtest
self._current_iter_remained = self._iter_req_per_subtest
self._cumulative_val = 0
return True
def timer_event(self, countdown_label):
time_remaining = max(0, self._deadline - time.time())
if time_remaining is 0:
self.update_status(self._active_subtest, ful.FAILED)
self._status_label[self._active_subtest].queue_draw()
factory.log('Timeout on subtest "%s"' % self._active_subtest)
if not self.next_subtest():
return True
countdown_label.set_text('%d' % time_remaining)
countdown_label.queue_draw()
return True
def key_release_callback(self, widget, event):
if event.keyval == ord('Q'):
gtk.main_quit()
return True
if event.keyval == ord(' ') and not self._started:
self._started = True
self._active_subtest = self._subtest_list[0]
self.update_status(self._active_subtest, ful.ACTIVE)
self._status_label[self._active_subtest].queue_draw()
self._deadline = time.time() + self._timeout_per_subtest
gobject.timeout_add(1000, self.timer_event, self._countdown_label)
return True
def pass_one_iter(self, name):
self._current_iter_remained -= 1
if self._current_iter_remained is 0:
self.update_status(name, ful.PASSED)
self._status_label[name].queue_draw()
self._current_iter_remained = self._iter_req_per_subtest
mean_val = self._cumulative_val / self._iter_req_per_subtest
factory.log('Passed subtest "%s" with mean value %d.' %
(name, mean_val))
if not self.next_subtest():
return
def sensor_event(self, sensor_value):
val = self._als.read('mean', samples=5, delay=0)
if self._started:
name = self._active_subtest
cfg = self._subtest_cfg[name]
passed = 0
if 'above' in cfg:
if val > cfg['above']:
factory.log('Passed checking "above" %d > %d' %
(val, cfg['above']))
passed = 1
elif 'below' in cfg:
if val < cfg['below']:
factory.log('Passed checking "below" %d < %d' %
(val, cfg['below']))
passed = 1
elif 'between' in cfg:
lb, ub = cfg['between']
if val > lb and val < ub:
factory.log('Passed checking "between" %d < %d < %d' %
(lb, val, ub))
passed = 1
if passed is 1:
self._cumulative_val += val
self.pass_one_iter(name)
else:
if self._current_iter_remained != self._iter_req_per_subtest:
factory.log('Resetting iter count.')
self._cumulative_val = 0
self._current_iter_remained = self._iter_req_per_subtest
sensor_value.set_text('%d' % val)
sensor_value.queue_draw()
return True
def update_status(self, name, status):
self._status_map[name] = status
self._label_status[name].set_text(status)
self._label_status[name].modify_fg(gtk.STATE_NORMAL,
ful.LABEL_COLORS[status])
self._label_status[name].queue_draw()
def calc_cfg_description(self, cfg):
if 'above' in cfg:
return 'Input > %d' % cfg['above']
elif 'below' in cfg:
return 'Input < %d' % cfg['below']
elif 'between' in cfg:
return '%d < Input < %d' % cfg['between']
else:
raise error.ValueError('Unknown type in subtest configuration')
def make_subtest_label_box(self, name):
eb = gtk.EventBox()
eb.modify_bg(gtk.STATE_NORMAL, ful.BLACK)
label_status = ful.make_label(ful.UNTESTED, size=_LABEL_STATUS_SIZE,
alignment=(0, 0.5),
fg=ful.LABEL_COLORS[ful.UNTESTED])
self._label_status[name] = label_status
label_desc = ful.make_label(self._subtest_instruction[name],
alignment=(0.5, 0.5), fg=ful.WHITE)
cfg_desc = self.calc_cfg_description(self._subtest_cfg[name])
label_en = ful.make_label("%s (%s)" % (name, cfg_desc),
alignment=(1, 0.5))
label_sep = ful.make_label(' : ', alignment=(0.5, 0.5))
hbox = gtk.HBox()
hbox.pack_end(label_status, False, False)
hbox.pack_end(label_sep, False, False)
hbox.pack_end(label_en, False, False)
vbox = gtk.VBox()
vbox.pack_start(label_desc, False, False)
vbox.pack_start(hbox, False, False)
eb.add(vbox)
return eb
def register_callbacks(self, window):
window.connect('key-release-event', self.key_release_callback)
window.add_events(gtk.gdk.KEY_RELEASE_MASK)
def get_subtest(self, subtest_list, subtest_cfg, subtest_instruction):
has_specified = (subtest_list is not None or
subtest_cfg is not None or
subtest_instruction is not None)
all_specified = (subtest_list is not None and
subtest_cfg is not None and
subtest_instruction is not None)
if has_specified and not all_specified:
raise error.ValueError('Missing parameters of subtests.')
if all_specified:
self._subtest_list = subtest_list
self._subtest_cfg = subtest_cfg
self._subtest_instruction = subtest_instruction
else:
self._subtest_list = _DEFAULT_SUBTEST_LIST
self._subtest_cfg = _DEFAULT_SUBTEST_CFG
self._subtest_instruction = _DEFAULT_SUBTEST_INSTRUCTION
def run_once(self,
device_path=None,
timeout_per_subtest=10,
subtest_list=None,
subtest_cfg=None,
subtest_instruction=None,
range_value=None):
factory.log('%s run_once' % self.__class__)
self.get_subtest(subtest_list, subtest_cfg, subtest_instruction)
self._als = iio_generic(device_path, range_value)
self._timeout_per_subtest = timeout_per_subtest
self._iter_req_per_subtest = 3
self._current_iter_remained = self._iter_req_per_subtest
self._cumulative_val = 0
self._status_map = dict((n, ful.UNTESTED) for n in self._subtest_list)
self._label_status = dict()
self._tested = 0
self._started = False
vbox = gtk.VBox()
prompt_label = ful.make_label(
'Use indicated light source to pass each subtest\n'
'Hit "space" to begin...\n',
fg=ful.WHITE)
vbox.pack_start(prompt_label, False, False)
padding_size = 15
self._status_label = {}
for name in self._subtest_list:
label_box = self.make_subtest_label_box(name)
vbox.pack_start(label_box, False, False, padding_size)
self._status_label[name] = label_box
vbox.pack_start(ful.make_hsep(), False, False, padding_size)
hbox = gtk.HBox()
sensor_label = ful.make_label('Input: ', fg=ful.WHITE)
sensor_value = ful.make_label(' ', fg=ful.WHITE)
hbox.pack_start(sensor_label, False, False)
hbox.pack_start(sensor_value, False, False)
countdown_widget, self._countdown_label = ful.make_countdown_widget()
self._countdown_label.set_text('%d' % timeout_per_subtest)
hbox.pack_end(countdown_widget, False, False)
vbox.pack_start(hbox, False, False)
# This function sets the period of getting sensor value in ms.
# If the value is too small, gtk may not redraw message box properly.
gobject.timeout_add(600, self.sensor_event, sensor_value)
self._test_widget = vbox
ful.run_test_widget(self.job, vbox,
window_registration_callback=self.register_callbacks)
failed_set = set(name for name, status in self._status_map.items()
if status is not ful.PASSED)
if failed_set:
raise error.TestFail('some subtests timed out (%s)' %
', '.join(failed_set))
factory.log('%s run_once finished' % self.__class__)