Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / tools / swarming_client / tests / swarming_test.py
1 #!/usr/bin/env python
2 # Copyright 2013 The Swarming Authors. All rights reserved.
3 # Use of this source code is governed under the Apache License, Version 2.0 that
4 # can be found in the LICENSE file.
5
6 import hashlib
7 import json
8 import logging
9 import os
10 import StringIO
11 import sys
12 import threading
13 import unittest
14
15 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
16 sys.path.insert(0, ROOT_DIR)
17 sys.path.insert(0, os.path.join(ROOT_DIR, 'third_party'))
18
19 from depot_tools import auto_stub
20 import swarming
21 from utils import net
22
23
24 ALGO = hashlib.sha1
25 FILE_NAME = u'test.isolated'
26 FILE_HASH = u'1' * 40
27 TEST_NAME = u'unit_tests'
28
29
30 TEST_CASE_SUCCESS = (
31   '[----------] 2 tests from StaticCookiePolicyTest\n'
32   '[ RUN      ] StaticCookiePolicyTest.AllowAllCookiesTest\n'
33   '[       OK ] StaticCookiePolicyTest.AllowAllCookiesTest (0 ms)\n'
34   '[ RUN      ] StaticCookiePolicyTest.BlockAllCookiesTest\n'
35   '[       OK ] StaticCookiePolicyTest.BlockAllCookiesTest (0 ms)\n'
36   '[----------] 2 tests from StaticCookiePolicyTest (0 ms total)\n'
37   '\n'
38   '[----------] 1 test from TCPListenSocketTest\n'
39   '[ RUN      ] TCPListenSocketTest.ServerSend\n'
40   '[       OK ] TCPListenSocketTest.ServerSend (1 ms)\n'
41   '[----------] 1 test from TCPListenSocketTest (1 ms total)\n')
42
43
44 TEST_CASE_FAILURE = (
45   '[----------] 2 tests from StaticCookiePolicyTest\n'
46   '[ RUN      ] StaticCookiePolicyTest.AllowAllCookiesTest\n'
47   '[       OK ] StaticCookiePolicyTest.AllowAllCookiesTest (0 ms)\n'
48   '[ RUN      ] StaticCookiePolicyTest.BlockAllCookiesTest\n'
49   'C:\\win\\build\\src\\chrome\\test.cc: error: Value of: result()\n'
50   '  Actual: false\n'
51   'Expected: true\n'
52   '[  FAILED  ] StaticCookiePolicyTest.BlockAllCookiesTest (0 ms)\n'
53   '[----------] 2 tests from StaticCookiePolicyTest (0 ms total)\n'
54   '\n'
55   '[----------] 1 test from TCPListenSocketTest\n'
56   '[ RUN      ] TCPListenSocketTest.ServerSend\n'
57   '[       OK ] TCPListenSocketTest.ServerSend (1 ms)\n'
58   '[----------] 1 test from TCPListenSocketTest (1 ms total)\n')
59
60
61 SWARM_OUTPUT_SUCCESS = (
62   '[ RUN      ] unittests.Run Test\n' +
63   TEST_CASE_SUCCESS +
64   '[       OK ] unittests.Run Test (2549 ms)\n'
65   '[ RUN      ] unittests.Clean Up\n'
66   'No output!\n'
67   '[       OK ] unittests.Clean Up (6 ms)\n'
68   '\n'
69   '[----------] unittests summary\n'
70   '[==========] 2 tests ran. (2556 ms total)\n')
71
72
73 SWARM_OUTPUT_FAILURE = (
74   '[ RUN      ] unittests.Run Test\n' +
75   TEST_CASE_FAILURE +
76   '[       OK ] unittests.Run Test (2549 ms)\n'
77   '[ RUN      ] unittests.Clean Up\n'
78   'No output!\n'
79   '[       OK ] unittests.Clean Up (6 ms)\n'
80   '\n'
81   '[----------] unittests summary\n'
82   '[==========] 2 tests ran. (2556 ms total)\n')
83
84
85 SWARM_OUTPUT_WITH_NO_TEST_OUTPUT = (
86   '\n'
87   'Unable to connection to swarm machine.\n')
88
89
90 TEST_SHARD_1 = 'Note: This is test shard 1 of 3.'
91 TEST_SHARD_2 = 'Note: This is test shard 2 of 3.'
92 TEST_SHARD_3 = 'Note: This is test shard 3 of 3.'
93
94
95 SWARM_SHARD_OUTPUT = (
96   '[ RUN      ] unittests.Run Test\n'
97   '%s\n'
98   '[       OK ] unittests.Run Test (2549 ms)\n'
99   '[ RUN      ] unittests.Clean Up\n'
100   'No output!\n'
101   '[       OK ] unittests.Clean Up (6 ms)\n'
102   '\n'
103   '[----------] unittests summary\n'
104   '[==========] 2 tests ran. (2556 ms total)\n')
105
106
107 TEST_SHARD_OUTPUT_1 = SWARM_SHARD_OUTPUT % TEST_SHARD_1
108 TEST_SHARD_OUTPUT_2 = SWARM_SHARD_OUTPUT % TEST_SHARD_2
109 TEST_SHARD_OUTPUT_3 = SWARM_SHARD_OUTPUT % TEST_SHARD_3
110
111
112 def gen_data(index, shard_output, exit_codes):
113   return {
114     u'config_instance_index': index,
115     u'exit_codes': unicode(exit_codes),
116     u'machine_id': u'host',
117     u'machine_tag': u'localhost',
118     u'output': unicode(shard_output),
119   }
120
121
122 def gen_yielded_data(index, shard_output, exit_codes):
123   """Returns an entry as it would be yielded by yield_results()."""
124   return index, gen_data(index, shard_output, exit_codes)
125
126
127 def generate_url_response(index, shard_output, exit_codes):
128   return net.HttpResponse.get_fake_response(
129       json.dumps(gen_data(index, shard_output, exit_codes)), 'mocked_url')
130
131
132 def get_swarm_results(keys):
133   """Simplifies the call to yield_results().
134
135   The timeout is hard-coded to 10 seconds.
136   """
137   return list(swarming.yield_results('http://host:9001', keys, 10., None))
138
139
140 def main(args):
141   """Bypassies swarming.main()'s exception handling.
142
143   It gets in the way when debugging test failures.
144   """
145   dispatcher = swarming.subcommand.CommandDispatcher('swarming')
146   return dispatcher.execute(swarming.OptionParserSwarming(), args)
147
148
149 class TestCase(auto_stub.TestCase):
150   """Base class that defines the url_open mock."""
151   def setUp(self):
152     super(TestCase, self).setUp()
153     self._lock = threading.Lock()
154     self.requests = []
155     self.mock(swarming.net, 'url_open', self._url_open)
156     self.mock(swarming.time, 'sleep', lambda x: None)
157     self.mock(sys, 'stdout', StringIO.StringIO())
158     self.mock(sys, 'stderr', StringIO.StringIO())
159
160   def tearDown(self):
161     try:
162       if not self.has_failed():
163         self._check_output('', '')
164         self.assertEqual([], self.requests)
165     finally:
166       super(TestCase, self).tearDown()
167
168   def _check_output(self, out, err):
169     self.assertEqual(out, sys.stdout.getvalue())
170     self.assertEqual(err, sys.stderr.getvalue())
171
172     # Flush their content by mocking them again.
173     self.mock(sys, 'stdout', StringIO.StringIO())
174     self.mock(sys, 'stderr', StringIO.StringIO())
175
176   def _url_open(self, url, **kwargs):
177     logging.info('url_open(%s)', url)
178     with self._lock:
179       # Ignore 'stream' argument, it's not important for these tests.
180       kwargs.pop('stream')
181       # Since the client is multi-threaded, requests can be processed out of
182       # order.
183       for index, r in enumerate(self.requests):
184         if r[0] == url and r[1] == kwargs:
185           _, _, returned = self.requests.pop(index)
186           break
187       else:
188         self.fail(
189             'Failed to find url %s\n%s\nRemaining:\n%s' % (
190               url,
191               json.dumps(kwargs, indent=2, sort_keys=True),
192               json.dumps(
193                 [(i[0], i[1]) for i in self.requests],
194                 indent=2, sort_keys=True)))
195     return returned
196
197
198 class TestGetTestKeys(TestCase):
199   def test_no_keys(self):
200     self.mock(swarming.time, 'sleep', lambda x: x)
201     self.requests = [
202       (
203         'http://host:9001/get_matching_test_cases?name=my_test',
204         {'retry_404': True},
205         StringIO.StringIO('No matching Test Cases'),
206       ) for _ in range(net.URL_OPEN_MAX_ATTEMPTS)
207     ]
208     try:
209       swarming.get_task_keys('http://host:9001', 'my_test')
210       self.fail()
211     except swarming.Failure as e:
212       msg = (
213           'Error: Unable to find any task with the name, my_test, on swarming '
214           'server')
215       self.assertEqual(msg, e.args[0])
216
217   def test_no_keys_on_first_attempt(self):
218     self.mock(swarming.time, 'sleep', lambda x: x)
219     keys = ['key_1', 'key_2']
220     self.requests = [
221       (
222         'http://host:9001/get_matching_test_cases?name=my_test',
223         {'retry_404': True},
224         StringIO.StringIO('No matching Test Cases'),
225       ),
226       (
227         'http://host:9001/get_matching_test_cases?name=my_test',
228         {'retry_404': True},
229         StringIO.StringIO(json.dumps(keys)),
230       ),
231     ]
232     actual = swarming.get_task_keys('http://host:9001', 'my_test')
233     self.assertEqual(keys, actual)
234
235   def test_find_keys(self):
236     keys = ['key_1', 'key_2']
237     self.requests = [
238       (
239         'http://host:9001/get_matching_test_cases?name=my_test',
240         {'retry_404': True},
241         StringIO.StringIO(json.dumps(keys)),
242       ),
243     ]
244     actual = swarming.get_task_keys('http://host:9001', 'my_test')
245     self.assertEqual(keys, actual)
246
247
248 class TestGetSwarmResults(TestCase):
249   def test_success(self):
250     self.requests = [
251       (
252         'http://host:9001/get_result?r=key1',
253         {'retry_404': False, 'retry_50x': False},
254         generate_url_response(0, SWARM_OUTPUT_SUCCESS, '0, 0'),
255       ),
256     ]
257     expected = [gen_yielded_data(0, SWARM_OUTPUT_SUCCESS, '0, 0')]
258     actual = get_swarm_results(['key1'])
259     self.assertEqual(expected, actual)
260
261   def test_failure(self):
262     self.requests = [
263       (
264         'http://host:9001/get_result?r=key1',
265         {'retry_404': False, 'retry_50x': False},
266         generate_url_response(0, SWARM_OUTPUT_FAILURE, '0, 1'),
267       ),
268     ]
269     expected = [gen_yielded_data(0, SWARM_OUTPUT_FAILURE, '0, 1')]
270     actual = get_swarm_results(['key1'])
271     self.assertEqual(expected, actual)
272
273   def test_no_test_output(self):
274     self.requests = [
275       (
276         'http://host:9001/get_result?r=key1',
277         {'retry_404': False, 'retry_50x': False},
278         generate_url_response(0, SWARM_OUTPUT_WITH_NO_TEST_OUTPUT, '0, 0'),
279       ),
280     ]
281     expected = [gen_yielded_data(0, SWARM_OUTPUT_WITH_NO_TEST_OUTPUT, '0, 0')]
282     actual = get_swarm_results(['key1'])
283     self.assertEqual(expected, actual)
284
285   def test_no_keys(self):
286     actual = get_swarm_results([])
287     self.assertEqual([], actual)
288
289   def test_url_errors(self):
290     self.mock(logging, 'error', lambda *_: None)
291     # NOTE: get_swarm_results() hardcodes timeout=10. range(12) is because of an
292     # additional time.time() call deep in net.url_open().
293     now = {}
294     lock = threading.Lock()
295     def get_now():
296       t = threading.current_thread()
297       with lock:
298         return now.setdefault(t, range(12)).pop(0)
299     self.mock(swarming.net, 'sleep_before_retry', lambda _x, _y: None)
300     self.mock(swarming, 'now', get_now)
301     # The actual number of requests here depends on 'now' progressing to 10
302     # seconds. It's called twice per loop.
303     self.requests = [
304       (
305         'http://host:9001/get_result?r=key1',
306         {'retry_404': False, 'retry_50x': False},
307         None,
308       ),
309       (
310         'http://host:9001/get_result?r=key1',
311         {'retry_404': False, 'retry_50x': False},
312         None,
313       ),
314       (
315         'http://host:9001/get_result?r=key1',
316         {'retry_404': False, 'retry_50x': False},
317         None,
318       ),
319       (
320         'http://host:9001/get_result?r=key1',
321         {'retry_404': False, 'retry_50x': False},
322         None,
323       ),
324       (
325         'http://host:9001/get_result?r=key1',
326         {'retry_404': False, 'retry_50x': False},
327         None,
328       ),
329     ]
330     actual = get_swarm_results(['key1'])
331     self.assertEqual([], actual)
332     self.assertTrue(all(not v for v in now.itervalues()), now)
333
334   def test_shard_repeated(self):
335     self.requests = [
336       (
337         'http://host:9001/get_result?r=key1',
338         {'retry_404': False, 'retry_50x': False},
339         generate_url_response(0, SWARM_OUTPUT_SUCCESS, '0, 0'),
340       ),
341       (
342         'http://host:9001/get_result?r=key1-repeat',
343         {'retry_404': False, 'retry_50x': False},
344         generate_url_response(0, SWARM_OUTPUT_SUCCESS, '0, 0'),
345       ),
346     ]
347     expected = [gen_yielded_data(0, SWARM_OUTPUT_SUCCESS, '0, 0')]
348     actual = get_swarm_results(['key1', 'key1-repeat'])
349     self.assertEqual(expected, actual)
350
351   def test_one_shard_repeated(self):
352     """Have shard 1 repeated twice, then shard 2 and 3."""
353     self.requests = [
354       (
355         'http://host:9001/get_result?r=key1',
356         {'retry_404': False, 'retry_50x': False},
357         generate_url_response(0, TEST_SHARD_OUTPUT_1, '0, 0'),
358       ),
359       (
360         'http://host:9001/get_result?r=key1-repeat',
361         {'retry_404': False, 'retry_50x': False},
362         generate_url_response(0, TEST_SHARD_OUTPUT_1, '0, 0'),
363       ),
364       (
365         'http://host:9001/get_result?r=key2',
366         {'retry_404': False, 'retry_50x': False},
367         generate_url_response(1, TEST_SHARD_OUTPUT_2, '0, 0'),
368       ),
369       (
370         'http://host:9001/get_result?r=key3',
371         {'retry_404': False, 'retry_50x': False},
372         generate_url_response(2, TEST_SHARD_OUTPUT_3, '0, 0'),
373       ),
374     ]
375     expected = [
376       gen_yielded_data(0, TEST_SHARD_OUTPUT_1, '0, 0'),
377       gen_yielded_data(1, TEST_SHARD_OUTPUT_2, '0, 0'),
378       gen_yielded_data(2, TEST_SHARD_OUTPUT_3, '0, 0'),
379     ]
380     actual = get_swarm_results(['key1', 'key1-repeat', 'key2', 'key3'])
381     self.assertEqual(expected, sorted(actual))
382
383   def test_collect_nothing(self):
384     self.mock(swarming, 'get_task_keys', lambda *_: [1, 2])
385     self.mock(swarming, 'yield_results', lambda *_: [])
386     self.assertEqual(
387         1, swarming.collect('url', 'test_name', 'timeout', 'decorate'))
388
389   def test_collect_success(self):
390     self.mock(swarming, 'get_task_keys', lambda *_: [1, 2])
391     data = {
392       'config_instance_index': 0,
393       'exit_codes': '0',
394       'machine_id': 0,
395       'output': 'Foo',
396     }
397     self.mock(swarming, 'yield_results', lambda *_: [(0, data)])
398     self.assertEqual(
399         0, swarming.collect('url', 'test_name', 'timeout', 'decorate'))
400     self._check_output(
401         '\n================================================================\n'
402         'Begin output from shard index 0 (machine tag: 0, id: unknown)\n'
403         '================================================================\n\n'
404         'Foo================================================================\n'
405         'End output from shard index 0 (machine tag: 0, id: unknown). Return 0'
406           '\n'
407         '================================================================\n\n',
408         '')
409
410   def test_collect_fail(self):
411     self.mock(swarming, 'get_task_keys', lambda *_: [1, 2])
412     data = {
413       'config_instance_index': 0,
414       'exit_codes': '0,8',
415       'machine_id': 0,
416       'output': 'Foo',
417     }
418     self.mock(swarming, 'yield_results', lambda *_: [(0, data)])
419     self.assertEqual(
420         8, swarming.collect('url', 'test_name', 'timeout', 'decorate'))
421     self._check_output(
422         '\n================================================================\n'
423         'Begin output from shard index 0 (machine tag: 0, id: unknown)\n'
424         '================================================================\n\n'
425         'Foo================================================================\n'
426         'End output from shard index 0 (machine tag: 0, id: unknown). Return 8'
427           '\n'
428         '================================================================\n\n',
429         '')
430
431
432 def chromium_tasks(retrieval_url):
433   return [
434     {
435       u'action': [
436         u'python', u'run_isolated.zip',
437         u'--hash', FILE_HASH,
438         u'--isolate-server', retrieval_url,
439       ],
440       u'decorate_output': False,
441       u'test_name': u'Run Test',
442       u'time_out': 600,
443     },
444     {
445       u'action' : [
446           u'python', u'swarm_cleanup.py',
447       ],
448       u'decorate_output': False,
449       u'test_name': u'Clean Up',
450       u'time_out': 600,
451     }
452   ]
453
454
455 def generate_expected_json(
456     shards,
457     dimensions,
458     env,
459     working_dir,
460     isolate_server,
461     profile):
462   expected = {
463     u'cleanup': u'root',
464     u'configurations': [
465       {
466         u'config_name': u'isolated',
467         u'dimensions': dimensions,
468         u'min_instances': shards,
469         u'priority': 101,
470       },
471     ],
472     u'data': [],
473     u'encoding': u'UTF-8',
474     u'env_vars': env.copy(),
475     u'restart_on_failure': True,
476     u'test_case_name': TEST_NAME,
477     u'tests': chromium_tasks(isolate_server),
478     u'working_dir': unicode(working_dir),
479   }
480   if shards > 1:
481     expected[u'env_vars'][u'GTEST_SHARD_INDEX'] = u'%(instance_index)s'
482     expected[u'env_vars'][u'GTEST_TOTAL_SHARDS'] = u'%(num_instances)s'
483   if profile:
484     expected[u'tests'][0][u'action'].append(u'--verbose')
485   return expected
486
487
488 class MockedStorage(object):
489   def __init__(self, warm_cache):
490     self._warm_cache = warm_cache
491
492   def __enter__(self):
493     pass
494
495   def __exit__(self, *_args):
496     pass
497
498   def upload_items(self, items):
499     return [] if self._warm_cache else items
500
501   def get_fetch_url(self, _digest):  # pylint: disable=R0201
502     return 'http://localhost:8081/fetch_url'
503
504
505 class ManifestTest(TestCase):
506   def test_basic_manifest(self):
507     env = {
508       u'GTEST_SHARD_INDEX': u'%(instance_index)s',
509       u'GTEST_TOTAL_SHARDS': u'%(num_instances)s',
510     }
511     dimensions = {'os': 'Windows'}
512     manifest = swarming.Manifest(
513         isolate_server='http://localhost:8081',
514         isolated_hash=FILE_HASH,
515         task_name=TEST_NAME,
516         shards=2,
517         env=env,
518         dimensions=dimensions,
519         working_dir='swarm_tests',
520         verbose=False,
521         profile=False,
522         priority=101,
523         algo=ALGO)
524
525     swarming.chromium_setup(manifest)
526     manifest_json = json.loads(manifest.to_json())
527
528     expected = generate_expected_json(
529         shards=2,
530         dimensions={u'os': u'Windows'},
531         env={},
532         working_dir='swarm_tests',
533         isolate_server=u'http://localhost:8081',
534         profile=False)
535     self.assertEqual(expected, manifest_json)
536
537   def test_basic_linux(self):
538     """A basic linux manifest test to ensure that windows specific values
539        aren't used.
540     """
541     dimensions = {'os': 'Linux'}
542     manifest = swarming.Manifest(
543         isolate_server='http://localhost:8081',
544         isolated_hash=FILE_HASH,
545         task_name=TEST_NAME,
546         shards=1,
547         env={},
548         dimensions=dimensions,
549         working_dir='swarm_tests',
550         verbose=False,
551         profile=False,
552         priority=101,
553         algo=ALGO)
554
555     swarming.chromium_setup(manifest)
556     manifest_json = json.loads(manifest.to_json())
557
558     expected = generate_expected_json(
559         shards=1,
560         dimensions={u'os': u'Linux'},
561         env={},
562         working_dir='swarm_tests',
563         isolate_server=u'http://localhost:8081',
564         profile=False)
565     self.assertEqual(expected, manifest_json)
566
567   def test_basic_linux_profile(self):
568     dimensions = {'os': 'Linux'}
569     manifest = swarming.Manifest(
570         isolate_server='http://localhost:8081',
571         isolated_hash=FILE_HASH,
572         task_name=TEST_NAME,
573         shards=1,
574         env={},
575         dimensions=dimensions,
576         working_dir='swarm_tests',
577         verbose=False,
578         profile=True,
579         priority=101,
580         algo=ALGO)
581
582     swarming.chromium_setup(manifest)
583     manifest_json = json.loads(manifest.to_json())
584
585     expected = generate_expected_json(
586         shards=1,
587         dimensions={u'os': u'Linux'},
588         env={},
589         working_dir='swarm_tests',
590         isolate_server=u'http://localhost:8081',
591         profile=True)
592     self.assertEqual(expected, manifest_json)
593
594   def test_process_manifest_success(self):
595     self.mock(swarming.net, 'url_read', lambda url, data=None: '{}')
596     self.mock(swarming.isolateserver, 'get_storage',
597         lambda *_: MockedStorage(warm_cache=False))
598
599     result = swarming.process_manifest(
600         swarming='http://localhost:8082',
601         isolate_server='http://localhost:8081',
602         isolated_hash=FILE_HASH,
603         task_name=TEST_NAME,
604         shards=1,
605         dimensions={},
606         env={},
607         working_dir='swarm_tests',
608         verbose=False,
609         profile=False,
610         priority=101,
611         algo=ALGO)
612     self.assertEqual(0, result)
613
614   def test_process_manifest_success_zip_already_uploaded(self):
615     self.mock(swarming.net, 'url_read', lambda url, data=None: '{}')
616     self.mock(swarming.isolateserver, 'get_storage',
617         lambda *_: MockedStorage(warm_cache=True))
618
619     dimensions = {'os': 'linux2'}
620     result = swarming.process_manifest(
621         swarming='http://localhost:8082',
622         isolate_server='http://localhost:8081',
623         isolated_hash=FILE_HASH,
624         task_name=TEST_NAME,
625         shards=1,
626         dimensions=dimensions,
627         env={},
628         working_dir='swarm_tests',
629         verbose=False,
630         profile=False,
631         priority=101,
632         algo=ALGO)
633     self.assertEqual(0, result)
634
635
636 class MainTest(TestCase):
637   def test_trigger_no_request(self):
638     with self.assertRaises(SystemExit):
639       main([
640             'trigger', '--swarming', 'https://host',
641             '--isolate-server', 'https://host',
642           ])
643     self._check_output(
644         '',
645         'Usage: swarming.py trigger [options] (hash|isolated)\n\n'
646         'swarming.py: error: Must pass one .isolated file or its hash (sha1).'
647         '\n')
648
649   def test_trigger_env(self):
650     self.mock(swarming.isolateserver, 'get_storage',
651         lambda *_: MockedStorage(warm_cache=False))
652     j = generate_expected_json(
653         shards=1,
654         dimensions={'os': 'Mac'},
655         env={'foo': 'bar'},
656         working_dir='swarm_tests',
657         isolate_server='https://host2',
658         profile=False)
659     j['data'] = [['http://localhost:8081/fetch_url', 'swarm_data.zip']]
660     data = {
661       'request': json.dumps(j, sort_keys=True, separators=(',',':')),
662     }
663     self.requests = [
664       (
665         'https://host1/test',
666         {'data': data},
667         # The actual output is ignored as long as it is valid json.
668         StringIO.StringIO('{}'),
669       ),
670     ]
671     ret = main([
672         'trigger',
673         '--swarming', 'https://host1',
674         '--isolate-server', 'https://host2',
675         '--shards', '1',
676         '--priority', '101',
677         '--env', 'foo', 'bar',
678         '--dimension', 'os', 'Mac',
679         '--task-name', TEST_NAME,
680         FILE_HASH,
681       ])
682     actual = sys.stdout.getvalue()
683     self.assertEqual(0, ret, (actual, sys.stderr.getvalue()))
684
685   def test_trigger_dimension_filter(self):
686     self.mock(swarming.isolateserver, 'get_storage',
687         lambda *_: MockedStorage(warm_cache=False))
688     j = generate_expected_json(
689         shards=1,
690         dimensions={'foo': 'bar', 'os': 'Mac'},
691         env={},
692         working_dir='swarm_tests',
693         isolate_server='https://host2',
694         profile=False)
695     j['data'] = [['http://localhost:8081/fetch_url', 'swarm_data.zip']]
696     data = {
697       'request': json.dumps(j, sort_keys=True, separators=(',',':')),
698     }
699     self.requests = [
700       (
701         'https://host1/test',
702         {'data': data},
703         # The actual output is ignored as long as it is valid json.
704         StringIO.StringIO('{}'),
705       ),
706     ]
707     ret = main([
708         'trigger',
709         '--swarming', 'https://host1',
710         '--isolate-server', 'https://host2',
711         '--shards', '1',
712         '--priority', '101',
713         '--dimension', 'foo', 'bar',
714         '--dimension', 'os', 'Mac',
715         '--task-name', TEST_NAME,
716         FILE_HASH,
717       ])
718     actual = sys.stdout.getvalue()
719     self.assertEqual(0, ret, (actual, sys.stderr.getvalue()))
720
721
722 if __name__ == '__main__':
723   logging.basicConfig(
724       level=logging.DEBUG if '-v' in sys.argv else logging.ERROR)
725   if '-v' in sys.argv:
726     unittest.TestCase.maxDiff = None
727   unittest.main()