blob: 8d83425c37aef47f13de3ca5300ca8cc0238facb [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()
Charlie Mooney3d00bdc2015-05-08 12:58:35 -070081 self.report.title = self.options.title
Charlie Mooney985cf402015-01-08 15:15:59 -080082
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080083 def RunNextTestAndVariation(self):
84 """ Run the next test.
85
86 This function runs the next test/variation combination in the test suite
87 and advances the internal state to the next one automatically. When
88 finished, this function return True if there are more tests to run, and
89 False if the whole test suite is done.
90
91 After a TestSuite is instantiated, this function should be called
92 repeatedly until it returns False to go through all tests, variations,
93 and iterations.
94 """
Charlie Mooney73051762015-02-06 11:52:38 -080095 if self.curr_test >= len(self.tests):
96 return False
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080097 test = self.tests[self.curr_test]
98
99 # Print the header for this new test and variation
100 prompt = test.PromptForVariation(self.curr_variation)
101 print color.Fore.WHITE + '-' * 80
102 print color.Fore.BLUE + test.name
103 print color.Fore.GREEN + prompt
104
Charlie Mooneydaaaa242015-02-03 14:20:23 -0800105 # Start the function generator (if applicable)
106 if self.fn_generator:
107 waveform = test.WaveformForVariation(self.curr_variation)
108 if waveform:
109 self.fn_generator.GenerateFunction(*waveform)
110 else:
111 self.fn_generator.Off()
112
Charlie Mooney08661912015-04-16 09:20:34 -0700113 # Start a new thread to perform the robot gesture (if applicable)
114 robot_thread = None
115 if self.robot:
116 robot_thread = gesture_interpreter.PerformCorrespondingGesture(
117 test, self.curr_variation,
118 self.robot, self.device_spec)
119 if robot_thread:
120 robot_thread.start()
Charlie Mooneyca3fc202015-05-21 11:16:53 -0700121 else:
122 next_test, next_var = self._Advance()
123 return (next_test is not None)
Charlie Mooney08661912015-04-16 09:20:34 -0700124
Charlie Mooney389e1f32015-03-06 13:33:20 -0800125 # Consume any stray events that happened since the last gesture
126 print 'Waiting for all contacts to leave before recording the gesture...'
127 self.touch_dev.FlushSnapshotBuffer()
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700128 self.plotter.Clear()
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800129
130 # Wait a long time for the first event, then have a much shorter
131 # timeout on subsequent incoming events
Charlie Mooney389e1f32015-03-06 13:33:20 -0800132 snapshots = []
Charlie Mooney389e1f32015-03-06 13:33:20 -0800133 print 'Waiting for 1st snapshot...',
Charlie Mooney1144c9b2015-04-16 13:59:56 -0700134 snapshot = None
135 if not robot_thread or not robot_thread.isAlive():
136 snapshot = self.touch_dev.NextSnapshot(TestSuite.FIRST_SNAPSHOT_TIMEOUT_S)
137 if not snapshot:
138 print '\rNo MT snapshots collected before timeout!'
Charlie Mooney08661912015-04-16 09:20:34 -0700139 while snapshot or (robot_thread and robot_thread.isAlive()):
140 if snapshot:
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700141 self.plotter.AddSnapshot(snapshot)
Charlie Mooney08661912015-04-16 09:20:34 -0700142 snapshots.append(snapshot)
143 print '\rCollected %d MT snapshots' % len(snapshots),
Charlie Mooney389e1f32015-03-06 13:33:20 -0800144 snapshot = self.touch_dev.NextSnapshot(test.timeout)
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800145 print
Charlie Mooney173396c2015-03-19 15:25:41 -0700146
Charlie Mooney08661912015-04-16 09:20:34 -0700147 # Cleanup the robot gesture thread, if it existed
148 if robot_thread:
149 robot_thread.join()
150
Charlie Mooney76c871f2015-05-21 12:01:52 -0700151 # Run the validators on these events
152 results = test.RunAllValidators(snapshots)
153 for result in results:
154 print result
155
Charlie Mooney173396c2015-03-19 15:25:41 -0700156 # Prompt to see if the gesture was performed acceptibly by the user before
157 # continuing. If the user is unsatisfied abort before advancing on to the
158 # next test/variation, allowing the test suite to retry this variation.
159 if not self.options.has_robot:
160 CONFIRMATION_PROMPT = 'Accept Gesture? ([y]/n) '
161 yorn = raw_input(CONFIRMATION_PROMPT).lower()
162 while yorn not in ['y', 'n', 'yes', 'no', '']:
163 print color.Fore.RED + 'Error: please enter "y" or "n" only'
164 yorn = raw_input(CONFIRMATION_PROMPT).lower()
Charlie Mooney173396c2015-03-19 15:25:41 -0700165 if yorn in ['n', 'no']:
166 print color.Fore.RED + 'Operator rejected the gesture. Please retry...'
167 return True
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700168
Charlie Mooney76c871f2015-05-21 12:01:52 -0700169 # Save the image displayed by the plotter
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700170 plot_image_png = self.plotter.Save(wait_for_image=True)
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800171
Charlie Mooney5e9a10f2015-01-13 14:54:27 -0800172 # Bundle the Validator results with some details of which gesture was used
173 # during the test for easier debugging.
Charlie Mooney2c270d12015-04-16 13:38:03 -0700174 test_result = tests.TestResult(test.variations[self.curr_variation],
175 results, prompt, plot_image_png)
Charlie Mooney5e9a10f2015-01-13 14:54:27 -0800176
Charlie Mooney985cf402015-01-08 15:15:59 -0800177 # Add the results into our report (And have it print them to stdout, too)
Charlie Mooney76c871f2015-05-21 12:01:52 -0700178 self.report.AddTestResult(test_result)
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800179
180 # Advance the test suite to the next test and variation and return an
181 # indicator as to whether this was the last thing to do or not.
182 next_test, next_var = self._Advance()
183 return (next_test is not None)
184
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700185 def StopPlotter(self):
186 self.plotter.Quit()
187
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800188 def _Advance(self):
189 """ Move on to the next test/variation pair
190
191 This function increments all the interal counters, according to the
192 number of tests, their variations, and the selected number of iterations
193 and returns the test object and the variation number that should be
194 done next.
195
196 When the TestSuite is complete, this function will return None, None
197 otherwise it will return the next Test object and the variation number
198 the test suite is on.
199 """
200 if self.curr_test >= len(self.tests):
201 return None, None
202 test = self.tests[self.curr_test]
203
204 if self.curr_variation >= len(test.variations):
205 self.curr_test += 1
206 self.curr_variation = 0
207 self.curr_iteration = 0
208 return self._Advance()
209
210 if self.curr_iteration >= self.options.num_iterations:
211 self.curr_variation += 1
212 self.curr_iteration = 0
213 return self._Advance()
214
215 self.curr_iteration += 1
216 return test, self.curr_variation