| # -*- 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__) |