blob: 107a9b4c1e1e30e5cafcd74857421fc3e3f2a381 [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
Charlie Mooney4291c4e2015-06-15 10:15:30 -070080 self.report = Report(title=self.options.title,
81 test_version=self.options.test_version)
Charlie Mooney8f07d832015-06-15 15:29:47 -070082 self.report.warnings = self.touch_dev.warnings
Charlie Mooney985cf402015-01-08 15:15:59 -080083
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080084 def RunNextTestAndVariation(self):
85 """ Run the next test.
86
87 This function runs the next test/variation combination in the test suite
88 and advances the internal state to the next one automatically. When
89 finished, this function return True if there are more tests to run, and
90 False if the whole test suite is done.
91
92 After a TestSuite is instantiated, this function should be called
93 repeatedly until it returns False to go through all tests, variations,
94 and iterations.
95 """
Charlie Mooney73051762015-02-06 11:52:38 -080096 if self.curr_test >= len(self.tests):
97 return False
Charlie Mooney3cca6ba2014-11-19 16:15:28 -080098 test = self.tests[self.curr_test]
99
100 # Print the header for this new test and variation
101 prompt = test.PromptForVariation(self.curr_variation)
102 print color.Fore.WHITE + '-' * 80
103 print color.Fore.BLUE + test.name
104 print color.Fore.GREEN + prompt
105
Charlie Mooneydaaaa242015-02-03 14:20:23 -0800106 # Start the function generator (if applicable)
107 if self.fn_generator:
108 waveform = test.WaveformForVariation(self.curr_variation)
109 if waveform:
110 self.fn_generator.GenerateFunction(*waveform)
111 else:
112 self.fn_generator.Off()
113
Charlie Mooney08661912015-04-16 09:20:34 -0700114 # Start a new thread to perform the robot gesture (if applicable)
115 robot_thread = None
116 if self.robot:
117 robot_thread = gesture_interpreter.PerformCorrespondingGesture(
118 test, self.curr_variation,
119 self.robot, self.device_spec)
120 if robot_thread:
121 robot_thread.start()
Charlie Mooneyca3fc202015-05-21 11:16:53 -0700122 else:
123 next_test, next_var = self._Advance()
124 return (next_test is not None)
Charlie Mooney08661912015-04-16 09:20:34 -0700125
Charlie Mooney389e1f32015-03-06 13:33:20 -0800126 # Consume any stray events that happened since the last gesture
127 print 'Waiting for all contacts to leave before recording the gesture...'
128 self.touch_dev.FlushSnapshotBuffer()
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700129 self.plotter.Clear()
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800130
131 # Wait a long time for the first event, then have a much shorter
132 # timeout on subsequent incoming events
Charlie Mooney389e1f32015-03-06 13:33:20 -0800133 snapshots = []
Charlie Mooney389e1f32015-03-06 13:33:20 -0800134 print 'Waiting for 1st snapshot...',
Charlie Mooney1144c9b2015-04-16 13:59:56 -0700135 snapshot = None
136 if not robot_thread or not robot_thread.isAlive():
137 snapshot = self.touch_dev.NextSnapshot(TestSuite.FIRST_SNAPSHOT_TIMEOUT_S)
138 if not snapshot:
139 print '\rNo MT snapshots collected before timeout!'
Charlie Mooney08661912015-04-16 09:20:34 -0700140 while snapshot or (robot_thread and robot_thread.isAlive()):
141 if snapshot:
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700142 self.plotter.AddSnapshot(snapshot)
Charlie Mooney08661912015-04-16 09:20:34 -0700143 snapshots.append(snapshot)
144 print '\rCollected %d MT snapshots' % len(snapshots),
Charlie Mooney389e1f32015-03-06 13:33:20 -0800145 snapshot = self.touch_dev.NextSnapshot(test.timeout)
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800146 print
Charlie Mooney173396c2015-03-19 15:25:41 -0700147
Charlie Mooney08661912015-04-16 09:20:34 -0700148 # Cleanup the robot gesture thread, if it existed
149 if robot_thread:
150 robot_thread.join()
151
Charlie Mooney76c871f2015-05-21 12:01:52 -0700152 # Run the validators on these events
153 results = test.RunAllValidators(snapshots)
154 for result in results:
155 print result
156
Charlie Mooney173396c2015-03-19 15:25:41 -0700157 # Prompt to see if the gesture was performed acceptibly by the user before
158 # continuing. If the user is unsatisfied abort before advancing on to the
159 # next test/variation, allowing the test suite to retry this variation.
160 if not self.options.has_robot:
161 CONFIRMATION_PROMPT = 'Accept Gesture? ([y]/n) '
162 yorn = raw_input(CONFIRMATION_PROMPT).lower()
163 while yorn not in ['y', 'n', 'yes', 'no', '']:
164 print color.Fore.RED + 'Error: please enter "y" or "n" only'
165 yorn = raw_input(CONFIRMATION_PROMPT).lower()
Charlie Mooney173396c2015-03-19 15:25:41 -0700166 if yorn in ['n', 'no']:
167 print color.Fore.RED + 'Operator rejected the gesture. Please retry...'
168 return True
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700169
Charlie Mooney76c871f2015-05-21 12:01:52 -0700170 # Save the image displayed by the plotter
Charlie Mooney990d0ea2015-07-28 09:57:59 -0700171 plot_image_png = self.plotter.Save()
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800172
Charlie Mooney5e9a10f2015-01-13 14:54:27 -0800173 # Bundle the Validator results with some details of which gesture was used
174 # during the test for easier debugging.
Charlie Mooney2c270d12015-04-16 13:38:03 -0700175 test_result = tests.TestResult(test.variations[self.curr_variation],
176 results, prompt, plot_image_png)
Charlie Mooney5e9a10f2015-01-13 14:54:27 -0800177
Charlie Mooney985cf402015-01-08 15:15:59 -0800178 # Add the results into our report (And have it print them to stdout, too)
Charlie Mooney76c871f2015-05-21 12:01:52 -0700179 self.report.AddTestResult(test_result)
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800180
181 # Advance the test suite to the next test and variation and return an
182 # indicator as to whether this was the last thing to do or not.
183 next_test, next_var = self._Advance()
184 return (next_test is not None)
185
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700186 def StopPlotter(self):
187 self.plotter.Quit()
188
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800189 def _Advance(self):
190 """ Move on to the next test/variation pair
191
192 This function increments all the interal counters, according to the
193 number of tests, their variations, and the selected number of iterations
194 and returns the test object and the variation number that should be
195 done next.
196
197 When the TestSuite is complete, this function will return None, None
198 otherwise it will return the next Test object and the variation number
199 the test suite is on.
200 """
201 if self.curr_test >= len(self.tests):
202 return None, None
203 test = self.tests[self.curr_test]
204
205 if self.curr_variation >= len(test.variations):
206 self.curr_test += 1
207 self.curr_variation = 0
208 self.curr_iteration = 0
209 return self._Advance()
210
211 if self.curr_iteration >= self.options.num_iterations:
212 self.curr_variation += 1
213 self.curr_iteration = 0
214 return self._Advance()
215
216 self.curr_iteration += 1
217 return test, self.curr_variation