bootstrap: Wipe selected buildroot contents as needed.

When we reuse a buildroot between branches, we can't always update the
chroot correctly, so wipe it if necessary. Note that this failure can
happen when transitioning from new to old as well as old to new.

BUG=chromium:684907
TEST=Unittests and manual testing.

Change-Id: Icc0af6352a323041c06e95cdc23a8bd16455d91c
Reviewed-on: https://chromium-review.googlesource.com/444082
Commit-Ready: Don Garrett <dgarrett@chromium.org>
Tested-by: Don Garrett <dgarrett@chromium.org>
Reviewed-by: Don Garrett <dgarrett@chromium.org>
diff --git a/scripts/bootstrap_unittest.py b/scripts/bootstrap_unittest.py
index fe59862..cb631b9 100644
--- a/scripts/bootstrap_unittest.py
+++ b/scripts/bootstrap_unittest.py
@@ -7,9 +7,11 @@
 from __future__ import print_function
 
 import mock
+import os
 
 from chromite.cbuildbot import repository
 from chromite.lib import cros_build_lib_unittest
+from chromite.lib import cros_test_lib
 from chromite.lib import osutils
 from chromite.scripts import bootstrap
 
@@ -23,12 +25,9 @@
   def testPreParseArguments(self):
     """Test that we can correctly extract branch values from cbuildbot args."""
     cases = (
-        (['--buildroot', '/build'],
-         None, '/build', None),
-        (['--branch', 'branch', '-r', '/build'],
-         'branch', '/build', None),
-        (['-r', '/build', '-b', 'branch', 'config'],
-         'branch', '/build', None),
+        (['--buildroot', '/build'], None, '/build', None),
+        (['--branch', 'branch', '-r', '/build'], 'branch', '/build', None),
+        (['-r', '/build', '-b', 'branch', 'config'], 'branch', '/build', None),
     )
 
     for args, expected_branch, expected_root, expected_git_cache in cases:
@@ -40,7 +39,6 @@
   def testInitialCheckoutMin(self):
     """Test InitialCheckout with minimum settings."""
     mock_repo = self.PatchObject(repository, 'RepoRepository', autospec=True)
-    self.PatchObject(osutils, 'SafeMakedirs', autospec=True)
 
     bootstrap.InitialCheckout(None, '/buildroot', None)
 
@@ -53,7 +51,6 @@
   def testInitialCheckoutMax(self):
     """Test InitialCheckout with all settings."""
     mock_repo = self.PatchObject(repository, 'RepoRepository', autospec=True)
-    self.PatchObject(osutils, 'SafeMakedirs', autospec=True)
 
     bootstrap.InitialCheckout('release-R56-9000.B', '/buildroot', '/git-cache')
 
@@ -71,11 +68,18 @@
 
   def testMainMin(self):
     """Test a minimal set of command line options."""
+    mock_clean = self.PatchObject(bootstrap, 'CleanBuildroot', autospec=True)
     mock_checkout = self.PatchObject(bootstrap, 'InitialCheckout',
                                      autospec=True)
+    self.PatchObject(osutils, 'SafeMakedirs', autospec=True)
+
 
     bootstrap.main(['--buildroot', '/buildroot', 'foo'])
 
+    # Ensure we clean, as expected.
+    self.assertEqual(mock_clean.mock_calls,
+                     [mock.call(None, '/buildroot')])
+
     # Ensure we checkout, as expected.
     self.assertEqual(mock_checkout.mock_calls,
                      [mock.call(None, '/buildroot', None)])
@@ -88,12 +92,18 @@
 
   def testMainMax(self):
     """Test a maximal set of command line options."""
+    mock_clean = self.PatchObject(bootstrap, 'CleanBuildroot', autospec=True)
     mock_checkout = self.PatchObject(bootstrap, 'InitialCheckout',
                                      autospec=True)
+    self.PatchObject(osutils, 'SafeMakedirs', autospec=True)
 
     bootstrap.main(['--buildroot', '/buildroot', '--branch', 'branch',
                     '--git-cache-dir', '/git-cache', 'foo'])
 
+    # Ensure we clean, as expected.
+    self.assertEqual(mock_clean.mock_calls,
+                     [mock.call('branch', '/buildroot')])
+
     # Ensure we checkout, as expected.
     self.assertEqual(mock_checkout.mock_calls,
                      [mock.call('branch', '/buildroot', '/git-cache')])
@@ -104,3 +114,62 @@
          '--buildroot', '/buildroot', '--branch', 'branch',
          '--git-cache-dir', '/git-cache', 'foo'],
         cwd='/buildroot', error_code_ok=True)
+
+
+class CleanBuildrootTest(cros_test_lib.TempDirTestCase):
+  """Tests for CleanBuildroot method."""
+
+  def setUp(self):
+    """Create standard buildroot contents for cleanup."""
+    self.state = os.path.join(self.tempdir, '.bootstrap_state')
+    self.repo = os.path.join(self.tempdir, '.repo/repo')
+    self.chroot = os.path.join(self.tempdir, 'chroot/chroot')
+    self.general = os.path.join(self.tempdir, 'general/general')
+
+  def populateBuildroot(self, state=None):
+    """Create standard buildroot contents for cleanup."""
+    if state:
+      osutils.WriteFile(self.state, state)
+
+    # Create files.
+    for f in (self.repo, self.chroot, self.general):
+      osutils.Touch(os.path.join(self.tempdir, f), makedirs=True)
+
+  def testBuildrootEmpty(self):
+    """Test CleanBuildroot with no history."""
+    bootstrap.CleanBuildroot(None, self.tempdir)
+
+    self.assertEqual(osutils.ReadFile(self.state), 'TOT')
+
+  def testBuildrootNoState(self):
+    """Test CleanBuildroot with no state information."""
+    self.populateBuildroot()
+
+    bootstrap.CleanBuildroot(None, self.tempdir)
+
+    self.assertEqual(osutils.ReadFile(self.state), 'TOT')
+    self.assertExists(self.repo)
+    self.assertNotExists(self.chroot)
+    self.assertExists(self.general)
+
+  def testBuildrootBranchChange(self):
+    """Test CleanBuildroot with a change in branches."""
+    self.populateBuildroot('branchA')
+
+    bootstrap.CleanBuildroot('branchB', self.tempdir)
+
+    self.assertEqual(osutils.ReadFile(self.state), 'branchB')
+    self.assertExists(self.repo)
+    self.assertNotExists(self.chroot)
+    self.assertExists(self.general)
+
+  def testBuildrootBranchMatch(self):
+    """Test CleanBuildroot with no change in branch."""
+    self.populateBuildroot('branchA')
+
+    bootstrap.CleanBuildroot('branchA', self.tempdir)
+
+    self.assertEqual(osutils.ReadFile(self.state), 'branchA')
+    self.assertExists(self.repo)
+    self.assertExists(self.chroot)
+    self.assertExists(self.general)