blob: 1870b3f0dec7698ad10b2a8bdf701aaac3462208 [file] [log] [blame]
Charlie Mooney3cca6ba2014-11-19 16:15:28 -08001# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Charlie Mooney08661912015-04-16 09:20:34 -07005import os
6import sys
7
Charlie Mooney3cca6ba2014-11-19 16:15:28 -08008import colorama as color
9
Charlie Mooney08661912015-04-16 09:20:34 -070010import gesture_interpreter
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080011import tests
Charlie Mooney86b5cf12014-12-04 09:34:09 -080012from plotter import TouchPlotter
Charlie Mooney985cf402015-01-08 15:15:59 -080013from report import Report
Charlie Mooney08661912015-04-16 09:20:34 -070014from touchbot import Touchbot, DeviceSpec
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080015
16
17class TestSuite:
18 """ This class represents a collection of tests and is used to run them
19
20 A TestSuite object will set up a connection to the DUT, robot, etc, and
21 determine which tests can be run. Once the object is instantiated,
22 RunNextTestAndVariation() can be run repeatedly to work your way through
23 the entire suite.
24 """
25
Charlie Mooney389e1f32015-03-06 13:33:20 -080026 NO_SNAPSHOT_DETECTED_TIMEOUT_MANUAL_S = 60
27 NO_SNAPSHOT_DETECTED_TIMEOUT_ROBOT_S = 5
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080028
Charlie Mooneyd5712b82015-02-23 12:06:59 -080029 def __init__(self, touch_dev, options, args):
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080030 color.init(autoreset=True)
31
32 self.options = options
Charlie Mooneyd5712b82015-02-23 12:06:59 -080033 self.touch_dev = touch_dev
Charlie Mooneyaf9d5122014-12-04 15:15:52 -080034 tests.validator.BaseValidator._device = self.touch_dev
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080035
Charlie Mooneydaaaa242015-02-03 14:20:23 -080036 # Connect to the function generator if the operator says they have one
37 self.fn_generator = None
38 if options.has_fn_gen:
39 self.fn_generator = tests.noise.HP33120A()
40 if not self.fn_generator.IsValid():
41 self.fn_generator = None
42 options.has_fn_gen = False
Charlie Mooney08661912015-04-16 09:20:34 -070043 print color.Fore.RED + 'Error: Unable to connect to function generator!'
44 sys.exit(1)
45
46 # Connect to the robot if the operator says they have it
47 self.robot = self.device_spec = None
48 if options.has_robot:
49 self.robot = Touchbot()
50 if not self.robot.comm:
51 print color.Fore.RED + 'Error: Unable to connect to robot!'
52 sys.exit(1)
53 # Load a copy of the device spec if it already exists or calibrate now
54 device_spec_filename = './%s.p' % options.name
55 if os.path.isfile(device_spec_filename):
56 self.device_spec = DeviceSpec.LoadFromDisk(device_spec_filename)
57 else:
58 print color.Fore.YELLOW + 'No spec found (%s)' % device_spec_filename
59 print (color.Fore.YELLOW + 'Please follow these instructions to '
60 'calibrate for your "%s" device' % options.name)
61 self.device_spec = self.robot.CalibrateDevice(device_spec_filename)
Charlie Mooneydaaaa242015-02-03 14:20:23 -080062
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080063 # Compute the list of tests to run
64 self.tests = tests.generate_test_list(options)
Charlie Mooney73051762015-02-06 11:52:38 -080065 if not self.tests:
66 print color.Fore.RED + 'Warning: No tests selected!'
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080067 self.curr_test = 0
68 self.curr_variation = 0
Charlie Mooneyaf9d5122014-12-04 15:15:52 -080069 self.curr_iteration = 1
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080070
Charlie Mooney389e1f32015-03-06 13:33:20 -080071 self.no_snapshot_timeout = TestSuite.NO_SNAPSHOT_DETECTED_TIMEOUT_MANUAL_S
Charlie Mooneyda8e13f2015-02-24 11:32:05 -080072 if options.has_robot:
Charlie Mooney389e1f32015-03-06 13:33:20 -080073 self.no_snapshot_timeout = TestSuite.NO_SNAPSHOT_DETECTED_TIMEOUT_ROBOT_S
Charlie Mooneyda8e13f2015-02-24 11:32:05 -080074
Charlie Mooney985cf402015-01-08 15:15:59 -080075 # Create a new Report that will store all the test Results
76 self.report = Report()
77
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080078 def RunNextTestAndVariation(self):
79 """ Run the next test.
80
81 This function runs the next test/variation combination in the test suite
82 and advances the internal state to the next one automatically. When
83 finished, this function return True if there are more tests to run, and
84 False if the whole test suite is done.
85
86 After a TestSuite is instantiated, this function should be called
87 repeatedly until it returns False to go through all tests, variations,
88 and iterations.
89 """
Charlie Mooney73051762015-02-06 11:52:38 -080090 if self.curr_test >= len(self.tests):
91 return False
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080092 test = self.tests[self.curr_test]
93
94 # Print the header for this new test and variation
95 prompt = test.PromptForVariation(self.curr_variation)
96 print color.Fore.WHITE + '-' * 80
97 print color.Fore.BLUE + test.name
98 print color.Fore.GREEN + prompt
99
Charlie Mooneydaaaa242015-02-03 14:20:23 -0800100 # Start the function generator (if applicable)
101 if self.fn_generator:
102 waveform = test.WaveformForVariation(self.curr_variation)
103 if waveform:
104 self.fn_generator.GenerateFunction(*waveform)
105 else:
106 self.fn_generator.Off()
107
Charlie Mooney08661912015-04-16 09:20:34 -0700108 # Start a new thread to perform the robot gesture (if applicable)
109 robot_thread = None
110 if self.robot:
111 robot_thread = gesture_interpreter.PerformCorrespondingGesture(
112 test, self.curr_variation,
113 self.robot, self.device_spec)
114 if robot_thread:
115 robot_thread.start()
116
Charlie Mooney389e1f32015-03-06 13:33:20 -0800117 # Consume any stray events that happened since the last gesture
118 print 'Waiting for all contacts to leave before recording the gesture...'
119 self.touch_dev.FlushSnapshotBuffer()
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800120
121 # Wait a long time for the first event, then have a much shorter
122 # timeout on subsequent incoming events
Charlie Mooney389e1f32015-03-06 13:33:20 -0800123 snapshots = []
Charlie Mooney50171442015-03-17 15:24:51 -0700124 plotter = TouchPlotter(self.touch_dev, window_title=prompt)
Charlie Mooney389e1f32015-03-06 13:33:20 -0800125 print 'Waiting for 1st snapshot...',
126 snapshot = self.touch_dev.NextSnapshot(self.no_snapshot_timeout)
127 if not snapshot:
128 print ('\rNo MT snapshots collected before timeout (%d seconds)!' %
129 self.no_snapshot_timeout),
Charlie Mooney08661912015-04-16 09:20:34 -0700130 while snapshot or (robot_thread and robot_thread.isAlive()):
131 if snapshot:
132 plotter.add_snapshot(snapshot)
133 snapshots.append(snapshot)
134 print '\rCollected %d MT snapshots' % len(snapshots),
Charlie Mooney389e1f32015-03-06 13:33:20 -0800135 snapshot = self.touch_dev.NextSnapshot(test.timeout)
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800136 print
Charlie Mooney173396c2015-03-19 15:25:41 -0700137
Charlie Mooney08661912015-04-16 09:20:34 -0700138 # Cleanup the robot gesture thread, if it existed
139 if robot_thread:
140 robot_thread.join()
141
Charlie Mooney173396c2015-03-19 15:25:41 -0700142 # Prompt to see if the gesture was performed acceptibly by the user before
143 # continuing. If the user is unsatisfied abort before advancing on to the
144 # next test/variation, allowing the test suite to retry this variation.
145 if not self.options.has_robot:
146 CONFIRMATION_PROMPT = 'Accept Gesture? ([y]/n) '
147 yorn = raw_input(CONFIRMATION_PROMPT).lower()
148 while yorn not in ['y', 'n', 'yes', 'no', '']:
149 print color.Fore.RED + 'Error: please enter "y" or "n" only'
150 yorn = raw_input(CONFIRMATION_PROMPT).lower()
151 plot_image_png = plotter.end()
152 if yorn in ['n', 'no']:
153 print color.Fore.RED + 'Operator rejected the gesture. Please retry...'
154 return True
155 else:
156 plot_image_png = plotter.end()
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800157
158 # Run the validators on these events
Charlie Mooneyaf9d5122014-12-04 15:15:52 -0800159 results = test.RunAllValidators(snapshots)
Charlie Mooney985cf402015-01-08 15:15:59 -0800160
Charlie Mooney5e9a10f2015-01-13 14:54:27 -0800161 # Bundle the Validator results with some details of which gesture was used
162 # during the test for easier debugging.
Charlie Mooney2c270d12015-04-16 13:38:03 -0700163 test_result = tests.TestResult(test.variations[self.curr_variation],
164 results, prompt, plot_image_png)
Charlie Mooney5e9a10f2015-01-13 14:54:27 -0800165
Charlie Mooney985cf402015-01-08 15:15:59 -0800166 # Add the results into our report (And have it print them to stdout, too)
Charlie Mooney5e9a10f2015-01-13 14:54:27 -0800167 self.report.AddTestResult(test_result, verbose=True)
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800168
169 # Advance the test suite to the next test and variation and return an
170 # indicator as to whether this was the last thing to do or not.
171 next_test, next_var = self._Advance()
172 return (next_test is not None)
173
174 def _Advance(self):
175 """ Move on to the next test/variation pair
176
177 This function increments all the interal counters, according to the
178 number of tests, their variations, and the selected number of iterations
179 and returns the test object and the variation number that should be
180 done next.
181
182 When the TestSuite is complete, this function will return None, None
183 otherwise it will return the next Test object and the variation number
184 the test suite is on.
185 """
186 if self.curr_test >= len(self.tests):
187 return None, None
188 test = self.tests[self.curr_test]
189
190 if self.curr_variation >= len(test.variations):
191 self.curr_test += 1
192 self.curr_variation = 0
193 self.curr_iteration = 0
194 return self._Advance()
195
196 if self.curr_iteration >= self.options.num_iterations:
197 self.curr_variation += 1
198 self.curr_iteration = 0
199 return self._Advance()
200
201 self.curr_iteration += 1
202 return test, self.curr_variation