blob: fa6dc005980c9c0a82ede7156248d8f733f380b3 [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 """
Charlie Mooney1144c9b2015-04-16 13:59:56 -070025 FIRST_SNAPSHOT_TIMEOUT_S = 60
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080026
Charlie Mooneyd5712b82015-02-23 12:06:59 -080027 def __init__(self, touch_dev, options, args):
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080028 color.init(autoreset=True)
29
30 self.options = options
Charlie Mooneyd5712b82015-02-23 12:06:59 -080031 self.touch_dev = touch_dev
Charlie Mooneyaf9d5122014-12-04 15:15:52 -080032 tests.validator.BaseValidator._device = self.touch_dev
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080033
Charlie Mooneydaaaa242015-02-03 14:20:23 -080034 # Connect to the function generator if the operator says they have one
35 self.fn_generator = None
36 if options.has_fn_gen:
37 self.fn_generator = tests.noise.HP33120A()
38 if not self.fn_generator.IsValid():
39 self.fn_generator = None
40 options.has_fn_gen = False
Charlie Mooney08661912015-04-16 09:20:34 -070041 print color.Fore.RED + 'Error: Unable to connect to function generator!'
42 sys.exit(1)
43
44 # Connect to the robot if the operator says they have it
45 self.robot = self.device_spec = None
46 if options.has_robot:
47 self.robot = Touchbot()
48 if not self.robot.comm:
49 print color.Fore.RED + 'Error: Unable to connect to robot!'
50 sys.exit(1)
51 # Load a copy of the device spec if it already exists or calibrate now
52 device_spec_filename = './%s.p' % options.name
53 if os.path.isfile(device_spec_filename):
54 self.device_spec = DeviceSpec.LoadFromDisk(device_spec_filename)
55 else:
56 print color.Fore.YELLOW + 'No spec found (%s)' % device_spec_filename
57 print (color.Fore.YELLOW + 'Please follow these instructions to '
58 'calibrate for your "%s" device' % options.name)
59 self.device_spec = self.robot.CalibrateDevice(device_spec_filename)
Charlie Mooneydaaaa242015-02-03 14:20:23 -080060
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080061 # Compute the list of tests to run
62 self.tests = tests.generate_test_list(options)
Charlie Mooney73051762015-02-06 11:52:38 -080063 if not self.tests:
64 print color.Fore.RED + 'Warning: No tests selected!'
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080065 self.curr_test = 0
66 self.curr_variation = 0
Charlie Mooneyaf9d5122014-12-04 15:15:52 -080067 self.curr_iteration = 1
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080068
Charlie Mooney985cf402015-01-08 15:15:59 -080069 # Create a new Report that will store all the test Results
70 self.report = Report()
71
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080072 def RunNextTestAndVariation(self):
73 """ Run the next test.
74
75 This function runs the next test/variation combination in the test suite
76 and advances the internal state to the next one automatically. When
77 finished, this function return True if there are more tests to run, and
78 False if the whole test suite is done.
79
80 After a TestSuite is instantiated, this function should be called
81 repeatedly until it returns False to go through all tests, variations,
82 and iterations.
83 """
Charlie Mooney73051762015-02-06 11:52:38 -080084 if self.curr_test >= len(self.tests):
85 return False
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080086 test = self.tests[self.curr_test]
87
88 # Print the header for this new test and variation
89 prompt = test.PromptForVariation(self.curr_variation)
90 print color.Fore.WHITE + '-' * 80
91 print color.Fore.BLUE + test.name
92 print color.Fore.GREEN + prompt
93
Charlie Mooneydaaaa242015-02-03 14:20:23 -080094 # Start the function generator (if applicable)
95 if self.fn_generator:
96 waveform = test.WaveformForVariation(self.curr_variation)
97 if waveform:
98 self.fn_generator.GenerateFunction(*waveform)
99 else:
100 self.fn_generator.Off()
101
Charlie Mooney08661912015-04-16 09:20:34 -0700102 # Start a new thread to perform the robot gesture (if applicable)
103 robot_thread = None
104 if self.robot:
105 robot_thread = gesture_interpreter.PerformCorrespondingGesture(
106 test, self.curr_variation,
107 self.robot, self.device_spec)
108 if robot_thread:
109 robot_thread.start()
110
Charlie Mooney389e1f32015-03-06 13:33:20 -0800111 # Consume any stray events that happened since the last gesture
112 print 'Waiting for all contacts to leave before recording the gesture...'
113 self.touch_dev.FlushSnapshotBuffer()
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800114
115 # Wait a long time for the first event, then have a much shorter
116 # timeout on subsequent incoming events
Charlie Mooney389e1f32015-03-06 13:33:20 -0800117 snapshots = []
Charlie Mooney50171442015-03-17 15:24:51 -0700118 plotter = TouchPlotter(self.touch_dev, window_title=prompt)
Charlie Mooney389e1f32015-03-06 13:33:20 -0800119 print 'Waiting for 1st snapshot...',
Charlie Mooney1144c9b2015-04-16 13:59:56 -0700120 snapshot = None
121 if not robot_thread or not robot_thread.isAlive():
122 snapshot = self.touch_dev.NextSnapshot(TestSuite.FIRST_SNAPSHOT_TIMEOUT_S)
123 if not snapshot:
124 print '\rNo MT snapshots collected before timeout!'
Charlie Mooney08661912015-04-16 09:20:34 -0700125 while snapshot or (robot_thread and robot_thread.isAlive()):
126 if snapshot:
127 plotter.add_snapshot(snapshot)
128 snapshots.append(snapshot)
129 print '\rCollected %d MT snapshots' % len(snapshots),
Charlie Mooney389e1f32015-03-06 13:33:20 -0800130 snapshot = self.touch_dev.NextSnapshot(test.timeout)
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800131 print
Charlie Mooney173396c2015-03-19 15:25:41 -0700132
Charlie Mooney08661912015-04-16 09:20:34 -0700133 # Cleanup the robot gesture thread, if it existed
134 if robot_thread:
135 robot_thread.join()
136
Charlie Mooney173396c2015-03-19 15:25:41 -0700137 # Prompt to see if the gesture was performed acceptibly by the user before
138 # continuing. If the user is unsatisfied abort before advancing on to the
139 # next test/variation, allowing the test suite to retry this variation.
140 if not self.options.has_robot:
141 CONFIRMATION_PROMPT = 'Accept Gesture? ([y]/n) '
142 yorn = raw_input(CONFIRMATION_PROMPT).lower()
143 while yorn not in ['y', 'n', 'yes', 'no', '']:
144 print color.Fore.RED + 'Error: please enter "y" or "n" only'
145 yorn = raw_input(CONFIRMATION_PROMPT).lower()
146 plot_image_png = plotter.end()
147 if yorn in ['n', 'no']:
148 print color.Fore.RED + 'Operator rejected the gesture. Please retry...'
149 return True
150 else:
151 plot_image_png = plotter.end()
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800152
153 # Run the validators on these events
Charlie Mooneyaf9d5122014-12-04 15:15:52 -0800154 results = test.RunAllValidators(snapshots)
Charlie Mooney985cf402015-01-08 15:15:59 -0800155
Charlie Mooney5e9a10f2015-01-13 14:54:27 -0800156 # Bundle the Validator results with some details of which gesture was used
157 # during the test for easier debugging.
Charlie Mooney2c270d12015-04-16 13:38:03 -0700158 test_result = tests.TestResult(test.variations[self.curr_variation],
159 results, prompt, plot_image_png)
Charlie Mooney5e9a10f2015-01-13 14:54:27 -0800160
Charlie Mooney985cf402015-01-08 15:15:59 -0800161 # Add the results into our report (And have it print them to stdout, too)
Charlie Mooney5e9a10f2015-01-13 14:54:27 -0800162 self.report.AddTestResult(test_result, verbose=True)
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800163
164 # Advance the test suite to the next test and variation and return an
165 # indicator as to whether this was the last thing to do or not.
166 next_test, next_var = self._Advance()
167 return (next_test is not None)
168
169 def _Advance(self):
170 """ Move on to the next test/variation pair
171
172 This function increments all the interal counters, according to the
173 number of tests, their variations, and the selected number of iterations
174 and returns the test object and the variation number that should be
175 done next.
176
177 When the TestSuite is complete, this function will return None, None
178 otherwise it will return the next Test object and the variation number
179 the test suite is on.
180 """
181 if self.curr_test >= len(self.tests):
182 return None, None
183 test = self.tests[self.curr_test]
184
185 if self.curr_variation >= len(test.variations):
186 self.curr_test += 1
187 self.curr_variation = 0
188 self.curr_iteration = 0
189 return self._Advance()
190
191 if self.curr_iteration >= self.options.num_iterations:
192 self.curr_variation += 1
193 self.curr_iteration = 0
194 return self._Advance()
195
196 self.curr_iteration += 1
197 return test, self.curr_variation