| # Copyright (c) 2014 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. |
| |
| import ConfigParser |
| import datetime |
| import os |
| |
| from autotest_lib.client.cros.video import bp_image_comparer, \ |
| golden_image_downloader, import_screenshot_capturer, sequence_generator, \ |
| media_player, method_logger, screenshot_file_namer, \ |
| video_screenshot_collector |
| |
| from autotest_lib.client.bin import utils |
| |
| |
| class MediaTestFactory(object): |
| """ |
| |
| Responsible for instantiating objects that are needed by other objects. |
| |
| We chose to use this approach in order to separate an object's creation |
| from its use. Most of the classes built in this library demand their |
| dependencies to be supplied in their constructors. |
| Separating object creation from use enables us to build the rest |
| of the system assuming we will be supplied with that we want. |
| The factory takes care of supplying the needed dependencies. |
| |
| It will also isolate to one place that will be changed if we introduce new |
| object that offer a different 'strategy' of doing things. |
| |
| For example new ways to compare images, we will build logic to decide |
| which one here and then supply the client with an object that will do |
| the comparison the 'correct' way. |
| |
| See make_capture_sequence_generator() below for an example. If test asks for |
| a random capture sequence we will create that, or it wants an interval one |
| we will create that. The client of the capture sequence will just *use* the |
| sequence not caring whether it is random or it is with an interval. |
| |
| """ |
| |
| |
| @method_logger.log |
| def __init__(self, tab, http_server, bin_dir, channel, video_format, |
| video_def): |
| """ |
| Initializes factory. |
| |
| @param tab: object, tab of browser that will load the test page. |
| @param http_server: object, http server to serve the test page |
| @param bin_dir: path to autotest bin directory. This is where autotest |
| will put files that the library code depends on. |
| e.g:Configuration files |
| @param channel: string, The channel of the build this test is running. |
| Configures how granular we want to collect screenshots. |
| See channel_spec.conf. |
| @param video_format: string, format of the video.e.g: mp4 |
| @param video_def: string, definition of video. e.g: 480p |
| |
| Video format and definition will be used to find the path of the video |
| source file stored in the cloud. |
| |
| """ |
| |
| self.tab = tab |
| self.http_server = http_server |
| self.bin_dir = bin_dir |
| |
| self.channel = channel |
| |
| # Configuration file names |
| self.autotest_dir = '/usr/local/autotest/cros/video' |
| self.device_spec_filename = 'device_spec.conf' |
| self.test_constants_filename = 'test_constants.conf' |
| self.video_info_filename = 'video_spec.conf' |
| self.channel_spec_filename = 'channel_spec.conf' |
| |
| # HTML file specs |
| self.html_filename = 'video.html' |
| |
| self.device_under_test = None |
| |
| # Video specifications |
| self.video_name = None |
| self.video_format = video_format |
| self.video_def = video_def |
| self.time_format = "%H:%M:%S" |
| self.video_source_file = None |
| |
| # Test constants |
| self.test_working_dir = None |
| self.local_golden_images_dir = None |
| self.remote_golden_image_root_dir = None |
| |
| # Screenshot capturing specs |
| self.screenshot_image_format = None |
| self.capture_sequence_style = None |
| self.start_capture = None |
| self.stop_capture = None |
| self.samples_per_min = None |
| self.capture_interval = None |
| |
| # Verification specs |
| self.biopic_project_name = None |
| self.biopic_contact_email = None |
| |
| self.parser = None |
| |
| self._load_configuration() |
| |
| |
| def _load_configuration(self): |
| """ |
| Loads all configuration parameters from specified configuration files. |
| |
| """ |
| |
| self.parser = ConfigParser.SafeConfigParser() |
| |
| self.device_under_test = utils.get_current_board() |
| |
| self._load_test_constants() |
| self._load_device_info() |
| self._load_video_info() |
| self._load_channel_specs() |
| |
| |
| @method_logger.log |
| def _load_test_constants(self): |
| """ |
| Reads test constants configuration file and stores parameters. |
| |
| """ |
| self.parser.read(os.path.join(self.autotest_dir, |
| self.test_constants_filename)) |
| |
| # test_constants.conf has a constant section storing conf values |
| section = 'constants' |
| |
| self.test_working_dir = self.parser.get(section, 'working_dir') |
| |
| self.local_golden_images_dir = self.parser.get( |
| section, 'local_golden_images_dir') |
| |
| self.screenshot_image_format = self.parser.get(section, 'image_format') |
| |
| self.remote_golden_image_root_dir = self.parser.get( |
| section, 'remote_golden_image_root_dir') |
| |
| self.video_name = self.parser.get(section, 'video_name') |
| |
| self.media_id = self.parser.get(section, 'video_id') |
| |
| self.time_out_events_s = self.parser.getint(section, |
| 'time_out_events_s') |
| |
| self.time_btwn_polling_s = self.parser.getfloat(section, |
| 'time_btwn_polling_s') |
| |
| self.capture_sequence_style = self.parser.get(section, |
| 'capture_sequence_style') |
| |
| self.biopic_project_name = self.parser.get('biopic', 'project_name') |
| self.biopic_contact_email = self.parser.get('biopic', 'contact_email') |
| |
| |
| @method_logger.log |
| def _load_device_info(self): |
| """ |
| Reads device info configuration file and stores parameters. |
| |
| """ |
| self.parser.read(os.path.join(self.autotest_dir, |
| self.device_spec_filename)) |
| |
| self.screen_height_pixels = self.parser.getint(self.device_under_test, |
| 'screen_height_pixels') |
| |
| self.top_pixels_to_crop = self.parser.getint(self.device_under_test, |
| 'top_pixels_to_crop') |
| |
| self.bottom_pixels_to_crop = self.parser.getint(self.device_under_test, |
| 'bottom_pixels_to_crop') |
| |
| |
| @method_logger.log |
| def _load_video_info(self): |
| """ |
| Reads video info configuration file and stores parameters. |
| |
| """ |
| self.parser.read( os.path.join(self.autotest_dir, |
| self.video_info_filename)) |
| |
| length_str = self.parser.get(self.video_name, 'length') |
| |
| duration = datetime.datetime.strptime(length_str, self.time_format) |
| |
| self.media_length = datetime.timedelta(hours=duration.hour, |
| minutes=duration.minute, |
| seconds=duration.second) |
| |
| http_fullpath = os.path.join(self.bin_dir, self.html_filename) |
| |
| self.media_url = self.http_server.UrlOf(http_fullpath) |
| |
| video_filename = '%s_%s.%s' % (self.video_name, |
| self.video_def, |
| self.video_format) |
| |
| self.video_source_file = os.path.join(self.remote_golden_image_root_dir, |
| self.video_name, |
| self.video_format, |
| self.video_def, |
| video_filename) |
| |
| |
| @method_logger.log |
| def _load_channel_specs(self): |
| """ |
| Reads channel info configuration file and stores parameters. |
| |
| """ |
| self.parser.read(os.path.join(self.autotest_dir, |
| self.channel_spec_filename)) |
| |
| self.samples_per_min = self.parser.getint(self.channel, |
| 'samples_per_min') |
| |
| self.start_capture = datetime.timedelta(seconds=1) |
| |
| duration_in_minutes = self.parser.getfloat(self.channel, |
| 'duration_in_minutes') |
| |
| self.stop_capture = (self.start_capture + |
| datetime.timedelta(minutes=duration_in_minutes)) |
| |
| |
| def make_golden_image_downloader(self): |
| """ |
| @returns a golden image downloader based on configuration data. |
| |
| """ |
| return golden_image_downloader.GoldenImageDownloader( |
| self.test_working_dir, |
| self.remote_golden_image_root_dir, |
| self.video_name, |
| self.video_format, |
| self.video_def, |
| self.device_under_test, |
| self.make_screenshot_file_namer()) |
| |
| |
| def make_capture_sequence_generator(self): |
| """ |
| Create a (time) sequence generator based on configuration data. |
| |
| Create a random sequence generator if 'random' is specified else create |
| an interval one. |
| |
| Note that we expect the client to specify capture_sequence_style. |
| |
| @returns an object that can generate a sequence of timestamps. |
| |
| """ |
| gn = None |
| style = self.capture_sequence_style |
| |
| if style == 'random': |
| gn = sequence_generator.RandomSequenceGenerator( |
| self.start_capture, |
| self.stop_capture, |
| self.samples_per_min) |
| elif style == 'interval': |
| gn = sequence_generator.IntervalSequenceGenerator( |
| self.start_capture, |
| self.stop_capture, |
| self.capture_interval) |
| |
| return gn |
| |
| |
| def make_video_screenshot_collector(self): |
| """ |
| Create an object to coordinate navigating video to specific times and |
| taking screenshots. |
| |
| @returns an object that accepts timestamps as input and takes |
| screenshots of a video at those times. |
| |
| """ |
| player = media_player.VideoPlayer(self.tab, |
| self.media_url, |
| self.video_source_file, |
| self.media_id, |
| self.time_out_events_s, |
| self.time_btwn_polling_s) |
| |
| namer = screenshot_file_namer.ScreenShotFileNamer( |
| self.screenshot_image_format) |
| |
| capturer = import_screenshot_capturer.ImportScreenShotCapturer( |
| self.test_working_dir, |
| self.screen_height_pixels, |
| self.top_pixels_to_crop, |
| self.bottom_pixels_to_crop) |
| |
| |
| return video_screenshot_collector.VideoScreenShotCollector(player, |
| namer, |
| capturer) |
| |
| |
| def make_image_comparer(self): |
| """ |
| Add logic here to support other comparers e.g: Chameleon v2 comparer. |
| |
| @returns an object that can compare two images. |
| """ |
| return bp_image_comparer.BpImageComparer(self.biopic_project_name, |
| self.biopic_contact_email) |