Introduce new test API for ProjectCreation

Change-Id: I279d48286dde5782575bb395c30c6eb5f43cfad2
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 8c64aa9..b435bcc 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -38,6 +38,7 @@
 import com.google.common.primitives.Chars;
 import com.google.gerrit.acceptance.AcceptanceTestRequestScope.Context;
 import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.GroupDescription;
@@ -291,6 +292,7 @@
   @Inject private AccountIndexer accountIndexer;
   @Inject private Groups groups;
   @Inject private GroupIndexer groupIndexer;
+  @Inject private ProjectOperations projectOperations;
 
   private ProjectResetter resetter;
   private List<Repository> toClose;
diff --git a/java/com/google/gerrit/acceptance/BUILD b/java/com/google/gerrit/acceptance/BUILD
index 861372d..b0a39cf 100644
--- a/java/com/google/gerrit/acceptance/BUILD
+++ b/java/com/google/gerrit/acceptance/BUILD
@@ -115,6 +115,7 @@
         "//lib:gwtorm",
         "//lib:jsch",
         "//lib:servlet-api-3_1",
+        "//lib/commons:lang",
         "//lib/greenmail",
         "//lib/guice",
         "//lib/guice:guice-assistedinject",
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index be8932e..063b298 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -28,6 +28,8 @@
 import com.google.gerrit.acceptance.testsuite.account.AccountOperationsImpl;
 import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
 import com.google.gerrit.acceptance.testsuite.group.GroupOperationsImpl;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperationsImpl;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.lucene.LuceneIndexModule;
@@ -498,6 +500,7 @@
             bind(AccountCreator.class);
             bind(AccountOperations.class).to(AccountOperationsImpl.class);
             bind(GroupOperations.class).to(GroupOperationsImpl.class);
+            bind(ProjectOperations.class).to(ProjectOperationsImpl.class);
             factory(PushOneCommit.Factory.class);
             install(InProcessProtocol.module());
             install(new NoSshModule());
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperations.java b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperations.java
new file mode 100644
index 0000000..16dc6c6
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperations.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// 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.gerrit.acceptance.testsuite.project;
+
+/**
+ * Operations for constructing projects in tests. This does not necessarily use the project REST
+ * API, so don't use it for testing that.
+ */
+public interface ProjectOperations {
+
+  /** Starts a fluent chain for creating a new project. */
+  TestProjectCreation.Builder newProject();
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
new file mode 100644
index 0000000..6bf4114
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
@@ -0,0 +1,57 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// 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.gerrit.acceptance.testsuite.project;
+
+import com.google.gerrit.acceptance.testsuite.project.TestProjectCreation.Builder;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.config.ProjectOwnerGroupsProvider;
+import com.google.gerrit.server.project.CreateProjectArgs;
+import com.google.gerrit.server.project.ProjectCreator;
+import java.util.ArrayList;
+import java.util.Collections;
+import javax.inject.Inject;
+import org.apache.commons.lang.RandomStringUtils;
+import org.eclipse.jgit.lib.Constants;
+
+public class ProjectOperationsImpl implements ProjectOperations {
+  private final ProjectCreator projectCreator;
+  private final ProjectOwnerGroupsProvider.Factory projectOwnerGroups;
+
+  @Inject
+  ProjectOperationsImpl(
+      ProjectOwnerGroupsProvider.Factory projectOwnerGroups, ProjectCreator projectCreator) {
+    this.projectCreator = projectCreator;
+    this.projectOwnerGroups = projectOwnerGroups;
+  }
+
+  @Override
+  public Builder newProject() {
+    return TestProjectCreation.builder(this::createNewProject);
+  }
+
+  private Project.NameKey createNewProject(TestProjectCreation projectCreation) throws Exception {
+    String name = projectCreation.name().orElse(RandomStringUtils.randomAlphabetic(8));
+
+    CreateProjectArgs args = new CreateProjectArgs();
+    args.setProjectName(name);
+    args.branch = Collections.singletonList(Constants.R_HEADS + Constants.MASTER);
+    args.createEmptyCommit = projectCreation.createEmptyCommit().orElse(true);
+    projectCreation.parent().ifPresent(p -> args.newParent = p);
+    args.ownerIds = new ArrayList<>(projectOwnerGroups.create(args.getProject()).get());
+    projectCreation.submitType().ifPresent(st -> args.submitType = st);
+    projectCreator.createProject(args);
+    return new Project.NameKey(name);
+  }
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/TestProjectCreation.java b/java/com/google/gerrit/acceptance/testsuite/project/TestProjectCreation.java
new file mode 100644
index 0000000..aef2625
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/project/TestProjectCreation.java
@@ -0,0 +1,77 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// 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.gerrit.acceptance.testsuite.project;
+
+import com.google.auto.value.AutoValue;
+import com.google.gerrit.acceptance.testsuite.ThrowingFunction;
+import com.google.gerrit.extensions.client.SubmitType;
+import com.google.gerrit.reviewdb.client.Project;
+import java.util.Optional;
+
+@AutoValue
+public abstract class TestProjectCreation {
+
+  public abstract Optional<String> name();
+
+  public abstract Optional<Project.NameKey> parent();
+
+  public abstract Optional<Boolean> createEmptyCommit();
+
+  public abstract Optional<SubmitType> submitType();
+
+  abstract ThrowingFunction<TestProjectCreation, Project.NameKey> projectCreator();
+
+  public static Builder builder(
+      ThrowingFunction<TestProjectCreation, Project.NameKey> projectCreator) {
+    return new AutoValue_TestProjectCreation.Builder().projectCreator(projectCreator);
+  }
+
+  @AutoValue.Builder
+  public abstract static class Builder {
+    public abstract TestProjectCreation.Builder name(String name);
+
+    public abstract TestProjectCreation.Builder parent(Project.NameKey parent);
+
+    public abstract TestProjectCreation.Builder submitType(SubmitType submitType);
+
+    public abstract TestProjectCreation.Builder createEmptyCommit(boolean value);
+
+    /**
+     * Creates empty commit on creation. This is necessary for the project's branches to be born.
+     */
+    public TestProjectCreation.Builder withEmptyCommit() {
+      return createEmptyCommit(true);
+    }
+
+    public TestProjectCreation.Builder noEmptyCommit() {
+      return createEmptyCommit(false);
+    }
+
+    abstract TestProjectCreation.Builder projectCreator(
+        ThrowingFunction<TestProjectCreation, Project.NameKey> projectCreator);
+
+    abstract TestProjectCreation autoBuild();
+
+    /**
+     * Executes the project creation as specified.
+     *
+     * @return the name of the created project
+     */
+    public Project.NameKey create() throws Exception {
+      TestProjectCreation creation = autoBuild();
+      return creation.projectCreator().apply(creation);
+    }
+  }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java b/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
index a64305c..6ffc179 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.GitUtil;
 import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.common.data.Permission;
@@ -63,8 +64,6 @@
 
 public class AccessIT extends AbstractDaemonTest {
 
-  private static final String PROJECT_NAME = "newProject";
-
   private static final String REFS_ALL = Constants.R_REFS + "*";
   private static final String REFS_HEADS = Constants.R_HEADS + "*";
 
@@ -73,10 +72,11 @@
   private Project.NameKey newProjectName;
 
   @Inject private DynamicSet<FileHistoryWebLink> fileHistoryWebLinkDynamicSet;
+  @Inject protected ProjectOperations projectOperations;
 
   @Before
   public void setUp() throws Exception {
-    newProjectName = createProject(PROJECT_NAME);
+    newProjectName = projectOperations.newProject().withEmptyCommit().create();
   }
 
   @Test
@@ -412,7 +412,7 @@
   @Test
   public void updateParentAsUser() throws Exception {
     // Create child
-    String newParentProjectName = createProject(PROJECT_NAME + "PA").get();
+    String newParentProjectName = projectOperations.newProject().create().get();
 
     // Set new parent
     ProjectAccessInput accessInput = newProjectAccessInput();
@@ -427,7 +427,7 @@
   @Test
   public void updateParentAsAdministrator() throws Exception {
     // Create parent
-    String newParentProjectName = createProject(PROJECT_NAME + "PA").get();
+    String newParentProjectName = projectOperations.newProject().create().get();
 
     // Set new parent
     ProjectAccessInput accessInput = newProjectAccessInput();
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java b/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java
new file mode 100644
index 0000000..6dc69be
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java
@@ -0,0 +1,56 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// 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.gerrit.acceptance.testsuite.project;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.util.stream.Collectors.toList;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.extensions.api.projects.BranchInfo;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.inject.Inject;
+import java.util.List;
+import org.junit.Test;
+
+public class ProjectOperationsImplTest extends AbstractDaemonTest {
+
+  @Inject private ProjectOperations projectOperations;
+
+  @Test
+  public void defaultName() throws Exception {
+    Project.NameKey name = projectOperations.newProject().create();
+    // check that the project was created (throws exception if not found.)
+    gApi.projects().name(name.get());
+    Project.NameKey name2 = projectOperations.newProject().create();
+    assertThat(name2).isNotEqualTo(name);
+  }
+
+  @Test
+  public void specifiedName() throws Exception {
+    String name = "somename";
+    Project.NameKey key = projectOperations.newProject().name(name).create();
+    assertThat(key.get()).isEqualTo(name);
+  }
+
+  @Test
+  public void emptyCommit() throws Exception {
+    Project.NameKey key = projectOperations.newProject().withEmptyCommit().create();
+    List<BranchInfo> branches = gApi.projects().name(key.get()).branches().get();
+    assertThat(branches).isNotEmpty();
+    assertThat(branches.stream().map(x -> x.ref).collect(toList()))
+        .isEqualTo(ImmutableList.of("HEAD", "refs/meta/config", "refs/heads/master"));
+  }
+}