Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(267)

Side by Side Diff: client/tests/run_isolated_test.py

Issue 2853413002: Add unit test to clean_caches() (Closed)
Patch Set: os.path.relpath Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « client/tests/run_isolated_smoke_test.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2013 The LUCI Authors. All rights reserved. 2 # Copyright 2013 The LUCI Authors. All rights reserved.
3 # Use of this source code is governed under the Apache License, Version 2.0 3 # Use of this source code is governed under the Apache License, Version 2.0
4 # that can be found in the LICENSE file. 4 # that can be found in the LICENSE file.
5 5
6 # pylint: disable=R0201 6 # pylint: disable=R0201
7 7
8 import StringIO 8 import StringIO
9 import base64 9 import base64
10 import contextlib 10 import contextlib
11 import functools 11 import functools
12 import hashlib
12 import json 13 import json
13 import logging 14 import logging
14 import os 15 import os
15 import sys 16 import sys
16 import tempfile 17 import tempfile
17 import unittest 18 import unittest
18 19
19 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath( 20 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(
20 __file__.decode(sys.getfilesystemencoding())))) 21 __file__.decode(sys.getfilesystemencoding()))))
21 sys.path.insert(0, ROOT_DIR) 22 sys.path.insert(0, ROOT_DIR)
22 sys.path.insert(0, os.path.join(ROOT_DIR, 'third_party')) 23 sys.path.insert(0, os.path.join(ROOT_DIR, 'third_party'))
23 24
24 import cipd 25 import cipd
25 import isolated_format 26 import isolated_format
26 import isolateserver 27 import isolateserver
28 import named_cache
27 import run_isolated 29 import run_isolated
28 from depot_tools import auto_stub 30 from depot_tools import auto_stub
29 from depot_tools import fix_encoding 31 from depot_tools import fix_encoding
30 from utils import file_path 32 from utils import file_path
31 from utils import fs 33 from utils import fs
32 from utils import large 34 from utils import large
33 from utils import logging_utils 35 from utils import logging_utils
34 from utils import on_error 36 from utils import on_error
35 from utils import subprocess42 37 from utils import subprocess42
36 from utils import tools 38 from utils import tools
37 39
38 import isolateserver_mock 40 import isolateserver_mock
39 import cipdserver_mock 41 import cipdserver_mock
40 42
41 43
44 ALGO = hashlib.sha1
45
46
42 def write_content(filepath, content): 47 def write_content(filepath, content):
43 with open(filepath, 'wb') as f: 48 with open(filepath, 'wb') as f:
44 f.write(content) 49 f.write(content)
45 50
46 51
47 def json_dumps(data): 52 def json_dumps(data):
48 return json.dumps(data, sort_keys=True, separators=(',', ':')) 53 return json.dumps(data, sort_keys=True, separators=(',', ':'))
49 54
50 55
56 def genTree(path):
57 """Returns a dict with {filepath: content}."""
58 if not os.path.isdir(path):
59 return None
60 out = {}
61 for root, _, filenames in os.walk(path):
62 for filename in filenames:
63 p = os.path.join(root, filename)
64 with open(p, 'rb') as f:
65 out[os.path.relpath(p, path)] = f.read()
66 return out
67
68
51 @contextlib.contextmanager 69 @contextlib.contextmanager
52 def init_named_caches_stub(_run_dir): 70 def init_named_caches_stub(_run_dir):
53 yield 71 yield
54 72
55 73
56 class StorageFake(object): 74 class StorageFake(object):
57 def __init__(self, files): 75 def __init__(self, files):
58 self._files = files.copy() 76 self._files = files.copy()
59 self.namespace = 'default-gzip' 77 self.namespace = 'default-gzip'
60 self.location = 'http://localhost:1' 78 self.location = 'http://localhost:1'
(...skipping 24 matching lines...) Expand all
85 logging.debug(self.tempdir) 103 logging.debug(self.tempdir)
86 self.mock(run_isolated, 'make_temp_dir', self.fake_make_temp_dir) 104 self.mock(run_isolated, 'make_temp_dir', self.fake_make_temp_dir)
87 self.mock(run_isolated.auth, 'ensure_logged_in', lambda _: None) 105 self.mock(run_isolated.auth, 'ensure_logged_in', lambda _: None)
88 self.mock( 106 self.mock(
89 logging_utils.OptionParserWithLogging, 'logger_root', 107 logging_utils.OptionParserWithLogging, 'logger_root',
90 logging.Logger('unittest')) 108 logging.Logger('unittest'))
91 109
92 self.cipd_server = cipdserver_mock.MockCipdServer() 110 self.cipd_server = cipdserver_mock.MockCipdServer()
93 111
94 def tearDown(self): 112 def tearDown(self):
113 # Remove mocks.
114 super(RunIsolatedTestBase, self).tearDown()
95 file_path.rmtree(self.tempdir) 115 file_path.rmtree(self.tempdir)
96 self.cipd_server.close() 116 self.cipd_server.close()
97 super(RunIsolatedTestBase, self).tearDown()
98 117
99 @property 118 @property
100 def run_test_temp_dir(self): 119 def run_test_temp_dir(self):
101 """Where to map all files in run_isolated.run_tha_test.""" 120 """Where to map all files in run_isolated.run_tha_test."""
102 return os.path.join(self.tempdir, run_isolated.ISOLATED_RUN_DIR) 121 return os.path.join(self.tempdir, run_isolated.ISOLATED_RUN_DIR)
103 122
104 def fake_make_temp_dir(self, prefix, _root_dir): 123 def fake_make_temp_dir(self, prefix, _root_dir):
105 """Predictably returns directory for run_tha_test (one per test case).""" 124 """Predictably returns directory for run_tha_test (one per test case)."""
106 self.assertIn( 125 self.assertIn(
107 prefix, 126 prefix,
(...skipping 443 matching lines...) Expand 10 before | Expand all | Expand 10 after
551 {'detached': True}), 570 {'detached': True}),
552 ], 571 ],
553 self.popen_calls) 572 self.popen_calls)
554 573
555 def test_run_tha_test_non_isolated(self): 574 def test_run_tha_test_non_isolated(self):
556 _ = self._run_tha_test(command=['/bin/echo', 'hello', 'world']) 575 _ = self._run_tha_test(command=['/bin/echo', 'hello', 'world'])
557 self.assertEqual( 576 self.assertEqual(
558 [([u'/bin/echo', u'hello', u'world'], {'detached': True})], 577 [([u'/bin/echo', u'hello', u'world'], {'detached': True})],
559 self.popen_calls) 578 self.popen_calls)
560 579
580 def test_clean_caches(self):
581 # Create an isolated cache and a named cache each with 2 items. Ensure that
582 # one item from each is removed.
583 fake_time = 1
584 fake_free_space = [102400]
585 np = self.temp_join('named_cache')
586 ip = self.temp_join('isolated_cache')
587 args = [
588 '--named-cache-root', np, '--cache', ip, '--clean',
589 '--min-free-space', '10240',
590 ]
591 self.mock(file_path, 'get_free_space', lambda _: fake_free_space[0])
592 parser, options, _ = run_isolated.parse_args(args)
593 isolate_cache = isolateserver.process_cache_options(
594 options, trim=False, time_fn=lambda: fake_time)
595 self.assertIsInstance(isolate_cache, isolateserver.DiskCache)
596 named_cache_manager = named_cache.process_named_cache_options(
597 parser, options)
598 self.assertIsInstance(named_cache_manager, named_cache.CacheManager)
599
600 # Add items to these caches.
601 small = '0123456789'
602 big = small * 1014
603 small_digest = unicode(ALGO(small).hexdigest())
604 big_digest = unicode(ALGO(big).hexdigest())
605 with isolate_cache:
606 fake_time = 1
607 isolate_cache.write(big_digest, [big])
608 fake_time = 2
609 isolate_cache.write(small_digest, [small])
610 with named_cache_manager.open(time_fn=lambda: fake_time):
611 fake_time = 1
612 p = named_cache_manager.request('first')
613 with open(os.path.join(p, 'big'), 'wb') as f:
614 f.write(big)
615 fake_time = 3
616 p = named_cache_manager.request('second')
617 with open(os.path.join(p, 'small'), 'wb') as f:
618 f.write(small)
619
620 # Ensures the cache contain the expected data.
621 actual = genTree(np)
622 # Figure out the cache path names.
623 cache_small = [
624 os.path.dirname(n) for n in actual if os.path.basename(n) == 'small'][0]
625 cache_big = [
626 os.path.dirname(n) for n in actual if os.path.basename(n) == 'big'][0]
627 expected = {
628 os.path.join(cache_small, u'small'): small,
629 os.path.join(cache_big, u'big'): big,
630 u'state.json':
631 '{"items":[["first",["%s",1]],["second",["%s",3]]],"version":2}' % (
632 cache_big, cache_small),
633 }
634 self.assertEqual(expected, actual)
635 expected = {
636 big_digest: big,
637 small_digest: small,
638 u'state.json':
639 '{"items":[["%s",[10140,1]],["%s",[10,2]]],"version":2}' % (
640 big_digest, small_digest),
641 }
642 self.assertEqual(expected, genTree(ip))
643
644 # Request triming.
645 fake_free_space[0] = 1020
646 # Abuse the fact that named cache is trimed after isolated cache.
647 def rmtree(p):
648 self.assertEqual(os.path.join(np, cache_big), p)
649 fake_free_space[0] += 10240
650 return old_rmtree(p)
651 old_rmtree = self.mock(file_path, 'rmtree', rmtree)
652 isolate_cache = isolateserver.process_cache_options(options, trim=False)
653 named_cache_manager = named_cache.process_named_cache_options(
654 parser, options)
655 actual = run_isolated.clean_caches(
656 options, isolate_cache, named_cache_manager)
657 self.assertEqual(2, actual)
658 # One of each entry should have been cleaned up. This only happen to work
659 # because:
660 # - file_path.get_free_space() is mocked
661 # - DiskCache.trim() keeps its own internal counter while deleting files so
662 # it ignores get_free_space() output while deleting files.
663 actual = genTree(np)
664 expected = {
665 os.path.join(cache_small, u'small'): small,
666 u'state.json':
667 '{"items":[["second",["%s",3]]],"version":2}' % cache_small,
668 }
669 self.assertEqual(expected, actual)
670 expected = {
671 small_digest: small,
672 u'state.json':
673 '{"items":[["%s",[10,2]]],"version":2}' % small_digest,
674 }
675 self.assertEqual(expected, genTree(ip))
676
561 677
562 class RunIsolatedTestRun(RunIsolatedTestBase): 678 class RunIsolatedTestRun(RunIsolatedTestBase):
563 def test_output(self): 679 def test_output(self):
564 # Starts a full isolate server mock and have run_tha_test() uploads results 680 # Starts a full isolate server mock and have run_tha_test() uploads results
565 # back after the task completed. 681 # back after the task completed.
566 server = isolateserver_mock.MockIsolateServer() 682 server = isolateserver_mock.MockIsolateServer()
567 try: 683 try:
568 script = ( 684 script = (
569 'import sys\n' 685 'import sys\n'
570 'open(sys.argv[1], "w").write("bar")\n') 686 'open(sys.argv[1], "w").write("bar")\n')
(...skipping 298 matching lines...) Expand 10 before | Expand all | Expand 10 after
869 self.assertEqual(expected, actual) 985 self.assertEqual(expected, actual)
870 986
871 987
872 if __name__ == '__main__': 988 if __name__ == '__main__':
873 fix_encoding.fix_encoding() 989 fix_encoding.fix_encoding()
874 if '-v' in sys.argv: 990 if '-v' in sys.argv:
875 unittest.TestCase.maxDiff = None 991 unittest.TestCase.maxDiff = None
876 logging.basicConfig( 992 logging.basicConfig(
877 level=logging.DEBUG if '-v' in sys.argv else logging.ERROR) 993 level=logging.DEBUG if '-v' in sys.argv else logging.ERROR)
878 unittest.main() 994 unittest.main()
OLDNEW
« no previous file with comments | « client/tests/run_isolated_smoke_test.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698