e2e-tests: Add {Approve|Submit}Change core scenarios

Add an ApproveChange scenario and base the SubmitChange scenario on it.

Make the ApproveChange scenario runnable alone as well. In this case,
allow the change number being approved to be set through the existing
'number' test environment property; cf. [1]. In the other case of
running approval from SubmitChange, set the change number automatically.

Adapt the existing standalone DeleteChange scenario, for SubmitChange.
That scenario showed to be very similar to the added ApproveChange one.

Adapt the existing standalone CreateChange scenario, so it can be run
from SubmitChange now as well. Make the CreateChange project field no
longer automatic or solely set by CreateChange alone. Instead, make it
either set from composing scenarios such as SubmitChange or as per [1].
Enable this all by making CreateChange a core ProjectSimulation now.

Rather than having DeleteChange hold the change number, move it to the
CreateChange class. Enable this new SubmitChange scenario flow that way,
while preserving existing scenario abilities. Having the CreateChange
scenario generate *and* hold the resulting change number should also be
more intuitive, compared to DeleteChange holding it before.

[1] https://gerrit-documentation.storage.googleapis.com/Documentation/2.16.19/dev-e2e-tests.html#_environment_properties

Change-Id: I86dda57672ef9f34a286bfd151226b5988cb7a34
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange-body.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange-body.json
new file mode 100644
index 0000000..670aa9f
--- /dev/null
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange-body.json
@@ -0,0 +1,5 @@
+{
+  "labels": {
+    "Code-Review": 2
+  }
+}
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json
new file mode 100644
index 0000000..3577a6a
--- /dev/null
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/ApproveChange.json
@@ -0,0 +1,6 @@
+[
+  {
+    "url": "http://HOSTNAME:HTTP_PORT/a/changes/",
+    "number": "NUMBER"
+  }
+]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
index c267ab3..b4ee549 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange.json
@@ -1,6 +1,6 @@
 [
   {
     "url": "http://HOSTNAME:HTTP_PORT/a/changes/",
-    "project": "_PROJECT"
+    "project": "PROJECT"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
index 53b947a..3577a6a 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteChange.json
@@ -1,6 +1,6 @@
 [
   {
     "url": "http://HOSTNAME:HTTP_PORT/a/changes/",
-    "number": "_NUMBER"
+    "number": "NUMBER"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json
new file mode 100644
index 0000000..a371757
--- /dev/null
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChange.json
@@ -0,0 +1,5 @@
+[
+  {
+    "url": "http://HOSTNAME:HTTP_PORT/a/changes/"
+  }
+]
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ApproveChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ApproveChange.scala
new file mode 100644
index 0000000..fe46bd6
--- /dev/null
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ApproveChange.scala
@@ -0,0 +1,48 @@
+// Copyright (C) 2020 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.scenarios
+
+import io.gatling.core.Predef.{atOnceUsers, _}
+import io.gatling.core.feeder.FeederBuilder
+import io.gatling.core.structure.ScenarioBuilder
+import io.gatling.http.Predef.http
+
+class ApproveChange extends GerritSimulation {
+  private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
+  private var createChange: Option[CreateChange] = None
+
+  def this(createChange: CreateChange) {
+    this()
+    this.createChange = Some(createChange)
+  }
+
+  val test: ScenarioBuilder = scenario(unique)
+      .feed(data)
+      .exec(session => {
+        if (createChange.nonEmpty) {
+          session.set("number", createChange.get.number)
+        } else {
+          session
+        }
+      })
+      .exec(http(unique)
+          .post("${url}${number}/revisions/current/review")
+          .body(ElFileBody(body)).asJson)
+
+  setUp(
+    test.inject(
+      atOnceUsers(1)
+    )).protocols(httpProtocol)
+}
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala
index 57e6bcd..c7fb8ed 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala
@@ -21,26 +21,31 @@
 
 import scala.concurrent.duration._
 
-class CreateChange extends GerritSimulation {
+class CreateChange extends ProjectSimulation {
   private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
-  private val default: String = name
   private val numberKey = "_number"
+  var number = 0
 
   override def relativeRuntimeWeight = 2
 
-  private val test: ScenarioBuilder = scenario(unique)
+  def this(default: String) {
+    this()
+    this.default = default
+  }
+
+  val test: ScenarioBuilder = scenario(unique)
       .feed(data)
       .exec(httpRequest
           .body(ElFileBody(body)).asJson
           .check(regex("\"" + numberKey + "\":(\\d+),").saveAs(numberKey)))
       .exec(session => {
-        deleteChange.number = Some(session(numberKey).as[Int])
+        number = session(numberKey).as[Int]
         session
       })
 
   private val createProject = new CreateProject(default)
   private val deleteProject = new DeleteProject(default)
-  private val deleteChange = new DeleteChange
+  private val deleteChange = new DeleteChange(this)
 
   setUp(
     createProject.test.inject(
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala
index 1b3bbc1..aa6fe0d 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteChange.scala
@@ -21,15 +21,20 @@
 
 class DeleteChange extends GerritSimulation {
   private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
-  var number: Option[Int] = None
+  private var createChange: Option[CreateChange] = None
 
   override def relativeRuntimeWeight = 2
 
+  def this(createChange: CreateChange) {
+    this()
+    this.createChange = Some(createChange)
+  }
+
   val test: ScenarioBuilder = scenario(unique)
       .feed(data)
       .exec(session => {
-        if (number.nonEmpty) {
-          session.set("number", number.get)
+        if (createChange.nonEmpty) {
+          session.set("number", createChange.get.number)
         } else {
           session
         }
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
index b427c0d..5d6176d 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
@@ -66,7 +66,8 @@
       val precedes = replaceKeyWith("_number", 0, number.toString)
       replaceProperty("number", 1, precedes)
     case ("project", project) =>
-      val precedes = replaceKeyWith("_project", name, project.toString)
+      var precedes = replaceKeyWith("_project", name, project.toString)
+      precedes = replaceOverride(precedes)
       replaceProperty("project", precedes)
     case ("entries", entries) =>
       replaceProperty("projects_entries", "1", entries.toString)
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChange.scala
new file mode 100644
index 0000000..2f67274
--- /dev/null
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChange.scala
@@ -0,0 +1,62 @@
+// Copyright (C) 2020 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.scenarios
+
+import io.gatling.core.Predef.{atOnceUsers, _}
+import io.gatling.core.feeder.FeederBuilder
+import io.gatling.core.structure.ScenarioBuilder
+import io.gatling.http.Predef.http
+
+import scala.concurrent.duration._
+
+class SubmitChange extends GerritSimulation {
+  private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
+  private val default: String = name
+
+  private val test: ScenarioBuilder = scenario(unique)
+      .feed(data)
+      .exec(session => {
+        session.set("number", createChange.number)
+      })
+      .exec(http(unique).post("${url}${number}/submit"))
+
+  private val createProject = new CreateProject(default)
+  private val createChange = new CreateChange(default)
+  private val approveChange = new ApproveChange(createChange)
+  private val deleteProject = new DeleteProject(default)
+
+  setUp(
+    createProject.test.inject(
+      nothingFor(stepWaitTime(createProject) seconds),
+      atOnceUsers(1)
+    ),
+    createChange.test.inject(
+      nothingFor(stepWaitTime(createChange) seconds),
+      atOnceUsers(1)
+    ),
+    approveChange.test.inject(
+      nothingFor(stepWaitTime(approveChange) seconds),
+      atOnceUsers(1)
+    ),
+    test.inject(
+      nothingFor(stepWaitTime(this) seconds),
+      atOnceUsers(1)
+    ),
+    deleteProject.test.inject(
+      nothingFor(stepWaitTime(deleteProject) seconds),
+      atOnceUsers(1)
+    ),
+  ).protocols(httpProtocol)
+}