Reland (with fix) "Automatically pick a target directory which exists for run_web_tests.py script"

This is a reland of 5f74e5433d04e79587c8f452a0d6a5dc3c96ca05.

The original reland is in patch set 1, fixes are in subsequent patchset.

The fix implements:
If --release or --debug is specified and there are both more than one target dirs,
it would try to choose the dir that match the configuration on args.gn

Original change's description:
>Automatically pick a target directory which exists for run_web_tests.py script
>
> This also updates how we compute the default for 'configuration'. First, we try to look for the
> configuration definition (args.gn) & use that value. If that doesn't succeed, we guess the
> value of configuration using the target value.
>
> Reviewers: the main change is in third_party/blink/tools/blinkpy/web_tests/port/base.py, the rest are followed by.
>
>
> Bug: 893618
> Change-Id: I578c977bcaccd6294596f8cf7079748809698db6
> Reviewed-on: https://chromium-review.googlesource.com/c/1281043
> Reviewed-by: Dirk Pranke <dpranke@chromium.org>
> Reviewed-by: Robert Ma <robertma@chromium.org>
> Commit-Queue: Ned Nguyen <nednguyen@google.com>
> Cr-Commit-Position: refs/heads/master@{#600760}

Bug: 893618
Change-Id: I386b8261fc4abddd51a5b4f290c5eb3ac6ce5361
Reviewed-on: https://chromium-review.googlesource.com/c/1289308
Commit-Queue: Ned Nguyen <nednguyen@google.com>
Reviewed-by: Dirk Pranke <dpranke@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#601271}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 0d994d5998d6f901576bea2152e2099b4c800f57
diff --git a/blink/tools/blinkpy/web_tests/lint_test_expectations.py b/blink/tools/blinkpy/web_tests/lint_test_expectations.py
index 190ab13..0a55b6f 100644
--- a/blink/tools/blinkpy/web_tests/lint_test_expectations.py
+++ b/blink/tools/blinkpy/web_tests/lint_test_expectations.py
@@ -42,7 +42,8 @@
 
 
 def lint(host, options):
-    ports_to_lint = [host.port_factory.get(name) for name in host.port_factory.all_port_names(options.platform)]
+    ports_to_lint = [host.port_factory.get(name)
+                     for name in host.port_factory.all_port_names(options.platform)]
     files_linted = set()
 
     # In general, the set of TestExpectation files should be the same for
@@ -80,7 +81,7 @@
 
 
 def check_virtual_test_suites(host, options):
-    port = host.port_factory.get(options=options)
+    port = host.port_factory.get()
     fs = host.filesystem
     layout_tests_dir = port.layout_tests_dir()
     virtual_suites = port.virtual_test_suites()
@@ -99,7 +100,7 @@
 
 
 def check_smoke_tests(host, options):
-    port = host.port_factory.get(options=options)
+    port = host.port_factory.get()
     smoke_tests_file = host.filesystem.join(port.layout_tests_dir(), 'SmokeTests')
     failures = []
     if not host.filesystem.exists(smoke_tests_file):
diff --git a/blink/tools/blinkpy/web_tests/lint_test_expectations_unittest.py b/blink/tools/blinkpy/web_tests/lint_test_expectations_unittest.py
index 55952a0..34497d1 100644
--- a/blink/tools/blinkpy/web_tests/lint_test_expectations_unittest.py
+++ b/blink/tools/blinkpy/web_tests/lint_test_expectations_unittest.py
@@ -171,7 +171,7 @@
         host = MockHost()
         options = optparse.Values({'platform': 'test', 'debug_rwt_logging': False})
         orig_get = host.port_factory.get
-        host.port_factory.get = lambda options: orig_get('test', options=options)
+        host.port_factory.get = lambda : orig_get('test', options=options)
 
         res = lint_test_expectations.check_virtual_test_suites(host, options)
         self.assertTrue(res)
diff --git a/blink/tools/blinkpy/web_tests/port/android.py b/blink/tools/blinkpy/web_tests/port/android.py
index 632d46e..f96662c 100644
--- a/blink/tools/blinkpy/web_tests/port/android.py
+++ b/blink/tools/blinkpy/web_tests/port/android.py
@@ -242,12 +242,13 @@
 
     def __init__(self, host, port_name, **kwargs):
         _import_android_packages_if_necessary()
+        self._driver_details = ContentShellDriverDetails()
+        self._host_port = factory.PortFactory(host).get(**kwargs)
         super(AndroidPort, self).__init__(host, port_name, **kwargs)
 
         self._operating_system = 'android'
         self._version = 'kitkat'
 
-        self._host_port = factory.PortFactory(host).get(**kwargs)
         self.server_process_constructor = self._android_server_process_constructor
 
         if not self.get_option('disable_breakpad'):
@@ -256,7 +257,6 @@
         if self.driver_name() != self.CONTENT_SHELL_NAME:
             raise AssertionError('Layout tests on Android only support content_shell as the driver.')
 
-        self._driver_details = ContentShellDriverDetails()
 
         # Initialize the AndroidDevices class which tracks available devices.
         default_devices = None
diff --git a/blink/tools/blinkpy/web_tests/port/android_unittest.py b/blink/tools/blinkpy/web_tests/port/android_unittest.py
index ba4f5d1..1aae22d 100644
--- a/blink/tools/blinkpy/web_tests/port/android_unittest.py
+++ b/blink/tools/blinkpy/web_tests/port/android_unittest.py
@@ -94,7 +94,8 @@
 
     def test_check_build(self):
         host = MockSystemHost()
-        port = self.make_port(host=host, options=optparse.Values({'child_processes': 1}))
+        port = self.make_port(host=host, options=optparse.Values(
+            {'child_processes': 1, 'target': 'Release'}))
         # Checking the devices is not tested in this unit test.
         port._check_devices = lambda _: None
         host.filesystem.exists = lambda p: True
@@ -107,12 +108,14 @@
     # Test that content_shell currently is the only supported driver.
     def test_non_content_shell_driver(self):
         with self.assertRaises(Exception):
-            self.make_port(options=optparse.Values({'driver_name': 'foobar'}))
+          self.make_port(options=optparse.Values({'driver_name': 'foobar', 'target': 'Release'}))
 
     # Test that the number of child processes to create depends on the devices.
     def test_default_child_processes(self):
         port_default = self.make_port(device_count=5)
-        port_fixed_device = self.make_port(device_count=5, options=optparse.Values({'adb_devices': ['123456789ABCDEF9']}))
+        port_fixed_device = self.make_port(
+            device_count=5,
+            options=optparse.Values({'adb_devices': ['123456789ABCDEF9'], 'target': 'Release'}))
 
         self.assertEquals(6, port_default.default_child_processes())
         self.assertEquals(1, port_fixed_device.default_child_processes())
@@ -123,8 +126,8 @@
 
     # Tests the default timeouts for Android, which are different than the rest of Chromium.
     def test_default_timeout_ms(self):
-        self.assertEqual(self.make_port(options=optparse.Values({'configuration': 'Release'})).default_timeout_ms(), 10000)
-        self.assertEqual(self.make_port(options=optparse.Values({'configuration': 'Debug'})).default_timeout_ms(), 10000)
+        self.assertEqual(self.make_port(options=optparse.Values({'target': 'Release'})).default_timeout_ms(), 10000)
+        self.assertEqual(self.make_port(options=optparse.Values({'target': 'Debug'})).default_timeout_ms(), 10000)
 
     def test_path_to_apache_config_file(self):
         port = self.make_port()
@@ -149,7 +152,9 @@
             'devil.android.perf.perf_control.PerfControl')
         self._mock_perf_control.start()
 
-        self._port = android.AndroidPort(MockSystemHost(executive=MockExecutive()), 'android')
+        self._port = android.AndroidPort(
+            MockSystemHost(executive=MockExecutive()), 'android',
+            options=optparse.Values({'target': 'Release'}))
         self._driver = android.ChromiumAndroidDriver(
             self._port,
             worker_number=0,
@@ -199,7 +204,8 @@
         self._mock_perf_control.stop()
 
     def test_two_drivers(self):
-        port = android.AndroidPort(MockSystemHost(executive=MockExecutive()), 'android')
+        options = optparse.Values({'target': 'Release'})
+        port = android.AndroidPort(MockSystemHost(executive=MockExecutive()), 'android', options=options)
         driver0 = android.ChromiumAndroidDriver(port, worker_number=0,
                                                 driver_details=android.ContentShellDriverDetails(), android_devices=port._devices)
         driver1 = android.ChromiumAndroidDriver(port, worker_number=1,
@@ -235,10 +241,12 @@
     def test_options_with_two_ports(self):
         port0 = android.AndroidPort(
             MockSystemHost(executive=MockExecutive()), 'android',
-            options=optparse.Values({'additional_driver_flag': ['--foo=bar']}))
+            options=optparse.Values(
+                {'additional_driver_flag': ['--foo=bar'], 'target': 'Release'}))
         port1 = android.AndroidPort(
             MockSystemHost(executive=MockExecutive()), 'android',
-            options=optparse.Values({'driver_name': 'content_shell'}))
+            options=optparse.Values(
+                {'driver_name': 'content_shell', 'target': 'Release'}))
 
         self.assertEqual(1, port0.driver_cmd_line().count('--foo=bar'))
         self.assertEqual(0, port1.driver_cmd_line().count('--create-stdin-fifo'))
@@ -258,7 +266,9 @@
             return_value={'level': 100})
         self._mock_battery.start()
 
-        self._port = android.AndroidPort(MockSystemHost(executive=MockExecutive()), 'android')
+        self._port = android.AndroidPort(
+            MockSystemHost(executive=MockExecutive()), 'android',
+            options=optparse.Values({'target': 'Release'}))
         self._driver = android.ChromiumAndroidDriver(
             self._port,
             worker_number=0,
diff --git a/blink/tools/blinkpy/web_tests/port/base.py b/blink/tools/blinkpy/web_tests/port/base.py
index 36b7eb9..5c1570d 100644
--- a/blink/tools/blinkpy/web_tests/port/base.py
+++ b/blink/tools/blinkpy/web_tests/port/base.py
@@ -58,6 +58,8 @@
 from blinkpy.web_tests.port import driver
 from blinkpy.web_tests.port import server_process
 from blinkpy.web_tests.port.factory import PortFactory
+from blinkpy.web_tests.port.factory import check_configuration_and_target
+from blinkpy.web_tests.port.factory import read_configuration_from_gn
 from blinkpy.web_tests.servers import apache_http
 from blinkpy.web_tests.servers import pywebsocket
 from blinkpy.web_tests.servers import wptserve
@@ -217,11 +219,10 @@
         self.server_process_constructor = server_process.ServerProcess  # This can be overridden for testing.
         self._http_lock = None  # FIXME: Why does this live on the port object?
         self._dump_reader = None
-
-        if not hasattr(options, 'configuration') or not options.configuration:
-            self.set_option_default('configuration', self.default_configuration())
         if not hasattr(options, 'target') or not options.target:
-            self.set_option_default('target', self._options.configuration)
+            self.set_option_default('target', self.default_target())
+        self.check_configuration_and_target()
+
         self._test_configuration = None
         self._reftest_list = {}
         self._results_directory = None
@@ -1448,8 +1449,29 @@
         """Returns the repository path for the chromium code base."""
         return self._path_from_chromium_base('build')
 
-    def default_configuration(self):
-        return 'Release'
+    def default_target(self):
+      configuration = getattr(self._options, 'configuration', None)
+      runnable_targets = []
+      for possible_target in ('Release', 'Default', 'Debug'):
+          if not self._filesystem.exists(self._path_to_driver(possible_target)):
+              continue
+          if (configuration is None or configuration ==
+              read_configuration_from_gn(self._filesystem, self._options, possible_target)):
+              runnable_targets.append(possible_target)
+      if len(runnable_targets) == 0:
+          _log.error('Driver cannot be found in common build directories.')
+          sys.exit(1)
+      elif len(runnable_targets) == 1:
+          _log.info('Automatically picked a target: %s', runnable_targets[0])
+          return runnable_targets[0]
+      else:
+          _log.error('Multiple runnable targets are found: %s. '
+                     'Specify the exact target with --target flag.',
+                     runnable_targets)
+          sys.exit(1)
+
+    def check_configuration_and_target(self):
+      check_configuration_and_target(self._filesystem, self._options)
 
     def clobber_old_port_specific_results(self):
         pass
@@ -1753,7 +1775,10 @@
 
     def _build_path(self, *comps):
         """Returns a path from the build directory."""
-        return self._build_path_with_target(self._options.target, *comps)
+        target = None
+        if 'target' in comps:
+          target = comps.pop('target')
+        return self._build_path_with_target(target, *comps)
 
     def _build_path_with_target(self, target, *comps):
         target = target or self.get_option('target')
diff --git a/blink/tools/blinkpy/web_tests/port/base_unittest.py b/blink/tools/blinkpy/web_tests/port/base_unittest.py
index a77d830..1c49edd 100644
--- a/blink/tools/blinkpy/web_tests/port/base_unittest.py
+++ b/blink/tools/blinkpy/web_tests/port/base_unittest.py
@@ -49,8 +49,10 @@
 
 class PortTest(LoggingTestCase):
 
-    def make_port(self, executive=None, with_tests=False, port_name=None, **kwargs):
-        host = MockSystemHost()
+    def make_port(self, executive=None, with_tests=False, port_name=None, host=None, **kwargs):
+        if not 'options' in kwargs:
+            kwargs['options'] = optparse.Values({'target': 'Release', 'configuration': 'Release'})
+        host = host or MockSystemHost()
         if executive:
             host.executive = executive
         if with_tests:
@@ -58,6 +60,18 @@
             return TestPort(host, **kwargs)
         return Port(host, port_name or 'baseport', **kwargs)
 
+    def make_target_dir(self, host, target, configuration):
+        temp_port = self.make_port(
+            options=optparse.Values({'target': target, 'configuration': target}))
+        fake_driver_path = temp_port._path_to_driver()
+        host.filesystem.write_binary_file(fake_driver_path, 'DeathStar')
+        fake_arg_gn_path = host.filesystem.normpath(
+            host.filesystem.join(fake_driver_path, '..', 'args.gn'))
+        if configuration == 'Debug':
+            host.filesystem.write_text_file(fake_arg_gn_path, 'is_debug=true')
+        else:
+            host.filesystem.write_text_file(fake_arg_gn_path, 'is_debug=false')
+
     def test_setup_test_run(self):
         port = self.make_port()
         # This routine is a no-op. We just test it for coverage.
@@ -74,6 +88,7 @@
     def test_get_option__set(self):
         options, _ = optparse.OptionParser().parse_args([])
         options.foo = 'bar'
+        options.target = 'Release'
         port = self.make_port(options=options)
         self.assertEqual(port.get_option('foo'), 'bar')
 
@@ -85,6 +100,31 @@
         port = self.make_port()
         self.assertEqual(port.get_option('foo', 'bar'), 'bar')
 
+    def test_get_option__default_target(self):
+        host = MockSystemHost()
+        self.make_target_dir(host, target='Default', configuration='Release')
+        options, _ = optparse.OptionParser().parse_args([])
+        port = self.make_port(host=host, options=options)
+        self.assertEqual(port.get_option('target'), 'Default')
+
+    def test_get_option__default_configuration(self):
+        host = MockSystemHost()
+        self.make_target_dir(host, target='Default', configuration='Debug')
+        options, _ = optparse.OptionParser().parse_args([])
+        port = self.make_port(host=host, options=options)
+        self.assertEqual(port.get_option('target'), 'Default')
+        self.assertEqual(port.get_option('configuration'), 'Debug')
+
+    def test_get_option__default_target_when_configuration_specified(self):
+        host = MockSystemHost()
+        self.make_target_dir(host, target='Default', configuration='Debug')
+        self.make_target_dir(host, target='Release', configuration='Release')
+        options = optparse.Values({'configuration': 'Release'})
+        port = self.make_port(host=host, options=options)
+        self.assertEqual(port.get_option('target'), 'Release')
+        self.assertEqual(port.get_option('configuration'), 'Release')
+
+
     def test_output_filename(self):
         port = self.make_port()
 
@@ -365,11 +405,13 @@
         # --additional-driver-flag. additional_driver_flags() excludes primary_driver_flag().
 
         port_a = self.make_port(options=optparse.Values(
-            {'additional_driver_flag': []}))
+            {'additional_driver_flag': [], 'target': 'Release', 'configuration': 'Release'}))
         port_b = self.make_port(options=optparse.Values(
-            {'additional_driver_flag': ['--bb']}))
+            {'additional_driver_flag': ['--bb'], 'target': 'Release',
+             'configuration': 'Release'}))
         port_c = self.make_port(options=optparse.Values(
-            {'additional_driver_flag': ['--bb', '--cc']}))
+            {'additional_driver_flag': ['--bb', '--cc'], 'target': 'Release',
+             'configuration': 'Release'}))
 
         self.assertEqual(port_a.primary_driver_flag(), None)
         self.assertEqual(port_b.primary_driver_flag(), '--bb')
@@ -396,7 +438,9 @@
                          ['--cc'] + default_flags)
 
     def test_additional_env_var(self):
-        port = self.make_port(options=optparse.Values({'additional_env_var': ['FOO=BAR', 'BAR=FOO']}))
+        port = self.make_port(options=optparse.Values(
+            {'additional_env_var': ['FOO=BAR', 'BAR=FOO'], 'target': 'Release',
+             'configuration': 'Release'}))
         self.assertEqual(port.get_option('additional_env_var'), ['FOO=BAR', 'BAR=FOO'])
         environment = port.setup_environ_for_server()
         self.assertTrue(('FOO' in environment) & ('BAR' in environment))
@@ -604,12 +648,14 @@
 
     def test_should_run_as_pixel_test_with_no_pixel_tests_in_args(self):
         # With the --no-pixel-tests flag, no tests should run as pixel tests.
-        options = optparse.Values({'pixel_tests': False})
+        options = optparse.Values(
+            {'pixel_tests': False, 'target': 'Release', 'configuration': 'Release'})
         port = self.make_port(options=options)
         self.assertFalse(port.should_run_as_pixel_test('fast/css/001.html'))
 
     def test_should_run_as_pixel_test_default(self):
-        options = optparse.Values({'pixel_tests': True})
+        options = optparse.Values(
+            {'pixel_tests': True, 'target': 'Release', 'configuration': 'Release'})
         port = self.make_port(options=options)
         self.assertFalse(port.should_run_as_pixel_test('external/wpt/dom/interfaces.html'))
         self.assertFalse(port.should_run_as_pixel_test('virtual/a-name/external/wpt/dom/interfaces.html'))
@@ -791,11 +837,13 @@
     def test_build_path(self):
         # Test for a protected method - pylint: disable=protected-access
         # Test that optional paths are used regardless of whether they exist.
-        options = optparse.Values({'configuration': 'Release', 'build_directory': 'xcodebuild'})
+        options = optparse.Values({'target': 'Release', 'build_directory': 'xcodebuild',
+                                   'configuration': 'Release'})
         self.assertEqual(self.make_port(options=options)._build_path(), '/mock-checkout/xcodebuild/Release')
 
         # Test that "out" is used as the default.
-        options = optparse.Values({'configuration': 'Release', 'build_directory': None})
+        options = optparse.Values({'target': 'Release', 'build_directory': None,
+                                   'configuration': 'Release'})
         self.assertEqual(self.make_port(options=options)._build_path(), '/mock-checkout/out/Release')
 
     def test_dont_require_http_server(self):
@@ -803,7 +851,8 @@
         self.assertEqual(port.requires_http_server(), False)
 
     def test_can_load_actual_virtual_test_suite_file(self):
-        port = Port(SystemHost(), 'baseport')
+        port = Port(SystemHost(), 'baseport', options=optparse.Values(
+            {'target': 'Release', 'configuration': 'Release'}))
 
         # If this call returns successfully, we found and loaded the LayoutTests/VirtualTestSuites.
         _ = port.virtual_test_suites()
@@ -845,7 +894,10 @@
         self.assertEqual(port.default_results_directory(), '/mock-checkout/out/Default/layout-test-results')
 
     def test_results_directory(self):
-        port = self.make_port(options=optparse.Values({'results_directory': 'some-directory/results'}))
+        options = options=optparse.Values({
+            'results_directory': 'some-directory/results', 'target': 'Release',
+            'configuration': 'Release'})
+        port = self.make_port(options=options)
         # A results directory can be given as an option, and it is relative to current working directory.
         self.assertEqual(port.host.filesystem.cwd, '/')
         self.assertEqual(port.results_directory(), '/some-directory/results')
diff --git a/blink/tools/blinkpy/web_tests/port/browser_test_unittest.py b/blink/tools/blinkpy/web_tests/port/browser_test_unittest.py
index 71e9f80..5d8bf62 100644
--- a/blink/tools/blinkpy/web_tests/port/browser_test_unittest.py
+++ b/blink/tools/blinkpy/web_tests/port/browser_test_unittest.py
@@ -42,14 +42,15 @@
         self.assertTrue(self.make_port()._path_to_driver().endswith(self.driver_name_endswith))
 
     def test_default_timeout_ms(self):
-        self.assertEqual(self.make_port(options=optparse.Values({'configuration': 'Release'})).default_timeout_ms(),
-                         self.timeout_ms)
-        self.assertEqual(self.make_port(options=optparse.Values({'configuration': 'Debug'})).default_timeout_ms(),
-                         3 * self.timeout_ms)
+        options = optparse.Values({'target': 'Release', 'configuration': 'Release'})
+        self.assertEqual(self.make_port(options=options).default_timeout_ms(), self.timeout_ms)
+        options = optparse.Values({'target': 'Debug', 'configuration': 'Debug'})
+        self.assertEqual(self.make_port(options=options).default_timeout_ms(), 3 * self.timeout_ms)
 
     def test_driver_type(self):
-        self.assertTrue(isinstance(self.make_port(options=optparse.Values({'driver_name': 'browser_tests'})
-                                                  ).create_driver(1), browser_test_driver.BrowserTestDriver))
+        port = self.make_port(options=optparse.Values(
+            {'driver_name': 'browser_tests', 'target': 'Release', 'configuration': 'Release'}))
+        self.assertTrue(isinstance(port.create_driver(1), browser_test_driver.BrowserTestDriver))
 
     def test_layout_tests_dir(self):
         self.assertTrue(self.make_port().layout_tests_dir().endswith('chrome/test/data/printing/layout_tests'))
@@ -90,5 +91,7 @@
     timeout_ms = 20 * 1000
 
     def test_driver_path(self):
-        test_port = self.make_port(options=optparse.Values({'driver_name': 'browser_tests'}))
+        options = optparse.Values(
+            {'driver_name': 'browser_tests', 'target': 'Release', 'configuration': 'Release'})
+        test_port = self.make_port(options=options)
         self.assertNotIn('.app/Contents/MacOS', test_port._path_to_driver())
diff --git a/blink/tools/blinkpy/web_tests/port/driver_unittest.py b/blink/tools/blinkpy/web_tests/port/driver_unittest.py
index 964deaf..5228bde 100644
--- a/blink/tools/blinkpy/web_tests/port/driver_unittest.py
+++ b/blink/tools/blinkpy/web_tests/port/driver_unittest.py
@@ -38,7 +38,8 @@
 class DriverTest(unittest.TestCase):
 
     def make_port(self):
-        return Port(MockSystemHost(), 'test', optparse.Values({'configuration': 'Release'}))
+        return Port(MockSystemHost(), 'test',
+                    optparse.Values({'configuration': 'Release', 'target': 'Release'}))
 
     def _assert_wrapper(self, wrapper_string, expected_wrapper):
         wrapper = Driver(self.make_port(), None)._command_wrapper(wrapper_string)
diff --git a/blink/tools/blinkpy/web_tests/port/factory.py b/blink/tools/blinkpy/web_tests/port/factory.py
index 0aeaa03..c9ae358 100644
--- a/blink/tools/blinkpy/web_tests/port/factory.py
+++ b/blink/tools/blinkpy/web_tests/port/factory.py
@@ -59,15 +59,19 @@
             return 'win'
         raise NotImplementedError('unknown platform: %s' % platform)
 
+    def _default_options(self):
+        return optparse.Values({'configuration': 'Release', 'target': 'Release'})
+
     def get(self, port_name=None, options=None, **kwargs):
         """Returns an object implementing the Port interface.
 
         If port_name is None, this routine attempts to guess at the most
         appropriate port on this platform.
         """
+        options = options or self._default_options()
         port_name = port_name or self._default_port()
 
-        _check_configuration_and_target(self._host.filesystem, options)
+        check_configuration_and_target(self._host.filesystem, options)
 
         if 'browser_test' in port_name:
             module_name, class_name = port_name.rsplit('.', 1)
@@ -130,28 +134,29 @@
 
 
 def _builder_options(builder_name):
+    configuration = 'Debug' if re.search(r'[d|D](ebu|b)g', builder_name) else 'Release'
     return optparse.Values({
         'builder_name': builder_name,
-        'configuration': 'Debug' if re.search(r'[d|D](ebu|b)g', builder_name) else 'Release',
-        'target': None,
+        'configuration': configuration,
+        'target': configuration,
     })
 
 
-def _check_configuration_and_target(host, options):
+def check_configuration_and_target(host, options):
     """Updates options.configuration based on options.target."""
     if not options or not getattr(options, 'target', None):
         return
 
-    gn_configuration = _read_configuration_from_gn(host, options)
+    gn_configuration = read_configuration_from_gn(host, options)
     if gn_configuration:
-        expected_configuration = getattr(options, 'configuration')
+        expected_configuration = getattr(options, 'configuration', None)
         if expected_configuration not in (None, gn_configuration):
             raise ValueError('Configuration does not match the GN build args. '
                              'Expected "%s" but got "%s".' % (gn_configuration, expected_configuration))
         options.configuration = gn_configuration
         return
 
-    if options.target in ('Debug', 'Debug_x64'):
+    if options.target in ('Default', 'Debug', 'Debug_x64'):
         options.configuration = 'Debug'
     elif options.target in ('Release', 'Release_x64'):
         options.configuration = 'Release'
@@ -162,10 +167,10 @@
                          'If the directory is out/<dir>, then pass -t <dir>.')
 
 
-def _read_configuration_from_gn(fs, options):
+def read_configuration_from_gn(fs, options, target=None):
     """Returns the configuration to used based on args.gn, if possible."""
     build_directory = getattr(options, 'build_directory', 'out')
-    target = options.target
+    target = target or options.target
     finder = PathFinder(fs)
     path = fs.join(finder.chromium_base(), build_directory, target, 'args.gn')
     if not fs.exists(path):
diff --git a/blink/tools/blinkpy/web_tests/port/factory_unittest.py b/blink/tools/blinkpy/web_tests/port/factory_unittest.py
index 2e241d3..aba6def 100644
--- a/blink/tools/blinkpy/web_tests/port/factory_unittest.py
+++ b/blink/tools/blinkpy/web_tests/port/factory_unittest.py
@@ -45,7 +45,7 @@
     # instead of passing generic "options".
 
     def setUp(self):
-        self.webkit_options = optparse.Values({'pixel_tests': False})
+      self.webkit_options = optparse.Values({'pixel_tests': False, 'configuration': 'Release'})
 
     def assert_port(self, port_name=None, os_name=None, os_version=None, options=None, cls=None):
         host = MockHost(os_name=os_name, os_version=os_version)
@@ -98,32 +98,35 @@
         return factory.PortFactory(host).get(options=options)
 
     def test_default_target_and_configuration(self):
-        port = self.get_port()
-        self.assertEqual(port._options.configuration, 'Release')
-        self.assertEqual(port._options.target, 'Release')
+        # Generate a fake 'content shell' binary in 'Debug' target
+        temp_port = self.get_port(target='Debug', configuration='Debug')
+        path_to_fake_driver = temp_port._path_to_driver()
+        port = self.get_port(files={path_to_fake_driver: 'blah'})
+        self.assertEqual(port._options.configuration, 'Debug')
+        self.assertEqual(port._options.target, 'Debug')
 
     def test_debug_configuration(self):
-        port = self.get_port(configuration='Debug')
+        port = self.get_port(target='Debug', configuration='Debug')
         self.assertEqual(port._options.configuration, 'Debug')
         self.assertEqual(port._options.target, 'Debug')
 
     def test_release_configuration(self):
-        port = self.get_port(configuration='Release')
+        port = self.get_port(target='Release', configuration='Release')
         self.assertEqual(port._options.configuration, 'Release')
         self.assertEqual(port._options.target, 'Release')
 
     def test_debug_target(self):
-        port = self.get_port(target='Debug')
+        port = self.get_port(target='Debug', configuration='Debug')
         self.assertEqual(port._options.configuration, 'Debug')
         self.assertEqual(port._options.target, 'Debug')
 
     def test_debug_x64_target(self):
-        port = self.get_port(target='Debug_x64')
+        port = self.get_port(target='Debug_x64', configuration='Debug')
         self.assertEqual(port._options.configuration, 'Debug')
         self.assertEqual(port._options.target, 'Debug_x64')
 
     def test_release_x64_target(self):
-        port = self.get_port(target='Release_x64')
+        port = self.get_port(target='Release_x64', configuration='Release')
         self.assertEqual(port._options.configuration, 'Release')
         self.assertEqual(port._options.target, 'Release_x64')
 
@@ -158,7 +161,7 @@
 
     def test_unknown_dir(self):
         with self.assertRaises(ValueError):
-            self.get_port(target='unknown')
+            self.get_port(target='unknown', configuration='Debug')
 
     def test_both_configuration_and_target_is_an_error(self):
         with self.assertRaises(ValueError):
diff --git a/blink/tools/blinkpy/web_tests/port/linux_unittest.py b/blink/tools/blinkpy/web_tests/port/linux_unittest.py
index 7619944..6bfcf74 100644
--- a/blink/tools/blinkpy/web_tests/port/linux_unittest.py
+++ b/blink/tools/blinkpy/web_tests/port/linux_unittest.py
@@ -79,14 +79,17 @@
         # FIXME: Check that, for now, these are illegal port names.
         # Eventually we should be able to do the right thing here.
         with self.assertRaises(AssertionError):
-            linux.LinuxPort(MockSystemHost(), port_name='linux-x86')
+          linux.LinuxPort(
+              MockSystemHost(), port_name='linux-x86',
+              options=optparse.Values({'configuration': 'Release', 'target': 'Release'}))
 
     def test_operating_system(self):
         self.assertEqual('linux', self.make_port().operating_system())
 
     def test_driver_name_option(self):
         self.assertTrue(self.make_port()._path_to_driver().endswith('content_shell'))
-        port = self.make_port(options=optparse.Values({'driver_name': 'OtherDriver'}))
+        port = self.make_port(options=optparse.Values(
+          {'driver_name': 'OtherDriver', 'configuration': 'Release', 'target': 'Release'}))
         self.assertTrue(port._path_to_driver().endswith('OtherDriver'))  # pylint: disable=protected-access
 
     def test_path_to_image_diff(self):
diff --git a/blink/tools/blinkpy/web_tests/port/mac_unittest.py b/blink/tools/blinkpy/web_tests/port/mac_unittest.py
index 7cf62d3..55fbdff 100644
--- a/blink/tools/blinkpy/web_tests/port/mac_unittest.py
+++ b/blink/tools/blinkpy/web_tests/port/mac_unittest.py
@@ -48,7 +48,8 @@
 
     def test_driver_name_option(self):
         self.assertTrue(self.make_port()._path_to_driver().endswith('Content Shell'))
-        port = self.make_port(options=optparse.Values(dict(driver_name='OtherDriver')))
+        port = self.make_port(options=optparse.Values(
+            {'driver_name': 'OtherDriver', 'configuration': 'Release', 'target': 'Release'}))
         self.assertTrue(port._path_to_driver().endswith('OtherDriver'))  # pylint: disable=protected-access
 
     def test_path_to_image_diff(self):
diff --git a/blink/tools/blinkpy/web_tests/port/mock_drt_unittest.py b/blink/tools/blinkpy/web_tests/port/mock_drt_unittest.py
index 4103cae..944fe41 100644
--- a/blink/tools/blinkpy/web_tests/port/mock_drt_unittest.py
+++ b/blink/tools/blinkpy/web_tests/port/mock_drt_unittest.py
@@ -41,7 +41,8 @@
 
 class MockDRTPortTest(port_testcase.PortTestCase):
 
-    def make_port(self, host=None, options=optparse.Values({'configuration': 'Release'})):
+    def make_port(self, host=None,
+                  options=optparse.Values({'configuration': 'Release', 'target': 'Release'})):
         host = host or MockSystemHost()
         test.add_unit_tests_to_mock_filesystem(host.filesystem)
         return mock_drt.MockDRTPort(host, port_name='mock-mac', options=options)
diff --git a/blink/tools/blinkpy/web_tests/port/port_testcase.py b/blink/tools/blinkpy/web_tests/port/port_testcase.py
index 8628e69..e9d0415 100644
--- a/blink/tools/blinkpy/web_tests/port/port_testcase.py
+++ b/blink/tools/blinkpy/web_tests/port/port_testcase.py
@@ -66,7 +66,9 @@
 
     def make_port(self, host=None, port_name=None, options=None, os_name=None, os_version=None, **kwargs):
         host = host or MockSystemHost(os_name=(os_name or self.os_name), os_version=(os_version or self.os_version))
-        options = options or optparse.Values({'configuration': 'Release'})
+        options = options or optparse.Values(
+            {'target': 'Release', 'configuration': 'Release'})
+
         port_name = port_name or self.port_name
         port_name = self.port_maker.determine_full_port_name(host, options, port_name)
         return self.port_maker(host, port_name, options=options, **kwargs)
@@ -117,14 +119,18 @@
         self.assertEqual(port.default_max_locked_shards(), 1)
 
     def test_default_timeout_ms(self):
-        self.assertEqual(self.make_port(options=optparse.Values({'configuration': 'Release'})).default_timeout_ms(), 6000)
-        self.assertEqual(self.make_port(options=optparse.Values({'configuration': 'Debug'})).default_timeout_ms(), 18000)
+        options = optparse.Values({'configuration': 'Release', 'target': 'Release'})
+        self.assertEqual(self.make_port(options=options).default_timeout_ms(), 6000)
+        options = optparse.Values({'configuration': 'Debug', 'target': 'Debug'})
+        self.assertEqual(self.make_port(options=options).default_timeout_ms(), 18000)
 
     def test_driver_cmd_line(self):
         port = self.make_port()
         self.assertTrue(len(port.driver_cmd_line()))
 
-        options = optparse.Values(dict(additional_driver_flag=['--foo=bar', '--foo=baz']))
+        options = optparse.Values({
+            'additional_driver_flag': ['--foo=bar', '--foo=baz'],
+            'configuration': 'Release', 'target': 'Release'})
         port = self.make_port(options=options)
         cmd_line = port.driver_cmd_line()
         self.assertTrue('--foo=bar' in cmd_line)
@@ -261,7 +267,9 @@
         ordered_dict = port.expectations_dict()
         self.assertEqual(port.path_to_generic_test_expectations_file(), ordered_dict.keys()[0])
 
-        options = optparse.Values(dict(additional_expectations=['/tmp/foo', '/tmp/bar']))
+        options = optparse.Values(
+            {'additional_expectations': ['/tmp/foo', '/tmp/bar'],
+             'configuration': 'Release', 'target': 'Release'})
         port = self.make_port(options=options)
         for path in port.expectations_files():
             port.host.filesystem.write_text_file(path, '')
@@ -294,7 +302,9 @@
         self.assertEqual(port.path_to_apache_config_file(), '/existing/httpd.conf')
 
     def test_additional_platform_directory(self):
-        port = self.make_port(options=optparse.Values(dict(additional_platform_directory=['/tmp/foo'])))
+        port = self.make_port(options=optparse.Values(
+            {'additional_platform_directory': ['/tmp/foo'],
+             'configuration': 'Release', 'target': 'Release'}))
         self.assertEqual(port.baseline_search_path()[0], '/tmp/foo')
 
     def test_virtual_test_suites(self):
diff --git a/blink/tools/blinkpy/web_tests/port/test.py b/blink/tools/blinkpy/web_tests/port/test.py
index 24ef1d7..cc7a63b 100644
--- a/blink/tools/blinkpy/web_tests/port/test.py
+++ b/blink/tools/blinkpy/web_tests/port/test.py
@@ -447,9 +447,10 @@
             'linux': ['precise', 'trusty']
         }
 
-    def _path_to_driver(self):
+    def _path_to_driver(self, target=None):
         # This routine shouldn't normally be called, but it is called by
         # the mock_drt Driver. We return something, but make sure it's useless.
+        del target  # Unused
         return 'MOCK _path_to_driver'
 
     def default_child_processes(self):
@@ -461,9 +462,13 @@
     def check_sys_deps(self, needs_http):
         return exit_codes.OK_EXIT_STATUS
 
-    def default_configuration(self):
+    def default_target(self):
         return 'Release'
 
+    def check_configuration_and_target(self):
+        if not getattr(self._options, 'configuration', None):
+             self._options.configuration = 'Release'
+
     def diff_image(self, expected_contents, actual_contents):
         diffed = actual_contents != expected_contents
         if not actual_contents and not expected_contents:
diff --git a/blink/tools/blinkpy/web_tests/port/win_unittest.py b/blink/tools/blinkpy/web_tests/port/win_unittest.py
index 5c73f13..7cb1653 100644
--- a/blink/tools/blinkpy/web_tests/port/win_unittest.py
+++ b/blink/tools/blinkpy/web_tests/port/win_unittest.py
@@ -105,8 +105,9 @@
 
     def test_driver_name_option(self):
         self.assertTrue(self.make_port()._path_to_driver().endswith('content_shell.exe'))
-        self.assertTrue(
-            self.make_port(options=optparse.Values({'driver_name': 'OtherDriver'}))._path_to_driver().endswith('OtherDriver.exe'))
+        port = self.make_port(options=optparse.Values(
+            {'driver_name': 'OtherDriver', 'configuration': 'Release', 'target': 'Release'}))
+        self.assertTrue(port._path_to_driver().endswith('OtherDriver.exe'))
 
     def test_path_to_image_diff(self):
         self.assertEqual(self.make_port()._path_to_image_diff(), '/mock-checkout/out/Release/image_diff.exe')
diff --git a/blink/tools/blinkpy/web_tests/run_webkit_tests.py b/blink/tools/blinkpy/web_tests/run_webkit_tests.py
index a3f3f9e..05d8306 100644
--- a/blink/tools/blinkpy/web_tests/run_webkit_tests.py
+++ b/blink/tools/blinkpy/web_tests/run_webkit_tests.py
@@ -548,7 +548,7 @@
             'WEBKIT_TEST_MAX_LOCKED_SHARDS', str(port.default_max_locked_shards())))
 
     if not options.configuration:
-        options.configuration = port.default_configuration()
+        options.configuration = 'Release'
 
     if not options.time_out_ms:
         options.time_out_ms = str(port.default_timeout_ms())
diff --git a/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py b/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py
index 5ba2ed8..c546496 100644
--- a/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py
+++ b/blink/tools/blinkpy/web_tests/run_webkit_tests_unittest.py
@@ -1622,7 +1622,7 @@
         stderr = StringIO.StringIO()
         try:
             run_webkit_tests.run = interrupting_run
-            res = run_webkit_tests.main([], stderr)
+            res = run_webkit_tests.main(['--target', 'Release'], stderr)
             self.assertEqual(res, exit_codes.INTERRUPTED_EXIT_STATUS)
 
             run_webkit_tests.run = successful_run
@@ -1630,7 +1630,7 @@
             self.assertEqual(res, exit_codes.UNEXPECTED_ERROR_EXIT_STATUS)
 
             run_webkit_tests.run = exception_raising_run
-            res = run_webkit_tests.main([], stderr)
+            res = run_webkit_tests.main(['--target', 'Release'], stderr)
             self.assertEqual(res, exit_codes.UNEXPECTED_ERROR_EXIT_STATUS)
         finally:
             run_webkit_tests.run = orig_run_fn
diff --git a/blink/tools/blinkpy/web_tests/servers/cli_wrapper.py b/blink/tools/blinkpy/web_tests/servers/cli_wrapper.py
index dd6d965..a0a3581 100644
--- a/blink/tools/blinkpy/web_tests/servers/cli_wrapper.py
+++ b/blink/tools/blinkpy/web_tests/servers/cli_wrapper.py
@@ -61,6 +61,9 @@
     logger.setLevel(logging.DEBUG if options.verbose else logging.INFO)
 
     host = Host()
+    # Constructing a port requires explicitly setting the configuration & target.
+    options.configuration = 'Release'
+    options.target = 'Release'
     port_obj = host.port_factory.get(options=options)
     if not options.output_dir:
         options.output_dir = port_obj.default_results_directory()