blob: 2af98624aac0613cc91be323d39f8393f82785cb [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()
121
Charlie Mooney389e1f32015-03-06 13:33:20 -0800122 # Consume any stray events that happened since the last gesture
123 print 'Waiting for all contacts to leave before recording the gesture...'
124 self.touch_dev.FlushSnapshotBuffer()
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700125 self.plotter.Clear()
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800126
127 # Wait a long time for the first event, then have a much shorter
128 # timeout on subsequent incoming events
Charlie Mooney389e1f32015-03-06 13:33:20 -0800129 snapshots = []
Charlie Mooney389e1f32015-03-06 13:33:20 -0800130 print 'Waiting for 1st snapshot...',
Charlie Mooney1144c9b2015-04-16 13:59:56 -0700131 snapshot = None
132 if not robot_thread or not robot_thread.isAlive():
133 snapshot = self.touch_dev.NextSnapshot(TestSuite.FIRST_SNAPSHOT_TIMEOUT_S)
134 if not snapshot:
135 print '\rNo MT snapshots collected before timeout!'
Charlie Mooney08661912015-04-16 09:20:34 -0700136 while snapshot or (robot_thread and robot_thread.isAlive()):
137 if snapshot:
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700138 self.plotter.AddSnapshot(snapshot)
Charlie Mooney08661912015-04-16 09:20:34 -0700139 snapshots.append(snapshot)
140 print '\rCollected %d MT snapshots' % len(snapshots),
Charlie Mooney389e1f32015-03-06 13:33:20 -0800141 snapshot = self.touch_dev.NextSnapshot(test.timeout)
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800142 print
Charlie Mooney173396c2015-03-19 15:25:41 -0700143
Charlie Mooney08661912015-04-16 09:20:34 -0700144 # Cleanup the robot gesture thread, if it existed
145 if robot_thread:
146 robot_thread.join()
147
Charlie Mooney173396c2015-03-19 15:25:41 -0700148 # Prompt to see if the gesture was performed acceptibly by the user before
149 # continuing. If the user is unsatisfied abort before advancing on to the
150 # next test/variation, allowing the test suite to retry this variation.
151 if not self.options.has_robot:
152 CONFIRMATION_PROMPT = 'Accept Gesture? ([y]/n) '
153 yorn = raw_input(CONFIRMATION_PROMPT).lower()
154 while yorn not in ['y', 'n', 'yes', 'no', '']:
155 print color.Fore.RED + 'Error: please enter "y" or "n" only'
156 yorn = raw_input(CONFIRMATION_PROMPT).lower()
Charlie Mooney173396c2015-03-19 15:25:41 -0700157 if yorn in ['n', 'no']:
158 print color.Fore.RED + 'Operator rejected the gesture. Please retry...'
159 return True
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700160
161 plot_image_png = self.plotter.Save(wait_for_image=True)
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800162
163 # Run the validators on these events
Charlie Mooneyaf9d5122014-12-04 15:15:52 -0800164 results = test.RunAllValidators(snapshots)
Charlie Mooney985cf402015-01-08 15:15:59 -0800165
Charlie Mooney5e9a10f2015-01-13 14:54:27 -0800166 # Bundle the Validator results with some details of which gesture was used
167 # during the test for easier debugging.
Charlie Mooney2c270d12015-04-16 13:38:03 -0700168 test_result = tests.TestResult(test.variations[self.curr_variation],
169 results, prompt, plot_image_png)
Charlie Mooney5e9a10f2015-01-13 14:54:27 -0800170
Charlie Mooney985cf402015-01-08 15:15:59 -0800171 # Add the results into our report (And have it print them to stdout, too)
Charlie Mooney5e9a10f2015-01-13 14:54:27 -0800172 self.report.AddTestResult(test_result, verbose=True)
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800173
174 # Advance the test suite to the next test and variation and return an
175 # indicator as to whether this was the last thing to do or not.
176 next_test, next_var = self._Advance()
177 return (next_test is not None)
178
Charlie Mooneyc68f9c32015-04-16 15:23:22 -0700179 def StopPlotter(self):
180 self.plotter.Quit()
181
Charlie Mooney3cca6ba2014-11-19 16:15:28 -0800182 def _Advance(self):
183 """ Move on to the next test/variation pair
184
185 This function increments all the interal counters, according to the
186 number of tests, their variations, and the selected number of iterations
187 and returns the test object and the variation number that should be
188 done next.
189
190 When the TestSuite is complete, this function will return None, None
191 otherwise it will return the next Test object and the variation number
192 the test suite is on.
193 """
194 if self.curr_test >= len(self.tests):
195 return None, None
196 test = self.tests[self.curr_test]
197
198 if self.curr_variation >= len(test.variations):
199 self.curr_test += 1
200 self.curr_variation = 0
201 self.curr_iteration = 0
202 return self._Advance()
203
204 if self.curr_iteration >= self.options.num_iterations:
205 self.curr_variation += 1
206 self.curr_iteration = 0
207 return self._Advance()
208
209 self.curr_iteration += 1
210 return test, self.curr_variation