Import of bazel plugin using copybara

PiperOrigin-RevId: 145080193
diff --git a/aswb/2.3/tests/integrationtests/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProviderIntegrationTest.java b/aswb/2.3/tests/integrationtests/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProviderIntegrationTest.java
index 05f1ccf..0be0dc0 100644
--- a/aswb/2.3/tests/integrationtests/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProviderIntegrationTest.java
+++ b/aswb/2.3/tests/integrationtests/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProviderIntegrationTest.java
@@ -33,8 +33,11 @@
 import com.google.idea.blaze.base.ideinfo.TargetMap;
 import com.google.idea.blaze.base.ideinfo.TargetMapBuilder;
 import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataBuilder;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataManager;
 import com.google.idea.blaze.base.model.primitives.Kind;
 import com.google.idea.blaze.base.model.primitives.Label;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
 import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
 import com.intellij.facet.FacetManager;
 import com.intellij.facet.ModifiableFacetModel;
@@ -66,9 +69,13 @@
     ArtifactLocationDecoder decoder =
         (location) -> new File("/src", location.getExecutionRootRelativePath());
 
-    mockBlazeProjectDataManager(
-        new BlazeProjectData(
-            0L, buildTargetMap(), null, null, null, null, decoder, null, null, null));
+    BlazeProjectData blazeProjectData =
+        MockBlazeProjectDataBuilder.builder(workspaceRoot)
+            .setTargetMap(buildTargetMap())
+            .setArtifactLocationDecoder(decoder)
+            .build();
+    registerProjectService(
+        BlazeProjectDataManager.class, new MockBlazeProjectDataManager(blazeProjectData));
     classJarProvider = new BlazeClassJarProvider(getProject());
   }
 
diff --git a/aswb/2.3/tests/unittests/com/google/idea/blaze/android/project/BlazeBuildSystemServiceTest.java b/aswb/2.3/tests/unittests/com/google/idea/blaze/android/project/BlazeBuildSystemServiceTest.java
index 72541b3..9075d66 100755
--- a/aswb/2.3/tests/unittests/com/google/idea/blaze/android/project/BlazeBuildSystemServiceTest.java
+++ b/aswb/2.3/tests/unittests/com/google/idea/blaze/android/project/BlazeBuildSystemServiceTest.java
@@ -35,7 +35,10 @@
 import com.google.idea.blaze.base.ideinfo.TargetMapBuilder;
 import com.google.idea.blaze.base.lang.buildfile.references.BuildReferenceManager;
 import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataBuilder;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataManager;
 import com.google.idea.blaze.base.model.primitives.Label;
+import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
 import com.google.idea.blaze.base.projectview.ProjectViewManager;
 import com.google.idea.blaze.base.projectview.ProjectViewSet;
 import com.google.idea.blaze.base.scope.BlazeContext;
@@ -43,7 +46,6 @@
 import com.google.idea.blaze.base.settings.BlazeImportSettings;
 import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
 import com.google.idea.blaze.base.sync.BlazeSyncManager;
-import com.google.idea.blaze.base.sync.BlazeSyncPlugin.ModuleEditor;
 import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
 import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
 import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
@@ -71,6 +73,7 @@
 /** Test cases for {@link BlazeBuildSystemService}. */
 @RunWith(JUnit4.class)
 public class BlazeBuildSystemServiceTest extends BlazeTestCase {
+  WorkspaceRoot workspaceRoot = new WorkspaceRoot(new File("/"));
   Module module;
   BuildSystemService service;
 
@@ -164,18 +167,20 @@
     projectServices.register(BlazeImportSettingsManager.class, importSettingsManager);
   }
 
-  private static void createMocksForBuildProject(Container applicationServices) {
+  private void createMocksForBuildProject(Container applicationServices) {
     applicationServices.register(BlazeBuildService.class, mock(BlazeBuildService.class));
   }
 
-  private static void createMocksForSyncProject(Container projectServices) {
+  private void createMocksForSyncProject(Container projectServices) {
     projectServices.register(ProjectViewManager.class, new MockProjectViewManager());
     projectServices.register(BlazeSyncManager.class, mock(BlazeSyncManager.class));
   }
 
   private void createMocksForAddDependency(
       Container applicationServices, Container projectServices) {
-    projectServices.register(BlazeProjectDataManager.class, new MockProjectDataManager());
+    projectServices.register(
+        BlazeProjectDataManager.class,
+        new MockBlazeProjectDataManager(createMockBlazeProjectData()));
     projectServices.register(FileEditorManager.class, mock(FileEditorManager.class));
     projectServices.register(BuildReferenceManager.class, mock(BuildReferenceManager.class));
     projectServices.register(LazyRangeMarkerFactory.class, mock(LazyRangeMarkerFactoryImpl.class));
@@ -189,6 +194,22 @@
     projectServices.register(AndroidResourceModuleRegistry.class, moduleRegistry);
   }
 
+  private BlazeProjectData createMockBlazeProjectData() {
+    TargetMap targetMap =
+        TargetMapBuilder.builder()
+            .addTarget(
+                TargetIdeInfo.builder()
+                    .setLabel(new Label("//foo:bar"))
+                    .setBuildFile(ArtifactLocation.builder().setRelativePath("foo/BUILD").build())
+                    .build())
+            .build();
+    ArtifactLocationDecoder decoder = (location) -> new File("/", location.getRelativePath());
+    return MockBlazeProjectDataBuilder.builder(workspaceRoot)
+        .setTargetMap(targetMap)
+        .setArtifactLocationDecoder(decoder)
+        .build();
+  }
+
   private static class MockProjectViewManager extends ProjectViewManager {
     private ProjectViewSet viewSet;
 
@@ -210,36 +231,6 @@
     }
   }
 
-  private static class MockProjectDataManager implements BlazeProjectDataManager {
-    private BlazeProjectData projectData;
-
-    public MockProjectDataManager() {
-      TargetMap targetMap =
-          TargetMapBuilder.builder()
-              .addTarget(
-                  TargetIdeInfo.builder()
-                      .setLabel(new Label("//foo:bar"))
-                      .setBuildFile(ArtifactLocation.builder().setRelativePath("foo/BUILD").build())
-                      .build())
-              .build();
-      ArtifactLocationDecoder decoder = (location) -> new File("/", location.getRelativePath());
-
-      projectData =
-          new BlazeProjectData(0L, targetMap, null, null, null, null, decoder, null, null, null);
-    }
-
-    @Nullable
-    @Override
-    public BlazeProjectData getBlazeProjectData() {
-      return projectData;
-    }
-
-    @Override
-    public ModuleEditor editModules() {
-      return null;
-    }
-  }
-
   private static class MockFileSystem extends TempFileSystem {
     private Map<String, VirtualFile> files;
 
diff --git a/aswb/2.3/tests/unittests/com/google/idea/blaze/android/project/BlazeFeatureEnabledServiceTest.java b/aswb/2.3/tests/unittests/com/google/idea/blaze/android/project/BlazeFeatureEnabledServiceTest.java
index e4fe775..efd77ea 100644
--- a/aswb/2.3/tests/unittests/com/google/idea/blaze/android/project/BlazeFeatureEnabledServiceTest.java
+++ b/aswb/2.3/tests/unittests/com/google/idea/blaze/android/project/BlazeFeatureEnabledServiceTest.java
@@ -28,7 +28,6 @@
 import com.google.idea.blaze.base.settings.BlazeImportSettings;
 import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
 import com.google.idea.blaze.base.settings.BlazeImportSettingsManagerLegacy;
-import com.google.idea.blaze.base.sync.BlazeSyncPlugin.ModuleEditor;
 import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
 import com.google.idea.common.experiments.ExperimentService;
 import com.intellij.openapi.extensions.ExtensionPoint;
@@ -116,11 +115,6 @@
     public BlazeProjectData getBlazeProjectData() {
       return blazeProjectData;
     }
-
-    @Override
-    public ModuleEditor editModules() {
-      return null;
-    }
   }
 
   private static class MockExperimentService implements ExperimentService {
diff --git a/aswb/2.3/tests/unittests/com/google/idea/blaze/android/rendering/BlazeRenderErrorContributorTest.java b/aswb/2.3/tests/unittests/com/google/idea/blaze/android/rendering/BlazeRenderErrorContributorTest.java
index 5bbeec6..ffe8267 100644
--- a/aswb/2.3/tests/unittests/com/google/idea/blaze/android/rendering/BlazeRenderErrorContributorTest.java
+++ b/aswb/2.3/tests/unittests/com/google/idea/blaze/android/rendering/BlazeRenderErrorContributorTest.java
@@ -38,12 +38,13 @@
 import com.google.idea.blaze.base.ideinfo.TargetMapBuilder;
 import com.google.idea.blaze.base.lang.buildfile.references.BuildReferenceManager;
 import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataBuilder;
 import com.google.idea.blaze.base.model.primitives.Label;
+import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
 import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
 import com.google.idea.blaze.base.settings.BlazeImportSettings;
 import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
 import com.google.idea.blaze.base.settings.BlazeImportSettingsManagerLegacy;
-import com.google.idea.blaze.base.sync.BlazeSyncPlugin.ModuleEditor;
 import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
 import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
 import com.google.idea.blaze.base.targetmaps.SourceToTargetMap;
@@ -86,6 +87,7 @@
   private static final String NON_STANDARD_MANIFEST_NAME_ERROR = "Non-standard manifest name";
   private static final String MISSING_CLASS_DEPENDENCIES_ERROR = "Missing class dependencies";
 
+  private static final WorkspaceRoot workspaceRoot = new WorkspaceRoot(new File("/"));
   private Module module;
   private MockBlazeProjectDataManager projectDataManager;
   private BlazeRenderErrorContributor.BlazeProvider provider;
@@ -687,7 +689,10 @@
       ArtifactLocationDecoder decoder =
           (location) -> new File("/src", location.getExecutionRootRelativePath());
       this.blazeProjectData =
-          new BlazeProjectData(0L, targetMap, null, null, null, null, decoder, null, null, null);
+          MockBlazeProjectDataBuilder.builder(workspaceRoot)
+              .setTargetMap(targetMap)
+              .setArtifactLocationDecoder(decoder)
+              .build();
     }
 
     @Nullable
@@ -695,11 +700,6 @@
     public BlazeProjectData getBlazeProjectData() {
       return blazeProjectData;
     }
-
-    @Override
-    public ModuleEditor editModules() {
-      return null;
-    }
   }
 
   private static class MockBuildReferenceManager extends BuildReferenceManager {
diff --git a/aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java b/aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java
index cd5ff8f..1b55ff1 100644
--- a/aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java
+++ b/aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java
@@ -45,8 +45,10 @@
 import com.google.idea.blaze.base.projectview.section.sections.TargetSection;
 import com.google.idea.blaze.base.scope.BlazeContext;
 import com.google.idea.blaze.base.scope.output.PrintOutput;
+import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
 import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
 import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import com.google.idea.blaze.base.sync.projectstructure.ModuleEditorProvider;
 import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
 import com.intellij.execution.RunManager;
 import com.intellij.execution.configurations.RunConfiguration;
@@ -240,7 +242,9 @@
     }
 
     BlazeSyncPlugin.ModuleEditor moduleEditor =
-        BlazeProjectDataManager.getInstance(project).editModules();
+        ModuleEditorProvider.getInstance()
+            .getModuleEditor(
+                project, BlazeImportSettingsManager.getInstance(project).getImportSettings());
     Module newModule = moduleEditor.createModule(moduleName, StdModuleTypes.JAVA);
     ApplicationManager.getApplication()
         .runWriteAction(
diff --git a/base/src/com/google/idea/blaze/base/model/BlazeProjectData.java b/base/src/com/google/idea/blaze/base/model/BlazeProjectData.java
index 4a401c8..77862fd 100644
--- a/base/src/com/google/idea/blaze/base/model/BlazeProjectData.java
+++ b/base/src/com/google/idea/blaze/base/model/BlazeProjectData.java
@@ -52,7 +52,8 @@
       ArtifactLocationDecoder artifactLocationDecoder,
       WorkspaceLanguageSettings workspaceLanguageSettings,
       SyncState syncState,
-      ImmutableMultimap<TargetKey, TargetKey> reverseDependencies) {
+      ImmutableMultimap<TargetKey, TargetKey> reverseDependencies,
+      Object dummy) {
     this.syncTime = syncTime;
     this.targetMap = targetMap;
     this.blazeInfo = blazeInfo;
diff --git a/base/src/com/google/idea/blaze/base/sync/BlazeSyncTask.java b/base/src/com/google/idea/blaze/base/sync/BlazeSyncTask.java
index 1fb59c9..db199e3 100644
--- a/base/src/com/google/idea/blaze/base/sync/BlazeSyncTask.java
+++ b/base/src/com/google/idea/blaze/base/sync/BlazeSyncTask.java
@@ -392,7 +392,8 @@
             artifactLocationDecoder,
             workspaceLanguageSettings,
             syncStateBuilder.build(),
-            reverseDependencies);
+            reverseDependencies,
+            null);
 
     FileCaches.onSync(project, context, projectViewSet, newBlazeProjectData, syncMode);
     ListenableFuture<?> prefetch =
diff --git a/base/src/com/google/idea/blaze/base/sync/data/BlazeProjectDataManager.java b/base/src/com/google/idea/blaze/base/sync/data/BlazeProjectDataManager.java
index f92cf85..4109a1f 100644
--- a/base/src/com/google/idea/blaze/base/sync/data/BlazeProjectDataManager.java
+++ b/base/src/com/google/idea/blaze/base/sync/data/BlazeProjectDataManager.java
@@ -16,7 +16,6 @@
 package com.google.idea.blaze.base.sync.data;
 
 import com.google.idea.blaze.base.model.BlazeProjectData;
-import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
 import com.intellij.openapi.components.ServiceManager;
 import com.intellij.openapi.project.Project;
 import javax.annotation.Nullable;
@@ -29,6 +28,4 @@
 
   @Nullable
   BlazeProjectData getBlazeProjectData();
-
-  BlazeSyncPlugin.ModuleEditor editModules();
 }
diff --git a/base/src/com/google/idea/blaze/base/sync/data/BlazeProjectDataManagerImpl.java b/base/src/com/google/idea/blaze/base/sync/data/BlazeProjectDataManagerImpl.java
index 821109d..754e60f 100644
--- a/base/src/com/google/idea/blaze/base/sync/data/BlazeProjectDataManagerImpl.java
+++ b/base/src/com/google/idea/blaze/base/sync/data/BlazeProjectDataManagerImpl.java
@@ -21,9 +21,7 @@
 import com.google.idea.blaze.base.scope.BlazeContext;
 import com.google.idea.blaze.base.scope.output.StatusOutput;
 import com.google.idea.blaze.base.settings.BlazeImportSettings;
-import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
 import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
-import com.google.idea.blaze.base.sync.projectstructure.ModuleEditorProvider;
 import com.google.idea.blaze.base.util.SerializationUtil;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.progress.ProgressIndicator;
@@ -71,13 +69,6 @@
     return blazeProjectData;
   }
 
-  @Override
-  public BlazeSyncPlugin.ModuleEditor editModules() {
-    return ModuleEditorProvider.getInstance()
-        .getModuleEditor(
-            project, BlazeImportSettingsManager.getInstance(project).getImportSettings());
-  }
-
   @Nullable
   private synchronized BlazeProjectData loadProject(
       BlazeContext context, BlazeImportSettings importSettings) {
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/lang/projectview/ProjectViewIntegrationTestCase.java b/base/tests/integrationtests/com/google/idea/blaze/base/lang/projectview/ProjectViewIntegrationTestCase.java
index 960660e..63b36c6 100644
--- a/base/tests/integrationtests/com/google/idea/blaze/base/lang/projectview/ProjectViewIntegrationTestCase.java
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/lang/projectview/ProjectViewIntegrationTestCase.java
@@ -15,20 +15,11 @@
  */
 package com.google.idea.blaze.base.lang.projectview;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.idea.blaze.base.BlazeIntegrationTestCase;
 import com.google.idea.blaze.base.EditorTestHelper;
-import com.google.idea.blaze.base.ideinfo.TargetMap;
-import com.google.idea.blaze.base.model.BlazeProjectData;
-import com.google.idea.blaze.base.model.BlazeVersionData;
-import com.google.idea.blaze.base.model.SyncState;
-import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
-import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
-import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoderImpl;
-import com.google.idea.blaze.base.sync.workspace.BlazeRoots;
-import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
-import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataBuilder;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataManager;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
 import org.junit.Before;
 
 /** Project view file specific integration test base */
@@ -37,32 +28,9 @@
 
   @Before
   public final void doSetup() {
-    mockBlazeProjectDataManager(getMockBlazeProjectData());
+    BlazeProjectDataManager mockProjectDataManager =
+        new MockBlazeProjectDataManager(MockBlazeProjectDataBuilder.builder(workspaceRoot).build());
+    registerProjectService(BlazeProjectDataManager.class, mockProjectDataManager);
     editorTest = new EditorTestHelper(getProject(), testFixture);
   }
-
-  private BlazeProjectData getMockBlazeProjectData() {
-    BlazeRoots fakeRoots =
-        new BlazeRoots(
-            null,
-            ImmutableList.of(workspaceRoot.directory()),
-            new ExecutionRootPath("out/crosstool/bin"),
-            new ExecutionRootPath("out/crosstool/gen"),
-            null);
-    WorkspacePathResolver workspacePathResolver =
-        new WorkspacePathResolverImpl(workspaceRoot, fakeRoots);
-    ArtifactLocationDecoder artifactLocationDecoder =
-        new ArtifactLocationDecoderImpl(fakeRoots, workspacePathResolver);
-    return new BlazeProjectData(
-        0,
-        new TargetMap(ImmutableMap.of()),
-        ImmutableMap.of(),
-        fakeRoots,
-        new BlazeVersionData(),
-        workspacePathResolver,
-        artifactLocationDecoder,
-        null,
-        new SyncState.Builder().build(),
-        null);
-  }
 }
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationGenericHandlerIntegrationTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationGenericHandlerIntegrationTest.java
index eb8d69f..b6b1c80 100644
--- a/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationGenericHandlerIntegrationTest.java
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationGenericHandlerIntegrationTest.java
@@ -18,22 +18,15 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.idea.blaze.base.BlazeIntegrationTestCase;
 import com.google.idea.blaze.base.command.BlazeCommandName;
-import com.google.idea.blaze.base.ideinfo.TargetMap;
-import com.google.idea.blaze.base.model.BlazeProjectData;
-import com.google.idea.blaze.base.model.BlazeVersionData;
-import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataBuilder;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataManager;
 import com.google.idea.blaze.base.model.primitives.TargetExpression;
 import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration.BlazeCommandRunConfigurationSettingsEditor;
 import com.google.idea.blaze.base.run.confighandler.BlazeCommandGenericRunConfigurationHandler;
 import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
-import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
-import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoderImpl;
-import com.google.idea.blaze.base.sync.workspace.BlazeRoots;
-import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
-import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
 import com.intellij.openapi.options.ConfigurationException;
 import com.intellij.openapi.util.Disposer;
 import org.jdom.Element;
@@ -57,36 +50,13 @@
   @Before
   public final void doSetup() throws Exception {
     // Without BlazeProjectData, the configuration editor is always disabled.
-    mockBlazeProjectDataManager(getMockBlazeProjectData());
+    BlazeProjectDataManager mockProjectDataManager =
+        new MockBlazeProjectDataManager(MockBlazeProjectDataBuilder.builder(workspaceRoot).build());
+    registerProjectService(BlazeProjectDataManager.class, mockProjectDataManager);
     type = BlazeCommandRunConfigurationType.getInstance();
     configuration = type.getFactory().createTemplateConfiguration(getProject());
   }
 
-  private BlazeProjectData getMockBlazeProjectData() {
-    BlazeRoots fakeRoots =
-        new BlazeRoots(
-            null,
-            ImmutableList.of(workspaceRoot.directory()),
-            new ExecutionRootPath("out/crosstool/bin"),
-            new ExecutionRootPath("out/crosstool/gen"),
-            null);
-    WorkspacePathResolver workspacePathResolver =
-        new WorkspacePathResolverImpl(workspaceRoot, fakeRoots);
-    ArtifactLocationDecoder artifactLocationDecoder =
-        new ArtifactLocationDecoderImpl(fakeRoots, workspacePathResolver);
-    return new BlazeProjectData(
-        0,
-        new TargetMap(ImmutableMap.of()),
-        ImmutableMap.of(),
-        fakeRoots,
-        new BlazeVersionData(),
-        workspacePathResolver,
-        artifactLocationDecoder,
-        null,
-        null,
-        null);
-  }
-
   @Test
   public void testNewConfigurationHasGenericHandler() {
     assertThat(configuration.getHandler())
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationRunManagerImplTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationRunManagerImplTest.java
index 237fa7f..d1e3b7f 100644
--- a/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationRunManagerImplTest.java
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationRunManagerImplTest.java
@@ -18,20 +18,12 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.idea.blaze.base.BlazeIntegrationTestCase;
-import com.google.idea.blaze.base.ideinfo.TargetMap;
-import com.google.idea.blaze.base.model.BlazeProjectData;
-import com.google.idea.blaze.base.model.BlazeVersionData;
-import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataBuilder;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataManager;
 import com.google.idea.blaze.base.model.primitives.Label;
 import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration.BlazeCommandRunConfigurationSettingsEditor;
-import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
-import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoderImpl;
-import com.google.idea.blaze.base.sync.workspace.BlazeRoots;
-import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
-import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
 import com.intellij.execution.RunnerAndConfigurationSettings;
 import com.intellij.execution.configurations.ConfigurationType;
 import com.intellij.execution.configurations.RunConfiguration;
@@ -62,7 +54,9 @@
   public final void doSetup() {
     runManager = RunManagerImpl.getInstanceImpl(getProject());
     // Without BlazeProjectData, the configuration editor is always disabled.
-    mockBlazeProjectDataManager(getMockBlazeProjectData());
+    BlazeProjectDataManager mockProjectDataManager =
+        new MockBlazeProjectDataManager(MockBlazeProjectDataBuilder.builder(workspaceRoot).build());
+    registerProjectService(BlazeProjectDataManager.class, mockProjectDataManager);
     type = BlazeCommandRunConfigurationType.getInstance();
 
     RunnerAndConfigurationSettings runnerAndConfigurationSettings =
@@ -72,31 +66,6 @@
         (BlazeCommandRunConfiguration) runnerAndConfigurationSettings.getConfiguration();
   }
 
-  private BlazeProjectData getMockBlazeProjectData() {
-    BlazeRoots fakeRoots =
-        new BlazeRoots(
-            null,
-            ImmutableList.of(workspaceRoot.directory()),
-            new ExecutionRootPath("out/crosstool/bin"),
-            new ExecutionRootPath("out/crosstool/gen"),
-            null);
-    WorkspacePathResolver workspacePathResolver =
-        new WorkspacePathResolverImpl(workspaceRoot, fakeRoots);
-    ArtifactLocationDecoder artifactLocationDecoder =
-        new ArtifactLocationDecoderImpl(fakeRoots, workspacePathResolver);
-    return new BlazeProjectData(
-        0,
-        new TargetMap(ImmutableMap.of()),
-        ImmutableMap.of(),
-        fakeRoots,
-        new BlazeVersionData(),
-        workspacePathResolver,
-        artifactLocationDecoder,
-        null,
-        null,
-        null);
-  }
-
   @After
   public final void doTeardown() {
     runManager.clearAll();
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationSettingsEditorTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationSettingsEditorTest.java
index b9a2b48..61b71f5 100644
--- a/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationSettingsEditorTest.java
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/run/BlazeCommandRunConfigurationSettingsEditorTest.java
@@ -17,20 +17,12 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.idea.blaze.base.BlazeIntegrationTestCase;
-import com.google.idea.blaze.base.ideinfo.TargetMap;
-import com.google.idea.blaze.base.model.BlazeProjectData;
-import com.google.idea.blaze.base.model.BlazeVersionData;
-import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataBuilder;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataManager;
 import com.google.idea.blaze.base.model.primitives.Label;
 import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration.BlazeCommandRunConfigurationSettingsEditor;
-import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
-import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoderImpl;
-import com.google.idea.blaze.base.sync.workspace.BlazeRoots;
-import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
-import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
 import com.intellij.openapi.options.ConfigurationException;
 import com.intellij.openapi.util.Disposer;
 import org.junit.Before;
@@ -48,36 +40,13 @@
   @Before
   public final void doSetup() throws Exception {
     // Without BlazeProjectData, the configuration editor is always disabled.
-    mockBlazeProjectDataManager(getMockBlazeProjectData());
+    BlazeProjectDataManager mockProjectDataManager =
+        new MockBlazeProjectDataManager(MockBlazeProjectDataBuilder.builder(workspaceRoot).build());
+    registerProjectService(BlazeProjectDataManager.class, mockProjectDataManager);
     type = BlazeCommandRunConfigurationType.getInstance();
     configuration = type.getFactory().createTemplateConfiguration(getProject());
   }
 
-  private BlazeProjectData getMockBlazeProjectData() {
-    BlazeRoots fakeRoots =
-        new BlazeRoots(
-            null,
-            ImmutableList.of(workspaceRoot.directory()),
-            new ExecutionRootPath("out/crosstool/bin"),
-            new ExecutionRootPath("out/crosstool/gen"),
-            null);
-    WorkspacePathResolver workspacePathResolver =
-        new WorkspacePathResolverImpl(workspaceRoot, fakeRoots);
-    ArtifactLocationDecoder artifactLocationDecoder =
-        new ArtifactLocationDecoderImpl(fakeRoots, workspacePathResolver);
-    return new BlazeProjectData(
-        0,
-        new TargetMap(ImmutableMap.of()),
-        ImmutableMap.of(),
-        fakeRoots,
-        new BlazeVersionData(),
-        workspacePathResolver,
-        artifactLocationDecoder,
-        null,
-        null,
-        null);
-  }
-
   @Test
   public void testEditorApplyToAndResetFromMatches() throws ConfigurationException {
     BlazeCommandRunConfigurationSettingsEditor editor =
diff --git a/base/tests/integrationtests/com/google/idea/blaze/base/run/exporter/RunConfigurationSerializerTest.java b/base/tests/integrationtests/com/google/idea/blaze/base/run/exporter/RunConfigurationSerializerTest.java
index fd5896b..775e65f 100644
--- a/base/tests/integrationtests/com/google/idea/blaze/base/run/exporter/RunConfigurationSerializerTest.java
+++ b/base/tests/integrationtests/com/google/idea/blaze/base/run/exporter/RunConfigurationSerializerTest.java
@@ -17,21 +17,13 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.idea.blaze.base.BlazeIntegrationTestCase;
-import com.google.idea.blaze.base.ideinfo.TargetMap;
-import com.google.idea.blaze.base.model.BlazeProjectData;
-import com.google.idea.blaze.base.model.BlazeVersionData;
-import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataBuilder;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataManager;
 import com.google.idea.blaze.base.model.primitives.Label;
 import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
 import com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType;
-import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
-import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoderImpl;
-import com.google.idea.blaze.base.sync.workspace.BlazeRoots;
-import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
-import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
 import com.intellij.execution.RunnerAndConfigurationSettings;
 import com.intellij.execution.configurations.ConfigurationType;
 import com.intellij.execution.configurations.RunConfiguration;
@@ -61,7 +53,9 @@
   public final void doSetup() {
     runManager = RunManagerImpl.getInstanceImpl(getProject());
     // Without BlazeProjectData, the configuration editor is always disabled.
-    mockBlazeProjectDataManager(getMockBlazeProjectData());
+    BlazeProjectDataManager mockProjectDataManager =
+        new MockBlazeProjectDataManager(MockBlazeProjectDataBuilder.builder(workspaceRoot).build());
+    registerProjectService(BlazeProjectDataManager.class, mockProjectDataManager);
     type = BlazeCommandRunConfigurationType.getInstance();
 
     RunnerAndConfigurationSettings runnerAndConfigurationSettings =
@@ -71,31 +65,6 @@
         (BlazeCommandRunConfiguration) runnerAndConfigurationSettings.getConfiguration();
   }
 
-  private BlazeProjectData getMockBlazeProjectData() {
-    BlazeRoots fakeRoots =
-        new BlazeRoots(
-            null,
-            ImmutableList.of(workspaceRoot.directory()),
-            new ExecutionRootPath("out/crosstool/bin"),
-            new ExecutionRootPath("out/crosstool/gen"),
-            null);
-    WorkspacePathResolver workspacePathResolver =
-        new WorkspacePathResolverImpl(workspaceRoot, fakeRoots);
-    ArtifactLocationDecoder artifactLocationDecoder =
-        new ArtifactLocationDecoderImpl(fakeRoots, workspacePathResolver);
-    return new BlazeProjectData(
-        0,
-        new TargetMap(ImmutableMap.of()),
-        ImmutableMap.of(),
-        fakeRoots,
-        new BlazeVersionData(),
-        workspacePathResolver,
-        artifactLocationDecoder,
-        null,
-        null,
-        null);
-  }
-
   @After
   public final void doTeardown() {
     clearRunManager();
diff --git a/base/tests/unittests/com/google/idea/blaze/base/actions/BlazeBuildServiceTest.java b/base/tests/unittests/com/google/idea/blaze/base/actions/BlazeBuildServiceTest.java
index e86bd3f..6ca947d 100644
--- a/base/tests/unittests/com/google/idea/blaze/base/actions/BlazeBuildServiceTest.java
+++ b/base/tests/unittests/com/google/idea/blaze/base/actions/BlazeBuildServiceTest.java
@@ -27,8 +27,11 @@
 import com.google.common.collect.Lists;
 import com.google.idea.blaze.base.BlazeTestCase;
 import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataBuilder;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataManager;
 import com.google.idea.blaze.base.model.primitives.Label;
 import com.google.idea.blaze.base.model.primitives.TargetExpression;
+import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
 import com.google.idea.blaze.base.projectview.ProjectView;
 import com.google.idea.blaze.base.projectview.ProjectViewManager;
 import com.google.idea.blaze.base.projectview.ProjectViewSet;
@@ -38,7 +41,6 @@
 import com.google.idea.blaze.base.settings.Blaze;
 import com.google.idea.blaze.base.settings.BlazeImportSettings;
 import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
-import com.google.idea.blaze.base.sync.BlazeSyncPlugin.ModuleEditor;
 import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
 import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
 import java.io.File;
@@ -51,8 +53,9 @@
 /** Test cases for {@link BlazeBuildService}. */
 @RunWith(JUnit4.class)
 public class BlazeBuildServiceTest extends BlazeTestCase {
-  BlazeBuildService service;
-  ProjectViewSet viewSet;
+  private BlazeBuildService service;
+  private ProjectViewSet viewSet;
+  private final WorkspaceRoot workspaceRoot = new WorkspaceRoot(new File("/"));
 
   @Override
   protected void initTest(Container applicationServices, Container projectServices) {
@@ -72,7 +75,9 @@
     ProjectViewManager viewManager = new MockProjectViewManager(viewSet);
     projectServices.register(ProjectViewManager.class, viewManager);
 
-    projectServices.register(BlazeProjectDataManager.class, new MockProjectDataManager());
+    BlazeProjectData blazeProjectData = MockBlazeProjectDataBuilder.builder(workspaceRoot).build();
+    projectServices.register(
+        BlazeProjectDataManager.class, new MockBlazeProjectDataManager(blazeProjectData));
 
     applicationServices.register(BlazeBuildService.class, spy(new BlazeBuildService()));
 
@@ -122,24 +127,4 @@
       return viewSet;
     }
   }
-
-  private static class MockProjectDataManager implements BlazeProjectDataManager {
-    private final BlazeProjectData projectData;
-
-    public MockProjectDataManager() {
-      this.projectData =
-          new BlazeProjectData(0L, null, null, null, null, null, null, null, null, null);
-    }
-
-    @Nullable
-    @Override
-    public BlazeProjectData getBlazeProjectData() {
-      return projectData;
-    }
-
-    @Override
-    public ModuleEditor editModules() {
-      return null;
-    }
-  }
 }
diff --git a/base/tests/unittests/com/google/idea/blaze/base/targetmaps/TransitiveDependencyMapTest.java b/base/tests/unittests/com/google/idea/blaze/base/targetmaps/TransitiveDependencyMapTest.java
index 3037ff1..922cbd1 100644
--- a/base/tests/unittests/com/google/idea/blaze/base/targetmaps/TransitiveDependencyMapTest.java
+++ b/base/tests/unittests/com/google/idea/blaze/base/targetmaps/TransitiveDependencyMapTest.java
@@ -22,11 +22,12 @@
 import com.google.idea.blaze.base.ideinfo.TargetKey;
 import com.google.idea.blaze.base.ideinfo.TargetMap;
 import com.google.idea.blaze.base.ideinfo.TargetMapBuilder;
-import com.google.idea.blaze.base.model.BlazeProjectData;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataBuilder;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataManager;
 import com.google.idea.blaze.base.model.primitives.Label;
-import com.google.idea.blaze.base.sync.BlazeSyncPlugin.ModuleEditor;
+import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
 import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
-import javax.annotation.Nullable;
+import java.io.File;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -35,6 +36,7 @@
 @RunWith(JUnit4.class)
 public class TransitiveDependencyMapTest extends BlazeTestCase {
   private TransitiveDependencyMap transitiveDependencyMap;
+  private final WorkspaceRoot workspaceRoot = new WorkspaceRoot(new File("/"));
 
   @Override
   protected void initTest(Container applicationServices, Container projectServices) {
@@ -42,8 +44,9 @@
     projectServices.register(
         BlazeProjectDataManager.class,
         new MockBlazeProjectDataManager(
-            new BlazeProjectData(
-                0L, buildTargetMap(), null, null, null, null, null, null, null, null)));
+            MockBlazeProjectDataBuilder.builder(workspaceRoot)
+                .setTargetMap(buildTargetMap())
+                .build()));
     projectServices.register(TransitiveDependencyMap.class, new TransitiveDependencyMap(project));
     transitiveDependencyMap = TransitiveDependencyMap.getInstance(project);
   }
@@ -145,23 +148,4 @@
         .addTarget(TargetIdeInfo.builder().setLabel(diamondCCC))
         .build();
   }
-
-  private static class MockBlazeProjectDataManager implements BlazeProjectDataManager {
-    private final BlazeProjectData blazeProjectData;
-
-    public MockBlazeProjectDataManager(BlazeProjectData blazeProjectData) {
-      this.blazeProjectData = blazeProjectData;
-    }
-
-    @Nullable
-    @Override
-    public BlazeProjectData getBlazeProjectData() {
-      return blazeProjectData;
-    }
-
-    @Override
-    public ModuleEditor editModules() {
-      return null;
-    }
-  }
 }
diff --git a/base/tests/utils/integration/com/google/idea/blaze/base/BlazeIntegrationTestCase.java b/base/tests/utils/integration/com/google/idea/blaze/base/BlazeIntegrationTestCase.java
index 81d3e75..bd21294 100644
--- a/base/tests/utils/integration/com/google/idea/blaze/base/BlazeIntegrationTestCase.java
+++ b/base/tests/utils/integration/com/google/idea/blaze/base/BlazeIntegrationTestCase.java
@@ -17,15 +17,11 @@
 
 import com.google.idea.blaze.base.io.FileAttributeProvider;
 import com.google.idea.blaze.base.io.InputStreamProvider;
-import com.google.idea.blaze.base.model.BlazeProjectData;
 import com.google.idea.blaze.base.model.primitives.WorkspacePath;
 import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
 import com.google.idea.blaze.base.settings.Blaze.BuildSystem;
 import com.google.idea.blaze.base.settings.BlazeImportSettings;
 import com.google.idea.blaze.base.settings.BlazeImportSettingsManager;
-import com.google.idea.blaze.base.sync.BlazeSyncPlugin;
-import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
-import com.google.idea.blaze.base.sync.projectstructure.ModuleEditorProvider;
 import com.google.idea.testing.EdtRule;
 import com.google.idea.testing.IntellijTestSetupRule;
 import com.google.idea.testing.ServiceHelper;
@@ -48,7 +44,6 @@
 import com.intellij.util.ThrowableRunnable;
 import java.io.File;
 import java.io.FileNotFoundException;
-import javax.annotation.Nullable;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -176,24 +171,4 @@
   protected <T> void registerExtension(ExtensionPointName<T> name, T instance) {
     ServiceHelper.registerExtension(name, instance, getTestRootDisposable());
   }
-
-  protected void mockBlazeProjectDataManager(BlazeProjectData data) {
-    BlazeProjectDataManager mockProjectDataManager =
-        new BlazeProjectDataManager() {
-          @Nullable
-          @Override
-          public BlazeProjectData getBlazeProjectData() {
-            return data;
-          }
-
-          @Override
-          public BlazeSyncPlugin.ModuleEditor editModules() {
-            return ModuleEditorProvider.getInstance()
-                .getModuleEditor(
-                    getProject(),
-                    BlazeImportSettingsManager.getInstance(getProject()).getImportSettings());
-          }
-        };
-    registerProjectService(BlazeProjectDataManager.class, mockProjectDataManager);
-  }
 }
diff --git a/base/tests/utils/integration/com/google/idea/blaze/base/lang/buildfile/BuildFileIntegrationTestCase.java b/base/tests/utils/integration/com/google/idea/blaze/base/lang/buildfile/BuildFileIntegrationTestCase.java
index 78d1ec3..44ce8c1 100644
--- a/base/tests/utils/integration/com/google/idea/blaze/base/lang/buildfile/BuildFileIntegrationTestCase.java
+++ b/base/tests/utils/integration/com/google/idea/blaze/base/lang/buildfile/BuildFileIntegrationTestCase.java
@@ -18,22 +18,13 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.idea.blaze.base.BlazeIntegrationTestCase;
 import com.google.idea.blaze.base.EditorTestHelper;
-import com.google.idea.blaze.base.ideinfo.TargetMap;
 import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
-import com.google.idea.blaze.base.model.BlazeProjectData;
-import com.google.idea.blaze.base.model.BlazeVersionData;
-import com.google.idea.blaze.base.model.SyncState;
-import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataBuilder;
+import com.google.idea.blaze.base.model.MockBlazeProjectDataManager;
 import com.google.idea.blaze.base.model.primitives.WorkspacePath;
-import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
-import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoderImpl;
-import com.google.idea.blaze.base.sync.workspace.BlazeRoots;
-import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
-import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.PsiFile;
 import org.junit.Before;
@@ -44,7 +35,9 @@
 
   @Before
   public final void doSetup() {
-    mockBlazeProjectDataManager(getMockBlazeProjectData());
+    BlazeProjectDataManager mockProjectDataManager =
+        new MockBlazeProjectDataManager(MockBlazeProjectDataBuilder.builder(workspaceRoot).build());
+    registerProjectService(BlazeProjectDataManager.class, mockProjectDataManager);
     editorTest = new EditorTestHelper(getProject(), testFixture);
   }
 
@@ -66,29 +59,4 @@
     String contents = Joiner.on("\n").join(contentLines);
     assertThat(file.getText()).isEqualTo(contents);
   }
-
-  private BlazeProjectData getMockBlazeProjectData() {
-    BlazeRoots fakeRoots =
-        new BlazeRoots(
-            null,
-            ImmutableList.of(workspaceRoot.directory()),
-            new ExecutionRootPath("out/crosstool/bin"),
-            new ExecutionRootPath("out/crosstool/gen"),
-            null);
-    WorkspacePathResolver workspacePathResolver =
-        new WorkspacePathResolverImpl(workspaceRoot, fakeRoots);
-    ArtifactLocationDecoder artifactLocationDecoder =
-        new ArtifactLocationDecoderImpl(fakeRoots, workspacePathResolver);
-    return new BlazeProjectData(
-        0,
-        new TargetMap(ImmutableMap.of()),
-        ImmutableMap.of(),
-        fakeRoots,
-        new BlazeVersionData(),
-        workspacePathResolver,
-        artifactLocationDecoder,
-        null,
-        new SyncState.Builder().build(),
-        null);
-  }
 }
diff --git a/base/tests/utils/unit/com/google/idea/blaze/base/model/MockBlazeProjectDataBuilder.java b/base/tests/utils/unit/com/google/idea/blaze/base/model/MockBlazeProjectDataBuilder.java
new file mode 100644
index 0000000..25bd08d
--- /dev/null
+++ b/base/tests/utils/unit/com/google/idea/blaze/base/model/MockBlazeProjectDataBuilder.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.model;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.idea.blaze.base.ideinfo.TargetKey;
+import com.google.idea.blaze.base.ideinfo.TargetMap;
+import com.google.idea.blaze.base.model.primitives.ExecutionRootPath;
+import com.google.idea.blaze.base.model.primitives.WorkspaceRoot;
+import com.google.idea.blaze.base.model.primitives.WorkspaceType;
+import com.google.idea.blaze.base.sync.projectview.WorkspaceLanguageSettings;
+import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoder;
+import com.google.idea.blaze.base.sync.workspace.ArtifactLocationDecoderImpl;
+import com.google.idea.blaze.base.sync.workspace.BlazeRoots;
+import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver;
+import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolverImpl;
+
+/**
+ * Use to build mock praject data for tests.
+ *
+ * <p>For any data you don't supply, the builder make a best-effort attempt to create default
+ * objects using whatever data you have supplied if applicable.
+ */
+public class MockBlazeProjectDataBuilder {
+  private final WorkspaceRoot workspaceRoot;
+
+  private long syncTime = 0;
+  private TargetMap targetMap;
+  private ImmutableMap<String, String> blazeInfo;
+  private BlazeRoots blazeRoots;
+  private BlazeVersionData blazeVersionData;
+  private WorkspacePathResolver workspacePathResolver;
+  private ArtifactLocationDecoder artifactLocationDecoder;
+  private WorkspaceLanguageSettings workspaceLanguageSettings;
+  private SyncState syncState;
+  private ImmutableMultimap<TargetKey, TargetKey> reverseDependencies;
+
+  public MockBlazeProjectDataBuilder(WorkspaceRoot workspaceRoot) {
+    this.workspaceRoot = workspaceRoot;
+  }
+
+  public static MockBlazeProjectDataBuilder builder(WorkspaceRoot workspaceRoot) {
+    return new MockBlazeProjectDataBuilder(workspaceRoot);
+  }
+
+  public MockBlazeProjectDataBuilder setSyncTime(long syncTime) {
+    this.syncTime = syncTime;
+    return this;
+  }
+
+  public MockBlazeProjectDataBuilder setTargetMap(TargetMap targetMap) {
+    this.targetMap = targetMap;
+    return this;
+  }
+
+  public MockBlazeProjectDataBuilder setBlazeInfo(ImmutableMap<String, String> blazeInfo) {
+    this.blazeInfo = blazeInfo;
+    return this;
+  }
+
+  public MockBlazeProjectDataBuilder setBlazeRoots(BlazeRoots blazeRoots) {
+    this.blazeRoots = blazeRoots;
+    return this;
+  }
+
+  public MockBlazeProjectDataBuilder setBlazeVersionData(BlazeVersionData blazeVersionData) {
+    this.blazeVersionData = blazeVersionData;
+    return this;
+  }
+
+  public MockBlazeProjectDataBuilder setWorkspacePathResolver(
+      WorkspacePathResolver workspacePathResolver) {
+    this.workspacePathResolver = workspacePathResolver;
+    return this;
+  }
+
+  public MockBlazeProjectDataBuilder setArtifactLocationDecoder(
+      ArtifactLocationDecoder artifactLocationDecoder) {
+    this.artifactLocationDecoder = artifactLocationDecoder;
+    return this;
+  }
+
+  public MockBlazeProjectDataBuilder setWorkspaceLanguageSettings(
+      WorkspaceLanguageSettings workspaceLanguageSettings) {
+    this.workspaceLanguageSettings = workspaceLanguageSettings;
+    return this;
+  }
+
+  public MockBlazeProjectDataBuilder setSyncState(SyncState syncState) {
+    this.syncState = syncState;
+    return this;
+  }
+
+  public MockBlazeProjectDataBuilder setReverseDependencies(
+      ImmutableMultimap<TargetKey, TargetKey> reverseDependencies) {
+    this.reverseDependencies = reverseDependencies;
+    return this;
+  }
+
+  public BlazeProjectData build() {
+    TargetMap targetMap =
+        this.targetMap != null ? this.targetMap : new TargetMap(ImmutableMap.of());
+    ImmutableMap<String, String> blazeInfo =
+        this.blazeInfo != null ? this.blazeInfo : ImmutableMap.of();
+    BlazeRoots blazeRoots =
+        this.blazeRoots != null
+            ? this.blazeRoots
+            : new BlazeRoots(
+                null,
+                ImmutableList.of(workspaceRoot.directory()),
+                new ExecutionRootPath("bin"),
+                new ExecutionRootPath("gen"),
+                null);
+    BlazeVersionData blazeVersionData =
+        this.blazeVersionData != null ? this.blazeVersionData : new BlazeVersionData();
+    WorkspacePathResolver workspacePathResolver =
+        this.workspacePathResolver != null
+            ? this.workspacePathResolver
+            : new WorkspacePathResolverImpl(workspaceRoot, blazeRoots);
+    ArtifactLocationDecoder artifactLocationDecoder =
+        this.artifactLocationDecoder != null
+            ? this.artifactLocationDecoder
+            : new ArtifactLocationDecoderImpl(blazeRoots, workspacePathResolver);
+    WorkspaceLanguageSettings workspaceLanguageSettings =
+        this.workspaceLanguageSettings != null
+            ? this.workspaceLanguageSettings
+            : new WorkspaceLanguageSettings(WorkspaceType.JAVA, ImmutableSet.of());
+    SyncState syncState =
+        this.syncState != null ? this.syncState : new SyncState(ImmutableMap.of());
+    ImmutableMultimap<TargetKey, TargetKey> reverseDependencies =
+        this.reverseDependencies != null ? this.reverseDependencies : ImmutableMultimap.of();
+
+    return new BlazeProjectData(
+        syncTime,
+        targetMap,
+        blazeInfo,
+        blazeRoots,
+        blazeVersionData,
+        workspacePathResolver,
+        artifactLocationDecoder,
+        workspaceLanguageSettings,
+        syncState,
+        reverseDependencies,
+        null);
+  }
+}
diff --git a/base/tests/utils/unit/com/google/idea/blaze/base/model/MockBlazeProjectDataManager.java b/base/tests/utils/unit/com/google/idea/blaze/base/model/MockBlazeProjectDataManager.java
new file mode 100644
index 0000000..97d1b3d
--- /dev/null
+++ b/base/tests/utils/unit/com/google/idea/blaze/base/model/MockBlazeProjectDataManager.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.idea.blaze.base.model;
+
+import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
+import javax.annotation.Nullable;
+
+/** Mocks the blaze project data manager. */
+public class MockBlazeProjectDataManager implements BlazeProjectDataManager {
+  private final BlazeProjectData blazeProjectData;
+
+  public MockBlazeProjectDataManager(BlazeProjectData blazeProjectData) {
+    this.blazeProjectData = blazeProjectData;
+  }
+
+  @Nullable
+  @Override
+  public BlazeProjectData getBlazeProjectData() {
+    return blazeProjectData;
+  }
+}
diff --git a/intellij_platform_sdk/BUILD.android_studio b/intellij_platform_sdk/BUILD.android_studio
index f768640..1a8a88c 100644
--- a/intellij_platform_sdk/BUILD.android_studio
+++ b/intellij_platform_sdk/BUILD.android_studio
@@ -22,6 +22,13 @@
 )
 
 java_import(
+    name = "test_recorder",
+    jars = glob([
+        "android-studio/plugins/test-recorder/lib/*.jar",
+    ]),
+)
+
+java_import(
     name = "junit",
     jars = glob(["android-studio/plugins/junit/lib/*.jar"]),
 )
@@ -30,14 +37,20 @@
 # when running integration tests.
 java_import(
     name = "bundled_plugins",
-    jars = glob([
-        "android-studio/plugins/gradle/lib/*.jar",
-        "android-studio/plugins/Groovy/lib/*.jar",
-        "android-studio/plugins/java-i18n/lib/*.jar",
-        "android-studio/plugins/junit/lib/*.jar",
-        "android-studio/plugins/ndk-workspace/lib/*.jar",
-        "android-studio/plugins/properties/lib/*.jar",
-    ]),
+    jars = glob(
+        [
+            "android-studio/plugins/gradle/lib/*.jar",
+            "android-studio/plugins/Groovy/lib/*.jar",
+            "android-studio/plugins/java-i18n/lib/*.jar",
+            "android-studio/plugins/junit/lib/*.jar",
+            "android-studio/plugins/ndk-workspace/lib/*.jar",
+            "android-studio/plugins/properties/lib/*.jar",
+        ],
+        exclude = [
+            # Conflict with lib/guava-*.jar
+            "android-studio/plugins/gradle/lib/guava-*.jar",
+        ],
+    ),
     tags = ["intellij-provided-by-sdk"],
 )
 
diff --git a/intellij_platform_sdk/missing/src/dummy/pkg/DummyClassToAvoidAnEmptyJavaLibrary.java b/intellij_platform_sdk/missing/src/dummy/pkg/DummyClassToAvoidAnEmptyJavaLibrary.java
new file mode 100644
index 0000000..73ba9b6
--- /dev/null
+++ b/intellij_platform_sdk/missing/src/dummy/pkg/DummyClassToAvoidAnEmptyJavaLibrary.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dummy.pkg;
+
+class DummyClassToAvoidAnEmptyJavaLibrary {}
diff --git a/intellij_platform_sdk/missing/tests/com/jetbrains/cidr/modulemap/resolve/MockModuleMapManagerImpl.java b/intellij_platform_sdk/missing/tests/com/jetbrains/cidr/modulemap/resolve/MockModuleMapManagerImpl.java
new file mode 100644
index 0000000..e22d36e
--- /dev/null
+++ b/intellij_platform_sdk/missing/tests/com/jetbrains/cidr/modulemap/resolve/MockModuleMapManagerImpl.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.cidr.modulemap.resolve;
+
+import com.jetbrains.cidr.lang.workspace.OCResolveConfiguration;
+import com.jetbrains.cidr.lang.workspace.headerRoots.HeadersSearchRoot;
+import com.jetbrains.cidr.modulemap.ModuleMapModules;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Dummy class that integration tests depend on, but missing from Android Studio 2.3.
+ *
+ * <p>Android Studio 2.3 added a ModuleMapManager projectService with MockModuleMapManagerImpl as
+ * testServiceImplementation to CidrLangPlugin.xml. We don't use this service at all, but since
+ * Android Studio releases don't contain test classes, trying to load the cidr-lang plugin during
+ * integration tests will fail due to the missing test service implementation.
+ */
+public class MockModuleMapManagerImpl extends ModuleMapManager {
+  @Override
+  public ModuleMapModules getModules(OCResolveConfiguration ocResolveConfiguration) {
+    return ModuleMapModules.Companion.getEMPTY();
+  }
+
+  @Override
+  public List<HeadersSearchRoot> getHeaderSearchRoots(
+      OCResolveConfiguration ocResolveConfiguration) {
+    return Collections.emptyList();
+  }
+}