diff options
author | Cassidy Burden <cburden@google.com> | 2016-06-21 11:20:13 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-06-22 14:26:44 -0700 |
commit | 58f24cae7e6dfed8196d0b96713afaa42cd1fdde (patch) | |
tree | 95fcaaa1af140035e6e6e653d5c658f55b55e982 /binary_search_tool | |
parent | 61ace4c75764cbab29bc0bcddf40d6c2d71996a6 (diff) | |
download | toolchain-utils-58f24cae7e6dfed8196d0b96713afaa42cd1fdde.tar.gz |
Add initial unified bisection script
Add new script for unifying package and object bisection script
interfaces. Currently only package bisection is implemented.
TEST=Add unit test, test cros_pkg with testing script and interactive
script
Change-Id: I7491e3fb73eae863e24de3869092cfbf9df56c8d
Reviewed-on: https://chrome-internal-review.googlesource.com/266096
Commit-Ready: Cassidy Burden <cburden@google.com>
Tested-by: Cassidy Burden <cburden@google.com>
Reviewed-by: Caroline Tice <cmtice@google.com>
Diffstat (limited to 'binary_search_tool')
-rwxr-xr-x | binary_search_tool/bisect.py | 148 | ||||
-rwxr-xr-x | binary_search_tool/test/binary_search_tool_tester.py | 97 |
2 files changed, 236 insertions, 9 deletions
diff --git a/binary_search_tool/bisect.py b/binary_search_tool/bisect.py new file mode 100755 index 00000000..06be35fc --- /dev/null +++ b/binary_search_tool/bisect.py @@ -0,0 +1,148 @@ +#!/usr/bin/python2 +"""The unified package/object bisecting tool.""" + +from __future__ import print_function + +import abc +import argparse +import os +import sys + +if os.path.isabs(sys.argv[0]): + utils_pythonpath = os.path.abspath('{0}/..'.format(os.path.dirname(sys.argv[ + 0]))) +else: + wdir = os.getcwd() + utils_pythonpath = os.path.abspath('{0}/{1}/..'.format(wdir, os.path.dirname( + sys.argv[0]))) +sys.path.append(utils_pythonpath) +from utils import command_executer +from utils import logger + +import binary_search_state + + +class Bisector(object): + """The abstract base class for Bisectors.""" + + # Make Bisector an abstract class + __metaclass__ = abc.ABCMeta + + def __init__(self, options): + self.options = options + self.logger = logger.GetLogger() + self.ce = command_executer.GetCommandExecuter() + + @abc.abstractmethod + def PreRun(self): + pass + + @abc.abstractmethod + def Run(self): + pass + + @abc.abstractmethod + def PostRun(self): + pass + + +class BisectPackage(Bisector): + """The class for package bisection steps.""" + + cros_pkg_setup = './cros_pkg_setup.sh' + cros_pkg_cleanup = './cros_pkg_%s_cleanup.sh' + default_kwargs = { + 'get_initial_items': './cros_pkg_get_initial_items.sh', + 'switch_to_good': './cros_pkg_switch_to_good.sh', + 'switch_to_bad': './cros_pkg_switch_to_bad.sh', + 'install_script': './cros_pkg_install.sh', + 'test_script': './cros_pkg_interactive_test.sh', + 'noincremental': False, + 'prune': True, + 'file_args': True + } + + def __init__(self, options): + super(BisectPackage, self).__init__(options) + + def PreRun(self): + os.chdir('./cros_pkg') + cmd = ('%s %s %s' % + (self.cros_pkg_setup, self.options.board, self.options.remote)) + ret, _, _ = self.ce.RunCommandWExceptionCleanup(cmd, print_to_console=True) + if ret: + self.logger.LogError('Package bisector setup failed w/ error %d' % ret) + return 1 + return 0 + + def Run(self): + return binary_search_state.Run(**self.default_kwargs) + + def PostRun(self): + cmd = self.cros_pkg_cleanup % self.options.board + ret, _, _ = self.ce.RunCommandWExceptionCleanup(cmd, print_to_console=True) + if ret: + self.logger.LogError('Package bisector cleanup failed w/ error %d' % ret) + return 1 + return 0 + + +class BisectObject(Bisector): + """The class for object bisection steps.""" + + def __init__(self, options): + super(BisectObject, self).__init__(options) + + def PreRun(self): + raise NotImplementedError('Object bisecting still WIP') + + def Run(self): + return 1 + + def PostRun(self): + return 1 + + +def Run(bisector): + ret = bisector.PreRun() + if ret: + return ret + + ret = bisector.Run() + if ret: + return ret + + ret = bisector.PostRun() + if ret: + return ret + + return 0 + + +def Main(argv): + parser = argparse.ArgumentParser(epilog=('Run ./bisect.py {command} --help ' + 'for individual subcommand ' + 'help/args.')) + subparsers = parser.add_subparsers(title='Bisect mode', + description=('Whether to package or object' + 'bisect')) + + parser_package = subparsers.add_parser('package') + parser_package.add_argument('board', help='Board to target') + parser_package.add_argument('remote', help='Remote machine to test on') + parser_package.set_defaults(handler=BisectPackage) + + parser_object = subparsers.add_parser('object') + parser_object.set_defaults(handler=BisectObject) + + options = parser.parse_args(argv) + + subcmd = options.handler + del options.handler + + bisector = subcmd(options) + return Run(bisector) + + +if __name__ == '__main__': + sys.exit(Main(sys.argv[1:])) diff --git a/binary_search_tool/test/binary_search_tool_tester.py b/binary_search_tool/test/binary_search_tool_tester.py index 5a108f8e..96f3e2b4 100755 --- a/binary_search_tool/test/binary_search_tool_tester.py +++ b/binary_search_tool/test/binary_search_tool_tester.py @@ -14,21 +14,101 @@ import unittest from utils import command_executer from binary_search_tool import binary_search_state +from binary_search_tool import bisect import common import gen_obj +def GenObj(): + obj_num = random.randint(100, 1000) + bad_obj_num = random.randint(obj_num / 100, obj_num / 20) + if bad_obj_num == 0: + bad_obj_num = 1 + gen_obj.Main(['--obj_num', str(obj_num), '--bad_obj_num', str(bad_obj_num)]) + + +def CleanObj(): + os.remove(common.OBJECTS_FILE) + os.remove(common.WORKING_SET_FILE) + print('Deleted "{0}" and "{1}"'.format(common.OBJECTS_FILE, + common.WORKING_SET_FILE)) + + +class BisectTest(unittest.TestCase): + """Tests for bisect.py""" + + def setUp(self): + with open('./installed', 'w'): + pass + + try: + os.remove(binary_search_state.STATE_FILE) + except OSError: + pass + + def tearDown(self): + try: + os.remove('./installed') + os.remove(os.readlink(binary_search_state.STATE_FILE)) + os.remove(binary_search_state.STATE_FILE) + except OSError: + pass + + class FullBisector(bisect.Bisector): + """Test bisector to test bisect.py with""" + + def __init__(self, options): + super(BisectTest.FullBisector, self).__init__(options) + + def PreRun(self): + GenObj() + return 0 + + def Run(self): + return binary_search_state.Run(get_initial_items='./gen_init_list.py', + switch_to_good='./switch_to_good.py', + switch_to_bad='./switch_to_bad.py', + test_script='./is_good.py', + prune=True, + file_args=True) + + def PostRun(self): + CleanObj() + return 0 + + def test_full_bisector(self): + ret = bisect.Run(self.FullBisector({})) + self.assertEquals(ret, 0) + self.assertFalse(os.path.exists(common.OBJECTS_FILE)) + self.assertFalse(os.path.exists(common.WORKING_SET_FILE)) + + def check_output(self): + _, out, _ = command_executer.GetCommandExecuter().RunCommandWOutput( + ('grep "Bad items are: " logs/binary_search_tool_tester.py.out | ' + 'tail -n1')) + ls = out.splitlines() + self.assertEqual(len(ls), 1) + line = ls[0] + + _, _, bad_ones = line.partition('Bad items are: ') + bad_ones = bad_ones.split() + expected_result = common.ReadObjectsFile() + + # Reconstruct objects file from bad_ones and compare + actual_result = [0] * len(expected_result) + for bad_obj in bad_ones: + actual_result[int(bad_obj)] = 1 + + self.assertEqual(actual_result, expected_result) + + class BisectingUtilsTest(unittest.TestCase): """Tests for bisecting tool.""" def setUp(self): """Generate [100-1000] object files, and 1-5% of which are bad ones.""" - obj_num = random.randint(100, 1000) - bad_obj_num = random.randint(obj_num / 100, obj_num / 20) - if bad_obj_num == 0: - bad_obj_num = 1 - gen_obj.Main(['--obj_num', str(obj_num), '--bad_obj_num', str(bad_obj_num)]) + GenObj() with open('./installed', 'w'): pass @@ -40,10 +120,8 @@ class BisectingUtilsTest(unittest.TestCase): def tearDown(self): """Cleanup temp files.""" - os.remove(common.OBJECTS_FILE) - os.remove(common.WORKING_SET_FILE) - print('Deleted "{0}" and "{1}"'.format(common.OBJECTS_FILE, - common.WORKING_SET_FILE)) + CleanObj() + try: os.remove('./installed') os.remove(os.readlink(binary_search_state.STATE_FILE)) @@ -202,6 +280,7 @@ def Main(argv): suite.addTest(BisectingUtilsTest('test_save_state')) suite.addTest(BisectingUtilsTest('test_load_state')) suite.addTest(BisectingUtilsTest('test_tmp_cleanup')) + suite.addTest(BisectTest('test_full_bisector')) runner = unittest.TextTestRunner() runner.run(suite) |