blob: f0235c89b5efa671cad9da05ec16cc2ce1e672d4 [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
Charlie Mooneyc68f9c32015-04-16 15:23:22 -07007import webbrowser
Charlie Mooney08661912015-04-16 09:20:34 -07008
Charlie Mooney3cca6ba2014-11-19 16:15:28 -08009import colorama as color
10
Charlie Mooney08661912015-04-16 09:20:34 -070011import gesture_interpreter
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080012import tests
Charlie Mooneyc68f9c32015-04-16 15:23:22 -070013from webplot import Webplot
Charlie Mooney985cf402015-01-08 15:15:59 -080014from report import Report
Charlie Mooney08661912015-04-16 09:20:34 -070015from touchbot import Touchbot, DeviceSpec
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080016
17
18class TestSuite:
19 """ This class represents a collection of tests and is used to run them
20
21 A TestSuite object will set up a connection to the DUT, robot, etc, and
22 determine which tests can be run. Once the object is instantiated,
23 RunNextTestAndVariation() can be run repeatedly to work your way through
24 the entire suite.
25 """
Charlie Mooney1144c9b2015-04-16 13:59:56 -070026 FIRST_SNAPSHOT_TIMEOUT_S = 60
Charlie Mooneyc68f9c32015-04-16 15:23:22 -070027 WEBPLOT_ADDR = '127.0.0.1'
28 WEBPLOT_PORT = 8080
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080029
Charlie Mooneyd5712b82015-02-23 12:06:59 -080030 def __init__(self, touch_dev, options, args):
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080031 color.init(autoreset=True)
32
33 self.options = options
Charlie Mooneyd5712b82015-02-23 12:06:59 -080034 self.touch_dev = touch_dev
Charlie Mooneyaf9d5122014-12-04 15:15:52 -080035 tests.validator.BaseValidator._device = self.touch_dev
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080036
Charlie Mooneydaaaa242015-02-03 14:20:23 -080037 # Connect to the function generator if the operator says they have one
38 self.fn_generator = None
39 if options.has_fn_gen:
40 self.fn_generator = tests.noise.HP33120A()
41 if not self.fn_generator.IsValid():
42 self.fn_generator = None
43 options.has_fn_gen = False
Charlie Mooney08661912015-04-16 09:20:34 -070044 print color.Fore.RED + 'Error: Unable to connect to function generator!'
45 sys.exit(1)
46
47 # Connect to the robot if the operator says they have it
48 self.robot = self.device_spec = None
49 if options.has_robot:
50 self.robot = Touchbot()
51 if not self.robot.comm:
52 print color.Fore.RED + 'Error: Unable to connect to robot!'
53 sys.exit(1)
54 # Load a copy of the device spec if it already exists or calibrate now
55 device_spec_filename = './%s.p' % options.name
56 if os.path.isfile(device_spec_filename):
57 self.device_spec = DeviceSpec.LoadFromDisk(device_spec_filename)
58 else:
59 print color.Fore.YELLOW + 'No spec found (%s)' % device_spec_filename
60 print (color.Fore.YELLOW + 'Please follow these instructions to '
61 'calibrate for your "%s" device' % options.name)
62 self.device_spec = self.robot.CalibrateDevice(device_spec_filename)
Charlie Mooneydaaaa242015-02-03 14:20:23 -080063
Charlie Mooneyc68f9c32015-04-16 15:23:22 -070064 # Start up the plotter
65 self.plotter = Webplot(TestSuite.WEBPLOT_ADDR, TestSuite.WEBPLOT_PORT,
66 self.touch_dev)
67 self.plotter.start()
68 print color.Fore.YELLOW + 'Plots visible at "%s"' % self.plotter.Url()
69 webbrowser.open(self.plotter.Url())
70
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080071 # Compute the list of tests to run
72 self.tests = tests.generate_test_list(options)
Charlie Mooney73051762015-02-06 11:52:38 -080073 if not self.tests:
74 print color.Fore.RED + 'Warning: No tests selected!'
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080075 self.curr_test = 0
76 self.curr_variation = 0
Charlie Mooneyaf9d5122014-12-04 15:15:52 -080077 self.curr_iteration = 1
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080078
Charlie Mooney985cf402015-01-08 15:15:59 -080079 # Create a new Report that will store all the test Results
80 self.report = Report()
81
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080082 def RunNextTestAndVariation(self):
83 """ Run the next test.
84
85 This function runs the next test/variation combination in the test suite
86 and advances the internal state to the next one automatically. When
87 finished, this function return True if there are more tests to run, and
88 False if the whole test suite is done.
89
90 After a TestSuite is instantiated, this function should be called
91 repeatedly until it returns False to go through all tests, variations,
92 and iterations.
93 """
Charlie Mooney73051762015-02-06 11:52:38 -080094 if self.curr_test >= len(self.tests):
95 return False
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080096 test = self.tests[self.curr_test]
97
98 # Print the header for this new test and variation
99 prompt = test.PromptForVariation(self.curr_variation)
100 print color.Fore.WHITE + '-' * 80
101 print color.Fore.BLUE + test.name
102 print color.Fore.GREEN + prompt
103
Charlie Mooneydaaaa242015-02-03 14:20:23 -0800104 # Start the function generator (if applicable)
105 if self.fn_generator:
106 waveform = test.WaveformForVariation(self.curr_variation)
107 if waveform:
108 self.fn_generator.GenerateFunction(*waveform)
109 else:
110 self.fn_generator.Off()
111
Charlie Mooney08661912015-04-16 09:20:34 -0700112 # Start a new thread to perform the robot gesture (if applicable)
113 robot_thread = None
114 if self.robot:
115 robot_thread = gesture_interpreter.PerformCorrespondingGesture(
116 test, self.curr_variation,
117 self.robot, self.device_spec)
118 if robot_thread:
119 robot_thread.start()
120
Charlie Mooney389e1f32015-03-06 13:33:20 -0800121 # Consume any stray events that happened since the last gesture
122 print 'Waiting for all contacts to leave before recording the gesture...'
123 self.touch_dev.FlushSnapshotBuffer()
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700124 self.plotter.Clear()
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800125
126 # Wait a long time for the first event, then have a much shorter
127 # timeout on subsequent incoming events
Charlie Mooney389e1f32015-03-06 13:33:20 -0800128 snapshots = []
Charlie Mooney389e1f32015-03-06 13:33:20 -0800129 print 'Waiting for 1st snapshot...',
Charlie Mooney1144c9b2015-04-16 13:59:56 -0700130 snapshot = None
131 if not robot_thread or not robot_thread.isAlive():
132 snapshot = self.touch_dev.NextSnapshot(TestSuite.FIRST_SNAPSHOT_TIMEOUT_S)
133 if not snapshot:
134 print '\rNo MT snapshots collected before timeout!'
Charlie Mooney08661912015-04-16 09:20:34 -0700135 while snapshot or (robot_thread and robot_thread.isAlive()):
136 if snapshot:
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700137 self.plotter.AddSnapshot(snapshot)
Charlie Mooney08661912015-04-16 09:20:34 -0700138 snapshots.append(snapshot)
139 print '\rCollected %d MT snapshots' % len(snapshots),
Charlie Mooney389e1f32015-03-06 13:33:20 -0800140 snapshot = self.touch_dev.NextSnapshot(test.timeout)
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800141 print
Charlie Mooney173396c2015-03-19 15:25:41 -0700142
Charlie Mooney08661912015-04-16 09:20:34 -0700143 # Cleanup the robot gesture thread, if it existed
144 if robot_thread:
145 robot_thread.join()
146
Charlie Mooney173396c2015-03-19 15:25:41 -0700147 # Prompt to see if the gesture was performed acceptibly by the user before
148 # continuing. If the user is unsatisfied abort before advancing on to the
149 # next test/variation, allowing the test suite to retry this variation.
150 if not self.options.has_robot:
151 CONFIRMATION_PROMPT = 'Accept Gesture? ([y]/n) '
152 yorn = raw_input(CONFIRMATION_PROMPT).lower()
153 while yorn not in ['y', 'n', 'yes', 'no', '']:
154 print color.Fore.RED + 'Error: please enter "y" or "n" only'
155 yorn = raw_input(CONFIRMATION_PROMPT).lower()
Charlie Mooney173396c2015-03-19 15:25:41 -0700156 if yorn in ['n', 'no']:
157 print color.Fore.RED + 'Operator rejected the gesture. Please retry...'
158 return True
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700159
160 plot_image_png = self.plotter.Save(wait_for_image=True)
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800161
162 # Run the validators on these events
Charlie Mooneyaf9d5122014-12-04 15:15:52 -0800163 results = test.RunAllValidators(snapshots)
Charlie Mooney985cf402015-01-08 15:15:59 -0800164
Charlie Mooney5e9a10f2015-01-13 14:54:27 -0800165 # Bundle the Validator results with some details of which gesture was used
166 # during the test for easier debugging.
Charlie Mooney2c270d12015-04-16 13:38:03 -0700167 test_result = tests.TestResult(test.variations[self.curr_variation],
168 results, prompt, plot_image_png)
Charlie Mooney5e9a10f2015-01-13 14:54:27 -0800169
Charlie Mooney985cf402015-01-08 15:15:59 -0800170 # Add the results into our report (And have it print them to stdout, too)
Charlie Mooney5e9a10f2015-01-13 14:54:27 -0800171 self.report.AddTestResult(test_result, verbose=True)
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800172
173 # Advance the test suite to the next test and variation and return an
174 # indicator as to whether this was the last thing to do or not.
175 next_test, next_var = self._Advance()
176 return (next_test is not None)
177
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700178 def StopPlotter(self):
179 self.plotter.Quit()
180
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800181 def _Advance(self):
182 """ Move on to the next test/variation pair
183
184 This function increments all the interal counters, according to the
185 number of tests, their variations, and the selected number of iterations
186 and returns the test object and the variation number that should be
187 done next.
188
189 When the TestSuite is complete, this function will return None, None
190 otherwise it will return the next Test object and the variation number
191 the test suite is on.
192 """
193 if self.curr_test >= len(self.tests):
194 return None, None
195 test = self.tests[self.curr_test]
196
197 if self.curr_variation >= len(test.variations):
198 self.curr_test += 1
199 self.curr_variation = 0
200 self.curr_iteration = 0
201 return self._Advance()
202
203 if self.curr_iteration >= self.options.num_iterations:
204 self.curr_variation += 1
205 self.curr_iteration = 0
206 return self._Advance()
207
208 self.curr_iteration += 1
209 return test, self.curr_variation