aboutsummaryrefslogtreecommitdiff
path: root/binary_search_tool
diff options
context:
space:
mode:
authorCassidy Burden <cburden@google.com>2016-06-21 11:20:13 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-06-22 14:26:44 -0700
commit58f24cae7e6dfed8196d0b96713afaa42cd1fdde (patch)
tree95fcaaa1af140035e6e6e653d5c658f55b55e982 /binary_search_tool
parent61ace4c75764cbab29bc0bcddf40d6c2d71996a6 (diff)
downloadtoolchain-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-xbinary_search_tool/bisect.py148
-rwxr-xr-xbinary_search_tool/test/binary_search_tool_tester.py97
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)