Clone of chromium aad1ce808763f59c7a3753e08f1500a104ecc6fd refs/remotes/origin/HEAD
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
new file mode 100644
index 0000000..add74cb
--- /dev/null
+++ b/base/test/BUILD.gn
@@ -0,0 +1,173 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/ui.gni")
+
+if (is_android) {
+  import("//build/config/android/rules.gni")
+}
+
+# GYP: //base/base.gyp:test_support_base
+source_set("test_support") {
+  # TODO http://crbug.com/412064 enable this flag all the time.
+  testonly = !is_component_build
+  sources = [
+    "expectations/expectation.cc",
+    "expectations/expectation.h",
+    "expectations/parser.cc",
+    "expectations/parser.h",
+    "gtest_xml_util.cc",
+    "gtest_xml_util.h",
+    "histogram_tester.cc",
+    "histogram_tester.h",
+    "launcher/test_launcher.cc",
+    "launcher/test_launcher.h",
+    "launcher/test_result.cc",
+    "launcher/test_result.h",
+    "launcher/test_results_tracker.cc",
+    "launcher/test_results_tracker.h",
+    "launcher/unit_test_launcher.cc",
+    "launcher/unit_test_launcher.h",
+    "launcher/unit_test_launcher_ios.cc",
+    "mock_chrome_application_mac.h",
+    "mock_chrome_application_mac.mm",
+    "mock_devices_changed_observer.cc",
+    "mock_devices_changed_observer.h",
+    "mock_time_provider.cc",
+    "mock_time_provider.h",
+    "multiprocess_test.cc",
+    "multiprocess_test.h",
+    "multiprocess_test_android.cc",
+    "null_task_runner.cc",
+    "null_task_runner.h",
+    "perf_log.cc",
+    "perf_log.h",
+    "perf_test_suite.cc",
+    "perf_test_suite.h",
+    "perf_time_logger.cc",
+    "perf_time_logger.h",
+    "power_monitor_test_base.cc",
+    "power_monitor_test_base.h",
+    "scoped_locale.cc",
+    "scoped_locale.h",
+    "scoped_path_override.cc",
+    "scoped_path_override.h",
+    "sequenced_task_runner_test_template.cc",
+    "sequenced_task_runner_test_template.h",
+    "sequenced_worker_pool_owner.cc",
+    "sequenced_worker_pool_owner.h",
+    "simple_test_clock.cc",
+    "simple_test_clock.h",
+    "simple_test_tick_clock.cc",
+    "simple_test_tick_clock.h",
+    "task_runner_test_template.cc",
+    "task_runner_test_template.h",
+    "test_file_util.cc",
+    "test_file_util.h",
+    "test_file_util_android.cc",
+    "test_file_util_linux.cc",
+    "test_file_util_mac.cc",
+    "test_file_util_posix.cc",
+    "test_file_util_win.cc",
+    "test_io_thread.cc",
+    "test_io_thread.h",
+    "test_listener_ios.h",
+    "test_listener_ios.mm",
+    "test_pending_task.cc",
+    "test_pending_task.h",
+    "test_reg_util_win.cc",
+    "test_reg_util_win.h",
+    "test_shortcut_win.cc",
+    "test_shortcut_win.h",
+    "test_simple_task_runner.cc",
+    "test_simple_task_runner.h",
+    "test_suite.cc",
+    "test_suite.h",
+    "test_support_android.cc",
+    "test_support_android.h",
+    "test_support_ios.h",
+    "test_support_ios.mm",
+    "test_switches.cc",
+    "test_switches.h",
+    "test_timeouts.cc",
+    "test_timeouts.h",
+    "thread_test_helper.cc",
+    "thread_test_helper.h",
+    "trace_event_analyzer.cc",
+    "trace_event_analyzer.h",
+    "trace_to_file.cc",
+    "trace_to_file.h",
+    "values_test_util.cc",
+    "values_test_util.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//base:i18n",
+    "//base:base_static",
+  ]
+  deps = [
+    "//base/third_party/dynamic_annotations",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/libxml"
+  ]
+
+  if (!is_posix) {
+    sources -= [
+      "scoped_locale.cc",
+      "scoped_locale.h",
+    ]
+  }
+  if (is_ios) {
+    # iOS uses its own unit test launcher.
+    sources -= [ "launcher/unit_test_launcher.cc" ]
+
+    # Pull in specific Mac files for iOS (which have been filtered out
+    # by file name rules).
+    set_sources_assignment_filter([])
+    sources += [ "test_file_util_mac.cc" ]
+  }
+
+  if (is_android) {
+    deps += [ ":base_unittests_jni_headers" ]
+  }
+}
+
+config("perf_test_config") {
+  defines = [ "PERF_TEST" ]
+}
+
+source_set("test_support_perf") {
+  testonly = true
+  sources = [
+    "run_all_perftests.cc",
+  ]
+  deps = [
+    ":test_support",
+    "//base",
+    "//testing/gtest",
+  ]
+
+  public_configs = [ ":perf_test_config" ]
+}
+
+source_set("run_all_unittests") {
+  testonly = true
+  sources = [
+    "run_all_unittests.cc",
+  ]
+  deps = [
+    ":test_support",
+  ]
+}
+
+if (is_android) {
+  generate_jni("base_unittests_jni_headers") {
+    sources = [
+      "android/java/src/org/chromium/base/ContentUriTestUtils.java",
+    ]
+    jni_package = "base"
+  }
+}
diff --git a/base/test/DEPS b/base/test/DEPS
new file mode 100644
index 0000000..5827c26
--- /dev/null
+++ b/base/test/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+third_party/libxml",
+]
diff --git a/base/test/OWNERS b/base/test/OWNERS
new file mode 100644
index 0000000..92ecc88
--- /dev/null
+++ b/base/test/OWNERS
@@ -0,0 +1 @@
+phajdan.jr@chromium.org
diff --git a/base/test/android/OWNERS b/base/test/android/OWNERS
new file mode 100644
index 0000000..25c1d80
--- /dev/null
+++ b/base/test/android/OWNERS
@@ -0,0 +1,2 @@
+nyquist@chromium.org
+yfriedman@chromium.org
diff --git a/base/test/android/java/src/org/chromium/base/ContentUriTestUtils.java b/base/test/android/java/src/org/chromium/base/ContentUriTestUtils.java
new file mode 100644
index 0000000..4a1613b
--- /dev/null
+++ b/base/test/android/java/src/org/chromium/base/ContentUriTestUtils.java
@@ -0,0 +1,48 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.MediaStore;
+
+/**
+ * Utilities for testing operations on content URI.
+ */
+public class ContentUriTestUtils {
+    /**
+     * Insert an image into the MediaStore, and return the content URI. If the
+     * image already exists in the MediaStore, just retrieve the URI.
+     *
+     * @param context Application context.
+     * @param path Path to the image file.
+     * @return Content URI of the image.
+     */
+    @CalledByNative
+    private static String insertImageIntoMediaStore(Context context, String path) {
+        // Check whether the content URI exists.
+        Cursor c = context.getContentResolver().query(
+                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+                new String[] { MediaStore.Video.VideoColumns._ID },
+                MediaStore.Images.Media.DATA + " LIKE ?",
+                new String[] { path },
+                null);
+        if (c != null && c.getCount() > 0) {
+            c.moveToFirst();
+            int id = c.getInt(0);
+            return Uri.withAppendedPath(
+                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + id).toString();
+        }
+
+        // Insert the content URI into MediaStore.
+        ContentValues values = new ContentValues();
+        values.put(MediaStore.MediaColumns.DATA, path);
+        Uri uri = context.getContentResolver().insert(
+                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+        return uri.toString();
+    }
+}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/AdvancedMockContext.java b/base/test/android/javatests/src/org/chromium/base/test/util/AdvancedMockContext.java
new file mode 100644
index 0000000..c8117f7
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/AdvancedMockContext.java
@@ -0,0 +1,118 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import android.content.ComponentCallbacks;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.SharedPreferences;
+import android.test.mock.MockContentResolver;
+import android.test.mock.MockContext;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * ContextWrapper that adds functionality for SharedPreferences and a way to set and retrieve flags.
+ */
+public class AdvancedMockContext extends ContextWrapper {
+
+    private final MockContentResolver mMockContentResolver = new MockContentResolver();
+
+    private final Map<String, SharedPreferences> mSharedPreferences =
+            new HashMap<String, SharedPreferences>();
+
+    private final Map<String, Boolean> mFlags = new HashMap<String, Boolean>();
+
+    public AdvancedMockContext(Context base) {
+        super(base);
+    }
+
+    public AdvancedMockContext() {
+        super(new MockContext());
+    }
+
+    @Override
+    public String getPackageName() {
+        return getBaseContext().getPackageName();
+    }
+
+    @Override
+    public Context getApplicationContext() {
+        return this;
+    }
+
+    @Override
+    public ContentResolver getContentResolver() {
+        return mMockContentResolver;
+    }
+
+    public MockContentResolver getMockContentResolver() {
+        return mMockContentResolver;
+    }
+
+    @Override
+    public SharedPreferences getSharedPreferences(String name, int mode) {
+        synchronized (mSharedPreferences) {
+            if (!mSharedPreferences.containsKey(name)) {
+                // Auto-create shared preferences to mimic Android Context behavior
+                mSharedPreferences.put(name, new InMemorySharedPreferences());
+            }
+            return mSharedPreferences.get(name);
+        }
+    }
+
+    @Override
+    public void registerComponentCallbacks(ComponentCallbacks callback) {
+        getBaseContext().registerComponentCallbacks(callback);
+    }
+
+    @Override
+    public void unregisterComponentCallbacks(ComponentCallbacks callback) {
+        getBaseContext().unregisterComponentCallbacks(callback);
+    }
+
+    public void addSharedPreferences(String name, Map<String, Object> data) {
+        synchronized (mSharedPreferences) {
+            mSharedPreferences.put(name, new InMemorySharedPreferences(data));
+        }
+    }
+
+    public void setFlag(String key) {
+        mFlags.put(key, true);
+    }
+
+    public void clearFlag(String key) {
+        mFlags.remove(key);
+    }
+
+    public boolean isFlagSet(String key) {
+        return mFlags.containsKey(key) && mFlags.get(key);
+    }
+
+    /**
+     * Builder for maps of type Map<String, Object> to be used with
+     * {@link #addSharedPreferences(String, java.util.Map)}.
+     */
+    public static class MapBuilder {
+
+        private final Map<String, Object> mData = new HashMap<String, Object>();
+
+        public static MapBuilder create() {
+            return new MapBuilder();
+        }
+
+        public MapBuilder add(String key, Object value) {
+            mData.put(key, value);
+            return this;
+        }
+
+        public Map<String, Object> build() {
+            return mData;
+        }
+
+    }
+}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/DisabledTest.java b/base/test/android/javatests/src/org/chromium/base/test/util/DisabledTest.java
new file mode 100644
index 0000000..0dfb4be
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/DisabledTest.java
@@ -0,0 +1,21 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation is for disabled tests.
+ * <p>
+ * Tests with this annotation will not be run on any of the normal bots.
+ * Please note that they might eventually run on a special bot.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DisabledTest {
+}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/EnormousTest.java b/base/test/android/javatests/src/org/chromium/base/test/util/EnormousTest.java
new file mode 100644
index 0000000..af483ec
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/EnormousTest.java
@@ -0,0 +1,24 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation is for enormous tests.
+ * <p>
+ * Examples of enormous tests are tests that depend on external web sites or
+ * tests that are long running.
+ * <p>
+ * Such tests are likely NOT reliable enough to run on tree closing bots and
+ * should only be run on FYI bots.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EnormousTest {
+}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/Feature.java b/base/test/android/javatests/src/org/chromium/base/test/util/Feature.java
new file mode 100644
index 0000000..1bc9226
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/Feature.java
@@ -0,0 +1,29 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The java instrumentation tests are normally fairly large (in terms of
+ * dependencies), and the test suite ends up containing a large amount of
+ * tests that are not trivial to filter / group just by their names.
+ * Instead, we use this annotation: each test should be annotated as:
+ *     @Feature({"Foo", "Bar"})
+ * in order for the test runner scripts to be able to filter and group
+ * them accordingly (for instance, this enable us to run all tests that exercise
+ * feature Foo).
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Feature {
+    /**
+     * @return A list of feature names.
+     */
+    public String[] value();
+}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/HostDrivenTest.java b/base/test/android/javatests/src/org/chromium/base/test/util/HostDrivenTest.java
new file mode 100644
index 0000000..b52fb2c
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/HostDrivenTest.java
@@ -0,0 +1,22 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation is for host-driven tests.
+ * <p>
+ * Tests with these annotations are run explicitly by HostDrivenTestCase-derived
+ * python tests on the host and are excluded from regular instrumentation test runs.
+ * <p>
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface HostDrivenTest {
+}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/InMemorySharedPreferences.java b/base/test/android/javatests/src/org/chromium/base/test/util/InMemorySharedPreferences.java
new file mode 100644
index 0000000..2587d72
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/InMemorySharedPreferences.java
@@ -0,0 +1,238 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import android.content.SharedPreferences;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An implementation of SharedPreferences that can be used in tests.
+ * <p/>
+ * It keeps all state in memory, and there is no difference between apply() and commit().
+ */
+public class InMemorySharedPreferences implements SharedPreferences {
+
+    // Guarded on its own monitor.
+    private final Map<String, Object> mData;
+
+    public InMemorySharedPreferences() {
+        mData = new HashMap<String, Object>();
+    }
+
+    public InMemorySharedPreferences(Map<String, Object> data) {
+        mData = data;
+    }
+
+    @Override
+    public Map<String, ?> getAll() {
+        synchronized (mData) {
+            return Collections.unmodifiableMap(mData);
+        }
+    }
+
+    @Override
+    public String getString(String key, String defValue) {
+        synchronized (mData) {
+            if (mData.containsKey(key)) {
+                return (String) mData.get(key);
+            }
+        }
+        return defValue;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Set<String> getStringSet(String key, Set<String> defValues) {
+        synchronized (mData) {
+            if (mData.containsKey(key)) {
+                return Collections.unmodifiableSet((Set<String>) mData.get(key));
+            }
+        }
+        return defValues;
+    }
+
+    @Override
+    public int getInt(String key, int defValue) {
+        synchronized (mData) {
+            if (mData.containsKey(key)) {
+                return (Integer) mData.get(key);
+            }
+        }
+        return defValue;
+    }
+
+    @Override
+    public long getLong(String key, long defValue) {
+        synchronized (mData) {
+            if (mData.containsKey(key)) {
+                return (Long) mData.get(key);
+            }
+        }
+        return defValue;
+    }
+
+    @Override
+    public float getFloat(String key, float defValue) {
+        synchronized (mData) {
+            if (mData.containsKey(key)) {
+                return (Float) mData.get(key);
+            }
+        }
+        return defValue;
+    }
+
+    @Override
+    public boolean getBoolean(String key, boolean defValue) {
+        synchronized (mData) {
+            if (mData.containsKey(key)) {
+                return (Boolean) mData.get(key);
+            }
+        }
+        return defValue;
+    }
+
+    @Override
+    public boolean contains(String key) {
+        synchronized (mData) {
+            return mData.containsKey(key);
+        }
+    }
+
+    @Override
+    public SharedPreferences.Editor edit() {
+        return new InMemoryEditor();
+    }
+
+    @Override
+    public void registerOnSharedPreferenceChangeListener(
+            SharedPreferences.OnSharedPreferenceChangeListener
+                    listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void unregisterOnSharedPreferenceChangeListener(
+            SharedPreferences.OnSharedPreferenceChangeListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    private class InMemoryEditor implements SharedPreferences.Editor {
+
+        // All guarded by |mChanges|
+        private boolean mClearCalled;
+        private volatile boolean mApplyCalled;
+        private final Map<String, Object> mChanges = new HashMap<String, Object>();
+
+        @Override
+        public SharedPreferences.Editor putString(String key, String value) {
+            synchronized (mChanges) {
+                if (mApplyCalled) throw new IllegalStateException();
+                mChanges.put(key, value);
+                return this;
+            }
+        }
+
+        @Override
+        public SharedPreferences.Editor putStringSet(String key, Set<String> values) {
+            synchronized (mChanges) {
+                if (mApplyCalled) throw new IllegalStateException();
+                mChanges.put(key, values);
+                return this;
+            }
+        }
+
+        @Override
+        public SharedPreferences.Editor putInt(String key, int value) {
+            synchronized (mChanges) {
+                if (mApplyCalled) throw new IllegalStateException();
+                mChanges.put(key, value);
+                return this;
+            }
+        }
+
+        @Override
+        public SharedPreferences.Editor putLong(String key, long value) {
+            synchronized (mChanges) {
+                if (mApplyCalled) throw new IllegalStateException();
+                mChanges.put(key, value);
+                return this;
+            }
+        }
+
+        @Override
+        public SharedPreferences.Editor putFloat(String key, float value) {
+            synchronized (mChanges) {
+                if (mApplyCalled) throw new IllegalStateException();
+                mChanges.put(key, value);
+                return this;
+            }
+        }
+
+        @Override
+        public SharedPreferences.Editor putBoolean(String key, boolean value) {
+            synchronized (mChanges) {
+                if (mApplyCalled) throw new IllegalStateException();
+                mChanges.put(key, value);
+                return this;
+            }
+        }
+
+        @Override
+        public SharedPreferences.Editor remove(String key) {
+            synchronized (mChanges) {
+                if (mApplyCalled) throw new IllegalStateException();
+                // Magic value for removes
+                mChanges.put(key, this);
+                return this;
+            }
+        }
+
+        @Override
+        public SharedPreferences.Editor clear() {
+            synchronized (mChanges) {
+                if (mApplyCalled) throw new IllegalStateException();
+                mClearCalled = true;
+                return this;
+            }
+        }
+
+        @Override
+        public boolean commit() {
+            apply();
+            return true;
+        }
+
+        @Override
+        public void apply() {
+            synchronized (mData) {
+                synchronized (mChanges) {
+                    if (mApplyCalled) throw new IllegalStateException();
+                    if (mClearCalled) {
+                        mData.clear();
+                    }
+                    for (Map.Entry<String, Object> entry : mChanges.entrySet()) {
+                        String key = entry.getKey();
+                        Object value = entry.getValue();
+                        if (value == this) {
+                            // Special value for removal
+                            mData.remove(key);
+                        } else {
+                            mData.put(key, value);
+                        }
+                    }
+                    // The real shared prefs clears out the temporaries allowing the caller to
+                    // reuse the Editor instance, however this is undocumented behavior and subtle
+                    // to read, so instead we just ban any future use of this instance.
+                    mApplyCalled = true;
+                }
+            }
+        }
+    }
+
+}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/InstrumentationUtils.java b/base/test/android/javatests/src/org/chromium/base/test/util/InstrumentationUtils.java
new file mode 100644
index 0000000..20cfd9d
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/InstrumentationUtils.java
@@ -0,0 +1,32 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import android.app.Instrumentation;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
+/**
+ * Utility methods built around the android.app.Instrumentation class.
+ */
+public final class InstrumentationUtils {
+
+    private InstrumentationUtils() {
+    }
+
+    public static <R> R runOnMainSyncAndGetResult(Instrumentation instrumentation,
+            Callable<R> callable) throws Throwable {
+        FutureTask<R> task = new FutureTask<R>(callable);
+        instrumentation.runOnMainSync(task);
+        try {
+            return task.get();
+        } catch (ExecutionException e) {
+            // Unwrap the cause of the exception and re-throw it.
+            throw e.getCause();
+        }
+    }
+}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/IntegrationTest.java b/base/test/android/javatests/src/org/chromium/base/test/util/IntegrationTest.java
new file mode 100644
index 0000000..8b6550d
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/IntegrationTest.java
@@ -0,0 +1,26 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation is for integration tests.
+ * <p>
+ * Examples of integration tests are tests that rely on real instances of the
+ * application's services and components (e.g. Search) to test the system as
+ * a whole. These tests may use additional command-line flags to configure the
+ * existing backends to use.
+ * <p>
+ * Such tests are likely NOT reliable enough to run on tree closing bots and
+ * should only be run on FYI bots.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface IntegrationTest {
+}
\ No newline at end of file
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/MinAndroidSdkLevel.java b/base/test/android/javatests/src/org/chromium/base/test/util/MinAndroidSdkLevel.java
new file mode 100644
index 0000000..d7c45e7
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/MinAndroidSdkLevel.java
@@ -0,0 +1,19 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface MinAndroidSdkLevel {
+    int value() default 0;
+}
+
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/Restriction.java b/base/test/android/javatests/src/org/chromium/base/test/util/Restriction.java
new file mode 100644
index 0000000..11026ef
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/Restriction.java
@@ -0,0 +1,37 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation for listing restrictions for a test method. For example, if a test method is only
+ * applicable on a phone with small memory:
+ *     @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_SMALL_MEMORY})
+ * Test classes are free to define restrictions and enforce them using reflection at runtime.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Restriction {
+    /** Specifies the test is only valid on phone form factors. */
+    public static final String RESTRICTION_TYPE_PHONE = "Phone";
+
+    /** Specifies the test is only valid on tablet form factors. */
+    public static final String RESTRICTION_TYPE_TABLET = "Tablet";
+
+    /** Specifies the test is only valid on low end devices that have less memory. */
+    public static final String RESTRICTION_TYPE_LOW_END_DEVICE = "Low_End_Device";
+
+    /** Specifies the test is only valid on non-low end devices. */
+    public static final String RESTRICTION_TYPE_NON_LOW_END_DEVICE = "Non_Low_End_Device";
+
+    /**
+     * @return A list of restrictions.
+     */
+    public String[] value();
+}
\ No newline at end of file
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/ScalableTimeout.java b/base/test/android/javatests/src/org/chromium/base/test/util/ScalableTimeout.java
new file mode 100644
index 0000000..c21bff9
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/ScalableTimeout.java
@@ -0,0 +1,28 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+/**
+ * Utility class for scaling various timeouts by a common factor.
+ * For example, to run tests under Valgrind, you might want the following:
+ *   adb shell "echo 20.0 > /data/local/tmp/chrome_timeout_scale"
+ */
+public class ScalableTimeout {
+    private static Double sTimeoutScale = null;
+    private static final String PROPERTY_FILE = "/data/local/tmp/chrome_timeout_scale";
+
+    public static long scaleTimeout(long timeout) {
+        if (sTimeoutScale == null) {
+            try {
+                char[] data = TestFileUtil.readUtf8File(PROPERTY_FILE, 32);
+                sTimeoutScale = Double.parseDouble(new String(data));
+            } catch (Exception e) {
+                // NumberFormatException, FileNotFoundException, IOException
+                sTimeoutScale = 1.0;
+            }
+        }
+        return (long) (timeout * sTimeoutScale);
+    }
+}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/TestFileUtil.java b/base/test/android/javatests/src/org/chromium/base/test/util/TestFileUtil.java
new file mode 100644
index 0000000..ca6e73b
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/TestFileUtil.java
@@ -0,0 +1,78 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Arrays;
+
+/**
+ * Utility class for dealing with files for test.
+ */
+public class TestFileUtil {
+    public static void createNewHtmlFile(String name, String title, String body)
+            throws IOException {
+        File file = new File(name);
+        if (!file.createNewFile()) {
+            throw new IOException("File \"" + name + "\" already exists");
+        }
+
+        Writer writer = null;
+        try {
+            writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
+            writer.write("<html><meta charset=\"UTF-8\" />" +
+                         "<head><title>" + title + "</title></head>" +
+                         "<body>" +
+                         (body != null ? body : "") +
+                         "</body>" +
+                         "</html>");
+        } finally {
+            if (writer != null) {
+                writer.close();
+            }
+        }
+    }
+
+    public static void deleteFile(String name) {
+        File file = new File(name);
+        boolean deleted = file.delete();
+        assert (deleted || !file.exists());
+    }
+
+    /**
+     * @param fileName the file to read in.
+     * @param sizeLimit cap on the file size: will throw an exception if exceeded
+     * @return Array of chars read from the file
+     * @throws FileNotFoundException file does not exceed
+     * @throws IOException error encountered accessing the file
+     */
+    public static char[] readUtf8File(String fileName, int sizeLimit) throws
+            FileNotFoundException, IOException {
+        Reader reader = null;
+        try {
+            File f = new File(fileName);
+            if (f.length() > sizeLimit) {
+                throw new IOException("File " + fileName + " length " + f.length() +
+                        " exceeds limit " + sizeLimit);
+            }
+            char[] buffer = new char[(int) f.length()];
+            reader = new InputStreamReader(new FileInputStream(f), "UTF-8");
+            int charsRead = reader.read(buffer);
+            // Debug check that we've exhausted the input stream (will fail e.g. if the
+            // file grew after we inspected its length).
+            assert !reader.ready();
+            return charsRead < buffer.length ? Arrays.copyOfRange(buffer, 0, charsRead) : buffer;
+        } finally {
+            if (reader != null) reader.close();
+        }
+    }
+}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/TestThread.java b/base/test/android/javatests/src/org/chromium/base/test/util/TestThread.java
new file mode 100644
index 0000000..93c23f7
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/TestThread.java
@@ -0,0 +1,143 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This class is usefull when writing instrumentation tests that exercise code that posts tasks
+ * (to the same thread).
+ * Since the test code is run in a single thread, the posted tasks are never executed.
+ * The TestThread class lets you run that code on a specific thread synchronously and flush the
+ * message loop on that thread.
+ *
+ * Example of test using this:
+ *
+ * public void testMyAwesomeClass() {
+ *   TestThread testThread = new TestThread();
+ *   testThread.startAndWaitForReadyState();
+ *
+ *   testThread.runOnTestThreadSyncAndProcessPendingTasks(new Runnable() {
+ *       @Override
+ *       public void run() {
+ *           MyAwesomeClass.doStuffAsync();
+ *       }
+ *   });
+ *   // Once we get there we know doStuffAsync has been executed and all the tasks it posted.
+ *   assertTrue(MyAwesomeClass.stuffWasDone());
+ * }
+ *
+ * Notes:
+ * - this is only for tasks posted to the same thread. Anyway if you were posting to a different
+ *   thread, you'd probably need to set that other thread up.
+ * - this only supports tasks posted using Handler.post(), it won't work with postDelayed and
+ *   postAtTime.
+ * - if your test instanciates an object and that object is the one doing the posting of tasks, you
+ *   probably want to instanciate it on the test thread as it might create the Handler it posts
+ *   tasks to in the constructor.
+ */
+
+public class TestThread extends Thread {
+    private Object mThreadReadyLock;
+    private AtomicBoolean mThreadReady;
+    private Handler mMainThreadHandler;
+    private Handler mTestThreadHandler;
+
+    public TestThread() {
+        mMainThreadHandler = new Handler();
+        // We can't use the AtomicBoolean as the lock or findbugs will freak out...
+        mThreadReadyLock = new Object();
+        mThreadReady = new AtomicBoolean();
+    }
+
+    @Override
+    public void run() {
+        Looper.prepare();
+        mTestThreadHandler = new Handler();
+        mTestThreadHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                synchronized (mThreadReadyLock) {
+                    mThreadReady.set(true);
+                    mThreadReadyLock.notify();
+                }
+            }
+        });
+        Looper.loop();
+    }
+
+    /**
+     * Starts this TestThread and blocks until it's ready to accept calls.
+     */
+    public void startAndWaitForReadyState() {
+        checkOnMainThread();
+        start();
+        synchronized (mThreadReadyLock) {
+            try {
+                // Note the mThreadReady and while are not really needed.
+                // There are there so findbugs don't report warnings.
+                while (!mThreadReady.get()) {
+                    mThreadReadyLock.wait();
+                }
+            } catch (InterruptedException ie) {
+                System.err.println("Error starting TestThread.");
+                ie.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * Runs the passed Runnable synchronously on the TestThread and returns when all pending
+     * runnables have been excuted.
+     * Should be called from the main thread.
+     */
+    public void runOnTestThreadSyncAndProcessPendingTasks(Runnable r) {
+        checkOnMainThread();
+
+        runOnTestThreadSync(r);
+
+        // Run another task, when it's done it means all pendings tasks have executed.
+        runOnTestThreadSync(null);
+    }
+
+    /**
+     * Runs the passed Runnable on the test thread and blocks until it has finished executing.
+     * Should be called from the main thread.
+     * @param r The runnable to be executed.
+     */
+    public void runOnTestThreadSync(final Runnable r) {
+        checkOnMainThread();
+        final Object lock = new Object();
+        // Task executed is not really needed since we are only on one thread, it is here to appease
+        // findbugs.
+        final AtomicBoolean taskExecuted = new AtomicBoolean();
+        mTestThreadHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (r != null) r.run();
+                synchronized (lock) {
+                    taskExecuted.set(true);
+                    lock.notify();
+                }
+            }
+        });
+        synchronized (lock) {
+            try {
+                while (!taskExecuted.get()) {
+                    lock.wait();
+                }
+            } catch (InterruptedException ie) {
+                ie.printStackTrace();
+            }
+        }
+    }
+
+    private void checkOnMainThread() {
+        assert Looper.myLooper() == mMainThreadHandler.getLooper();
+    }
+}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/TimeoutScale.java b/base/test/android/javatests/src/org/chromium/base/test/util/TimeoutScale.java
new file mode 100644
index 0000000..5aee05e
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/TimeoutScale.java
@@ -0,0 +1,22 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation can be used to scale a specific test timeout.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface TimeoutScale {
+    /**
+     * @return A number to scale the test timeout.
+     */
+    public int value();
+}
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/UrlUtils.java b/base/test/android/javatests/src/org/chromium/base/test/util/UrlUtils.java
new file mode 100644
index 0000000..09e1fd6
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/UrlUtils.java
@@ -0,0 +1,53 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test.util;
+
+import junit.framework.Assert;
+
+import org.chromium.base.PathUtils;
+
+/**
+ * Collection of URL utilities.
+ */
+public class UrlUtils {
+    private static final String DATA_DIR = "/chrome/test/data/";
+
+    /**
+     * Construct the full path of a test data file.
+     * @param path Pathname relative to external/chrome/testing/data
+     */
+    public static String getTestFilePath(String path) {
+        return PathUtils.getExternalStorageDirectory() + DATA_DIR + path;
+    }
+
+    /**
+     * Construct a suitable URL for loading a test data file.
+     * @param path Pathname relative to external/chrome/testing/data
+     */
+    public static String getTestFileUrl(String path) {
+        return "file://" + getTestFilePath(path);
+    }
+
+    /**
+     * Construct a data:text/html URI for loading from an inline HTML.
+     * @param html An unencoded HTML
+     * @return String An URI that contains the given HTML
+     */
+    public static String encodeHtmlDataUri(String html) {
+        try {
+            // URLEncoder encodes into application/x-www-form-encoded, so
+            // ' '->'+' needs to be undone and replaced with ' '->'%20'
+            // to match the Data URI requirements.
+            String encoded =
+                    "data:text/html;utf-8," +
+                    java.net.URLEncoder.encode(html, "UTF-8");
+            encoded = encoded.replace("+", "%20");
+            return encoded;
+        } catch (java.io.UnsupportedEncodingException e) {
+            Assert.fail("Unsupported encoding: " + e.getMessage());
+            return null;
+        }
+    }
+}
diff --git a/base/test/data/file_util/binary_file.bin b/base/test/data/file_util/binary_file.bin
new file mode 100644
index 0000000..f53cc82
--- /dev/null
+++ b/base/test/data/file_util/binary_file.bin
Binary files differ
diff --git a/base/test/data/file_util/binary_file_diff.bin b/base/test/data/file_util/binary_file_diff.bin
new file mode 100644
index 0000000..103b26d
--- /dev/null
+++ b/base/test/data/file_util/binary_file_diff.bin
Binary files differ
diff --git a/base/test/data/file_util/binary_file_same.bin b/base/test/data/file_util/binary_file_same.bin
new file mode 100644
index 0000000..f53cc82
--- /dev/null
+++ b/base/test/data/file_util/binary_file_same.bin
Binary files differ
diff --git a/base/test/data/file_util/blank_line.txt b/base/test/data/file_util/blank_line.txt
new file mode 100644
index 0000000..8892069
--- /dev/null
+++ b/base/test/data/file_util/blank_line.txt
@@ -0,0 +1,3 @@
+The next line is blank.
+
+But this one isn't.
diff --git a/base/test/data/file_util/blank_line_crlf.txt b/base/test/data/file_util/blank_line_crlf.txt
new file mode 100644
index 0000000..3aefe52
--- /dev/null
+++ b/base/test/data/file_util/blank_line_crlf.txt
@@ -0,0 +1,3 @@
+The next line is blank.

+

+But this one isn't.

diff --git a/base/test/data/file_util/crlf.txt b/base/test/data/file_util/crlf.txt
new file mode 100644
index 0000000..0e62728
--- /dev/null
+++ b/base/test/data/file_util/crlf.txt
@@ -0,0 +1 @@
+This file is the same.

diff --git a/base/test/data/file_util/different.txt b/base/test/data/file_util/different.txt
new file mode 100644
index 0000000..5b9f9c4
--- /dev/null
+++ b/base/test/data/file_util/different.txt
@@ -0,0 +1 @@
+This file is different.
diff --git a/base/test/data/file_util/different_first.txt b/base/test/data/file_util/different_first.txt
new file mode 100644
index 0000000..8661d66
--- /dev/null
+++ b/base/test/data/file_util/different_first.txt
@@ -0,0 +1 @@
+this file is the same.
diff --git a/base/test/data/file_util/different_last.txt b/base/test/data/file_util/different_last.txt
new file mode 100644
index 0000000..e8b3e5a
--- /dev/null
+++ b/base/test/data/file_util/different_last.txt
@@ -0,0 +1 @@
+This file is the same. 
\ No newline at end of file
diff --git a/base/test/data/file_util/empty1.txt b/base/test/data/file_util/empty1.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/test/data/file_util/empty1.txt
diff --git a/base/test/data/file_util/empty2.txt b/base/test/data/file_util/empty2.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/test/data/file_util/empty2.txt
diff --git a/base/test/data/file_util/first1.txt b/base/test/data/file_util/first1.txt
new file mode 100644
index 0000000..2c6e300
--- /dev/null
+++ b/base/test/data/file_util/first1.txt
@@ -0,0 +1,2 @@
+The first line is the same.
+The second line is different.
diff --git a/base/test/data/file_util/first2.txt b/base/test/data/file_util/first2.txt
new file mode 100644
index 0000000..e39b5ec
--- /dev/null
+++ b/base/test/data/file_util/first2.txt
@@ -0,0 +1,2 @@
+The first line is the same.
+The second line is not.
diff --git a/base/test/data/file_util/original.txt b/base/test/data/file_util/original.txt
new file mode 100644
index 0000000..4422f57
--- /dev/null
+++ b/base/test/data/file_util/original.txt
@@ -0,0 +1 @@
+This file is the same.
diff --git a/base/test/data/file_util/red.png b/base/test/data/file_util/red.png
new file mode 100644
index 0000000..0806141
--- /dev/null
+++ b/base/test/data/file_util/red.png
Binary files differ
diff --git a/base/test/data/file_util/same.txt b/base/test/data/file_util/same.txt
new file mode 100644
index 0000000..4422f57
--- /dev/null
+++ b/base/test/data/file_util/same.txt
@@ -0,0 +1 @@
+This file is the same.
diff --git a/base/test/data/file_util/same_length.txt b/base/test/data/file_util/same_length.txt
new file mode 100644
index 0000000..157405c
--- /dev/null
+++ b/base/test/data/file_util/same_length.txt
@@ -0,0 +1 @@
+This file is not same.
diff --git a/base/test/data/file_util/shortened.txt b/base/test/data/file_util/shortened.txt
new file mode 100644
index 0000000..2bee82c
--- /dev/null
+++ b/base/test/data/file_util/shortened.txt
@@ -0,0 +1 @@
+This file is the
\ No newline at end of file
diff --git a/base/test/data/file_version_info_unittest/FileVersionInfoTest1.dll b/base/test/data/file_version_info_unittest/FileVersionInfoTest1.dll
new file mode 100755
index 0000000..bdf8dc0
--- /dev/null
+++ b/base/test/data/file_version_info_unittest/FileVersionInfoTest1.dll
Binary files differ
diff --git a/base/test/data/file_version_info_unittest/FileVersionInfoTest2.dll b/base/test/data/file_version_info_unittest/FileVersionInfoTest2.dll
new file mode 100755
index 0000000..51e7966
--- /dev/null
+++ b/base/test/data/file_version_info_unittest/FileVersionInfoTest2.dll
Binary files differ
diff --git a/base/test/data/json/bom_feff.json b/base/test/data/json/bom_feff.json
new file mode 100644
index 0000000..b05ae50
--- /dev/null
+++ b/base/test/data/json/bom_feff.json
@@ -0,0 +1,10 @@
+{

+  "appName": {

+    "message": "Gmail",

+    "description": "App name."

+  },

+  "appDesc": {

+    "message": "بريد إلكتروني يوفر إمكانية البحث مع مقدار أقل من الرسائل غير المرغوب فيها.", 

+    "description":"App description."

+  }

+}
\ No newline at end of file
diff --git a/base/test/data/prefs/invalid.json b/base/test/data/prefs/invalid.json
new file mode 100644
index 0000000..43392a9
--- /dev/null
+++ b/base/test/data/prefs/invalid.json
@@ -0,0 +1 @@
+!@#$%^&
\ No newline at end of file
diff --git a/base/test/data/prefs/read.json b/base/test/data/prefs/read.json
new file mode 100644
index 0000000..ea578a4
--- /dev/null
+++ b/base/test/data/prefs/read.json
@@ -0,0 +1,8 @@
+{
+  "homepage": "http://www.cnn.com",
+  "some_directory": "/usr/local/",
+  "tabs": {
+    "new_windows_in_tabs": true,
+    "max_tabs": 20
+  }
+}
diff --git a/base/test/data/prefs/write.golden.json b/base/test/data/prefs/write.golden.json
new file mode 100644
index 0000000..9a5523c
--- /dev/null
+++ b/base/test/data/prefs/write.golden.json
@@ -0,0 +1,11 @@
+{
+   "homepage": "http://www.cnn.com",
+   "long_int": {
+      "pref": "214748364842"
+   },
+   "some_directory": "/usr/sbin/",
+   "tabs": {
+      "max_tabs": 10,
+      "new_windows_in_tabs": false
+   }
+}
diff --git a/base/test/data/serializer_nested_test.json b/base/test/data/serializer_nested_test.json
new file mode 100644
index 0000000..cfea8e8
--- /dev/null
+++ b/base/test/data/serializer_nested_test.json
@@ -0,0 +1,17 @@
+{
+   "bool": true,
+   "dict": {
+      "bool": true,
+      "dict": {
+         "bees": "knees",
+         "cats": "meow"
+      },
+      "foos": "bar",
+      "list": [ 3.4, "second", null ]
+   },
+   "int": 42,
+   "list": [ 1, 2 ],
+   "null": null,
+   "real": 3.14,
+   "string": "hello"
+}
diff --git a/base/test/data/serializer_test.json b/base/test/data/serializer_test.json
new file mode 100644
index 0000000..446925e
--- /dev/null
+++ b/base/test/data/serializer_test.json
@@ -0,0 +1,8 @@
+{
+   "bool": true,
+   "int": 42,
+   "list": [ 1, 2 ],
+   "null": null,
+   "real": 3.14,
+   "string": "hello"
+}
diff --git a/base/test/data/serializer_test_nowhitespace.json b/base/test/data/serializer_test_nowhitespace.json
new file mode 100644
index 0000000..a1afdc5
--- /dev/null
+++ b/base/test/data/serializer_test_nowhitespace.json
@@ -0,0 +1 @@
+{"bool":true,"int":42,"list":[1,2],"null":null,"real":3.14,"string":"hello"}
\ No newline at end of file
diff --git a/base/test/expectations/OWNERS b/base/test/expectations/OWNERS
new file mode 100644
index 0000000..14fce2a
--- /dev/null
+++ b/base/test/expectations/OWNERS
@@ -0,0 +1 @@
+rsesek@chromium.org
diff --git a/base/test/expectations/expectation.cc b/base/test/expectations/expectation.cc
new file mode 100644
index 0000000..9b06e28
--- /dev/null
+++ b/base/test/expectations/expectation.cc
@@ -0,0 +1,161 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/expectations/expectation.h"
+
+#include "base/logging.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+#include "base/mac/mac_util.h"
+#elif defined(OS_LINUX)
+#include "base/sys_info.h"
+#endif
+
+namespace test_expectations {
+
+bool ResultFromString(const base::StringPiece& result, Result* out_result) {
+  if (result == "Failure")
+    *out_result = RESULT_FAILURE;
+  else if (result == "Timeout")
+    *out_result = RESULT_TIMEOUT;
+  else if (result == "Crash")
+    *out_result = RESULT_CRASH;
+  else if (result == "Skip")
+    *out_result = RESULT_SKIP;
+  else if (result == "Pass")
+    *out_result = RESULT_PASS;
+  else
+    return false;
+
+  return true;
+}
+
+static bool IsValidPlatform(const Platform* platform) {
+  const std::string& name = platform->name;
+  const std::string& variant = platform->variant;
+
+  if (name == "Win") {
+    if (variant != "" &&
+        variant != "XP" &&
+        variant != "Vista" &&
+        variant != "7" &&
+        variant != "8") {
+      return false;
+    }
+  } else if (name == "Mac") {
+    if (variant != "" &&
+        variant != "10.6" &&
+        variant != "10.7" &&
+        variant != "10.8" &&
+        variant != "10.9" &&
+        variant != "10.10") {
+      return false;
+    }
+  } else if (name == "Linux") {
+    if (variant != "" &&
+        variant != "32" &&
+        variant != "64") {
+      return false;
+    }
+  } else if (name == "ChromeOS") {
+    // TODO(rsesek): Figure out what ChromeOS needs.
+  } else if (name == "iOS") {
+    // TODO(rsesek): Figure out what iOS needs. Probably Device and Simulator.
+  } else if (name == "Android") {
+    // TODO(rsesek): Figure out what Android needs.
+  } else {
+    return false;
+  }
+
+  return true;
+}
+
+bool PlatformFromString(const base::StringPiece& modifier,
+                        Platform* out_platform) {
+  size_t sep = modifier.find('-');
+  if (sep == std::string::npos) {
+    out_platform->name = modifier.as_string();
+    out_platform->variant.clear();
+  } else {
+    out_platform->name = modifier.substr(0, sep).as_string();
+    out_platform->variant = modifier.substr(sep + 1).as_string();
+  }
+
+  return IsValidPlatform(out_platform);
+}
+
+Platform GetCurrentPlatform() {
+  Platform platform;
+#if defined(OS_WIN)
+  platform.name = "Win";
+  base::win::Version version = base::win::GetVersion();
+  if (version == base::win::VERSION_XP)
+    platform.variant = "XP";
+  else if (version == base::win::VERSION_VISTA)
+    platform.variant = "Vista";
+  else if (version == base::win::VERSION_WIN7)
+    platform.variant = "7";
+  else if (version == base::win::VERSION_WIN8)
+    platform.variant = "8";
+#elif defined(OS_IOS)
+  platform.name = "iOS";
+#elif defined(OS_MACOSX)
+  platform.name = "Mac";
+  if (base::mac::IsOSSnowLeopard())
+    platform.variant = "10.6";
+  else if (base::mac::IsOSLion())
+    platform.variant = "10.7";
+  else if (base::mac::IsOSMountainLion())
+    platform.variant = "10.8";
+  else if (base::mac::IsOSMavericks())
+    platform.variant = "10.9";
+  else if (base::mac::IsOSYosemite())
+    platform.variant = "10.10";
+#elif defined(OS_CHROMEOS)
+  platform.name = "ChromeOS";
+#elif defined(OS_ANDROID)
+  platform.name = "Android";
+#elif defined(OS_LINUX)
+  platform.name = "Linux";
+  std::string arch = base::SysInfo::OperatingSystemArchitecture();
+  if (arch == "x86")
+    platform.variant = "32";
+  else if (arch == "x86_64")
+    platform.variant = "64";
+#else
+  NOTREACHED();
+#endif
+  return platform;
+}
+
+bool ConfigurationFromString(const base::StringPiece& modifier,
+                             Configuration* out_configuration) {
+  if (modifier == "Debug")
+    *out_configuration = CONFIGURATION_DEBUG;
+  else if (modifier == "Release")
+    *out_configuration = CONFIGURATION_RELEASE;
+  else
+    return false;
+
+  return true;
+}
+
+Configuration GetCurrentConfiguration() {
+#if NDEBUG
+  return CONFIGURATION_RELEASE;
+#else
+  return CONFIGURATION_DEBUG;
+#endif
+}
+
+Expectation::Expectation()
+    : configuration(CONFIGURATION_UNSPECIFIED),
+      result(RESULT_PASS) {
+}
+
+Expectation::~Expectation() {}
+
+}  // namespace test_expectations
diff --git a/base/test/expectations/expectation.h b/base/test/expectations/expectation.h
new file mode 100644
index 0000000..be5a9d7
--- /dev/null
+++ b/base/test/expectations/expectation.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_EXPECTATIONS_EXPECTATION_H_
+#define BASE_TEST_EXPECTATIONS_EXPECTATION_H_
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/strings/string_piece.h"
+
+namespace test_expectations {
+
+// A Result is the expectation of a test's behavior.
+enum Result {
+  // The test has a failing assertion.
+  RESULT_FAILURE,
+
+  // The test does not complete within the test runner's alloted duration.
+  RESULT_TIMEOUT,
+
+  // The test crashes during the course of its execution.
+  RESULT_CRASH,
+
+  // The test should not be run ever.
+  RESULT_SKIP,
+
+  // The test passes, used to override a more general expectation.
+  RESULT_PASS,
+};
+
+// Converts a text string form of a |result| to its enum value, written to
+// |out_result|. Returns true on success and false on error.
+bool ResultFromString(const base::StringPiece& result,
+                      Result* out_result) WARN_UNUSED_RESULT;
+
+// A Platform stores information about the OS environment.
+struct Platform {
+  // The name of the platform. E.g., "Win", or "Mac".
+  std::string name;
+
+  // The variant of the platform, either an OS version like "XP" or "10.8", or
+  // "Device" or "Simulator" in the case of mobile.
+  std::string variant;
+};
+
+// Converts a text string |modifier| to a Platform struct, written to
+// |out_platform|. Returns true on success and false on failure.
+bool PlatformFromString(const base::StringPiece& modifier,
+                        Platform* out_platform) WARN_UNUSED_RESULT;
+
+// Returns the Platform for the currently running binary.
+Platform GetCurrentPlatform();
+
+// The build configuration.
+enum Configuration {
+  CONFIGURATION_UNSPECIFIED,
+  CONFIGURATION_DEBUG,
+  CONFIGURATION_RELEASE,
+};
+
+// Converts the |modifier| to a Configuration constant, writing the value to
+// |out_configuration|. Returns true on success or false on failure.
+bool ConfigurationFromString(const base::StringPiece& modifier,
+    Configuration* out_configuration) WARN_UNUSED_RESULT;
+
+// Returns the Configuration for the currently running binary.
+Configuration GetCurrentConfiguration();
+
+// An Expectation is records what the result for a given test name should be on
+// the specified platforms and configuration.
+struct Expectation {
+  Expectation();
+  ~Expectation();
+
+  // The name of the test, like FooBarTest.BarIsBaz.
+  std::string test_name;
+
+  // The set of platforms for which this expectation is applicable.
+  std::vector<Platform> platforms;
+
+  // The build configuration.
+  Configuration configuration;
+
+  // The expected result of this test.
+  Result result;
+};
+
+}  // namespace test_expectations
+
+#endif  // BASE_TEST_EXPECTATIONS_EXPECTATION_H_
diff --git a/base/test/expectations/expectation_unittest.cc b/base/test/expectations/expectation_unittest.cc
new file mode 100644
index 0000000..8a7af71
--- /dev/null
+++ b/base/test/expectations/expectation_unittest.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/expectations/expectation.h"
+
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(TestExpectationsFunctionsTest, ResultFromString) {
+  test_expectations::Result result = test_expectations::RESULT_PASS;
+
+  EXPECT_TRUE(ResultFromString("Failure", &result));
+  EXPECT_EQ(test_expectations::RESULT_FAILURE, result);
+
+  EXPECT_TRUE(ResultFromString("Timeout", &result));
+  EXPECT_EQ(test_expectations::RESULT_TIMEOUT, result);
+
+  EXPECT_TRUE(ResultFromString("Crash", &result));
+  EXPECT_EQ(test_expectations::RESULT_CRASH, result);
+
+  EXPECT_TRUE(ResultFromString("Skip", &result));
+  EXPECT_EQ(test_expectations::RESULT_SKIP, result);
+
+  EXPECT_TRUE(ResultFromString("Pass", &result));
+  EXPECT_EQ(test_expectations::RESULT_PASS, result);
+
+  // Case sensitive.
+  EXPECT_FALSE(ResultFromString("failure", &result));
+  EXPECT_EQ(test_expectations::RESULT_PASS, result);
+}
+
+TEST(TestExpectationsFunctionsTest, ConfigurationFromString) {
+  test_expectations::Configuration config =
+      test_expectations::CONFIGURATION_UNSPECIFIED;
+
+  EXPECT_TRUE(ConfigurationFromString("Debug", &config));
+  EXPECT_EQ(test_expectations::CONFIGURATION_DEBUG, config);
+
+  EXPECT_TRUE(ConfigurationFromString("Release", &config));
+  EXPECT_EQ(test_expectations::CONFIGURATION_RELEASE, config);
+
+  EXPECT_FALSE(ConfigurationFromString("NotAConfig", &config));
+  EXPECT_EQ(test_expectations::CONFIGURATION_RELEASE, config);
+
+  // Case sensitive.
+  EXPECT_FALSE(ConfigurationFromString("debug", &config));
+  EXPECT_EQ(test_expectations::CONFIGURATION_RELEASE, config);
+}
+
+TEST(TestExpectationsFunctionsTest, PlatformFromString) {
+  test_expectations::Platform platform;
+
+  EXPECT_TRUE(PlatformFromString("Win", &platform));
+  EXPECT_EQ("Win", platform.name);
+  EXPECT_EQ("", platform.variant);
+
+  EXPECT_TRUE(PlatformFromString("Mac-10.6", &platform));
+  EXPECT_EQ("Mac", platform.name);
+  EXPECT_EQ("10.6", platform.variant);
+
+  EXPECT_TRUE(PlatformFromString("ChromeOS", &platform));
+  EXPECT_EQ("ChromeOS", platform.name);
+  EXPECT_EQ("", platform.variant);
+
+  EXPECT_TRUE(PlatformFromString("Linux-", &platform));
+  EXPECT_EQ("Linux", platform.name);
+  EXPECT_EQ("", platform.variant);
+
+  EXPECT_FALSE(PlatformFromString("", &platform));
+}
+
+TEST(TestExpectationsFunctionsTest, IsValidPlatform) {
+  const char* kValidPlatforms[] = {
+    "Win",
+    "Win-XP",
+    "Win-Vista",
+    "Win-7",
+    "Win-8",
+    "Mac",
+    "Mac-10.6",
+    "Mac-10.7",
+    "Mac-10.8",
+    "Linux",
+    "Linux-32",
+    "Linux-64",
+    "ChromeOS",
+    "iOS",
+    "Android",
+  };
+
+  const char* kInvalidPlatforms[] = {
+    "Solaris",
+    "Plan9",
+  };
+
+  for (size_t i = 0; i < arraysize(kValidPlatforms); ++i) {
+    test_expectations::Platform platform;
+    EXPECT_TRUE(test_expectations::PlatformFromString(
+        kValidPlatforms[i], &platform)) << kValidPlatforms[i];
+  }
+
+  for (size_t i = 0; i < arraysize(kInvalidPlatforms); ++i) {
+    test_expectations::Platform platform;
+    EXPECT_FALSE(test_expectations::PlatformFromString(
+        kInvalidPlatforms[i], &platform)) << kInvalidPlatforms[i];
+  }
+}
+
+TEST(TestExpectationsFunctionsTest, CurrentPlatform) {
+  test_expectations::Platform current =
+      test_expectations::GetCurrentPlatform();
+  EXPECT_FALSE(current.name.empty());
+}
+
+TEST(TestExpectationsFunctionsTest, CurrentConfiguration) {
+  test_expectations::Configuration current =
+      test_expectations::GetCurrentConfiguration();
+  EXPECT_NE(test_expectations::CONFIGURATION_UNSPECIFIED, current);
+}
diff --git a/base/test/expectations/parser.cc b/base/test/expectations/parser.cc
new file mode 100644
index 0000000..c7132e5
--- /dev/null
+++ b/base/test/expectations/parser.cc
@@ -0,0 +1,201 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/expectations/parser.h"
+
+#include "base/strings/string_util.h"
+
+namespace test_expectations {
+
+Parser::Parser(Delegate* delegate, const std::string& input)
+    : delegate_(delegate),
+      input_(input),
+      pos_(NULL),
+      end_(NULL),
+      line_number_(0),
+      data_error_(false) {
+}
+
+Parser::~Parser() {
+}
+
+void Parser::Parse() {
+  pos_ = &input_[0];
+  end_ = pos_ + input_.length();
+
+  line_number_ = 1;
+
+  StateFuncPtr state = &Parser::Start;
+  while (state) {
+    state = (this->*state)();
+  }
+}
+
+inline bool Parser::HasNext() {
+  return pos_ < end_;
+}
+
+Parser::StateFunc Parser::Start() {
+  // If at the start of a line is whitespace, skip it and arrange to come back
+  // here.
+  if (IsAsciiWhitespace(*pos_))
+    return SkipWhitespaceAndNewLines(&Parser::Start);
+
+  // Handle comments at the start of lines.
+  if (*pos_ == '#')
+    return &Parser::ParseComment;
+
+  // After arranging to come back here from skipping whitespace and comments,
+  // the parser may be at the end of the input.
+  if (pos_ >= end_)
+    return NULL;
+
+  current_ = Expectation();
+  data_error_ = false;
+
+  return &Parser::ParseBugURL;
+}
+
+Parser::StateFunc Parser::ParseComment() {
+  if (*pos_ != '#')
+    return SyntaxError("Invalid start of comment");
+
+  do {
+    ++pos_;
+  } while (HasNext() && *pos_ != '\n');
+
+  return &Parser::Start;
+}
+
+Parser::StateFunc Parser::ParseBugURL() {
+  return SkipWhitespace(ExtractString(
+      &Parser::BeginModifiers));
+}
+
+Parser::StateFunc Parser::BeginModifiers() {
+  if (*pos_ != '[' || !HasNext())
+    return SyntaxError("Expected '[' for start of modifiers");
+
+  ++pos_;
+  return SkipWhitespace(&Parser::InModifiers);
+}
+
+Parser::StateFunc Parser::InModifiers() {
+  if (*pos_ == ']')
+    return &Parser::EndModifiers;
+
+  return ExtractString(SkipWhitespace(
+      &Parser::SaveModifier));
+}
+
+Parser::StateFunc Parser::SaveModifier() {
+  if (extracted_string_.empty())
+    return SyntaxError("Invalid modifier list");
+
+  Configuration config;
+  if (ConfigurationFromString(extracted_string_, &config)) {
+    if (current_.configuration != CONFIGURATION_UNSPECIFIED)
+      DataError("Cannot use more than one configuration modifier");
+    else
+      current_.configuration = config;
+  } else {
+    Platform platform;
+    if (PlatformFromString(extracted_string_, &platform))
+      current_.platforms.push_back(platform);
+    else
+      DataError("Invalid modifier string");
+  }
+
+  return SkipWhitespace(&Parser::InModifiers);
+}
+
+Parser::StateFunc Parser::EndModifiers() {
+ if (*pos_ != ']' || !HasNext())
+    return SyntaxError("Expected ']' for end of modifiers list");
+
+  ++pos_;
+  return SkipWhitespace(&Parser::ParseTestName);
+}
+
+Parser::StateFunc Parser::ParseTestName() {
+  return ExtractString(&Parser::SaveTestName);
+}
+
+Parser::StateFunc Parser::SaveTestName() {
+  if (extracted_string_.empty())
+    return SyntaxError("Invalid test name");
+
+  current_.test_name = extracted_string_.as_string();
+  return SkipWhitespace(&Parser::ParseExpectation);
+}
+
+Parser::StateFunc Parser::ParseExpectation() {
+  if (*pos_ != '=' || !HasNext())
+    return SyntaxError("Expected '=' for expectation result");
+
+  ++pos_;
+  return SkipWhitespace(&Parser::ParseExpectationType);
+}
+
+Parser::StateFunc Parser::ParseExpectationType() {
+  return ExtractString(&Parser::SaveExpectationType);
+}
+
+Parser::StateFunc Parser::SaveExpectationType() {
+  if (!ResultFromString(extracted_string_, &current_.result))
+    DataError("Unknown expectation type");
+
+  return SkipWhitespace(&Parser::End);
+}
+
+Parser::StateFunc Parser::End() {
+  if (!data_error_)
+    delegate_->EmitExpectation(current_);
+
+  if (HasNext())
+    return SkipWhitespaceAndNewLines(&Parser::Start);
+
+  return NULL;
+}
+
+Parser::StateFunc Parser::ExtractString(StateFunc success) {
+  const char* start = pos_;
+  while (!IsAsciiWhitespace(*pos_) && *pos_ != ']' && HasNext()) {
+    ++pos_;
+    if (*pos_ == '#') {
+      return SyntaxError("Unexpected start of comment");
+    }
+  }
+  extracted_string_ = base::StringPiece(start, pos_ - start);
+  return success;
+}
+
+Parser::StateFunc Parser::SkipWhitespace(Parser::StateFunc next) {
+  while ((*pos_ == ' ' || *pos_ == '\t') && HasNext()) {
+    ++pos_;
+  }
+  return next;
+}
+
+Parser::StateFunc Parser::SkipWhitespaceAndNewLines(Parser::StateFunc next) {
+  while (IsAsciiWhitespace(*pos_) && HasNext()) {
+    if (*pos_ == '\n') {
+      ++line_number_;
+    }
+    ++pos_;
+  }
+  return next;
+}
+
+Parser::StateFunc Parser::SyntaxError(const std::string& message) {
+  delegate_->OnSyntaxError(message);
+  return NULL;
+}
+
+void Parser::DataError(const std::string& error) {
+  data_error_ = true;
+  delegate_->OnDataError(error);
+}
+
+}  // namespace test_expectations
diff --git a/base/test/expectations/parser.h b/base/test/expectations/parser.h
new file mode 100644
index 0000000..69a741a
--- /dev/null
+++ b/base/test/expectations/parser.h
@@ -0,0 +1,143 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_EXPECTATIONS_PARSER_H_
+#define BASE_TEST_EXPECTATIONS_PARSER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "base/test/expectations/expectation.h"
+
+namespace test_expectations {
+
+// This is the internal parser for test expectations. It parses an input
+// string and reports information to its Delegate as it's processing the
+// input.
+//
+// The input format is documented here:
+// https://docs.google.com/a/chromium.org/document/d/1edhMJ5doY_dzfbKNCzeJJ-8XxPrexTbNL2Y_jVvLB8Q/view
+//
+// Basic format:
+// "http://bug/1234 [ OS-Version ] Test.Name = Result"
+//
+// The parser is implemented as a state machine, with each state returning a
+// function pointer to the next state.
+class Parser {
+ public:
+  // The parser will call these methods on its delegate during a Parse()
+  // operation.
+  class Delegate {
+   public:
+    // When a well-formed and valid Expectation has been parsed from the input,
+    // it is reported to the delegate via this method.
+    virtual void EmitExpectation(const Expectation& expectation) = 0;
+
+    // Called when the input string is not well-formed. Parsing will stop after
+    // this method is called.
+    virtual void OnSyntaxError(const std::string& message) = 0;
+
+    // Called when an Expectation has been parsed because it is well-formed but
+    // contains invalid data (i.e. the modifiers or result are not valid
+    // keywords). This Expectation will not be reported via EmitExpectation.
+    virtual void OnDataError(const std::string& message) = 0;
+  };
+
+  // Creates a new parser for |input| that will send data to |delegate|.
+  Parser(Delegate* delegate, const std::string& input);
+  ~Parser();
+
+  // Runs the parser of the input string.
+  void Parse();
+
+ private:
+  // This bit of hackery is used to implement a function pointer type that
+  // returns a pointer to a function of the same signature. Since a definition
+  // like that is inherently recursive, it's impossible to do:
+  //     type StateFunc(*StateFunc)(StateData*);
+  // However, this approach works without the need to use void*. Inspired by
+  // <http://www.gotw.ca/gotw/057.htm>.
+  struct StateFunc;
+  typedef StateFunc(Parser::*StateFuncPtr)();
+  struct StateFunc {
+    StateFunc(StateFuncPtr pf) : pf_(pf) {}
+    operator StateFuncPtr() {
+      return pf_;
+    }
+    StateFuncPtr pf_;
+  };
+
+  // Tests whether there is at least one more character at pos_ before end_.
+  bool HasNext();
+
+  // The parser state functions. On entry, the parser state is at the beginning
+  // of the token. Each returns a function pointer to the next state function,
+  // or NULL to end parsing. On return, the parser is at the beginning of the
+  // next token.
+  StateFunc Start();
+  StateFunc ParseComment();
+  StateFunc ParseBugURL();
+  StateFunc BeginModifiers();
+  StateFunc InModifiers();
+  StateFunc SaveModifier();
+  StateFunc EndModifiers();
+  StateFunc ParseTestName();
+  StateFunc SaveTestName();
+  StateFunc ParseExpectation();
+  StateFunc ParseExpectationType();
+  StateFunc SaveExpectationType();
+  StateFunc End();
+
+  // A state function that collects character data from the current position
+  // to the next whitespace character. Returns the |success| function when at
+  // the end of the string, with the data stored in |extracted_string_|.
+  StateFunc ExtractString(StateFunc success);
+
+  // Function that skips over horizontal whitespace characters and then returns
+  // the |next| state.
+  StateFunc SkipWhitespace(StateFunc next);
+
+  // Does the same as SkipWhitespace but includes newlines.
+  StateFunc SkipWhitespaceAndNewLines(StateFunc next);
+
+  // State function that reports the given syntax error |message| to the
+  // delegate and then returns NULL, ending the parse loop.
+  StateFunc SyntaxError(const std::string& message);
+
+  // Function that reports the data |error| to the delegate without stopping
+  // parsing.
+  void DataError(const std::string& error);
+
+  // Parser delegate.
+  Delegate* delegate_;
+
+  // The input string.
+  std::string input_;
+
+  // Current location in the |input_|.
+  const char* pos_;
+
+  // Pointer to the end of the |input_|.
+  const char* end_;
+
+  // Current line number, as updated by SkipWhitespace().
+  int line_number_;
+
+  // The character data extracted from |input_| as a result of the
+  // ExtractString() state.
+  base::StringPiece extracted_string_;
+
+  // The Expectation object that is currently being processed by the parser.
+  // Reset in Start().
+  Expectation current_;
+
+  // If DataError() has been called during the course of parsing |current_|.
+  // If true, then |current_| will not be emitted to the Delegate.
+  bool data_error_;
+};
+
+}  // namespace test_expectations
+
+#endif  // BASE_TEST_EXPECTATIONS_PARSER_H_
diff --git a/base/test/expectations/parser_unittest.cc b/base/test/expectations/parser_unittest.cc
new file mode 100644
index 0000000..1c55a05
--- /dev/null
+++ b/base/test/expectations/parser_unittest.cc
@@ -0,0 +1,209 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/expectations/parser.h"
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using test_expectations::Parser;
+
+class TestExpectationParserTest : public testing::Test,
+                                  public Parser::Delegate {
+ public:
+  virtual void EmitExpectation(
+      const test_expectations::Expectation& expectation) OVERRIDE {
+    expectations_.push_back(expectation);
+  }
+
+  virtual void OnSyntaxError(const std::string& message) OVERRIDE {
+    syntax_error_ = message;
+  }
+
+  virtual void OnDataError(const std::string& error) OVERRIDE {
+    data_errors_.push_back(error);
+  }
+
+ protected:
+  std::vector<test_expectations::Expectation> expectations_;
+  std::string syntax_error_;
+  std::vector<std::string> data_errors_;
+};
+
+TEST_F(TestExpectationParserTest, Basic) {
+  Parser(this,
+      "http://crbug.com/1234 [ Win-8 ] DouglasTest.PoopsOk = Timeout").
+          Parse();
+  EXPECT_TRUE(syntax_error_.empty());
+  EXPECT_EQ(0u, data_errors_.size());
+
+  ASSERT_EQ(1u, expectations_.size());
+  EXPECT_EQ("DouglasTest.PoopsOk", expectations_[0].test_name);
+  EXPECT_EQ(test_expectations::RESULT_TIMEOUT, expectations_[0].result);
+  EXPECT_EQ(test_expectations::CONFIGURATION_UNSPECIFIED,
+            expectations_[0].configuration);
+
+  ASSERT_EQ(1u, expectations_[0].platforms.size());
+  EXPECT_EQ("Win", expectations_[0].platforms[0].name);
+  EXPECT_EQ("8", expectations_[0].platforms[0].variant);
+}
+
+TEST_F(TestExpectationParserTest, MultiModifier) {
+  Parser(this, "BUG [ Win-XP Mac ] OhMy.MeOhMy = Failure").Parse();
+  EXPECT_TRUE(syntax_error_.empty());
+  EXPECT_EQ(0u, data_errors_.size());
+
+  ASSERT_EQ(1u, expectations_.size());
+  EXPECT_EQ("OhMy.MeOhMy", expectations_[0].test_name);
+  EXPECT_EQ(test_expectations::RESULT_FAILURE,
+            expectations_[0].result);
+  EXPECT_EQ(test_expectations::CONFIGURATION_UNSPECIFIED,
+            expectations_[0].configuration);
+
+  ASSERT_EQ(2u, expectations_[0].platforms.size());
+
+  EXPECT_EQ("Win", expectations_[0].platforms[0].name);
+  EXPECT_EQ("XP", expectations_[0].platforms[0].variant);
+
+  EXPECT_EQ("Mac", expectations_[0].platforms[1].name);
+  EXPECT_EQ("", expectations_[0].platforms[1].variant);
+}
+
+TEST_F(TestExpectationParserTest, EmptyModifier) {
+  Parser(this,
+      "BUG [] First.Test = Failure\n"
+      "BUG2 [   ] Second.Test = Crash").Parse();
+  EXPECT_EQ(0u, data_errors_.size());
+
+  ASSERT_EQ(2u, expectations_.size());
+
+  EXPECT_EQ("First.Test", expectations_[0].test_name);
+  EXPECT_EQ(test_expectations::RESULT_FAILURE,
+            expectations_[0].result);
+  EXPECT_EQ(test_expectations::CONFIGURATION_UNSPECIFIED,
+            expectations_[0].configuration);
+  EXPECT_EQ(0u, expectations_[0].platforms.size());
+
+  EXPECT_EQ("Second.Test", expectations_[1].test_name);
+  EXPECT_EQ(test_expectations::RESULT_CRASH,
+            expectations_[1].result);
+  EXPECT_EQ(test_expectations::CONFIGURATION_UNSPECIFIED,
+            expectations_[1].configuration);
+  EXPECT_EQ(0u, expectations_[1].platforms.size());
+}
+
+TEST_F(TestExpectationParserTest, MultiLine) {
+  Parser(this,
+      "BUG [ Linux ] Line.First = Failure\n"
+      "\n"
+      "# A test comment.\n"
+      "BUG2 [ Release ] Line.Second = Skip").Parse();
+  EXPECT_TRUE(syntax_error_.empty());
+  EXPECT_EQ(0u, data_errors_.size());
+
+  ASSERT_EQ(2u, expectations_.size());
+  EXPECT_EQ("Line.First", expectations_[0].test_name);
+  EXPECT_EQ(test_expectations::RESULT_FAILURE, expectations_[0].result);
+  EXPECT_EQ(test_expectations::CONFIGURATION_UNSPECIFIED,
+            expectations_[0].configuration);
+
+  ASSERT_EQ(1u, expectations_[0].platforms.size());
+  EXPECT_EQ("Linux", expectations_[0].platforms[0].name);
+  EXPECT_EQ("", expectations_[0].platforms[0].variant);
+
+  EXPECT_EQ("Line.Second", expectations_[1].test_name);
+  EXPECT_EQ(test_expectations::RESULT_SKIP, expectations_[1].result);
+  EXPECT_EQ(test_expectations::CONFIGURATION_RELEASE,
+            expectations_[1].configuration);
+  EXPECT_EQ(0u, expectations_[1].platforms.size());
+}
+
+TEST_F(TestExpectationParserTest, MultiLineWithComments) {
+  Parser(this,
+      "  # Comment for your thoughts\n"
+      "  \t \n"
+      "BUG [ Mac-10.8 Debug] Foo=Bar =Skip   # Why not another comment?\n"
+      "BUG2 [Win-XP\tWin-Vista ] Cow.GoesMoo   =\tTimeout\n\n").Parse();
+  EXPECT_TRUE(syntax_error_.empty()) << syntax_error_;
+  EXPECT_EQ(0u, data_errors_.size());
+
+  ASSERT_EQ(2u, expectations_.size());
+  EXPECT_EQ("Foo=Bar", expectations_[0].test_name);
+  EXPECT_EQ(test_expectations::RESULT_SKIP, expectations_[0].result);
+  EXPECT_EQ(test_expectations::CONFIGURATION_DEBUG,
+            expectations_[0].configuration);
+
+  ASSERT_EQ(1u, expectations_[0].platforms.size());
+  EXPECT_EQ("Mac", expectations_[0].platforms[0].name);
+  EXPECT_EQ("10.8", expectations_[0].platforms[0].variant);
+
+  EXPECT_EQ("Cow.GoesMoo", expectations_[1].test_name);
+  EXPECT_EQ(test_expectations::RESULT_TIMEOUT, expectations_[1].result);
+  EXPECT_EQ(test_expectations::CONFIGURATION_UNSPECIFIED,
+            expectations_[1].configuration);
+
+  ASSERT_EQ(2u, expectations_[1].platforms.size());
+  EXPECT_EQ("Win", expectations_[1].platforms[0].name);
+  EXPECT_EQ("XP", expectations_[1].platforms[0].variant);
+  EXPECT_EQ("Win", expectations_[1].platforms[0].name);
+  EXPECT_EQ("Vista", expectations_[1].platforms[1].variant);
+}
+
+TEST_F(TestExpectationParserTest, WeirdSpaces) {
+  Parser(this, "   BUG       [Linux]        Weird  = Skip    ").Parse();
+  EXPECT_EQ(1u, expectations_.size());
+  EXPECT_TRUE(syntax_error_.empty());
+  EXPECT_EQ(0u, data_errors_.size());
+}
+
+TEST_F(TestExpectationParserTest, SyntaxErrors) {
+  const char* kErrors[] = {
+    "Foo [ dfasd",
+    "Foo [Linux] # This is an illegal comment",
+    "Foo [Linux] Bar # Another illegal comment.",
+    "Foo [Linux] Bar = # Another illegal comment.",
+    "Foo[Linux]Bar=Failure",
+    "Foo\n[Linux] Bar = Failure",
+    "Foo [\nLinux] Bar = Failure",
+    "Foo [Linux\n] Bar = Failure",
+    "Foo [ Linux ] \n Bar = Failure",
+    "Foo [ Linux ] Bar =\nFailure",
+    "Foo [ Linux \n ] Bar =\nFailure",
+  };
+
+  for (size_t i = 0; i < arraysize(kErrors); ++i) {
+    Parser(this, kErrors[i]).Parse();
+    EXPECT_FALSE(syntax_error_.empty())
+        << "Should have error for #" << i << ": " << kErrors[i];
+    syntax_error_.clear();
+  }
+}
+
+TEST_F(TestExpectationParserTest, DataErrors) {
+  const char* kOneError[] = {
+    "http://crbug.com/1234 [MagicBrowzR] BadModifier = Timeout",
+    "________ [Linux] BadResult = WhatNow",
+    "http://wkb.ug/1234 [Debug Release Win-7] MultipleConfigs = Skip",
+  };
+
+  for (size_t i = 0; i < arraysize(kOneError); ++i) {
+    Parser(this, kOneError[i]).Parse();
+    EXPECT_EQ(1u, data_errors_.size()) << kOneError[i];
+    data_errors_.clear();
+  }
+
+  const char* kTwoErrors[] = {
+    ". [Mac-TurningIntoiOS] BadModifierVariant.BadResult = Foobar",
+    "1234 [ Debug Release OS/2 ] MultipleConfigs.BadModifier = Pass",
+  };
+
+  for (size_t i = 0; i < arraysize(kTwoErrors); ++i) {
+    Parser(this, kTwoErrors[i]).Parse();
+    EXPECT_EQ(2u, data_errors_.size()) << kTwoErrors[i];
+    data_errors_.clear();
+  }
+}
diff --git a/base/test/gtest_xml_util.cc b/base/test/gtest_xml_util.cc
new file mode 100644
index 0000000..7a5ba8a
--- /dev/null
+++ b/base/test/gtest_xml_util.cc
@@ -0,0 +1,229 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/gtest_xml_util.h"
+
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/launcher/test_launcher.h"
+#include "third_party/libxml/chromium/libxml_utils.h"
+
+namespace base {
+
+namespace {
+
+// This is used for the xml parser to report errors. This assumes the context
+// is a pointer to a std::string where the error message should be appended.
+static void XmlErrorFunc(void *context, const char *message, ...) {
+  va_list args;
+  va_start(args, message);
+  std::string* error = static_cast<std::string*>(context);
+  base::StringAppendV(error, message, args);
+  va_end(args);
+}
+
+}  // namespace
+
+XmlUnitTestResultPrinter::XmlUnitTestResultPrinter() : output_file_(NULL) {
+}
+
+XmlUnitTestResultPrinter::~XmlUnitTestResultPrinter() {
+  if (output_file_) {
+    fprintf(output_file_, "</testsuites>\n");
+    fflush(output_file_);
+    base::CloseFile(output_file_);
+  }
+}
+
+bool XmlUnitTestResultPrinter::Initialize(const FilePath& output_file_path) {
+  DCHECK(!output_file_);
+  output_file_ = OpenFile(output_file_path, "w");
+  if (!output_file_)
+    return false;
+
+  fprintf(output_file_,
+          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<testsuites>\n");
+  fflush(output_file_);
+
+  return true;
+}
+
+void XmlUnitTestResultPrinter::OnTestCaseStart(
+    const testing::TestCase& test_case) {
+  fprintf(output_file_, "  <testsuite>\n");
+  fflush(output_file_);
+}
+
+void XmlUnitTestResultPrinter::OnTestStart(const testing::TestInfo& test_info) {
+  // This is our custom extension - it helps to recognize which test was running
+  // when the test binary crashed. Note that we cannot even open the <testcase>
+  // tag here - it requires e.g. run time of the test to be known.
+  fprintf(output_file_,
+          "    <x-teststart name=\"%s\" classname=\"%s\" />\n",
+          test_info.name(),
+          test_info.test_case_name());
+  fflush(output_file_);
+}
+
+void XmlUnitTestResultPrinter::OnTestEnd(const testing::TestInfo& test_info) {
+  fprintf(output_file_,
+          "    <testcase name=\"%s\" status=\"run\" time=\"%.3f\""
+          " classname=\"%s\">\n",
+          test_info.name(),
+          static_cast<double>(test_info.result()->elapsed_time()) /
+              Time::kMillisecondsPerSecond,
+          test_info.test_case_name());
+  if (test_info.result()->Failed())
+    fprintf(output_file_, "      <failure message=\"\" type=\"\"></failure>\n");
+  fprintf(output_file_, "    </testcase>\n");
+  fflush(output_file_);
+}
+
+void XmlUnitTestResultPrinter::OnTestCaseEnd(
+    const testing::TestCase& test_case) {
+  fprintf(output_file_, "  </testsuite>\n");
+  fflush(output_file_);
+}
+
+bool ProcessGTestOutput(const base::FilePath& output_file,
+                        std::vector<TestResult>* results,
+                        bool* crashed) {
+  DCHECK(results);
+
+  std::string xml_contents;
+  if (!ReadFileToString(output_file, &xml_contents))
+    return false;
+
+  // Silence XML errors - otherwise they go to stderr.
+  std::string xml_errors;
+  ScopedXmlErrorFunc error_func(&xml_errors, &XmlErrorFunc);
+
+  XmlReader xml_reader;
+  if (!xml_reader.Load(xml_contents))
+    return false;
+
+  enum {
+    STATE_INIT,
+    STATE_TESTSUITE,
+    STATE_TESTCASE,
+    STATE_FAILURE,
+    STATE_END,
+  } state = STATE_INIT;
+
+  while (xml_reader.Read()) {
+    xml_reader.SkipToElement();
+    std::string node_name(xml_reader.NodeName());
+
+    switch (state) {
+      case STATE_INIT:
+        if (node_name == "testsuites" && !xml_reader.IsClosingElement())
+          state = STATE_TESTSUITE;
+        else
+          return false;
+        break;
+      case STATE_TESTSUITE:
+        if (node_name == "testsuites" && xml_reader.IsClosingElement())
+          state = STATE_END;
+        else if (node_name == "testsuite" && !xml_reader.IsClosingElement())
+          state = STATE_TESTCASE;
+        else
+          return false;
+        break;
+      case STATE_TESTCASE:
+        if (node_name == "testsuite" && xml_reader.IsClosingElement()) {
+          state = STATE_TESTSUITE;
+        } else if (node_name == "x-teststart" &&
+                   !xml_reader.IsClosingElement()) {
+          // This is our custom extension that helps recognize which test was
+          // running when the test binary crashed.
+          TestResult result;
+
+          std::string test_case_name;
+          if (!xml_reader.NodeAttribute("classname", &test_case_name))
+            return false;
+          std::string test_name;
+          if (!xml_reader.NodeAttribute("name", &test_name))
+            return false;
+          result.full_name = TestLauncher::FormatFullTestName(test_case_name,
+                                                              test_name);
+
+          result.elapsed_time = TimeDelta();
+
+          // Assume the test crashed - we can correct that later.
+          result.status = TestResult::TEST_CRASH;
+
+          results->push_back(result);
+        } else if (node_name == "testcase" && !xml_reader.IsClosingElement()) {
+          std::string test_status;
+          if (!xml_reader.NodeAttribute("status", &test_status))
+            return false;
+
+          if (test_status != "run" && test_status != "notrun")
+            return false;
+          if (test_status != "run")
+            break;
+
+          TestResult result;
+
+          std::string test_case_name;
+          if (!xml_reader.NodeAttribute("classname", &test_case_name))
+            return false;
+          std::string test_name;
+          if (!xml_reader.NodeAttribute("name", &test_name))
+            return false;
+          result.full_name = test_case_name + "." + test_name;
+
+          std::string test_time_str;
+          if (!xml_reader.NodeAttribute("time", &test_time_str))
+            return false;
+          result.elapsed_time = TimeDelta::FromMicroseconds(
+              static_cast<int64>(strtod(test_time_str.c_str(), NULL) *
+                  Time::kMicrosecondsPerSecond));
+
+          result.status = TestResult::TEST_SUCCESS;
+
+          if (!results->empty() &&
+              results->at(results->size() - 1).full_name == result.full_name &&
+              results->at(results->size() - 1).status ==
+                  TestResult::TEST_CRASH) {
+            // Erase the fail-safe "crashed" result - now we know the test did
+            // not crash.
+            results->pop_back();
+          }
+
+          results->push_back(result);
+        } else if (node_name == "failure" && !xml_reader.IsClosingElement()) {
+          std::string failure_message;
+          if (!xml_reader.NodeAttribute("message", &failure_message))
+            return false;
+
+          DCHECK(!results->empty());
+          results->at(results->size() - 1).status = TestResult::TEST_FAILURE;
+
+          state = STATE_FAILURE;
+        } else if (node_name == "testcase" && xml_reader.IsClosingElement()) {
+          // Deliberately empty.
+        } else {
+          return false;
+        }
+        break;
+      case STATE_FAILURE:
+        if (node_name == "failure" && xml_reader.IsClosingElement())
+          state = STATE_TESTCASE;
+        else
+          return false;
+        break;
+      case STATE_END:
+        // If we are here and there are still XML elements, the file has wrong
+        // format.
+        return false;
+    }
+  }
+
+  *crashed = (state != STATE_END);
+  return true;
+}
+
+}  // namespace base
diff --git a/base/test/gtest_xml_util.h b/base/test/gtest_xml_util.h
new file mode 100644
index 0000000..79527e5
--- /dev/null
+++ b/base/test/gtest_xml_util.h
@@ -0,0 +1,51 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_GTEST_XML_UTIL_H_
+#define BASE_TEST_GTEST_XML_UTIL_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+class FilePath;
+struct TestResult;
+
+// Generates an XML output file. Format is very close to GTest, but has
+// extensions needed by the test launcher.
+class XmlUnitTestResultPrinter : public testing::EmptyTestEventListener {
+ public:
+  XmlUnitTestResultPrinter();
+  virtual ~XmlUnitTestResultPrinter();
+
+  // Must be called before adding as a listener. Returns true on success.
+  bool Initialize(const FilePath& output_file_path) WARN_UNUSED_RESULT;
+
+ private:
+  // testing::EmptyTestEventListener:
+  virtual void OnTestCaseStart(const testing::TestCase& test_case) OVERRIDE;
+  virtual void OnTestStart(const testing::TestInfo& test_info) OVERRIDE;
+  virtual void OnTestEnd(const testing::TestInfo& test_info) OVERRIDE;
+  virtual void OnTestCaseEnd(const testing::TestCase& test_case) OVERRIDE;
+
+  FILE* output_file_;
+
+  DISALLOW_COPY_AND_ASSIGN(XmlUnitTestResultPrinter);
+};
+
+// Produces a vector of test results based on GTest output file.
+// Returns true iff the output file exists and has been successfully parsed.
+// On successful return |crashed| is set to true if the test results
+// are valid but incomplete.
+bool ProcessGTestOutput(const base::FilePath& output_file,
+                        std::vector<TestResult>* results,
+                        bool* crashed) WARN_UNUSED_RESULT;
+
+}  // namespace base
+
+#endif  // BASE_TEST_GTEST_XML_UTIL_H_
diff --git a/base/test/histogram_tester.cc b/base/test/histogram_tester.cc
new file mode 100644
index 0000000..412059a
--- /dev/null
+++ b/base/test/histogram_tester.cc
@@ -0,0 +1,122 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/histogram_tester.h"
+
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/stl_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+HistogramTester::HistogramTester() {
+  StatisticsRecorder::Initialize();  // Safe to call multiple times.
+
+  // Record any histogram data that exists when the object is created so it can
+  // be subtracted later.
+  StatisticsRecorder::Histograms histograms;
+  StatisticsRecorder::GetSnapshot(std::string(), &histograms);
+  for (size_t i = 0; i < histograms.size(); ++i) {
+    histograms_snapshot_[histograms[i]->histogram_name()] =
+        histograms[i]->SnapshotSamples().release();
+  }
+}
+
+HistogramTester::~HistogramTester() {
+  STLDeleteValues(&histograms_snapshot_);
+}
+
+void HistogramTester::ExpectUniqueSample(
+    const std::string& name,
+    base::HistogramBase::Sample sample,
+    base::HistogramBase::Count expected_count) const {
+  base::HistogramBase* histogram =
+      base::StatisticsRecorder::FindHistogram(name);
+  EXPECT_NE(static_cast<base::HistogramBase*>(NULL), histogram)
+      << "Histogram \"" << name << "\" does not exist.";
+
+  if (histogram) {
+    scoped_ptr<base::HistogramSamples> samples(histogram->SnapshotSamples());
+    CheckBucketCount(name, sample, expected_count, *samples);
+    CheckTotalCount(name, expected_count, *samples);
+  }
+}
+
+void HistogramTester::ExpectBucketCount(
+    const std::string& name,
+    base::HistogramBase::Sample sample,
+    base::HistogramBase::Count expected_count) const {
+  base::HistogramBase* histogram =
+      base::StatisticsRecorder::FindHistogram(name);
+  EXPECT_NE(static_cast<base::HistogramBase*>(NULL), histogram)
+      << "Histogram \"" << name << "\" does not exist.";
+
+  if (histogram) {
+    scoped_ptr<base::HistogramSamples> samples(histogram->SnapshotSamples());
+    CheckBucketCount(name, sample, expected_count, *samples);
+  }
+}
+
+void HistogramTester::ExpectTotalCount(const std::string& name,
+                                       base::HistogramBase::Count count) const {
+  base::HistogramBase* histogram =
+      base::StatisticsRecorder::FindHistogram(name);
+  if (histogram) {
+    scoped_ptr<base::HistogramSamples> samples(histogram->SnapshotSamples());
+    CheckTotalCount(name, count, *samples);
+  } else {
+    // No histogram means there were zero samples.
+    EXPECT_EQ(count, 0) << "Histogram \"" << name << "\" does not exist.";
+  }
+}
+
+scoped_ptr<HistogramSamples> HistogramTester::GetHistogramSamplesSinceCreation(
+    const std::string& histogram_name) {
+  HistogramBase* histogram = StatisticsRecorder::FindHistogram(histogram_name);
+  if (!histogram)
+    return scoped_ptr<HistogramSamples>();
+  scoped_ptr<HistogramSamples> named_samples(histogram->SnapshotSamples());
+  HistogramSamples* named_original_samples =
+      histograms_snapshot_[histogram_name];
+  if (named_original_samples)
+    named_samples->Subtract(*named_original_samples);
+  return named_samples.Pass();
+}
+
+void HistogramTester::CheckBucketCount(
+    const std::string& name,
+    base::HistogramBase::Sample sample,
+    base::HistogramBase::Count expected_count,
+    base::HistogramSamples& samples) const {
+  int actual_count = samples.GetCount(sample);
+  std::map<std::string, HistogramSamples*>::const_iterator histogram_data;
+  histogram_data = histograms_snapshot_.find(name);
+  if (histogram_data != histograms_snapshot_.end())
+    actual_count -= histogram_data->second->GetCount(sample);
+
+  EXPECT_EQ(expected_count, actual_count)
+      << "Histogram \"" << name
+      << "\" does not have the right number of samples (" << expected_count
+      << ") in the expected bucket (" << sample << "). It has (" << actual_count
+      << ").";
+}
+
+void HistogramTester::CheckTotalCount(const std::string& name,
+                                      base::HistogramBase::Count expected_count,
+                                      base::HistogramSamples& samples) const {
+  int actual_count = samples.TotalCount();
+  std::map<std::string, HistogramSamples*>::const_iterator histogram_data;
+  histogram_data = histograms_snapshot_.find(name);
+  if (histogram_data != histograms_snapshot_.end())
+    actual_count -= histogram_data->second->TotalCount();
+
+  EXPECT_EQ(expected_count, actual_count)
+      << "Histogram \"" << name
+      << "\" does not have the right total number of samples ("
+      << expected_count << "). It has (" << actual_count << ").";
+}
+
+}  // namespace base
diff --git a/base/test/histogram_tester.h b/base/test/histogram_tester.h
new file mode 100644
index 0000000..b672308
--- /dev/null
+++ b/base/test/histogram_tester.h
@@ -0,0 +1,81 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_HISTOGRAM_TESTER_H_
+#define BASE_TEST_HISTOGRAM_TESTER_H_
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_base.h"
+
+namespace base {
+
+class HistogramSamples;
+
+// HistogramTester provides a simple interface for examining histograms, UMA
+// or otherwise. Tests can use this interface to verify that histogram data is
+// getting logged as intended.
+class HistogramTester {
+ public:
+  // The constructor will call StatisticsRecorder::Initialize() for you. Also,
+  // this takes a snapshot of all current histograms counts.
+  HistogramTester();
+  ~HistogramTester();
+
+  // We know the exact number of samples in a bucket, and that no other bucket
+  // should have samples. Measures the diff from the snapshot taken when this
+  // object was constructed.
+  void ExpectUniqueSample(const std::string& name,
+                          base::HistogramBase::Sample sample,
+                          base::HistogramBase::Count expected_count) const;
+
+  // We know the exact number of samples in a bucket, but other buckets may
+  // have samples as well. Measures the diff from the snapshot taken when this
+  // object was constructed.
+  void ExpectBucketCount(const std::string& name,
+                         base::HistogramBase::Sample sample,
+                         base::HistogramBase::Count expected_count) const;
+
+  // We don't know the values of the samples, but we know how many there are.
+  // This measures the diff from the snapshot taken when this object was
+  // constructed.
+  void ExpectTotalCount(const std::string& name,
+                        base::HistogramBase::Count count) const;
+
+  // Access a modified HistogramSamples containing only what has been logged
+  // to the histogram since the creation of this object.
+  scoped_ptr<HistogramSamples> GetHistogramSamplesSinceCreation(
+      const std::string& histogram_name);
+
+ private:
+  // Verifies and asserts that value in the |sample| bucket matches the
+  // |expected_count|. The bucket's current value is determined from |samples|
+  // and is modified based on the snapshot stored for histogram |name|.
+  void CheckBucketCount(const std::string& name,
+                        base::HistogramBase::Sample sample,
+                        base::Histogram::Count expected_count,
+                        base::HistogramSamples& samples) const;
+
+  // Verifies that the total number of values recorded for the histogram |name|
+  // is |expected_count|. This is checked against |samples| minus the snapshot
+  // that was taken for |name|.
+  void CheckTotalCount(const std::string& name,
+                       base::Histogram::Count expected_count,
+                       base::HistogramSamples& samples) const;
+
+  // Used to determine the histogram changes made during this instance's
+  // lifecycle. This instance takes ownership of the samples, which are deleted
+  // when the instance is destroyed.
+  std::map<std::string, HistogramSamples*> histograms_snapshot_;
+
+  DISALLOW_COPY_AND_ASSIGN(HistogramTester);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_HISTOGRAM_TESTER_H_
diff --git a/base/test/histogram_tester_unittest.cc b/base/test/histogram_tester_unittest.cc
new file mode 100644
index 0000000..a03ee13
--- /dev/null
+++ b/base/test/histogram_tester_unittest.cc
@@ -0,0 +1,81 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/histogram_tester.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_samples.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+const std::string kHistogram1 = "Test1";
+const std::string kHistogram2 = "Test2";
+const std::string kHistogram3 = "Test3";
+const std::string kHistogram4 = "Test4";
+
+typedef testing::Test HistogramTesterTest;
+
+TEST_F(HistogramTesterTest, Scope) {
+  // Record a histogram before the creation of the recorder.
+  UMA_HISTOGRAM_BOOLEAN(kHistogram1, true);
+
+  HistogramTester tester;
+
+  // Verify that no histogram is recorded.
+  scoped_ptr<HistogramSamples> samples(
+      tester.GetHistogramSamplesSinceCreation(kHistogram1));
+  EXPECT_FALSE(samples);
+
+  // Record a histogram after the creation of the recorder.
+  UMA_HISTOGRAM_BOOLEAN(kHistogram1, true);
+
+  // Verify that one histogram is recorded.
+  samples = tester.GetHistogramSamplesSinceCreation(kHistogram1);
+  EXPECT_TRUE(samples);
+  EXPECT_EQ(1, samples->TotalCount());
+}
+
+TEST_F(HistogramTesterTest, TestUniqueSample) {
+  HistogramTester tester;
+
+  // Record into a sample thrice
+  UMA_HISTOGRAM_COUNTS_100(kHistogram2, 2);
+  UMA_HISTOGRAM_COUNTS_100(kHistogram2, 2);
+  UMA_HISTOGRAM_COUNTS_100(kHistogram2, 2);
+
+  tester.ExpectUniqueSample(kHistogram2, 2, 3);
+}
+
+TEST_F(HistogramTesterTest, TestBucketsSample) {
+  HistogramTester tester;
+
+  // Record into a sample twice
+  UMA_HISTOGRAM_COUNTS_100(kHistogram3, 2);
+  UMA_HISTOGRAM_COUNTS_100(kHistogram3, 2);
+  UMA_HISTOGRAM_COUNTS_100(kHistogram3, 2);
+  UMA_HISTOGRAM_COUNTS_100(kHistogram3, 2);
+  UMA_HISTOGRAM_COUNTS_100(kHistogram3, 3);
+
+  tester.ExpectBucketCount(kHistogram3, 2, 4);
+  tester.ExpectBucketCount(kHistogram3, 3, 1);
+
+  tester.ExpectTotalCount(kHistogram3, 5);
+}
+
+TEST_F(HistogramTesterTest, TestBucketsSampleWithScope) {
+  // Record into a sample twice, once before the tester creation and once after.
+  UMA_HISTOGRAM_COUNTS_100(kHistogram4, 2);
+
+  HistogramTester tester;
+  UMA_HISTOGRAM_COUNTS_100(kHistogram4, 3);
+
+  tester.ExpectBucketCount(kHistogram4, 2, 0);
+  tester.ExpectBucketCount(kHistogram4, 3, 1);
+
+  tester.ExpectTotalCount(kHistogram4, 1);
+}
+
+}  // namespace base
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
new file mode 100644
index 0000000..2a36c69
--- /dev/null
+++ b/base/test/launcher/test_launcher.cc
@@ -0,0 +1,1099 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/launcher/test_launcher.h"
+
+#if defined(OS_POSIX)
+#include <fcntl.h>
+#endif
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/format_macros.h"
+#include "base/hash.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/process/kill.h"
+#include "base/process/launch.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringize_macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/launcher/test_results_tracker.h"
+#include "base/test/sequenced_worker_pool_owner.h"
+#include "base/test/test_switches.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_nsautorelease_pool.h"
+#endif
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
+namespace base {
+
+// See https://groups.google.com/a/chromium.org/d/msg/chromium-dev/nkdTP7sstSc/uT3FaE_sgkAJ .
+using ::operator<<;
+
+// The environment variable name for the total number of test shards.
+const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS";
+// The environment variable name for the test shard index.
+const char kTestShardIndex[] = "GTEST_SHARD_INDEX";
+
+namespace {
+
+// Global tag for test runs where the results are incomplete or unreliable
+// for any reason, e.g. early exit because of too many broken tests.
+const char kUnreliableResultsTag[] = "UNRELIABLE_RESULTS";
+
+// Maximum time of no output after which we print list of processes still
+// running. This deliberately doesn't use TestTimeouts (which is otherwise
+// a recommended solution), because they can be increased. This would defeat
+// the purpose of this timeout, which is 1) to avoid buildbot "no output for
+// X seconds" timeout killing the process 2) help communicate status of
+// the test launcher to people looking at the output (no output for a long
+// time is mysterious and gives no info about what is happening) 3) help
+// debugging in case the process hangs anyway.
+const int kOutputTimeoutSeconds = 15;
+
+// Limit of output snippet lines when printing to stdout.
+// Avoids flooding the logs with amount of output that gums up
+// the infrastructure.
+const size_t kOutputSnippetLinesLimit = 5000;
+
+// Set of live launch test processes with corresponding lock (it is allowed
+// for callers to launch processes on different threads).
+LazyInstance<std::map<ProcessHandle, CommandLine> > g_live_processes
+    = LAZY_INSTANCE_INITIALIZER;
+LazyInstance<Lock> g_live_processes_lock = LAZY_INSTANCE_INITIALIZER;
+
+#if defined(OS_POSIX)
+// Self-pipe that makes it possible to do complex shutdown handling
+// outside of the signal handler.
+int g_shutdown_pipe[2] = { -1, -1 };
+
+void ShutdownPipeSignalHandler(int signal) {
+  HANDLE_EINTR(write(g_shutdown_pipe[1], "q", 1));
+}
+
+void KillSpawnedTestProcesses() {
+  // Keep the lock until exiting the process to prevent further processes
+  // from being spawned.
+  AutoLock lock(g_live_processes_lock.Get());
+
+  fprintf(stdout,
+          "Sending SIGTERM to %" PRIuS " child processes... ",
+          g_live_processes.Get().size());
+  fflush(stdout);
+
+  for (std::map<ProcessHandle, CommandLine>::iterator i =
+           g_live_processes.Get().begin();
+       i != g_live_processes.Get().end();
+       ++i) {
+    // Send the signal to entire process group.
+    kill((-1) * (i->first), SIGTERM);
+  }
+
+  fprintf(stdout,
+          "done.\nGiving processes a chance to terminate cleanly... ");
+  fflush(stdout);
+
+  PlatformThread::Sleep(TimeDelta::FromMilliseconds(500));
+
+  fprintf(stdout, "done.\n");
+  fflush(stdout);
+
+  fprintf(stdout,
+          "Sending SIGKILL to %" PRIuS " child processes... ",
+          g_live_processes.Get().size());
+  fflush(stdout);
+
+  for (std::map<ProcessHandle, CommandLine>::iterator i =
+           g_live_processes.Get().begin();
+       i != g_live_processes.Get().end();
+       ++i) {
+    // Send the signal to entire process group.
+    kill((-1) * (i->first), SIGKILL);
+  }
+
+  fprintf(stdout, "done.\n");
+  fflush(stdout);
+}
+
+// I/O watcher for the reading end of the self-pipe above.
+// Terminates any launched child processes and exits the process.
+class SignalFDWatcher : public MessageLoopForIO::Watcher {
+ public:
+  SignalFDWatcher() {
+  }
+
+  virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
+    fprintf(stdout, "\nCaught signal. Killing spawned test processes...\n");
+    fflush(stdout);
+
+    KillSpawnedTestProcesses();
+
+    // The signal would normally kill the process, so exit now.
+    exit(1);
+  }
+
+  virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {
+    NOTREACHED();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SignalFDWatcher);
+};
+#endif  // defined(OS_POSIX)
+
+// Parses the environment variable var as an Int32.  If it is unset, returns
+// true.  If it is set, unsets it then converts it to Int32 before
+// returning it in |result|.  Returns true on success.
+bool TakeInt32FromEnvironment(const char* const var, int32* result) {
+  scoped_ptr<Environment> env(Environment::Create());
+  std::string str_val;
+
+  if (!env->GetVar(var, &str_val))
+    return true;
+
+  if (!env->UnSetVar(var)) {
+    LOG(ERROR) << "Invalid environment: we could not unset " << var << ".\n";
+    return false;
+  }
+
+  if (!StringToInt(str_val, result)) {
+    LOG(ERROR) << "Invalid environment: " << var << " is not an integer.\n";
+    return false;
+  }
+
+  return true;
+}
+
+// Unsets the environment variable |name| and returns true on success.
+// Also returns true if the variable just doesn't exist.
+bool UnsetEnvironmentVariableIfExists(const std::string& name) {
+  scoped_ptr<Environment> env(Environment::Create());
+  std::string str_val;
+
+  if (!env->GetVar(name.c_str(), &str_val))
+    return true;
+
+  return env->UnSetVar(name.c_str());
+}
+
+// Returns true if bot mode has been requested, i.e. defaults optimized
+// for continuous integration bots. This way developers don't have to remember
+// special command-line flags.
+bool BotModeEnabled() {
+  scoped_ptr<Environment> env(Environment::Create());
+  return CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kTestLauncherBotMode) ||
+      env->HasVar("CHROMIUM_TEST_LAUNCHER_BOT_MODE");
+}
+
+// Returns command line command line after gtest-specific processing
+// and applying |wrapper|.
+CommandLine PrepareCommandLineForGTest(const CommandLine& command_line,
+                                       const std::string& wrapper) {
+  CommandLine new_command_line(command_line.GetProgram());
+  CommandLine::SwitchMap switches = command_line.GetSwitches();
+
+  // Strip out gtest_repeat flag - this is handled by the launcher process.
+  switches.erase(kGTestRepeatFlag);
+
+  // Don't try to write the final XML report in child processes.
+  switches.erase(kGTestOutputFlag);
+
+  for (CommandLine::SwitchMap::const_iterator iter = switches.begin();
+       iter != switches.end(); ++iter) {
+    new_command_line.AppendSwitchNative((*iter).first, (*iter).second);
+  }
+
+  // Prepend wrapper after last CommandLine quasi-copy operation. CommandLine
+  // does not really support removing switches well, and trying to do that
+  // on a CommandLine with a wrapper is known to break.
+  // TODO(phajdan.jr): Give it a try to support CommandLine removing switches.
+#if defined(OS_WIN)
+  new_command_line.PrependWrapper(ASCIIToWide(wrapper));
+#elif defined(OS_POSIX)
+  new_command_line.PrependWrapper(wrapper);
+#endif
+
+  return new_command_line;
+}
+
+// Launches a child process using |command_line|. If the child process is still
+// running after |timeout|, it is terminated and |*was_timeout| is set to true.
+// Returns exit code of the process.
+int LaunchChildTestProcessWithOptions(const CommandLine& command_line,
+                                      const LaunchOptions& options,
+                                      int flags,
+                                      base::TimeDelta timeout,
+                                      bool* was_timeout) {
+#if defined(OS_POSIX)
+  // Make sure an option we rely on is present - see LaunchChildGTestProcess.
+  DCHECK(options.new_process_group);
+#endif
+
+  LaunchOptions new_options(options);
+
+#if defined(OS_WIN)
+  DCHECK(!new_options.job_handle);
+
+  win::ScopedHandle job_handle;
+  if (flags & TestLauncher::USE_JOB_OBJECTS) {
+    job_handle.Set(CreateJobObject(NULL, NULL));
+    if (!job_handle.IsValid()) {
+      LOG(ERROR) << "Could not create JobObject.";
+      return -1;
+    }
+
+    DWORD job_flags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+
+    // Allow break-away from job since sandbox and few other places rely on it
+    // on Windows versions prior to Windows 8 (which supports nested jobs).
+    if (win::GetVersion() < win::VERSION_WIN8 &&
+        flags & TestLauncher::ALLOW_BREAKAWAY_FROM_JOB) {
+      job_flags |= JOB_OBJECT_LIMIT_BREAKAWAY_OK;
+    }
+
+    if (!SetJobObjectLimitFlags(job_handle.Get(), job_flags)) {
+      LOG(ERROR) << "Could not SetJobObjectLimitFlags.";
+      return -1;
+    }
+
+    new_options.job_handle = job_handle.Get();
+  }
+#endif  // defined(OS_WIN)
+
+#if defined(OS_LINUX)
+  // To prevent accidental privilege sharing to an untrusted child, processes
+  // are started with PR_SET_NO_NEW_PRIVS. Do not set that here, since this
+  // new child will be privileged and trusted.
+  new_options.allow_new_privs = true;
+#endif
+
+  base::ProcessHandle process_handle;
+
+  {
+    // Note how we grab the lock before the process possibly gets created.
+    // This ensures that when the lock is held, ALL the processes are registered
+    // in the set.
+    AutoLock lock(g_live_processes_lock.Get());
+
+    if (!base::LaunchProcess(command_line, new_options, &process_handle))
+      return -1;
+
+    g_live_processes.Get().insert(std::make_pair(process_handle, command_line));
+  }
+
+  int exit_code = 0;
+  if (!base::WaitForExitCodeWithTimeout(process_handle,
+                                        &exit_code,
+                                        timeout)) {
+    *was_timeout = true;
+    exit_code = -1;  // Set a non-zero exit code to signal a failure.
+
+    // Ensure that the process terminates.
+    base::KillProcess(process_handle, -1, true);
+  }
+
+  {
+    // Note how we grab the log before issuing a possibly broad process kill.
+    // Other code parts that grab the log kill processes, so avoid trying
+    // to do that twice and trigger all kinds of log messages.
+    AutoLock lock(g_live_processes_lock.Get());
+
+#if defined(OS_POSIX)
+    if (exit_code != 0) {
+      // On POSIX, in case the test does not exit cleanly, either due to a crash
+      // or due to it timing out, we need to clean up any child processes that
+      // it might have created. On Windows, child processes are automatically
+      // cleaned up using JobObjects.
+      base::KillProcessGroup(process_handle);
+    }
+#endif
+
+    g_live_processes.Get().erase(process_handle);
+  }
+
+  base::CloseProcessHandle(process_handle);
+
+  return exit_code;
+}
+
+void RunCallback(
+    const TestLauncher::LaunchChildGTestProcessCallback& callback,
+    int exit_code,
+    const TimeDelta& elapsed_time,
+    bool was_timeout,
+    const std::string& output) {
+  callback.Run(exit_code, elapsed_time, was_timeout, output);
+}
+
+void DoLaunchChildTestProcess(
+    const CommandLine& command_line,
+    base::TimeDelta timeout,
+    int flags,
+    bool redirect_stdio,
+    scoped_refptr<MessageLoopProxy> message_loop_proxy,
+    const TestLauncher::LaunchChildGTestProcessCallback& callback) {
+  TimeTicks start_time = TimeTicks::Now();
+
+  // Redirect child process output to a file.
+  base::FilePath output_file;
+  CHECK(base::CreateTemporaryFile(&output_file));
+
+  LaunchOptions options;
+#if defined(OS_WIN)
+  win::ScopedHandle handle;
+
+  if (redirect_stdio) {
+    // Make the file handle inheritable by the child.
+    SECURITY_ATTRIBUTES sa_attr;
+    sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
+    sa_attr.lpSecurityDescriptor = NULL;
+    sa_attr.bInheritHandle = TRUE;
+
+    handle.Set(CreateFile(output_file.value().c_str(),
+                          GENERIC_WRITE,
+                          FILE_SHARE_READ | FILE_SHARE_DELETE,
+                          &sa_attr,
+                          OPEN_EXISTING,
+                          FILE_ATTRIBUTE_TEMPORARY,
+                          NULL));
+    CHECK(handle.IsValid());
+    options.inherit_handles = true;
+    options.stdin_handle = INVALID_HANDLE_VALUE;
+    options.stdout_handle = handle.Get();
+    options.stderr_handle = handle.Get();
+  }
+#elif defined(OS_POSIX)
+  options.new_process_group = true;
+
+  base::FileHandleMappingVector fds_mapping;
+  base::ScopedFD output_file_fd;
+
+  if (redirect_stdio) {
+    output_file_fd.reset(open(output_file.value().c_str(), O_RDWR));
+    CHECK(output_file_fd.is_valid());
+
+    fds_mapping.push_back(std::make_pair(output_file_fd.get(), STDOUT_FILENO));
+    fds_mapping.push_back(std::make_pair(output_file_fd.get(), STDERR_FILENO));
+    options.fds_to_remap = &fds_mapping;
+  }
+#endif
+
+  bool was_timeout = false;
+  int exit_code = LaunchChildTestProcessWithOptions(
+      command_line, options, flags, timeout, &was_timeout);
+
+  if (redirect_stdio) {
+#if defined(OS_WIN)
+    FlushFileBuffers(handle.Get());
+    handle.Close();
+#elif defined(OS_POSIX)
+    output_file_fd.reset();
+#endif
+  }
+
+  std::string output_file_contents;
+  CHECK(base::ReadFileToString(output_file, &output_file_contents));
+
+  if (!base::DeleteFile(output_file, false)) {
+    // This needs to be non-fatal at least for Windows.
+    LOG(WARNING) << "Failed to delete " << output_file.AsUTF8Unsafe();
+  }
+
+  // Run target callback on the thread it was originating from, not on
+  // a worker pool thread.
+  message_loop_proxy->PostTask(
+      FROM_HERE,
+      Bind(&RunCallback,
+           callback,
+           exit_code,
+           TimeTicks::Now() - start_time,
+           was_timeout,
+           output_file_contents));
+}
+
+}  // namespace
+
+const char kGTestFilterFlag[] = "gtest_filter";
+const char kGTestHelpFlag[]   = "gtest_help";
+const char kGTestListTestsFlag[] = "gtest_list_tests";
+const char kGTestRepeatFlag[] = "gtest_repeat";
+const char kGTestRunDisabledTestsFlag[] = "gtest_also_run_disabled_tests";
+const char kGTestOutputFlag[] = "gtest_output";
+
+TestLauncherDelegate::~TestLauncherDelegate() {
+}
+
+TestLauncher::TestLauncher(TestLauncherDelegate* launcher_delegate,
+                           size_t parallel_jobs)
+    : launcher_delegate_(launcher_delegate),
+      total_shards_(1),
+      shard_index_(0),
+      cycles_(1),
+      test_started_count_(0),
+      test_finished_count_(0),
+      test_success_count_(0),
+      test_broken_count_(0),
+      retry_count_(0),
+      retry_limit_(0),
+      run_result_(true),
+      watchdog_timer_(FROM_HERE,
+                      TimeDelta::FromSeconds(kOutputTimeoutSeconds),
+                      this,
+                      &TestLauncher::OnOutputTimeout),
+      parallel_jobs_(parallel_jobs) {
+}
+
+TestLauncher::~TestLauncher() {
+  if (worker_pool_owner_)
+    worker_pool_owner_->pool()->Shutdown();
+}
+
+bool TestLauncher::Run() {
+  if (!Init())
+    return false;
+
+  // Value of |cycles_| changes after each iteration. Keep track of the
+  // original value.
+  int requested_cycles = cycles_;
+
+#if defined(OS_POSIX)
+  CHECK_EQ(0, pipe(g_shutdown_pipe));
+
+  struct sigaction action;
+  memset(&action, 0, sizeof(action));
+  sigemptyset(&action.sa_mask);
+  action.sa_handler = &ShutdownPipeSignalHandler;
+
+  CHECK_EQ(0, sigaction(SIGINT, &action, NULL));
+  CHECK_EQ(0, sigaction(SIGQUIT, &action, NULL));
+  CHECK_EQ(0, sigaction(SIGTERM, &action, NULL));
+
+  MessageLoopForIO::FileDescriptorWatcher controller;
+  SignalFDWatcher watcher;
+
+  CHECK(MessageLoopForIO::current()->WatchFileDescriptor(
+            g_shutdown_pipe[0],
+            true,
+            MessageLoopForIO::WATCH_READ,
+            &controller,
+            &watcher));
+#endif  // defined(OS_POSIX)
+
+  // Start the watchdog timer.
+  watchdog_timer_.Reset();
+
+  MessageLoop::current()->PostTask(
+      FROM_HERE,
+      Bind(&TestLauncher::RunTestIteration, Unretained(this)));
+
+  MessageLoop::current()->Run();
+
+  if (requested_cycles != 1)
+    results_tracker_.PrintSummaryOfAllIterations();
+
+  MaybeSaveSummaryAsJSON();
+
+  return run_result_;
+}
+
+void TestLauncher::LaunchChildGTestProcess(
+    const CommandLine& command_line,
+    const std::string& wrapper,
+    base::TimeDelta timeout,
+    int flags,
+    const LaunchChildGTestProcessCallback& callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Record the exact command line used to launch the child.
+  CommandLine new_command_line(
+      PrepareCommandLineForGTest(command_line, wrapper));
+
+  // When running in parallel mode we need to redirect stdio to avoid mixed-up
+  // output. We also always redirect on the bots to get the test output into
+  // JSON summary.
+  bool redirect_stdio = (parallel_jobs_ > 1) || BotModeEnabled();
+
+  worker_pool_owner_->pool()->PostWorkerTask(
+      FROM_HERE,
+      Bind(&DoLaunchChildTestProcess,
+           new_command_line,
+           timeout,
+           flags,
+           redirect_stdio,
+           MessageLoopProxy::current(),
+           Bind(&TestLauncher::OnLaunchTestProcessFinished,
+                Unretained(this),
+                callback)));
+}
+
+void TestLauncher::OnTestFinished(const TestResult& result) {
+  ++test_finished_count_;
+
+  bool print_snippet = false;
+  std::string print_test_stdio("auto");
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kTestLauncherPrintTestStdio)) {
+    print_test_stdio = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+        switches::kTestLauncherPrintTestStdio);
+  }
+  if (print_test_stdio == "auto") {
+    print_snippet = (result.status != TestResult::TEST_SUCCESS);
+  } else if (print_test_stdio == "always") {
+    print_snippet = true;
+  } else if (print_test_stdio == "never") {
+    print_snippet = false;
+  } else {
+    LOG(WARNING) << "Invalid value of " << switches::kTestLauncherPrintTestStdio
+                 << ": " << print_test_stdio;
+  }
+  if (print_snippet) {
+    std::vector<std::string> snippet_lines;
+    SplitString(result.output_snippet, '\n', &snippet_lines);
+    if (snippet_lines.size() > kOutputSnippetLinesLimit) {
+      size_t truncated_size = snippet_lines.size() - kOutputSnippetLinesLimit;
+      snippet_lines.erase(
+          snippet_lines.begin(),
+          snippet_lines.begin() + truncated_size);
+      snippet_lines.insert(snippet_lines.begin(), "<truncated>");
+    }
+    fprintf(stdout, "%s", JoinString(snippet_lines, "\n").c_str());
+    fflush(stdout);
+  }
+
+  if (result.status == TestResult::TEST_SUCCESS) {
+    ++test_success_count_;
+  } else {
+    tests_to_retry_.insert(result.full_name);
+  }
+
+  results_tracker_.AddTestResult(result);
+
+  // TODO(phajdan.jr): Align counter (padding).
+  std::string status_line(
+      StringPrintf("[%" PRIuS "/%" PRIuS "] %s ",
+                   test_finished_count_,
+                   test_started_count_,
+                   result.full_name.c_str()));
+  if (result.completed()) {
+    status_line.append(StringPrintf("(%" PRId64 " ms)",
+                                    result.elapsed_time.InMilliseconds()));
+  } else if (result.status == TestResult::TEST_TIMEOUT) {
+    status_line.append("(TIMED OUT)");
+  } else if (result.status == TestResult::TEST_CRASH) {
+    status_line.append("(CRASHED)");
+  } else if (result.status == TestResult::TEST_SKIPPED) {
+    status_line.append("(SKIPPED)");
+  } else if (result.status == TestResult::TEST_UNKNOWN) {
+    status_line.append("(UNKNOWN)");
+  } else {
+    // Fail very loudly so it's not ignored.
+    CHECK(false) << "Unhandled test result status: " << result.status;
+  }
+  fprintf(stdout, "%s\n", status_line.c_str());
+  fflush(stdout);
+
+  // We just printed a status line, reset the watchdog timer.
+  watchdog_timer_.Reset();
+
+  // Do not waste time on timeouts. We include tests with unknown results here
+  // because sometimes (e.g. hang in between unit tests) that's how a timeout
+  // gets reported.
+  if (result.status == TestResult::TEST_TIMEOUT ||
+      result.status == TestResult::TEST_UNKNOWN) {
+    test_broken_count_++;
+  }
+  size_t broken_threshold =
+      std::max(static_cast<size_t>(20), test_started_count_ / 10);
+  if (test_broken_count_ >= broken_threshold) {
+    fprintf(stdout, "Too many badly broken tests (%" PRIuS "), exiting now.\n",
+            test_broken_count_);
+    fflush(stdout);
+
+#if defined(OS_POSIX)
+    KillSpawnedTestProcesses();
+#endif  // defined(OS_POSIX)
+
+    results_tracker_.AddGlobalTag("BROKEN_TEST_EARLY_EXIT");
+    results_tracker_.AddGlobalTag(kUnreliableResultsTag);
+    MaybeSaveSummaryAsJSON();
+
+    exit(1);
+  }
+
+  if (test_finished_count_ != test_started_count_)
+    return;
+
+  if (tests_to_retry_.empty() || retry_count_ >= retry_limit_) {
+    OnTestIterationFinished();
+    return;
+  }
+
+  if (tests_to_retry_.size() >= broken_threshold) {
+    fprintf(stdout,
+            "Too many failing tests (%" PRIuS "), skipping retries.\n",
+            tests_to_retry_.size());
+    fflush(stdout);
+
+    results_tracker_.AddGlobalTag("BROKEN_TEST_SKIPPED_RETRIES");
+    results_tracker_.AddGlobalTag(kUnreliableResultsTag);
+
+    OnTestIterationFinished();
+    return;
+  }
+
+  retry_count_++;
+
+  std::vector<std::string> test_names(tests_to_retry_.begin(),
+                                      tests_to_retry_.end());
+
+  tests_to_retry_.clear();
+
+  size_t retry_started_count = launcher_delegate_->RetryTests(this, test_names);
+  if (retry_started_count == 0) {
+    // Signal failure, but continue to run all requested test iterations.
+    // With the summary of all iterations at the end this is a good default.
+    run_result_ = false;
+
+    OnTestIterationFinished();
+    return;
+  }
+
+  fprintf(stdout, "Retrying %" PRIuS " test%s (retry #%" PRIuS ")\n",
+          retry_started_count,
+          retry_started_count > 1 ? "s" : "",
+          retry_count_);
+  fflush(stdout);
+
+  test_started_count_ += retry_started_count;
+}
+
+// static
+std::string TestLauncher::FormatFullTestName(const std::string& test_case_name,
+                                             const std::string& test_name) {
+  return test_case_name + "." + test_name;
+}
+
+bool TestLauncher::Init() {
+  const CommandLine* command_line = CommandLine::ForCurrentProcess();
+
+  // Initialize sharding. Command line takes precedence over legacy environment
+  // variables.
+  if (command_line->HasSwitch(switches::kTestLauncherTotalShards) &&
+      command_line->HasSwitch(switches::kTestLauncherShardIndex)) {
+    if (!StringToInt(
+            command_line->GetSwitchValueASCII(
+                switches::kTestLauncherTotalShards),
+            &total_shards_)) {
+      LOG(ERROR) << "Invalid value for " << switches::kTestLauncherTotalShards;
+      return false;
+    }
+    if (!StringToInt(
+            command_line->GetSwitchValueASCII(
+                switches::kTestLauncherShardIndex),
+            &shard_index_)) {
+      LOG(ERROR) << "Invalid value for " << switches::kTestLauncherShardIndex;
+      return false;
+    }
+    fprintf(stdout,
+            "Using sharding settings from command line. This is shard %d/%d\n",
+            shard_index_, total_shards_);
+    fflush(stdout);
+  } else {
+    if (!TakeInt32FromEnvironment(kTestTotalShards, &total_shards_))
+      return false;
+    if (!TakeInt32FromEnvironment(kTestShardIndex, &shard_index_))
+      return false;
+    fprintf(stdout,
+            "Using sharding settings from environment. This is shard %d/%d\n",
+            shard_index_, total_shards_);
+    fflush(stdout);
+  }
+  if (shard_index_ < 0 ||
+      total_shards_ < 0 ||
+      shard_index_ >= total_shards_) {
+    LOG(ERROR) << "Invalid sharding settings: we require 0 <= "
+               << kTestShardIndex << " < " << kTestTotalShards
+               << ", but you have " << kTestShardIndex << "=" << shard_index_
+               << ", " << kTestTotalShards << "=" << total_shards_ << ".\n";
+    return false;
+  }
+
+  // Make sure we don't pass any sharding-related environment to the child
+  // processes. This test launcher implements the sharding completely.
+  CHECK(UnsetEnvironmentVariableIfExists("GTEST_TOTAL_SHARDS"));
+  CHECK(UnsetEnvironmentVariableIfExists("GTEST_SHARD_INDEX"));
+
+  if (command_line->HasSwitch(kGTestRepeatFlag) &&
+      !StringToInt(command_line->GetSwitchValueASCII(kGTestRepeatFlag),
+                   &cycles_)) {
+    LOG(ERROR) << "Invalid value for " << kGTestRepeatFlag;
+    return false;
+  }
+
+  if (command_line->HasSwitch(switches::kTestLauncherRetryLimit)) {
+    int retry_limit = -1;
+    if (!StringToInt(command_line->GetSwitchValueASCII(
+                         switches::kTestLauncherRetryLimit), &retry_limit) ||
+        retry_limit < 0) {
+      LOG(ERROR) << "Invalid value for " << switches::kTestLauncherRetryLimit;
+      return false;
+    }
+
+    retry_limit_ = retry_limit;
+  } else if (!command_line->HasSwitch(kGTestFilterFlag) || BotModeEnabled()) {
+    // Retry failures 3 times by default if we are running all of the tests or
+    // in bot mode.
+    retry_limit_ = 3;
+  }
+
+  if (command_line->HasSwitch(switches::kTestLauncherJobs)) {
+    int jobs = -1;
+    if (!StringToInt(command_line->GetSwitchValueASCII(
+                         switches::kTestLauncherJobs), &jobs) ||
+        jobs < 0) {
+      LOG(ERROR) << "Invalid value for " << switches::kTestLauncherJobs;
+      return false;
+    }
+
+    parallel_jobs_ = jobs;
+  } else if (command_line->HasSwitch(kGTestFilterFlag) && !BotModeEnabled()) {
+    // Do not run jobs in parallel by default if we are running a subset of
+    // the tests and if bot mode is off.
+    parallel_jobs_ = 1;
+  }
+
+  fprintf(stdout, "Using %" PRIuS " parallel jobs.\n", parallel_jobs_);
+  fflush(stdout);
+  worker_pool_owner_.reset(
+      new SequencedWorkerPoolOwner(parallel_jobs_, "test_launcher"));
+
+  if (command_line->HasSwitch(switches::kTestLauncherFilterFile) &&
+      command_line->HasSwitch(kGTestFilterFlag)) {
+    LOG(ERROR) << "Only one of --test-launcher-filter-file and --gtest_filter "
+               << "at a time is allowed.";
+    return false;
+  }
+
+  if (command_line->HasSwitch(switches::kTestLauncherFilterFile)) {
+    std::string filter;
+    if (!ReadFileToString(
+            command_line->GetSwitchValuePath(switches::kTestLauncherFilterFile),
+            &filter)) {
+      LOG(ERROR) << "Failed to read the filter file.";
+      return false;
+    }
+
+    std::vector<std::string> filter_lines;
+    SplitString(filter, '\n', &filter_lines);
+    for (size_t i = 0; i < filter_lines.size(); i++) {
+      if (filter_lines[i].empty())
+        continue;
+
+      if (filter_lines[i][0] == '-')
+        negative_test_filter_.push_back(filter_lines[i].substr(1));
+      else
+        positive_test_filter_.push_back(filter_lines[i]);
+    }
+  } else {
+    // Split --gtest_filter at '-', if there is one, to separate into
+    // positive filter and negative filter portions.
+    std::string filter = command_line->GetSwitchValueASCII(kGTestFilterFlag);
+    size_t dash_pos = filter.find('-');
+    if (dash_pos == std::string::npos) {
+      SplitString(filter, ':', &positive_test_filter_);
+    } else {
+      // Everything up to the dash.
+      SplitString(filter.substr(0, dash_pos), ':', &positive_test_filter_);
+
+      // Everything after the dash.
+      SplitString(filter.substr(dash_pos + 1), ':', &negative_test_filter_);
+    }
+  }
+
+  if (!results_tracker_.Init(*command_line)) {
+    LOG(ERROR) << "Failed to initialize test results tracker.";
+    return 1;
+  }
+
+#if defined(NDEBUG)
+  results_tracker_.AddGlobalTag("MODE_RELEASE");
+#else
+  results_tracker_.AddGlobalTag("MODE_DEBUG");
+#endif
+
+  // Operating systems (sorted alphabetically).
+  // Note that they can deliberately overlap, e.g. OS_LINUX is a subset
+  // of OS_POSIX.
+#if defined(OS_ANDROID)
+  results_tracker_.AddGlobalTag("OS_ANDROID");
+#endif
+
+#if defined(OS_BSD)
+  results_tracker_.AddGlobalTag("OS_BSD");
+#endif
+
+#if defined(OS_FREEBSD)
+  results_tracker_.AddGlobalTag("OS_FREEBSD");
+#endif
+
+#if defined(OS_IOS)
+  results_tracker_.AddGlobalTag("OS_IOS");
+#endif
+
+#if defined(OS_LINUX)
+  results_tracker_.AddGlobalTag("OS_LINUX");
+#endif
+
+#if defined(OS_MACOSX)
+  results_tracker_.AddGlobalTag("OS_MACOSX");
+#endif
+
+#if defined(OS_NACL)
+  results_tracker_.AddGlobalTag("OS_NACL");
+#endif
+
+#if defined(OS_OPENBSD)
+  results_tracker_.AddGlobalTag("OS_OPENBSD");
+#endif
+
+#if defined(OS_POSIX)
+  results_tracker_.AddGlobalTag("OS_POSIX");
+#endif
+
+#if defined(OS_SOLARIS)
+  results_tracker_.AddGlobalTag("OS_SOLARIS");
+#endif
+
+#if defined(OS_WIN)
+  results_tracker_.AddGlobalTag("OS_WIN");
+#endif
+
+  // CPU-related tags.
+#if defined(ARCH_CPU_32_BITS)
+  results_tracker_.AddGlobalTag("CPU_32_BITS");
+#endif
+
+#if defined(ARCH_CPU_64_BITS)
+  results_tracker_.AddGlobalTag("CPU_64_BITS");
+#endif
+
+  return true;
+}
+
+void TestLauncher::RunTests() {
+  testing::UnitTest* const unit_test = testing::UnitTest::GetInstance();
+
+  std::vector<std::string> test_names;
+
+  for (int i = 0; i < unit_test->total_test_case_count(); ++i) {
+    const testing::TestCase* test_case = unit_test->GetTestCase(i);
+    for (int j = 0; j < test_case->total_test_count(); ++j) {
+      const testing::TestInfo* test_info = test_case->GetTestInfo(j);
+      std::string test_name = FormatFullTestName(
+          test_info->test_case_name(), test_info->name());
+
+      results_tracker_.AddTest(test_name);
+
+      const CommandLine* command_line = CommandLine::ForCurrentProcess();
+      if (test_name.find("DISABLED") != std::string::npos) {
+        results_tracker_.AddDisabledTest(test_name);
+
+        // Skip disabled tests unless explicitly requested.
+        if (!command_line->HasSwitch(kGTestRunDisabledTestsFlag))
+          continue;
+      }
+
+      if (!launcher_delegate_->ShouldRunTest(test_case, test_info))
+        continue;
+
+      // Skip the test that doesn't match the filter (if given).
+      if (!positive_test_filter_.empty()) {
+        bool found = false;
+        for (size_t k = 0; k < positive_test_filter_.size(); ++k) {
+          if (MatchPattern(test_name, positive_test_filter_[k])) {
+            found = true;
+            break;
+          }
+        }
+
+        if (!found)
+          continue;
+      }
+      bool excluded = false;
+      for (size_t k = 0; k < negative_test_filter_.size(); ++k) {
+        if (MatchPattern(test_name, negative_test_filter_[k])) {
+          excluded = true;
+          break;
+        }
+      }
+      if (excluded)
+        continue;
+
+      if (base::Hash(test_name) % total_shards_ !=
+          static_cast<uint32>(shard_index_)) {
+        continue;
+      }
+
+      test_names.push_back(test_name);
+    }
+  }
+
+  test_started_count_ = launcher_delegate_->RunTests(this, test_names);
+
+  if (test_started_count_ == 0) {
+    fprintf(stdout, "0 tests run\n");
+    fflush(stdout);
+
+    // No tests have actually been started, so kick off the next iteration.
+    MessageLoop::current()->PostTask(
+        FROM_HERE,
+        Bind(&TestLauncher::RunTestIteration, Unretained(this)));
+  }
+}
+
+void TestLauncher::RunTestIteration() {
+  if (cycles_ == 0) {
+    MessageLoop::current()->Quit();
+    return;
+  }
+
+  // Special value "-1" means "repeat indefinitely".
+  cycles_ = (cycles_ == -1) ? cycles_ : cycles_ - 1;
+
+  test_started_count_ = 0;
+  test_finished_count_ = 0;
+  test_success_count_ = 0;
+  test_broken_count_ = 0;
+  retry_count_ = 0;
+  tests_to_retry_.clear();
+  results_tracker_.OnTestIterationStarting();
+
+  MessageLoop::current()->PostTask(
+      FROM_HERE, Bind(&TestLauncher::RunTests, Unretained(this)));
+}
+
+void TestLauncher::MaybeSaveSummaryAsJSON() {
+  const CommandLine* command_line = CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kTestLauncherSummaryOutput)) {
+    FilePath summary_path(command_line->GetSwitchValuePath(
+                              switches::kTestLauncherSummaryOutput));
+    if (!results_tracker_.SaveSummaryAsJSON(summary_path)) {
+      LOG(ERROR) << "Failed to save test launcher output summary.";
+    }
+  }
+}
+
+void TestLauncher::OnLaunchTestProcessFinished(
+    const LaunchChildGTestProcessCallback& callback,
+    int exit_code,
+    const TimeDelta& elapsed_time,
+    bool was_timeout,
+    const std::string& output) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  callback.Run(exit_code, elapsed_time, was_timeout, output);
+}
+
+void TestLauncher::OnTestIterationFinished() {
+  TestResultsTracker::TestStatusMap tests_by_status(
+      results_tracker_.GetTestStatusMapForCurrentIteration());
+  if (!tests_by_status[TestResult::TEST_UNKNOWN].empty())
+    results_tracker_.AddGlobalTag(kUnreliableResultsTag);
+
+  // When we retry tests, success is determined by having nothing more
+  // to retry (everything eventually passed), as opposed to having
+  // no failures at all.
+  if (tests_to_retry_.empty()) {
+    fprintf(stdout, "SUCCESS: all tests passed.\n");
+    fflush(stdout);
+  } else {
+    // Signal failure, but continue to run all requested test iterations.
+    // With the summary of all iterations at the end this is a good default.
+    run_result_ = false;
+  }
+
+  results_tracker_.PrintSummaryOfCurrentIteration();
+
+  // Kick off the next iteration.
+  MessageLoop::current()->PostTask(
+      FROM_HERE,
+      Bind(&TestLauncher::RunTestIteration, Unretained(this)));
+}
+
+void TestLauncher::OnOutputTimeout() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  AutoLock lock(g_live_processes_lock.Get());
+
+  fprintf(stdout, "Still waiting for the following processes to finish:\n");
+
+  for (std::map<ProcessHandle, CommandLine>::iterator i =
+           g_live_processes.Get().begin();
+       i != g_live_processes.Get().end();
+       ++i) {
+#if defined(OS_WIN)
+    fwprintf(stdout, L"\t%s\n", i->second.GetCommandLineString().c_str());
+#else
+    fprintf(stdout, "\t%s\n", i->second.GetCommandLineString().c_str());
+#endif
+  }
+
+  fflush(stdout);
+
+  // Arm the timer again - otherwise it would fire only once.
+  watchdog_timer_.Reset();
+}
+
+std::string GetTestOutputSnippet(const TestResult& result,
+                                 const std::string& full_output) {
+  size_t run_pos = full_output.find(std::string("[ RUN      ] ") +
+                                    result.full_name);
+  if (run_pos == std::string::npos)
+    return std::string();
+
+  size_t end_pos = full_output.find(std::string("[  FAILED  ] ") +
+                                    result.full_name,
+                                    run_pos);
+  // Only clip the snippet to the "OK" message if the test really
+  // succeeded. It still might have e.g. crashed after printing it.
+  if (end_pos == std::string::npos &&
+      result.status == TestResult::TEST_SUCCESS) {
+    end_pos = full_output.find(std::string("[       OK ] ") +
+                               result.full_name,
+                               run_pos);
+  }
+  if (end_pos != std::string::npos) {
+    size_t newline_pos = full_output.find("\n", end_pos);
+    if (newline_pos != std::string::npos)
+      end_pos = newline_pos + 1;
+  }
+
+  std::string snippet(full_output.substr(run_pos));
+  if (end_pos != std::string::npos)
+    snippet = full_output.substr(run_pos, end_pos - run_pos);
+
+  return snippet;
+}
+
+}  // namespace base
diff --git a/base/test/launcher/test_launcher.h b/base/test/launcher/test_launcher.h
new file mode 100644
index 0000000..8c72ee7
--- /dev/null
+++ b/base/test/launcher/test_launcher.h
@@ -0,0 +1,206 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_LAUNCHER_TEST_LAUNCHER_H_
+#define BASE_TEST_LAUNCHER_TEST_LAUNCHER_H_
+
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/test/launcher/test_result.h"
+#include "base/test/launcher/test_results_tracker.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+
+namespace testing {
+class TestCase;
+class TestInfo;
+}
+
+namespace base {
+
+class CommandLine;
+struct LaunchOptions;
+class SequencedWorkerPoolOwner;
+class TestLauncher;
+
+// Constants for GTest command-line flags.
+extern const char kGTestFilterFlag[];
+extern const char kGTestHelpFlag[];
+extern const char kGTestListTestsFlag[];
+extern const char kGTestRepeatFlag[];
+extern const char kGTestRunDisabledTestsFlag[];
+extern const char kGTestOutputFlag[];
+
+// Interface for use with LaunchTests that abstracts away exact details
+// which tests and how are run.
+class TestLauncherDelegate {
+ public:
+  // Called before a test is considered for running. If it returns false,
+  // the test is not run. If it returns true, the test will be run provided
+  // it is part of the current shard.
+  virtual bool ShouldRunTest(const testing::TestCase* test_case,
+                             const testing::TestInfo* test_info) = 0;
+
+  // Called to make the delegate run the specified tests. The delegate must
+  // return the number of actual tests it's going to run (can be smaller,
+  // equal to, or larger than size of |test_names|). It must also call
+  // |test_launcher|'s OnTestFinished method once per every run test,
+  // regardless of its success.
+  virtual size_t RunTests(TestLauncher* test_launcher,
+                          const std::vector<std::string>& test_names) = 0;
+
+  // Called to make the delegate retry the specified tests. The delegate must
+  // return the number of actual tests it's going to retry (can be smaller,
+  // equal to, or larger than size of |test_names|). It must also call
+  // |test_launcher|'s OnTestFinished method once per every retried test,
+  // regardless of its success.
+  virtual size_t RetryTests(TestLauncher* test_launcher,
+                            const std::vector<std::string>& test_names) = 0;
+
+ protected:
+  virtual ~TestLauncherDelegate();
+};
+
+// Launches tests using a TestLauncherDelegate.
+class TestLauncher {
+ public:
+  // Flags controlling behavior of LaunchChildGTestProcess.
+  enum LaunchChildGTestProcessFlags {
+    // Allows usage of job objects on Windows. Helps properly clean up child
+    // processes.
+    USE_JOB_OBJECTS = (1 << 0),
+
+    // Allows breakaway from job on Windows. May result in some child processes
+    // not being properly terminated after launcher dies if these processes
+    // fail to cooperate.
+    ALLOW_BREAKAWAY_FROM_JOB = (1 << 1),
+  };
+
+  // Constructor. |parallel_jobs| is the limit of simultaneous parallel test
+  // jobs.
+  TestLauncher(TestLauncherDelegate* launcher_delegate, size_t parallel_jobs);
+  ~TestLauncher();
+
+  // Runs the launcher. Must be called at most once.
+  bool Run() WARN_UNUSED_RESULT;
+
+  // Callback called after a child process finishes. First argument is the exit
+  // code, second one is child process elapsed time, third one is true if
+  // the child process was terminated because of a timeout, and fourth one
+  // contains output of the child (stdout and stderr together).
+  typedef Callback<void(int, const TimeDelta&, bool, const std::string&)>
+      LaunchChildGTestProcessCallback;
+
+  // Launches a child process (assumed to be gtest-based binary) using
+  // |command_line|. If |wrapper| is not empty, it is prepended to the final
+  // command line. If the child process is still running after |timeout|, it
+  // is terminated. After the child process finishes |callback| is called
+  // on the same thread this method was called.
+  void LaunchChildGTestProcess(const CommandLine& command_line,
+                               const std::string& wrapper,
+                               base::TimeDelta timeout,
+                               int flags,
+                               const LaunchChildGTestProcessCallback& callback);
+
+  // Called when a test has finished running.
+  void OnTestFinished(const TestResult& result);
+
+  // Constructs a full test name given a test case name and a test name.
+  static std::string FormatFullTestName(const std::string& test_case_name,
+                                        const std::string& test_name);
+
+ private:
+  bool Init() WARN_UNUSED_RESULT;
+
+  // Runs all tests in current iteration. Uses callbacks to communicate success.
+  void RunTests();
+
+  void RunTestIteration();
+
+  // Saves test results summary as JSON if requested from command line.
+  void MaybeSaveSummaryAsJSON();
+
+  // Called on a worker thread after a child process finishes.
+  void OnLaunchTestProcessFinished(
+      const LaunchChildGTestProcessCallback& callback,
+      int exit_code,
+      const TimeDelta& elapsed_time,
+      bool was_timeout,
+      const std::string& output);
+
+  // Called when a test iteration is finished.
+  void OnTestIterationFinished();
+
+  // Called by the delay timer when no output was made for a while.
+  void OnOutputTimeout();
+
+  // Make sure we don't accidentally call the wrong methods e.g. on the worker
+  // pool thread. With lots of callbacks used this is non-trivial.
+  // Should be the first member so that it's destroyed last: when destroying
+  // other members, especially the worker pool, we may check the code is running
+  // on the correct thread.
+  ThreadChecker thread_checker_;
+
+  TestLauncherDelegate* launcher_delegate_;
+
+  // Support for outer sharding, just like gtest does.
+  int32 total_shards_;  // Total number of outer shards, at least one.
+  int32 shard_index_;   // Index of shard the launcher is to run.
+
+  int cycles_;  // Number of remaining test itreations, or -1 for infinite.
+
+  // Test filters (empty means no filter).
+  std::vector<std::string> positive_test_filter_;
+  std::vector<std::string> negative_test_filter_;
+
+  // Number of tests started in this iteration.
+  size_t test_started_count_;
+
+  // Number of tests finished in this iteration.
+  size_t test_finished_count_;
+
+  // Number of tests successfully finished in this iteration.
+  size_t test_success_count_;
+
+  // Number of tests either timing out or having an unknown result,
+  // likely indicating a more systemic problem if widespread.
+  size_t test_broken_count_;
+
+  // Number of retries in this iteration.
+  size_t retry_count_;
+
+  // Maximum number of retries per iteration.
+  size_t retry_limit_;
+
+  // Tests to retry in this iteration.
+  std::set<std::string> tests_to_retry_;
+
+  // Result to be returned from Run.
+  bool run_result_;
+
+  TestResultsTracker results_tracker_;
+
+  // Watchdog timer to make sure we do not go without output for too long.
+  DelayTimer<TestLauncher> watchdog_timer_;
+
+  // Number of jobs to run in parallel.
+  size_t parallel_jobs_;
+
+  // Worker pool used to launch processes in parallel.
+  scoped_ptr<SequencedWorkerPoolOwner> worker_pool_owner_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestLauncher);
+};
+
+// Extract part from |full_output| that applies to |result|.
+std::string GetTestOutputSnippet(const TestResult& result,
+                                 const std::string& full_output);
+
+}  // namespace base
+
+#endif  // BASE_TEST_LAUNCHER_TEST_LAUNCHER_H_
diff --git a/base/test/launcher/test_result.cc b/base/test/launcher/test_result.cc
new file mode 100644
index 0000000..70d7a80
--- /dev/null
+++ b/base/test/launcher/test_result.cc
@@ -0,0 +1,52 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/launcher/test_result.h"
+
+#include "base/logging.h"
+
+namespace base {
+
+TestResult::TestResult() : status(TEST_UNKNOWN) {
+}
+
+TestResult::~TestResult() {
+}
+
+std::string TestResult::StatusAsString() const {
+  switch (status) {
+    case TEST_UNKNOWN:
+      return "UNKNOWN";
+    case TEST_SUCCESS:
+      return "SUCCESS";
+    case TEST_FAILURE:
+      return "FAILURE";
+    case TEST_FAILURE_ON_EXIT:
+      return "FAILURE_ON_EXIT";
+    case TEST_CRASH:
+      return "CRASH";
+    case TEST_TIMEOUT:
+      return "TIMEOUT";
+    case TEST_SKIPPED:
+      return "SKIPPED";
+     // Rely on compiler warnings to ensure all possible values are handled.
+  }
+
+  NOTREACHED();
+  return std::string();
+}
+
+std::string TestResult::GetTestName() const {
+  size_t dot_pos = full_name.find('.');
+  CHECK_NE(dot_pos, std::string::npos);
+  return full_name.substr(dot_pos + 1);
+}
+
+std::string TestResult::GetTestCaseName() const {
+  size_t dot_pos = full_name.find('.');
+  CHECK_NE(dot_pos, std::string::npos);
+  return full_name.substr(0, dot_pos);
+}
+
+}  // namespace base
diff --git a/base/test/launcher/test_result.h b/base/test/launcher/test_result.h
new file mode 100644
index 0000000..b61cdd4
--- /dev/null
+++ b/base/test/launcher/test_result.h
@@ -0,0 +1,61 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_LAUNCHER_TEST_RESULT_H_
+#define BASE_TEST_LAUNCHER_TEST_RESULT_H_
+
+#include <string>
+
+#include "base/time/time.h"
+
+namespace base {
+
+// Structure containing result of a single test.
+struct TestResult {
+  enum Status {
+    TEST_UNKNOWN,          // Status not set.
+    TEST_SUCCESS,          // Test passed.
+    TEST_FAILURE,          // Assertion failure (think EXPECT_TRUE, not DCHECK).
+    TEST_FAILURE_ON_EXIT,  // Test passed but executable exit code was non-zero.
+    TEST_TIMEOUT,          // Test timed out and was killed.
+    TEST_CRASH,            // Test crashed (includes CHECK/DCHECK failures).
+    TEST_SKIPPED,          // Test skipped (not run at all).
+  };
+
+  TestResult();
+  ~TestResult();
+
+  // Returns the test status as string (e.g. for display).
+  std::string StatusAsString() const;
+
+  // Returns the test name (e.g. "B" for "A.B").
+  std::string GetTestName() const;
+
+  // Returns the test case name (e.g. "A" for "A.B").
+  std::string GetTestCaseName() const;
+
+  // Returns true if the test has completed (i.e. the test binary exited
+  // normally, possibly with an exit code indicating failure, but didn't crash
+  // or time out in the middle of the test).
+  bool completed() const {
+    return status == TEST_SUCCESS ||
+        status == TEST_FAILURE ||
+        status == TEST_FAILURE_ON_EXIT;
+  }
+
+  // Full name of the test (e.g. "A.B").
+  std::string full_name;
+
+  Status status;
+
+  // Time it took to run the test.
+  base::TimeDelta elapsed_time;
+
+  // Output of just this test (optional).
+  std::string output_snippet;
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_LAUNCHER_TEST_RESULT_H_
diff --git a/base/test/launcher/test_results_tracker.cc b/base/test/launcher/test_results_tracker.cc
new file mode 100644
index 0000000..b553fd6
--- /dev/null
+++ b/base/test/launcher/test_results_tracker.cc
@@ -0,0 +1,360 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/launcher/test_results_tracker.h"
+
+#include "base/base64.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/format_macros.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/json/string_escape.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/launcher/test_launcher.h"
+#include "base/values.h"
+
+namespace base {
+
+namespace {
+
+// The default output file for XML output.
+const FilePath::CharType kDefaultOutputFile[] = FILE_PATH_LITERAL(
+    "test_detail.xml");
+
+// Utility function to print a list of test names. Uses iterator to be
+// compatible with different containers, like vector and set.
+template<typename InputIterator>
+void PrintTests(InputIterator first,
+                InputIterator last,
+                const std::string& description) {
+  size_t count = std::distance(first, last);
+  if (count == 0)
+    return;
+
+  fprintf(stdout,
+          "%" PRIuS " test%s %s:\n",
+          count,
+          count != 1 ? "s" : "",
+          description.c_str());
+  for (InputIterator i = first; i != last; ++i)
+    fprintf(stdout, "    %s\n", (*i).c_str());
+  fflush(stdout);
+}
+
+std::string TestNameWithoutDisabledPrefix(const std::string& test_name) {
+  std::string test_name_no_disabled(test_name);
+  ReplaceSubstringsAfterOffset(&test_name_no_disabled, 0, "DISABLED_", "");
+  return test_name_no_disabled;
+}
+
+}  // namespace
+
+TestResultsTracker::TestResultsTracker() : iteration_(-1), out_(NULL) {
+}
+
+TestResultsTracker::~TestResultsTracker() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (!out_)
+    return;
+  fprintf(out_, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+  fprintf(out_, "<testsuites name=\"AllTests\" tests=\"\" failures=\"\""
+          " disabled=\"\" errors=\"\" time=\"\">\n");
+
+  // Maps test case names to test results.
+  typedef std::map<std::string, std::vector<TestResult> > TestCaseMap;
+  TestCaseMap test_case_map;
+
+  for (PerIterationData::ResultsMap::iterator i =
+           per_iteration_data_[iteration_].results.begin();
+       i != per_iteration_data_[iteration_].results.end();
+       ++i) {
+    // Use the last test result as the final one.
+    TestResult result = i->second.test_results.back();
+    test_case_map[result.GetTestCaseName()].push_back(result);
+  }
+  for (TestCaseMap::iterator i = test_case_map.begin();
+       i != test_case_map.end();
+       ++i) {
+    fprintf(out_, "  <testsuite name=\"%s\" tests=\"%" PRIuS "\" failures=\"\""
+            " disabled=\"\" errors=\"\" time=\"\">\n",
+            i->first.c_str(), i->second.size());
+    for (size_t j = 0; j < i->second.size(); ++j) {
+      const TestResult& result = i->second[j];
+      fprintf(out_, "    <testcase name=\"%s\" status=\"run\" time=\"%.3f\""
+              " classname=\"%s\">\n",
+              result.GetTestName().c_str(),
+              result.elapsed_time.InSecondsF(),
+              result.GetTestCaseName().c_str());
+      if (result.status != TestResult::TEST_SUCCESS)
+        fprintf(out_, "      <failure message=\"\" type=\"\"></failure>\n");
+      fprintf(out_, "    </testcase>\n");
+    }
+    fprintf(out_, "  </testsuite>\n");
+  }
+  fprintf(out_, "</testsuites>\n");
+  fclose(out_);
+}
+
+bool TestResultsTracker::Init(const CommandLine& command_line) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Prevent initializing twice.
+  if (out_) {
+    NOTREACHED();
+    return false;
+  }
+
+  if (!command_line.HasSwitch(kGTestOutputFlag))
+    return true;
+
+  std::string flag = command_line.GetSwitchValueASCII(kGTestOutputFlag);
+  size_t colon_pos = flag.find(':');
+  FilePath path;
+  if (colon_pos != std::string::npos) {
+    FilePath flag_path =
+        command_line.GetSwitchValuePath(kGTestOutputFlag);
+    FilePath::StringType path_string = flag_path.value();
+    path = FilePath(path_string.substr(colon_pos + 1));
+    // If the given path ends with '/', consider it is a directory.
+    // Note: This does NOT check that a directory (or file) actually exists
+    // (the behavior is same as what gtest does).
+    if (path.EndsWithSeparator()) {
+      FilePath executable = command_line.GetProgram().BaseName();
+      path = path.Append(executable.ReplaceExtension(
+                             FilePath::StringType(FILE_PATH_LITERAL("xml"))));
+    }
+  }
+  if (path.value().empty())
+    path = FilePath(kDefaultOutputFile);
+  FilePath dir_name = path.DirName();
+  if (!DirectoryExists(dir_name)) {
+    LOG(WARNING) << "The output directory does not exist. "
+                 << "Creating the directory: " << dir_name.value();
+    // Create the directory if necessary (because the gtest does the same).
+    if (!base::CreateDirectory(dir_name)) {
+      LOG(ERROR) << "Failed to created directory " << dir_name.value();
+      return false;
+    }
+  }
+  out_ = OpenFile(path, "w");
+  if (!out_) {
+    LOG(ERROR) << "Cannot open output file: "
+               << path.value() << ".";
+    return false;
+  }
+
+  return true;
+}
+
+void TestResultsTracker::OnTestIterationStarting() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Start with a fresh state for new iteration.
+  iteration_++;
+  per_iteration_data_.push_back(PerIterationData());
+}
+
+void TestResultsTracker::AddTest(const std::string& test_name) {
+  // Record disabled test names without DISABLED_ prefix so that they are easy
+  // to compare with regular test names, e.g. before or after disabling.
+  all_tests_.insert(TestNameWithoutDisabledPrefix(test_name));
+}
+
+void TestResultsTracker::AddDisabledTest(const std::string& test_name) {
+  // Record disabled test names without DISABLED_ prefix so that they are easy
+  // to compare with regular test names, e.g. before or after disabling.
+  disabled_tests_.insert(TestNameWithoutDisabledPrefix(test_name));
+}
+
+void TestResultsTracker::AddTestResult(const TestResult& result) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  per_iteration_data_[iteration_].results[
+      result.full_name].test_results.push_back(result);
+}
+
+void TestResultsTracker::PrintSummaryOfCurrentIteration() const {
+  TestStatusMap tests_by_status(GetTestStatusMapForCurrentIteration());
+
+  PrintTests(tests_by_status[TestResult::TEST_FAILURE].begin(),
+             tests_by_status[TestResult::TEST_FAILURE].end(),
+             "failed");
+  PrintTests(tests_by_status[TestResult::TEST_FAILURE_ON_EXIT].begin(),
+             tests_by_status[TestResult::TEST_FAILURE_ON_EXIT].end(),
+             "failed on exit");
+  PrintTests(tests_by_status[TestResult::TEST_TIMEOUT].begin(),
+             tests_by_status[TestResult::TEST_TIMEOUT].end(),
+             "timed out");
+  PrintTests(tests_by_status[TestResult::TEST_CRASH].begin(),
+             tests_by_status[TestResult::TEST_CRASH].end(),
+             "crashed");
+  PrintTests(tests_by_status[TestResult::TEST_SKIPPED].begin(),
+             tests_by_status[TestResult::TEST_SKIPPED].end(),
+             "skipped");
+  PrintTests(tests_by_status[TestResult::TEST_UNKNOWN].begin(),
+             tests_by_status[TestResult::TEST_UNKNOWN].end(),
+             "had unknown result");
+}
+
+void TestResultsTracker::PrintSummaryOfAllIterations() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  TestStatusMap tests_by_status(GetTestStatusMapForAllIterations());
+
+  fprintf(stdout, "Summary of all test iterations:\n");
+  fflush(stdout);
+
+  PrintTests(tests_by_status[TestResult::TEST_FAILURE].begin(),
+             tests_by_status[TestResult::TEST_FAILURE].end(),
+             "failed");
+  PrintTests(tests_by_status[TestResult::TEST_FAILURE_ON_EXIT].begin(),
+             tests_by_status[TestResult::TEST_FAILURE_ON_EXIT].end(),
+             "failed on exit");
+  PrintTests(tests_by_status[TestResult::TEST_TIMEOUT].begin(),
+             tests_by_status[TestResult::TEST_TIMEOUT].end(),
+             "timed out");
+  PrintTests(tests_by_status[TestResult::TEST_CRASH].begin(),
+             tests_by_status[TestResult::TEST_CRASH].end(),
+             "crashed");
+  PrintTests(tests_by_status[TestResult::TEST_SKIPPED].begin(),
+             tests_by_status[TestResult::TEST_SKIPPED].end(),
+             "skipped");
+  PrintTests(tests_by_status[TestResult::TEST_UNKNOWN].begin(),
+             tests_by_status[TestResult::TEST_UNKNOWN].end(),
+             "had unknown result");
+
+  fprintf(stdout, "End of the summary.\n");
+  fflush(stdout);
+}
+
+void TestResultsTracker::AddGlobalTag(const std::string& tag) {
+  global_tags_.insert(tag);
+}
+
+bool TestResultsTracker::SaveSummaryAsJSON(const FilePath& path) const {
+  scoped_ptr<DictionaryValue> summary_root(new DictionaryValue);
+
+  ListValue* global_tags = new ListValue;
+  summary_root->Set("global_tags", global_tags);
+
+  for (std::set<std::string>::const_iterator i = global_tags_.begin();
+       i != global_tags_.end();
+       ++i) {
+    global_tags->AppendString(*i);
+  }
+
+  ListValue* all_tests = new ListValue;
+  summary_root->Set("all_tests", all_tests);
+
+  for (std::set<std::string>::const_iterator i = all_tests_.begin();
+       i != all_tests_.end();
+       ++i) {
+    all_tests->AppendString(*i);
+  }
+
+  ListValue* disabled_tests = new ListValue;
+  summary_root->Set("disabled_tests", disabled_tests);
+
+  for (std::set<std::string>::const_iterator i = disabled_tests_.begin();
+       i != disabled_tests_.end();
+       ++i) {
+    disabled_tests->AppendString(*i);
+  }
+
+  ListValue* per_iteration_data = new ListValue;
+  summary_root->Set("per_iteration_data", per_iteration_data);
+
+  for (int i = 0; i <= iteration_; i++) {
+    DictionaryValue* current_iteration_data = new DictionaryValue;
+    per_iteration_data->Append(current_iteration_data);
+
+    for (PerIterationData::ResultsMap::const_iterator j =
+             per_iteration_data_[i].results.begin();
+         j != per_iteration_data_[i].results.end();
+         ++j) {
+      ListValue* test_results = new ListValue;
+      current_iteration_data->SetWithoutPathExpansion(j->first, test_results);
+
+      for (size_t k = 0; k < j->second.test_results.size(); k++) {
+        const TestResult& test_result = j->second.test_results[k];
+
+        DictionaryValue* test_result_value = new DictionaryValue;
+        test_results->Append(test_result_value);
+
+        test_result_value->SetString("status", test_result.StatusAsString());
+        test_result_value->SetInteger(
+            "elapsed_time_ms",
+            static_cast<int>(test_result.elapsed_time.InMilliseconds()));
+
+        // There are no guarantees about character encoding of the output
+        // snippet. Escape it and record whether it was losless.
+        // It's useful to have the output snippet as string in the summary
+        // for easy viewing.
+        std::string escaped_output_snippet;
+        bool losless_snippet = EscapeJSONString(
+            test_result.output_snippet, false, &escaped_output_snippet);
+        test_result_value->SetString("output_snippet",
+                                     escaped_output_snippet);
+        test_result_value->SetBoolean("losless_snippet", losless_snippet);
+
+        // Also include the raw version (base64-encoded so that it can be safely
+        // JSON-serialized - there are no guarantees about character encoding
+        // of the snippet). This can be very useful piece of information when
+        // debugging a test failure related to character encoding.
+        std::string base64_output_snippet;
+        Base64Encode(test_result.output_snippet, &base64_output_snippet);
+        test_result_value->SetString("output_snippet_base64",
+                                     base64_output_snippet);
+      }
+    }
+  }
+
+  JSONFileValueSerializer serializer(path);
+  return serializer.Serialize(*summary_root);
+}
+
+TestResultsTracker::TestStatusMap
+    TestResultsTracker::GetTestStatusMapForCurrentIteration() const {
+  TestStatusMap tests_by_status;
+  GetTestStatusForIteration(iteration_, &tests_by_status);
+  return tests_by_status;
+}
+
+TestResultsTracker::TestStatusMap
+    TestResultsTracker::GetTestStatusMapForAllIterations() const {
+  TestStatusMap tests_by_status;
+  for (int i = 0; i <= iteration_; i++)
+    GetTestStatusForIteration(i, &tests_by_status);
+  return tests_by_status;
+}
+
+void TestResultsTracker::GetTestStatusForIteration(
+    int iteration, TestStatusMap* map) const {
+  for (PerIterationData::ResultsMap::const_iterator j =
+           per_iteration_data_[iteration].results.begin();
+       j != per_iteration_data_[iteration].results.end();
+       ++j) {
+    // Use the last test result as the final one.
+    const TestResult& result = j->second.test_results.back();
+    (*map)[result.status].insert(result.full_name);
+  }
+}
+
+TestResultsTracker::AggregateTestResult::AggregateTestResult() {
+}
+
+TestResultsTracker::AggregateTestResult::~AggregateTestResult() {
+}
+
+TestResultsTracker::PerIterationData::PerIterationData() {
+}
+
+TestResultsTracker::PerIterationData::~PerIterationData() {
+}
+
+}  // namespace base
diff --git a/base/test/launcher/test_results_tracker.h b/base/test/launcher/test_results_tracker.h
new file mode 100644
index 0000000..2bddebc
--- /dev/null
+++ b/base/test/launcher/test_results_tracker.h
@@ -0,0 +1,123 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_LAUNCHER_TEST_RESULTS_TRACKER_H_
+#define BASE_TEST_LAUNCHER_TEST_RESULTS_TRACKER_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/test/launcher/test_result.h"
+#include "base/threading/thread_checker.h"
+
+namespace base {
+
+class CommandLine;
+class FilePath;
+
+// A helper class to output results.
+// Note: as currently XML is the only supported format by gtest, we don't
+// check output format (e.g. "xml:" prefix) here and output an XML file
+// unconditionally.
+// Note: we don't output per-test-case or total summary info like
+// total failed_test_count, disabled_test_count, elapsed_time and so on.
+// Only each test (testcase element in the XML) will have the correct
+// failed/disabled/elapsed_time information. Each test won't include
+// detailed failure messages either.
+class TestResultsTracker {
+ public:
+  TestResultsTracker();
+  ~TestResultsTracker();
+
+  // Initialize the result tracker. Must be called exactly once before
+  // calling any other methods. Returns true on success.
+  bool Init(const CommandLine& command_line) WARN_UNUSED_RESULT;
+
+  // Called when a test iteration is starting.
+  void OnTestIterationStarting();
+
+  // Adds |test_name| to the set of discovered tests (this includes all tests
+  // present in the executable, not necessarily run).
+  void AddTest(const std::string& test_name);
+
+  // Adds |test_name| to the set of disabled tests.
+  void AddDisabledTest(const std::string& test_name);
+
+  // Adds |result| to the stored test results.
+  void AddTestResult(const TestResult& result);
+
+  // Prints a summary of current test iteration to stdout.
+  void PrintSummaryOfCurrentIteration() const;
+
+  // Prints a summary of all test iterations (not just the last one) to stdout.
+  void PrintSummaryOfAllIterations() const;
+
+  // Adds a string tag to the JSON summary. This is intended to indicate
+  // conditions that affect the entire test run, as opposed to individual tests.
+  void AddGlobalTag(const std::string& tag);
+
+  // Saves a JSON summary of all test iterations results to |path|. Returns
+  // true on success.
+  bool SaveSummaryAsJSON(const FilePath& path) const WARN_UNUSED_RESULT;
+
+  // Map where keys are test result statuses, and values are sets of tests
+  // which finished with that status.
+  typedef std::map<TestResult::Status, std::set<std::string> > TestStatusMap;
+
+  // Returns a test status map (see above) for current test iteration.
+  TestStatusMap GetTestStatusMapForCurrentIteration() const;
+
+  // Returns a test status map (see above) for all test iterations.
+  TestStatusMap GetTestStatusMapForAllIterations() const;
+
+ private:
+  void GetTestStatusForIteration(int iteration, TestStatusMap* map) const;
+
+  struct AggregateTestResult {
+    AggregateTestResult();
+    ~AggregateTestResult();
+
+    std::vector<TestResult> test_results;
+  };
+
+  struct PerIterationData {
+    PerIterationData();
+    ~PerIterationData();
+
+    // Aggregate test results grouped by full test name.
+    typedef std::map<std::string, AggregateTestResult> ResultsMap;
+    ResultsMap results;
+  };
+
+  ThreadChecker thread_checker_;
+
+  // Set of global tags, i.e. strings indicating conditions that apply to
+  // the entire test run.
+  std::set<std::string> global_tags_;
+
+  // Set of all test names discovered in the current executable.
+  std::set<std::string> all_tests_;
+
+  // Set of all disabled tests in the current executable.
+  std::set<std::string> disabled_tests_;
+
+  // Store test results for each iteration.
+  std::vector<PerIterationData> per_iteration_data_;
+
+  // Index of current iteration (starting from 0). -1 before the first
+  // iteration.
+  int iteration_;
+
+  // File handle of output file (can be NULL if no file).
+  FILE* out_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestResultsTracker);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_LAUNCHER_TEST_RESULTS_TRACKER_H_
diff --git a/base/test/launcher/unit_test_launcher.cc b/base/test/launcher/unit_test_launcher.cc
new file mode 100644
index 0000000..0cbae2f
--- /dev/null
+++ b/base/test/launcher/unit_test_launcher.cc
@@ -0,0 +1,586 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/launcher/unit_test_launcher.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/debug/debugger.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/format_macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/sys_info.h"
+#include "base/test/gtest_xml_util.h"
+#include "base/test/launcher/test_launcher.h"
+#include "base/test/test_switches.h"
+#include "base/test/test_timeouts.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/threading/thread_checker.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+// This constant controls how many tests are run in a single batch by default.
+const size_t kDefaultTestBatchLimit = 10;
+
+const char kHelpFlag[] = "help";
+
+// Flag to run all tests in a single process.
+const char kSingleProcessTestsFlag[] = "single-process-tests";
+
+void PrintUsage() {
+  fprintf(stdout,
+          "Runs tests using the gtest framework, each batch of tests being\n"
+          "run in their own process. Supported command-line flags:\n"
+          "\n"
+          " Common flags:\n"
+          "  --gtest_filter=...\n"
+          "    Runs a subset of tests (see --gtest_help for more info).\n"
+          "\n"
+          "  --help\n"
+          "    Shows this message.\n"
+          "\n"
+          "  --gtest_help\n"
+          "    Shows the gtest help message.\n"
+          "\n"
+          "  --test-launcher-jobs=N\n"
+          "    Sets the number of parallel test jobs to N.\n"
+          "\n"
+          "  --single-process-tests\n"
+          "    Runs the tests and the launcher in the same process. Useful\n"
+          "    for debugging a specific test in a debugger.\n"
+          "\n"
+          " Other flags:\n"
+          "  --test-launcher-batch-limit=N\n"
+          "    Sets the limit of test batch to run in a single process to N.\n"
+          "\n"
+          "  --test-launcher-debug-launcher\n"
+          "    Disables autodetection of debuggers and similar tools,\n"
+          "    making it possible to use them to debug launcher itself.\n"
+          "\n"
+          "  --test-launcher-retry-limit=N\n"
+          "    Sets the limit of test retries on failures to N.\n"
+          "\n"
+          "  --test-launcher-summary-output=PATH\n"
+          "    Saves a JSON machine-readable summary of the run.\n"
+          "\n"
+          "  --test-launcher-print-test-stdio=auto|always|never\n"
+          "    Controls when full test output is printed.\n"
+          "    auto means to print it when the test failed.\n"
+          "\n"
+          "  --test-launcher-total-shards=N\n"
+          "    Sets the total number of shards to N.\n"
+          "\n"
+          "  --test-launcher-shard-index=N\n"
+          "    Sets the shard index to run to N (from 0 to TOTAL - 1).\n");
+  fflush(stdout);
+}
+
+// Returns command line for child GTest process based on the command line
+// of current process. |test_names| is a vector of test full names
+// (e.g. "A.B"), |output_file| is path to the GTest XML output file.
+CommandLine GetCommandLineForChildGTestProcess(
+    const std::vector<std::string>& test_names,
+    const base::FilePath& output_file) {
+  CommandLine new_cmd_line(*CommandLine::ForCurrentProcess());
+
+  new_cmd_line.AppendSwitchPath(switches::kTestLauncherOutput, output_file);
+  new_cmd_line.AppendSwitchASCII(kGTestFilterFlag, JoinString(test_names, ":"));
+  new_cmd_line.AppendSwitch(kSingleProcessTestsFlag);
+
+  return new_cmd_line;
+}
+
+class UnitTestLauncherDelegate : public TestLauncherDelegate {
+ public:
+  explicit UnitTestLauncherDelegate(size_t batch_limit, bool use_job_objects)
+      : batch_limit_(batch_limit),
+        use_job_objects_(use_job_objects) {
+  }
+
+  virtual ~UnitTestLauncherDelegate() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+  }
+
+ private:
+  struct GTestCallbackState {
+    TestLauncher* test_launcher;
+    std::vector<std::string> test_names;
+    FilePath output_file;
+  };
+
+  virtual bool ShouldRunTest(const testing::TestCase* test_case,
+                             const testing::TestInfo* test_info) OVERRIDE {
+    DCHECK(thread_checker_.CalledOnValidThread());
+
+    // There is no additional logic to disable specific tests.
+    return true;
+  }
+
+  virtual size_t RunTests(TestLauncher* test_launcher,
+                          const std::vector<std::string>& test_names) OVERRIDE {
+    DCHECK(thread_checker_.CalledOnValidThread());
+
+    std::vector<std::string> batch;
+    for (size_t i = 0; i < test_names.size(); i++) {
+      batch.push_back(test_names[i]);
+
+      if (batch.size() >= batch_limit_) {
+        RunBatch(test_launcher, batch);
+        batch.clear();
+      }
+    }
+
+    RunBatch(test_launcher, batch);
+
+    return test_names.size();
+  }
+
+  virtual size_t RetryTests(
+      TestLauncher* test_launcher,
+      const std::vector<std::string>& test_names) OVERRIDE {
+    MessageLoop::current()->PostTask(
+        FROM_HERE,
+        Bind(&UnitTestLauncherDelegate::RunSerially,
+             Unretained(this),
+             test_launcher,
+             test_names));
+    return test_names.size();
+  }
+
+  void RunSerially(TestLauncher* test_launcher,
+                   const std::vector<std::string>& test_names) {
+    if (test_names.empty())
+      return;
+
+    std::vector<std::string> new_test_names(test_names);
+    std::string test_name(new_test_names.back());
+    new_test_names.pop_back();
+
+    // Create a dedicated temporary directory to store the xml result data
+    // per run to ensure clean state and make it possible to launch multiple
+    // processes in parallel.
+    base::FilePath output_file;
+    CHECK(CreateNewTempDirectory(FilePath::StringType(), &output_file));
+    output_file = output_file.AppendASCII("test_results.xml");
+
+    std::vector<std::string> current_test_names;
+    current_test_names.push_back(test_name);
+    CommandLine cmd_line(
+        GetCommandLineForChildGTestProcess(current_test_names, output_file));
+
+    GTestCallbackState callback_state;
+    callback_state.test_launcher = test_launcher;
+    callback_state.test_names = current_test_names;
+    callback_state.output_file = output_file;
+
+    test_launcher->LaunchChildGTestProcess(
+        cmd_line,
+        std::string(),
+        TestTimeouts::test_launcher_timeout(),
+        use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0,
+        Bind(&UnitTestLauncherDelegate::SerialGTestCallback,
+             Unretained(this),
+             callback_state,
+             new_test_names));
+  }
+
+  void RunBatch(TestLauncher* test_launcher,
+                const std::vector<std::string>& test_names) {
+    DCHECK(thread_checker_.CalledOnValidThread());
+
+    if (test_names.empty())
+      return;
+
+    // Create a dedicated temporary directory to store the xml result data
+    // per run to ensure clean state and make it possible to launch multiple
+    // processes in parallel.
+    base::FilePath output_file;
+    CHECK(CreateNewTempDirectory(FilePath::StringType(), &output_file));
+    output_file = output_file.AppendASCII("test_results.xml");
+
+    CommandLine cmd_line(
+        GetCommandLineForChildGTestProcess(test_names, output_file));
+
+    // Adjust the timeout depending on how many tests we're running
+    // (note that e.g. the last batch of tests will be smaller).
+    // TODO(phajdan.jr): Consider an adaptive timeout, which can change
+    // depending on how many tests ran and how many remain.
+    // Note: do NOT parse child's stdout to do that, it's known to be
+    // unreliable (e.g. buffering issues can mix up the output).
+    base::TimeDelta timeout =
+        test_names.size() * TestTimeouts::test_launcher_timeout();
+
+    GTestCallbackState callback_state;
+    callback_state.test_launcher = test_launcher;
+    callback_state.test_names = test_names;
+    callback_state.output_file = output_file;
+
+    test_launcher->LaunchChildGTestProcess(
+        cmd_line,
+        std::string(),
+        timeout,
+        use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0,
+        Bind(&UnitTestLauncherDelegate::GTestCallback,
+             Unretained(this),
+             callback_state));
+  }
+
+  void GTestCallback(const GTestCallbackState& callback_state,
+                     int exit_code,
+                     const TimeDelta& elapsed_time,
+                     bool was_timeout,
+                     const std::string& output) {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    std::vector<std::string> tests_to_relaunch;
+    ProcessTestResults(callback_state.test_launcher,
+                       callback_state.test_names,
+                       callback_state.output_file,
+                       output,
+                       exit_code,
+                       was_timeout,
+                       &tests_to_relaunch);
+
+    // Relaunch requested tests in parallel, but only use single
+    // test per batch for more precise results (crashes, test passes
+    // but non-zero exit codes etc).
+    for (size_t i = 0; i < tests_to_relaunch.size(); i++) {
+      std::vector<std::string> batch;
+      batch.push_back(tests_to_relaunch[i]);
+      RunBatch(callback_state.test_launcher, batch);
+    }
+
+    // The temporary file's directory is also temporary.
+    DeleteFile(callback_state.output_file.DirName(), true);
+  }
+
+  void SerialGTestCallback(const GTestCallbackState& callback_state,
+                           const std::vector<std::string>& test_names,
+                           int exit_code,
+                           const TimeDelta& elapsed_time,
+                           bool was_timeout,
+                           const std::string& output) {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    std::vector<std::string> tests_to_relaunch;
+    bool called_any_callbacks =
+        ProcessTestResults(callback_state.test_launcher,
+                           callback_state.test_names,
+                           callback_state.output_file,
+                           output,
+                           exit_code,
+                           was_timeout,
+                           &tests_to_relaunch);
+
+    // There is only one test, there cannot be other tests to relaunch
+    // due to a crash.
+    DCHECK(tests_to_relaunch.empty());
+
+    // There is only one test, we should have called back with its result.
+    DCHECK(called_any_callbacks);
+
+    // The temporary file's directory is also temporary.
+    DeleteFile(callback_state.output_file.DirName(), true);
+
+    MessageLoop::current()->PostTask(
+        FROM_HERE,
+        Bind(&UnitTestLauncherDelegate::RunSerially,
+             Unretained(this),
+             callback_state.test_launcher,
+             test_names));
+  }
+
+  static bool ProcessTestResults(
+      TestLauncher* test_launcher,
+      const std::vector<std::string>& test_names,
+      const base::FilePath& output_file,
+      const std::string& output,
+      int exit_code,
+      bool was_timeout,
+      std::vector<std::string>* tests_to_relaunch) {
+    std::vector<TestResult> test_results;
+    bool crashed = false;
+    bool have_test_results =
+        ProcessGTestOutput(output_file, &test_results, &crashed);
+
+    bool called_any_callback = false;
+
+    if (have_test_results) {
+      // TODO(phajdan.jr): Check for duplicates and mismatches between
+      // the results we got from XML file and tests we intended to run.
+      std::map<std::string, TestResult> results_map;
+      for (size_t i = 0; i < test_results.size(); i++)
+        results_map[test_results[i].full_name] = test_results[i];
+
+      bool had_interrupted_test = false;
+
+      // Results to be reported back to the test launcher.
+      std::vector<TestResult> final_results;
+
+      for (size_t i = 0; i < test_names.size(); i++) {
+        if (ContainsKey(results_map, test_names[i])) {
+          TestResult test_result = results_map[test_names[i]];
+          if (test_result.status == TestResult::TEST_CRASH) {
+            had_interrupted_test = true;
+
+            if (was_timeout) {
+              // Fix up the test status: we forcibly kill the child process
+              // after the timeout, so from XML results it looks just like
+              // a crash.
+              test_result.status = TestResult::TEST_TIMEOUT;
+            }
+          } else if (test_result.status == TestResult::TEST_SUCCESS ||
+                     test_result.status == TestResult::TEST_FAILURE) {
+            // We run multiple tests in a batch with a timeout applied
+            // to the entire batch. It is possible that with other tests
+            // running quickly some tests take longer than the per-test timeout.
+            // For consistent handling of tests independent of order and other
+            // factors, mark them as timing out.
+            if (test_result.elapsed_time >
+                TestTimeouts::test_launcher_timeout()) {
+              test_result.status = TestResult::TEST_TIMEOUT;
+            }
+          }
+          test_result.output_snippet =
+              GetTestOutputSnippet(test_result, output);
+          final_results.push_back(test_result);
+        } else if (had_interrupted_test) {
+          tests_to_relaunch->push_back(test_names[i]);
+        } else {
+          // TODO(phajdan.jr): Explicitly pass the info that the test didn't
+          // run for a mysterious reason.
+          LOG(ERROR) << "no test result for " << test_names[i];
+          TestResult test_result;
+          test_result.full_name = test_names[i];
+          test_result.status = TestResult::TEST_UNKNOWN;
+          test_result.output_snippet =
+              GetTestOutputSnippet(test_result, output);
+          final_results.push_back(test_result);
+        }
+      }
+
+      // TODO(phajdan.jr): Handle the case where processing XML output
+      // indicates a crash but none of the test results is marked as crashing.
+
+      if (final_results.empty())
+        return false;
+
+      bool has_non_success_test = false;
+      for (size_t i = 0; i < final_results.size(); i++) {
+        if (final_results[i].status != TestResult::TEST_SUCCESS) {
+          has_non_success_test = true;
+          break;
+        }
+      }
+
+      if (!has_non_success_test && exit_code != 0) {
+        // This is a bit surprising case: all tests are marked as successful,
+        // but the exit code was not zero. This can happen e.g. under memory
+        // tools that report leaks this way.
+
+        if (final_results.size() == 1) {
+          // Easy case. One test only so we know the non-zero exit code
+          // was caused by that one test.
+          final_results[0].status = TestResult::TEST_FAILURE_ON_EXIT;
+        } else {
+          // Harder case. Discard the results and request relaunching all
+          // tests without batching. This will trigger above branch on
+          // relaunch leading to more precise results.
+          LOG(WARNING) << "Not sure which test caused non-zero exit code, "
+                       << "relaunching all of them without batching.";
+
+          for (size_t i = 0; i < final_results.size(); i++)
+            tests_to_relaunch->push_back(final_results[i].full_name);
+
+          return false;
+        }
+      }
+
+      for (size_t i = 0; i < final_results.size(); i++) {
+        // Fix the output snippet after possible changes to the test result.
+        final_results[i].output_snippet =
+            GetTestOutputSnippet(final_results[i], output);
+        test_launcher->OnTestFinished(final_results[i]);
+        called_any_callback = true;
+      }
+    } else {
+      fprintf(stdout,
+              "Failed to get out-of-band test success data, "
+              "dumping full stdio below:\n%s\n",
+              output.c_str());
+      fflush(stdout);
+
+      // We do not have reliable details about test results (parsing test
+      // stdout is known to be unreliable), apply the executable exit code
+      // to all tests.
+      // TODO(phajdan.jr): Be smarter about this, e.g. retry each test
+      // individually.
+      for (size_t i = 0; i < test_names.size(); i++) {
+        TestResult test_result;
+        test_result.full_name = test_names[i];
+        test_result.status = TestResult::TEST_UNKNOWN;
+        test_launcher->OnTestFinished(test_result);
+        called_any_callback = true;
+      }
+    }
+
+    return called_any_callback;
+  }
+
+  ThreadChecker thread_checker_;
+
+  // Maximum number of tests to run in a single batch.
+  size_t batch_limit_;
+
+  // Determines whether we use job objects on Windows.
+  bool use_job_objects_;
+};
+
+bool GetSwitchValueAsInt(const std::string& switch_name, int* result) {
+  if (!CommandLine::ForCurrentProcess()->HasSwitch(switch_name))
+    return true;
+
+  std::string switch_value =
+      CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switch_name);
+  if (!StringToInt(switch_value, result) || *result < 1) {
+    LOG(ERROR) << "Invalid value for " << switch_name << ": " << switch_value;
+    return false;
+  }
+
+  return true;
+}
+
+int LaunchUnitTestsInternal(const RunTestSuiteCallback& run_test_suite,
+                            int default_jobs,
+                            bool use_job_objects,
+                            const Closure& gtest_init) {
+#if defined(OS_ANDROID)
+  // We can't easily fork on Android, just run the test suite directly.
+  return run_test_suite.Run();
+#else
+  bool force_single_process = false;
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kTestLauncherDebugLauncher)) {
+    fprintf(stdout, "Forcing test launcher debugging mode.\n");
+    fflush(stdout);
+  } else {
+    if (base::debug::BeingDebugged()) {
+      fprintf(stdout,
+              "Debugger detected, switching to single process mode.\n"
+              "Pass --test-launcher-debug-launcher to debug the launcher "
+              "itself.\n");
+      fflush(stdout);
+      force_single_process = true;
+    }
+
+    if (RunningOnValgrind()) {
+      fprintf(stdout,
+              "Valgrind detected, switching to single process mode.\n"
+              "Pass --test-launcher-debug-launcher to valgrind the launcher "
+              "itself.\n");
+      fflush(stdout);
+      force_single_process = true;
+    }
+  }
+
+  if (CommandLine::ForCurrentProcess()->HasSwitch(kGTestHelpFlag) ||
+      CommandLine::ForCurrentProcess()->HasSwitch(kGTestListTestsFlag) ||
+      CommandLine::ForCurrentProcess()->HasSwitch(kSingleProcessTestsFlag) ||
+      force_single_process) {
+    return run_test_suite.Run();
+  }
+#endif
+
+  if (CommandLine::ForCurrentProcess()->HasSwitch(kHelpFlag)) {
+    PrintUsage();
+    return 0;
+  }
+
+  base::TimeTicks start_time(base::TimeTicks::Now());
+
+  gtest_init.Run();
+  TestTimeouts::Initialize();
+
+  int batch_limit = kDefaultTestBatchLimit;
+  if (!GetSwitchValueAsInt(switches::kTestLauncherBatchLimit, &batch_limit))
+    return 1;
+
+  fprintf(stdout,
+          "IMPORTANT DEBUGGING NOTE: batches of tests are run inside their\n"
+          "own process. For debugging a test inside a debugger, use the\n"
+          "--gtest_filter=<your_test_name> flag along with\n"
+          "--single-process-tests.\n");
+  fflush(stdout);
+
+  MessageLoopForIO message_loop;
+
+  UnitTestLauncherDelegate delegate(batch_limit, use_job_objects);
+  base::TestLauncher launcher(&delegate, default_jobs);
+  bool success = launcher.Run();
+
+  fprintf(stdout,
+          "Tests took %" PRId64 " seconds.\n",
+          (base::TimeTicks::Now() - start_time).InSeconds());
+  fflush(stdout);
+
+  return (success ? 0 : 1);
+}
+
+void InitGoogleTestChar(int* argc, char** argv) {
+  testing::InitGoogleTest(argc, argv);
+}
+
+#if defined(OS_WIN)
+void InitGoogleTestWChar(int* argc, wchar_t** argv) {
+  testing::InitGoogleTest(argc, argv);
+}
+#endif  // defined(OS_WIN)
+
+}  // namespace
+
+int LaunchUnitTests(int argc,
+                    char** argv,
+                    const RunTestSuiteCallback& run_test_suite) {
+  CommandLine::Init(argc, argv);
+  return LaunchUnitTestsInternal(
+      run_test_suite,
+      SysInfo::NumberOfProcessors(),
+      true,
+      Bind(&InitGoogleTestChar, &argc, argv));
+}
+
+int LaunchUnitTestsSerially(int argc,
+                            char** argv,
+                            const RunTestSuiteCallback& run_test_suite) {
+  CommandLine::Init(argc, argv);
+  return LaunchUnitTestsInternal(
+      run_test_suite,
+      1,
+      true,
+      Bind(&InitGoogleTestChar, &argc, argv));
+}
+
+#if defined(OS_WIN)
+int LaunchUnitTests(int argc,
+                    wchar_t** argv,
+                    bool use_job_objects,
+                    const RunTestSuiteCallback& run_test_suite) {
+  // Windows CommandLine::Init ignores argv anyway.
+  CommandLine::Init(argc, NULL);
+  return LaunchUnitTestsInternal(
+      run_test_suite,
+      SysInfo::NumberOfProcessors(),
+      use_job_objects,
+      Bind(&InitGoogleTestWChar, &argc, argv));
+}
+#endif  // defined(OS_WIN)
+
+}  // namespace base
diff --git a/base/test/launcher/unit_test_launcher.h b/base/test/launcher/unit_test_launcher.h
new file mode 100644
index 0000000..5682ed9
--- /dev/null
+++ b/base/test/launcher/unit_test_launcher.h
@@ -0,0 +1,36 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_LAUNCHER_UNIT_TEST_LAUNCHER_H_
+#define BASE_TEST_LAUNCHER_UNIT_TEST_LAUNCHER_H_
+
+#include "base/callback.h"
+
+namespace base {
+
+// Callback that runs a test suite and returns exit code.
+typedef base::Callback<int(void)> RunTestSuiteCallback;
+
+// Launches unit tests in given test suite. Returns exit code.
+int LaunchUnitTests(int argc,
+                    char** argv,
+                    const RunTestSuiteCallback& run_test_suite);
+
+// Same as above, but always runs tests serially.
+int LaunchUnitTestsSerially(int argc,
+                            char** argv,
+                            const RunTestSuiteCallback& run_test_suite);
+
+#if defined(OS_WIN)
+// Launches unit tests in given test suite. Returns exit code.
+// |use_job_objects| determines whether to use job objects.
+int LaunchUnitTests(int argc,
+                    wchar_t** argv,
+                    bool use_job_objects,
+                    const RunTestSuiteCallback& run_test_suite);
+#endif  // defined(OS_WIN)
+
+}   // namespace base
+
+#endif  // BASE_TEST_LAUNCHER_UNIT_TEST_LAUNCHER_H_
diff --git a/base/test/launcher/unit_test_launcher_ios.cc b/base/test/launcher/unit_test_launcher_ios.cc
new file mode 100644
index 0000000..ae08acd
--- /dev/null
+++ b/base/test/launcher/unit_test_launcher_ios.cc
@@ -0,0 +1,17 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/launcher/unit_test_launcher.h"
+
+namespace base {
+
+int LaunchUnitTests(int argc,
+                    char** argv,
+                    const RunTestSuiteCallback& run_test_suite) {
+  // Stub implementation - iOS doesn't support features we need for
+  // the full test launcher (e.g. process_util).
+  return run_test_suite.Run();
+}
+
+}  // namespace base
diff --git a/base/test/mock_chrome_application_mac.h b/base/test/mock_chrome_application_mac.h
new file mode 100644
index 0000000..ffa3080
--- /dev/null
+++ b/base/test/mock_chrome_application_mac.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_MOCK_CHROME_APPLICATION_MAC_H_
+#define BASE_TEST_MOCK_CHROME_APPLICATION_MAC_H_
+
+#if defined(__OBJC__)
+
+#import <AppKit/AppKit.h>
+
+#include "base/mac/scoped_sending_event.h"
+#include "base/message_loop/message_pump_mac.h"
+
+// A basic implementation of CrAppProtocol and
+// CrAppControlProtocol. This can be used in tests that need an
+// NSApplication and use a runloop, or which need a ScopedSendingEvent
+// when handling a nested event loop.
+@interface MockCrApp : NSApplication<CrAppProtocol,
+                                     CrAppControlProtocol> {
+ @private
+  BOOL handlingSendEvent_;
+}
+@end
+
+#endif
+
+// To be used to instantiate MockCrApp from C++ code.
+namespace mock_cr_app {
+void RegisterMockCrApp();
+}  // namespace mock_cr_app
+
+#endif  // BASE_TEST_MOCK_CHROME_APPLICATION_MAC_H_
diff --git a/base/test/mock_chrome_application_mac.mm b/base/test/mock_chrome_application_mac.mm
new file mode 100644
index 0000000..0890553
--- /dev/null
+++ b/base/test/mock_chrome_application_mac.mm
@@ -0,0 +1,44 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/mock_chrome_application_mac.h"
+
+#include "base/auto_reset.h"
+#include "base/logging.h"
+
+@implementation MockCrApp
+
++ (NSApplication*)sharedApplication {
+  NSApplication* app = [super sharedApplication];
+  DCHECK([app conformsToProtocol:@protocol(CrAppControlProtocol)])
+      << "Existing NSApp (class " << [[app className] UTF8String]
+      << ") does not conform to required protocol.";
+  DCHECK(base::MessagePumpMac::UsingCrApp())
+      << "MessagePumpMac::Create() was called before "
+      << "+[MockCrApp sharedApplication]";
+  return app;
+}
+
+- (void)sendEvent:(NSEvent*)event {
+  base::AutoReset<BOOL> scoper(&handlingSendEvent_, YES);
+  [super sendEvent:event];
+}
+
+- (void)setHandlingSendEvent:(BOOL)handlingSendEvent {
+  handlingSendEvent_ = handlingSendEvent;
+}
+
+- (BOOL)isHandlingSendEvent {
+  return handlingSendEvent_;
+}
+
+@end
+
+namespace mock_cr_app {
+
+void RegisterMockCrApp() {
+  [MockCrApp sharedApplication];
+}
+
+}  // namespace mock_cr_app
diff --git a/base/test/mock_devices_changed_observer.cc b/base/test/mock_devices_changed_observer.cc
new file mode 100644
index 0000000..c05f26a
--- /dev/null
+++ b/base/test/mock_devices_changed_observer.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/mock_devices_changed_observer.h"
+
+namespace base {
+
+MockDevicesChangedObserver::MockDevicesChangedObserver() {
+}
+
+MockDevicesChangedObserver::~MockDevicesChangedObserver() {
+}
+
+}  // namespace base
diff --git a/base/test/mock_devices_changed_observer.h b/base/test/mock_devices_changed_observer.h
new file mode 100644
index 0000000..3ada16b
--- /dev/null
+++ b/base/test/mock_devices_changed_observer.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_MOCK_DEVICES_CHANGED_OBSERVER_H_
+#define BASE_TEST_MOCK_DEVICES_CHANGED_OBSERVER_H_
+
+#include <string>
+
+#include "base/system_monitor/system_monitor.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace base {
+
+class MockDevicesChangedObserver
+    : public base::SystemMonitor::DevicesChangedObserver {
+ public:
+  MockDevicesChangedObserver();
+  ~MockDevicesChangedObserver();
+
+  MOCK_METHOD1(OnDevicesChanged,
+               void(base::SystemMonitor::DeviceType device_type));
+
+  DISALLOW_COPY_AND_ASSIGN(MockDevicesChangedObserver);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_MOCK_DEVICES_CHANGED_OBSERVER_H_
diff --git a/base/test/mock_time_provider.cc b/base/test/mock_time_provider.cc
new file mode 100644
index 0000000..9e5547f
--- /dev/null
+++ b/base/test/mock_time_provider.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "base/test/mock_time_provider.h"
+
+using ::testing::DefaultValue;
+
+namespace base {
+
+MockTimeProvider* MockTimeProvider::instance_ = NULL;
+
+MockTimeProvider::MockTimeProvider() {
+  DCHECK(!instance_) << "Only one instance of MockTimeProvider can exist";
+  DCHECK(!DefaultValue<Time>::IsSet());
+  instance_ = this;
+  DefaultValue<Time>::Set(Time::FromInternalValue(0));
+}
+
+MockTimeProvider::~MockTimeProvider() {
+  instance_ = NULL;
+  DefaultValue<Time>::Clear();
+}
+
+// static
+Time MockTimeProvider::StaticNow() {
+  return instance_->Now();
+}
+
+}  // namespace base
diff --git a/base/test/mock_time_provider.h b/base/test/mock_time_provider.h
new file mode 100644
index 0000000..7c58648
--- /dev/null
+++ b/base/test/mock_time_provider.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(akalin): Change all users of this class to use SimpleTestClock
+// or SimpleTestTickClock and remove this class.
+
+// A helper class used to mock out calls to the static method base::Time::Now.
+//
+// Example usage:
+//
+// typedef base::Time(TimeProvider)();
+// class StopWatch {
+//  public:
+//   StopWatch(TimeProvider* time_provider);
+//   void Start();
+//   base::TimeDelta Stop();
+//  private:
+//   TimeProvider* time_provider_;
+//   ...
+// }
+//
+// Normally, you would instantiate a StopWatch with the real Now function:
+//
+// StopWatch watch(&base::Time::Now);
+//
+// But when testing, you want to instantiate it with
+// MockTimeProvider::StaticNow, which calls an internally mocked out member.
+// This allows you to set expectations on the Now method. For example:
+//
+// TEST_F(StopWatchTest, BasicTest) {
+//   InSequence s;
+//   StrictMock<MockTimeProvider> mock_time;
+//   EXPECT_CALL(mock_time, Now())
+//       .WillOnce(Return(Time::FromDoubleT(4)));
+//   EXPECT_CALL(mock_time, Now())
+//       .WillOnce(Return(Time::FromDoubleT(10)));
+//
+//   StopWatch sw(&MockTimeProvider::StaticNow);
+//   sw.Start();  // First call to Now.
+//   TimeDelta elapsed = sw.stop();  // Second call to Now.
+//   ASSERT_EQ(elapsed, TimeDelta::FromSeconds(6));
+// }
+
+#ifndef BASE_TEST_MOCK_TIME_PROVIDER_H_
+#define BASE_TEST_MOCK_TIME_PROVIDER_H_
+
+#include "base/time/time.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace base {
+
+class MockTimeProvider {
+ public:
+  MockTimeProvider();
+  ~MockTimeProvider();
+
+  MOCK_METHOD0(Now, Time());
+
+  static Time StaticNow();
+
+ private:
+  static MockTimeProvider* instance_;
+  DISALLOW_COPY_AND_ASSIGN(MockTimeProvider);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_MOCK_TIME_PROVIDER_H_
diff --git a/base/test/multiprocess_test.cc b/base/test/multiprocess_test.cc
new file mode 100644
index 0000000..306c109
--- /dev/null
+++ b/base/test/multiprocess_test.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/multiprocess_test.h"
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+
+namespace base {
+
+#if !defined(OS_ANDROID)
+ProcessHandle SpawnMultiProcessTestChild(
+    const std::string& procname,
+    const CommandLine& base_command_line,
+    const LaunchOptions& options) {
+  CommandLine command_line(base_command_line);
+  // TODO(viettrungluu): See comment above |MakeCmdLine()| in the header file.
+  // This is a temporary hack, since |MakeCmdLine()| has to provide a full
+  // command line.
+  if (!command_line.HasSwitch(switches::kTestChildProcess))
+    command_line.AppendSwitchASCII(switches::kTestChildProcess, procname);
+
+  ProcessHandle handle = kNullProcessHandle;
+  LaunchProcess(command_line, options, &handle);
+  return handle;
+}
+#endif  // !defined(OS_ANDROID)
+
+CommandLine GetMultiProcessTestChildBaseCommandLine() {
+  return *CommandLine::ForCurrentProcess();
+}
+
+// MultiProcessTest ------------------------------------------------------------
+
+MultiProcessTest::MultiProcessTest() {
+}
+
+ProcessHandle MultiProcessTest::SpawnChild(const std::string& procname) {
+  LaunchOptions options;
+#if defined(OS_WIN)
+  options.start_hidden = true;
+#endif
+  return SpawnChildWithOptions(procname, options);
+}
+
+ProcessHandle MultiProcessTest::SpawnChildWithOptions(
+    const std::string& procname,
+    const LaunchOptions& options) {
+  return SpawnMultiProcessTestChild(procname, MakeCmdLine(procname), options);
+}
+
+CommandLine MultiProcessTest::MakeCmdLine(const std::string& procname) {
+  CommandLine command_line = GetMultiProcessTestChildBaseCommandLine();
+  command_line.AppendSwitchASCII(switches::kTestChildProcess, procname);
+  return command_line;
+}
+
+}  // namespace base
diff --git a/base/test/multiprocess_test.h b/base/test/multiprocess_test.h
new file mode 100644
index 0000000..b830f73
--- /dev/null
+++ b/base/test/multiprocess_test.h
@@ -0,0 +1,132 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_MULTIPROCESS_TEST_H_
+#define BASE_TEST_MULTIPROCESS_TEST_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/process/launch.h"
+#include "base/process/process_handle.h"
+#include "build/build_config.h"
+#include "testing/platform_test.h"
+
+namespace base {
+
+class CommandLine;
+
+// Helpers to spawn a child for a multiprocess test and execute a designated
+// function. Use these when you already have another base class for your test
+// fixture, but you want (some) of your tests to be multiprocess (otherwise you
+// may just want to derive your fixture from |MultiProcessTest|, below).
+//
+// Use these helpers as follows:
+//
+//   TEST_F(MyTest, ATest) {
+//     CommandLine command_line(
+//         base::GetMultiProcessTestChildBaseCommandLine());
+//     // Maybe add our own switches to |command_line|....
+//
+//     LaunchOptions options;
+//     // Maybe set some options (e.g., |start_hidden| on Windows)....
+//
+//     // Start a child process and run |a_test_func|.
+//     base::ProcessHandle test_child_handle =
+//         base::SpawnMultiProcessTestChild("a_test_func", command_line,
+//                                          options);
+//
+//     // Do stuff involving |test_child_handle| and the child process....
+//
+//     int rv = -1;
+//     ASSERT_TRUE(base::WaitForExitCodeWithTimeout(
+//         test_child_handle, &rv, TestTimeouts::action_timeout()));
+//     base::CloseProcessHandle(test_child_handle);
+//     EXPECT_EQ(0, rv);
+//   }
+//
+//   // Note: |MULTIPROCESS_TEST_MAIN()| is defined in
+//   // testing/multi_process_function_list.h.
+//   MULTIPROCESS_TEST_MAIN(a_test_func) {
+//     // Code here runs in a child process....
+//     return 0;
+//   }
+
+// Spawns a child process and executes the function |procname| declared using
+// |MULTIPROCESS_TEST_MAIN()| or |MULTIPROCESS_TEST_MAIN_WITH_SETUP()|.
+// |command_line| should be as provided by
+// |GetMultiProcessTestChildBaseCommandLine()| (below), possibly with arguments
+// added. Note: On Windows, you probably want to set |options.start_hidden|.
+ProcessHandle SpawnMultiProcessTestChild(
+    const std::string& procname,
+    const CommandLine& command_line,
+    const LaunchOptions& options);
+
+// Gets the base command line for |SpawnMultiProcessTestChild()|. To this, you
+// may add any flags needed for your child process.
+CommandLine GetMultiProcessTestChildBaseCommandLine();
+
+// MultiProcessTest ------------------------------------------------------------
+
+// A MultiProcessTest is a test class which makes it easier to
+// write a test which requires code running out of process.
+//
+// To create a multiprocess test simply follow these steps:
+//
+// 1) Derive your test from MultiProcessTest. Example:
+//
+//    class MyTest : public MultiProcessTest {
+//    };
+//
+//    TEST_F(MyTest, TestCaseName) {
+//      ...
+//    }
+//
+// 2) Create a mainline function for the child processes and include
+//    testing/multiprocess_func_list.h.
+//    See the declaration of the MULTIPROCESS_TEST_MAIN macro
+//    in that file for an example.
+// 3) Call SpawnChild("foo"), where "foo" is the name of
+//    the function you wish to run in the child processes.
+// That's it!
+class MultiProcessTest : public PlatformTest {
+ public:
+  MultiProcessTest();
+
+ protected:
+  // Run a child process.
+  // 'procname' is the name of a function which the child will
+  // execute.  It must be exported from this library in order to
+  // run.
+  //
+  // Example signature:
+  //    extern "C" int __declspec(dllexport) FooBar() {
+  //         // do client work here
+  //    }
+  //
+  // Returns the handle to the child, or NULL on failure
+  ProcessHandle SpawnChild(const std::string& procname);
+
+  // Run a child process using the given launch options.
+  //
+  // Note: On Windows, you probably want to set |options.start_hidden|.
+  ProcessHandle SpawnChildWithOptions(const std::string& procname,
+                                      const LaunchOptions& options);
+
+  // Set up the command line used to spawn the child process.
+  // Override this to add things to the command line (calling this first in the
+  // override).
+  // Note that currently some tests rely on this providing a full command line,
+  // which they then use directly with |LaunchProcess()|.
+  // TODO(viettrungluu): Remove this and add a virtual
+  // |ModifyChildCommandLine()|; make the two divergent uses more sane.
+  virtual CommandLine MakeCmdLine(const std::string& procname);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MultiProcessTest);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_MULTIPROCESS_TEST_H_
diff --git a/base/test/multiprocess_test_android.cc b/base/test/multiprocess_test_android.cc
new file mode 100644
index 0000000..6eb30b8
--- /dev/null
+++ b/base/test/multiprocess_test_android.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/posix/global_descriptors.h"
+#include "base/test/multiprocess_test.h"
+
+#include <unistd.h>
+
+#include "base/containers/hash_tables.h"
+#include "base/logging.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace base {
+
+// A very basic implementation for Android. On Android tests can run in an APK
+// and we don't have an executable to exec*. This implementation does the bare
+// minimum to execute the method specified by procname (in the child process).
+//  - |base_command_line| is ignored.
+//  - All options except |fds_to_remap| are ignored.
+ProcessHandle SpawnMultiProcessTestChild(const std::string& procname,
+                                         const CommandLine& base_command_line,
+                                         const LaunchOptions& options) {
+  // TODO(viettrungluu): The FD-remapping done below is wrong in the presence of
+  // cycles (e.g., fd1 -> fd2, fd2 -> fd1). crbug.com/326576
+  FileHandleMappingVector empty;
+  const FileHandleMappingVector* fds_to_remap =
+      options.fds_to_remap ? options.fds_to_remap : &empty;
+
+  pid_t pid = fork();
+
+  if (pid < 0) {
+    PLOG(ERROR) << "fork";
+    return kNullProcessHandle;
+  }
+  if (pid > 0) {
+    // Parent process.
+    return pid;
+  }
+  // Child process.
+  std::hash_set<int> fds_to_keep_open;
+  for (FileHandleMappingVector::const_iterator it = fds_to_remap->begin();
+       it != fds_to_remap->end(); ++it) {
+    fds_to_keep_open.insert(it->first);
+  }
+  // Keep standard FDs (stdin, stdout, stderr, etc.) open since this
+  // is not meant to spawn a daemon.
+  int base = GlobalDescriptors::kBaseDescriptor;
+  for (int fd = base; fd < sysconf(_SC_OPEN_MAX); ++fd) {
+    if (fds_to_keep_open.find(fd) == fds_to_keep_open.end()) {
+      close(fd);
+    }
+  }
+  for (FileHandleMappingVector::const_iterator it = fds_to_remap->begin();
+       it != fds_to_remap->end(); ++it) {
+    int old_fd = it->first;
+    int new_fd = it->second;
+    if (dup2(old_fd, new_fd) < 0) {
+      PLOG(FATAL) << "dup2";
+    }
+    close(old_fd);
+  }
+  _exit(multi_process_function_list::InvokeChildProcessTest(procname));
+  return 0;
+}
+
+}  // namespace base
diff --git a/base/test/null_task_runner.cc b/base/test/null_task_runner.cc
new file mode 100644
index 0000000..bf43e6d
--- /dev/null
+++ b/base/test/null_task_runner.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/null_task_runner.h"
+
+namespace base {
+
+NullTaskRunner::NullTaskRunner() {}
+
+NullTaskRunner::~NullTaskRunner() {}
+
+bool NullTaskRunner::PostDelayedTask(
+    const tracked_objects::Location& from_here,
+    const base::Closure& task,
+    base::TimeDelta delay) {
+  return false;
+}
+
+bool NullTaskRunner::PostNonNestableDelayedTask(
+    const tracked_objects::Location& from_here,
+    const base::Closure& task,
+    base::TimeDelta delay) {
+  return false;
+}
+
+bool NullTaskRunner::RunsTasksOnCurrentThread() const {
+  return true;
+}
+
+}  // namespace
diff --git a/base/test/null_task_runner.h b/base/test/null_task_runner.h
new file mode 100644
index 0000000..d6390e5
--- /dev/null
+++ b/base/test/null_task_runner.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/single_thread_task_runner.h"
+
+namespace base {
+
+// Helper class for tests that need to provide an implementation of a
+// *TaskRunner class but don't actually care about tasks being run.
+
+class NullTaskRunner : public base::SingleThreadTaskRunner {
+ public:
+  NullTaskRunner();
+
+  virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
+                               const base::Closure& task,
+                               base::TimeDelta delay) OVERRIDE;
+  virtual bool PostNonNestableDelayedTask(
+      const tracked_objects::Location& from_here,
+      const base::Closure& task,
+      base::TimeDelta delay) OVERRIDE;
+  // Always returns true to avoid triggering DCHECKs.
+  virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
+
+ protected:
+  virtual ~NullTaskRunner();
+
+  DISALLOW_COPY_AND_ASSIGN(NullTaskRunner);
+};
+
+}  // namespace
diff --git a/base/test/perf_log.cc b/base/test/perf_log.cc
new file mode 100644
index 0000000..22884b8
--- /dev/null
+++ b/base/test/perf_log.cc
@@ -0,0 +1,45 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/perf_log.h"
+
+#include "base/files/file_util.h"
+#include "base/logging.h"
+
+namespace base {
+
+static FILE* perf_log_file = NULL;
+
+bool InitPerfLog(const FilePath& log_file) {
+  if (perf_log_file) {
+    // trying to initialize twice
+    NOTREACHED();
+    return false;
+  }
+
+  perf_log_file = OpenFile(log_file, "w");
+  return perf_log_file != NULL;
+}
+
+void FinalizePerfLog() {
+  if (!perf_log_file) {
+    // trying to cleanup without initializing
+    NOTREACHED();
+    return;
+  }
+  base::CloseFile(perf_log_file);
+}
+
+void LogPerfResult(const char* test_name, double value, const char* units) {
+  if (!perf_log_file) {
+    NOTREACHED();
+    return;
+  }
+
+  fprintf(perf_log_file, "%s\t%g\t%s\n", test_name, value, units);
+  printf("%s\t%g\t%s\n", test_name, value, units);
+  fflush(stdout);
+}
+
+}  // namespace base
diff --git a/base/test/perf_log.h b/base/test/perf_log.h
new file mode 100644
index 0000000..5d6ed9f
--- /dev/null
+++ b/base/test/perf_log.h
@@ -0,0 +1,24 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_PERF_LOG_H_
+#define BASE_TEST_PERF_LOG_H_
+
+namespace base {
+
+class FilePath;
+
+// Initializes and finalizes the perf log. These functions should be
+// called at the beginning and end (respectively) of running all the
+// performance tests. The init function returns true on success.
+bool InitPerfLog(const FilePath& log_path);
+void FinalizePerfLog();
+
+// Writes to the perf result log the given 'value' resulting from the
+// named 'test'. The units are to aid in reading the log by people.
+void LogPerfResult(const char* test_name, double value, const char* units);
+
+}  // namespace base
+
+#endif  // BASE_TEST_PERF_LOG_H_
diff --git a/base/test/perf_test_suite.cc b/base/test/perf_test_suite.cc
new file mode 100644
index 0000000..415aaef
--- /dev/null
+++ b/base/test/perf_test_suite.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/perf_test_suite.h"
+
+#include "base/command_line.h"
+#include "base/debug/debugger.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/process/launch.h"
+#include "base/strings/string_util.h"
+#include "base/test/perf_log.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+PerfTestSuite::PerfTestSuite(int argc, char** argv) : TestSuite(argc, argv) {}
+
+void PerfTestSuite::Initialize() {
+  TestSuite::Initialize();
+
+  // Initialize the perf timer log
+  FilePath log_path =
+      CommandLine::ForCurrentProcess()->GetSwitchValuePath("log-file");
+  if (log_path.empty()) {
+    PathService::Get(FILE_EXE, &log_path);
+#if defined(OS_ANDROID)
+    base::FilePath tmp_dir;
+    PathService::Get(base::DIR_CACHE, &tmp_dir);
+    log_path = tmp_dir.Append(log_path.BaseName());
+#endif
+    log_path = log_path.ReplaceExtension(FILE_PATH_LITERAL("log"));
+    log_path = log_path.InsertBeforeExtension(FILE_PATH_LITERAL("_perf"));
+  }
+  ASSERT_TRUE(InitPerfLog(log_path));
+
+  // Raise to high priority to have more precise measurements. Since we don't
+  // aim at 1% precision, it is not necessary to run at realtime level.
+  if (!debug::BeingDebugged())
+    RaiseProcessToHighPriority();
+}
+
+void PerfTestSuite::Shutdown() {
+  TestSuite::Shutdown();
+  FinalizePerfLog();
+}
+
+}  // namespace base
diff --git a/base/test/perf_test_suite.h b/base/test/perf_test_suite.h
new file mode 100644
index 0000000..85bfc41
--- /dev/null
+++ b/base/test/perf_test_suite.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_PERF_TEST_SUITE_H_
+#define BASE_TEST_PERF_TEST_SUITE_H_
+
+#include "base/test/test_suite.h"
+
+namespace base {
+
+class PerfTestSuite : public TestSuite {
+ public:
+  PerfTestSuite(int argc, char** argv);
+
+  virtual void Initialize() OVERRIDE;
+  virtual void Shutdown() OVERRIDE;
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_PERF_TEST_SUITE_H_
diff --git a/base/test/perf_time_logger.cc b/base/test/perf_time_logger.cc
new file mode 100644
index 0000000..c05ba51
--- /dev/null
+++ b/base/test/perf_time_logger.cc
@@ -0,0 +1,27 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/perf_time_logger.h"
+
+#include "base/test/perf_log.h"
+
+namespace base {
+
+PerfTimeLogger::PerfTimeLogger(const char* test_name)
+    : logged_(false), test_name_(test_name) {}
+
+PerfTimeLogger::~PerfTimeLogger() {
+  if (!logged_)
+    Done();
+}
+
+void PerfTimeLogger::Done() {
+  // we use a floating-point millisecond value because it is more
+  // intuitive than microseconds and we want more precision than
+  // integer milliseconds
+  LogPerfResult(test_name_.c_str(), timer_.Elapsed().InMillisecondsF(), "ms");
+  logged_ = true;
+}
+
+}  // namespace base
diff --git a/base/test/perf_time_logger.h b/base/test/perf_time_logger.h
new file mode 100644
index 0000000..403b272
--- /dev/null
+++ b/base/test/perf_time_logger.h
@@ -0,0 +1,37 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_PERF_TIME_LOGGER_H_
+#define BASE_TEST_PERF_TIME_LOGGER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/timer/elapsed_timer.h"
+
+namespace base {
+
+// Automates calling LogPerfResult for the common case where you want
+// to measure the time that something took. Call Done() when the test
+// is complete if you do extra work after the test or there are stack
+// objects with potentially expensive constructors. Otherwise, this
+// class with automatically log on destruction.
+class PerfTimeLogger {
+ public:
+  explicit PerfTimeLogger(const char* test_name);
+  ~PerfTimeLogger();
+
+  void Done();
+
+ private:
+  bool logged_;
+  std::string test_name_;
+  ElapsedTimer timer_;
+
+  DISALLOW_COPY_AND_ASSIGN(PerfTimeLogger);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_PERF_TIME_LOGGER_H_
diff --git a/base/test/power_monitor_test_base.cc b/base/test/power_monitor_test_base.cc
new file mode 100644
index 0000000..73438ea
--- /dev/null
+++ b/base/test/power_monitor_test_base.cc
@@ -0,0 +1,64 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/power_monitor_test_base.h"
+
+#include "base/message_loop/message_loop.h"
+#include "base/power_monitor/power_monitor.h"
+#include "base/power_monitor/power_monitor_source.h"
+
+namespace base {
+
+PowerMonitorTestSource::PowerMonitorTestSource()
+    : test_on_battery_power_(false) {
+}
+
+PowerMonitorTestSource::~PowerMonitorTestSource() {
+}
+
+void PowerMonitorTestSource::GeneratePowerStateEvent(bool on_battery_power) {
+  test_on_battery_power_ = on_battery_power;
+  ProcessPowerEvent(POWER_STATE_EVENT);
+  message_loop_.RunUntilIdle();
+}
+
+void PowerMonitorTestSource::GenerateSuspendEvent() {
+  ProcessPowerEvent(SUSPEND_EVENT);
+  message_loop_.RunUntilIdle();
+}
+
+void PowerMonitorTestSource::GenerateResumeEvent() {
+  ProcessPowerEvent(RESUME_EVENT);
+  message_loop_.RunUntilIdle();
+}
+
+bool PowerMonitorTestSource::IsOnBatteryPowerImpl() {
+  return test_on_battery_power_;
+};
+
+PowerMonitorTestObserver::PowerMonitorTestObserver()
+    : last_power_state_(false),
+      power_state_changes_(0),
+      suspends_(0),
+      resumes_(0) {
+}
+
+PowerMonitorTestObserver::~PowerMonitorTestObserver() {
+}
+
+// PowerObserver callbacks.
+void PowerMonitorTestObserver::OnPowerStateChange(bool on_battery_power) {
+  last_power_state_ = on_battery_power;
+  power_state_changes_++;
+}
+
+void PowerMonitorTestObserver::OnSuspend() {
+  suspends_++;
+}
+
+void PowerMonitorTestObserver::OnResume() {
+  resumes_++;
+}
+
+}  // namespace base
diff --git a/base/test/power_monitor_test_base.h b/base/test/power_monitor_test_base.h
new file mode 100644
index 0000000..6e37f3d
--- /dev/null
+++ b/base/test/power_monitor_test_base.h
@@ -0,0 +1,55 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_POWER_MONITOR_TEST_BASE_H_
+#define BASE_TEST_POWER_MONITOR_TEST_BASE_H_
+
+#include "base/message_loop/message_loop.h"
+#include "base/power_monitor/power_monitor.h"
+#include "base/power_monitor/power_monitor_source.h"
+
+namespace base {
+
+class PowerMonitorTestSource : public PowerMonitorSource {
+ public:
+  PowerMonitorTestSource();
+  virtual ~PowerMonitorTestSource();
+
+  void GeneratePowerStateEvent(bool on_battery_power);
+  void GenerateSuspendEvent();
+  void GenerateResumeEvent();
+
+ protected:
+  virtual bool IsOnBatteryPowerImpl() OVERRIDE;
+
+  bool test_on_battery_power_;
+  MessageLoop message_loop_;
+};
+
+class PowerMonitorTestObserver : public PowerObserver {
+ public:
+  PowerMonitorTestObserver();
+  virtual ~PowerMonitorTestObserver();
+
+  // PowerObserver callbacks.
+  virtual void OnPowerStateChange(bool on_battery_power) OVERRIDE;
+  virtual void OnSuspend() OVERRIDE;
+  virtual void OnResume() OVERRIDE;
+
+  // Test status counts.
+  bool last_power_state() { return last_power_state_; }
+  int power_state_changes() { return power_state_changes_; }
+  int suspends() { return suspends_; }
+  int resumes() { return resumes_; }
+
+ private:
+  bool last_power_state_; // Last power state we were notified of.
+  int power_state_changes_;  // Count of OnPowerStateChange notifications.
+  int suspends_;  // Count of OnSuspend notifications.
+  int resumes_;  // Count of OnResume notifications.
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_POWER_MONITOR_TEST_BASE_H_
diff --git a/base/test/run_all_perftests.cc b/base/test/run_all_perftests.cc
new file mode 100644
index 0000000..6e38109
--- /dev/null
+++ b/base/test/run_all_perftests.cc
@@ -0,0 +1,9 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/perf_test_suite.h"
+
+int main(int argc, char** argv) {
+  return base::PerfTestSuite(argc, argv).Run();
+}
diff --git a/base/test/run_all_unittests.cc b/base/test/run_all_unittests.cc
new file mode 100644
index 0000000..93cb8cb
--- /dev/null
+++ b/base/test/run_all_unittests.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/jni_android.h"
+#include "base/test/test_file_util.h"
+#endif
+
+namespace {
+
+class NoAtExitBaseTestSuite : public base::TestSuite {
+ public:
+  NoAtExitBaseTestSuite(int argc, char** argv)
+      : base::TestSuite(argc, argv, false) {
+  }
+};
+
+int RunTestSuite(int argc, char** argv) {
+  return NoAtExitBaseTestSuite(argc, argv).Run();
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+#if defined(OS_ANDROID)
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::RegisterContentUriTestUtils(env);
+#else
+  base::AtExitManager at_exit;
+#endif
+  return base::LaunchUnitTests(argc,
+                               argv,
+                               base::Bind(&RunTestSuite, argc, argv));
+}
diff --git a/base/test/scoped_locale.cc b/base/test/scoped_locale.cc
new file mode 100644
index 0000000..35b3fbe
--- /dev/null
+++ b/base/test/scoped_locale.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_locale.h"
+
+#include <locale.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+ScopedLocale::ScopedLocale(const std::string& locale) {
+  prev_locale_ = setlocale(LC_ALL, NULL);
+  EXPECT_TRUE(setlocale(LC_ALL, locale.c_str()) != NULL) <<
+      "Failed to set locale: " << locale;
+}
+
+ScopedLocale::~ScopedLocale() {
+  EXPECT_STREQ(prev_locale_.c_str(), setlocale(LC_ALL, prev_locale_.c_str()));
+}
+
+}  // namespace base
diff --git a/base/test/scoped_locale.h b/base/test/scoped_locale.h
new file mode 100644
index 0000000..a9f9348
--- /dev/null
+++ b/base/test/scoped_locale.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_SCOPED_LOCALE_H_
+#define BASE_TEST_SCOPED_LOCALE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace base {
+
+// Sets the given |locale| on construction, and restores the previous locale
+// on destruction.
+class ScopedLocale {
+ public:
+  explicit ScopedLocale(const std::string& locale);
+  ~ScopedLocale();
+
+ private:
+  std::string prev_locale_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedLocale);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_SCOPED_LOCALE_H_
diff --git a/base/test/scoped_path_override.cc b/base/test/scoped_path_override.cc
new file mode 100644
index 0000000..495ba2f
--- /dev/null
+++ b/base/test/scoped_path_override.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_path_override.h"
+
+#include "base/logging.h"
+#include "base/path_service.h"
+
+namespace base {
+
+ScopedPathOverride::ScopedPathOverride(int key) : key_(key) {
+  bool result = temp_dir_.CreateUniqueTempDir();
+  CHECK(result);
+  result = PathService::Override(key, temp_dir_.path());
+  CHECK(result);
+}
+
+ScopedPathOverride::ScopedPathOverride(int key, const base::FilePath& dir)
+    : key_(key) {
+  bool result = PathService::Override(key, dir);
+  CHECK(result);
+}
+
+ScopedPathOverride::~ScopedPathOverride() {
+   bool result = PathService::RemoveOverride(key_);
+   CHECK(result) << "The override seems to have been removed already!";
+}
+
+}  // namespace base
diff --git a/base/test/scoped_path_override.h b/base/test/scoped_path_override.h
new file mode 100644
index 0000000..a1e18e3
--- /dev/null
+++ b/base/test/scoped_path_override.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_SCOPED_PATH_OVERRIDE_H_
+#define BASE_TEST_SCOPED_PATH_OVERRIDE_H_
+
+#include "base/basictypes.h"
+#include "base/files/scoped_temp_dir.h"
+
+namespace base {
+
+class FilePath;
+
+// Sets a path override on construction, and removes it when the object goes out
+// of scope. This class is intended to be used by tests that need to override
+// paths to ensure their overrides are properly handled and reverted when the
+// scope of the test is left.
+class ScopedPathOverride {
+ public:
+  // Contructor that initializes the override to a scoped temp directory.
+  explicit ScopedPathOverride(int key);
+  // Constructor that would use a path provided by the user.
+  ScopedPathOverride(int key, const FilePath& dir);
+  ~ScopedPathOverride();
+
+ private:
+  int key_;
+  ScopedTempDir temp_dir_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedPathOverride);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_SCOPED_PATH_OVERRIDE_H_
diff --git a/base/test/sequenced_task_runner_test_template.cc b/base/test/sequenced_task_runner_test_template.cc
new file mode 100644
index 0000000..010f439
--- /dev/null
+++ b/base/test/sequenced_task_runner_test_template.cc
@@ -0,0 +1,270 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/sequenced_task_runner_test_template.h"
+
+#include <ostream>
+
+#include "base/location.h"
+
+namespace base {
+
+namespace internal {
+
+TaskEvent::TaskEvent(int i, Type type)
+  : i(i), type(type) {
+}
+
+SequencedTaskTracker::SequencedTaskTracker()
+    : next_post_i_(0),
+      task_end_count_(0),
+      task_end_cv_(&lock_) {
+}
+
+void SequencedTaskTracker::PostWrappedNonNestableTask(
+    const scoped_refptr<SequencedTaskRunner>& task_runner,
+    const Closure& task) {
+  AutoLock event_lock(lock_);
+  const int post_i = next_post_i_++;
+  Closure wrapped_task = Bind(&SequencedTaskTracker::RunTask, this,
+                              task, post_i);
+  task_runner->PostNonNestableTask(FROM_HERE, wrapped_task);
+  TaskPosted(post_i);
+}
+
+void SequencedTaskTracker::PostWrappedNestableTask(
+    const scoped_refptr<SequencedTaskRunner>& task_runner,
+    const Closure& task) {
+  AutoLock event_lock(lock_);
+  const int post_i = next_post_i_++;
+  Closure wrapped_task = Bind(&SequencedTaskTracker::RunTask, this,
+                              task, post_i);
+  task_runner->PostTask(FROM_HERE, wrapped_task);
+  TaskPosted(post_i);
+}
+
+void SequencedTaskTracker::PostWrappedDelayedNonNestableTask(
+    const scoped_refptr<SequencedTaskRunner>& task_runner,
+    const Closure& task,
+    TimeDelta delay) {
+  AutoLock event_lock(lock_);
+  const int post_i = next_post_i_++;
+  Closure wrapped_task = Bind(&SequencedTaskTracker::RunTask, this,
+                              task, post_i);
+  task_runner->PostNonNestableDelayedTask(FROM_HERE, wrapped_task, delay);
+  TaskPosted(post_i);
+}
+
+void SequencedTaskTracker::PostNonNestableTasks(
+    const scoped_refptr<SequencedTaskRunner>& task_runner,
+    int task_count) {
+  for (int i = 0; i < task_count; ++i) {
+    PostWrappedNonNestableTask(task_runner, Closure());
+  }
+}
+
+void SequencedTaskTracker::RunTask(const Closure& task, int task_i) {
+  TaskStarted(task_i);
+  if (!task.is_null())
+    task.Run();
+  TaskEnded(task_i);
+}
+
+void SequencedTaskTracker::TaskPosted(int i) {
+  // Caller must own |lock_|.
+  events_.push_back(TaskEvent(i, TaskEvent::POST));
+}
+
+void SequencedTaskTracker::TaskStarted(int i) {
+  AutoLock lock(lock_);
+  events_.push_back(TaskEvent(i, TaskEvent::START));
+}
+
+void SequencedTaskTracker::TaskEnded(int i) {
+  AutoLock lock(lock_);
+  events_.push_back(TaskEvent(i, TaskEvent::END));
+  ++task_end_count_;
+  task_end_cv_.Signal();
+}
+
+const std::vector<TaskEvent>&
+SequencedTaskTracker::GetTaskEvents() const {
+  return events_;
+}
+
+void SequencedTaskTracker::WaitForCompletedTasks(int count) {
+  AutoLock lock(lock_);
+  while (task_end_count_ < count)
+    task_end_cv_.Wait();
+}
+
+SequencedTaskTracker::~SequencedTaskTracker() {
+}
+
+void PrintTo(const TaskEvent& event, std::ostream* os) {
+  *os << "(i=" << event.i << ", type=";
+  switch (event.type) {
+    case TaskEvent::POST: *os << "POST"; break;
+    case TaskEvent::START: *os << "START"; break;
+    case TaskEvent::END: *os << "END"; break;
+  }
+  *os << ")";
+}
+
+namespace {
+
+// Returns the task ordinals for the task event type |type| in the order that
+// they were recorded.
+std::vector<int> GetEventTypeOrder(const std::vector<TaskEvent>& events,
+                                   TaskEvent::Type type) {
+  std::vector<int> tasks;
+  std::vector<TaskEvent>::const_iterator event;
+  for (event = events.begin(); event != events.end(); ++event) {
+    if (event->type == type)
+      tasks.push_back(event->i);
+  }
+  return tasks;
+}
+
+// Returns all task events for task |task_i|.
+std::vector<TaskEvent::Type> GetEventsForTask(
+    const std::vector<TaskEvent>& events,
+    int task_i) {
+  std::vector<TaskEvent::Type> task_event_orders;
+  std::vector<TaskEvent>::const_iterator event;
+  for (event = events.begin(); event != events.end(); ++event) {
+    if (event->i == task_i)
+      task_event_orders.push_back(event->type);
+  }
+  return task_event_orders;
+}
+
+// Checks that the task events for each task in |events| occur in the order
+// {POST, START, END}, and that there is only one instance of each event type
+// per task.
+::testing::AssertionResult CheckEventOrdersForEachTask(
+    const std::vector<TaskEvent>& events,
+    int task_count) {
+  std::vector<TaskEvent::Type> expected_order;
+  expected_order.push_back(TaskEvent::POST);
+  expected_order.push_back(TaskEvent::START);
+  expected_order.push_back(TaskEvent::END);
+
+  // This is O(n^2), but it runs fast enough currently so is not worth
+  // optimizing.
+  for (int i = 0; i < task_count; ++i) {
+    const std::vector<TaskEvent::Type> task_events =
+        GetEventsForTask(events, i);
+    if (task_events != expected_order) {
+      return ::testing::AssertionFailure()
+          << "Events for task " << i << " are out of order; expected: "
+          << ::testing::PrintToString(expected_order) << "; actual: "
+          << ::testing::PrintToString(task_events);
+    }
+  }
+  return ::testing::AssertionSuccess();
+}
+
+// Checks that no two tasks were running at the same time. I.e. the only
+// events allowed between the START and END of a task are the POSTs of other
+// tasks.
+::testing::AssertionResult CheckNoTaskRunsOverlap(
+    const std::vector<TaskEvent>& events) {
+  // If > -1, we're currently inside a START, END pair.
+  int current_task_i = -1;
+
+  std::vector<TaskEvent>::const_iterator event;
+  for (event = events.begin(); event != events.end(); ++event) {
+    bool spurious_event_found = false;
+
+    if (current_task_i == -1) {  // Not inside a START, END pair.
+      switch (event->type) {
+        case TaskEvent::POST:
+          break;
+        case TaskEvent::START:
+          current_task_i = event->i;
+          break;
+        case TaskEvent::END:
+          spurious_event_found = true;
+          break;
+      }
+
+    } else {  // Inside a START, END pair.
+      bool interleaved_task_detected = false;
+
+      switch (event->type) {
+        case TaskEvent::POST:
+          if (event->i == current_task_i)
+            spurious_event_found = true;
+          break;
+        case TaskEvent::START:
+          interleaved_task_detected = true;
+          break;
+        case TaskEvent::END:
+          if (event->i != current_task_i)
+            interleaved_task_detected = true;
+          else
+            current_task_i = -1;
+          break;
+      }
+
+      if (interleaved_task_detected) {
+        return ::testing::AssertionFailure()
+            << "Found event " << ::testing::PrintToString(*event)
+            << " between START and END events for task " << current_task_i
+            << "; event dump: " << ::testing::PrintToString(events);
+      }
+    }
+
+    if (spurious_event_found) {
+      const int event_i = event - events.begin();
+      return ::testing::AssertionFailure()
+          << "Spurious event " << ::testing::PrintToString(*event)
+          << " at position " << event_i << "; event dump: "
+          << ::testing::PrintToString(events);
+    }
+  }
+
+  return ::testing::AssertionSuccess();
+}
+
+}  // namespace
+
+::testing::AssertionResult CheckNonNestableInvariants(
+    const std::vector<TaskEvent>& events,
+    int task_count) {
+  const std::vector<int> post_order =
+      GetEventTypeOrder(events, TaskEvent::POST);
+  const std::vector<int> start_order =
+      GetEventTypeOrder(events, TaskEvent::START);
+  const std::vector<int> end_order =
+      GetEventTypeOrder(events, TaskEvent::END);
+
+  if (start_order != post_order) {
+    return ::testing::AssertionFailure()
+        << "Expected START order (which equals actual POST order): \n"
+        << ::testing::PrintToString(post_order)
+        << "\n Actual START order:\n"
+        << ::testing::PrintToString(start_order);
+  }
+
+  if (end_order != post_order) {
+    return ::testing::AssertionFailure()
+        << "Expected END order (which equals actual POST order): \n"
+        << ::testing::PrintToString(post_order)
+        << "\n Actual END order:\n"
+        << ::testing::PrintToString(end_order);
+  }
+
+  const ::testing::AssertionResult result =
+      CheckEventOrdersForEachTask(events, task_count);
+  if (!result)
+    return result;
+
+  return CheckNoTaskRunsOverlap(events);
+}
+
+}  // namespace internal
+
+}  // namespace base
diff --git a/base/test/sequenced_task_runner_test_template.h b/base/test/sequenced_task_runner_test_template.h
new file mode 100644
index 0000000..48f5354
--- /dev/null
+++ b/base/test/sequenced_task_runner_test_template.h
@@ -0,0 +1,341 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This class defines tests that implementations of SequencedTaskRunner should
+// pass in order to be conformant. See task_runner_test_template.h for a
+// description of how to use the constructs in this file; these work the same.
+
+#ifndef BASE_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
+#define BASE_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
+
+#include <cstddef>
+#include <iosfwd>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace internal {
+
+struct TaskEvent {
+  enum Type { POST, START, END };
+  TaskEvent(int i, Type type);
+  int i;
+  Type type;
+};
+
+// Utility class used in the tests below.
+class SequencedTaskTracker : public RefCountedThreadSafe<SequencedTaskTracker> {
+ public:
+  SequencedTaskTracker();
+
+  // Posts the non-nestable task |task|, and records its post event.
+  void PostWrappedNonNestableTask(
+      const scoped_refptr<SequencedTaskRunner>& task_runner,
+      const Closure& task);
+
+  // Posts the nestable task |task|, and records its post event.
+  void PostWrappedNestableTask(
+      const scoped_refptr<SequencedTaskRunner>& task_runner,
+      const Closure& task);
+
+  // Posts the delayed non-nestable task |task|, and records its post event.
+  void PostWrappedDelayedNonNestableTask(
+      const scoped_refptr<SequencedTaskRunner>& task_runner,
+      const Closure& task,
+      TimeDelta delay);
+
+  // Posts |task_count| non-nestable tasks.
+  void PostNonNestableTasks(
+      const scoped_refptr<SequencedTaskRunner>& task_runner,
+      int task_count);
+
+  const std::vector<TaskEvent>& GetTaskEvents() const;
+
+  // Returns after the tracker observes a total of |count| task completions.
+  void WaitForCompletedTasks(int count);
+
+ private:
+  friend class RefCountedThreadSafe<SequencedTaskTracker>;
+
+  ~SequencedTaskTracker();
+
+  // A task which runs |task|, recording the start and end events.
+  void RunTask(const Closure& task, int task_i);
+
+  // Records a post event for task |i|. The owner is expected to be holding
+  // |lock_| (unlike |TaskStarted| and |TaskEnded|).
+  void TaskPosted(int i);
+
+  // Records a start event for task |i|.
+  void TaskStarted(int i);
+
+  // Records a end event for task |i|.
+  void TaskEnded(int i);
+
+  // Protects events_, next_post_i_, task_end_count_ and task_end_cv_.
+  Lock lock_;
+
+  // The events as they occurred for each task (protected by lock_).
+  std::vector<TaskEvent> events_;
+
+  // The ordinal to be used for the next task-posting task (protected by
+  // lock_).
+  int next_post_i_;
+
+  // The number of task end events we've received.
+  int task_end_count_;
+  ConditionVariable task_end_cv_;
+
+  DISALLOW_COPY_AND_ASSIGN(SequencedTaskTracker);
+};
+
+void PrintTo(const TaskEvent& event, std::ostream* os);
+
+// Checks the non-nestable task invariants for all tasks in |events|.
+//
+// The invariants are:
+// 1) Events started and ended in the same order that they were posted.
+// 2) Events for an individual tasks occur in the order {POST, START, END},
+//    and there is only one instance of each event type for a task.
+// 3) The only events between a task's START and END events are the POSTs of
+//    other tasks. I.e. tasks were run sequentially, not interleaved.
+::testing::AssertionResult CheckNonNestableInvariants(
+    const std::vector<TaskEvent>& events,
+    int task_count);
+
+}  // namespace internal
+
+template <typename TaskRunnerTestDelegate>
+class SequencedTaskRunnerTest : public testing::Test {
+ protected:
+  SequencedTaskRunnerTest()
+      : task_tracker_(new internal::SequencedTaskTracker()) {}
+
+  const scoped_refptr<internal::SequencedTaskTracker> task_tracker_;
+  TaskRunnerTestDelegate delegate_;
+};
+
+TYPED_TEST_CASE_P(SequencedTaskRunnerTest);
+
+// This test posts N non-nestable tasks in sequence, and expects them to run
+// in FIFO order, with no part of any two tasks' execution
+// overlapping. I.e. that each task starts only after the previously-posted
+// one has finished.
+TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNonNestable) {
+  const int kTaskCount = 1000;
+
+  this->delegate_.StartTaskRunner();
+  const scoped_refptr<SequencedTaskRunner> task_runner =
+      this->delegate_.GetTaskRunner();
+
+  this->task_tracker_->PostWrappedNonNestableTask(
+      task_runner, Bind(&PlatformThread::Sleep, TimeDelta::FromSeconds(1)));
+  for (int i = 1; i < kTaskCount; ++i) {
+    this->task_tracker_->PostWrappedNonNestableTask(task_runner, Closure());
+  }
+
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+                                         kTaskCount));
+}
+
+// This test posts N nestable tasks in sequence. It has the same expectations
+// as SequentialNonNestable because even though the tasks are nestable, they
+// will not be run nestedly in this case.
+TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNestable) {
+  const int kTaskCount = 1000;
+
+  this->delegate_.StartTaskRunner();
+  const scoped_refptr<SequencedTaskRunner> task_runner =
+      this->delegate_.GetTaskRunner();
+
+  this->task_tracker_->PostWrappedNestableTask(
+      task_runner,
+      Bind(&PlatformThread::Sleep, TimeDelta::FromSeconds(1)));
+  for (int i = 1; i < kTaskCount; ++i) {
+    this->task_tracker_->PostWrappedNestableTask(task_runner, Closure());
+  }
+
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+                                         kTaskCount));
+}
+
+// This test posts non-nestable tasks in order of increasing delay, and checks
+// that that the tasks are run in FIFO order and that there is no execution
+// overlap whatsoever between any two tasks.
+TYPED_TEST_P(SequencedTaskRunnerTest, SequentialDelayedNonNestable) {
+  const int kTaskCount = 20;
+  const int kDelayIncrementMs = 50;
+
+  this->delegate_.StartTaskRunner();
+  const scoped_refptr<SequencedTaskRunner> task_runner =
+      this->delegate_.GetTaskRunner();
+
+  for (int i = 0; i < kTaskCount; ++i) {
+    this->task_tracker_->PostWrappedDelayedNonNestableTask(
+        task_runner,
+        Closure(),
+        TimeDelta::FromMilliseconds(kDelayIncrementMs * i));
+  }
+
+  this->task_tracker_->WaitForCompletedTasks(kTaskCount);
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+                                         kTaskCount));
+}
+
+// This test posts a fast, non-nestable task from within each of a number of
+// slow, non-nestable tasks and checks that they all run in the sequence they
+// were posted in and that there is no execution overlap whatsoever.
+TYPED_TEST_P(SequencedTaskRunnerTest, NonNestablePostFromNonNestableTask) {
+  const int kParentCount = 10;
+  const int kChildrenPerParent = 10;
+
+  this->delegate_.StartTaskRunner();
+  const scoped_refptr<SequencedTaskRunner> task_runner =
+      this->delegate_.GetTaskRunner();
+
+  for (int i = 0; i < kParentCount; ++i) {
+    Closure task = Bind(
+        &internal::SequencedTaskTracker::PostNonNestableTasks,
+        this->task_tracker_,
+        task_runner,
+        kChildrenPerParent);
+    this->task_tracker_->PostWrappedNonNestableTask(task_runner, task);
+  }
+
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_TRUE(CheckNonNestableInvariants(
+      this->task_tracker_->GetTaskEvents(),
+      kParentCount * (kChildrenPerParent + 1)));
+}
+
+// This test posts a delayed task, and checks that the task is run later than
+// the specified time.
+TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskBasic) {
+  const int kTaskCount = 1;
+  const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
+
+  this->delegate_.StartTaskRunner();
+  const scoped_refptr<SequencedTaskRunner> task_runner =
+      this->delegate_.GetTaskRunner();
+
+  Time time_before_run = Time::Now();
+  this->task_tracker_->PostWrappedDelayedNonNestableTask(
+      task_runner, Closure(), kDelay);
+  this->task_tracker_->WaitForCompletedTasks(kTaskCount);
+  this->delegate_.StopTaskRunner();
+  Time time_after_run = Time::Now();
+
+  EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+                                         kTaskCount));
+  EXPECT_LE(kDelay, time_after_run - time_before_run);
+}
+
+// This test posts two tasks with the same delay, and checks that the tasks are
+// run in the order in which they were posted.
+//
+// NOTE: This is actually an approximate test since the API only takes a
+// "delay" parameter, so we are not exactly simulating two tasks that get
+// posted at the exact same time. It would be nice if the API allowed us to
+// specify the desired run time.
+TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTasksSameDelay) {
+  const int kTaskCount = 2;
+  const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
+
+  this->delegate_.StartTaskRunner();
+  const scoped_refptr<SequencedTaskRunner> task_runner =
+      this->delegate_.GetTaskRunner();
+
+  this->task_tracker_->PostWrappedDelayedNonNestableTask(
+      task_runner, Closure(), kDelay);
+  this->task_tracker_->PostWrappedDelayedNonNestableTask(
+      task_runner, Closure(), kDelay);
+  this->task_tracker_->WaitForCompletedTasks(kTaskCount);
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+                                         kTaskCount));
+}
+
+// This test posts a normal task and a delayed task, and checks that the
+// delayed task runs after the normal task even if the normal task takes
+// a long time to run.
+TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterLongTask) {
+  const int kTaskCount = 2;
+
+  this->delegate_.StartTaskRunner();
+  const scoped_refptr<SequencedTaskRunner> task_runner =
+      this->delegate_.GetTaskRunner();
+
+  this->task_tracker_->PostWrappedNonNestableTask(
+      task_runner, base::Bind(&PlatformThread::Sleep,
+                              TimeDelta::FromMilliseconds(50)));
+  this->task_tracker_->PostWrappedDelayedNonNestableTask(
+      task_runner, Closure(), TimeDelta::FromMilliseconds(10));
+  this->task_tracker_->WaitForCompletedTasks(kTaskCount);
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+                                         kTaskCount));
+}
+
+// Test that a pile of normal tasks and a delayed task run in the
+// time-to-run order.
+TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterManyLongTasks) {
+  const int kTaskCount = 11;
+
+  this->delegate_.StartTaskRunner();
+  const scoped_refptr<SequencedTaskRunner> task_runner =
+      this->delegate_.GetTaskRunner();
+
+  for (int i = 0; i < kTaskCount - 1; i++) {
+    this->task_tracker_->PostWrappedNonNestableTask(
+        task_runner, base::Bind(&PlatformThread::Sleep,
+                                TimeDelta::FromMilliseconds(50)));
+  }
+  this->task_tracker_->PostWrappedDelayedNonNestableTask(
+      task_runner, Closure(), TimeDelta::FromMilliseconds(10));
+  this->task_tracker_->WaitForCompletedTasks(kTaskCount);
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
+                                         kTaskCount));
+}
+
+
+// TODO(francoisk777@gmail.com) Add a test, similiar to the above, which runs
+// some tasked nestedly (which should be implemented in the test
+// delegate). Also add, to the the test delegate, a predicate which checks
+// whether the implementation supports nested tasks.
+//
+
+REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerTest,
+                           SequentialNonNestable,
+                           SequentialNestable,
+                           SequentialDelayedNonNestable,
+                           NonNestablePostFromNonNestableTask,
+                           DelayedTaskBasic,
+                           DelayedTasksSameDelay,
+                           DelayedTaskAfterLongTask,
+                           DelayedTaskAfterManyLongTasks);
+
+}  // namespace base
+
+#endif  // BASE_TASK_RUNNER_TEST_TEMPLATE_H_
diff --git a/base/test/sequenced_worker_pool_owner.cc b/base/test/sequenced_worker_pool_owner.cc
new file mode 100644
index 0000000..f6a0d01
--- /dev/null
+++ b/base/test/sequenced_worker_pool_owner.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/sequenced_worker_pool_owner.h"
+
+#include "base/location.h"
+#include "base/message_loop/message_loop.h"
+
+namespace base {
+
+SequencedWorkerPoolOwner::SequencedWorkerPoolOwner(
+    size_t max_threads,
+    const std::string& thread_name_prefix)
+    : constructor_message_loop_(MessageLoop::current()),
+      pool_(new SequencedWorkerPool(max_threads, thread_name_prefix, this)),
+      has_work_call_count_(0) {}
+
+SequencedWorkerPoolOwner::~SequencedWorkerPoolOwner() {
+  pool_ = NULL;
+  MessageLoop::current()->Run();
+}
+
+const scoped_refptr<SequencedWorkerPool>& SequencedWorkerPoolOwner::pool() {
+  return pool_;
+}
+
+void SequencedWorkerPoolOwner::SetWillWaitForShutdownCallback(
+    const Closure& callback) {
+  will_wait_for_shutdown_callback_ = callback;
+}
+
+int SequencedWorkerPoolOwner::has_work_call_count() const {
+  AutoLock lock(has_work_lock_);
+  return has_work_call_count_;
+}
+
+void SequencedWorkerPoolOwner::OnHasWork() {
+  AutoLock lock(has_work_lock_);
+  ++has_work_call_count_;
+}
+
+void SequencedWorkerPoolOwner::WillWaitForShutdown() {
+  if (!will_wait_for_shutdown_callback_.is_null()) {
+    will_wait_for_shutdown_callback_.Run();
+  }
+}
+
+void SequencedWorkerPoolOwner::OnDestruct() {
+  constructor_message_loop_->PostTask(
+      FROM_HERE,
+      constructor_message_loop_->QuitWhenIdleClosure());
+}
+
+}  // namespace base
diff --git a/base/test/sequenced_worker_pool_owner.h b/base/test/sequenced_worker_pool_owner.h
new file mode 100644
index 0000000..1cc3fd6
--- /dev/null
+++ b/base/test/sequenced_worker_pool_owner.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_SEQUENCED_WORKER_POOL_UNITTEST_H_
+#define BASE_THREADING_SEQUENCED_WORKER_POOL_UNITTEST_H_
+
+#include <cstddef>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/sequenced_worker_pool.h"
+
+namespace base {
+
+class MessageLoop;
+
+// Wrapper around SequencedWorkerPool for testing that blocks destruction
+// until the pool is actually destroyed.  This is so that a
+// SequencedWorkerPool from one test doesn't outlive its test and cause
+// strange races with other tests that touch global stuff (like histograms and
+// logging).  However, this requires that nothing else on this thread holds a
+// ref to the pool when the SequencedWorkerPoolOwner is destroyed.
+class SequencedWorkerPoolOwner : public SequencedWorkerPool::TestingObserver {
+ public:
+  SequencedWorkerPoolOwner(size_t max_threads,
+                           const std::string& thread_name_prefix);
+
+  virtual ~SequencedWorkerPoolOwner();
+
+  // Don't change the returned pool's testing observer.
+  const scoped_refptr<SequencedWorkerPool>& pool();
+
+  // The given callback will be called on WillWaitForShutdown().
+  void SetWillWaitForShutdownCallback(const Closure& callback);
+
+  int has_work_call_count() const;
+
+ private:
+  // SequencedWorkerPool::TestingObserver implementation.
+  virtual void OnHasWork() OVERRIDE;
+  virtual void WillWaitForShutdown() OVERRIDE;
+  virtual void OnDestruct() OVERRIDE;
+
+  MessageLoop* const constructor_message_loop_;
+  scoped_refptr<SequencedWorkerPool> pool_;
+  Closure will_wait_for_shutdown_callback_;
+
+  mutable Lock has_work_lock_;
+  int has_work_call_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(SequencedWorkerPoolOwner);
+};
+
+}  // namespace base
+
+#endif  // BASE_THREADING_SEQUENCED_WORKER_POOL_UNITTEST_H_
diff --git a/base/test/simple_test_clock.cc b/base/test/simple_test_clock.cc
new file mode 100644
index 0000000..a2bdc2a
--- /dev/null
+++ b/base/test/simple_test_clock.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/simple_test_clock.h"
+
+namespace base {
+
+SimpleTestClock::SimpleTestClock() {}
+
+SimpleTestClock::~SimpleTestClock() {}
+
+Time SimpleTestClock::Now() {
+  AutoLock lock(lock_);
+  return now_;
+}
+
+void SimpleTestClock::Advance(TimeDelta delta) {
+  AutoLock lock(lock_);
+  now_ += delta;
+}
+
+void SimpleTestClock::SetNow(Time now) {
+  AutoLock lock(lock_);
+  now_ = now;
+}
+
+}  // namespace base
diff --git a/base/test/simple_test_clock.h b/base/test/simple_test_clock.h
new file mode 100644
index 0000000..2056aab
--- /dev/null
+++ b/base/test/simple_test_clock.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SIMPLE_TEST_CLOCK_H_
+#define BASE_SIMPLE_TEST_CLOCK_H_
+
+#include "base/compiler_specific.h"
+#include "base/synchronization/lock.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+
+namespace base {
+
+// SimpleTestClock is a Clock implementation that gives control over
+// the returned Time objects.  All methods may be called from any
+// thread.
+class SimpleTestClock : public Clock {
+ public:
+  // Starts off with a clock set to Time().
+  SimpleTestClock();
+  virtual ~SimpleTestClock();
+
+  virtual Time Now() OVERRIDE;
+
+  // Advances the clock by |delta|.
+  void Advance(TimeDelta delta);
+
+  // Sets the clock to the given time.
+  void SetNow(Time now);
+
+ private:
+  // Protects |now_|.
+  Lock lock_;
+
+  Time now_;
+};
+
+}  // namespace base
+
+#endif  // BASE_SIMPLE_TEST_CLOCK_H_
diff --git a/base/test/simple_test_tick_clock.cc b/base/test/simple_test_tick_clock.cc
new file mode 100644
index 0000000..1b4696f
--- /dev/null
+++ b/base/test/simple_test_tick_clock.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/simple_test_tick_clock.h"
+
+#include "base/logging.h"
+
+namespace base {
+
+SimpleTestTickClock::SimpleTestTickClock() {}
+
+SimpleTestTickClock::~SimpleTestTickClock() {}
+
+TimeTicks SimpleTestTickClock::NowTicks() {
+  AutoLock lock(lock_);
+  return now_ticks_;
+}
+
+void SimpleTestTickClock::Advance(TimeDelta delta) {
+  AutoLock lock(lock_);
+  DCHECK(delta >= TimeDelta());
+  now_ticks_ += delta;
+}
+
+}  // namespace base
diff --git a/base/test/simple_test_tick_clock.h b/base/test/simple_test_tick_clock.h
new file mode 100644
index 0000000..867de80
--- /dev/null
+++ b/base/test/simple_test_tick_clock.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SIMPLE_TEST_TICK_CLOCK_H_
+#define BASE_SIMPLE_TEST_TICK_CLOCK_H_
+
+#include "base/compiler_specific.h"
+#include "base/synchronization/lock.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+
+namespace base {
+
+// SimpleTestTickClock is a TickClock implementation that gives
+// control over the returned TimeTicks objects.  All methods may be
+// called from any thread.
+class SimpleTestTickClock : public TickClock {
+ public:
+  // Starts off with a clock set to TimeTicks().
+  SimpleTestTickClock();
+  virtual ~SimpleTestTickClock();
+
+  virtual TimeTicks NowTicks() OVERRIDE;
+
+  // Advances the clock by |delta|, which must not be negative.
+  void Advance(TimeDelta delta);
+
+ private:
+  // Protects |now_ticks_|.
+  Lock lock_;
+
+  TimeTicks now_ticks_;
+};
+
+}  // namespace base
+
+#endif  // BASE_SIMPLE_TEST_TICK_CLOCK_H_
diff --git a/base/test/task_runner_test_template.cc b/base/test/task_runner_test_template.cc
new file mode 100644
index 0000000..b756203
--- /dev/null
+++ b/base/test/task_runner_test_template.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/task_runner_test_template.h"
+
+namespace base {
+
+namespace internal {
+
+TaskTracker::TaskTracker() : task_runs_(0), task_runs_cv_(&lock_) {}
+
+TaskTracker::~TaskTracker() {}
+
+Closure TaskTracker::WrapTask(const Closure& task, int i) {
+  return Bind(&TaskTracker::RunTask, this, task, i);
+}
+
+void TaskTracker::RunTask(const Closure& task, int i) {
+  AutoLock lock(lock_);
+  if (!task.is_null()) {
+    task.Run();
+  }
+  ++task_run_counts_[i];
+  ++task_runs_;
+  task_runs_cv_.Signal();
+}
+
+std::map<int, int> TaskTracker::GetTaskRunCounts() const {
+  AutoLock lock(lock_);
+  return task_run_counts_;
+}
+
+void TaskTracker::WaitForCompletedTasks(int count) {
+  AutoLock lock(lock_);
+  while (task_runs_ < count)
+    task_runs_cv_.Wait();
+}
+
+void ExpectRunsTasksOnCurrentThread(
+    bool expected_value,
+    const scoped_refptr<TaskRunner>& task_runner) {
+  EXPECT_EQ(expected_value, task_runner->RunsTasksOnCurrentThread());
+}
+
+}  // namespace internal
+
+}  // namespace base
diff --git a/base/test/task_runner_test_template.h b/base/test/task_runner_test_template.h
new file mode 100644
index 0000000..73aa1f4
--- /dev/null
+++ b/base/test/task_runner_test_template.h
@@ -0,0 +1,215 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This class defines tests that implementations of TaskRunner should
+// pass in order to be conformant.  Here's how you use it to test your
+// implementation.
+//
+// Say your class is called MyTaskRunner.  Then you need to define a
+// class called MyTaskRunnerTestDelegate in my_task_runner_unittest.cc
+// like this:
+//
+//   class MyTaskRunnerTestDelegate {
+//    public:
+//     // Tasks posted to the task runner after this and before
+//     // StopTaskRunner() is called is called should run successfully.
+//     void StartTaskRunner() {
+//       ...
+//     }
+//
+//     // Should return the task runner implementation.  Only called
+//     // after StartTaskRunner and before StopTaskRunner.
+//     scoped_refptr<MyTaskRunner> GetTaskRunner() {
+//       ...
+//     }
+//
+//     // Stop the task runner and make sure all tasks posted before
+//     // this is called are run. Caveat: delayed tasks are not run,
+       // they're simply deleted.
+//     void StopTaskRunner() {
+//       ...
+//     }
+//   };
+//
+// The TaskRunnerTest test harness will have a member variable of
+// this delegate type and will call its functions in the various
+// tests.
+//
+// Then you simply #include this file as well as gtest.h and add the
+// following statement to my_task_runner_unittest.cc:
+//
+//   INSTANTIATE_TYPED_TEST_CASE_P(
+//       MyTaskRunner, TaskRunnerTest, MyTaskRunnerTestDelegate);
+//
+// Easy!
+
+#ifndef BASE_TEST_TASK_RUNNER_TEST_TEMPLATE_H_
+#define BASE_TEST_TASK_RUNNER_TEST_TEMPLATE_H_
+
+#include <cstddef>
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/task_runner.h"
+#include "base/threading/thread.h"
+#include "base/tracked_objects.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace internal {
+
+// Utility class that keeps track of how many times particular tasks
+// are run.
+class TaskTracker : public RefCountedThreadSafe<TaskTracker> {
+ public:
+  TaskTracker();
+
+  // Returns a closure that runs the given task and increments the run
+  // count of |i| by one.  |task| may be null.  It is guaranteed that
+  // only one task wrapped by a given tracker will be run at a time.
+  Closure WrapTask(const Closure& task, int i);
+
+  std::map<int, int> GetTaskRunCounts() const;
+
+  // Returns after the tracker observes a total of |count| task completions.
+  void WaitForCompletedTasks(int count);
+
+ private:
+  friend class RefCountedThreadSafe<TaskTracker>;
+
+  ~TaskTracker();
+
+  void RunTask(const Closure& task, int i);
+
+  mutable Lock lock_;
+  std::map<int, int> task_run_counts_;
+  int task_runs_;
+  ConditionVariable task_runs_cv_;
+
+  DISALLOW_COPY_AND_ASSIGN(TaskTracker);
+};
+
+}  // namespace internal
+
+template <typename TaskRunnerTestDelegate>
+class TaskRunnerTest : public testing::Test {
+ protected:
+  TaskRunnerTest() : task_tracker_(new internal::TaskTracker()) {}
+
+  const scoped_refptr<internal::TaskTracker> task_tracker_;
+  TaskRunnerTestDelegate delegate_;
+};
+
+TYPED_TEST_CASE_P(TaskRunnerTest);
+
+// We can't really test much, since TaskRunner provides very few
+// guarantees.
+
+// Post a bunch of tasks to the task runner.  They should all
+// complete.
+TYPED_TEST_P(TaskRunnerTest, Basic) {
+  std::map<int, int> expected_task_run_counts;
+
+  this->delegate_.StartTaskRunner();
+  scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner();
+  // Post each ith task i+1 times.
+  for (int i = 0; i < 20; ++i) {
+    const Closure& ith_task = this->task_tracker_->WrapTask(Closure(), i);
+    for (int j = 0; j < i + 1; ++j) {
+      task_runner->PostTask(FROM_HERE, ith_task);
+      ++expected_task_run_counts[i];
+    }
+  }
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_EQ(expected_task_run_counts,
+            this->task_tracker_->GetTaskRunCounts());
+}
+
+// Post a bunch of delayed tasks to the task runner.  They should all
+// complete.
+TYPED_TEST_P(TaskRunnerTest, Delayed) {
+  std::map<int, int> expected_task_run_counts;
+  int expected_total_tasks = 0;
+
+  this->delegate_.StartTaskRunner();
+  scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner();
+  // Post each ith task i+1 times with delays from 0-i.
+  for (int i = 0; i < 20; ++i) {
+    const Closure& ith_task = this->task_tracker_->WrapTask(Closure(), i);
+    for (int j = 0; j < i + 1; ++j) {
+      task_runner->PostDelayedTask(
+          FROM_HERE, ith_task, base::TimeDelta::FromMilliseconds(j));
+      ++expected_task_run_counts[i];
+      ++expected_total_tasks;
+    }
+  }
+  this->task_tracker_->WaitForCompletedTasks(expected_total_tasks);
+  this->delegate_.StopTaskRunner();
+
+  EXPECT_EQ(expected_task_run_counts,
+            this->task_tracker_->GetTaskRunCounts());
+}
+
+namespace internal {
+
+// Calls RunsTasksOnCurrentThread() on |task_runner| and expects it to
+// equal |expected_value|.
+void ExpectRunsTasksOnCurrentThread(
+    bool expected_value,
+    const scoped_refptr<TaskRunner>& task_runner);
+
+}  // namespace internal
+
+// Post a bunch of tasks to the task runner as well as to a separate
+// thread, each checking the value of RunsTasksOnCurrentThread(),
+// which should return true for the tasks posted on the task runner
+// and false for the tasks posted on the separate thread.
+TYPED_TEST_P(TaskRunnerTest, RunsTasksOnCurrentThread) {
+  std::map<int, int> expected_task_run_counts;
+
+  Thread thread("Non-task-runner thread");
+  ASSERT_TRUE(thread.Start());
+  this->delegate_.StartTaskRunner();
+
+  scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner();
+  // Post each ith task i+1 times on the task runner and i+1 times on
+  // the non-task-runner thread.
+  for (int i = 0; i < 20; ++i) {
+    const Closure& ith_task_runner_task =
+        this->task_tracker_->WrapTask(
+            Bind(&internal::ExpectRunsTasksOnCurrentThread,
+                 true, task_runner),
+            i);
+    const Closure& ith_non_task_runner_task =
+        this->task_tracker_->WrapTask(
+            Bind(&internal::ExpectRunsTasksOnCurrentThread,
+                 false, task_runner),
+            i);
+    for (int j = 0; j < i + 1; ++j) {
+      task_runner->PostTask(FROM_HERE, ith_task_runner_task);
+      thread.message_loop()->PostTask(FROM_HERE, ith_non_task_runner_task);
+      expected_task_run_counts[i] += 2;
+    }
+  }
+
+  this->delegate_.StopTaskRunner();
+  thread.Stop();
+
+  EXPECT_EQ(expected_task_run_counts,
+            this->task_tracker_->GetTaskRunCounts());
+}
+
+REGISTER_TYPED_TEST_CASE_P(
+    TaskRunnerTest, Basic, Delayed, RunsTasksOnCurrentThread);
+
+}  // namespace base
+
+#endif  //#define BASE_TEST_TASK_RUNNER_TEST_TEMPLATE_H_
diff --git a/base/test/test_file_util.cc b/base/test/test_file_util.cc
new file mode 100644
index 0000000..8dafc58
--- /dev/null
+++ b/base/test/test_file_util.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_file_util.h"
+
+#include "base/test/test_timeouts.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+bool EvictFileFromSystemCacheWithRetry(const FilePath& path) {
+  const int kCycles = 10;
+  const TimeDelta kDelay = TestTimeouts::action_timeout() / kCycles;
+  for (int i = 0; i < kCycles; i++) {
+    if (EvictFileFromSystemCache(path))
+      return true;
+    PlatformThread::Sleep(kDelay);
+  }
+  return false;
+}
+
+}  // namespace base
diff --git a/base/test/test_file_util.h b/base/test/test_file_util.h
new file mode 100644
index 0000000..27197f2
--- /dev/null
+++ b/base/test/test_file_util.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_FILE_UTIL_H_
+#define BASE_TEST_TEST_FILE_UTIL_H_
+
+// File utility functions used only by tests.
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+
+#if defined(OS_ANDROID)
+#include <jni.h>
+#include "base/basictypes.h"
+#endif
+
+namespace base {
+
+class FilePath;
+
+// Clear a specific file from the system cache like EvictFileFromSystemCache,
+// but on failure it will sleep and retry. On the Windows buildbots, eviction
+// can fail if the file is marked in use, and this will throw off timings that
+// rely on uncached files.
+bool EvictFileFromSystemCacheWithRetry(const FilePath& file);
+
+// Wrapper over base::Delete. On Windows repeatedly invokes Delete in case
+// of failure to workaround Windows file locking semantics. Returns true on
+// success.
+bool DieFileDie(const FilePath& file, bool recurse);
+
+// Clear a specific file from the system cache. After this call, trying
+// to access this file will result in a cold load from the hard drive.
+bool EvictFileFromSystemCache(const FilePath& file);
+
+#if defined(OS_WIN)
+// Returns true if the volume supports Alternate Data Streams.
+bool VolumeSupportsADS(const FilePath& path);
+
+// Returns true if the ZoneIdentifier is correctly set to "Internet" (3).
+// Note that this function must be called from the same process as
+// the one that set the zone identifier.  I.e. don't use it in UI/automation
+// based tests.
+bool HasInternetZoneIdentifier(const FilePath& full_path);
+#endif  // defined(OS_WIN)
+
+// For testing, make the file unreadable or unwritable.
+// In POSIX, this does not apply to the root user.
+bool MakeFileUnreadable(const FilePath& path) WARN_UNUSED_RESULT;
+bool MakeFileUnwritable(const FilePath& path) WARN_UNUSED_RESULT;
+
+// Saves the current permissions for a path, and restores it on destruction.
+class FilePermissionRestorer {
+ public:
+  explicit FilePermissionRestorer(const FilePath& path);
+  ~FilePermissionRestorer();
+
+ private:
+  const FilePath path_;
+  void* info_;  // The opaque stored permission information.
+  size_t length_;  // The length of the stored permission information.
+
+  DISALLOW_COPY_AND_ASSIGN(FilePermissionRestorer);
+};
+
+#if defined(OS_ANDROID)
+// Register the ContentUriTestUrils JNI bindings.
+bool RegisterContentUriTestUtils(JNIEnv* env);
+
+// Insert an image file into the MediaStore, and retrieve the content URI for
+// testing purpose.
+FilePath InsertImageIntoMediaStore(const FilePath& path);
+#endif  // defined(OS_ANDROID)
+
+}  // namespace base
+
+#endif  // BASE_TEST_TEST_FILE_UTIL_H_
diff --git a/base/test/test_file_util_android.cc b/base/test/test_file_util_android.cc
new file mode 100644
index 0000000..b8fd50c
--- /dev/null
+++ b/base/test/test_file_util_android.cc
@@ -0,0 +1,29 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_file_util.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/files/file_path.h"
+#include "jni/ContentUriTestUtils_jni.h"
+
+namespace base {
+
+bool RegisterContentUriTestUtils(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+FilePath InsertImageIntoMediaStore(const FilePath& path) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> j_path =
+      base::android::ConvertUTF8ToJavaString(env, path.value());
+  ScopedJavaLocalRef<jstring> j_uri =
+      Java_ContentUriTestUtils_insertImageIntoMediaStore(
+          env, base::android::GetApplicationContext(), j_path.obj());
+  std::string uri = base::android::ConvertJavaStringToUTF8(j_uri);
+  return FilePath(uri);
+}
+
+}  // namespace base
diff --git a/base/test/test_file_util_linux.cc b/base/test/test_file_util_linux.cc
new file mode 100644
index 0000000..0ef5c0a
--- /dev/null
+++ b/base/test/test_file_util_linux.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_file_util.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+
+namespace base {
+
+bool EvictFileFromSystemCache(const FilePath& file) {
+  ScopedFD fd(open(file.value().c_str(), O_RDONLY));
+  if (!fd.is_valid())
+    return false;
+  if (fdatasync(fd.get()) != 0)
+    return false;
+  if (posix_fadvise(fd.get(), 0, 0, POSIX_FADV_DONTNEED) != 0)
+    return false;
+  return true;
+}
+
+}  // namespace base
diff --git a/base/test/test_file_util_mac.cc b/base/test/test_file_util_mac.cc
new file mode 100644
index 0000000..11592c3
--- /dev/null
+++ b/base/test/test_file_util_mac.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_file_util.h"
+
+#include <sys/mman.h>
+#include <errno.h>
+
+#include "base/files/file_util.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/logging.h"
+
+namespace base {
+
+bool EvictFileFromSystemCache(const FilePath& file) {
+  // There aren't any really direct ways to purge a file from the UBC.  From
+  // talking with Amit Singh, the safest is to mmap the file with MAP_FILE (the
+  // default) + MAP_SHARED, then do an msync to invalidate the memory.  The next
+  // open should then have to load the file from disk.
+
+  int64 length;
+  if (!GetFileSize(file, &length)) {
+    DLOG(ERROR) << "failed to get size of " << file.value();
+    return false;
+  }
+
+  // When a file is empty, we do not need to evict it from cache.
+  // In fact, an attempt to map it to memory will result in error.
+  if (length == 0) {
+    DLOG(WARNING) << "file size is zero, will not attempt to map to memory";
+    return true;
+  }
+
+  MemoryMappedFile mapped_file;
+  if (!mapped_file.Initialize(file)) {
+    DLOG(WARNING) << "failed to memory map " << file.value();
+    return false;
+  }
+
+  if (msync(const_cast<uint8*>(mapped_file.data()), mapped_file.length(),
+            MS_INVALIDATE) != 0) {
+    DLOG(WARNING) << "failed to invalidate memory map of " << file.value()
+                  << ", errno: " << errno;
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace base
diff --git a/base/test/test_file_util_posix.cc b/base/test/test_file_util_posix.cc
new file mode 100644
index 0000000..12b892c
--- /dev/null
+++ b/base/test/test_file_util_posix.cc
@@ -0,0 +1,108 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_file_util.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace base {
+
+namespace {
+
+// Deny |permission| on the file |path|.
+bool DenyFilePermission(const FilePath& path, mode_t permission) {
+  struct stat stat_buf;
+  if (stat(path.value().c_str(), &stat_buf) != 0)
+    return false;
+  stat_buf.st_mode &= ~permission;
+
+  int rv = HANDLE_EINTR(chmod(path.value().c_str(), stat_buf.st_mode));
+  return rv == 0;
+}
+
+// Gets a blob indicating the permission information for |path|.
+// |length| is the length of the blob.  Zero on failure.
+// Returns the blob pointer, or NULL on failure.
+void* GetPermissionInfo(const FilePath& path, size_t* length) {
+  DCHECK(length);
+  *length = 0;
+
+  struct stat stat_buf;
+  if (stat(path.value().c_str(), &stat_buf) != 0)
+    return NULL;
+
+  *length = sizeof(mode_t);
+  mode_t* mode = new mode_t;
+  *mode = stat_buf.st_mode & ~S_IFMT;  // Filter out file/path kind.
+
+  return mode;
+}
+
+// Restores the permission information for |path|, given the blob retrieved
+// using |GetPermissionInfo()|.
+// |info| is the pointer to the blob.
+// |length| is the length of the blob.
+// Either |info| or |length| may be NULL/0, in which case nothing happens.
+bool RestorePermissionInfo(const FilePath& path, void* info, size_t length) {
+  if (!info || (length == 0))
+    return false;
+
+  DCHECK_EQ(sizeof(mode_t), length);
+  mode_t* mode = reinterpret_cast<mode_t*>(info);
+
+  int rv = HANDLE_EINTR(chmod(path.value().c_str(), *mode));
+
+  delete mode;
+
+  return rv == 0;
+}
+
+}  // namespace
+
+bool DieFileDie(const FilePath& file, bool recurse) {
+  // There is no need to workaround Windows problems on POSIX.
+  // Just pass-through.
+  return DeleteFile(file, recurse);
+}
+
+#if !defined(OS_LINUX) && !defined(OS_MACOSX)
+bool EvictFileFromSystemCache(const FilePath& file) {
+  // There doesn't seem to be a POSIX way to cool the disk cache.
+  NOTIMPLEMENTED();
+  return false;
+}
+#endif
+
+bool MakeFileUnreadable(const FilePath& path) {
+  return DenyFilePermission(path, S_IRUSR | S_IRGRP | S_IROTH);
+}
+
+bool MakeFileUnwritable(const FilePath& path) {
+  return DenyFilePermission(path, S_IWUSR | S_IWGRP | S_IWOTH);
+}
+
+FilePermissionRestorer::FilePermissionRestorer(const FilePath& path)
+    : path_(path), info_(NULL), length_(0) {
+  info_ = GetPermissionInfo(path_, &length_);
+  DCHECK(info_ != NULL);
+  DCHECK_NE(0u, length_);
+}
+
+FilePermissionRestorer::~FilePermissionRestorer() {
+  if (!RestorePermissionInfo(path_, info_, length_))
+    NOTREACHED();
+}
+
+}  // namespace base
diff --git a/base/test/test_file_util_win.cc b/base/test/test_file_util_win.cc
new file mode 100644
index 0000000..fd22a63
--- /dev/null
+++ b/base/test/test_file_util_win.cc
@@ -0,0 +1,282 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_file_util.h"
+
+#include <windows.h>
+#include <aclapi.h>
+#include <shlwapi.h>
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/strings/string_split.h"
+#include "base/threading/platform_thread.h"
+#include "base/win/scoped_handle.h"
+
+namespace base {
+
+static const ptrdiff_t kOneMB = 1024 * 1024;
+
+namespace {
+
+struct PermissionInfo {
+  PSECURITY_DESCRIPTOR security_descriptor;
+  ACL dacl;
+};
+
+// Deny |permission| on the file |path|, for the current user.
+bool DenyFilePermission(const FilePath& path, DWORD permission) {
+  PACL old_dacl;
+  PSECURITY_DESCRIPTOR security_descriptor;
+  if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
+                           SE_FILE_OBJECT,
+                           DACL_SECURITY_INFORMATION, NULL, NULL, &old_dacl,
+                           NULL, &security_descriptor) != ERROR_SUCCESS) {
+    return false;
+  }
+
+  EXPLICIT_ACCESS change;
+  change.grfAccessPermissions = permission;
+  change.grfAccessMode = DENY_ACCESS;
+  change.grfInheritance = 0;
+  change.Trustee.pMultipleTrustee = NULL;
+  change.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+  change.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
+  change.Trustee.TrusteeType = TRUSTEE_IS_USER;
+  change.Trustee.ptstrName = const_cast<wchar_t*>(L"CURRENT_USER");
+
+  PACL new_dacl;
+  if (SetEntriesInAcl(1, &change, old_dacl, &new_dacl) != ERROR_SUCCESS) {
+    LocalFree(security_descriptor);
+    return false;
+  }
+
+  DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
+                                  SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
+                                  NULL, NULL, new_dacl, NULL);
+  LocalFree(security_descriptor);
+  LocalFree(new_dacl);
+
+  return rc == ERROR_SUCCESS;
+}
+
+// Gets a blob indicating the permission information for |path|.
+// |length| is the length of the blob.  Zero on failure.
+// Returns the blob pointer, or NULL on failure.
+void* GetPermissionInfo(const FilePath& path, size_t* length) {
+  DCHECK(length != NULL);
+  *length = 0;
+  PACL dacl = NULL;
+  PSECURITY_DESCRIPTOR security_descriptor;
+  if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
+                           SE_FILE_OBJECT,
+                           DACL_SECURITY_INFORMATION, NULL, NULL, &dacl,
+                           NULL, &security_descriptor) != ERROR_SUCCESS) {
+    return NULL;
+  }
+  DCHECK(dacl != NULL);
+
+  *length = sizeof(PSECURITY_DESCRIPTOR) + dacl->AclSize;
+  PermissionInfo* info = reinterpret_cast<PermissionInfo*>(new char[*length]);
+  info->security_descriptor = security_descriptor;
+  memcpy(&info->dacl, dacl, dacl->AclSize);
+
+  return info;
+}
+
+// Restores the permission information for |path|, given the blob retrieved
+// using |GetPermissionInfo()|.
+// |info| is the pointer to the blob.
+// |length| is the length of the blob.
+// Either |info| or |length| may be NULL/0, in which case nothing happens.
+bool RestorePermissionInfo(const FilePath& path, void* info, size_t length) {
+  if (!info || !length)
+    return false;
+
+  PermissionInfo* perm = reinterpret_cast<PermissionInfo*>(info);
+
+  DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
+                                  SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
+                                  NULL, NULL, &perm->dacl, NULL);
+  LocalFree(perm->security_descriptor);
+
+  char* char_array = reinterpret_cast<char*>(info);
+  delete [] char_array;
+
+  return rc == ERROR_SUCCESS;
+}
+
+}  // namespace
+
+bool DieFileDie(const FilePath& file, bool recurse) {
+  // It turns out that to not induce flakiness a long timeout is needed.
+  const int kIterations = 25;
+  const TimeDelta kTimeout = TimeDelta::FromSeconds(10) / kIterations;
+
+  if (!PathExists(file))
+    return true;
+
+  // Sometimes Delete fails, so try a few more times. Divide the timeout
+  // into short chunks, so that if a try succeeds, we won't delay the test
+  // for too long.
+  for (int i = 0; i < kIterations; ++i) {
+    if (DeleteFile(file, recurse))
+      return true;
+    PlatformThread::Sleep(kTimeout);
+  }
+  return false;
+}
+
+bool EvictFileFromSystemCache(const FilePath& file) {
+  // Request exclusive access to the file and overwrite it with no buffering.
+  base::win::ScopedHandle file_handle(
+      CreateFile(file.value().c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,
+                 OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL));
+  if (!file_handle.IsValid())
+    return false;
+
+  // Get some attributes to restore later.
+  BY_HANDLE_FILE_INFORMATION bhi = {0};
+  CHECK(::GetFileInformationByHandle(file_handle.Get(), &bhi));
+
+  // Execute in chunks. It could be optimized. We want to do few of these since
+  // these operations will be slow without the cache.
+
+  // Allocate a buffer for the reads and the writes.
+  char* buffer = reinterpret_cast<char*>(VirtualAlloc(NULL,
+                                                      kOneMB,
+                                                      MEM_COMMIT | MEM_RESERVE,
+                                                      PAGE_READWRITE));
+
+  // If the file size isn't a multiple of kOneMB, we'll need special
+  // processing.
+  bool file_is_aligned = true;
+  int total_bytes = 0;
+  DWORD bytes_read, bytes_written;
+  for (;;) {
+    bytes_read = 0;
+    ::ReadFile(file_handle.Get(), buffer, kOneMB, &bytes_read, NULL);
+    if (bytes_read == 0)
+      break;
+
+    if (bytes_read < kOneMB) {
+      // Zero out the remaining part of the buffer.
+      // WriteFile will fail if we provide a buffer size that isn't a
+      // sector multiple, so we'll have to write the entire buffer with
+      // padded zeros and then use SetEndOfFile to truncate the file.
+      ZeroMemory(buffer + bytes_read, kOneMB - bytes_read);
+      file_is_aligned = false;
+    }
+
+    // Move back to the position we just read from.
+    // Note that SetFilePointer will also fail if total_bytes isn't sector
+    // aligned, but that shouldn't happen here.
+    DCHECK((total_bytes % kOneMB) == 0);
+    SetFilePointer(file_handle.Get(), total_bytes, NULL, FILE_BEGIN);
+    if (!::WriteFile(file_handle.Get(), buffer, kOneMB, &bytes_written, NULL) ||
+        bytes_written != kOneMB) {
+      BOOL freed = VirtualFree(buffer, 0, MEM_RELEASE);
+      DCHECK(freed);
+      NOTREACHED();
+      return false;
+    }
+
+    total_bytes += bytes_read;
+
+    // If this is false, then we just processed the last portion of the file.
+    if (!file_is_aligned)
+      break;
+  }
+
+  BOOL freed = VirtualFree(buffer, 0, MEM_RELEASE);
+  DCHECK(freed);
+
+  if (!file_is_aligned) {
+    // The size of the file isn't a multiple of 1 MB, so we'll have
+    // to open the file again, this time without the FILE_FLAG_NO_BUFFERING
+    // flag and use SetEndOfFile to mark EOF.
+    file_handle.Set(NULL);
+    file_handle.Set(CreateFile(file.value().c_str(), GENERIC_WRITE, 0, NULL,
+                               OPEN_EXISTING, 0, NULL));
+    CHECK_NE(SetFilePointer(file_handle.Get(), total_bytes, NULL, FILE_BEGIN),
+             INVALID_SET_FILE_POINTER);
+    CHECK(::SetEndOfFile(file_handle.Get()));
+  }
+
+  // Restore the file attributes.
+  CHECK(::SetFileTime(file_handle.Get(), &bhi.ftCreationTime,
+                      &bhi.ftLastAccessTime, &bhi.ftLastWriteTime));
+
+  return true;
+}
+
+// Checks if the volume supports Alternate Data Streams. This is required for
+// the Zone Identifier implementation.
+bool VolumeSupportsADS(const FilePath& path) {
+  wchar_t drive[MAX_PATH] = {0};
+  wcscpy_s(drive, MAX_PATH, path.value().c_str());
+
+  if (!PathStripToRootW(drive))
+    return false;
+
+  DWORD fs_flags = 0;
+  if (!GetVolumeInformationW(drive, NULL, 0, 0, NULL, &fs_flags, NULL, 0))
+    return false;
+
+  if (fs_flags & FILE_NAMED_STREAMS)
+    return true;
+
+  return false;
+}
+
+// Return whether the ZoneIdentifier is correctly set to "Internet" (3)
+// Only returns a valid result when called from same process as the
+// one that (was supposed to have) set the zone identifier.
+bool HasInternetZoneIdentifier(const FilePath& full_path) {
+  FilePath zone_path(full_path.value() + L":Zone.Identifier");
+  std::string zone_path_contents;
+  if (!ReadFileToString(zone_path, &zone_path_contents))
+    return false;
+
+  std::vector<std::string> lines;
+  // This call also trims whitespaces, including carriage-returns (\r).
+  SplitString(zone_path_contents, '\n', &lines);
+
+  switch (lines.size()) {
+    case 3:
+      // optional empty line at end of file:
+      if (lines[2] != "")
+        return false;
+      // fall through:
+    case 2:
+      return lines[0] == "[ZoneTransfer]" && lines[1] == "ZoneId=3";
+    default:
+      return false;
+  }
+}
+
+bool MakeFileUnreadable(const FilePath& path) {
+  return DenyFilePermission(path, GENERIC_READ);
+}
+
+bool MakeFileUnwritable(const FilePath& path) {
+  return DenyFilePermission(path, GENERIC_WRITE);
+}
+
+FilePermissionRestorer::FilePermissionRestorer(const FilePath& path)
+    : path_(path), info_(NULL), length_(0) {
+  info_ = GetPermissionInfo(path_, &length_);
+  DCHECK(info_ != NULL);
+  DCHECK_NE(0u, length_);
+}
+
+FilePermissionRestorer::~FilePermissionRestorer() {
+  if (!RestorePermissionInfo(path_, info_, length_))
+    NOTREACHED();
+}
+
+}  // namespace base
diff --git a/base/test/test_io_thread.cc b/base/test/test_io_thread.cc
new file mode 100644
index 0000000..48c1e16
--- /dev/null
+++ b/base/test/test_io_thread.cc
@@ -0,0 +1,65 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_io_thread.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace {
+
+void PostTaskAndWaitHelper(base::WaitableEvent* event,
+                           const base::Closure& task) {
+  task.Run();
+  event->Signal();
+}
+
+}  // namespace
+
+namespace base {
+
+TestIOThread::TestIOThread(Mode mode)
+    : io_thread_("test_io_thread"), io_thread_started_(false) {
+  switch (mode) {
+    case kAutoStart:
+      Start();
+      return;
+    case kManualStart:
+      return;
+  }
+  CHECK(false) << "Invalid mode";
+}
+
+TestIOThread::~TestIOThread() {
+  Stop();
+}
+
+void TestIOThread::Start() {
+  CHECK(!io_thread_started_);
+  io_thread_started_ = true;
+  CHECK(io_thread_.StartWithOptions(
+      base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
+}
+
+void TestIOThread::Stop() {
+  // Note: It's okay to call |Stop()| even if the thread isn't running.
+  io_thread_.Stop();
+  io_thread_started_ = false;
+}
+
+void TestIOThread::PostTask(const tracked_objects::Location& from_here,
+                            const base::Closure& task) {
+  task_runner()->PostTask(from_here, task);
+}
+
+void TestIOThread::PostTaskAndWait(const tracked_objects::Location& from_here,
+                                   const base::Closure& task) {
+  base::WaitableEvent event(false, false);
+  task_runner()->PostTask(from_here,
+                          base::Bind(&PostTaskAndWaitHelper, &event, task));
+  event.Wait();
+}
+
+}  // namespace base
diff --git a/base/test/test_io_thread.h b/base/test/test_io_thread.h
new file mode 100644
index 0000000..c2ed187
--- /dev/null
+++ b/base/test/test_io_thread.h
@@ -0,0 +1,59 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_IO_THREAD_H_
+#define BASE_TEST_TEST_IO_THREAD_H_
+
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/task_runner.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+
+namespace base {
+
+// Create and run an IO thread with a MessageLoop, and
+// making the MessageLoop accessible from its client.
+// It also provides some ideomatic API like PostTaskAndWait().
+class TestIOThread {
+ public:
+  enum Mode { kAutoStart, kManualStart };
+  explicit TestIOThread(Mode mode);
+  // Stops the I/O thread if necessary.
+  ~TestIOThread();
+
+  // |Start()|/|Stop()| should only be called from the main (creation) thread.
+  // After |Stop()|, |Start()| may be called again to start a new I/O thread.
+  // |Stop()| may be called even when the I/O thread is not started.
+  void Start();
+  void Stop();
+
+  // Post |task| to the IO thread.
+  void PostTask(const tracked_objects::Location& from_here,
+                const base::Closure& task);
+  // Posts |task| to the IO-thread with an WaitableEvent associated blocks on
+  // it until the posted |task| is executed, then returns.
+  void PostTaskAndWait(const tracked_objects::Location& from_here,
+                       const base::Closure& task);
+
+  base::MessageLoopForIO* message_loop() {
+    return static_cast<base::MessageLoopForIO*>(io_thread_.message_loop());
+  }
+
+  scoped_refptr<SingleThreadTaskRunner> task_runner() {
+    return message_loop()->task_runner();
+  }
+
+ private:
+  base::Thread io_thread_;
+  bool io_thread_started_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestIOThread);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_TEST_IO_THREAD_H_
diff --git a/base/test/test_listener_ios.h b/base/test/test_listener_ios.h
new file mode 100644
index 0000000..c312250
--- /dev/null
+++ b/base/test/test_listener_ios.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_LISTENER_IOS_H_
+#define BASE_TEST_TEST_LISTENER_IOS_H_
+
+namespace base {
+namespace test_listener_ios {
+
+// Register an IOSRunLoopListener.
+void RegisterTestEndListener();
+
+}  // namespace test_listener_ios
+}  // namespace base
+
+#endif  // BASE_TEST_TEST_LISTENER_IOS_H_
diff --git a/base/test/test_listener_ios.mm b/base/test/test_listener_ios.mm
new file mode 100644
index 0000000..12cf5bb
--- /dev/null
+++ b/base/test/test_listener_ios.mm
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_listener_ios.h"
+
+#import <Foundation/Foundation.h>
+
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// The iOS watchdog timer will kill an app that doesn't spin the main event
+// loop often enough. This uses a Gtest TestEventListener to spin the current
+// loop after each test finishes. However, if any individual test takes too
+// long, it is still possible that the app will get killed.
+
+namespace {
+
+class IOSRunLoopListener : public testing::EmptyTestEventListener {
+ public:
+  virtual void OnTestEnd(const testing::TestInfo& test_info);
+};
+
+void IOSRunLoopListener::OnTestEnd(const testing::TestInfo& test_info) {
+  base::mac::ScopedNSAutoreleasePool scoped_pool;
+
+  // At the end of the test, spin the default loop for a moment.
+  NSDate* stop_date = [NSDate dateWithTimeIntervalSinceNow:0.001];
+  [[NSRunLoop currentRunLoop] runUntilDate:stop_date];
+}
+
+}  // namespace
+
+
+namespace base {
+namespace test_listener_ios {
+
+void RegisterTestEndListener() {
+  testing::TestEventListeners& listeners =
+      testing::UnitTest::GetInstance()->listeners();
+  listeners.Append(new IOSRunLoopListener);
+}
+
+}  // namespace test_listener_ios
+}  // namespace base
diff --git a/base/test/test_pending_task.cc b/base/test/test_pending_task.cc
new file mode 100644
index 0000000..94989de
--- /dev/null
+++ b/base/test/test_pending_task.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "base/test/test_pending_task.h"
+
+namespace base {
+
+TestPendingTask::TestPendingTask() : nestability(NESTABLE) {}
+
+TestPendingTask::TestPendingTask(
+    const tracked_objects::Location& location,
+    const Closure& task,
+    TimeTicks post_time,
+    TimeDelta delay,
+    TestNestability nestability)
+    : location(location),
+      task(task),
+      post_time(post_time),
+      delay(delay),
+      nestability(nestability) {}
+
+TimeTicks TestPendingTask::GetTimeToRun() const {
+  return post_time + delay;
+}
+
+bool TestPendingTask::ShouldRunBefore(const TestPendingTask& other) const {
+  if (nestability != other.nestability)
+    return (nestability == NESTABLE);
+  return GetTimeToRun() < other.GetTimeToRun();
+}
+
+TestPendingTask::~TestPendingTask() {}
+
+void TestPendingTask::AsValueInto(base::debug::TracedValue* state) const {
+  state->SetInteger("run_at", GetTimeToRun().ToInternalValue());
+  state->SetString("posting_function", location.ToString());
+  state->SetInteger("post_time", post_time.ToInternalValue());
+  state->SetInteger("delay", delay.ToInternalValue());
+  switch (nestability) {
+    case NESTABLE:
+      state->SetString("nestability", "NESTABLE");
+      break;
+    case NON_NESTABLE:
+      state->SetString("nestability", "NON_NESTABLE");
+      break;
+  }
+  state->SetInteger("delay", delay.ToInternalValue());
+}
+
+scoped_refptr<base::debug::ConvertableToTraceFormat> TestPendingTask::AsValue()
+    const {
+  scoped_refptr<base::debug::TracedValue> state =
+      new base::debug::TracedValue();
+  AsValueInto(state.get());
+  return state;
+}
+
+std::string TestPendingTask::ToString() const {
+  std::string output("TestPendingTask(");
+  AsValue()->AppendAsTraceFormat(&output);
+  output += ")";
+  return output;
+}
+
+std::ostream& operator<<(std::ostream& os, const TestPendingTask& task) {
+  PrintTo(task, &os);
+  return os;
+}
+
+void PrintTo(const TestPendingTask& task, std::ostream* os) {
+  *os << task.ToString();
+}
+
+}  // namespace base
diff --git a/base/test/test_pending_task.h b/base/test/test_pending_task.h
new file mode 100644
index 0000000..fa06800
--- /dev/null
+++ b/base/test/test_pending_task.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_PENDING_TASK_H_
+#define BASE_TEST_TEST_PENDING_TASK_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/debug/trace_event_argument.h"
+#include "base/location.h"
+#include "base/time/time.h"
+
+namespace base {
+
+// TestPendingTask is a helper class for test TaskRunner
+// implementations.  See test_simple_task_runner.h for example usage.
+
+struct TestPendingTask {
+  enum TestNestability { NESTABLE, NON_NESTABLE };
+
+  TestPendingTask();
+  TestPendingTask(const tracked_objects::Location& location,
+                  const Closure& task,
+                  TimeTicks post_time,
+                  TimeDelta delay,
+                  TestNestability nestability);
+  ~TestPendingTask();
+
+  // Returns post_time + delay.
+  TimeTicks GetTimeToRun() const;
+
+  // Returns true if this task is nestable and |other| isn't, or if
+  // this task's time to run is strictly earlier than |other|'s time
+  // to run.
+  //
+  // Note that two tasks may both have the same nestability and delay.
+  // In that case, the caller must use some other criterion (probably
+  // the position in some queue) to break the tie.  Conveniently, the
+  // following STL functions already do so:
+  //
+  //   - std::min_element
+  //   - std::stable_sort
+  //
+  // but the following STL functions don't:
+  //
+  //   - std::max_element
+  //   - std::sort.
+  bool ShouldRunBefore(const TestPendingTask& other) const;
+
+  tracked_objects::Location location;
+  Closure task;
+  TimeTicks post_time;
+  TimeDelta delay;
+  TestNestability nestability;
+
+  // Functions for using test pending task with tracing, useful in unit
+  // testing.
+  void AsValueInto(base::debug::TracedValue* state) const;
+  scoped_refptr<base::debug::ConvertableToTraceFormat> AsValue() const;
+  std::string ToString() const;
+};
+
+// gtest helpers which allow pretty printing of the tasks, very useful in unit
+// testing.
+std::ostream& operator<<(std::ostream& os, const TestPendingTask& task);
+void PrintTo(const TestPendingTask& task, std::ostream* os);
+
+}  // namespace base
+
+#endif  // BASE_TEST_TEST_PENDING_TASK_H_
diff --git a/base/test/test_pending_task_unittest.cc b/base/test/test_pending_task_unittest.cc
new file mode 100644
index 0000000..4a6bd24
--- /dev/null
+++ b/base/test/test_pending_task_unittest.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_pending_task.h"
+
+#include "base/bind.h"
+#include "base/debug/trace_event.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest-spi.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+TEST(TestPendingTaskTest, TraceSupport) {
+  base::TestPendingTask task;
+
+  // Check that TestPendingTask can be sent to the trace subsystem.
+  TRACE_EVENT1("test", "TestPendingTask::TraceSupport", "task", task.AsValue());
+
+  // Just a basic check that the trace output has *something* in it.
+  EXPECT_THAT(task.AsValue()->ToString(), ::testing::HasSubstr("post_time"));
+}
+
+TEST(TestPendingTaskTest, ToString) {
+  base::TestPendingTask task;
+
+  // Just a basic check that ToString has *something* in it.
+  EXPECT_THAT(task.ToString(), ::testing::StartsWith("TestPendingTask("));
+}
+
+TEST(TestPendingTaskTest, GTestPrettyPrint) {
+  base::TestPendingTask task;
+
+  // Check that gtest is calling the TestPendingTask's PrintTo method.
+  EXPECT_THAT(::testing::PrintToString(task),
+              ::testing::StartsWith("TestPendingTask("));
+
+  // Check that pretty printing works with the gtest iostreams operator.
+  EXPECT_NONFATAL_FAILURE(EXPECT_TRUE(false) << task, "TestPendingTask(");
+}
+
+TEST(TestPendingTaskTest, ShouldRunBefore) {
+  base::TestPendingTask task_first;
+  task_first.delay = base::TimeDelta::FromMilliseconds(1);
+  base::TestPendingTask task_after;
+  task_after.delay = base::TimeDelta::FromMilliseconds(2);
+
+  EXPECT_FALSE(task_after.ShouldRunBefore(task_first))
+      << task_after << ".ShouldRunBefore(" << task_first << ")\n";
+  EXPECT_TRUE(task_first.ShouldRunBefore(task_after))
+      << task_first << ".ShouldRunBefore(" << task_after << ")\n";
+}
+
+}  // namespace
diff --git a/base/test/test_reg_util_win.cc b/base/test/test_reg_util_win.cc
new file mode 100644
index 0000000..2cbafef
--- /dev/null
+++ b/base/test/test_reg_util_win.cc
@@ -0,0 +1,105 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_reg_util_win.h"
+
+#include "base/guid.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace registry_util {
+
+namespace {
+
+const wchar_t kTimestampDelimiter[] = L"$";
+const wchar_t kTempTestKeyPath[] = L"Software\\Chromium\\TempTestKeys";
+
+void DeleteStaleTestKeys(const base::Time& now,
+                         const base::string16& test_key_root) {
+  base::win::RegKey test_root_key;
+  if (test_root_key.Open(HKEY_CURRENT_USER,
+                         test_key_root.c_str(),
+                         KEY_ALL_ACCESS) != ERROR_SUCCESS) {
+    // This will occur on first-run, but is harmless.
+    return;
+  }
+
+  base::win::RegistryKeyIterator iterator_test_root_key(HKEY_CURRENT_USER,
+                                                        test_key_root.c_str());
+  for (; iterator_test_root_key.Valid(); ++iterator_test_root_key) {
+    base::string16 key_name = iterator_test_root_key.Name();
+    std::vector<base::string16> tokens;
+    if (!Tokenize(key_name, base::string16(kTimestampDelimiter), &tokens))
+      continue;
+    int64 key_name_as_number = 0;
+
+    if (!base::StringToInt64(tokens[0], &key_name_as_number)) {
+      test_root_key.DeleteKey(key_name.c_str());
+      continue;
+    }
+
+    base::Time key_time = base::Time::FromInternalValue(key_name_as_number);
+    base::TimeDelta age = now - key_time;
+
+    if (age > base::TimeDelta::FromHours(24))
+      test_root_key.DeleteKey(key_name.c_str());
+  }
+}
+
+base::string16 GenerateTempKeyPath(const base::string16& test_key_root,
+                                   const base::Time& timestamp) {
+  base::string16 key_path = test_key_root;
+  key_path += L"\\" + base::Int64ToString16(timestamp.ToInternalValue());
+  key_path += kTimestampDelimiter + base::ASCIIToWide(base::GenerateGUID());
+
+  return key_path;
+}
+
+}  // namespace
+
+RegistryOverrideManager::ScopedRegistryKeyOverride::ScopedRegistryKeyOverride(
+    HKEY override,
+    const base::string16& key_path)
+    : override_(override) {
+  EXPECT_EQ(
+      ERROR_SUCCESS,
+      temp_key_.Create(HKEY_CURRENT_USER, key_path.c_str(), KEY_ALL_ACCESS));
+  EXPECT_EQ(ERROR_SUCCESS,
+            ::RegOverridePredefKey(override_, temp_key_.Handle()));
+}
+
+RegistryOverrideManager::
+    ScopedRegistryKeyOverride::~ScopedRegistryKeyOverride() {
+  ::RegOverridePredefKey(override_, NULL);
+  temp_key_.DeleteKey(L"");
+}
+
+RegistryOverrideManager::RegistryOverrideManager()
+    : timestamp_(base::Time::Now()), test_key_root_(kTempTestKeyPath) {
+  DeleteStaleTestKeys(timestamp_, test_key_root_);
+}
+
+RegistryOverrideManager::RegistryOverrideManager(
+    const base::Time& timestamp,
+    const base::string16& test_key_root)
+    : timestamp_(timestamp), test_key_root_(test_key_root) {
+  DeleteStaleTestKeys(timestamp_, test_key_root_);
+}
+
+RegistryOverrideManager::~RegistryOverrideManager() {}
+
+void RegistryOverrideManager::OverrideRegistry(HKEY override) {
+  base::string16 key_path = GenerateTempKeyPath(test_key_root_, timestamp_);
+  overrides_.push_back(new ScopedRegistryKeyOverride(override, key_path));
+}
+
+base::string16 GenerateTempKeyPath() {
+  return GenerateTempKeyPath(base::string16(kTempTestKeyPath),
+                             base::Time::Now());
+}
+
+}  // namespace registry_util
diff --git a/base/test/test_reg_util_win.h b/base/test/test_reg_util_win.h
new file mode 100644
index 0000000..db71838
--- /dev/null
+++ b/base/test/test_reg_util_win.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_REG_UTIL_H_
+#define BASE_TEST_TEST_REG_UTIL_H_
+
+// Registry utility functions used only by tests.
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
+#include "base/win/registry.h"
+
+namespace registry_util {
+
+// Allows a test to easily override registry hives so that it can start from a
+// known good state, or make sure to not leave any side effects once the test
+// completes. This supports parallel tests. All the overrides are scoped to the
+// lifetime of the override manager. Destroy the manager to undo the overrides.
+//
+// Overridden hives use keys stored at, for instance:
+//   HKCU\Software\Chromium\TempTestKeys\
+//       13028145911617809$02AB211C-CF73-478D-8D91-618E11998AED
+// The key path are comprises of:
+//   - The test key root, HKCU\Software\Chromium\TempTestKeys\
+//   - The base::Time::ToInternalValue of the creation time. This is used to
+//     delete stale keys left over from crashed tests.
+//   - A GUID used for preventing name collisions (although unlikely) between
+//     two RegistryOverrideManagers created with the same timestamp.
+class RegistryOverrideManager {
+ public:
+  RegistryOverrideManager();
+  ~RegistryOverrideManager();
+
+  // Override the given registry hive using a randomly generated temporary key.
+  // Multiple overrides to the same hive are not supported and lead to undefined
+  // behavior.
+  void OverrideRegistry(HKEY override);
+
+ private:
+  friend class RegistryOverrideManagerTest;
+
+  // Keeps track of one override.
+  class ScopedRegistryKeyOverride {
+   public:
+    ScopedRegistryKeyOverride(HKEY override, const base::string16& key_path);
+    ~ScopedRegistryKeyOverride();
+
+   private:
+    HKEY override_;
+    base::win::RegKey temp_key_;
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedRegistryKeyOverride);
+  };
+
+  // Used for testing only.
+  RegistryOverrideManager(const base::Time& timestamp,
+                          const base::string16& test_key_root);
+
+  base::Time timestamp_;
+  base::string16 guid_;
+
+  base::string16 test_key_root_;
+  ScopedVector<ScopedRegistryKeyOverride> overrides_;
+
+  DISALLOW_COPY_AND_ASSIGN(RegistryOverrideManager);
+};
+
+// Generates a temporary key path that will be eventually deleted
+// automatically if the process crashes.
+base::string16 GenerateTempKeyPath();
+
+}  // namespace registry_util
+
+#endif  // BASE_TEST_TEST_REG_UTIL_H_
diff --git a/base/test/test_reg_util_win_unittest.cc b/base/test/test_reg_util_win_unittest.cc
new file mode 100644
index 0000000..11abe5d
--- /dev/null
+++ b/base/test/test_reg_util_win_unittest.cc
@@ -0,0 +1,130 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/test_reg_util_win.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace registry_util {
+
+namespace {
+const wchar_t kTestKeyPath[] = L"Software\\Chromium\\Foo\\Baz\\TestKey";
+const wchar_t kTestValueName[] = L"TestValue";
+}  // namespace
+
+class RegistryOverrideManagerTest : public testing::Test {
+ protected:
+  RegistryOverrideManagerTest() {
+    // We assign a fake test key path to our test RegistryOverrideManager
+    // so we don't interfere with any actual RegistryOverrideManagers running
+    // on the system. This fake path will be auto-deleted by other
+    // RegistryOverrideManagers in case we crash.
+    fake_test_key_root_ = registry_util::GenerateTempKeyPath();
+
+    // Ensure a clean test environment.
+    base::win::RegKey key(HKEY_CURRENT_USER);
+    key.DeleteKey(fake_test_key_root_.c_str());
+    key.DeleteKey(kTestKeyPath);
+  }
+
+  virtual ~RegistryOverrideManagerTest() {
+    base::win::RegKey key(HKEY_CURRENT_USER);
+    key.DeleteKey(fake_test_key_root_.c_str());
+  }
+
+  void AssertKeyExists(const base::string16& key_path) {
+    base::win::RegKey key;
+    ASSERT_EQ(ERROR_SUCCESS,
+              key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_READ))
+        << key_path << " does not exist.";
+  }
+
+  void AssertKeyAbsent(const base::string16& key_path) {
+    base::win::RegKey key;
+    ASSERT_NE(ERROR_SUCCESS,
+              key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_READ))
+        << key_path << " exists but it should not.";
+  }
+
+  void CreateKey(const base::string16& key_path) {
+    base::win::RegKey key;
+    EXPECT_EQ(ERROR_SUCCESS,
+              key.Create(HKEY_CURRENT_USER, key_path.c_str(), KEY_ALL_ACCESS));
+  }
+
+  base::string16 FakeOverrideManagerPath(const base::Time& time) {
+    return fake_test_key_root_ + L"\\" +
+           base::Int64ToString16(time.ToInternalValue());
+  }
+
+  void CreateManager(const base::Time& timestamp) {
+    manager_.reset(new RegistryOverrideManager(timestamp, fake_test_key_root_));
+    manager_->OverrideRegistry(HKEY_CURRENT_USER);
+  }
+
+  base::string16 fake_test_key_root_;
+  scoped_ptr<RegistryOverrideManager> manager_;
+};
+
+TEST_F(RegistryOverrideManagerTest, Basic) {
+  CreateManager(base::Time::Now());
+
+  base::win::RegKey create_key;
+  EXPECT_EQ(ERROR_SUCCESS,
+            create_key.Create(HKEY_CURRENT_USER, kTestKeyPath, KEY_ALL_ACCESS));
+  EXPECT_TRUE(create_key.Valid());
+  EXPECT_EQ(ERROR_SUCCESS, create_key.WriteValue(kTestValueName, 42));
+  create_key.Close();
+
+  AssertKeyExists(kTestKeyPath);
+
+  DWORD value;
+  base::win::RegKey read_key;
+  EXPECT_EQ(ERROR_SUCCESS,
+            read_key.Open(HKEY_CURRENT_USER, kTestKeyPath, KEY_READ));
+  EXPECT_TRUE(read_key.Valid());
+  EXPECT_EQ(ERROR_SUCCESS, read_key.ReadValueDW(kTestValueName, &value));
+  EXPECT_EQ(42, value);
+  read_key.Close();
+
+  manager_.reset();
+
+  AssertKeyAbsent(kTestKeyPath);
+}
+
+TEST_F(RegistryOverrideManagerTest, DeleteStaleKeys) {
+  base::Time::Exploded kTestTimeExploded = {2013, 11, 1, 4, 0, 0, 0, 0};
+  base::Time kTestTime = base::Time::FromUTCExploded(kTestTimeExploded);
+
+  base::string16 path_garbage = fake_test_key_root_ + L"\\Blah";
+  base::string16 path_very_stale =
+      FakeOverrideManagerPath(kTestTime - base::TimeDelta::FromDays(100));
+  base::string16 path_stale =
+      FakeOverrideManagerPath(kTestTime - base::TimeDelta::FromDays(5));
+  base::string16 path_current =
+      FakeOverrideManagerPath(kTestTime - base::TimeDelta::FromMinutes(1));
+  base::string16 path_future =
+      FakeOverrideManagerPath(kTestTime + base::TimeDelta::FromMinutes(1));
+
+  CreateKey(path_garbage);
+  CreateKey(path_very_stale);
+  CreateKey(path_stale);
+  CreateKey(path_current);
+  CreateKey(path_future);
+
+  CreateManager(kTestTime);
+  manager_.reset();
+
+  AssertKeyAbsent(path_garbage);
+  AssertKeyAbsent(path_very_stale);
+  AssertKeyAbsent(path_stale);
+  AssertKeyExists(path_current);
+  AssertKeyExists(path_future);
+}
+
+}  // namespace registry_util
diff --git a/base/test/test_shortcut_win.cc b/base/test/test_shortcut_win.cc
new file mode 100644
index 0000000..c772d87
--- /dev/null
+++ b/base/test/test_shortcut_win.cc
@@ -0,0 +1,155 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_shortcut_win.h"
+
+#include <windows.h>
+#include <shlobj.h>
+#include <propkey.h>
+
+#include "base/files/file_path.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/scoped_comptr.h"
+#include "base/win/scoped_propvariant.h"
+#include "base/win/windows_version.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace win {
+
+void ValidatePathsAreEqual(const base::FilePath& expected_path,
+                           const base::FilePath& actual_path) {
+  wchar_t long_expected_path_chars[MAX_PATH] = {0};
+  wchar_t long_actual_path_chars[MAX_PATH] = {0};
+
+  // If |expected_path| is empty confirm immediately that |actual_path| is also
+  // empty.
+  if (expected_path.empty()) {
+    EXPECT_TRUE(actual_path.empty());
+    return;
+  }
+
+  // Proceed with LongPathName matching which will also confirm the paths exist.
+  EXPECT_NE(0U, ::GetLongPathName(
+      expected_path.value().c_str(), long_expected_path_chars, MAX_PATH))
+          << "Failed to get LongPathName of " << expected_path.value();
+  EXPECT_NE(0U, ::GetLongPathName(
+      actual_path.value().c_str(), long_actual_path_chars, MAX_PATH))
+          << "Failed to get LongPathName of " << actual_path.value();
+
+  base::FilePath long_expected_path(long_expected_path_chars);
+  base::FilePath long_actual_path(long_actual_path_chars);
+  EXPECT_FALSE(long_expected_path.empty());
+  EXPECT_FALSE(long_actual_path.empty());
+
+  EXPECT_EQ(long_expected_path, long_actual_path);
+}
+
+void ValidateShortcut(const base::FilePath& shortcut_path,
+                      const ShortcutProperties& properties) {
+  ScopedComPtr<IShellLink> i_shell_link;
+  ScopedComPtr<IPersistFile> i_persist_file;
+
+  wchar_t read_target[MAX_PATH] = {0};
+  wchar_t read_working_dir[MAX_PATH] = {0};
+  wchar_t read_arguments[MAX_PATH] = {0};
+  wchar_t read_description[MAX_PATH] = {0};
+  wchar_t read_icon[MAX_PATH] = {0};
+  int read_icon_index = 0;
+
+  HRESULT hr;
+
+  // Initialize the shell interfaces.
+  EXPECT_TRUE(SUCCEEDED(hr = i_shell_link.CreateInstance(
+      CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER)));
+  if (FAILED(hr))
+    return;
+
+  EXPECT_TRUE(SUCCEEDED(hr = i_persist_file.QueryFrom(i_shell_link)));
+  if (FAILED(hr))
+    return;
+
+  // Load the shortcut.
+  EXPECT_TRUE(SUCCEEDED(hr = i_persist_file->Load(
+      shortcut_path.value().c_str(), 0))) << "Failed to load shortcut at "
+                                          << shortcut_path.value();
+  if (FAILED(hr))
+    return;
+
+  if (properties.options & ShortcutProperties::PROPERTIES_TARGET) {
+    EXPECT_TRUE(SUCCEEDED(
+        i_shell_link->GetPath(read_target, MAX_PATH, NULL, SLGP_SHORTPATH)));
+    ValidatePathsAreEqual(properties.target, base::FilePath(read_target));
+  }
+
+  if (properties.options & ShortcutProperties::PROPERTIES_WORKING_DIR) {
+    EXPECT_TRUE(SUCCEEDED(
+        i_shell_link->GetWorkingDirectory(read_working_dir, MAX_PATH)));
+    ValidatePathsAreEqual(properties.working_dir,
+                          base::FilePath(read_working_dir));
+  }
+
+  if (properties.options & ShortcutProperties::PROPERTIES_ARGUMENTS) {
+    EXPECT_TRUE(SUCCEEDED(
+        i_shell_link->GetArguments(read_arguments, MAX_PATH)));
+    EXPECT_EQ(properties.arguments, read_arguments);
+  }
+
+  if (properties.options & ShortcutProperties::PROPERTIES_DESCRIPTION) {
+    EXPECT_TRUE(SUCCEEDED(
+        i_shell_link->GetDescription(read_description, MAX_PATH)));
+    EXPECT_EQ(properties.description, read_description);
+  }
+
+  if (properties.options & ShortcutProperties::PROPERTIES_ICON) {
+    EXPECT_TRUE(SUCCEEDED(
+        i_shell_link->GetIconLocation(read_icon, MAX_PATH, &read_icon_index)));
+    ValidatePathsAreEqual(properties.icon, base::FilePath(read_icon));
+    EXPECT_EQ(properties.icon_index, read_icon_index);
+  }
+
+  if (GetVersion() >= VERSION_WIN7) {
+    ScopedComPtr<IPropertyStore> property_store;
+    EXPECT_TRUE(SUCCEEDED(hr = property_store.QueryFrom(i_shell_link)));
+    if (FAILED(hr))
+      return;
+
+    if (properties.options & ShortcutProperties::PROPERTIES_APP_ID) {
+      ScopedPropVariant pv_app_id;
+      EXPECT_EQ(S_OK, property_store->GetValue(PKEY_AppUserModel_ID,
+                                               pv_app_id.Receive()));
+      switch (pv_app_id.get().vt) {
+        case VT_EMPTY:
+          EXPECT_TRUE(properties.app_id.empty());
+          break;
+        case VT_LPWSTR:
+          EXPECT_EQ(properties.app_id, pv_app_id.get().pwszVal);
+          break;
+        default:
+          ADD_FAILURE() << "Unexpected variant type: " << pv_app_id.get().vt;
+      }
+    }
+
+    if (properties.options & ShortcutProperties::PROPERTIES_DUAL_MODE) {
+      ScopedPropVariant pv_dual_mode;
+      EXPECT_EQ(S_OK, property_store->GetValue(PKEY_AppUserModel_IsDualMode,
+                                               pv_dual_mode.Receive()));
+      switch (pv_dual_mode.get().vt) {
+        case VT_EMPTY:
+          EXPECT_FALSE(properties.dual_mode);
+          break;
+        case VT_BOOL:
+          EXPECT_EQ(properties.dual_mode,
+                    static_cast<bool>(pv_dual_mode.get().boolVal));
+          break;
+        default:
+          ADD_FAILURE() << "Unexpected variant type: " << pv_dual_mode.get().vt;
+      }
+    }
+  }
+}
+
+}  // namespace win
+}  // namespace base
diff --git a/base/test/test_shortcut_win.h b/base/test/test_shortcut_win.h
new file mode 100644
index 0000000..b828e8b
--- /dev/null
+++ b/base/test/test_shortcut_win.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_SHORTCUT_WIN_H_
+#define BASE_TEST_TEST_SHORTCUT_WIN_H_
+
+#include "base/files/file_path.h"
+#include "base/win/shortcut.h"
+
+// Windows shortcut functions used only by tests.
+
+namespace base {
+namespace win {
+
+// Validates |actual_path|'s LongPathName case-insensitively matches
+// |expected_path|'s LongPathName.
+void ValidatePathsAreEqual(const base::FilePath& expected_path,
+                           const base::FilePath& actual_path);
+
+// Validates that a shortcut exists at |shortcut_path| with the expected
+// |properties|.
+// Logs gtest failures on failed verifications.
+void ValidateShortcut(const FilePath& shortcut_path,
+                      const ShortcutProperties& properties);
+
+}  // namespace win
+}  // namespace base
+
+#endif  // BASE_TEST_TEST_SHORTCUT_WIN_H_
diff --git a/base/test/test_simple_task_runner.cc b/base/test/test_simple_task_runner.cc
new file mode 100644
index 0000000..cc39fab
--- /dev/null
+++ b/base/test/test_simple_task_runner.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_simple_task_runner.h"
+
+#include "base/logging.h"
+
+namespace base {
+
+TestSimpleTaskRunner::TestSimpleTaskRunner() {}
+
+TestSimpleTaskRunner::~TestSimpleTaskRunner() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+bool TestSimpleTaskRunner::PostDelayedTask(
+    const tracked_objects::Location& from_here,
+    const Closure& task,
+    TimeDelta delay) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  pending_tasks_.push_back(
+      TestPendingTask(from_here, task, TimeTicks(), delay,
+                      TestPendingTask::NESTABLE));
+  return true;
+}
+
+bool TestSimpleTaskRunner::PostNonNestableDelayedTask(
+    const tracked_objects::Location& from_here,
+    const Closure& task,
+    TimeDelta delay) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  pending_tasks_.push_back(
+      TestPendingTask(from_here, task, TimeTicks(), delay,
+                      TestPendingTask::NON_NESTABLE));
+  return true;
+}
+
+bool TestSimpleTaskRunner::RunsTasksOnCurrentThread() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return true;
+}
+
+const std::deque<TestPendingTask>&
+TestSimpleTaskRunner::GetPendingTasks() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return pending_tasks_;
+}
+
+bool TestSimpleTaskRunner::HasPendingTask() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return !pending_tasks_.empty();
+}
+
+base::TimeDelta TestSimpleTaskRunner::NextPendingTaskDelay() const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  return pending_tasks_.front().GetTimeToRun() - base::TimeTicks();
+}
+
+void TestSimpleTaskRunner::ClearPendingTasks() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  pending_tasks_.clear();
+}
+
+void TestSimpleTaskRunner::RunPendingTasks() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // Swap with a local variable to avoid re-entrancy problems.
+  std::deque<TestPendingTask> tasks_to_run;
+  tasks_to_run.swap(pending_tasks_);
+  for (std::deque<TestPendingTask>::iterator it = tasks_to_run.begin();
+       it != tasks_to_run.end(); ++it) {
+    it->task.Run();
+  }
+}
+
+void TestSimpleTaskRunner::RunUntilIdle() {
+  while (!pending_tasks_.empty()) {
+    RunPendingTasks();
+  }
+}
+
+}  // namespace base
diff --git a/base/test/test_simple_task_runner.h b/base/test/test_simple_task_runner.h
new file mode 100644
index 0000000..af4f4eb
--- /dev/null
+++ b/base/test/test_simple_task_runner.h
@@ -0,0 +1,86 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_SIMPLE_TASK_RUNNER_H_
+#define BASE_TEST_TEST_SIMPLE_TASK_RUNNER_H_
+
+#include <deque>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/test_pending_task.h"
+#include "base/threading/thread_checker.h"
+
+namespace base {
+
+class TimeDelta;
+
+// TestSimpleTaskRunner is a simple TaskRunner implementation that can
+// be used for testing.  It implements SingleThreadTaskRunner as that
+// interface implements SequencedTaskRunner, which in turn implements
+// TaskRunner, so TestSimpleTaskRunner can be passed in to a function
+// that accepts any *TaskRunner object.
+//
+// TestSimpleTaskRunner has the following properties which make it simple:
+//
+//   - It is non-thread safe; all member functions must be called on
+//     the same thread.
+//   - Tasks are simply stored in a queue in FIFO order, ignoring delay
+//     and nestability.
+//   - Tasks aren't guaranteed to be destroyed immediately after
+//     they're run.
+//
+// However, TestSimpleTaskRunner allows for reentrancy, in that it
+// handles the running of tasks that in turn call back into itself
+// (e.g., to post more tasks).
+//
+// If you need more complicated properties, consider using this class
+// as a template for writing a test TaskRunner implementation using
+// TestPendingTask.
+//
+// Note that, like any TaskRunner, TestSimpleTaskRunner is
+// ref-counted.
+class TestSimpleTaskRunner : public SingleThreadTaskRunner {
+ public:
+  TestSimpleTaskRunner();
+
+  // SingleThreadTaskRunner implementation.
+  virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
+                               const Closure& task,
+                               TimeDelta delay) OVERRIDE;
+  virtual bool PostNonNestableDelayedTask(
+      const tracked_objects::Location& from_here,
+      const Closure& task,
+      TimeDelta delay) OVERRIDE;
+
+  virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
+
+  const std::deque<TestPendingTask>& GetPendingTasks() const;
+  bool HasPendingTask() const;
+  base::TimeDelta NextPendingTaskDelay() const;
+
+  // Clears the queue of pending tasks without running them.
+  void ClearPendingTasks();
+
+  // Runs each current pending task in order and clears the queue.
+  // Any tasks posted by the tasks are not run.
+  virtual void RunPendingTasks();
+
+  // Runs pending tasks until the queue is empty.
+  void RunUntilIdle();
+
+ protected:
+  virtual ~TestSimpleTaskRunner();
+
+  std::deque<TestPendingTask> pending_tasks_;
+  ThreadChecker thread_checker_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestSimpleTaskRunner);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_TEST_SIMPLE_TASK_RUNNER_H_
diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc
new file mode 100644
index 0000000..45b02f9
--- /dev/null
+++ b/base/test/test_suite.cc
@@ -0,0 +1,345 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_suite.h"
+
+#include "base/at_exit.h"
+#include "base/base_paths.h"
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/debug/debugger.h"
+#include "base/debug/stack_trace.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/i18n/icu_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/process/memory.h"
+#include "base/test/gtest_xml_util.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_switches.h"
+#include "base/test/test_timeouts.h"
+#include "base/time/time.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_nsautorelease_pool.h"
+#if defined(OS_IOS)
+#include "base/test/test_listener_ios.h"
+#else
+#include "base/test/mock_chrome_application_mac.h"
+#endif  // OS_IOS
+#endif  // OS_MACOSX
+
+#if defined(OS_ANDROID)
+#include "base/test/test_support_android.h"
+#endif
+
+#if defined(OS_IOS)
+#include "base/test/test_support_ios.h"
+#endif
+
+namespace {
+
+class MaybeTestDisabler : public testing::EmptyTestEventListener {
+ public:
+  virtual void OnTestStart(const testing::TestInfo& test_info) OVERRIDE {
+    ASSERT_FALSE(TestSuite::IsMarkedMaybe(test_info))
+        << "Probably the OS #ifdefs don't include all of the necessary "
+           "platforms.\nPlease ensure that no tests have the MAYBE_ prefix "
+           "after the code is preprocessed.";
+  }
+};
+
+class TestClientInitializer : public testing::EmptyTestEventListener {
+ public:
+  TestClientInitializer()
+      : old_command_line_(CommandLine::NO_PROGRAM) {
+  }
+
+  virtual void OnTestStart(const testing::TestInfo& test_info) OVERRIDE {
+    old_command_line_ = *CommandLine::ForCurrentProcess();
+  }
+
+  virtual void OnTestEnd(const testing::TestInfo& test_info) OVERRIDE {
+    *CommandLine::ForCurrentProcess() = old_command_line_;
+  }
+
+ private:
+  CommandLine old_command_line_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestClientInitializer);
+};
+
+}  // namespace
+
+namespace base {
+
+int RunUnitTestsUsingBaseTestSuite(int argc, char **argv) {
+  TestSuite test_suite(argc, argv);
+  return base::LaunchUnitTests(
+      argc, argv, Bind(&TestSuite::Run, Unretained(&test_suite)));
+}
+
+}  // namespace base
+
+TestSuite::TestSuite(int argc, char** argv) : initialized_command_line_(false) {
+  PreInitialize(true);
+  InitializeFromCommandLine(argc, argv);
+}
+
+#if defined(OS_WIN)
+TestSuite::TestSuite(int argc, wchar_t** argv)
+    : initialized_command_line_(false) {
+  PreInitialize(true);
+  InitializeFromCommandLine(argc, argv);
+}
+#endif  // defined(OS_WIN)
+
+TestSuite::TestSuite(int argc, char** argv, bool create_at_exit_manager)
+    : initialized_command_line_(false) {
+  PreInitialize(create_at_exit_manager);
+  InitializeFromCommandLine(argc, argv);
+}
+
+TestSuite::~TestSuite() {
+  if (initialized_command_line_)
+    CommandLine::Reset();
+}
+
+void TestSuite::InitializeFromCommandLine(int argc, char** argv) {
+  initialized_command_line_ = CommandLine::Init(argc, argv);
+  testing::InitGoogleTest(&argc, argv);
+  testing::InitGoogleMock(&argc, argv);
+
+#if defined(OS_IOS)
+  InitIOSRunHook(this, argc, argv);
+#endif
+}
+
+#if defined(OS_WIN)
+void TestSuite::InitializeFromCommandLine(int argc, wchar_t** argv) {
+  // Windows CommandLine::Init ignores argv anyway.
+  initialized_command_line_ = CommandLine::Init(argc, NULL);
+  testing::InitGoogleTest(&argc, argv);
+  testing::InitGoogleMock(&argc, argv);
+}
+#endif  // defined(OS_WIN)
+
+void TestSuite::PreInitialize(bool create_at_exit_manager) {
+#if defined(OS_WIN)
+  testing::GTEST_FLAG(catch_exceptions) = false;
+#endif
+  base::EnableTerminationOnHeapCorruption();
+#if defined(OS_LINUX) && defined(USE_AURA)
+  // When calling native char conversion functions (e.g wrctomb) we need to
+  // have the locale set. In the absence of such a call the "C" locale is the
+  // default. In the gtk code (below) gtk_init() implicitly sets a locale.
+  setlocale(LC_ALL, "");
+#endif  // defined(OS_LINUX) && defined(USE_AURA)
+
+  // On Android, AtExitManager is created in
+  // testing/android/native_test_wrapper.cc before main() is called.
+#if !defined(OS_ANDROID)
+  if (create_at_exit_manager)
+    at_exit_manager_.reset(new base::AtExitManager);
+#endif
+
+  // Don't add additional code to this function.  Instead add it to
+  // Initialize().  See bug 6436.
+}
+
+
+// static
+bool TestSuite::IsMarkedMaybe(const testing::TestInfo& test) {
+  return strncmp(test.name(), "MAYBE_", 6) == 0;
+}
+
+void TestSuite::CatchMaybeTests() {
+  testing::TestEventListeners& listeners =
+      testing::UnitTest::GetInstance()->listeners();
+  listeners.Append(new MaybeTestDisabler);
+}
+
+void TestSuite::ResetCommandLine() {
+  testing::TestEventListeners& listeners =
+      testing::UnitTest::GetInstance()->listeners();
+  listeners.Append(new TestClientInitializer);
+}
+
+#if !defined(OS_IOS)
+void TestSuite::AddTestLauncherResultPrinter() {
+  // Only add the custom printer if requested.
+  if (!CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kTestLauncherOutput)) {
+    return;
+  }
+
+  FilePath output_path(CommandLine::ForCurrentProcess()->GetSwitchValuePath(
+                           switches::kTestLauncherOutput));
+
+  // Do not add the result printer if output path already exists. It's an
+  // indicator there is a process printing to that file, and we're likely
+  // its child. Do not clobber the results in that case.
+  if (PathExists(output_path)) {
+    LOG(WARNING) << "Test launcher output path " << output_path.AsUTF8Unsafe()
+                 << " exists. Not adding test launcher result printer.";
+    return;
+  }
+
+  XmlUnitTestResultPrinter* printer = new XmlUnitTestResultPrinter;
+  CHECK(printer->Initialize(output_path));
+  testing::TestEventListeners& listeners =
+      testing::UnitTest::GetInstance()->listeners();
+  listeners.Append(printer);
+}
+#endif  // !defined(OS_IOS)
+
+// Don't add additional code to this method.  Instead add it to
+// Initialize().  See bug 6436.
+int TestSuite::Run() {
+#if defined(OS_IOS)
+  RunTestsFromIOSApp();
+#endif
+
+#if defined(OS_MACOSX)
+  base::mac::ScopedNSAutoreleasePool scoped_pool;
+#endif
+
+  Initialize();
+  std::string client_func =
+      CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          switches::kTestChildProcess);
+
+  // Check to see if we are being run as a client process.
+  if (!client_func.empty())
+    return multi_process_function_list::InvokeChildProcessTest(client_func);
+#if defined(OS_IOS)
+  base::test_listener_ios::RegisterTestEndListener();
+#endif
+  int result = RUN_ALL_TESTS();
+
+#if defined(OS_MACOSX)
+  // This MUST happen before Shutdown() since Shutdown() tears down
+  // objects (such as NotificationService::current()) that Cocoa
+  // objects use to remove themselves as observers.
+  scoped_pool.Recycle();
+#endif
+
+  Shutdown();
+
+  return result;
+}
+
+// static
+void TestSuite::UnitTestAssertHandler(const std::string& str) {
+#if defined(OS_ANDROID)
+  // Correlating test stdio with logcat can be difficult, so we emit this
+  // helpful little hint about what was running.  Only do this for Android
+  // because other platforms don't separate out the relevant logs in the same
+  // way.
+  const ::testing::TestInfo* const test_info =
+      ::testing::UnitTest::GetInstance()->current_test_info();
+  if (test_info) {
+    LOG(ERROR) << "Currently running: " << test_info->test_case_name() << "."
+               << test_info->name();
+    fflush(stderr);
+  }
+#endif  // defined(OS_ANDROID)
+
+  // The logging system actually prints the message before calling the assert
+  // handler. Just exit now to avoid printing too many stack traces.
+  _exit(1);
+}
+
+void TestSuite::SuppressErrorDialogs() {
+#if defined(OS_WIN)
+  UINT new_flags = SEM_FAILCRITICALERRORS |
+                   SEM_NOGPFAULTERRORBOX |
+                   SEM_NOOPENFILEERRORBOX;
+
+  // Preserve existing error mode, as discussed at
+  // http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx
+  UINT existing_flags = SetErrorMode(new_flags);
+  SetErrorMode(existing_flags | new_flags);
+
+#if defined(_DEBUG) && defined(_HAS_EXCEPTIONS) && (_HAS_EXCEPTIONS == 1)
+  // Suppress the "Debug Assertion Failed" dialog.
+  // TODO(hbono): remove this code when gtest has it.
+  // http://groups.google.com/d/topic/googletestframework/OjuwNlXy5ac/discussion
+  _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+  _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+#endif  // defined(_DEBUG) && defined(_HAS_EXCEPTIONS) && (_HAS_EXCEPTIONS == 1)
+#endif  // defined(OS_WIN)
+}
+
+void TestSuite::Initialize() {
+#if !defined(OS_IOS)
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kWaitForDebugger)) {
+    base::debug::WaitForDebugger(60, true);
+  }
+#endif
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+  // Some of the app unit tests spin runloops.
+  mock_cr_app::RegisterMockCrApp();
+#endif
+
+#if defined(OS_IOS)
+  InitIOSTestMessageLoop();
+#endif  // OS_IOS
+
+#if defined(OS_ANDROID)
+  InitAndroidTest();
+#else
+  // Initialize logging.
+  base::FilePath exe;
+  PathService::Get(base::FILE_EXE, &exe);
+  base::FilePath log_filename = exe.ReplaceExtension(FILE_PATH_LITERAL("log"));
+  logging::LoggingSettings settings;
+  settings.logging_dest = logging::LOG_TO_ALL;
+  settings.log_file = log_filename.value().c_str();
+  settings.delete_old = logging::DELETE_OLD_LOG_FILE;
+  logging::InitLogging(settings);
+  // We want process and thread IDs because we may have multiple processes.
+  // Note: temporarily enabled timestamps in an effort to catch bug 6361.
+  logging::SetLogItems(true, true, true, true);
+#endif  // else defined(OS_ANDROID)
+
+  CHECK(base::debug::EnableInProcessStackDumping());
+#if defined(OS_WIN)
+  // Make sure we run with high resolution timer to minimize differences
+  // between production code and test code.
+  base::Time::EnableHighResolutionTimer(true);
+#endif  // defined(OS_WIN)
+
+  // In some cases, we do not want to see standard error dialogs.
+  if (!base::debug::BeingDebugged() &&
+      !CommandLine::ForCurrentProcess()->HasSwitch("show-error-dialogs")) {
+    SuppressErrorDialogs();
+    base::debug::SetSuppressDebugUI(true);
+    logging::SetLogAssertHandler(UnitTestAssertHandler);
+  }
+
+  base::i18n::InitializeICU();
+
+  CatchMaybeTests();
+  ResetCommandLine();
+#if !defined(OS_IOS)
+  AddTestLauncherResultPrinter();
+#endif  // !defined(OS_IOS)
+
+  TestTimeouts::Initialize();
+
+  trace_to_file_.BeginTracingFromCommandLineOptions();
+}
+
+void TestSuite::Shutdown() {
+}
diff --git a/base/test/test_suite.h b/base/test/test_suite.h
new file mode 100644
index 0000000..fa0ab6c
--- /dev/null
+++ b/base/test/test_suite.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_SUITE_H_
+#define BASE_TEST_TEST_SUITE_H_
+
+// Defines a basic test suite framework for running gtest based tests.  You can
+// instantiate this class in your main function and call its Run method to run
+// any gtest based tests that are linked into your executable.
+
+#include <string>
+
+#include "base/at_exit.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/test/trace_to_file.h"
+
+namespace testing {
+class TestInfo;
+}
+
+namespace base {
+
+// Instantiates TestSuite, runs it and returns exit code.
+int RunUnitTestsUsingBaseTestSuite(int argc, char **argv);
+
+class TestSuite {
+ public:
+  // Match function used by the GetTestCount method.
+  typedef bool (*TestMatch)(const testing::TestInfo&);
+
+  TestSuite(int argc, char** argv);
+#if defined(OS_WIN)
+  TestSuite(int argc, wchar_t** argv);
+#endif  // defined(OS_WIN)
+  virtual ~TestSuite();
+
+  // Returns true if the test is marked as "MAYBE_".
+  // When using different prefixes depending on platform, we use MAYBE_ and
+  // preprocessor directives to replace MAYBE_ with the target prefix.
+  static bool IsMarkedMaybe(const testing::TestInfo& test);
+
+  void CatchMaybeTests();
+
+  void ResetCommandLine();
+
+  void AddTestLauncherResultPrinter();
+
+  int Run();
+
+ protected:
+  // This constructor is only accessible to specialized test suite
+  // implementations which need to control the creation of an AtExitManager
+  // instance for the duration of the test.
+  TestSuite(int argc, char** argv, bool create_at_exit_manager);
+
+  // By default fatal log messages (e.g. from DCHECKs) result in error dialogs
+  // which gum up buildbots. Use a minimalistic assert handler which just
+  // terminates the process.
+  static void UnitTestAssertHandler(const std::string& str);
+
+  // Disable crash dialogs so that it doesn't gum up the buildbot
+  virtual void SuppressErrorDialogs();
+
+  // Override these for custom initialization and shutdown handling.  Use these
+  // instead of putting complex code in your constructor/destructor.
+
+  virtual void Initialize();
+  virtual void Shutdown();
+
+  // Make sure that we setup an AtExitManager so Singleton objects will be
+  // destroyed.
+  scoped_ptr<base::AtExitManager> at_exit_manager_;
+
+ private:
+  void InitializeFromCommandLine(int argc, char** argv);
+#if defined(OS_WIN)
+  void InitializeFromCommandLine(int argc, wchar_t** argv);
+#endif  // defined(OS_WIN)
+
+  // Basic initialization for the test suite happens here.
+  void PreInitialize(bool create_at_exit_manager);
+
+  test::TraceToFile trace_to_file_;
+
+  bool initialized_command_line_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSuite);
+};
+
+}  // namespace base
+
+// TODO(brettw) remove this. This is a temporary hack to allow WebKit to compile
+// until we can update it to use "base::" (preventing a two-sided patch).
+using base::TestSuite;
+
+#endif  // BASE_TEST_TEST_SUITE_H_
diff --git a/base/test/test_support_android.cc b/base/test/test_support_android.cc
new file mode 100644
index 0000000..4ada567
--- /dev/null
+++ b/base/test/test_support_android.cc
@@ -0,0 +1,189 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdarg.h>
+#include <string.h>
+
+#include "base/android/path_utils.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_pump_android.h"
+#include "base/path_service.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace {
+
+struct RunState {
+  RunState(base::MessagePump::Delegate* delegate, int run_depth)
+      : delegate(delegate),
+        run_depth(run_depth),
+        should_quit(false) {
+  }
+
+  base::MessagePump::Delegate* delegate;
+
+  // Used to count how many Run() invocations are on the stack.
+  int run_depth;
+
+  // Used to flag that the current Run() invocation should return ASAP.
+  bool should_quit;
+};
+
+RunState* g_state = NULL;
+
+// A singleton WaitableEvent wrapper so we avoid a busy loop in
+// MessagePumpForUIStub. Other platforms use the native event loop which blocks
+// when there are no pending messages.
+class Waitable {
+ public:
+   static Waitable* GetInstance() {
+     return Singleton<Waitable>::get();
+   }
+
+   // Signals that there are more work to do.
+   void Signal() {
+     waitable_event_.Signal();
+   }
+
+   // Blocks until more work is scheduled.
+   void Block() {
+     waitable_event_.Wait();
+   }
+
+   void Quit() {
+     g_state->should_quit = true;
+     Signal();
+   }
+
+ private:
+  friend struct DefaultSingletonTraits<Waitable>;
+
+  Waitable()
+      : waitable_event_(false, false) {
+  }
+
+  base::WaitableEvent waitable_event_;
+
+  DISALLOW_COPY_AND_ASSIGN(Waitable);
+};
+
+// The MessagePumpForUI implementation for test purpose.
+class MessagePumpForUIStub : public base::MessagePumpForUI {
+  virtual ~MessagePumpForUIStub() {}
+
+  virtual void Start(base::MessagePump::Delegate* delegate) OVERRIDE {
+    NOTREACHED() << "The Start() method shouldn't be called in test, using"
+        " Run() method should be used.";
+  }
+
+  virtual void Run(base::MessagePump::Delegate* delegate) OVERRIDE {
+    // The following was based on message_pump_glib.cc, except we're using a
+    // WaitableEvent since there are no native message loop to use.
+    RunState state(delegate, g_state ? g_state->run_depth + 1 : 1);
+
+    RunState* previous_state = g_state;
+    g_state = &state;
+
+    bool more_work_is_plausible = true;
+
+    for (;;) {
+      if (!more_work_is_plausible) {
+        Waitable::GetInstance()->Block();
+        if (g_state->should_quit)
+          break;
+      }
+
+      more_work_is_plausible = g_state->delegate->DoWork();
+      if (g_state->should_quit)
+        break;
+
+      base::TimeTicks delayed_work_time;
+      more_work_is_plausible |=
+          g_state->delegate->DoDelayedWork(&delayed_work_time);
+      if (g_state->should_quit)
+        break;
+
+      if (more_work_is_plausible)
+        continue;
+
+      more_work_is_plausible = g_state->delegate->DoIdleWork();
+      if (g_state->should_quit)
+        break;
+
+      more_work_is_plausible |= !delayed_work_time.is_null();
+    }
+
+    g_state = previous_state;
+  }
+
+  virtual void Quit() OVERRIDE {
+    Waitable::GetInstance()->Quit();
+  }
+
+  virtual void ScheduleWork() OVERRIDE {
+    Waitable::GetInstance()->Signal();
+  }
+
+  virtual void ScheduleDelayedWork(
+      const base::TimeTicks& delayed_work_time) OVERRIDE {
+    Waitable::GetInstance()->Signal();
+  }
+};
+
+scoped_ptr<base::MessagePump> CreateMessagePumpForUIStub() {
+  return scoped_ptr<base::MessagePump>(new MessagePumpForUIStub());
+};
+
+// Provides the test path for DIR_MODULE and DIR_ANDROID_APP_DATA.
+bool GetTestProviderPath(int key, base::FilePath* result) {
+  switch (key) {
+    case base::DIR_ANDROID_APP_DATA: {
+      // For tests, app data is put in external storage.
+      return base::android::GetExternalStorageDirectory(result);
+    }
+    default:
+      return false;
+  }
+}
+
+void InitPathProvider(int key) {
+  base::FilePath path;
+  // If failed to override the key, that means the way has not been registered.
+  if (GetTestProviderPath(key, &path) && !PathService::Override(key, path))
+    PathService::RegisterProvider(&GetTestProviderPath, key, key + 1);
+}
+
+}  // namespace
+
+namespace base {
+
+void InitAndroidTestLogging() {
+  logging::LoggingSettings settings;
+  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+  logging::InitLogging(settings);
+  // To view log output with IDs and timestamps use "adb logcat -v threadtime".
+  logging::SetLogItems(false,    // Process ID
+                       false,    // Thread ID
+                       false,    // Timestamp
+                       false);   // Tick count
+}
+
+void InitAndroidTestPaths() {
+  InitPathProvider(DIR_MODULE);
+  InitPathProvider(DIR_ANDROID_APP_DATA);
+}
+
+void InitAndroidTestMessageLoop() {
+  if (!MessageLoop::InitMessagePumpForUIFactory(&CreateMessagePumpForUIStub))
+    LOG(INFO) << "MessagePumpForUIFactory already set, unable to override.";
+}
+
+void InitAndroidTest() {
+  InitAndroidTestLogging();
+  InitAndroidTestPaths();
+  InitAndroidTestMessageLoop();
+}
+}  // namespace base
diff --git a/base/test/test_support_android.h b/base/test/test_support_android.h
new file mode 100644
index 0000000..062785e
--- /dev/null
+++ b/base/test/test_support_android.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_SUPPORT_ANDROID_H_
+#define BASE_TEST_TEST_SUPPORT_ANDROID_H_
+
+#include "base/base_export.h"
+
+namespace base {
+
+// Init logging for tests on Android. Logs will be output into Android's logcat.
+BASE_EXPORT void InitAndroidTestLogging();
+
+// Init path providers for tests on Android.
+BASE_EXPORT void InitAndroidTestPaths();
+
+// Init the message loop for tests on Android.
+BASE_EXPORT void InitAndroidTestMessageLoop();
+
+// Do all of the initializations above.
+BASE_EXPORT void InitAndroidTest();
+
+}  // namespace base
+
+#endif  // BASE_TEST_TEST_SUPPORT_ANDROID_H_
diff --git a/base/test/test_support_ios.h b/base/test/test_support_ios.h
new file mode 100644
index 0000000..c71cf0d
--- /dev/null
+++ b/base/test/test_support_ios.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_SUPPORT_IOS_H_
+#define BASE_TEST_TEST_SUPPORT_IOS_H_
+
+#include "base/test/test_suite.h"
+
+namespace base {
+
+// Inits the message loop for tests on iOS.
+void InitIOSTestMessageLoop();
+
+// Inits the run hook for tests on iOS.
+void InitIOSRunHook(TestSuite* suite, int argc, char* argv[]);
+
+// Launches an iOS app that runs the tests in the suite passed to
+// InitIOSRunHook.
+void RunTestsFromIOSApp();
+
+}  // namespace base
+
+#endif  // BASE_TEST_TEST_SUPPORT_IOS_H_
diff --git a/base/test/test_support_ios.mm b/base/test/test_support_ios.mm
new file mode 100644
index 0000000..80a4caf
--- /dev/null
+++ b/base/test/test_support_ios.mm
@@ -0,0 +1,212 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <UIKit/UIKit.h>
+
+#include "base/debug/debugger.h"
+#include "base/logging.h"
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_pump_default.h"
+#include "base/test/test_suite.h"
+
+// Springboard will kill any iOS app that fails to check in after launch within
+// a given time. Starting a UIApplication before invoking TestSuite::Run
+// prevents this from happening.
+
+// InitIOSRunHook saves the TestSuite and argc/argv, then invoking
+// RunTestsFromIOSApp calls UIApplicationMain(), providing an application
+// delegate class: ChromeUnitTestDelegate. The delegate implements
+// application:didFinishLaunchingWithOptions: to invoke the TestSuite's Run
+// method.
+
+// Since the executable isn't likely to be a real iOS UI, the delegate puts up a
+// window displaying the app name. If a bunch of apps using MainHook are being
+// run in a row, this provides an indication of which one is currently running.
+
+static base::TestSuite* g_test_suite = NULL;
+static int g_argc;
+static char** g_argv;
+
+@interface UIApplication (Testing)
+- (void) _terminateWithStatus:(int)status;
+@end
+
+#if TARGET_IPHONE_SIMULATOR
+// Xcode 6 introduced behavior in the iOS Simulator where the software
+// keyboard does not appear if a hardware keyboard is connected. The following
+// declaration allows this behavior to be overriden when the app starts up.
+@interface UIKeyboardImpl
++ (instancetype)sharedInstance;
+- (void)setAutomaticMinimizationEnabled:(BOOL)enabled;
+- (void)setSoftwareKeyboardShownByTouch:(BOOL)enabled;
+@end
+#endif  // TARGET_IPHONE_SIMULATOR
+
+@interface ChromeUnitTestDelegate : NSObject {
+ @private
+  base::scoped_nsobject<UIWindow> window_;
+}
+- (void)runTests;
+@end
+
+@implementation ChromeUnitTestDelegate
+
+- (BOOL)application:(UIApplication *)application
+    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+
+#if TARGET_IPHONE_SIMULATOR
+  // Xcode 6 introduced behavior in the iOS Simulator where the software
+  // keyboard does not appear if a hardware keyboard is connected. The following
+  // calls override this behavior by ensuring that the software keyboard is
+  // always shown.
+  [[UIKeyboardImpl sharedInstance] setAutomaticMinimizationEnabled:NO];
+  [[UIKeyboardImpl sharedInstance] setSoftwareKeyboardShownByTouch:YES];
+#endif  // TARGET_IPHONE_SIMULATOR
+
+  CGRect bounds = [[UIScreen mainScreen] bounds];
+
+  // Yes, this is leaked, it's just to make what's running visible.
+  window_.reset([[UIWindow alloc] initWithFrame:bounds]);
+  [window_ makeKeyAndVisible];
+
+  // Add a label with the app name.
+  UILabel* label = [[[UILabel alloc] initWithFrame:bounds] autorelease];
+  label.text = [[NSProcessInfo processInfo] processName];
+  label.textAlignment = NSTextAlignmentCenter;
+  [window_ addSubview:label];
+
+  if ([self shouldRedirectOutputToFile])
+    [self redirectOutput];
+
+  // Queue up the test run.
+  [self performSelector:@selector(runTests)
+             withObject:nil
+             afterDelay:0.1];
+  return YES;
+}
+
+// Returns true if the gtest output should be redirected to a file, then sent
+// to NSLog when compleete. This redirection is used because gtest only writes
+// output to stdout, but results must be written to NSLog in order to show up in
+// the device log that is retrieved from the device by the host.
+- (BOOL)shouldRedirectOutputToFile {
+#if !TARGET_IPHONE_SIMULATOR
+  return !base::debug::BeingDebugged();
+#endif  // TARGET_IPHONE_SIMULATOR
+  return NO;
+}
+
+// Returns the path to the directory to store gtest output files.
+- (NSString*)outputPath {
+  NSArray* searchPath =
+      NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
+                                          NSUserDomainMask,
+                                          YES);
+  CHECK([searchPath count] > 0) << "Failed to get the Documents folder";
+  return [searchPath objectAtIndex:0];
+}
+
+// Returns the path to file that stdout is redirected to.
+- (NSString*)stdoutPath {
+  return [[self outputPath] stringByAppendingPathComponent:@"stdout.log"];
+}
+
+// Returns the path to file that stderr is redirected to.
+- (NSString*)stderrPath {
+  return [[self outputPath] stringByAppendingPathComponent:@"stderr.log"];
+}
+
+// Redirects stdout and stderr to files in the Documents folder in the app's
+// sandbox.
+- (void)redirectOutput {
+  freopen([[self stdoutPath] UTF8String], "w+", stdout);
+  freopen([[self stderrPath] UTF8String], "w+", stderr);
+}
+
+// Reads the redirected gtest output from a file and writes it to NSLog.
+- (void)writeOutputToNSLog {
+  // Close the redirected stdout and stderr files so that the content written to
+  // NSLog doesn't end up in these files.
+  fclose(stdout);
+  fclose(stderr);
+  for (NSString* path in @[ [self stdoutPath], [self stderrPath]]) {
+    NSString* content = [NSString stringWithContentsOfFile:path
+                                                  encoding:NSUTF8StringEncoding
+                                                     error:NULL];
+    NSArray* lines = [content componentsSeparatedByCharactersInSet:
+        [NSCharacterSet newlineCharacterSet]];
+
+    NSLog(@"Writing contents of %@ to NSLog", path);
+    for (NSString* line in lines) {
+      NSLog(@"%@", line);
+    }
+  }
+}
+
+- (void)runTests {
+  int exitStatus = g_test_suite->Run();
+
+  if ([self shouldRedirectOutputToFile])
+    [self writeOutputToNSLog];
+
+  // If a test app is too fast, it will exit before Instruments has has a
+  // a chance to initialize and no test results will be seen.
+  // TODO(ios): crbug.com/137010 Figure out how much time is actually needed,
+  // and sleep only to make sure that much time has elapsed since launch.
+  [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
+  window_.reset();
+
+  // Use the hidden selector to try and cleanly take down the app (otherwise
+  // things can think the app crashed even on a zero exit status).
+  UIApplication* application = [UIApplication sharedApplication];
+  [application _terminateWithStatus:exitStatus];
+
+  exit(exitStatus);
+}
+
+@end
+
+namespace {
+
+scoped_ptr<base::MessagePump> CreateMessagePumpForUIForTests() {
+  // A default MessagePump will do quite nicely in tests.
+  return scoped_ptr<base::MessagePump>(new base::MessagePumpDefault());
+}
+
+}  // namespace
+
+namespace base {
+
+void InitIOSTestMessageLoop() {
+  MessageLoop::InitMessagePumpForUIFactory(&CreateMessagePumpForUIForTests);
+}
+
+void InitIOSRunHook(TestSuite* suite, int argc, char* argv[]) {
+  g_test_suite = suite;
+  g_argc = argc;
+  g_argv = argv;
+}
+
+void RunTestsFromIOSApp() {
+  // When TestSuite::Run is invoked it calls RunTestsFromIOSApp(). On the first
+  // invocation, this method fires up an iOS app via UIApplicationMain. Since
+  // UIApplicationMain does not return until the app exits, control does not
+  // return to the initial TestSuite::Run invocation, so the app invokes
+  // TestSuite::Run a second time and since |ran_hook| is true at this point,
+  // this method is a no-op and control returns to TestSuite:Run so that test
+  // are executed. Once the app exits, RunTestsFromIOSApp calls exit() so that
+  // control is not returned to the initial invocation of TestSuite::Run.
+  static bool ran_hook = false;
+  if (!ran_hook) {
+    ran_hook = true;
+    mac::ScopedNSAutoreleasePool pool;
+    int exit_status = UIApplicationMain(g_argc, g_argv, nil,
+                                        @"ChromeUnitTestDelegate");
+    exit(exit_status);
+  }
+}
+
+}  // namespace base
diff --git a/base/test/test_switches.cc b/base/test/test_switches.cc
new file mode 100644
index 0000000..c970fd2
--- /dev/null
+++ b/base/test/test_switches.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_switches.h"
+
+// Maximum number of tests to run in a single batch.
+const char switches::kTestLauncherBatchLimit[] = "test-launcher-batch-limit";
+
+// Sets defaults desirable for the continuous integration bots, e.g. parallel
+// test execution and test retries.
+const char switches::kTestLauncherBotMode[] =
+    "test-launcher-bot-mode";
+
+// Makes it possible to debug the launcher itself. By default the launcher
+// automatically switches to single process mode when it detects presence
+// of debugger.
+const char switches::kTestLauncherDebugLauncher[] =
+    "test-launcher-debug-launcher";
+
+// Path to file containing test filter (one pattern per line).
+const char switches::kTestLauncherFilterFile[] = "test-launcher-filter-file";
+
+// Number of parallel test launcher jobs.
+const char switches::kTestLauncherJobs[] = "test-launcher-jobs";
+
+// Path to test results file in our custom test launcher format.
+const char switches::kTestLauncherOutput[] = "test-launcher-output";
+
+// Maximum number of times to retry a test after failure.
+const char switches::kTestLauncherRetryLimit[] = "test-launcher-retry-limit";
+
+// Path to test results file with all the info from the test launcher.
+const char switches::kTestLauncherSummaryOutput[] =
+    "test-launcher-summary-output";
+
+// Flag controlling when test stdio is displayed as part of the launcher's
+// standard output.
+const char switches::kTestLauncherPrintTestStdio[] =
+    "test-launcher-print-test-stdio";
+
+// Index of the test shard to run, starting from 0 (first shard) to total shards
+// minus one (last shard).
+const char switches::kTestLauncherShardIndex[] =
+    "test-launcher-shard-index";
+
+// Total number of shards. Must be the same for all shards.
+const char switches::kTestLauncherTotalShards[] =
+    "test-launcher-total-shards";
+
+// Time (in milliseconds) that the tests should wait before timing out.
+const char switches::kTestLauncherTimeout[] = "test-launcher-timeout";
+// TODO(phajdan.jr): Clean up the switch names.
+const char switches::kTestTinyTimeout[] = "test-tiny-timeout";
+const char switches::kUiTestActionTimeout[] = "ui-test-action-timeout";
+const char switches::kUiTestActionMaxTimeout[] = "ui-test-action-max-timeout";
diff --git a/base/test/test_switches.h b/base/test/test_switches.h
new file mode 100644
index 0000000..c228cf0
--- /dev/null
+++ b/base/test/test_switches.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_SWITCHES_H_
+#define BASE_TEST_TEST_SWITCHES_H_
+
+namespace switches {
+
+// All switches in alphabetical order. The switches should be documented
+// alongside the definition of their values in the .cc file.
+extern const char kTestLauncherBatchLimit[];
+extern const char kTestLauncherBotMode[];
+extern const char kTestLauncherDebugLauncher[];
+extern const char kTestLauncherFilterFile[];
+extern const char kTestLauncherJobs[];
+extern const char kTestLauncherOutput[];
+extern const char kTestLauncherRetryLimit[];
+extern const char kTestLauncherSummaryOutput[];
+extern const char kTestLauncherPrintTestStdio[];
+extern const char kTestLauncherShardIndex[];
+extern const char kTestLauncherTotalShards[];
+extern const char kTestLauncherTimeout[];
+extern const char kTestTinyTimeout[];
+extern const char kUiTestActionTimeout[];
+extern const char kUiTestActionMaxTimeout[];
+
+}  // namespace switches
+
+#endif  // BASE_TEST_TEST_SWITCHES_H_
diff --git a/base/test/test_timeouts.cc b/base/test/test_timeouts.cc
new file mode 100644
index 0000000..c84cd38
--- /dev/null
+++ b/base/test/test_timeouts.cc
@@ -0,0 +1,113 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_timeouts.h"
+
+#include <algorithm>
+
+#include "base/command_line.h"
+#include "base/debug/debugger.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/test_switches.h"
+
+namespace {
+
+// ASan/TSan/MSan instrument each memory access. This may slow the execution
+// down significantly.
+#if defined(MEMORY_SANITIZER)
+// For MSan the slowdown depends heavily on the value of msan_track_origins GYP
+// flag. The multiplier below corresponds to msan_track_origins=1.
+static const int kTimeoutMultiplier = 6;
+#elif defined(ADDRESS_SANITIZER) && defined(OS_WIN)
+// Asan/Win has not been optimized yet, give it a higher
+// timeout multiplier. See http://crbug.com/412471
+static const int kTimeoutMultiplier = 8;
+#elif defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \
+    defined(SYZYASAN)
+static const int kTimeoutMultiplier = 2;
+#else
+static const int kTimeoutMultiplier = 1;
+#endif
+
+const int kAlmostInfiniteTimeoutMs = 100000000;
+
+// Sets value to the greatest of:
+// 1) value's current value multiplied by kTimeoutMultiplier (assuming
+// InitializeTimeout is called only once per value).
+// 2) min_value.
+// 3) the numerical value given by switch_name on the command line multiplied
+// by kTimeoutMultiplier.
+void InitializeTimeout(const char* switch_name, int min_value, int* value) {
+  DCHECK(value);
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switch_name)) {
+    std::string string_value(
+        CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switch_name));
+    int timeout;
+    base::StringToInt(string_value, &timeout);
+    *value = std::max(*value, timeout);
+  }
+  *value *= kTimeoutMultiplier;
+  *value = std::max(*value, min_value);
+}
+
+// Sets value to the greatest of:
+// 1) value's current value multiplied by kTimeoutMultiplier.
+// 2) 0
+// 3) the numerical value given by switch_name on the command line multiplied
+// by kTimeoutMultiplier.
+void InitializeTimeout(const char* switch_name, int* value) {
+  InitializeTimeout(switch_name, 0, value);
+}
+
+}  // namespace
+
+// static
+bool TestTimeouts::initialized_ = false;
+
+// The timeout values should increase in the order they appear in this block.
+// static
+int TestTimeouts::tiny_timeout_ms_ = 100;
+int TestTimeouts::action_timeout_ms_ = 10000;
+#ifndef NDEBUG
+int TestTimeouts::action_max_timeout_ms_ = 45000;
+#else
+int TestTimeouts::action_max_timeout_ms_ = 30000;
+#endif  // NDEBUG
+
+int TestTimeouts::test_launcher_timeout_ms_ = 45000;
+
+// static
+void TestTimeouts::Initialize() {
+  if (initialized_) {
+    NOTREACHED();
+    return;
+  }
+  initialized_ = true;
+
+  if (base::debug::BeingDebugged()) {
+    fprintf(stdout,
+        "Detected presence of a debugger, running without test timeouts.\n");
+  }
+
+  // Note that these timeouts MUST be initialized in the correct order as
+  // per the CHECKS below.
+  InitializeTimeout(switches::kTestTinyTimeout, &tiny_timeout_ms_);
+  InitializeTimeout(switches::kUiTestActionTimeout,
+                    base::debug::BeingDebugged() ? kAlmostInfiniteTimeoutMs
+                                                 : tiny_timeout_ms_,
+                    &action_timeout_ms_);
+  InitializeTimeout(switches::kUiTestActionMaxTimeout, action_timeout_ms_,
+                    &action_max_timeout_ms_);
+
+  // Test launcher timeout is independent from anything above action timeout.
+  InitializeTimeout(switches::kTestLauncherTimeout, action_timeout_ms_,
+                    &test_launcher_timeout_ms_);
+
+  // The timeout values should be increasing in the right order.
+  CHECK(tiny_timeout_ms_ <= action_timeout_ms_);
+  CHECK(action_timeout_ms_ <= action_max_timeout_ms_);
+
+  CHECK(action_timeout_ms_ <= test_launcher_timeout_ms_);
+}
diff --git a/base/test/test_timeouts.h b/base/test/test_timeouts.h
new file mode 100644
index 0000000..2819e4a
--- /dev/null
+++ b/base/test/test_timeouts.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TEST_TIMEOUTS_H_
+#define BASE_TEST_TEST_TIMEOUTS_H_
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+
+// Returns common timeouts to use in tests. Makes it possible to adjust
+// the timeouts for different environments (like Valgrind).
+class TestTimeouts {
+ public:
+  // Initializes the timeouts. Non thread-safe. Should be called exactly once
+  // by the test suite.
+  static void Initialize();
+
+  // Timeout for actions that are expected to finish "almost instantly".
+  static base::TimeDelta tiny_timeout() {
+    DCHECK(initialized_);
+    return base::TimeDelta::FromMilliseconds(tiny_timeout_ms_);
+  }
+
+  // Timeout to wait for something to happen. If you are not sure
+  // which timeout to use, this is the one you want.
+  static base::TimeDelta action_timeout() {
+    DCHECK(initialized_);
+    return base::TimeDelta::FromMilliseconds(action_timeout_ms_);
+  }
+
+  // Timeout longer than the above, but still suitable to use
+  // multiple times in a single test. Use if the timeout above
+  // is not sufficient.
+  static base::TimeDelta action_max_timeout() {
+    DCHECK(initialized_);
+    return base::TimeDelta::FromMilliseconds(action_max_timeout_ms_);
+  }
+
+  // Timeout for a single test launched used built-in test launcher.
+  // Do not use outside of the test launcher.
+  static base::TimeDelta test_launcher_timeout() {
+    DCHECK(initialized_);
+    return base::TimeDelta::FromMilliseconds(test_launcher_timeout_ms_);
+  }
+
+ private:
+  static bool initialized_;
+
+  static int tiny_timeout_ms_;
+  static int action_timeout_ms_;
+  static int action_max_timeout_ms_;
+  static int test_launcher_timeout_ms_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(TestTimeouts);
+};
+
+#endif  // BASE_TEST_TEST_TIMEOUTS_H_
diff --git a/base/test/thread_test_helper.cc b/base/test/thread_test_helper.cc
new file mode 100644
index 0000000..2bd3ba5
--- /dev/null
+++ b/base/test/thread_test_helper.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/thread_test_helper.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace base {
+
+ThreadTestHelper::ThreadTestHelper(
+    const scoped_refptr<MessageLoopProxy>& target_thread)
+    : test_result_(false),
+      target_thread_(target_thread),
+      done_event_(false, false) {
+}
+
+bool ThreadTestHelper::Run() {
+  if (!target_thread_->PostTask(
+          FROM_HERE, base::Bind(&ThreadTestHelper::RunInThread, this))) {
+    return false;
+  }
+  base::ThreadRestrictions::ScopedAllowWait allow_wait;
+  done_event_.Wait();
+  return test_result_;
+}
+
+void ThreadTestHelper::RunTest() { set_test_result(true); }
+
+ThreadTestHelper::~ThreadTestHelper() {}
+
+void ThreadTestHelper::RunInThread() {
+  RunTest();
+  done_event_.Signal();
+}
+
+}  // namespace base
diff --git a/base/test/thread_test_helper.h b/base/test/thread_test_helper.h
new file mode 100644
index 0000000..f6817b5
--- /dev/null
+++ b/base/test/thread_test_helper.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_THREAD_TEST_HELPER_H_
+#define BASE_TEST_THREAD_TEST_HELPER_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace base {
+
+// Helper class that executes code on a given thread while blocking on the
+// invoking thread. To use, derive from this class and overwrite RunTest. An
+// alternative use of this class is to use it directly.  It will then block
+// until all pending tasks on a given thread have been executed.
+class ThreadTestHelper : public RefCountedThreadSafe<ThreadTestHelper> {
+ public:
+  explicit ThreadTestHelper(
+      const scoped_refptr<MessageLoopProxy>& target_thread);
+
+  // True if RunTest() was successfully executed on the target thread.
+  bool Run() WARN_UNUSED_RESULT;
+
+  virtual void RunTest();
+
+ protected:
+  friend class RefCountedThreadSafe<ThreadTestHelper>;
+
+  virtual ~ThreadTestHelper();
+
+  // Use this method to store the result of RunTest().
+  void set_test_result(bool test_result) { test_result_ = test_result; }
+
+ private:
+  void RunInThread();
+
+  bool test_result_;
+  scoped_refptr<MessageLoopProxy> target_thread_;
+  WaitableEvent done_event_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadTestHelper);
+};
+
+}  // namespace base
+
+#endif  // BASE_TEST_THREAD_TEST_HELPER_H_
diff --git a/base/test/trace_event_analyzer.cc b/base/test/trace_event_analyzer.cc
new file mode 100644
index 0000000..3de58e7
--- /dev/null
+++ b/base/test/trace_event_analyzer.cc
@@ -0,0 +1,968 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/trace_event_analyzer.h"
+
+#include <algorithm>
+#include <math.h>
+#include <set>
+
+#include "base/json/json_reader.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+
+namespace trace_analyzer {
+
+// TraceEvent
+
+TraceEvent::TraceEvent()
+    : thread(0, 0),
+      timestamp(0),
+      duration(0),
+      phase(TRACE_EVENT_PHASE_BEGIN),
+      other_event(NULL) {
+}
+
+TraceEvent::~TraceEvent() {
+}
+
+bool TraceEvent::SetFromJSON(const base::Value* event_value) {
+  if (event_value->GetType() != base::Value::TYPE_DICTIONARY) {
+    LOG(ERROR) << "Value must be TYPE_DICTIONARY";
+    return false;
+  }
+  const base::DictionaryValue* dictionary =
+      static_cast<const base::DictionaryValue*>(event_value);
+
+  std::string phase_str;
+  const base::DictionaryValue* args = NULL;
+
+  if (!dictionary->GetString("ph", &phase_str)) {
+    LOG(ERROR) << "ph is missing from TraceEvent JSON";
+    return false;
+  }
+
+  phase = *phase_str.data();
+
+  bool may_have_duration = (phase == TRACE_EVENT_PHASE_COMPLETE);
+  bool require_origin = (phase != TRACE_EVENT_PHASE_METADATA);
+  bool require_id = (phase == TRACE_EVENT_PHASE_ASYNC_BEGIN ||
+                     phase == TRACE_EVENT_PHASE_ASYNC_STEP_INTO ||
+                     phase == TRACE_EVENT_PHASE_ASYNC_STEP_PAST ||
+                     phase == TRACE_EVENT_PHASE_ASYNC_END);
+
+  if (require_origin && !dictionary->GetInteger("pid", &thread.process_id)) {
+    LOG(ERROR) << "pid is missing from TraceEvent JSON";
+    return false;
+  }
+  if (require_origin && !dictionary->GetInteger("tid", &thread.thread_id)) {
+    LOG(ERROR) << "tid is missing from TraceEvent JSON";
+    return false;
+  }
+  if (require_origin && !dictionary->GetDouble("ts", &timestamp)) {
+    LOG(ERROR) << "ts is missing from TraceEvent JSON";
+    return false;
+  }
+  if (may_have_duration) {
+    dictionary->GetDouble("dur", &duration);
+  }
+  if (!dictionary->GetString("cat", &category)) {
+    LOG(ERROR) << "cat is missing from TraceEvent JSON";
+    return false;
+  }
+  if (!dictionary->GetString("name", &name)) {
+    LOG(ERROR) << "name is missing from TraceEvent JSON";
+    return false;
+  }
+  if (!dictionary->GetDictionary("args", &args)) {
+    LOG(ERROR) << "args is missing from TraceEvent JSON";
+    return false;
+  }
+  if (require_id && !dictionary->GetString("id", &id)) {
+    LOG(ERROR) << "id is missing from ASYNC_BEGIN/ASYNC_END TraceEvent JSON";
+    return false;
+  }
+
+  // For each argument, copy the type and create a trace_analyzer::TraceValue.
+  for (base::DictionaryValue::Iterator it(*args); !it.IsAtEnd();
+       it.Advance()) {
+    std::string str;
+    bool boolean = false;
+    int int_num = 0;
+    double double_num = 0.0;
+    if (it.value().GetAsString(&str)) {
+      arg_strings[it.key()] = str;
+    } else if (it.value().GetAsInteger(&int_num)) {
+      arg_numbers[it.key()] = static_cast<double>(int_num);
+    } else if (it.value().GetAsBoolean(&boolean)) {
+      arg_numbers[it.key()] = static_cast<double>(boolean ? 1 : 0);
+    } else if (it.value().GetAsDouble(&double_num)) {
+      arg_numbers[it.key()] = double_num;
+    } else {
+      LOG(WARNING) << "Value type of argument is not supported: " <<
+          static_cast<int>(it.value().GetType());
+      continue;  // Skip non-supported arguments.
+    }
+  }
+
+  return true;
+}
+
+double TraceEvent::GetAbsTimeToOtherEvent() const {
+  return fabs(other_event->timestamp - timestamp);
+}
+
+bool TraceEvent::GetArgAsString(const std::string& name,
+                                std::string* arg) const {
+  std::map<std::string, std::string>::const_iterator i = arg_strings.find(name);
+  if (i != arg_strings.end()) {
+    *arg = i->second;
+    return true;
+  }
+  return false;
+}
+
+bool TraceEvent::GetArgAsNumber(const std::string& name,
+                                double* arg) const {
+  std::map<std::string, double>::const_iterator i = arg_numbers.find(name);
+  if (i != arg_numbers.end()) {
+    *arg = i->second;
+    return true;
+  }
+  return false;
+}
+
+bool TraceEvent::HasStringArg(const std::string& name) const {
+  return (arg_strings.find(name) != arg_strings.end());
+}
+
+bool TraceEvent::HasNumberArg(const std::string& name) const {
+  return (arg_numbers.find(name) != arg_numbers.end());
+}
+
+std::string TraceEvent::GetKnownArgAsString(const std::string& name) const {
+  std::string arg_string;
+  bool result = GetArgAsString(name, &arg_string);
+  DCHECK(result);
+  return arg_string;
+}
+
+double TraceEvent::GetKnownArgAsDouble(const std::string& name) const {
+  double arg_double = 0;
+  bool result = GetArgAsNumber(name, &arg_double);
+  DCHECK(result);
+  return arg_double;
+}
+
+int TraceEvent::GetKnownArgAsInt(const std::string& name) const {
+  double arg_double = 0;
+  bool result = GetArgAsNumber(name, &arg_double);
+  DCHECK(result);
+  return static_cast<int>(arg_double);
+}
+
+bool TraceEvent::GetKnownArgAsBool(const std::string& name) const {
+  double arg_double = 0;
+  bool result = GetArgAsNumber(name, &arg_double);
+  DCHECK(result);
+  return (arg_double != 0.0);
+}
+
+// QueryNode
+
+QueryNode::QueryNode(const Query& query) : query_(query) {
+}
+
+QueryNode::~QueryNode() {
+}
+
+// Query
+
+Query::Query(TraceEventMember member)
+    : type_(QUERY_EVENT_MEMBER),
+      operator_(OP_INVALID),
+      member_(member),
+      number_(0),
+      is_pattern_(false) {
+}
+
+Query::Query(TraceEventMember member, const std::string& arg_name)
+    : type_(QUERY_EVENT_MEMBER),
+      operator_(OP_INVALID),
+      member_(member),
+      number_(0),
+      string_(arg_name),
+      is_pattern_(false) {
+}
+
+Query::Query(const Query& query)
+    : type_(query.type_),
+      operator_(query.operator_),
+      left_(query.left_),
+      right_(query.right_),
+      member_(query.member_),
+      number_(query.number_),
+      string_(query.string_),
+      is_pattern_(query.is_pattern_) {
+}
+
+Query::~Query() {
+}
+
+Query Query::String(const std::string& str) {
+  return Query(str);
+}
+
+Query Query::Double(double num) {
+  return Query(num);
+}
+
+Query Query::Int(int32 num) {
+  return Query(static_cast<double>(num));
+}
+
+Query Query::Uint(uint32 num) {
+  return Query(static_cast<double>(num));
+}
+
+Query Query::Bool(bool boolean) {
+  return Query(boolean ? 1.0 : 0.0);
+}
+
+Query Query::Phase(char phase) {
+  return Query(static_cast<double>(phase));
+}
+
+Query Query::Pattern(const std::string& pattern) {
+  Query query(pattern);
+  query.is_pattern_ = true;
+  return query;
+}
+
+bool Query::Evaluate(const TraceEvent& event) const {
+  // First check for values that can convert to bool.
+
+  // double is true if != 0:
+  double bool_value = 0.0;
+  bool is_bool = GetAsDouble(event, &bool_value);
+  if (is_bool)
+    return (bool_value != 0.0);
+
+  // string is true if it is non-empty:
+  std::string str_value;
+  bool is_str = GetAsString(event, &str_value);
+  if (is_str)
+    return !str_value.empty();
+
+  DCHECK_EQ(QUERY_BOOLEAN_OPERATOR, type_)
+      << "Invalid query: missing boolean expression";
+  DCHECK(left_.get());
+  DCHECK(right_.get() || is_unary_operator());
+
+  if (is_comparison_operator()) {
+    DCHECK(left().is_value() && right().is_value())
+        << "Invalid query: comparison operator used between event member and "
+           "value.";
+    bool compare_result = false;
+    if (CompareAsDouble(event, &compare_result))
+      return compare_result;
+    if (CompareAsString(event, &compare_result))
+      return compare_result;
+    return false;
+  }
+  // It's a logical operator.
+  switch (operator_) {
+    case OP_AND:
+      return left().Evaluate(event) && right().Evaluate(event);
+    case OP_OR:
+      return left().Evaluate(event) || right().Evaluate(event);
+    case OP_NOT:
+      return !left().Evaluate(event);
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
+bool Query::CompareAsDouble(const TraceEvent& event, bool* result) const {
+  double lhs, rhs;
+  if (!left().GetAsDouble(event, &lhs) || !right().GetAsDouble(event, &rhs))
+    return false;
+  switch (operator_) {
+    case OP_EQ:
+      *result = (lhs == rhs);
+      return true;
+    case OP_NE:
+      *result = (lhs != rhs);
+      return true;
+    case OP_LT:
+      *result = (lhs < rhs);
+      return true;
+    case OP_LE:
+      *result = (lhs <= rhs);
+      return true;
+    case OP_GT:
+      *result = (lhs > rhs);
+      return true;
+    case OP_GE:
+      *result = (lhs >= rhs);
+      return true;
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
+bool Query::CompareAsString(const TraceEvent& event, bool* result) const {
+  std::string lhs, rhs;
+  if (!left().GetAsString(event, &lhs) || !right().GetAsString(event, &rhs))
+    return false;
+  switch (operator_) {
+    case OP_EQ:
+      if (right().is_pattern_)
+        *result = MatchPattern(lhs, rhs);
+      else if (left().is_pattern_)
+        *result = MatchPattern(rhs, lhs);
+      else
+        *result = (lhs == rhs);
+      return true;
+    case OP_NE:
+      if (right().is_pattern_)
+        *result = !MatchPattern(lhs, rhs);
+      else if (left().is_pattern_)
+        *result = !MatchPattern(rhs, lhs);
+      else
+        *result = (lhs != rhs);
+      return true;
+    case OP_LT:
+      *result = (lhs < rhs);
+      return true;
+    case OP_LE:
+      *result = (lhs <= rhs);
+      return true;
+    case OP_GT:
+      *result = (lhs > rhs);
+      return true;
+    case OP_GE:
+      *result = (lhs >= rhs);
+      return true;
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
+bool Query::EvaluateArithmeticOperator(const TraceEvent& event,
+                                       double* num) const {
+  DCHECK_EQ(QUERY_ARITHMETIC_OPERATOR, type_);
+  DCHECK(left_.get());
+  DCHECK(right_.get() || is_unary_operator());
+
+  double lhs = 0, rhs = 0;
+  if (!left().GetAsDouble(event, &lhs))
+    return false;
+  if (!is_unary_operator() && !right().GetAsDouble(event, &rhs))
+    return false;
+
+  switch (operator_) {
+    case OP_ADD:
+      *num = lhs + rhs;
+      return true;
+    case OP_SUB:
+      *num = lhs - rhs;
+      return true;
+    case OP_MUL:
+      *num = lhs * rhs;
+      return true;
+    case OP_DIV:
+      *num = lhs / rhs;
+      return true;
+    case OP_MOD:
+      *num = static_cast<double>(static_cast<int64>(lhs) %
+                                 static_cast<int64>(rhs));
+      return true;
+    case OP_NEGATE:
+      *num = -lhs;
+      return true;
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
+bool Query::GetAsDouble(const TraceEvent& event, double* num) const {
+  switch (type_) {
+    case QUERY_ARITHMETIC_OPERATOR:
+      return EvaluateArithmeticOperator(event, num);
+    case QUERY_EVENT_MEMBER:
+      return GetMemberValueAsDouble(event, num);
+    case QUERY_NUMBER:
+      *num = number_;
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool Query::GetAsString(const TraceEvent& event, std::string* str) const {
+  switch (type_) {
+    case QUERY_EVENT_MEMBER:
+      return GetMemberValueAsString(event, str);
+    case QUERY_STRING:
+      *str = string_;
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool Query::GetMemberValueAsDouble(const TraceEvent& event,
+                                   double* num) const {
+  DCHECK_EQ(QUERY_EVENT_MEMBER, type_);
+
+  // This could be a request for a member of |event| or a member of |event|'s
+  // associated event. Store the target event in the_event:
+  const TraceEvent* the_event = (member_ < OTHER_PID) ?
+      &event : event.other_event;
+
+  // Request for member of associated event, but there is no associated event.
+  if (!the_event)
+    return false;
+
+  switch (member_) {
+    case EVENT_PID:
+    case OTHER_PID:
+      *num = static_cast<double>(the_event->thread.process_id);
+      return true;
+    case EVENT_TID:
+    case OTHER_TID:
+      *num = static_cast<double>(the_event->thread.thread_id);
+      return true;
+    case EVENT_TIME:
+    case OTHER_TIME:
+      *num = the_event->timestamp;
+      return true;
+    case EVENT_DURATION:
+      if (!the_event->has_other_event())
+        return false;
+      *num = the_event->GetAbsTimeToOtherEvent();
+      return true;
+    case EVENT_COMPLETE_DURATION:
+      if (the_event->phase != TRACE_EVENT_PHASE_COMPLETE)
+        return false;
+      *num = the_event->duration;
+      return true;
+    case EVENT_PHASE:
+    case OTHER_PHASE:
+      *num = static_cast<double>(the_event->phase);
+      return true;
+    case EVENT_HAS_STRING_ARG:
+    case OTHER_HAS_STRING_ARG:
+      *num = (the_event->HasStringArg(string_) ? 1.0 : 0.0);
+      return true;
+    case EVENT_HAS_NUMBER_ARG:
+    case OTHER_HAS_NUMBER_ARG:
+      *num = (the_event->HasNumberArg(string_) ? 1.0 : 0.0);
+      return true;
+    case EVENT_ARG:
+    case OTHER_ARG: {
+      // Search for the argument name and return its value if found.
+      std::map<std::string, double>::const_iterator num_i =
+          the_event->arg_numbers.find(string_);
+      if (num_i == the_event->arg_numbers.end())
+        return false;
+      *num = num_i->second;
+      return true;
+    }
+    case EVENT_HAS_OTHER:
+      // return 1.0 (true) if the other event exists
+      *num = event.other_event ? 1.0 : 0.0;
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool Query::GetMemberValueAsString(const TraceEvent& event,
+                                   std::string* str) const {
+  DCHECK_EQ(QUERY_EVENT_MEMBER, type_);
+
+  // This could be a request for a member of |event| or a member of |event|'s
+  // associated event. Store the target event in the_event:
+  const TraceEvent* the_event = (member_ < OTHER_PID) ?
+      &event : event.other_event;
+
+  // Request for member of associated event, but there is no associated event.
+  if (!the_event)
+    return false;
+
+  switch (member_) {
+    case EVENT_CATEGORY:
+    case OTHER_CATEGORY:
+      *str = the_event->category;
+      return true;
+    case EVENT_NAME:
+    case OTHER_NAME:
+      *str = the_event->name;
+      return true;
+    case EVENT_ID:
+    case OTHER_ID:
+      *str = the_event->id;
+      return true;
+    case EVENT_ARG:
+    case OTHER_ARG: {
+      // Search for the argument name and return its value if found.
+      std::map<std::string, std::string>::const_iterator str_i =
+          the_event->arg_strings.find(string_);
+      if (str_i == the_event->arg_strings.end())
+        return false;
+      *str = str_i->second;
+      return true;
+    }
+    default:
+      return false;
+  }
+}
+
+Query::Query(const std::string& str)
+    : type_(QUERY_STRING),
+      operator_(OP_INVALID),
+      member_(EVENT_INVALID),
+      number_(0),
+      string_(str),
+      is_pattern_(false) {
+}
+
+Query::Query(double num)
+    : type_(QUERY_NUMBER),
+      operator_(OP_INVALID),
+      member_(EVENT_INVALID),
+      number_(num),
+      is_pattern_(false) {
+}
+const Query& Query::left() const {
+  return left_->query();
+}
+
+const Query& Query::right() const {
+  return right_->query();
+}
+
+Query Query::operator==(const Query& rhs) const {
+  return Query(*this, rhs, OP_EQ);
+}
+
+Query Query::operator!=(const Query& rhs) const {
+  return Query(*this, rhs, OP_NE);
+}
+
+Query Query::operator<(const Query& rhs) const {
+  return Query(*this, rhs, OP_LT);
+}
+
+Query Query::operator<=(const Query& rhs) const {
+  return Query(*this, rhs, OP_LE);
+}
+
+Query Query::operator>(const Query& rhs) const {
+  return Query(*this, rhs, OP_GT);
+}
+
+Query Query::operator>=(const Query& rhs) const {
+  return Query(*this, rhs, OP_GE);
+}
+
+Query Query::operator&&(const Query& rhs) const {
+  return Query(*this, rhs, OP_AND);
+}
+
+Query Query::operator||(const Query& rhs) const {
+  return Query(*this, rhs, OP_OR);
+}
+
+Query Query::operator!() const {
+  return Query(*this, OP_NOT);
+}
+
+Query Query::operator+(const Query& rhs) const {
+  return Query(*this, rhs, OP_ADD);
+}
+
+Query Query::operator-(const Query& rhs) const {
+  return Query(*this, rhs, OP_SUB);
+}
+
+Query Query::operator*(const Query& rhs) const {
+  return Query(*this, rhs, OP_MUL);
+}
+
+Query Query::operator/(const Query& rhs) const {
+  return Query(*this, rhs, OP_DIV);
+}
+
+Query Query::operator%(const Query& rhs) const {
+  return Query(*this, rhs, OP_MOD);
+}
+
+Query Query::operator-() const {
+  return Query(*this, OP_NEGATE);
+}
+
+
+Query::Query(const Query& left, const Query& right, Operator binary_op)
+    : operator_(binary_op),
+      left_(new QueryNode(left)),
+      right_(new QueryNode(right)),
+      member_(EVENT_INVALID),
+      number_(0) {
+  type_ = (binary_op < OP_ADD ?
+           QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR);
+}
+
+Query::Query(const Query& left, Operator unary_op)
+    : operator_(unary_op),
+      left_(new QueryNode(left)),
+      member_(EVENT_INVALID),
+      number_(0) {
+  type_ = (unary_op < OP_ADD ?
+           QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR);
+}
+
+namespace {
+
+// Search |events| for |query| and add matches to |output|.
+size_t FindMatchingEvents(const std::vector<TraceEvent>& events,
+                          const Query& query,
+                          TraceEventVector* output,
+                          bool ignore_metadata_events) {
+  for (size_t i = 0; i < events.size(); ++i) {
+    if (ignore_metadata_events && events[i].phase == TRACE_EVENT_PHASE_METADATA)
+      continue;
+    if (query.Evaluate(events[i]))
+      output->push_back(&events[i]);
+  }
+  return output->size();
+}
+
+bool ParseEventsFromJson(const std::string& json,
+                         std::vector<TraceEvent>* output) {
+  scoped_ptr<base::Value> root;
+  root.reset(base::JSONReader::Read(json));
+
+  base::ListValue* root_list = NULL;
+  if (!root.get() || !root->GetAsList(&root_list))
+    return false;
+
+  for (size_t i = 0; i < root_list->GetSize(); ++i) {
+    base::Value* item = NULL;
+    if (root_list->Get(i, &item)) {
+      TraceEvent event;
+      if (event.SetFromJSON(item))
+        output->push_back(event);
+      else
+        return false;
+    }
+  }
+
+  return true;
+}
+
+}  // namespace
+
+// TraceAnalyzer
+
+TraceAnalyzer::TraceAnalyzer()
+    : ignore_metadata_events_(false),
+      allow_assocation_changes_(true) {}
+
+TraceAnalyzer::~TraceAnalyzer() {
+}
+
+// static
+TraceAnalyzer* TraceAnalyzer::Create(const std::string& json_events) {
+  scoped_ptr<TraceAnalyzer> analyzer(new TraceAnalyzer());
+  if (analyzer->SetEvents(json_events))
+    return analyzer.release();
+  return NULL;
+}
+
+bool TraceAnalyzer::SetEvents(const std::string& json_events) {
+  raw_events_.clear();
+  if (!ParseEventsFromJson(json_events, &raw_events_))
+    return false;
+  std::stable_sort(raw_events_.begin(), raw_events_.end());
+  ParseMetadata();
+  return true;
+}
+
+void TraceAnalyzer::AssociateBeginEndEvents() {
+  using trace_analyzer::Query;
+
+  Query begin(Query::EventPhaseIs(TRACE_EVENT_PHASE_BEGIN));
+  Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_END));
+  Query match(Query::EventName() == Query::OtherName() &&
+              Query::EventCategory() == Query::OtherCategory() &&
+              Query::EventTid() == Query::OtherTid() &&
+              Query::EventPid() == Query::OtherPid());
+
+  AssociateEvents(begin, end, match);
+}
+
+void TraceAnalyzer::AssociateAsyncBeginEndEvents() {
+  using trace_analyzer::Query;
+
+  Query begin(
+      Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_BEGIN) ||
+      Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO) ||
+      Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST));
+  Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_END) ||
+            Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO) ||
+            Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST));
+  Query match(Query::EventName() == Query::OtherName() &&
+              Query::EventCategory() == Query::OtherCategory() &&
+              Query::EventId() == Query::OtherId());
+
+  AssociateEvents(begin, end, match);
+}
+
+void TraceAnalyzer::AssociateEvents(const Query& first,
+                                    const Query& second,
+                                    const Query& match) {
+  DCHECK(allow_assocation_changes_)
+      << "AssociateEvents not allowed after FindEvents";
+
+  // Search for matching begin/end event pairs. When a matching end is found,
+  // it is associated with the begin event.
+  std::vector<TraceEvent*> begin_stack;
+  for (size_t event_index = 0; event_index < raw_events_.size();
+       ++event_index) {
+
+    TraceEvent& this_event = raw_events_[event_index];
+
+    if (second.Evaluate(this_event)) {
+      // Search stack for matching begin, starting from end.
+      for (int stack_index = static_cast<int>(begin_stack.size()) - 1;
+           stack_index >= 0; --stack_index) {
+        TraceEvent& begin_event = *begin_stack[stack_index];
+
+        // Temporarily set other to test against the match query.
+        const TraceEvent* other_backup = begin_event.other_event;
+        begin_event.other_event = &this_event;
+        if (match.Evaluate(begin_event)) {
+          // Found a matching begin/end pair.
+          // Erase the matching begin event index from the stack.
+          begin_stack.erase(begin_stack.begin() + stack_index);
+          break;
+        }
+
+        // Not a match, restore original other and continue.
+        begin_event.other_event = other_backup;
+      }
+    }
+    // Even if this_event is a |second| event that has matched an earlier
+    // |first| event, it can still also be a |first| event and be associated
+    // with a later |second| event.
+    if (first.Evaluate(this_event)) {
+      begin_stack.push_back(&this_event);
+    }
+  }
+}
+
+void TraceAnalyzer::MergeAssociatedEventArgs() {
+  for (size_t i = 0; i < raw_events_.size(); ++i) {
+    // Merge all associated events with the first event.
+    const TraceEvent* other = raw_events_[i].other_event;
+    // Avoid looping by keeping set of encountered TraceEvents.
+    std::set<const TraceEvent*> encounters;
+    encounters.insert(&raw_events_[i]);
+    while (other && encounters.find(other) == encounters.end()) {
+      encounters.insert(other);
+      raw_events_[i].arg_numbers.insert(
+          other->arg_numbers.begin(),
+          other->arg_numbers.end());
+      raw_events_[i].arg_strings.insert(
+          other->arg_strings.begin(),
+          other->arg_strings.end());
+      other = other->other_event;
+    }
+  }
+}
+
+size_t TraceAnalyzer::FindEvents(const Query& query, TraceEventVector* output) {
+  allow_assocation_changes_ = false;
+  output->clear();
+  return FindMatchingEvents(
+      raw_events_, query, output, ignore_metadata_events_);
+}
+
+const TraceEvent* TraceAnalyzer::FindFirstOf(const Query& query) {
+  TraceEventVector output;
+  if (FindEvents(query, &output) > 0)
+    return output.front();
+  return NULL;
+}
+
+const TraceEvent* TraceAnalyzer::FindLastOf(const Query& query) {
+  TraceEventVector output;
+  if (FindEvents(query, &output) > 0)
+    return output.back();
+  return NULL;
+}
+
+const std::string& TraceAnalyzer::GetThreadName(
+    const TraceEvent::ProcessThreadID& thread) {
+  // If thread is not found, just add and return empty string.
+  return thread_names_[thread];
+}
+
+void TraceAnalyzer::ParseMetadata() {
+  for (size_t i = 0; i < raw_events_.size(); ++i) {
+    TraceEvent& this_event = raw_events_[i];
+    // Check for thread name metadata.
+    if (this_event.phase != TRACE_EVENT_PHASE_METADATA ||
+        this_event.name != "thread_name")
+      continue;
+    std::map<std::string, std::string>::const_iterator string_it =
+        this_event.arg_strings.find("name");
+    if (string_it != this_event.arg_strings.end())
+      thread_names_[this_event.thread] = string_it->second;
+  }
+}
+
+// TraceEventVector utility functions.
+
+bool GetRateStats(const TraceEventVector& events,
+                  RateStats* stats,
+                  const RateStatsOptions* options) {
+  DCHECK(stats);
+  // Need at least 3 events to calculate rate stats.
+  const size_t kMinEvents = 3;
+  if (events.size() < kMinEvents) {
+    LOG(ERROR) << "Not enough events: " << events.size();
+    return false;
+  }
+
+  std::vector<double> deltas;
+  size_t num_deltas = events.size() - 1;
+  for (size_t i = 0; i < num_deltas; ++i) {
+    double delta = events.at(i + 1)->timestamp - events.at(i)->timestamp;
+    if (delta < 0.0) {
+      LOG(ERROR) << "Events are out of order";
+      return false;
+    }
+    deltas.push_back(delta);
+  }
+
+  std::sort(deltas.begin(), deltas.end());
+
+  if (options) {
+    if (options->trim_min + options->trim_max > events.size() - kMinEvents) {
+      LOG(ERROR) << "Attempt to trim too many events";
+      return false;
+    }
+    deltas.erase(deltas.begin(), deltas.begin() + options->trim_min);
+    deltas.erase(deltas.end() - options->trim_max, deltas.end());
+  }
+
+  num_deltas = deltas.size();
+  double delta_sum = 0.0;
+  for (size_t i = 0; i < num_deltas; ++i)
+    delta_sum += deltas[i];
+
+  stats->min_us = *std::min_element(deltas.begin(), deltas.end());
+  stats->max_us = *std::max_element(deltas.begin(), deltas.end());
+  stats->mean_us = delta_sum / static_cast<double>(num_deltas);
+
+  double sum_mean_offsets_squared = 0.0;
+  for (size_t i = 0; i < num_deltas; ++i) {
+    double offset = fabs(deltas[i] - stats->mean_us);
+    sum_mean_offsets_squared += offset * offset;
+  }
+  stats->standard_deviation_us =
+      sqrt(sum_mean_offsets_squared / static_cast<double>(num_deltas - 1));
+
+  return true;
+}
+
+bool FindFirstOf(const TraceEventVector& events,
+                 const Query& query,
+                 size_t position,
+                 size_t* return_index) {
+  DCHECK(return_index);
+  for (size_t i = position; i < events.size(); ++i) {
+    if (query.Evaluate(*events[i])) {
+      *return_index = i;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool FindLastOf(const TraceEventVector& events,
+                const Query& query,
+                size_t position,
+                size_t* return_index) {
+  DCHECK(return_index);
+  for (size_t i = std::min(position + 1, events.size()); i != 0; --i) {
+    if (query.Evaluate(*events[i - 1])) {
+      *return_index = i - 1;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool FindClosest(const TraceEventVector& events,
+                 const Query& query,
+                 size_t position,
+                 size_t* return_closest,
+                 size_t* return_second_closest) {
+  DCHECK(return_closest);
+  if (events.empty() || position >= events.size())
+    return false;
+  size_t closest = events.size();
+  size_t second_closest = events.size();
+  for (size_t i = 0; i < events.size(); ++i) {
+    if (!query.Evaluate(*events.at(i)))
+      continue;
+    if (closest == events.size()) {
+      closest = i;
+      continue;
+    }
+    if (fabs(events.at(i)->timestamp - events.at(position)->timestamp) <
+        fabs(events.at(closest)->timestamp - events.at(position)->timestamp)) {
+      second_closest = closest;
+      closest = i;
+    } else if (second_closest == events.size()) {
+      second_closest = i;
+    }
+  }
+
+  if (closest < events.size() &&
+      (!return_second_closest || second_closest < events.size())) {
+    *return_closest = closest;
+    if (return_second_closest)
+      *return_second_closest = second_closest;
+    return true;
+  }
+
+  return false;
+}
+
+size_t CountMatches(const TraceEventVector& events,
+                    const Query& query,
+                    size_t begin_position,
+                    size_t end_position) {
+  if (begin_position >= events.size())
+    return 0u;
+  end_position = (end_position < events.size()) ? end_position : events.size();
+  size_t count = 0u;
+  for (size_t i = begin_position; i < end_position; ++i) {
+    if (query.Evaluate(*events.at(i)))
+      ++count;
+  }
+  return count;
+}
+
+}  // namespace trace_analyzer
diff --git a/base/test/trace_event_analyzer.h b/base/test/trace_event_analyzer.h
new file mode 100644
index 0000000..2d3692d
--- /dev/null
+++ b/base/test/trace_event_analyzer.h
@@ -0,0 +1,705 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Use trace_analyzer::Query and trace_analyzer::TraceAnalyzer to search for
+// specific trace events that were generated by the trace_event.h API.
+//
+// Basic procedure:
+// - Get trace events JSON string from base::debug::TraceLog.
+// - Create TraceAnalyzer with JSON string.
+// - Call TraceAnalyzer::AssociateBeginEndEvents (optional).
+// - Call TraceAnalyzer::AssociateEvents (zero or more times).
+// - Call TraceAnalyzer::FindEvents with queries to find specific events.
+//
+// A Query is a boolean expression tree that evaluates to true or false for a
+// given trace event. Queries can be combined into a tree using boolean,
+// arithmetic and comparison operators that refer to data of an individual trace
+// event.
+//
+// The events are returned as trace_analyzer::TraceEvent objects.
+// TraceEvent contains a single trace event's data, as well as a pointer to
+// a related trace event. The related trace event is typically the matching end
+// of a begin event or the matching begin of an end event.
+//
+// The following examples use this basic setup code to construct TraceAnalyzer
+// with the json trace string retrieved from TraceLog and construct an event
+// vector for retrieving events:
+//
+// TraceAnalyzer analyzer(json_events);
+// TraceEventVector events;
+//
+// EXAMPLE 1: Find events named "my_event".
+//
+// analyzer.FindEvents(Query(EVENT_NAME) == "my_event", &events);
+//
+// EXAMPLE 2: Find begin events named "my_event" with duration > 1 second.
+//
+// Query q = (Query(EVENT_NAME) == Query::String("my_event") &&
+//            Query(EVENT_PHASE) == Query::Phase(TRACE_EVENT_PHASE_BEGIN) &&
+//            Query(EVENT_DURATION) > Query::Double(1000000.0));
+// analyzer.FindEvents(q, &events);
+//
+// EXAMPLE 3: Associating event pairs across threads.
+//
+// If the test needs to analyze something that starts and ends on different
+// threads, the test needs to use INSTANT events. The typical procedure is to
+// specify the same unique ID as a TRACE_EVENT argument on both the start and
+// finish INSTANT events. Then use the following procedure to associate those
+// events.
+//
+// Step 1: instrument code with custom begin/end trace events.
+//   [Thread 1 tracing code]
+//   TRACE_EVENT_INSTANT1("test_latency", "timing1_begin", "id", 3);
+//   [Thread 2 tracing code]
+//   TRACE_EVENT_INSTANT1("test_latency", "timing1_end", "id", 3);
+//
+// Step 2: associate these custom begin/end pairs.
+//   Query begin(Query(EVENT_NAME) == Query::String("timing1_begin"));
+//   Query end(Query(EVENT_NAME) == Query::String("timing1_end"));
+//   Query match(Query(EVENT_ARG, "id") == Query(OTHER_ARG, "id"));
+//   analyzer.AssociateEvents(begin, end, match);
+//
+// Step 3: search for "timing1_begin" events with existing other event.
+//   Query q = (Query(EVENT_NAME) == Query::String("timing1_begin") &&
+//              Query(EVENT_HAS_OTHER));
+//   analyzer.FindEvents(q, &events);
+//
+// Step 4: analyze events, such as checking durations.
+//   for (size_t i = 0; i < events.size(); ++i) {
+//     double duration;
+//     EXPECT_TRUE(events[i].GetAbsTimeToOtherEvent(&duration));
+//     EXPECT_LT(duration, 1000000.0/60.0); // expect less than 1/60 second.
+//   }
+
+
+#ifndef BASE_TEST_TRACE_EVENT_ANALYZER_H_
+#define BASE_TEST_TRACE_EVENT_ANALYZER_H_
+
+#include <map>
+
+#include "base/debug/trace_event.h"
+#include "base/memory/ref_counted.h"
+
+namespace base {
+class Value;
+}
+
+namespace trace_analyzer {
+class QueryNode;
+
+// trace_analyzer::TraceEvent is a more convenient form of the
+// base::debug::TraceEvent class to make tracing-based tests easier to write.
+struct TraceEvent {
+  // ProcessThreadID contains a Process ID and Thread ID.
+  struct ProcessThreadID {
+    ProcessThreadID() : process_id(0), thread_id(0) {}
+    ProcessThreadID(int process_id, int thread_id)
+        : process_id(process_id), thread_id(thread_id) {}
+    bool operator< (const ProcessThreadID& rhs) const {
+      if (process_id != rhs.process_id)
+        return process_id < rhs.process_id;
+      return thread_id < rhs.thread_id;
+    }
+    int process_id;
+    int thread_id;
+  };
+
+  TraceEvent();
+  ~TraceEvent();
+
+  bool SetFromJSON(const base::Value* event_value) WARN_UNUSED_RESULT;
+
+  bool operator< (const TraceEvent& rhs) const {
+    return timestamp < rhs.timestamp;
+  }
+
+  bool has_other_event() const { return other_event; }
+
+  // Returns absolute duration in microseconds between this event and other
+  // event. Must have already verified that other_event exists by
+  // Query(EVENT_HAS_OTHER) or by calling has_other_event().
+  double GetAbsTimeToOtherEvent() const;
+
+  // Return the argument value if it exists and it is a string.
+  bool GetArgAsString(const std::string& name, std::string* arg) const;
+  // Return the argument value if it exists and it is a number.
+  bool GetArgAsNumber(const std::string& name, double* arg) const;
+
+  // Check if argument exists and is string.
+  bool HasStringArg(const std::string& name) const;
+  // Check if argument exists and is number (double, int or bool).
+  bool HasNumberArg(const std::string& name) const;
+
+  // Get known existing arguments as specific types.
+  // Useful when you have already queried the argument with
+  // Query(HAS_NUMBER_ARG) or Query(HAS_STRING_ARG).
+  std::string GetKnownArgAsString(const std::string& name) const;
+  double GetKnownArgAsDouble(const std::string& name) const;
+  int GetKnownArgAsInt(const std::string& name) const;
+  bool GetKnownArgAsBool(const std::string& name) const;
+
+  // Process ID and Thread ID.
+  ProcessThreadID thread;
+
+  // Time since epoch in microseconds.
+  // Stored as double to match its JSON representation.
+  double timestamp;
+
+  double duration;
+
+  char phase;
+
+  std::string category;
+
+  std::string name;
+
+  std::string id;
+
+  // All numbers and bool values from TraceEvent args are cast to double.
+  // bool becomes 1.0 (true) or 0.0 (false).
+  std::map<std::string, double> arg_numbers;
+
+  std::map<std::string, std::string> arg_strings;
+
+  // The other event associated with this event (or NULL).
+  const TraceEvent* other_event;
+};
+
+typedef std::vector<const TraceEvent*> TraceEventVector;
+
+class Query {
+ public:
+  Query(const Query& query);
+
+  ~Query();
+
+  ////////////////////////////////////////////////////////////////
+  // Query literal values
+
+  // Compare with the given string.
+  static Query String(const std::string& str);
+
+  // Compare with the given number.
+  static Query Double(double num);
+  static Query Int(int32 num);
+  static Query Uint(uint32 num);
+
+  // Compare with the given bool.
+  static Query Bool(bool boolean);
+
+  // Compare with the given phase.
+  static Query Phase(char phase);
+
+  // Compare with the given string pattern. Only works with == and != operators.
+  // Example: Query(EVENT_NAME) == Query::Pattern("MyEvent*")
+  static Query Pattern(const std::string& pattern);
+
+  ////////////////////////////////////////////////////////////////
+  // Query event members
+
+  static Query EventPid() { return Query(EVENT_PID); }
+
+  static Query EventTid() { return Query(EVENT_TID); }
+
+  // Return the timestamp of the event in microseconds since epoch.
+  static Query EventTime() { return Query(EVENT_TIME); }
+
+  // Return the absolute time between event and other event in microseconds.
+  // Only works if Query::EventHasOther() == true.
+  static Query EventDuration() { return Query(EVENT_DURATION); }
+
+  // Return the duration of a COMPLETE event.
+  static Query EventCompleteDuration() {
+    return Query(EVENT_COMPLETE_DURATION);
+  }
+
+  static Query EventPhase() { return Query(EVENT_PHASE); }
+
+  static Query EventCategory() { return Query(EVENT_CATEGORY); }
+
+  static Query EventName() { return Query(EVENT_NAME); }
+
+  static Query EventId() { return Query(EVENT_ID); }
+
+  static Query EventPidIs(int process_id) {
+    return Query(EVENT_PID) == Query::Int(process_id);
+  }
+
+  static Query EventTidIs(int thread_id) {
+    return Query(EVENT_TID) == Query::Int(thread_id);
+  }
+
+  static Query EventThreadIs(const TraceEvent::ProcessThreadID& thread) {
+    return EventPidIs(thread.process_id) && EventTidIs(thread.thread_id);
+  }
+
+  static Query EventTimeIs(double timestamp) {
+    return Query(EVENT_TIME) == Query::Double(timestamp);
+  }
+
+  static Query EventDurationIs(double duration) {
+    return Query(EVENT_DURATION) == Query::Double(duration);
+  }
+
+  static Query EventPhaseIs(char phase) {
+    return Query(EVENT_PHASE) == Query::Phase(phase);
+  }
+
+  static Query EventCategoryIs(const std::string& category) {
+    return Query(EVENT_CATEGORY) == Query::String(category);
+  }
+
+  static Query EventNameIs(const std::string& name) {
+    return Query(EVENT_NAME) == Query::String(name);
+  }
+
+  static Query EventIdIs(const std::string& id) {
+    return Query(EVENT_ID) == Query::String(id);
+  }
+
+  // Evaluates to true if arg exists and is a string.
+  static Query EventHasStringArg(const std::string& arg_name) {
+    return Query(EVENT_HAS_STRING_ARG, arg_name);
+  }
+
+  // Evaluates to true if arg exists and is a number.
+  // Number arguments include types double, int and bool.
+  static Query EventHasNumberArg(const std::string& arg_name) {
+    return Query(EVENT_HAS_NUMBER_ARG, arg_name);
+  }
+
+  // Evaluates to arg value (string or number).
+  static Query EventArg(const std::string& arg_name) {
+    return Query(EVENT_ARG, arg_name);
+  }
+
+  // Return true if associated event exists.
+  static Query EventHasOther() { return Query(EVENT_HAS_OTHER); }
+
+  // Access the associated other_event's members:
+
+  static Query OtherPid() { return Query(OTHER_PID); }
+
+  static Query OtherTid() { return Query(OTHER_TID); }
+
+  static Query OtherTime() { return Query(OTHER_TIME); }
+
+  static Query OtherPhase() { return Query(OTHER_PHASE); }
+
+  static Query OtherCategory() { return Query(OTHER_CATEGORY); }
+
+  static Query OtherName() { return Query(OTHER_NAME); }
+
+  static Query OtherId() { return Query(OTHER_ID); }
+
+  static Query OtherPidIs(int process_id) {
+    return Query(OTHER_PID) == Query::Int(process_id);
+  }
+
+  static Query OtherTidIs(int thread_id) {
+    return Query(OTHER_TID) == Query::Int(thread_id);
+  }
+
+  static Query OtherThreadIs(const TraceEvent::ProcessThreadID& thread) {
+    return OtherPidIs(thread.process_id) && OtherTidIs(thread.thread_id);
+  }
+
+  static Query OtherTimeIs(double timestamp) {
+    return Query(OTHER_TIME) == Query::Double(timestamp);
+  }
+
+  static Query OtherPhaseIs(char phase) {
+    return Query(OTHER_PHASE) == Query::Phase(phase);
+  }
+
+  static Query OtherCategoryIs(const std::string& category) {
+    return Query(OTHER_CATEGORY) == Query::String(category);
+  }
+
+  static Query OtherNameIs(const std::string& name) {
+    return Query(OTHER_NAME) == Query::String(name);
+  }
+
+  static Query OtherIdIs(const std::string& id) {
+    return Query(OTHER_ID) == Query::String(id);
+  }
+
+  // Evaluates to true if arg exists and is a string.
+  static Query OtherHasStringArg(const std::string& arg_name) {
+    return Query(OTHER_HAS_STRING_ARG, arg_name);
+  }
+
+  // Evaluates to true if arg exists and is a number.
+  // Number arguments include types double, int and bool.
+  static Query OtherHasNumberArg(const std::string& arg_name) {
+    return Query(OTHER_HAS_NUMBER_ARG, arg_name);
+  }
+
+  // Evaluates to arg value (string or number).
+  static Query OtherArg(const std::string& arg_name) {
+    return Query(OTHER_ARG, arg_name);
+  }
+
+  ////////////////////////////////////////////////////////////////
+  // Common queries:
+
+  // Find BEGIN events that have a corresponding END event.
+  static Query MatchBeginWithEnd() {
+    return (Query(EVENT_PHASE) == Query::Phase(TRACE_EVENT_PHASE_BEGIN)) &&
+           Query(EVENT_HAS_OTHER);
+  }
+
+  // Find COMPLETE events.
+  static Query MatchComplete() {
+    return (Query(EVENT_PHASE) == Query::Phase(TRACE_EVENT_PHASE_COMPLETE));
+  }
+
+  // Find ASYNC_BEGIN events that have a corresponding ASYNC_END event.
+  static Query MatchAsyncBeginWithNext() {
+    return (Query(EVENT_PHASE) ==
+            Query::Phase(TRACE_EVENT_PHASE_ASYNC_BEGIN)) &&
+           Query(EVENT_HAS_OTHER);
+  }
+
+  // Find BEGIN events of given |name| which also have associated END events.
+  static Query MatchBeginName(const std::string& name) {
+    return (Query(EVENT_NAME) == Query(name)) && MatchBeginWithEnd();
+  }
+
+  // Find COMPLETE events of given |name|.
+  static Query MatchCompleteName(const std::string& name) {
+    return (Query(EVENT_NAME) == Query(name)) && MatchComplete();
+  }
+
+  // Match given Process ID and Thread ID.
+  static Query MatchThread(const TraceEvent::ProcessThreadID& thread) {
+    return (Query(EVENT_PID) == Query::Int(thread.process_id)) &&
+           (Query(EVENT_TID) == Query::Int(thread.thread_id));
+  }
+
+  // Match event pair that spans multiple threads.
+  static Query MatchCrossThread() {
+    return (Query(EVENT_PID) != Query(OTHER_PID)) ||
+           (Query(EVENT_TID) != Query(OTHER_TID));
+  }
+
+  ////////////////////////////////////////////////////////////////
+  // Operators:
+
+  // Boolean operators:
+  Query operator==(const Query& rhs) const;
+  Query operator!=(const Query& rhs) const;
+  Query operator< (const Query& rhs) const;
+  Query operator<=(const Query& rhs) const;
+  Query operator> (const Query& rhs) const;
+  Query operator>=(const Query& rhs) const;
+  Query operator&&(const Query& rhs) const;
+  Query operator||(const Query& rhs) const;
+  Query operator!() const;
+
+  // Arithmetic operators:
+  // Following operators are applied to double arguments:
+  Query operator+(const Query& rhs) const;
+  Query operator-(const Query& rhs) const;
+  Query operator*(const Query& rhs) const;
+  Query operator/(const Query& rhs) const;
+  Query operator-() const;
+  // Mod operates on int64 args (doubles are casted to int64 beforehand):
+  Query operator%(const Query& rhs) const;
+
+  // Return true if the given event matches this query tree.
+  // This is a recursive method that walks the query tree.
+  bool Evaluate(const TraceEvent& event) const;
+
+ private:
+  enum TraceEventMember {
+    EVENT_INVALID,
+    EVENT_PID,
+    EVENT_TID,
+    EVENT_TIME,
+    EVENT_DURATION,
+    EVENT_COMPLETE_DURATION,
+    EVENT_PHASE,
+    EVENT_CATEGORY,
+    EVENT_NAME,
+    EVENT_ID,
+    EVENT_HAS_STRING_ARG,
+    EVENT_HAS_NUMBER_ARG,
+    EVENT_ARG,
+    EVENT_HAS_OTHER,
+    OTHER_PID,
+    OTHER_TID,
+    OTHER_TIME,
+    OTHER_PHASE,
+    OTHER_CATEGORY,
+    OTHER_NAME,
+    OTHER_ID,
+    OTHER_HAS_STRING_ARG,
+    OTHER_HAS_NUMBER_ARG,
+    OTHER_ARG,
+  };
+
+  enum Operator {
+    OP_INVALID,
+    // Boolean operators:
+    OP_EQ,
+    OP_NE,
+    OP_LT,
+    OP_LE,
+    OP_GT,
+    OP_GE,
+    OP_AND,
+    OP_OR,
+    OP_NOT,
+    // Arithmetic operators:
+    OP_ADD,
+    OP_SUB,
+    OP_MUL,
+    OP_DIV,
+    OP_MOD,
+    OP_NEGATE
+  };
+
+  enum QueryType {
+    QUERY_BOOLEAN_OPERATOR,
+    QUERY_ARITHMETIC_OPERATOR,
+    QUERY_EVENT_MEMBER,
+    QUERY_NUMBER,
+    QUERY_STRING
+  };
+
+  // Compare with the given member.
+  explicit Query(TraceEventMember member);
+
+  // Compare with the given member argument value.
+  Query(TraceEventMember member, const std::string& arg_name);
+
+  // Compare with the given string.
+  explicit Query(const std::string& str);
+
+  // Compare with the given number.
+  explicit Query(double num);
+
+  // Construct a boolean Query that returns (left <binary_op> right).
+  Query(const Query& left, const Query& right, Operator binary_op);
+
+  // Construct a boolean Query that returns (<binary_op> left).
+  Query(const Query& left, Operator unary_op);
+
+  // Try to compare left_ against right_ based on operator_.
+  // If either left or right does not convert to double, false is returned.
+  // Otherwise, true is returned and |result| is set to the comparison result.
+  bool CompareAsDouble(const TraceEvent& event, bool* result) const;
+
+  // Try to compare left_ against right_ based on operator_.
+  // If either left or right does not convert to string, false is returned.
+  // Otherwise, true is returned and |result| is set to the comparison result.
+  bool CompareAsString(const TraceEvent& event, bool* result) const;
+
+  // Attempt to convert this Query to a double. On success, true is returned
+  // and the double value is stored in |num|.
+  bool GetAsDouble(const TraceEvent& event, double* num) const;
+
+  // Attempt to convert this Query to a string. On success, true is returned
+  // and the string value is stored in |str|.
+  bool GetAsString(const TraceEvent& event, std::string* str) const;
+
+  // Evaluate this Query as an arithmetic operator on left_ and right_.
+  bool EvaluateArithmeticOperator(const TraceEvent& event,
+                                  double* num) const;
+
+  // For QUERY_EVENT_MEMBER Query: attempt to get the double value of the Query.
+  bool GetMemberValueAsDouble(const TraceEvent& event, double* num) const;
+
+  // For QUERY_EVENT_MEMBER Query: attempt to get the string value of the Query.
+  bool GetMemberValueAsString(const TraceEvent& event, std::string* num) const;
+
+  // Does this Query represent a value?
+  bool is_value() const { return type_ != QUERY_BOOLEAN_OPERATOR; }
+
+  bool is_unary_operator() const {
+    return operator_ == OP_NOT || operator_ == OP_NEGATE;
+  }
+
+  bool is_comparison_operator() const {
+    return operator_ != OP_INVALID && operator_ < OP_AND;
+  }
+
+  const Query& left() const;
+  const Query& right() const;
+
+  QueryType type_;
+  Operator operator_;
+  scoped_refptr<QueryNode> left_;
+  scoped_refptr<QueryNode> right_;
+  TraceEventMember member_;
+  double number_;
+  std::string string_;
+  bool is_pattern_;
+};
+
+// Implementation detail:
+// QueryNode allows Query to store a ref-counted query tree.
+class QueryNode : public base::RefCounted<QueryNode> {
+ public:
+  explicit QueryNode(const Query& query);
+  const Query& query() const { return query_; }
+
+ private:
+  friend class base::RefCounted<QueryNode>;
+  ~QueryNode();
+
+  Query query_;
+};
+
+// TraceAnalyzer helps tests search for trace events.
+class TraceAnalyzer {
+ public:
+  ~TraceAnalyzer();
+
+  // Use trace events from JSON string generated by tracing API.
+  // Returns non-NULL if the JSON is successfully parsed.
+  static TraceAnalyzer* Create(const std::string& json_events)
+                               WARN_UNUSED_RESULT;
+
+  void SetIgnoreMetadataEvents(bool ignore) { ignore_metadata_events_ = true; }
+
+  // Associate BEGIN and END events with each other. This allows Query(OTHER_*)
+  // to access the associated event and enables Query(EVENT_DURATION).
+  // An end event will match the most recent begin event with the same name,
+  // category, process ID and thread ID. This matches what is shown in
+  // about:tracing. After association, the BEGIN event will point to the
+  // matching END event, but the END event will not point to the BEGIN event.
+  void AssociateBeginEndEvents();
+
+  // Associate ASYNC_BEGIN, ASYNC_STEP and ASYNC_END events with each other.
+  // An ASYNC_END event will match the most recent ASYNC_BEGIN or ASYNC_STEP
+  // event with the same name, category, and ID. This creates a singly linked
+  // list of ASYNC_BEGIN->ASYNC_STEP...->ASYNC_END.
+  void AssociateAsyncBeginEndEvents();
+
+  // AssociateEvents can be used to customize event associations by setting the
+  // other_event member of TraceEvent. This should be used to associate two
+  // INSTANT events.
+  //
+  // The assumptions are:
+  // - |first| events occur before |second| events.
+  // - the closest matching |second| event is the correct match.
+  //
+  // |first|  - Eligible |first| events match this query.
+  // |second| - Eligible |second| events match this query.
+  // |match|  - This query is run on the |first| event. The OTHER_* EventMember
+  //            queries will point to an eligible |second| event. The query
+  //            should evaluate to true if the |first|/|second| pair is a match.
+  //
+  // When a match is found, the pair will be associated by having the first
+  // event's other_event member point to the other. AssociateEvents does not
+  // clear previous associations, so it is possible to associate multiple pairs
+  // of events by calling AssociateEvents more than once with different queries.
+  //
+  // NOTE: AssociateEvents will overwrite existing other_event associations if
+  // the queries pass for events that already had a previous association.
+  //
+  // After calling any Find* method, it is not allowed to call AssociateEvents
+  // again.
+  void AssociateEvents(const Query& first,
+                       const Query& second,
+                       const Query& match);
+
+  // For each event, copy its arguments to the other_event argument map. If
+  // argument name already exists, it will not be overwritten.
+  void MergeAssociatedEventArgs();
+
+  // Find all events that match query and replace output vector.
+  size_t FindEvents(const Query& query, TraceEventVector* output);
+
+  // Find first event that matches query or NULL if not found.
+  const TraceEvent* FindFirstOf(const Query& query);
+
+  // Find last event that matches query or NULL if not found.
+  const TraceEvent* FindLastOf(const Query& query);
+
+  const std::string& GetThreadName(const TraceEvent::ProcessThreadID& thread);
+
+ private:
+  TraceAnalyzer();
+
+  bool SetEvents(const std::string& json_events) WARN_UNUSED_RESULT;
+
+  // Read metadata (thread names, etc) from events.
+  void ParseMetadata();
+
+  std::map<TraceEvent::ProcessThreadID, std::string> thread_names_;
+  std::vector<TraceEvent> raw_events_;
+  bool ignore_metadata_events_;
+  bool allow_assocation_changes_;
+
+  DISALLOW_COPY_AND_ASSIGN(TraceAnalyzer);
+};
+
+// Utility functions for TraceEventVector.
+
+struct RateStats {
+  double min_us;
+  double max_us;
+  double mean_us;
+  double standard_deviation_us;
+};
+
+struct RateStatsOptions {
+  RateStatsOptions() : trim_min(0u), trim_max(0u) {}
+  // After the times between events are sorted, the number of specified elements
+  // will be trimmed before calculating the RateStats. This is useful in cases
+  // where extreme outliers are tolerable and should not skew the overall
+  // average.
+  size_t trim_min;  // Trim this many minimum times.
+  size_t trim_max;  // Trim this many maximum times.
+};
+
+// Calculate min/max/mean and standard deviation from the times between
+// adjacent events.
+bool GetRateStats(const TraceEventVector& events,
+                  RateStats* stats,
+                  const RateStatsOptions* options);
+
+// Starting from |position|, find the first event that matches |query|.
+// Returns true if found, false otherwise.
+bool FindFirstOf(const TraceEventVector& events,
+                 const Query& query,
+                 size_t position,
+                 size_t* return_index);
+
+// Starting from |position|, find the last event that matches |query|.
+// Returns true if found, false otherwise.
+bool FindLastOf(const TraceEventVector& events,
+                const Query& query,
+                size_t position,
+                size_t* return_index);
+
+// Find the closest events to |position| in time that match |query|.
+// return_second_closest may be NULL. Closeness is determined by comparing
+// with the event timestamp.
+// Returns true if found, false otherwise. If both return parameters are
+// requested, both must be found for a successful result.
+bool FindClosest(const TraceEventVector& events,
+                 const Query& query,
+                 size_t position,
+                 size_t* return_closest,
+                 size_t* return_second_closest);
+
+// Count matches, inclusive of |begin_position|, exclusive of |end_position|.
+size_t CountMatches(const TraceEventVector& events,
+                    const Query& query,
+                    size_t begin_position,
+                    size_t end_position);
+
+// Count all matches.
+static inline size_t CountMatches(const TraceEventVector& events,
+                                  const Query& query) {
+  return CountMatches(events, query, 0u, events.size());
+}
+
+}  // namespace trace_analyzer
+
+#endif  // BASE_TEST_TRACE_EVENT_ANALYZER_H_
diff --git a/base/test/trace_event_analyzer_unittest.cc b/base/test/trace_event_analyzer_unittest.cc
new file mode 100644
index 0000000..5604508
--- /dev/null
+++ b/base/test/trace_event_analyzer_unittest.cc
@@ -0,0 +1,895 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/debug/trace_event_unittest.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/trace_event_analyzer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace trace_analyzer {
+
+namespace {
+
+class TraceEventAnalyzerTest : public testing::Test {
+ public:
+  void ManualSetUp();
+  void OnTraceDataCollected(
+      base::WaitableEvent* flush_complete_event,
+      const scoped_refptr<base::RefCountedString>& json_events_str,
+      bool has_more_events);
+  void BeginTracing();
+  void EndTracing();
+
+  base::debug::TraceResultBuffer::SimpleOutput output_;
+  base::debug::TraceResultBuffer buffer_;
+};
+
+void TraceEventAnalyzerTest::ManualSetUp() {
+  ASSERT_TRUE(base::debug::TraceLog::GetInstance());
+  buffer_.SetOutputCallback(output_.GetCallback());
+  output_.json_output.clear();
+}
+
+void TraceEventAnalyzerTest::OnTraceDataCollected(
+    base::WaitableEvent* flush_complete_event,
+    const scoped_refptr<base::RefCountedString>& json_events_str,
+    bool has_more_events) {
+  buffer_.AddFragment(json_events_str->data());
+  if (!has_more_events)
+    flush_complete_event->Signal();
+}
+
+void TraceEventAnalyzerTest::BeginTracing() {
+  output_.json_output.clear();
+  buffer_.Start();
+  base::debug::TraceLog::GetInstance()->SetEnabled(
+      base::debug::CategoryFilter("*"),
+      base::debug::TraceLog::RECORDING_MODE,
+      base::debug::TraceOptions());
+}
+
+void TraceEventAnalyzerTest::EndTracing() {
+  base::debug::TraceLog::GetInstance()->SetDisabled();
+  base::WaitableEvent flush_complete_event(false, false);
+  base::debug::TraceLog::GetInstance()->Flush(
+      base::Bind(&TraceEventAnalyzerTest::OnTraceDataCollected,
+                 base::Unretained(this),
+                 base::Unretained(&flush_complete_event)));
+  flush_complete_event.Wait();
+  buffer_.Finish();
+}
+
+}  // namespace
+
+TEST_F(TraceEventAnalyzerTest, NoEvents) {
+  ManualSetUp();
+
+  // Create an empty JSON event string:
+  buffer_.Start();
+  buffer_.Finish();
+
+  scoped_ptr<TraceAnalyzer>
+      analyzer(TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+
+  // Search for all events and verify that nothing is returned.
+  TraceEventVector found;
+  analyzer->FindEvents(Query::Bool(true), &found);
+  EXPECT_EQ(0u, found.size());
+}
+
+TEST_F(TraceEventAnalyzerTest, TraceEvent) {
+  ManualSetUp();
+
+  int int_num = 2;
+  double double_num = 3.5;
+  const char* str = "the string";
+
+  TraceEvent event;
+  event.arg_numbers["false"] = 0.0;
+  event.arg_numbers["true"] = 1.0;
+  event.arg_numbers["int"] = static_cast<double>(int_num);
+  event.arg_numbers["double"] = double_num;
+  event.arg_strings["string"] = str;
+
+  ASSERT_TRUE(event.HasNumberArg("false"));
+  ASSERT_TRUE(event.HasNumberArg("true"));
+  ASSERT_TRUE(event.HasNumberArg("int"));
+  ASSERT_TRUE(event.HasNumberArg("double"));
+  ASSERT_TRUE(event.HasStringArg("string"));
+  ASSERT_FALSE(event.HasNumberArg("notfound"));
+  ASSERT_FALSE(event.HasStringArg("notfound"));
+
+  EXPECT_FALSE(event.GetKnownArgAsBool("false"));
+  EXPECT_TRUE(event.GetKnownArgAsBool("true"));
+  EXPECT_EQ(int_num, event.GetKnownArgAsInt("int"));
+  EXPECT_EQ(double_num, event.GetKnownArgAsDouble("double"));
+  EXPECT_STREQ(str, event.GetKnownArgAsString("string").c_str());
+}
+
+TEST_F(TraceEventAnalyzerTest, QueryEventMember) {
+  ManualSetUp();
+
+  TraceEvent event;
+  event.thread.process_id = 3;
+  event.thread.thread_id = 4;
+  event.timestamp = 1.5;
+  event.phase = TRACE_EVENT_PHASE_BEGIN;
+  event.category = "category";
+  event.name = "name";
+  event.id = "1";
+  event.arg_numbers["num"] = 7.0;
+  event.arg_strings["str"] = "the string";
+
+  // Other event with all different members:
+  TraceEvent other;
+  other.thread.process_id = 5;
+  other.thread.thread_id = 6;
+  other.timestamp = 2.5;
+  other.phase = TRACE_EVENT_PHASE_END;
+  other.category = "category2";
+  other.name = "name2";
+  other.id = "2";
+  other.arg_numbers["num2"] = 8.0;
+  other.arg_strings["str2"] = "the string 2";
+
+  event.other_event = &other;
+  ASSERT_TRUE(event.has_other_event());
+  double duration = event.GetAbsTimeToOtherEvent();
+
+  Query event_pid = Query::EventPidIs(event.thread.process_id);
+  Query event_tid = Query::EventTidIs(event.thread.thread_id);
+  Query event_time = Query::EventTimeIs(event.timestamp);
+  Query event_duration = Query::EventDurationIs(duration);
+  Query event_phase = Query::EventPhaseIs(event.phase);
+  Query event_category = Query::EventCategoryIs(event.category);
+  Query event_name = Query::EventNameIs(event.name);
+  Query event_id = Query::EventIdIs(event.id);
+  Query event_has_arg1 = Query::EventHasNumberArg("num");
+  Query event_has_arg2 = Query::EventHasStringArg("str");
+  Query event_arg1 =
+      (Query::EventArg("num") == Query::Double(event.arg_numbers["num"]));
+  Query event_arg2 =
+      (Query::EventArg("str") == Query::String(event.arg_strings["str"]));
+  Query event_has_other = Query::EventHasOther();
+  Query other_pid = Query::OtherPidIs(other.thread.process_id);
+  Query other_tid = Query::OtherTidIs(other.thread.thread_id);
+  Query other_time = Query::OtherTimeIs(other.timestamp);
+  Query other_phase = Query::OtherPhaseIs(other.phase);
+  Query other_category = Query::OtherCategoryIs(other.category);
+  Query other_name = Query::OtherNameIs(other.name);
+  Query other_id = Query::OtherIdIs(other.id);
+  Query other_has_arg1 = Query::OtherHasNumberArg("num2");
+  Query other_has_arg2 = Query::OtherHasStringArg("str2");
+  Query other_arg1 =
+      (Query::OtherArg("num2") == Query::Double(other.arg_numbers["num2"]));
+  Query other_arg2 =
+      (Query::OtherArg("str2") == Query::String(other.arg_strings["str2"]));
+
+  EXPECT_TRUE(event_pid.Evaluate(event));
+  EXPECT_TRUE(event_tid.Evaluate(event));
+  EXPECT_TRUE(event_time.Evaluate(event));
+  EXPECT_TRUE(event_duration.Evaluate(event));
+  EXPECT_TRUE(event_phase.Evaluate(event));
+  EXPECT_TRUE(event_category.Evaluate(event));
+  EXPECT_TRUE(event_name.Evaluate(event));
+  EXPECT_TRUE(event_id.Evaluate(event));
+  EXPECT_TRUE(event_has_arg1.Evaluate(event));
+  EXPECT_TRUE(event_has_arg2.Evaluate(event));
+  EXPECT_TRUE(event_arg1.Evaluate(event));
+  EXPECT_TRUE(event_arg2.Evaluate(event));
+  EXPECT_TRUE(event_has_other.Evaluate(event));
+  EXPECT_TRUE(other_pid.Evaluate(event));
+  EXPECT_TRUE(other_tid.Evaluate(event));
+  EXPECT_TRUE(other_time.Evaluate(event));
+  EXPECT_TRUE(other_phase.Evaluate(event));
+  EXPECT_TRUE(other_category.Evaluate(event));
+  EXPECT_TRUE(other_name.Evaluate(event));
+  EXPECT_TRUE(other_id.Evaluate(event));
+  EXPECT_TRUE(other_has_arg1.Evaluate(event));
+  EXPECT_TRUE(other_has_arg2.Evaluate(event));
+  EXPECT_TRUE(other_arg1.Evaluate(event));
+  EXPECT_TRUE(other_arg2.Evaluate(event));
+
+  // Evaluate event queries against other to verify the queries fail when the
+  // event members are wrong.
+  EXPECT_FALSE(event_pid.Evaluate(other));
+  EXPECT_FALSE(event_tid.Evaluate(other));
+  EXPECT_FALSE(event_time.Evaluate(other));
+  EXPECT_FALSE(event_duration.Evaluate(other));
+  EXPECT_FALSE(event_phase.Evaluate(other));
+  EXPECT_FALSE(event_category.Evaluate(other));
+  EXPECT_FALSE(event_name.Evaluate(other));
+  EXPECT_FALSE(event_id.Evaluate(other));
+  EXPECT_FALSE(event_has_arg1.Evaluate(other));
+  EXPECT_FALSE(event_has_arg2.Evaluate(other));
+  EXPECT_FALSE(event_arg1.Evaluate(other));
+  EXPECT_FALSE(event_arg2.Evaluate(other));
+  EXPECT_FALSE(event_has_other.Evaluate(other));
+}
+
+TEST_F(TraceEventAnalyzerTest, BooleanOperators) {
+  ManualSetUp();
+
+  BeginTracing();
+  {
+    TRACE_EVENT_INSTANT1("cat1", "name1", TRACE_EVENT_SCOPE_THREAD, "num", 1);
+    TRACE_EVENT_INSTANT1("cat1", "name2", TRACE_EVENT_SCOPE_THREAD, "num", 2);
+    TRACE_EVENT_INSTANT1("cat2", "name3", TRACE_EVENT_SCOPE_THREAD, "num", 3);
+    TRACE_EVENT_INSTANT1("cat2", "name4", TRACE_EVENT_SCOPE_THREAD, "num", 4);
+  }
+  EndTracing();
+
+  scoped_ptr<TraceAnalyzer>
+      analyzer(TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(!!analyzer.get());
+  analyzer->SetIgnoreMetadataEvents(true);
+
+  TraceEventVector found;
+
+  // ==
+
+  analyzer->FindEvents(Query::EventCategory() == Query::String("cat1"), &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+  EXPECT_STREQ("name2", found[1]->name.c_str());
+
+  analyzer->FindEvents(Query::EventArg("num") == Query::Int(2), &found);
+  ASSERT_EQ(1u, found.size());
+  EXPECT_STREQ("name2", found[0]->name.c_str());
+
+  // !=
+
+  analyzer->FindEvents(Query::EventCategory() != Query::String("cat1"), &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STREQ("name3", found[0]->name.c_str());
+  EXPECT_STREQ("name4", found[1]->name.c_str());
+
+  analyzer->FindEvents(Query::EventArg("num") != Query::Int(2), &found);
+  ASSERT_EQ(3u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+  EXPECT_STREQ("name3", found[1]->name.c_str());
+  EXPECT_STREQ("name4", found[2]->name.c_str());
+
+  // <
+  analyzer->FindEvents(Query::EventArg("num") < Query::Int(2), &found);
+  ASSERT_EQ(1u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+
+  // <=
+  analyzer->FindEvents(Query::EventArg("num") <= Query::Int(2), &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+  EXPECT_STREQ("name2", found[1]->name.c_str());
+
+  // >
+  analyzer->FindEvents(Query::EventArg("num") > Query::Int(3), &found);
+  ASSERT_EQ(1u, found.size());
+  EXPECT_STREQ("name4", found[0]->name.c_str());
+
+  // >=
+  analyzer->FindEvents(Query::EventArg("num") >= Query::Int(4), &found);
+  ASSERT_EQ(1u, found.size());
+  EXPECT_STREQ("name4", found[0]->name.c_str());
+
+  // &&
+  analyzer->FindEvents(Query::EventName() != Query::String("name1") &&
+                       Query::EventArg("num") < Query::Int(3), &found);
+  ASSERT_EQ(1u, found.size());
+  EXPECT_STREQ("name2", found[0]->name.c_str());
+
+  // ||
+  analyzer->FindEvents(Query::EventName() == Query::String("name1") ||
+                       Query::EventArg("num") == Query::Int(3), &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+  EXPECT_STREQ("name3", found[1]->name.c_str());
+
+  // !
+  analyzer->FindEvents(!(Query::EventName() == Query::String("name1") ||
+                         Query::EventArg("num") == Query::Int(3)), &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STREQ("name2", found[0]->name.c_str());
+  EXPECT_STREQ("name4", found[1]->name.c_str());
+}
+
+TEST_F(TraceEventAnalyzerTest, ArithmeticOperators) {
+  ManualSetUp();
+
+  BeginTracing();
+  {
+    // These events are searched for:
+    TRACE_EVENT_INSTANT2("cat1", "math1", TRACE_EVENT_SCOPE_THREAD,
+                         "a", 10, "b", 5);
+    TRACE_EVENT_INSTANT2("cat1", "math2", TRACE_EVENT_SCOPE_THREAD,
+                         "a", 10, "b", 10);
+    // Extra events that never match, for noise:
+    TRACE_EVENT_INSTANT2("noise", "math3", TRACE_EVENT_SCOPE_THREAD,
+                         "a", 1,  "b", 3);
+    TRACE_EVENT_INSTANT2("noise", "math4", TRACE_EVENT_SCOPE_THREAD,
+                         "c", 10, "d", 5);
+  }
+  EndTracing();
+
+  scoped_ptr<TraceAnalyzer>
+      analyzer(TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+
+  TraceEventVector found;
+
+  // Verify that arithmetic operators function:
+
+  // +
+  analyzer->FindEvents(Query::EventArg("a") + Query::EventArg("b") ==
+                       Query::Int(20), &found);
+  EXPECT_EQ(1u, found.size());
+  EXPECT_STREQ("math2", found.front()->name.c_str());
+
+  // -
+  analyzer->FindEvents(Query::EventArg("a") - Query::EventArg("b") ==
+                       Query::Int(5), &found);
+  EXPECT_EQ(1u, found.size());
+  EXPECT_STREQ("math1", found.front()->name.c_str());
+
+  // *
+  analyzer->FindEvents(Query::EventArg("a") * Query::EventArg("b") ==
+                       Query::Int(50), &found);
+  EXPECT_EQ(1u, found.size());
+  EXPECT_STREQ("math1", found.front()->name.c_str());
+
+  // /
+  analyzer->FindEvents(Query::EventArg("a") / Query::EventArg("b") ==
+                       Query::Int(2), &found);
+  EXPECT_EQ(1u, found.size());
+  EXPECT_STREQ("math1", found.front()->name.c_str());
+
+  // %
+  analyzer->FindEvents(Query::EventArg("a") % Query::EventArg("b") ==
+                       Query::Int(0), &found);
+  EXPECT_EQ(2u, found.size());
+
+  // - (negate)
+  analyzer->FindEvents(-Query::EventArg("b") == Query::Int(-10), &found);
+  EXPECT_EQ(1u, found.size());
+  EXPECT_STREQ("math2", found.front()->name.c_str());
+}
+
+TEST_F(TraceEventAnalyzerTest, StringPattern) {
+  ManualSetUp();
+
+  BeginTracing();
+  {
+    TRACE_EVENT_INSTANT0("cat1", "name1", TRACE_EVENT_SCOPE_THREAD);
+    TRACE_EVENT_INSTANT0("cat1", "name2", TRACE_EVENT_SCOPE_THREAD);
+    TRACE_EVENT_INSTANT0("cat1", "no match", TRACE_EVENT_SCOPE_THREAD);
+    TRACE_EVENT_INSTANT0("cat1", "name3x", TRACE_EVENT_SCOPE_THREAD);
+  }
+  EndTracing();
+
+  scoped_ptr<TraceAnalyzer>
+      analyzer(TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+  analyzer->SetIgnoreMetadataEvents(true);
+
+  TraceEventVector found;
+
+  analyzer->FindEvents(Query::EventName() == Query::Pattern("name?"), &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+  EXPECT_STREQ("name2", found[1]->name.c_str());
+
+  analyzer->FindEvents(Query::EventName() == Query::Pattern("name*"), &found);
+  ASSERT_EQ(3u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+  EXPECT_STREQ("name2", found[1]->name.c_str());
+  EXPECT_STREQ("name3x", found[2]->name.c_str());
+
+  analyzer->FindEvents(Query::EventName() != Query::Pattern("name*"), &found);
+  ASSERT_EQ(1u, found.size());
+  EXPECT_STREQ("no match", found[0]->name.c_str());
+}
+
+// Test that duration queries work.
+TEST_F(TraceEventAnalyzerTest, BeginEndDuration) {
+  ManualSetUp();
+
+  const base::TimeDelta kSleepTime = base::TimeDelta::FromMilliseconds(200);
+  // We will search for events that have a duration of greater than 90% of the
+  // sleep time, so that there is no flakiness.
+  int64 duration_cutoff_us = (kSleepTime.InMicroseconds() * 9) / 10;
+
+  BeginTracing();
+  {
+    TRACE_EVENT_BEGIN0("cat1", "name1"); // found by duration query
+    TRACE_EVENT_BEGIN0("noise", "name2"); // not searched for, just noise
+    {
+      TRACE_EVENT_BEGIN0("cat2", "name3"); // found by duration query
+      // next event not searched for, just noise
+      TRACE_EVENT_INSTANT0("noise", "name4", TRACE_EVENT_SCOPE_THREAD);
+      base::debug::HighResSleepForTraceTest(kSleepTime);
+      TRACE_EVENT_BEGIN0("cat2", "name5"); // not found (duration too short)
+      TRACE_EVENT_END0("cat2", "name5"); // not found (duration too short)
+      TRACE_EVENT_END0("cat2", "name3"); // found by duration query
+    }
+    TRACE_EVENT_END0("noise", "name2"); // not searched for, just noise
+    TRACE_EVENT_END0("cat1", "name1"); // found by duration query
+  }
+  EndTracing();
+
+  scoped_ptr<TraceAnalyzer>
+      analyzer(TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+  analyzer->AssociateBeginEndEvents();
+
+  TraceEventVector found;
+  analyzer->FindEvents(
+      Query::MatchBeginWithEnd() &&
+      Query::EventDuration() >
+          Query::Int(static_cast<int>(duration_cutoff_us)) &&
+      (Query::EventCategory() == Query::String("cat1") ||
+       Query::EventCategory() == Query::String("cat2") ||
+       Query::EventCategory() == Query::String("cat3")),
+      &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+  EXPECT_STREQ("name3", found[1]->name.c_str());
+}
+
+// Test that duration queries work.
+TEST_F(TraceEventAnalyzerTest, CompleteDuration) {
+  ManualSetUp();
+
+  const base::TimeDelta kSleepTime = base::TimeDelta::FromMilliseconds(200);
+  // We will search for events that have a duration of greater than 90% of the
+  // sleep time, so that there is no flakiness.
+  int64 duration_cutoff_us = (kSleepTime.InMicroseconds() * 9) / 10;
+
+  BeginTracing();
+  {
+    TRACE_EVENT0("cat1", "name1"); // found by duration query
+    TRACE_EVENT0("noise", "name2"); // not searched for, just noise
+    {
+      TRACE_EVENT0("cat2", "name3"); // found by duration query
+      // next event not searched for, just noise
+      TRACE_EVENT_INSTANT0("noise", "name4", TRACE_EVENT_SCOPE_THREAD);
+      base::debug::HighResSleepForTraceTest(kSleepTime);
+      TRACE_EVENT0("cat2", "name5"); // not found (duration too short)
+    }
+  }
+  EndTracing();
+
+  scoped_ptr<TraceAnalyzer>
+      analyzer(TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+  analyzer->AssociateBeginEndEvents();
+
+  TraceEventVector found;
+  analyzer->FindEvents(
+      Query::EventCompleteDuration() >
+          Query::Int(static_cast<int>(duration_cutoff_us)) &&
+      (Query::EventCategory() == Query::String("cat1") ||
+       Query::EventCategory() == Query::String("cat2") ||
+       Query::EventCategory() == Query::String("cat3")),
+      &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STREQ("name1", found[0]->name.c_str());
+  EXPECT_STREQ("name3", found[1]->name.c_str());
+}
+
+// Test AssociateBeginEndEvents
+TEST_F(TraceEventAnalyzerTest, BeginEndAssocations) {
+  ManualSetUp();
+
+  BeginTracing();
+  {
+    TRACE_EVENT_END0("cat1", "name1"); // does not match out of order begin
+    TRACE_EVENT_BEGIN0("cat1", "name2");
+    TRACE_EVENT_INSTANT0("cat1", "name3", TRACE_EVENT_SCOPE_THREAD);
+    TRACE_EVENT_BEGIN0("cat1", "name1");
+    TRACE_EVENT_END0("cat1", "name2");
+  }
+  EndTracing();
+
+  scoped_ptr<TraceAnalyzer>
+      analyzer(TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+  analyzer->AssociateBeginEndEvents();
+
+  TraceEventVector found;
+  analyzer->FindEvents(Query::MatchBeginWithEnd(), &found);
+  ASSERT_EQ(1u, found.size());
+  EXPECT_STREQ("name2", found[0]->name.c_str());
+}
+
+// Test MergeAssociatedEventArgs
+TEST_F(TraceEventAnalyzerTest, MergeAssociatedEventArgs) {
+  ManualSetUp();
+
+  const char* arg_string = "arg_string";
+  BeginTracing();
+  {
+    TRACE_EVENT_BEGIN0("cat1", "name1");
+    TRACE_EVENT_END1("cat1", "name1", "arg", arg_string);
+  }
+  EndTracing();
+
+  scoped_ptr<TraceAnalyzer>
+      analyzer(TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+  analyzer->AssociateBeginEndEvents();
+
+  TraceEventVector found;
+  analyzer->FindEvents(Query::MatchBeginName("name1"), &found);
+  ASSERT_EQ(1u, found.size());
+  std::string arg_actual;
+  EXPECT_FALSE(found[0]->GetArgAsString("arg", &arg_actual));
+
+  analyzer->MergeAssociatedEventArgs();
+  EXPECT_TRUE(found[0]->GetArgAsString("arg", &arg_actual));
+  EXPECT_STREQ(arg_string, arg_actual.c_str());
+}
+
+// Test AssociateAsyncBeginEndEvents
+TEST_F(TraceEventAnalyzerTest, AsyncBeginEndAssocations) {
+  ManualSetUp();
+
+  BeginTracing();
+  {
+    TRACE_EVENT_ASYNC_END0("cat1", "name1", 0xA); // no match / out of order
+    TRACE_EVENT_ASYNC_BEGIN0("cat1", "name1", 0xB);
+    TRACE_EVENT_ASYNC_BEGIN0("cat1", "name1", 0xC);
+    TRACE_EVENT_INSTANT0("cat1", "name1", TRACE_EVENT_SCOPE_THREAD); // noise
+    TRACE_EVENT0("cat1", "name1"); // noise
+    TRACE_EVENT_ASYNC_END0("cat1", "name1", 0xB);
+    TRACE_EVENT_ASYNC_END0("cat1", "name1", 0xC);
+    TRACE_EVENT_ASYNC_BEGIN0("cat1", "name1", 0xA); // no match / out of order
+  }
+  EndTracing();
+
+  scoped_ptr<TraceAnalyzer>
+      analyzer(TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+  analyzer->AssociateAsyncBeginEndEvents();
+
+  TraceEventVector found;
+  analyzer->FindEvents(Query::MatchAsyncBeginWithNext(), &found);
+  ASSERT_EQ(2u, found.size());
+  EXPECT_STRCASEEQ("0xb", found[0]->id.c_str());
+  EXPECT_STRCASEEQ("0xc", found[1]->id.c_str());
+}
+
+// Test AssociateAsyncBeginEndEvents
+TEST_F(TraceEventAnalyzerTest, AsyncBeginEndAssocationsWithSteps) {
+  ManualSetUp();
+
+  BeginTracing();
+  {
+    TRACE_EVENT_ASYNC_STEP_INTO0("c", "n", 0xA, "s1");
+    TRACE_EVENT_ASYNC_END0("c", "n", 0xA);
+    TRACE_EVENT_ASYNC_BEGIN0("c", "n", 0xB);
+    TRACE_EVENT_ASYNC_BEGIN0("c", "n", 0xC);
+    TRACE_EVENT_ASYNC_STEP_PAST0("c", "n", 0xB, "s1");
+    TRACE_EVENT_ASYNC_STEP_INTO0("c", "n", 0xC, "s1");
+    TRACE_EVENT_ASYNC_STEP_INTO1("c", "n", 0xC, "s2", "a", 1);
+    TRACE_EVENT_ASYNC_END0("c", "n", 0xB);
+    TRACE_EVENT_ASYNC_END0("c", "n", 0xC);
+    TRACE_EVENT_ASYNC_BEGIN0("c", "n", 0xA);
+    TRACE_EVENT_ASYNC_STEP_INTO0("c", "n", 0xA, "s2");
+  }
+  EndTracing();
+
+  scoped_ptr<TraceAnalyzer>
+      analyzer(TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+  analyzer->AssociateAsyncBeginEndEvents();
+
+  TraceEventVector found;
+  analyzer->FindEvents(Query::MatchAsyncBeginWithNext(), &found);
+  ASSERT_EQ(3u, found.size());
+
+  EXPECT_STRCASEEQ("0xb", found[0]->id.c_str());
+  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_STEP_PAST, found[0]->other_event->phase);
+  EXPECT_TRUE(found[0]->other_event->other_event);
+  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_END,
+            found[0]->other_event->other_event->phase);
+
+  EXPECT_STRCASEEQ("0xc", found[1]->id.c_str());
+  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_STEP_INTO, found[1]->other_event->phase);
+  EXPECT_TRUE(found[1]->other_event->other_event);
+  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_STEP_INTO,
+            found[1]->other_event->other_event->phase);
+  double arg_actual = 0;
+  EXPECT_TRUE(found[1]->other_event->other_event->GetArgAsNumber(
+                  "a", &arg_actual));
+  EXPECT_EQ(1.0, arg_actual);
+  EXPECT_TRUE(found[1]->other_event->other_event->other_event);
+  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_END,
+            found[1]->other_event->other_event->other_event->phase);
+
+  EXPECT_STRCASEEQ("0xa", found[2]->id.c_str());
+  EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_STEP_INTO, found[2]->other_event->phase);
+}
+
+// Test that the TraceAnalyzer custom associations work.
+TEST_F(TraceEventAnalyzerTest, CustomAssociations) {
+  ManualSetUp();
+
+  // Add events that begin/end in pipelined ordering with unique ID parameter
+  // to match up the begin/end pairs.
+  BeginTracing();
+  {
+    // no begin match
+    TRACE_EVENT_INSTANT1("cat1", "end", TRACE_EVENT_SCOPE_THREAD, "id", 1);
+    // end is cat4
+    TRACE_EVENT_INSTANT1("cat2", "begin", TRACE_EVENT_SCOPE_THREAD, "id", 2);
+    // end is cat5
+    TRACE_EVENT_INSTANT1("cat3", "begin", TRACE_EVENT_SCOPE_THREAD, "id", 3);
+    TRACE_EVENT_INSTANT1("cat4", "end", TRACE_EVENT_SCOPE_THREAD, "id", 2);
+    TRACE_EVENT_INSTANT1("cat5", "end", TRACE_EVENT_SCOPE_THREAD, "id", 3);
+    // no end match
+    TRACE_EVENT_INSTANT1("cat6", "begin", TRACE_EVENT_SCOPE_THREAD, "id", 1);
+  }
+  EndTracing();
+
+  scoped_ptr<TraceAnalyzer>
+      analyzer(TraceAnalyzer::Create(output_.json_output));
+  ASSERT_TRUE(analyzer.get());
+
+  // begin, end, and match queries to find proper begin/end pairs.
+  Query begin(Query::EventName() == Query::String("begin"));
+  Query end(Query::EventName() == Query::String("end"));
+  Query match(Query::EventArg("id") == Query::OtherArg("id"));
+  analyzer->AssociateEvents(begin, end, match);
+
+  TraceEventVector found;
+
+  // cat1 has no other_event.
+  analyzer->FindEvents(Query::EventCategory() == Query::String("cat1") &&
+                       Query::EventHasOther(), &found);
+  EXPECT_EQ(0u, found.size());
+
+  // cat1 has no other_event.
+  analyzer->FindEvents(Query::EventCategory() == Query::String("cat1") &&
+                       !Query::EventHasOther(), &found);
+  EXPECT_EQ(1u, found.size());
+
+  // cat6 has no other_event.
+  analyzer->FindEvents(Query::EventCategory() == Query::String("cat6") &&
+                       !Query::EventHasOther(), &found);
+  EXPECT_EQ(1u, found.size());
+
+  // cat2 and cat4 are associated.
+  analyzer->FindEvents(Query::EventCategory() == Query::String("cat2") &&
+                       Query::OtherCategory() == Query::String("cat4"), &found);
+  EXPECT_EQ(1u, found.size());
+
+  // cat4 and cat2 are not associated.
+  analyzer->FindEvents(Query::EventCategory() == Query::String("cat4") &&
+                       Query::OtherCategory() == Query::String("cat2"), &found);
+  EXPECT_EQ(0u, found.size());
+
+  // cat3 and cat5 are associated.
+  analyzer->FindEvents(Query::EventCategory() == Query::String("cat3") &&
+                       Query::OtherCategory() == Query::String("cat5"), &found);
+  EXPECT_EQ(1u, found.size());
+
+  // cat5 and cat3 are not associated.
+  analyzer->FindEvents(Query::EventCategory() == Query::String("cat5") &&
+                       Query::OtherCategory() == Query::String("cat3"), &found);
+  EXPECT_EQ(0u, found.size());
+}
+
+// Verify that Query literals and types are properly casted.
+TEST_F(TraceEventAnalyzerTest, Literals) {
+  ManualSetUp();
+
+  // Since these queries don't refer to the event data, the dummy event below
+  // will never be accessed.
+  TraceEvent dummy;
+  char char_num = 5;
+  short short_num = -5;
+  EXPECT_TRUE((Query::Double(5.0) == Query::Int(char_num)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Double(-5.0) == Query::Int(short_num)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Double(1.0) == Query::Uint(1u)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Double(1.0) == Query::Int(1)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Double(-1.0) == Query::Int(-1)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Double(1.0) == Query::Double(1.0f)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Bool(true) == Query::Int(1)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Bool(false) == Query::Int(0)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Bool(true) == Query::Double(1.0f)).Evaluate(dummy));
+  EXPECT_TRUE((Query::Bool(false) == Query::Double(0.0f)).Evaluate(dummy));
+}
+
+// Test GetRateStats.
+TEST_F(TraceEventAnalyzerTest, RateStats) {
+  std::vector<TraceEvent> events;
+  events.reserve(100);
+  TraceEventVector event_ptrs;
+  TraceEvent event;
+  event.timestamp = 0.0;
+  double little_delta = 1.0;
+  double big_delta = 10.0;
+  double tiny_delta = 0.1;
+  RateStats stats;
+  RateStatsOptions options;
+
+  // Insert 10 events, each apart by little_delta.
+  for (int i = 0; i < 10; ++i) {
+    event.timestamp += little_delta;
+    events.push_back(event);
+    event_ptrs.push_back(&events.back());
+  }
+
+  ASSERT_TRUE(GetRateStats(event_ptrs, &stats, NULL));
+  EXPECT_EQ(little_delta, stats.mean_us);
+  EXPECT_EQ(little_delta, stats.min_us);
+  EXPECT_EQ(little_delta, stats.max_us);
+  EXPECT_EQ(0.0, stats.standard_deviation_us);
+
+  // Add an event apart by big_delta.
+  event.timestamp += big_delta;
+  events.push_back(event);
+  event_ptrs.push_back(&events.back());
+
+  ASSERT_TRUE(GetRateStats(event_ptrs, &stats, NULL));
+  EXPECT_LT(little_delta, stats.mean_us);
+  EXPECT_EQ(little_delta, stats.min_us);
+  EXPECT_EQ(big_delta, stats.max_us);
+  EXPECT_LT(0.0, stats.standard_deviation_us);
+
+  // Trim off the biggest delta and verify stats.
+  options.trim_min = 0;
+  options.trim_max = 1;
+  ASSERT_TRUE(GetRateStats(event_ptrs, &stats, &options));
+  EXPECT_EQ(little_delta, stats.mean_us);
+  EXPECT_EQ(little_delta, stats.min_us);
+  EXPECT_EQ(little_delta, stats.max_us);
+  EXPECT_EQ(0.0, stats.standard_deviation_us);
+
+  // Add an event apart by tiny_delta.
+  event.timestamp += tiny_delta;
+  events.push_back(event);
+  event_ptrs.push_back(&events.back());
+
+  // Trim off both the biggest and tiniest delta and verify stats.
+  options.trim_min = 1;
+  options.trim_max = 1;
+  ASSERT_TRUE(GetRateStats(event_ptrs, &stats, &options));
+  EXPECT_EQ(little_delta, stats.mean_us);
+  EXPECT_EQ(little_delta, stats.min_us);
+  EXPECT_EQ(little_delta, stats.max_us);
+  EXPECT_EQ(0.0, stats.standard_deviation_us);
+
+  // Verify smallest allowed number of events.
+  TraceEventVector few_event_ptrs;
+  few_event_ptrs.push_back(&event);
+  few_event_ptrs.push_back(&event);
+  ASSERT_FALSE(GetRateStats(few_event_ptrs, &stats, NULL));
+  few_event_ptrs.push_back(&event);
+  ASSERT_TRUE(GetRateStats(few_event_ptrs, &stats, NULL));
+
+  // Trim off more than allowed and verify failure.
+  options.trim_min = 0;
+  options.trim_max = 1;
+  ASSERT_FALSE(GetRateStats(few_event_ptrs, &stats, &options));
+}
+
+// Test FindFirstOf and FindLastOf.
+TEST_F(TraceEventAnalyzerTest, FindOf) {
+  size_t num_events = 100;
+  size_t index = 0;
+  TraceEventVector event_ptrs;
+  EXPECT_FALSE(FindFirstOf(event_ptrs, Query::Bool(true), 0, &index));
+  EXPECT_FALSE(FindFirstOf(event_ptrs, Query::Bool(true), 10, &index));
+  EXPECT_FALSE(FindLastOf(event_ptrs, Query::Bool(true), 0, &index));
+  EXPECT_FALSE(FindLastOf(event_ptrs, Query::Bool(true), 10, &index));
+
+  std::vector<TraceEvent> events;
+  events.resize(num_events);
+  for (size_t i = 0; i < events.size(); ++i)
+    event_ptrs.push_back(&events[i]);
+  size_t bam_index = num_events/2;
+  events[bam_index].name = "bam";
+  Query query_bam = Query::EventName() == Query::String(events[bam_index].name);
+
+  // FindFirstOf
+  EXPECT_FALSE(FindFirstOf(event_ptrs, Query::Bool(false), 0, &index));
+  EXPECT_TRUE(FindFirstOf(event_ptrs, Query::Bool(true), 0, &index));
+  EXPECT_EQ(0u, index);
+  EXPECT_TRUE(FindFirstOf(event_ptrs, Query::Bool(true), 5, &index));
+  EXPECT_EQ(5u, index);
+
+  EXPECT_FALSE(FindFirstOf(event_ptrs, query_bam, bam_index + 1, &index));
+  EXPECT_TRUE(FindFirstOf(event_ptrs, query_bam, 0, &index));
+  EXPECT_EQ(bam_index, index);
+  EXPECT_TRUE(FindFirstOf(event_ptrs, query_bam, bam_index, &index));
+  EXPECT_EQ(bam_index, index);
+
+  // FindLastOf
+  EXPECT_FALSE(FindLastOf(event_ptrs, Query::Bool(false), 1000, &index));
+  EXPECT_TRUE(FindLastOf(event_ptrs, Query::Bool(true), 1000, &index));
+  EXPECT_EQ(num_events - 1, index);
+  EXPECT_TRUE(FindLastOf(event_ptrs, Query::Bool(true), num_events - 5,
+                         &index));
+  EXPECT_EQ(num_events - 5, index);
+
+  EXPECT_FALSE(FindLastOf(event_ptrs, query_bam, bam_index - 1, &index));
+  EXPECT_TRUE(FindLastOf(event_ptrs, query_bam, num_events, &index));
+  EXPECT_EQ(bam_index, index);
+  EXPECT_TRUE(FindLastOf(event_ptrs, query_bam, bam_index, &index));
+  EXPECT_EQ(bam_index, index);
+}
+
+// Test FindClosest.
+TEST_F(TraceEventAnalyzerTest, FindClosest) {
+  size_t index_1 = 0;
+  size_t index_2 = 0;
+  TraceEventVector event_ptrs;
+  EXPECT_FALSE(FindClosest(event_ptrs, Query::Bool(true), 0,
+                           &index_1, &index_2));
+
+  size_t num_events = 5;
+  std::vector<TraceEvent> events;
+  events.resize(num_events);
+  for (size_t i = 0; i < events.size(); ++i) {
+    // timestamps go up exponentially so the lower index is always closer in
+    // time than the higher index.
+    events[i].timestamp = static_cast<double>(i) * static_cast<double>(i);
+    event_ptrs.push_back(&events[i]);
+  }
+  events[0].name = "one";
+  events[2].name = "two";
+  events[4].name = "three";
+  Query query_named = Query::EventName() != Query::String(std::string());
+  Query query_one = Query::EventName() == Query::String("one");
+
+  // Only one event matches query_one, so two closest can't be found.
+  EXPECT_FALSE(FindClosest(event_ptrs, query_one, 0, &index_1, &index_2));
+
+  EXPECT_TRUE(FindClosest(event_ptrs, query_one, 3, &index_1, NULL));
+  EXPECT_EQ(0u, index_1);
+
+  EXPECT_TRUE(FindClosest(event_ptrs, query_named, 1, &index_1, &index_2));
+  EXPECT_EQ(0u, index_1);
+  EXPECT_EQ(2u, index_2);
+
+  EXPECT_TRUE(FindClosest(event_ptrs, query_named, 4, &index_1, &index_2));
+  EXPECT_EQ(4u, index_1);
+  EXPECT_EQ(2u, index_2);
+
+  EXPECT_TRUE(FindClosest(event_ptrs, query_named, 3, &index_1, &index_2));
+  EXPECT_EQ(2u, index_1);
+  EXPECT_EQ(0u, index_2);
+}
+
+// Test CountMatches.
+TEST_F(TraceEventAnalyzerTest, CountMatches) {
+  TraceEventVector event_ptrs;
+  EXPECT_EQ(0u, CountMatches(event_ptrs, Query::Bool(true), 0, 10));
+
+  size_t num_events = 5;
+  size_t num_named = 3;
+  std::vector<TraceEvent> events;
+  events.resize(num_events);
+  for (size_t i = 0; i < events.size(); ++i)
+    event_ptrs.push_back(&events[i]);
+  events[0].name = "one";
+  events[2].name = "two";
+  events[4].name = "three";
+  Query query_named = Query::EventName() != Query::String(std::string());
+  Query query_one = Query::EventName() == Query::String("one");
+
+  EXPECT_EQ(0u, CountMatches(event_ptrs, Query::Bool(false)));
+  EXPECT_EQ(num_events, CountMatches(event_ptrs, Query::Bool(true)));
+  EXPECT_EQ(num_events - 1, CountMatches(event_ptrs, Query::Bool(true),
+                                         1, num_events));
+  EXPECT_EQ(1u, CountMatches(event_ptrs, query_one));
+  EXPECT_EQ(num_events - 1, CountMatches(event_ptrs, !query_one));
+  EXPECT_EQ(num_named, CountMatches(event_ptrs, query_named));
+}
+
+
+}  // namespace trace_analyzer
diff --git a/base/test/trace_to_file.cc b/base/test/trace_to_file.cc
new file mode 100644
index 0000000..6caaf47
--- /dev/null
+++ b/base/test/trace_to_file.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/trace_to_file.h"
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/debug/trace_event_impl.h"
+#include "base/files/file_util.h"
+#include "base/run_loop.h"
+
+namespace base {
+namespace test {
+
+TraceToFile::TraceToFile() : started_(false) {
+}
+
+TraceToFile::~TraceToFile() {
+  EndTracingIfNeeded();
+}
+
+void TraceToFile::BeginTracingFromCommandLineOptions() {
+  DCHECK(CommandLine::InitializedForCurrentProcess());
+  DCHECK(!started_);
+
+  if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kTraceToFile))
+    return;
+
+  // Empty filter (i.e. just --trace-to-file) turns into default categories in
+  // TraceEventImpl
+  std::string filter = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+      switches::kTraceToFile);
+
+  FilePath path;
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTraceToFileName)) {
+    path = FilePath(CommandLine::ForCurrentProcess()
+                        ->GetSwitchValuePath(switches::kTraceToFileName));
+  } else {
+    path = FilePath(FILE_PATH_LITERAL("trace.json"));
+  }
+
+  BeginTracing(path, filter);
+}
+
+void TraceToFile::BeginTracing(const FilePath& path,
+                               const std::string& categories) {
+  DCHECK(!started_);
+  started_ = true;
+  path_ = path;
+  WriteFileHeader();
+
+  debug::TraceLog::GetInstance()->SetEnabled(
+      debug::CategoryFilter(categories),
+      debug::TraceLog::RECORDING_MODE,
+      debug::TraceOptions(debug::RECORD_UNTIL_FULL));
+}
+
+void TraceToFile::WriteFileHeader() {
+  const char str[] = "{\"traceEvents\": [";
+  WriteFile(path_, str, static_cast<int>(strlen(str)));
+}
+
+void TraceToFile::AppendFileFooter() {
+  const char str[] = "]}";
+  AppendToFile(path_, str, static_cast<int>(strlen(str)));
+}
+
+void TraceToFile::TraceOutputCallback(const std::string& data) {
+  int ret = AppendToFile(path_, data.c_str(), static_cast<int>(data.size()));
+  DCHECK_NE(-1, ret);
+}
+
+static void OnTraceDataCollected(
+    Closure quit_closure,
+    debug::TraceResultBuffer* buffer,
+    const scoped_refptr<RefCountedString>& json_events_str,
+    bool has_more_events) {
+  buffer->AddFragment(json_events_str->data());
+  if (!has_more_events)
+    quit_closure.Run();
+}
+
+void TraceToFile::EndTracingIfNeeded() {
+  if (!started_)
+    return;
+  started_ = false;
+
+  debug::TraceLog::GetInstance()->SetDisabled();
+
+  debug::TraceResultBuffer buffer;
+  buffer.SetOutputCallback(
+      Bind(&TraceToFile::TraceOutputCallback, Unretained(this)));
+
+  RunLoop run_loop;
+  debug::TraceLog::GetInstance()->Flush(
+      Bind(&OnTraceDataCollected, run_loop.QuitClosure(), Unretained(&buffer)));
+  run_loop.Run();
+
+  AppendFileFooter();
+}
+
+}  // namespace test
+}  // namespace base
diff --git a/base/test/trace_to_file.h b/base/test/trace_to_file.h
new file mode 100644
index 0000000..4308736
--- /dev/null
+++ b/base/test/trace_to_file.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_TRACE_TO_FILE_H_
+#define BASE_TEST_TRACE_TO_FILE_H_
+
+#include "base/files/file_path.h"
+
+namespace base {
+namespace test {
+
+class TraceToFile {
+ public:
+  TraceToFile();
+  ~TraceToFile();
+
+  void BeginTracingFromCommandLineOptions();
+  void BeginTracing(const base::FilePath& path, const std::string& categories);
+  void EndTracingIfNeeded();
+
+ private:
+  void WriteFileHeader();
+  void AppendFileFooter();
+
+  void TraceOutputCallback(const std::string& data);
+
+  base::FilePath path_;
+  bool started_;
+};
+
+}  // namespace test
+}  // namespace base
+
+#endif  // BASE_TEST_TRACE_TO_FILE_H_
diff --git a/base/test/values_test_util.cc b/base/test/values_test_util.cc
new file mode 100644
index 0000000..c5dfd79
--- /dev/null
+++ b/base/test/values_test_util.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/values_test_util.h"
+
+#include "base/json/json_reader.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+void ExpectDictBooleanValue(bool expected_value,
+                            const DictionaryValue& value,
+                            const std::string& key) {
+  bool boolean_value = false;
+  EXPECT_TRUE(value.GetBoolean(key, &boolean_value)) << key;
+  EXPECT_EQ(expected_value, boolean_value) << key;
+}
+
+void ExpectDictDictionaryValue(const DictionaryValue& expected_value,
+                               const DictionaryValue& value,
+                               const std::string& key) {
+  const DictionaryValue* dict_value = NULL;
+  EXPECT_TRUE(value.GetDictionary(key, &dict_value)) << key;
+  EXPECT_TRUE(Value::Equals(dict_value, &expected_value)) << key;
+}
+
+void ExpectDictIntegerValue(int expected_value,
+                            const DictionaryValue& value,
+                            const std::string& key) {
+  int integer_value = 0;
+  EXPECT_TRUE(value.GetInteger(key, &integer_value)) << key;
+  EXPECT_EQ(expected_value, integer_value) << key;
+}
+
+void ExpectDictListValue(const ListValue& expected_value,
+                         const DictionaryValue& value,
+                         const std::string& key) {
+  const ListValue* list_value = NULL;
+  EXPECT_TRUE(value.GetList(key, &list_value)) << key;
+  EXPECT_TRUE(Value::Equals(list_value, &expected_value)) << key;
+}
+
+void ExpectDictStringValue(const std::string& expected_value,
+                           const DictionaryValue& value,
+                           const std::string& key) {
+  std::string string_value;
+  EXPECT_TRUE(value.GetString(key, &string_value)) << key;
+  EXPECT_EQ(expected_value, string_value) << key;
+}
+
+void ExpectStringValue(const std::string& expected_str,
+                       StringValue* actual) {
+  scoped_ptr<StringValue> scoped_actual(actual);
+  std::string actual_str;
+  EXPECT_TRUE(scoped_actual->GetAsString(&actual_str));
+  EXPECT_EQ(expected_str, actual_str);
+}
+
+namespace test {
+
+scoped_ptr<Value> ParseJson(base::StringPiece json) {
+  std::string error_msg;
+  scoped_ptr<Value> result(base::JSONReader::ReadAndReturnError(
+      json, base::JSON_ALLOW_TRAILING_COMMAS,
+      NULL, &error_msg));
+  if (!result) {
+    ADD_FAILURE() << "Failed to parse \"" << json << "\": " << error_msg;
+    result.reset(Value::CreateNullValue());
+  }
+  return result.Pass();
+}
+
+}  // namespace test
+}  // namespace base
diff --git a/base/test/values_test_util.h b/base/test/values_test_util.h
new file mode 100644
index 0000000..86d91c3
--- /dev/null
+++ b/base/test/values_test_util.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_VALUES_TEST_UTIL_H_
+#define BASE_TEST_VALUES_TEST_UTIL_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+class StringValue;
+class Value;
+
+// All the functions below expect that the value for the given key in
+// the given dictionary equals the given expected value.
+
+void ExpectDictBooleanValue(bool expected_value,
+                            const DictionaryValue& value,
+                            const std::string& key);
+
+void ExpectDictDictionaryValue(const DictionaryValue& expected_value,
+                               const DictionaryValue& value,
+                               const std::string& key);
+
+void ExpectDictIntegerValue(int expected_value,
+                            const DictionaryValue& value,
+                            const std::string& key);
+
+void ExpectDictListValue(const ListValue& expected_value,
+                         const DictionaryValue& value,
+                         const std::string& key);
+
+void ExpectDictStringValue(const std::string& expected_value,
+                           const DictionaryValue& value,
+                           const std::string& key);
+
+// Takes ownership of |actual|.
+void ExpectStringValue(const std::string& expected_str,
+                       StringValue* actual);
+
+namespace test {
+
+// Parses |json| as JSON, allowing trailing commas, and returns the
+// resulting value.  If the json fails to parse, causes an EXPECT
+// failure and returns the Null Value (but never a NULL pointer).
+scoped_ptr<Value> ParseJson(base::StringPiece json);
+
+}  // namespace test
+}  // namespace base
+
+#endif  // BASE_TEST_VALUES_TEST_UTIL_H_