Merge changes I8ab306e8,Ib1abbb96 * changes: Add AutoValue classes for Preferences Rename Preferences to StoredPreferences
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt index 1a4a75d..158e528 100644 --- a/Documentation/dev-bazel.txt +++ b/Documentation/dev-bazel.txt
@@ -43,8 +43,8 @@ $ bazel build \ --define=ABSOLUTE_JAVABASE=<path-to-java-12> \ --host_javabase=@bazel_tools//tools/jdk:absolute_javabase \ - --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_vanilla \ - --java_toolchain=@bazel_tools//tools/jdk:toolchain_vanilla \ + --host_java_toolchain=//tools:toolchain_vanilla \ + --java_toolchain=//tools:toolchain_vanilla \ :release ``` @@ -56,8 +56,8 @@ --define=ABSOLUTE_JAVABASE=<path-to-java-12> \ --javabase=@bazel_tools//tools/jdk:absolute_javabase \ --host_javabase=@bazel_tools//tools/jdk:absolute_javabase \ - --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_vanilla \ - --java_toolchain=@bazel_tools//tools/jdk:toolchain_vanilla \ + --host_java_toolchain=//tools:toolchain_vanilla \ + --java_toolchain=//tools:toolchain_vanilla \ //... ``` @@ -69,8 +69,8 @@ > build --define=ABSOLUTE_JAVABASE=<path-to-java-12> > build --javabase=@bazel_tools//tools/jdk:absolute_javabase > build --host_javabase=@bazel_tools//tools/jdk:absolute_javabase -> build --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_vanilla -> build --java_toolchain=@bazel_tools//tools/jdk:toolchain_vanilla +> build --host_java_toolchain=//tools:toolchain_vanilla +> build --java_toolchain=//tools:toolchain_vanilla > EOF ```
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt index 77ef60d..74ed725 100644 --- a/Documentation/dev-plugins.txt +++ b/Documentation/dev-plugins.txt
@@ -2738,6 +2738,20 @@ } ---- +[[exception-hook]] +== ExceptionHook + +An `ExceptionHook` allows implementors to control how certain +exceptions should be handled. + +This interface is intended to be implemented for multi-master setups to +control the behavior for handling exceptions that are thrown by a lower +layer that handles the consensus and synchronization between different +server nodes. E.g. if an operation fails because consensus for a Git +update could not be achieved (e.g. due to slow responding server nodes) +this interface can be used to retry the request instead of failing it +immediately. + [[quota-enforcer]] == Quota Enforcer
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt index 3eb259b..f03507a 100644 --- a/Documentation/rest-api-changes.txt +++ b/Documentation/rest-api-changes.txt
@@ -6019,7 +6019,9 @@ [options="header",cols="1,^1,5"] |=========================== |Field Name ||Description -|`message` ||Commit message for the cherry-pick change +|`message` |optional| +Commit message for the cherry-pick change. If not set, the commit message of +the cherry-picked commit is used. |`destination` ||Destination branch |`base` |optional| 40-hex digit SHA-1 of the commit which will be the parent commit of the newly created change.
diff --git a/Documentation/user-request-tracing.txt b/Documentation/user-request-tracing.txt index 1b9a6e5..d422dd9 100644 --- a/Documentation/user-request-tracing.txt +++ b/Documentation/user-request-tracing.txt
@@ -72,7 +72,8 @@ exception as recoverable. The trace IDs for auto-retries are generated and start with -`retry-on-failure-`. +`retry-on-failure-`. For REST requests they are returned to the client +as `X-Gerrit-Trace` header. The best way to search for auto-retries in logs is to do a grep by `AutoRetry`. For each auto-retry that happened this should match 1 or 2
diff --git a/WORKSPACE b/WORKSPACE index a21fe31..75a7eed 100644 --- a/WORKSPACE +++ b/WORKSPACE
@@ -56,29 +56,6 @@ check_bazel_version() -# Protobuf rules support -http_archive( - name = "rules_proto", - sha256 = "602e7161d9195e50246177e7c55b2f39950a9cf7366f74ed5f22fd45750cd208", - strip_prefix = "rules_proto-97d8af4dc474595af3900dd85cb3a29ad28cc313", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_proto/archive/97d8af4dc474595af3900dd85cb3a29ad28cc313.tar.gz", - "https://github.com/bazelbuild/rules_proto/archive/97d8af4dc474595af3900dd85cb3a29ad28cc313.tar.gz", - ], -) - -# Rules Python -http_archive( - name = "rules_python", - sha256 = "b5bab4c47e863e0fbb77df4a40c45ca85f98f5a2826939181585644c9f31b97b", - strip_prefix = "rules_python-9d68f24659e8ce8b736590ba1e4418af06ec2552", - urls = ["https://github.com/bazelbuild/rules_python/archive/9d68f24659e8ce8b736590ba1e4418af06ec2552.tar.gz"], -) - -load("@rules_python//python:repositories.bzl", "py_repositories") - -py_repositories() - load("@io_bazel_rules_closure//closure:repositories.bzl", "closure_repositories") # Prevent redundant loading of dependencies. @@ -118,17 +95,6 @@ gazelle_dependencies() -# Protobuf rules support -http_archive( - name = "rules_proto", - sha256 = "602e7161d9195e50246177e7c55b2f39950a9cf7366f74ed5f22fd45750cd208", - strip_prefix = "rules_proto-97d8af4dc474595af3900dd85cb3a29ad28cc313", - urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_proto/archive/97d8af4dc474595af3900dd85cb3a29ad28cc313.tar.gz", - "https://github.com/bazelbuild/rules_proto/archive/97d8af4dc474595af3900dd85cb3a29ad28cc313.tar.gz", - ], -) - # Dependencies for PolyGerrit local dev server. go_repository( name = "com_github_howeyc_fsnotify",
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java index fa9299a..97394c7 100644 --- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java +++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -128,6 +128,7 @@ import com.google.gerrit.testing.FakeEmailSender; import com.google.gerrit.testing.FakeEmailSender.Message; import com.google.gerrit.testing.SshMode; +import com.google.gerrit.testing.TestTimeUtil; import com.google.gson.Gson; import com.google.inject.Inject; import com.google.inject.Module; @@ -143,6 +144,8 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.sql.Timestamp; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -285,6 +288,7 @@ private ProjectResetter resetter; private List<Repository> toClose; + private String systemTimeZone; @Before public void clearSender() { @@ -435,6 +439,37 @@ if (!classDesc.skipProjectClone()) { testRepo = cloneProject(project, getCloneAsAccount(description)); } + + // Set the clock step last, so that the test setup isn't consuming any timestamps after the + // clock has been set. + setTimeSettings(classDesc.useSystemTime(), classDesc.useClockStep(), classDesc.useTimezone()); + setTimeSettings( + methodDesc.useSystemTime(), methodDesc.useClockStep(), methodDesc.useTimezone()); + } + + private void setTimeSettings( + boolean useSystemTime, + @Nullable UseClockStep useClockStep, + @Nullable UseTimezone useTimezone) { + if (useSystemTime) { + TestTimeUtil.useSystemTime(); + } else if (useClockStep != null) { + TestTimeUtil.resetWithClockStep(useClockStep.clockStep(), useClockStep.clockStepUnit()); + if (useClockStep.startAtEpoch()) { + TestTimeUtil.setClock(Timestamp.from(Instant.EPOCH)); + } + } + if (useTimezone != null) { + systemTimeZone = System.setProperty("user.timezone", useTimezone.timezone()); + } + } + + private void resetTimeSettings() { + TestTimeUtil.useSystemTime(); + if (systemTimeZone != null) { + System.setProperty("user.timezone", systemTimeZone); + systemTimeZone = null; + } } /** Override to bind an additional Guice module */ @@ -562,6 +597,7 @@ repo.close(); } closeSsh(); + resetTimeSettings(); if (server != commonServer) { server.close(); server = null;
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java index 4279764..adc1f1d 100644 --- a/java/com/google/gerrit/acceptance/GerritServer.java +++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -110,6 +110,9 @@ has(Sandboxed.class, testDesc.getTestClass()), has(SkipProjectClone.class, testDesc.getTestClass()), has(UseSsh.class, testDesc.getTestClass()), + false, // @UseSystemTime is only valid on methods. + get(UseClockStep.class, testDesc.getTestClass()), + get(UseTimezone.class, testDesc.getTestClass()), null, // @GerritConfig is only valid on methods. null, // @GerritConfigs is only valid on methods. null, // @GlobalPluginConfig is only valid on methods. @@ -119,6 +122,15 @@ public static Description forTestMethod( org.junit.runner.Description testDesc, String configName) { + UseClockStep useClockStep = testDesc.getAnnotation(UseClockStep.class); + if (testDesc.getAnnotation(UseSystemTime.class) == null && useClockStep == null) { + // Only read the UseClockStep from the class if on method level neither @UseSystemTime nor + // @UseClockStep have been used. + // If the method defines @UseSystemTime or @UseClockStep it should overwrite @UseClockStep + // on class level. + useClockStep = get(UseClockStep.class, testDesc.getTestClass()); + } + return new AutoValue_GerritServer_Description( testDesc, configName, @@ -133,6 +145,11 @@ || has(SkipProjectClone.class, testDesc.getTestClass()), testDesc.getAnnotation(UseSsh.class) != null || has(UseSsh.class, testDesc.getTestClass()), + testDesc.getAnnotation(UseSystemTime.class) != null, + useClockStep, + testDesc.getAnnotation(UseTimezone.class) != null + ? testDesc.getAnnotation(UseTimezone.class) + : get(UseTimezone.class, testDesc.getTestClass()), testDesc.getAnnotation(GerritConfig.class), testDesc.getAnnotation(GerritConfigs.class), testDesc.getAnnotation(GlobalPluginConfig.class), @@ -149,6 +166,16 @@ return false; } + @Nullable + private static <T extends Annotation> T get(Class<T> annotation, Class<?> clazz) { + for (; clazz != null; clazz = clazz.getSuperclass()) { + if (clazz.getAnnotation(annotation) != null) { + return clazz.getAnnotation(annotation); + } + } + return null; + } + private static Level getLogLevelThresholdAnnotation(org.junit.runner.Description testDesc) { LogThreshold logLevelThreshold = testDesc.getTestClass().getAnnotation(LogThreshold.class); if (logLevelThreshold == null) { @@ -176,6 +203,14 @@ return useSshAnnotation() && SshMode.useSsh(); } + abstract boolean useSystemTime(); + + @Nullable + abstract UseClockStep useClockStep(); + + @Nullable + abstract UseTimezone useTimezone(); + @Nullable abstract GerritConfig config(); @@ -191,12 +226,15 @@ abstract Level logLevelThreshold(); private void checkValidAnnotations() { + if (useClockStep() != null && useSystemTime()) { + throw new IllegalStateException("Use either @UseClockStep or @UseSystemTime, not both"); + } if (configs() != null && config() != null) { - throw new IllegalStateException("Use either @GerritConfigs or @GerritConfig not both"); + throw new IllegalStateException("Use either @GerritConfigs or @GerritConfig, not both"); } if (pluginConfigs() != null && pluginConfig() != null) { throw new IllegalStateException( - "Use either @GlobalPluginConfig or @GlobalPluginConfigs not both"); + "Use either @GlobalPluginConfig or @GlobalPluginConfigs, not both"); } if ((pluginConfigs() != null || pluginConfig() != null) && memory()) { throw new IllegalStateException("Must use @UseLocalDisk with @GlobalPluginConfig(s)"); @@ -412,7 +450,7 @@ @Nullable InMemoryRepositoryManager inMemoryRepoManager) throws Exception { Config cfg = desc.buildConfig(baseConfig); - daemon.setSlave(isSlave(baseConfig) || cfg.getBoolean("container", "slave", false)); + daemon.setSlave(isSlave(baseConfig) || isSlave(cfg)); mergeTestConfig(cfg); // Set the log4j configuration to an invalid one to prevent system logs // from getting configured and creating log files.
diff --git a/java/com/google/gerrit/acceptance/UseClockStep.java b/java/com/google/gerrit/acceptance/UseClockStep.java new file mode 100644 index 0000000..10a93fe --- /dev/null +++ b/java/com/google/gerrit/acceptance/UseClockStep.java
@@ -0,0 +1,42 @@ +// Copyright (C) 2019 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; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.concurrent.TimeUnit; + +/** + * Annotation to use a clock step for the execution of acceptance tests (the test class must inherit + * from {@link AbstractDaemonTest}). + * + * <p>Annotations on method level override annotations on class level. + */ +@Target({TYPE, METHOD}) +@Retention(RUNTIME) +public @interface UseClockStep { + /** Amount to increment clock by on each lookup. */ + long clockStep() default 1L; + + /** Time unit for {@link #clockStep()}. */ + TimeUnit clockStepUnit() default TimeUnit.SECONDS; + + /** Whether the clock should initially be set to {@link java.time.Instant#EPOCH}. */ + boolean startAtEpoch() default false; +}
diff --git a/java/com/google/gerrit/acceptance/UseSystemTime.java b/java/com/google/gerrit/acceptance/UseSystemTime.java new file mode 100644 index 0000000..e9cbd47 --- /dev/null +++ b/java/com/google/gerrit/acceptance/UseSystemTime.java
@@ -0,0 +1,35 @@ +// Copyright (C) 2019 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; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Annotation to use the system time for the execution of acceptance tests (the test class must + * inherit from {@link AbstractDaemonTest}). + * + * <p>Can only be applied on method level, since using system time on class level is the default if + * {@link UseClockStep} is not used. + * + * <p>Intended to be used to use system time for single tests when the test class is annotated with + * {@link UseClockStep}. + */ +@Target(METHOD) +@Retention(RUNTIME) +public @interface UseSystemTime {}
diff --git a/java/com/google/gerrit/acceptance/UseTimezone.java b/java/com/google/gerrit/acceptance/UseTimezone.java new file mode 100644 index 0000000..7412030 --- /dev/null +++ b/java/com/google/gerrit/acceptance/UseTimezone.java
@@ -0,0 +1,35 @@ +// Copyright (C) 2019 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; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Annotation to set a timezone for the execution of acceptance tests (the test class must inherit + * from {@link AbstractDaemonTest}). + * + * <p>Annotations on method level override annotations on class level. + */ +@Target({TYPE, METHOD}) +@Retention(RUNTIME) +public @interface UseTimezone { + /** The timezone that should be used for the test, e.g. "US/Eastern". */ + String timezone(); +}
diff --git a/java/com/google/gerrit/git/BUILD b/java/com/google/gerrit/git/BUILD index 5ece37a..1edba38 100644 --- a/java/com/google/gerrit/git/BUILD +++ b/java/com/google/gerrit/git/BUILD
@@ -7,6 +7,8 @@ deps = [ "//java/com/google/gerrit/common:annotations", "//lib:guava", + "//lib/auto:auto-value", + "//lib/auto:auto-value-annotations", "//lib/jgit/org.eclipse.jgit:jgit", ], )
diff --git a/java/com/google/gerrit/git/GitUpdateFailureException.java b/java/com/google/gerrit/git/GitUpdateFailureException.java new file mode 100644 index 0000000..76ef217 --- /dev/null +++ b/java/com/google/gerrit/git/GitUpdateFailureException.java
@@ -0,0 +1,95 @@ +// Copyright (C) 2019 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.git; + +import static com.google.common.collect.ImmutableList.toImmutableList; + +import com.google.auto.value.AutoValue; +import com.google.common.collect.ImmutableList; +import com.google.gerrit.common.Nullable; +import com.google.gerrit.common.UsedAt; +import java.io.IOException; +import java.util.Optional; +import org.eclipse.jgit.lib.BatchRefUpdate; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.transport.ReceiveCommand; + +/** Thrown when updating a ref in Git fails. */ +public class GitUpdateFailureException extends IOException { + private static final long serialVersionUID = 1L; + + private final ImmutableList<GitUpdateFailure> failures; + + public GitUpdateFailureException(String message, RefUpdate refUpdate) { + super(message); + this.failures = ImmutableList.of(GitUpdateFailure.create(refUpdate)); + } + + public GitUpdateFailureException(String message, BatchRefUpdate batchRefUpdate) { + super(message); + this.failures = + batchRefUpdate.getCommands().stream() + .filter(c -> c.getResult() != ReceiveCommand.Result.OK) + .map(GitUpdateFailure::create) + .collect(toImmutableList()); + } + + /** @return the names of the refs for which the update failed. */ + public ImmutableList<String> getFailedRefs() { + return failures.stream().map(GitUpdateFailure::ref).collect(toImmutableList()); + } + + /** @return the failures that caused this exception. */ + @UsedAt(UsedAt.Project.GOOGLE) + public ImmutableList<GitUpdateFailure> getFailures() { + return failures; + } + + @AutoValue + public abstract static class GitUpdateFailure { + private static GitUpdateFailure create(RefUpdate refUpdate) { + return builder().ref(refUpdate.getName()).result(refUpdate.getResult().name()).build(); + } + + private static GitUpdateFailure create(ReceiveCommand receiveCommand) { + return builder() + .ref(receiveCommand.getRefName()) + .result(receiveCommand.getResult().name()) + .message(receiveCommand.getMessage()) + .build(); + } + + public abstract String ref(); + + public abstract String result(); + + public abstract Optional<String> message(); + + public static GitUpdateFailure.Builder builder() { + return new AutoValue_GitUpdateFailureException_GitUpdateFailure.Builder(); + } + + @AutoValue.Builder + abstract static class Builder { + abstract Builder ref(String ref); + + abstract Builder result(String result); + + abstract Builder message(@Nullable String message); + + abstract GitUpdateFailure build(); + } + } +}
diff --git a/java/com/google/gerrit/git/LockFailureException.java b/java/com/google/gerrit/git/LockFailureException.java index 9e67d70..371488d 100644 --- a/java/com/google/gerrit/git/LockFailureException.java +++ b/java/com/google/gerrit/git/LockFailureException.java
@@ -14,36 +14,18 @@ package com.google.gerrit.git; -import static com.google.common.collect.ImmutableList.toImmutableList; - -import com.google.common.collect.ImmutableList; -import java.io.IOException; import org.eclipse.jgit.lib.BatchRefUpdate; import org.eclipse.jgit.lib.RefUpdate; -import org.eclipse.jgit.transport.ReceiveCommand; /** Thrown when updating a ref in Git fails with LOCK_FAILURE. */ -public class LockFailureException extends IOException { +public class LockFailureException extends GitUpdateFailureException { private static final long serialVersionUID = 1L; - private final ImmutableList<String> refs; - public LockFailureException(String message, RefUpdate refUpdate) { - super(message); - refs = ImmutableList.of(refUpdate.getName()); + super(message, refUpdate); } public LockFailureException(String message, BatchRefUpdate batchRefUpdate) { - super(message); - refs = - batchRefUpdate.getCommands().stream() - .filter(c -> c.getResult() == ReceiveCommand.Result.LOCK_FAILURE) - .map(ReceiveCommand::getRefName) - .collect(toImmutableList()); - } - - /** Subset of ref names that caused the lock failure. */ - public ImmutableList<String> getFailedRefs() { - return refs; + super(message, batchRefUpdate); } }
diff --git a/java/com/google/gerrit/git/RefUpdateUtil.java b/java/com/google/gerrit/git/RefUpdateUtil.java index 520d0f2..fa7b98f 100644 --- a/java/com/google/gerrit/git/RefUpdateUtil.java +++ b/java/com/google/gerrit/git/RefUpdateUtil.java
@@ -99,7 +99,7 @@ if (lockFailure + aborted == bru.getCommands().size()) { throw new LockFailureException("Update aborted with one or more lock failures: " + bru, bru); } else if (failure > 0) { - throw new IOException("Update failed: " + bru); + throw new GitUpdateFailureException("Update failed: " + bru, bru); } } @@ -130,7 +130,8 @@ case REJECTED_CURRENT_BRANCH: case REJECTED_MISSING_OBJECT: case REJECTED_OTHER_REASON: - throw new IOException("Failed to update " + ru.getName() + ": " + ru.getResult()); + throw new GitUpdateFailureException( + "Failed to update " + ru.getName() + ": " + ru.getResult(), ru); } } @@ -174,7 +175,8 @@ case REJECTED_MISSING_OBJECT: case REJECTED_OTHER_REASON: default: - throw new IOException("Failed to delete " + refName + ": " + ru.getResult()); + throw new GitUpdateFailureException( + "Failed to delete " + refName + ": " + ru.getResult(), ru); } }
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java index 33e4bdd..407a4bc 100644 --- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java +++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -166,6 +166,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.TimeUnit; @@ -280,6 +281,7 @@ private final Globals globals; private final Provider<RestCollection<RestResource, RestResource>> members; + private Optional<String> traceId; public RestApiServlet( Globals globals, RestCollection<? extends RestResource, ? extends RestResource> members) { @@ -328,10 +330,9 @@ logger.atFinest().log( "Received REST request: %s %s (parameters: %s)", req.getMethod(), req.getRequestURI(), getParameterNames(req)); + logger.atFinest().log("Calling user: %s", globals.currentUser.get().getLoggableName()); logger.atFinest().log( - "Calling user: %s (groups = %s)", - globals.currentUser.get().getLoggableName(), - globals.currentUser.get().getEffectiveGroups().getKnownGroups()); + "Groups: %s", globals.currentUser.get().getEffectiveGroups().getKnownGroups()); if (isCorsPreflight(req)) { doCorsPreflight(req, res); @@ -552,7 +553,8 @@ throw new ResourceNotFoundException(); } - response.traceId().ifPresent(traceId -> res.addHeader(X_GERRIT_TRACE, traceId)); + traceId = response.traceId(); + traceId.ifPresent(traceId -> res.addHeader(X_GERRIT_TRACE, traceId)); if (response instanceof Response.Redirect) { CacheHeaders.setNotCacheable(res); @@ -1486,11 +1488,12 @@ } } - private static long handleException( - Throwable err, HttpServletRequest req, HttpServletResponse res) throws IOException { + private long handleException(Throwable err, HttpServletRequest req, HttpServletResponse res) + throws IOException { logger.atSevere().withCause(err).log("Error in %s %s", req.getMethod(), uriForLogging(req)); if (!res.isCommitted()) { res.reset(); + traceId.ifPresent(traceId -> res.addHeader(X_GERRIT_TRACE, traceId)); return replyError(req, res, SC_INTERNAL_SERVER_ERROR, "Internal server error", err); } return 0;
diff --git a/java/com/google/gerrit/reviewdb/client/Change.java b/java/com/google/gerrit/reviewdb/client/Change.java index 9a3672f..f2e0596 100644 --- a/java/com/google/gerrit/reviewdb/client/Change.java +++ b/java/com/google/gerrit/reviewdb/client/Change.java
@@ -16,13 +16,19 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.gerrit.reviewdb.client.RefNames.REFS_CHANGES; +import static java.nio.charset.StandardCharsets.UTF_8; import com.google.auto.value.AutoValue; import com.google.common.primitives.Ints; import com.google.gerrit.common.Nullable; import com.google.gerrit.extensions.client.ChangeStatus; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.sql.Timestamp; import java.util.Arrays; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectInserter; /** * A change proposed to be merged into a branch. @@ -94,6 +100,16 @@ * notice of a replacement patch set is sent, or when notice of the change submission occurs. */ public final class Change { + private static final SecureRandom rng; + + static { + try { + rng = SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Cannot create RNG for Change-Id generator", e); + } + } + public static Id id(int id) { return new AutoValue_Change_Id(id); } @@ -248,6 +264,20 @@ } } + public static ObjectId generateChangeId() { + byte[] rand = new byte[Constants.OBJECT_ID_STRING_LENGTH]; + rng.nextBytes(rand); + String randomString = new String(rand, UTF_8); + + try (ObjectInserter f = new ObjectInserter.Formatter()) { + return f.idFor(Constants.OBJ_COMMIT, Constants.encode(randomString)); + } + } + + public static Key generateKey() { + return key("I" + generateChangeId().name()); + } + public static Key key(String key) { return new AutoValue_Change_Key(key); }
diff --git a/java/com/google/gerrit/server/ExceptionHook.java b/java/com/google/gerrit/server/ExceptionHook.java new file mode 100644 index 0000000..ea76330 --- /dev/null +++ b/java/com/google/gerrit/server/ExceptionHook.java
@@ -0,0 +1,42 @@ +// Copyright (C) 2019 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.server; + +import com.google.gerrit.extensions.annotations.ExtensionPoint; + +/** + * Allows implementors to control how certain exceptions should be handled. + * + * <p>This interface is intended to be implemented for multi-master setups to control the behavior + * for handling exceptions that are thrown by a lower layer that handles the consensus and + * synchronization between different server nodes. E.g. if an operation fails because consensus for + * a Git update could not be achieved (e.g. due to slow responding server nodes) this interface can + * be used to retry the request instead of failing it immediately. + */ +@ExtensionPoint +public interface ExceptionHook { + /** + * Whether an operation should be retried if it failed with the given throwable. + * + * <p>Only affects operations that are executed with {@link + * com.google.gerrit.server.update.RetryHelper}. + * + * @param throwable throwable that was thrown while executing the operation + * @return whether the operation should be retried + */ + default boolean shouldRetry(Throwable throwable) { + return false; + } +}
diff --git a/java/com/google/gerrit/server/StarredChangesUtil.java b/java/com/google/gerrit/server/StarredChangesUtil.java index 6a23084..1b1fab6 100644 --- a/java/com/google/gerrit/server/StarredChangesUtil.java +++ b/java/com/google/gerrit/server/StarredChangesUtil.java
@@ -32,6 +32,7 @@ import com.google.common.primitives.Ints; import com.google.gerrit.common.Nullable; import com.google.gerrit.exceptions.StorageException; +import com.google.gerrit.git.GitUpdateFailureException; import com.google.gerrit.git.LockFailureException; import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Change; @@ -268,7 +269,7 @@ if (command.getResult() == ReceiveCommand.Result.LOCK_FAILURE) { throw new LockFailureException(message, batchUpdate); } - throw new IOException(message); + throw new GitUpdateFailureException(message, batchUpdate); } } }
diff --git a/java/com/google/gerrit/server/change/ChangeInserter.java b/java/com/google/gerrit/server/change/ChangeInserter.java index 77f3e73..04ac477 100644 --- a/java/com/google/gerrit/server/change/ChangeInserter.java +++ b/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -83,7 +83,6 @@ import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.transport.ReceiveCommand; -import org.eclipse.jgit.util.ChangeIdUtil; public class ChangeInserter implements InsertChangeOp { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); @@ -203,18 +202,9 @@ if (!idList.isEmpty()) { return Change.key(idList.get(idList.size() - 1).trim()); } - // A Change-Id is computed for the review, but not appended to the commit message. + // A Change-Id is generated for the review, but not appended to the commit message. // This can happen if requireChangeId is false. - ObjectId changeId = - ChangeIdUtil.computeChangeId( - commit.getTree(), - commit, - commit.getAuthorIdent(), - commit.getCommitterIdent(), - commit.getShortMessage()); - StringBuilder changeIdStr = new StringBuilder(); - changeIdStr.append("I").append(ObjectId.toString(changeId)); - return Change.key(changeIdStr.toString()); + return Change.generateKey(); } public PatchSet.Id getPatchSetId() {
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java index 45b70b2..3794f04 100644 --- a/java/com/google/gerrit/server/config/GerritGlobalModule.java +++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -78,6 +78,7 @@ import com.google.gerrit.server.CmdLineParserModule; import com.google.gerrit.server.CreateGroupPermissionSyncer; import com.google.gerrit.server.DynamicOptions; +import com.google.gerrit.server.ExceptionHook; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.RequestListener; import com.google.gerrit.server.TraceRequestListener; @@ -390,6 +391,7 @@ DynamicSet.setOf(binder(), RequestListener.class); DynamicSet.bind(binder(), RequestListener.class).to(TraceRequestListener.class); DynamicSet.setOf(binder(), ChangeETagComputation.class); + DynamicSet.setOf(binder(), ExceptionHook.class); DynamicMap.mapOf(binder(), MailFilter.class); bind(MailFilter.class).annotatedWith(Exports.named("ListMailFilter")).to(ListMailFilter.class);
diff --git a/java/com/google/gerrit/server/git/meta/VersionedMetaData.java b/java/com/google/gerrit/server/git/meta/VersionedMetaData.java index f2180d7..aa735f9 100644 --- a/java/com/google/gerrit/server/git/meta/VersionedMetaData.java +++ b/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
@@ -18,8 +18,10 @@ import com.google.common.base.MoreObjects; import com.google.gerrit.common.Nullable; +import com.google.gerrit.git.GitUpdateFailureException; import com.google.gerrit.git.LockFailureException; import com.google.gerrit.git.ObjectIds; +import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.logging.Metadata; import com.google.gerrit.server.logging.TraceContext; @@ -327,14 +329,7 @@ } if (update.insertChangeId()) { - ObjectId id = - ChangeIdUtil.computeChangeId( - res, - getRevision(), - commit.getAuthor(), - commit.getCommitter(), - commit.getMessage()); - commit.setMessage(ChangeIdUtil.insertId(commit.getMessage(), id)); + commit.setMessage(ChangeIdUtil.insertId(commit.getMessage(), Change.generateChangeId())); } src = rw.parseCommit(inserter.insert(commit)); @@ -439,13 +434,14 @@ case REJECTED_MISSING_OBJECT: case REJECTED_OTHER_REASON: default: - throw new IOException( + throw new GitUpdateFailureException( "Cannot update " + ru.getName() + " in " + db.getDirectory() + ": " - + ru.getResult()); + + ru.getResult(), + ru); } } };
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java index 13ec5a8..3dcb695 100644 --- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java +++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -585,9 +585,8 @@ // Process as many commands as possible, but may leave some commands in state NOT_ATTEMPTED. private void processCommandsUnsafe( Collection<ReceiveCommand> commands, MultiProgressMonitor progress) { - logger.atFine().log( - "Calling user: %s (groups = %s)", - user.getLoggableName(), user.getEffectiveGroups().getKnownGroups()); + logger.atFine().log("Calling user: %s", user.getLoggableName()); + logger.atFine().log("Groups: %s", user.getEffectiveGroups().getKnownGroups()); if (!projectState.getProject().getState().permitsWrite()) { for (ReceiveCommand cmd : commands) {
diff --git a/java/com/google/gerrit/server/group/db/AuditLogReader.java b/java/com/google/gerrit/server/group/db/AuditLogReader.java index fb58577..77e0e0c 100644 --- a/java/com/google/gerrit/server/group/db/AuditLogReader.java +++ b/java/com/google/gerrit/server/group/db/AuditLogReader.java
@@ -27,7 +27,6 @@ import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit; import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.server.config.AllUsersName; -import com.google.gerrit.server.config.GerritServerId; import com.google.gerrit.server.notedb.NoteDbUtil; import com.google.inject.Inject; import com.google.inject.Singleton; @@ -51,12 +50,10 @@ public class AuditLogReader { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - private final String serverId; private final AllUsersName allUsersName; @Inject - public AuditLogReader(@GerritServerId String serverId, AllUsersName allUsersName) { - this.serverId = serverId; + public AuditLogReader(AllUsersName allUsersName) { this.allUsersName = allUsersName; } @@ -143,7 +140,7 @@ } private Optional<ParsedCommit> parse(AccountGroup.UUID uuid, RevCommit c) { - Optional<Account.Id> authorId = NoteDbUtil.parseIdent(c.getAuthorIdent(), serverId); + Optional<Account.Id> authorId = NoteDbUtil.parseIdent(c.getAuthorIdent()); if (!authorId.isPresent()) { // Only report audit events from identified users, since this was a non-nullable field in // ReviewDb. May be revisited. @@ -179,7 +176,7 @@ private Optional<Account.Id> parseAccount(AccountGroup.UUID uuid, RevCommit c, FooterLine line) { Optional<Account.Id> result = Optional.ofNullable(RawParseUtils.parsePersonIdent(line.getValue())) - .flatMap(ident -> NoteDbUtil.parseIdent(ident, serverId)); + .flatMap(ident -> NoteDbUtil.parseIdent(ident)); if (!result.isPresent()) { logInvalid(uuid, c, line); }
diff --git a/java/com/google/gerrit/server/logging/TraceContext.java b/java/com/google/gerrit/server/logging/TraceContext.java index 15a3532..30c5250 100644 --- a/java/com/google/gerrit/server/logging/TraceContext.java +++ b/java/com/google/gerrit/server/logging/TraceContext.java
@@ -179,38 +179,43 @@ public static class TraceTimer implements AutoCloseable { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - private final Consumer<Long> logFn; + private final Consumer<Long> doneLogFn; private final Stopwatch stopwatch; private TraceTimer(String operation) { this( + () -> logger.atFine().log("Starting timer for %s", operation), elapsedMs -> { LoggingContext.getInstance() .addPerformanceLogRecord(() -> PerformanceLogRecord.create(operation, elapsedMs)); - logger.atFine().log("%s (%d ms)", operation, elapsedMs); + logger.atFine().log("%s done (%d ms)", operation, elapsedMs); }); } private TraceTimer(String operation, Metadata metadata) { this( + () -> + logger.atFine().log( + "Starting timer for %s (%s)", operation, metadata.toStringForLogging()), elapsedMs -> { LoggingContext.getInstance() .addPerformanceLogRecord( () -> PerformanceLogRecord.create(operation, elapsedMs, metadata)); logger.atFine().log( - "%s (%s) (%d ms)", operation, metadata.toStringForLogging(), elapsedMs); + "%s (%s) done (%d ms)", operation, metadata.toStringForLogging(), elapsedMs); }); } - private TraceTimer(Consumer<Long> logFn) { - this.logFn = logFn; + private TraceTimer(Runnable startLogFn, Consumer<Long> doneLogFn) { + startLogFn.run(); + this.doneLogFn = doneLogFn; this.stopwatch = Stopwatch.createStarted(); } @Override public void close() { stopwatch.stop(); - logFn.accept(stopwatch.elapsed(TimeUnit.MILLISECONDS)); + doneLogFn.accept(stopwatch.elapsed(TimeUnit.MILLISECONDS)); } }
diff --git a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java index 9cc1c84..10bae48 100644 --- a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java +++ b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
@@ -25,6 +25,7 @@ import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.config.AllUsersName; +import com.google.gerrit.server.config.GerritServerId; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.notedb.ChangeNotesCommit.ChangeNotesRevWalk; import com.google.gerrit.server.project.NoSuchChangeException; @@ -51,6 +52,7 @@ public final AllUsersName allUsers; public final LegacyChangeNoteRead legacyChangeNoteRead; public final NoteDbMetrics metrics; + public final String serverId; // Providers required to avoid dependency cycles. @@ -64,7 +66,8 @@ ChangeNoteJson changeNoteJson, LegacyChangeNoteRead legacyChangeNoteRead, NoteDbMetrics metrics, - Provider<ChangeNotesCache> cache) { + Provider<ChangeNotesCache> cache, + @GerritServerId String serverId) { this.failOnLoadForTest = new AtomicBoolean(); this.repoManager = repoManager; this.allUsers = allUsers; @@ -72,6 +75,7 @@ this.changeNoteJson = changeNoteJson; this.metrics = metrics; this.cache = cache; + this.serverId = serverId; } }
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotes.java b/java/com/google/gerrit/server/notedb/ChangeNotes.java index e1217c2..929974d 100644 --- a/java/com/google/gerrit/server/notedb/ChangeNotes.java +++ b/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -22,6 +22,7 @@ import com.google.auto.value.AutoValue; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableSet; @@ -503,6 +504,19 @@ ChangeNotesCache.Value v = args.cache.get().get(getProjectName(), getChangeId(), rev, handle::walk); state = v.state(); + + String stateServerId = state.serverId(); + /** + * In earlier Gerrit versions serverId wasn't part of the change notes cache. That's why the + * earlier cached entries don't have the serverId attribute. That's fine because in earlier + * gerrit version serverId was already validated. Another approach to simplify the check would + * be to bump the cache version, but that would invalidate all persistent cache entries, what we + * rather try to avoid. + */ + checkState( + Strings.isNullOrEmpty(stateServerId) || args.serverId.equals(stateServerId), + String.format("invalid server id, expected %s: actual: %s", args.serverId, stateServerId)); + state.copyColumnsTo(change); revisionNoteMap = v.revisionNoteMap(); }
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java index 27c2aa6..b5087ff 100644 --- a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java +++ b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -135,6 +135,7 @@ private Timestamp createdOn; private Timestamp lastUpdatedOn; private Account.Id ownerId; + private String serverId; private String changeId; private String subject; private String originalSubject; @@ -223,6 +224,7 @@ createdOn, lastUpdatedOn, ownerId, + serverId, branch, buildCurrentPatchSetId(), subject, @@ -334,6 +336,10 @@ Account.Id accountId = parseIdent(commit); if (accountId != null) { ownerId = accountId; + PersonIdent personIdent = commit.getAuthorIdent(); + serverId = NoteDbUtil.extractHostPartFromPersonIdent(personIdent); + } else { + serverId = "UNKNOWN_SERVER_ID"; } Account.Id realAccountId = parseRealAccountId(commit, accountId);
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesState.java b/java/com/google/gerrit/server/notedb/ChangeNotesState.java index 2728516..5adc196 100644 --- a/java/com/google/gerrit/server/notedb/ChangeNotesState.java +++ b/java/com/google/gerrit/server/notedb/ChangeNotesState.java
@@ -96,6 +96,7 @@ Timestamp createdOn, Timestamp lastUpdatedOn, Account.Id owner, + String serverId, String branch, @Nullable PatchSet.Id currentPatchSetId, String subject, @@ -154,6 +155,7 @@ .build()) .pastAssignees(pastAssignees) .hashtags(hashtags) + .serverId(serverId) .patchSets(patchSets.entrySet()) .approvals(approvals.entries()) .reviewers(reviewers) @@ -276,6 +278,9 @@ abstract ImmutableSet<String> hashtags(); + @Nullable + abstract String serverId(); + abstract ImmutableList<Map.Entry<PatchSet.Id, PatchSet>> patchSets(); abstract ImmutableList<Map.Entry<PatchSet.Id, PatchSetApproval>> approvals(); @@ -376,6 +381,8 @@ abstract Builder columns(ChangeColumns columns); + abstract Builder serverId(String serverId); + abstract Builder pastAssignees(Set<Account.Id> pastAssignees); abstract Builder hashtags(Iterable<String> hashtags); @@ -427,6 +434,10 @@ .setChangeId(object.changeId().get()) .setColumns(toChangeColumnsProto(object.columns())); + if (object.serverId() != null) { + b.setServerId(object.serverId()); + b.setHasServerId(true); + } object.pastAssignees().forEach(a -> b.addPastAssignee(a.get())); object.hashtags().forEach(b::addHashtag); object @@ -549,6 +560,7 @@ .metaId(ObjectIdConverter.create().fromByteString(proto.getMetaId())) .changeId(changeId) .columns(toChangeColumns(changeId, proto.getColumns())) + .serverId(proto.getHasServerId() ? proto.getServerId() : null) .pastAssignees( proto.getPastAssigneeList().stream().map(Account::id).collect(toImmutableSet())) .hashtags(proto.getHashtagList())
diff --git a/java/com/google/gerrit/server/notedb/LegacyChangeNoteRead.java b/java/com/google/gerrit/server/notedb/LegacyChangeNoteRead.java index 36bfe47..7e301f7 100644 --- a/java/com/google/gerrit/server/notedb/LegacyChangeNoteRead.java +++ b/java/com/google/gerrit/server/notedb/LegacyChangeNoteRead.java
@@ -50,14 +50,11 @@ public Account.Id parseIdent(PersonIdent ident, Change.Id changeId) throws ConfigInvalidException { - return NoteDbUtil.parseIdent(ident, serverId) + return NoteDbUtil.parseIdent(ident) .orElseThrow( () -> parseException( - changeId, - "invalid identity, expected <id>@%s: %s", - serverId, - ident.getEmailAddress())); + changeId, "cannot retrieve account id: %s", ident.getEmailAddress())); } private static boolean match(byte[] note, MutableInteger p, byte[] expected) {
diff --git a/java/com/google/gerrit/server/notedb/NoteDbUtil.java b/java/com/google/gerrit/server/notedb/NoteDbUtil.java index c53f4b9..bfbdd18 100644 --- a/java/com/google/gerrit/server/notedb/NoteDbUtil.java +++ b/java/com/google/gerrit/server/notedb/NoteDbUtil.java
@@ -37,25 +37,28 @@ ImmutableSet.of( "com.google.gerrit.httpd.restapi.RestApiServlet", RetryingRestModifyView.class.getName()); - /** - * Returns an AccountId for the given email address. Returns empty if the address isn't on this - * server. - */ - public static Optional<Account.Id> parseIdent(PersonIdent ident, String serverId) { + /** Returns an AccountId for the given email address. */ + public static Optional<Account.Id> parseIdent(PersonIdent ident) { String email = ident.getEmailAddress(); int at = email.indexOf('@'); if (at >= 0) { - String host = email.substring(at + 1); - if (host.equals(serverId)) { - Integer id = Ints.tryParse(email.substring(0, at)); - if (id != null) { - return Optional.of(Account.id(id)); - } + Integer id = Ints.tryParse(email.substring(0, at)); + if (id != null) { + return Optional.of(Account.id(id)); } } return Optional.empty(); } + public static String extractHostPartFromPersonIdent(PersonIdent ident) { + String email = ident.getEmailAddress(); + int at = email.indexOf('@'); + if (at >= 0) { + return email.substring(at + 1); + } + throw new IllegalArgumentException("No host part found: " + email); + } + public static String formatTime(PersonIdent ident, Timestamp t) { GitDateFormatter dateFormatter = new GitDateFormatter(Format.DEFAULT); // TODO(dborowitz): Use a ThreadLocal or use Joda.
diff --git a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java index 858edf2..b8054cd 100644 --- a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java +++ b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
@@ -134,9 +134,8 @@ logger.atFinest().log( "Filter refs for repository %s by visibility (options = %s, refs = %s)", projectState.getNameKey(), opts, refs); - logger.atFinest().log( - "Calling user: %s (groups = %s)", - user.getLoggableName(), user.getEffectiveGroups().getKnownGroups()); + logger.atFinest().log("Calling user: %s", user.getLoggableName()); + logger.atFinest().log("Groups: %s", user.getEffectiveGroups().getKnownGroups()); logger.atFinest().log( "auth.skipFullRefEvaluationIfAllRefsAreVisible = %s", skipFullRefEvaluationIfAllRefsAreVisible);
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPick.java b/java/com/google/gerrit/server/restapi/change/CherryPick.java index bcbee4d..9dd6b86 100644 --- a/java/com/google/gerrit/server/restapi/change/CherryPick.java +++ b/java/com/google/gerrit/server/restapi/change/CherryPick.java
@@ -81,9 +81,7 @@ throws IOException, UpdateException, RestApiException, PermissionBackendException, ConfigInvalidException, NoSuchProjectException { input.parent = input.parent == null ? 1 : input.parent; - if (input.message == null || input.message.trim().isEmpty()) { - throw new BadRequestException("message must be non-empty"); - } else if (input.destination == null || input.destination.trim().isEmpty()) { + if (input.destination == null || input.destination.trim().isEmpty()) { throw new BadRequestException("destination must be non-empty"); }
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java index afd9d8e..bf920e1 100644 --- a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java +++ b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
@@ -182,17 +182,14 @@ input.parent, commitToCherryPick.getParentCount())); } + String message = Strings.nullToEmpty(input.message).trim(); + message = message.isEmpty() ? commitToCherryPick.getFullMessage() : message; + Timestamp now = TimeUtil.nowTs(); PersonIdent committerIdent = identifiedUser.newCommitterIdent(now, serverTimeZone); - final ObjectId computedChangeId = - ChangeIdUtil.computeChangeId( - commitToCherryPick.getTree(), - baseCommit, - commitToCherryPick.getAuthorIdent(), - committerIdent, - input.message); - String commitMessage = ChangeIdUtil.insertId(input.message, computedChangeId).trim() + '\n'; + final ObjectId generatedChangeId = Change.generateChangeId(); + String commitMessage = ChangeIdUtil.insertId(message, generatedChangeId).trim() + '\n'; CodeReviewCommit cherryPickCommit; ProjectState projectState = projectCache.checkedGet(dest.project()); @@ -227,7 +224,7 @@ final String idStr = idList.get(idList.size() - 1).trim(); changeKey = Change.key(idStr); } else { - changeKey = Change.key("I" + computedChangeId.name()); + changeKey = Change.key("I" + generatedChangeId.name()); } BranchNameKey newDest = BranchNameKey.create(project, destRef.getName());
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java index 25cd924..0216a90 100644 --- a/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java +++ b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
@@ -43,7 +43,6 @@ import com.google.inject.Singleton; import java.io.IOException; import org.eclipse.jgit.errors.ConfigInvalidException; -import org.eclipse.jgit.revwalk.RevCommit; @Singleton public class CherryPickCommit @@ -75,9 +74,6 @@ BatchUpdate.Factory updateFactory, CommitResource rsrc, CherryPickInput input) throws IOException, UpdateException, RestApiException, PermissionBackendException, ConfigInvalidException, NoSuchProjectException { - RevCommit commit = rsrc.getCommit(); - String message = Strings.nullToEmpty(input.message).trim(); - input.message = message.isEmpty() ? commit.getFullMessage() : message; String destination = Strings.nullToEmpty(input.destination).trim(); input.parent = input.parent == null ? 1 : input.parent; Project.NameKey projectName = rsrc.getProjectState().getNameKey(); @@ -101,7 +97,7 @@ updateFactory, null, projectName, - commit, + rsrc.getCommit(), input, BranchNameKey.create(rsrc.getProjectState().getNameKey(), refName)); CherryPickChangeInfo changeInfo =
diff --git a/java/com/google/gerrit/server/restapi/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java index 445279f..c6c9595 100644 --- a/java/com/google/gerrit/server/restapi/change/CreateChange.java +++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -37,6 +37,7 @@ import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.extensions.restapi.UnprocessableEntityException; import com.google.gerrit.reviewdb.client.BooleanProjectConfig; +import com.google.gerrit.reviewdb.client.BranchNameKey; import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.Project; @@ -64,6 +65,7 @@ import com.google.gerrit.server.project.InvalidChangeOperationException; import com.google.gerrit.server.project.ProjectResource; import com.google.gerrit.server.project.ProjectState; +import com.google.gerrit.server.query.change.InternalChangeQuery; import com.google.gerrit.server.restapi.project.CommitsCollection; import com.google.gerrit.server.restapi.project.ProjectsCollection; import com.google.gerrit.server.update.BatchUpdate; @@ -78,11 +80,13 @@ import java.sql.Timestamp; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.TimeZone; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.InvalidObjectIdException; import org.eclipse.jgit.lib.CommitBuilder; import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectReader; @@ -109,6 +113,7 @@ private final ChangeInserter.Factory changeInserterFactory; private final ChangeJson.Factory jsonFactory; private final ChangeFinder changeFinder; + private final Provider<InternalChangeQuery> queryProvider; private final PatchSetUtil psUtil; private final MergeUtil.Factory mergeUtilFactory; private final SubmitType submitType; @@ -129,6 +134,7 @@ ChangeInserter.Factory changeInserterFactory, ChangeJson.Factory json, ChangeFinder changeFinder, + Provider<InternalChangeQuery> queryProvider, RetryHelper retryHelper, PatchSetUtil psUtil, @GerritServerConfig Config config, @@ -147,6 +153,7 @@ this.changeInserterFactory = changeInserterFactory; this.jsonFactory = json; this.changeFinder = changeFinder; + this.queryProvider = queryProvider; this.psUtil = psUtil; this.submitType = config.getEnum("project", null, "submitType", SubmitType.MERGE_IF_NECESSARY); this.disablePrivateChanges = config.getBoolean("change", null, "disablePrivateChanges", false); @@ -208,6 +215,20 @@ } input.subject = subject; + Optional<String> changeId = getChangeIdFromMessage(input.subject); + if (changeId.isPresent()) { + if (!queryProvider + .get() + .setLimit(1) + .byBranchKey( + BranchNameKey.create(input.project, input.branch), Change.key(changeId.get())) + .isEmpty()) { + throw new ResourceConflictException( + String.format( + "A change with Change-Id %s already exists for this branch.", changeId.get())); + } + } + if (input.topic != null) { input.topic = Strings.emptyToNull(input.topic.trim()); } @@ -289,7 +310,7 @@ Timestamp now = TimeUtil.nowTs(); PersonIdent author = me.newCommitterIdent(now, serverTimeZone); - String commitMessage = getCommitMessage(input.subject, me, oi, mergeTip, author); + String commitMessage = getCommitMessage(input.subject, me); RevCommit c; if (input.merge != null) { @@ -386,18 +407,22 @@ return parentCommit; } - private String getCommitMessage( - String subject, - IdentifiedUser me, - ObjectInserter objectInserter, - RevCommit mergeTip, - PersonIdent author) - throws IOException { + private Optional<String> getChangeIdFromMessage(String subject) { + int indexOfChangeId = ChangeIdUtil.indexOfChangeId(subject, "\n"); + if (indexOfChangeId == -1) { + return Optional.empty(); + } + return Optional.of( + subject.substring( + indexOfChangeId + 11 /* "Change-Id: "*/, + indexOfChangeId + 12 /* "Change-Id: I" */ + Constants.OBJECT_ID_STRING_LENGTH)); + } + + private String getCommitMessage(String subject, IdentifiedUser me) { // Add a Change-Id line if there isn't already one String commitMessage = subject; if (ChangeIdUtil.indexOfChangeId(commitMessage, "\n") == -1) { - ObjectId treeId = mergeTip == null ? emptyTreeId(objectInserter) : mergeTip.getTree(); - ObjectId id = ChangeIdUtil.computeChangeId(treeId, mergeTip, author, author, commitMessage); + ObjectId id = Change.generateChangeId(); commitMessage = ChangeIdUtil.insertId(commitMessage, id); }
diff --git a/java/com/google/gerrit/server/restapi/change/Revert.java b/java/com/google/gerrit/server/restapi/change/Revert.java index 413d780..c69a348 100644 --- a/java/com/google/gerrit/server/restapi/change/Revert.java +++ b/java/com/google/gerrit/server/restapi/change/Revert.java
@@ -201,14 +201,8 @@ patch.commitId().name()); } - ObjectId computedChangeId = - ChangeIdUtil.computeChangeId( - parentToCommitToRevert.getTree(), - commitToRevert, - authorIdent, - committerIdent, - message); - revertCommitBuilder.setMessage(ChangeIdUtil.insertId(message, computedChangeId, true)); + ObjectId generatedChangeId = Change.generateChangeId(); + revertCommitBuilder.setMessage(ChangeIdUtil.insertId(message, generatedChangeId, true)); Change.Id changeId = Change.id(seq.nextChangeId()); ObjectId id = oi.insert(revertCommitBuilder); @@ -240,7 +234,7 @@ bu.setNotify(notify); bu.insertChange(ins); bu.addOp(changeId, new NotifyOp(changeToRevert, ins)); - bu.addOp(changeToRevert.getId(), new PostRevertedMessageOp(computedChangeId)); + bu.addOp(changeToRevert.getId(), new PostRevertedMessageOp(generatedChangeId)); bu.execute(); } return changeId;
diff --git a/java/com/google/gerrit/server/restapi/change/Submit.java b/java/com/google/gerrit/server/restapi/change/Submit.java index 9216eec..fdca897 100644 --- a/java/com/google/gerrit/server/restapi/change/Submit.java +++ b/java/com/google/gerrit/server/restapi/change/Submit.java
@@ -19,10 +19,12 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Strings; +import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Sets; import com.google.common.flogger.FluentLogger; +import com.google.gerrit.common.UsedAt; import com.google.gerrit.common.data.ParameterizedString; import com.google.gerrit.exceptions.StorageException; import com.google.gerrit.extensions.api.changes.SubmitInput; @@ -182,12 +184,13 @@ } projectCache.checkedGet(rsrc.getProject()).checkStatePermitsWrite(); - return Response.ok(new Output(mergeChange(rsrc, submitter, input))); + return mergeChange(rsrc, submitter, input); } - public Change mergeChange(RevisionResource rsrc, IdentifiedUser submitter, SubmitInput input) - throws RestApiException, IOException, UpdateException, ConfigInvalidException, - PermissionBackendException { + @UsedAt(UsedAt.Project.GOOGLE) + public Response<Output> mergeChange( + RevisionResource rsrc, IdentifiedUser submitter, SubmitInput input) + throws RestApiException, IOException { Change change = rsrc.getChange(); if (!change.isNew()) { throw new ResourceConflictException("change is " + ChangeUtil.status(change)); @@ -202,10 +205,17 @@ } try (MergeOp op = mergeOpProvider.get()) { - Change updatedChange = op.merge(change, submitter, true, input, false); + Change updatedChange; + + try { + updatedChange = op.merge(change, submitter, true, input, false); + } catch (Exception e) { + Throwables.throwIfInstanceOf(e, RestApiException.class); + return Response.<Output>internalServerError(e).traceId(op.getTraceId().orElse(null)); + } if (updatedChange.isMerged()) { - return change; + return Response.ok(new Output(change)); } String msg = @@ -460,8 +470,14 @@ throw new ResourceConflictException("current revision is missing"); } - Output out = submit.apply(new RevisionResource(rsrc, ps), input).value(); - return Response.ok(json.noOptions().format(out.change)); + Response<Output> response = submit.apply(new RevisionResource(rsrc, ps), input); + if (response instanceof Response.InternalServerError) { + Response.InternalServerError<?> ise = (Response.InternalServerError<?>) response; + return Response.<ChangeInfo>internalServerError(ise.cause()) + .traceId(ise.traceId().orElse(null)); + } + + return Response.ok(json.noOptions().format(response.value().change)); } } }
diff --git a/java/com/google/gerrit/server/submit/MergeOp.java b/java/com/google/gerrit/server/submit/MergeOp.java index 024880f..813f9ab 100644 --- a/java/com/google/gerrit/server/submit/MergeOp.java +++ b/java/com/google/gerrit/server/submit/MergeOp.java
@@ -91,6 +91,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; @@ -247,6 +248,7 @@ private Set<Project.NameKey> allProjects; private boolean dryrun; private TopicMetrics topicMetrics; + private String traceId; @Inject MergeOp( @@ -518,6 +520,7 @@ .multipliedBy(cs.projects().size())) .caller(getClass()) .retryWithTrace(t -> !(t instanceof RestApiException)) + .onAutoTrace(traceId -> this.traceId = traceId) .build()); if (projects > 1) { @@ -538,6 +541,10 @@ } } + public Optional<String> getTraceId() { + return Optional.ofNullable(traceId); + } + private void openRepoManager() { if (orm != null) { orm.close();
diff --git a/java/com/google/gerrit/server/update/RetryHelper.java b/java/com/google/gerrit/server/update/RetryHelper.java index b120379..bea3867 100644 --- a/java/com/google/gerrit/server/update/RetryHelper.java +++ b/java/com/google/gerrit/server/update/RetryHelper.java
@@ -39,10 +39,12 @@ import com.google.gerrit.metrics.Description; import com.google.gerrit.metrics.Field; import com.google.gerrit.metrics.MetricMaker; +import com.google.gerrit.server.ExceptionHook; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.logging.Metadata; import com.google.gerrit.server.logging.RequestId; import com.google.gerrit.server.logging.TraceContext; +import com.google.gerrit.server.plugincontext.PluginSetContext; import com.google.inject.Inject; import com.google.inject.Singleton; import java.time.Duration; @@ -182,14 +184,19 @@ private final Metrics metrics; private final BatchUpdate.Factory updateFactory; + private final PluginSetContext<ExceptionHook> exceptionHooks; private final Map<ActionType, Duration> defaultTimeouts; private final WaitStrategy waitStrategy; @Nullable private final Consumer<RetryerBuilder<?>> overwriteDefaultRetryerStrategySetup; private final boolean retryWithTraceOnFailure; @Inject - RetryHelper(@GerritServerConfig Config cfg, Metrics metrics, BatchUpdate.Factory updateFactory) { - this(cfg, metrics, updateFactory, null); + RetryHelper( + @GerritServerConfig Config cfg, + Metrics metrics, + PluginSetContext<ExceptionHook> exceptionHooks, + BatchUpdate.Factory updateFactory) { + this(cfg, metrics, updateFactory, exceptionHooks, null); } @VisibleForTesting @@ -197,9 +204,11 @@ @GerritServerConfig Config cfg, Metrics metrics, BatchUpdate.Factory updateFactory, + PluginSetContext<ExceptionHook> exceptionHooks, @Nullable Consumer<RetryerBuilder<?>> overwriteDefaultRetryerStrategySetup) { this.metrics = metrics; this.updateFactory = updateFactory; + this.exceptionHooks = exceptionHooks; Duration defaultTimeout = Duration.ofMillis( @@ -308,6 +317,11 @@ return true; } + // Exception hooks may identify additional exceptions for retry. + if (exceptionHooks.stream().anyMatch(h -> h.shouldRetry(t))) { + return true; + } + // A non-recoverable failure occurred. Check if we should retry to capture a trace // of the failure. If a trace was already done there is no need to retry. if (retryWithTraceOnFailure
diff --git a/javatests/com/google/gerrit/acceptance/annotation/BUILD b/javatests/com/google/gerrit/acceptance/annotation/BUILD index 5476bb6..8dd99c9 100644 --- a/javatests/com/google/gerrit/acceptance/annotation/BUILD +++ b/javatests/com/google/gerrit/acceptance/annotation/BUILD
@@ -4,4 +4,5 @@ srcs = glob(["*.java"]), group = "annotation", labels = ["annotation"], + deps = ["//java/com/google/gerrit/server/util/time"], )
diff --git a/javatests/com/google/gerrit/acceptance/annotation/UseClockStepTest.java b/javatests/com/google/gerrit/acceptance/annotation/UseClockStepTest.java new file mode 100644 index 0000000..ecfe3f5 --- /dev/null +++ b/javatests/com/google/gerrit/acceptance/annotation/UseClockStepTest.java
@@ -0,0 +1,57 @@ +// Copyright (C) 2019 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.annotation; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.acceptance.UseClockStep; +import com.google.gerrit.server.util.time.TimeUtil; +import java.sql.Timestamp; +import java.time.Instant; +import java.util.concurrent.TimeUnit; +import org.junit.Test; + +public class UseClockStepTest extends AbstractDaemonTest { + @Test + @UseClockStep + public void useClockStepWithDefaults() { + long firstTimestamp = TimeUtil.nowMs(); + long secondTimestamp = TimeUtil.nowMs(); + assertThat(secondTimestamp - firstTimestamp).isEqualTo(1000); + } + + @Test + @UseClockStep(clockStepUnit = TimeUnit.MINUTES) + public void useClockStepWithTimeUnit() { + long firstTimestamp = TimeUtil.nowMs(); + long secondTimestamp = TimeUtil.nowMs(); + assertThat(secondTimestamp - firstTimestamp).isEqualTo(60 * 1000); + } + + @Test + @UseClockStep(clockStep = 5) + public void useClockStepWithClockStep() { + long firstTimestamp = TimeUtil.nowMs(); + long secondTimestamp = TimeUtil.nowMs(); + assertThat(secondTimestamp - firstTimestamp).isEqualTo(5 * 1000); + } + + @Test + @UseClockStep(startAtEpoch = true) + public void useClockStepWithStartAtEpoch() { + assertThat(TimeUtil.nowTs()).isEqualTo(Timestamp.from(Instant.EPOCH)); + } +}
diff --git a/javatests/com/google/gerrit/acceptance/annotation/UseSystemTimeTest.java b/javatests/com/google/gerrit/acceptance/annotation/UseSystemTimeTest.java new file mode 100644 index 0000000..7480200 --- /dev/null +++ b/javatests/com/google/gerrit/acceptance/annotation/UseSystemTimeTest.java
@@ -0,0 +1,34 @@ +// Copyright (C) 2019 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.annotation; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.acceptance.UseClockStep; +import com.google.gerrit.acceptance.UseSystemTime; +import com.google.gerrit.server.util.time.TimeUtil; +import org.junit.Test; + +@UseClockStep +public class UseSystemTimeTest extends AbstractDaemonTest { + @Test + @UseSystemTime + public void useSystemTimeAlthoughClassIsAnnotatedWithUseClockStep() { + long firstTimestamp = TimeUtil.nowMs(); + long secondTimestamp = TimeUtil.nowMs(); + assertThat(secondTimestamp - firstTimestamp).isLessThan(1000); + } +}
diff --git a/javatests/com/google/gerrit/acceptance/annotation/UseTimezoneTest.java b/javatests/com/google/gerrit/acceptance/annotation/UseTimezoneTest.java new file mode 100644 index 0000000..abf5eda --- /dev/null +++ b/javatests/com/google/gerrit/acceptance/annotation/UseTimezoneTest.java
@@ -0,0 +1,35 @@ +// Copyright (C) 2019 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.annotation; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.acceptance.UseTimezone; +import org.junit.Test; + +public class UseTimezoneTest extends AbstractDaemonTest { + @Test + @UseTimezone(timezone = "US/Eastern") + public void usEastern() { + assertThat(System.getProperty("user.timezone")).isEqualTo("US/Eastern"); + } + + @Test + @UseTimezone(timezone = "UTC") + public void utc() { + assertThat(System.getProperty("user.timezone")).isEqualTo("UTC"); + } +}
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java index 9fb6d26..eb59669 100644 --- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java +++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -61,6 +61,7 @@ import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.acceptance.Sandboxed; import com.google.gerrit.acceptance.TestAccount; +import com.google.gerrit.acceptance.UseClockStep; import com.google.gerrit.acceptance.UseSsh; import com.google.gerrit.acceptance.testsuite.account.AccountOperations; import com.google.gerrit.acceptance.testsuite.account.TestSshKeys; @@ -133,6 +134,8 @@ import com.google.gerrit.server.notedb.Sequences; import com.google.gerrit.server.permissions.PermissionBackend; import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions; +import com.google.gerrit.server.plugincontext.PluginContext.PluginMetrics; +import com.google.gerrit.server.plugincontext.PluginSetContext; import com.google.gerrit.server.project.ProjectConfig; import com.google.gerrit.server.project.RefPattern; import com.google.gerrit.server.query.account.InternalAccountQuery; @@ -143,7 +146,6 @@ import com.google.gerrit.server.validators.ValidationException; import com.google.gerrit.testing.ConfigSuite; import com.google.gerrit.testing.FakeEmailSender.Message; -import com.google.gerrit.testing.TestTimeUtil; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.name.Named; @@ -419,36 +421,32 @@ } @Test + @UseClockStep public void createAtomically() throws Exception { - TestTimeUtil.resetWithClockStep(1, SECONDS); - try { - Account.Id accountId = Account.id(seq.nextAccountId()); - String fullName = "Foo"; - ExternalId extId = ExternalId.createEmail(accountId, "foo@example.com"); - AccountState accountState = - accountsUpdateProvider - .get() - .insert( - "Create Account Atomically", - accountId, - u -> u.setFullName(fullName).addExternalId(extId)); - assertThat(accountState.getAccount().fullName()).isEqualTo(fullName); + Account.Id accountId = Account.id(seq.nextAccountId()); + String fullName = "Foo"; + ExternalId extId = ExternalId.createEmail(accountId, "foo@example.com"); + AccountState accountState = + accountsUpdateProvider + .get() + .insert( + "Create Account Atomically", + accountId, + u -> u.setFullName(fullName).addExternalId(extId)); + assertThat(accountState.getAccount().fullName()).isEqualTo(fullName); - AccountInfo info = gApi.accounts().id(accountId.get()).get(); - assertThat(info.name).isEqualTo(fullName); + AccountInfo info = gApi.accounts().id(accountId.get()).get(); + assertThat(info.name).isEqualTo(fullName); - List<EmailInfo> emails = gApi.accounts().id(accountId.get()).getEmails(); - assertThat(emails.stream().map(e -> e.email).collect(toSet())).containsExactly(extId.email()); + List<EmailInfo> emails = gApi.accounts().id(accountId.get()).getEmails(); + assertThat(emails.stream().map(e -> e.email).collect(toSet())).containsExactly(extId.email()); - RevCommit commitUserBranch = - projectOperations.project(allUsers).getHead(RefNames.refsUsers(accountId)); - RevCommit commitRefsMetaExternalIds = - projectOperations.project(allUsers).getHead(RefNames.REFS_EXTERNAL_IDS); - assertThat(commitUserBranch.getCommitTime()) - .isEqualTo(commitRefsMetaExternalIds.getCommitTime()); - } finally { - TestTimeUtil.useSystemTime(); - } + RevCommit commitUserBranch = + projectOperations.project(allUsers).getHead(RefNames.refsUsers(accountId)); + RevCommit commitRefsMetaExternalIds = + projectOperations.project(allUsers).getHead(RefNames.REFS_EXTERNAL_IDS); + assertThat(commitUserBranch.getCommitTime()) + .isEqualTo(commitRefsMetaExternalIds.getCommitTime()); } @Test @@ -2593,7 +2591,11 @@ externalIds, metaDataUpdateInternalFactory, new RetryHelper( - cfg, retryMetrics, null, r -> r.withBlockStrategy(noSleepBlockStrategy)), + cfg, + retryMetrics, + null, + new PluginSetContext<>(DynamicSet.emptySet(), PluginMetrics.DISABLED_INSTANCE), + r -> r.withBlockStrategy(noSleepBlockStrategy)), extIdNotesFactory, ident, ident, @@ -2646,6 +2648,7 @@ cfg, retryMetrics, null, + new PluginSetContext<>(DynamicSet.emptySet(), PluginMetrics.DISABLED_INSTANCE), r -> r.withStopStrategy(StopStrategies.stopAfterAttempt(status.size())) .withBlockStrategy(noSleepBlockStrategy)), @@ -2700,7 +2703,11 @@ externalIds, metaDataUpdateInternalFactory, new RetryHelper( - cfg, retryMetrics, null, r -> r.withBlockStrategy(noSleepBlockStrategy)), + cfg, + retryMetrics, + null, + new PluginSetContext<>(DynamicSet.emptySet(), PluginMetrics.DISABLED_INSTANCE), + r -> r.withBlockStrategy(noSleepBlockStrategy)), extIdNotesFactory, ident, ident, @@ -2769,7 +2776,11 @@ externalIds, metaDataUpdateInternalFactory, new RetryHelper( - cfg, retryMetrics, null, r -> r.withBlockStrategy(noSleepBlockStrategy)), + cfg, + retryMetrics, + null, + new PluginSetContext<>(DynamicSet.emptySet(), PluginMetrics.DISABLED_INSTANCE), + r -> r.withBlockStrategy(noSleepBlockStrategy)), extIdNotesFactory, ident, ident, @@ -2906,9 +2917,9 @@ } @Test + @UseClockStep public void deleteAllDraftComments() throws Exception { try { - TestTimeUtil.resetWithClockStep(1, SECONDS); Project.NameKey project2 = projectOperations.newProject().create(); PushOneCommit.Result r1 = createChange();
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java index 6dc8fc5..e1af2e4 100644 --- a/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java +++ b/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
@@ -18,12 +18,12 @@ import static com.google.common.truth.TruthJUnit.assume; import static com.google.gerrit.testing.GerritJUnit.assertThrows; import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.collect.ImmutableList; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.GerritConfig; import com.google.gerrit.acceptance.PushOneCommit; +import com.google.gerrit.acceptance.UseClockStep; import com.google.gerrit.acceptance.testsuite.group.GroupOperations; import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; import com.google.gerrit.common.RawInputUtil; @@ -51,15 +51,13 @@ import com.google.gerrit.server.group.InternalGroup; import com.google.gerrit.server.project.ProjectConfig; import com.google.gerrit.testing.ConfigSuite; -import com.google.gerrit.testing.TestTimeUtil; import com.google.inject.Inject; import java.util.List; import org.eclipse.jgit.lib.Config; -import org.junit.AfterClass; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; +@UseClockStep public class AgreementsIT extends AbstractDaemonTest { private ContributorAgreement caAutoVerify; private ContributorAgreement caNoAutoVerify; @@ -112,16 +110,6 @@ return cfg; } - @BeforeClass - public static void setTimeForTesting() { - TestTimeUtil.resetWithClockStep(1, SECONDS); - } - - @AfterClass - public static void restoreTime() { - TestTimeUtil.useSystemTime(); - } - @Before public void setUp() throws Exception { caAutoVerify = configureContributorAgreement(true);
diff --git a/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java b/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java index 87f9a2d5..668dcfd 100644 --- a/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java +++ b/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java
@@ -20,7 +20,6 @@ import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; import static com.google.gerrit.testing.GerritJUnit.assertThrows; import static java.util.concurrent.TimeUnit.HOURS; -import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.stream.Collectors.toList; import com.google.common.collect.ImmutableList; @@ -28,6 +27,7 @@ import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.GerritConfig; import com.google.gerrit.acceptance.PushOneCommit; +import com.google.gerrit.acceptance.UseClockStep; import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; import com.google.gerrit.common.data.Permission; @@ -113,10 +113,9 @@ } @Test + @UseClockStep @GerritConfig(name = "changeCleanup.abandonAfter", value = "1w") public void abandonInactiveOpenChanges() throws Exception { - TestTimeUtil.resetWithClockStep(1, SECONDS); - // create 2 changes which will be abandoned ... int id1 = createChange().getChange().getId().get(); int id2 = createChange().getChange().getId().get();
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java index cf72874..61e7582 100644 --- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java +++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -58,7 +58,6 @@ import static com.google.gerrit.truth.CacheStatsSubject.assertThat; import static com.google.gerrit.truth.CacheStatsSubject.cloneStats; import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; @@ -78,6 +77,8 @@ import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.acceptance.TestProjectInput; +import com.google.gerrit.acceptance.UseClockStep; +import com.google.gerrit.acceptance.UseTimezone; import com.google.gerrit.acceptance.testsuite.account.AccountOperations; import com.google.gerrit.acceptance.testsuite.group.GroupOperations; import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; @@ -182,7 +183,6 @@ import com.google.gerrit.server.update.ChangeContext; import com.google.gerrit.server.util.time.TimeUtil; import com.google.gerrit.testing.FakeEmailSender.Message; -import com.google.gerrit.testing.TestTimeUtil; import com.google.inject.AbstractModule; import com.google.inject.Inject; import com.google.inject.name.Named; @@ -213,8 +213,8 @@ import org.junit.Test; @NoHttpd +@UseTimezone(timezone = "US/Eastern") public class ChangeIT extends AbstractDaemonTest { - private String systemTimeZone; @Inject private AccountOperations accountOperations; @Inject private ChangeIndexCollection changeIndexCollection; @@ -242,17 +242,6 @@ private RegistrationHandle changeIndexedCounterHandle; @Before - public void setTimeForTesting() { - systemTimeZone = System.setProperty("user.timezone", "US/Eastern"); - } - - @After - public void resetTime() { - TestTimeUtil.useSystemTime(); - System.setProperty("user.timezone", systemTimeZone); - } - - @Before public void addChangeIndexedCounter() { changeIndexedCounter = new ChangeIndexedCounter(); changeIndexedCounterHandle = changeIndexedListeners.add("gerrit", changeIndexedCounter); @@ -1896,6 +1885,7 @@ } @Test + @UseClockStep public void addReviewer() throws Exception { testAddReviewerViaPostReview( (changeId, reviewer) -> { @@ -1906,6 +1896,7 @@ } @Test + @UseClockStep public void addReviewerViaPostReview() throws Exception { testAddReviewerViaPostReview( (changeId, reviewer) -> { @@ -1918,7 +1909,6 @@ } private void testAddReviewerViaPostReview(AddReviewerCaller addReviewer) throws Exception { - TestTimeUtil.resetWithClockStep(1, SECONDS); PushOneCommit.Result r = createChange(); ChangeResource rsrc = parseResource(r); String oldETag = rsrc.getETag(); @@ -2028,8 +2018,8 @@ } @Test + @UseClockStep public void addReviewerThatIsNotPerfectMatch() throws Exception { - TestTimeUtil.resetWithClockStep(1, SECONDS); PushOneCommit.Result r = createChange(); ChangeResource rsrc = parseResource(r); String oldETag = rsrc.getETag(); @@ -2077,8 +2067,8 @@ } @Test + @UseClockStep public void addGroupAsReviewersWhenANotPerfectMatchedUserExists() throws Exception { - TestTimeUtil.resetWithClockStep(1, SECONDS); PushOneCommit.Result r = createChange(); ChangeResource rsrc = parseResource(r); String oldETag = rsrc.getETag(); @@ -2138,8 +2128,8 @@ } @Test + @UseClockStep public void addSelfAsReviewer() throws Exception { - TestTimeUtil.resetWithClockStep(1, SECONDS); PushOneCommit.Result r = createChange(); ChangeResource rsrc = parseResource(r); String oldETag = rsrc.getETag();
diff --git a/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java b/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java index 216a2ec..7156c8d 100644 --- a/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java +++ b/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java
@@ -16,6 +16,10 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.gerrit.testing.GerritJUnit.assertThrows; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -40,11 +44,10 @@ import com.google.inject.Inject; import com.google.inject.Module; import java.sql.Timestamp; -import org.easymock.Capture; -import org.easymock.EasyMock; -import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; /** Tests for comment validation in {@link PostReview}. */ public class PostReviewIT extends AbstractDaemonTest { @@ -53,14 +56,14 @@ private static final String COMMENT_TEXT = "The comment text"; - private Capture<ImmutableList<CommentForValidation>> capture = new Capture<>(); + @Captor private ArgumentCaptor<ImmutableList<CommentForValidation>> capture; @Override public Module createModule() { return new FactoryModule() { @Override public void configure() { - CommentValidator mockCommentValidator = EasyMock.createMock(CommentValidator.class); + CommentValidator mockCommentValidator = mock(CommentValidator.class); bind(CommentValidator.class) .annotatedWith(Exports.named(mockCommentValidator.getClass())) .toInstance(mockCommentValidator); @@ -71,23 +74,17 @@ @Before public void resetMock() { - EasyMock.reset(mockCommentValidator); - } - - @After - public void verifyMock() { - EasyMock.verify(mockCommentValidator); + initMocks(this); + clearInvocations(mockCommentValidator); } @Test public void validateCommentsInInput_commentOK() throws Exception { - EasyMock.expect( - mockCommentValidator.validateComments( - ImmutableList.of( - CommentForValidation.create( - CommentForValidation.CommentType.FILE_COMMENT, COMMENT_TEXT)))) - .andReturn(ImmutableList.of()); - EasyMock.replay(mockCommentValidator); + when(mockCommentValidator.validateComments( + ImmutableList.of( + CommentForValidation.create( + CommentForValidation.CommentType.FILE_COMMENT, COMMENT_TEXT)))) + .thenReturn(ImmutableList.of()); PushOneCommit.Result r = createChange(); @@ -106,12 +103,9 @@ public void validateCommentsInInput_commentRejected() throws Exception { CommentForValidation commentForValidation = CommentForValidation.create(CommentType.FILE_COMMENT, COMMENT_TEXT); - EasyMock.expect( - mockCommentValidator.validateComments( - ImmutableList.of( - CommentForValidation.create(CommentType.FILE_COMMENT, COMMENT_TEXT)))) - .andReturn(ImmutableList.of(commentForValidation.failValidation("Oh no!"))); - EasyMock.replay(mockCommentValidator); + when(mockCommentValidator.validateComments( + ImmutableList.of(CommentForValidation.create(CommentType.FILE_COMMENT, COMMENT_TEXT)))) + .thenReturn(ImmutableList.of(commentForValidation.failValidation("Oh no!"))); PushOneCommit.Result r = createChange(); @@ -139,8 +133,6 @@ @Test public void validateCommentsInInput_commentCleanedUp() throws Exception { - EasyMock.replay(mockCommentValidator); - PushOneCommit.Result r = createChange(); assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).isEmpty(); @@ -159,13 +151,11 @@ @Test public void validateDrafts_draftOK() throws Exception { - EasyMock.expect( - mockCommentValidator.validateComments( - ImmutableList.of( - CommentForValidation.create( - CommentForValidation.CommentType.INLINE_COMMENT, COMMENT_TEXT)))) - .andReturn(ImmutableList.of()); - EasyMock.replay(mockCommentValidator); + when(mockCommentValidator.validateComments( + ImmutableList.of( + CommentForValidation.create( + CommentForValidation.CommentType.INLINE_COMMENT, COMMENT_TEXT)))) + .thenReturn(ImmutableList.of()); PushOneCommit.Result r = createChange(); @@ -186,13 +176,11 @@ public void validateDrafts_draftRejected() throws Exception { CommentForValidation commentForValidation = CommentForValidation.create(CommentType.INLINE_COMMENT, COMMENT_TEXT); - EasyMock.expect( - mockCommentValidator.validateComments( - ImmutableList.of( - CommentForValidation.create( - CommentForValidation.CommentType.INLINE_COMMENT, COMMENT_TEXT)))) - .andReturn(ImmutableList.of(commentForValidation.failValidation("Oh no!"))); - EasyMock.replay(mockCommentValidator); + when(mockCommentValidator.validateComments( + ImmutableList.of( + CommentForValidation.create( + CommentForValidation.CommentType.INLINE_COMMENT, COMMENT_TEXT)))) + .thenReturn(ImmutableList.of(commentForValidation.failValidation("Oh no!"))); PushOneCommit.Result r = createChange(); DraftInput draft = @@ -230,16 +218,14 @@ testCommentHelper.addDraft(r.getChangeId(), r.getCommit().getName(), draftFile); assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).isEmpty(); - EasyMock.expect(mockCommentValidator.validateComments(EasyMock.capture(capture))) - .andReturn(ImmutableList.of()); - EasyMock.replay(mockCommentValidator); + when(mockCommentValidator.validateComments(capture.capture())).thenReturn(ImmutableList.of()); ReviewInput input = new ReviewInput(); input.drafts = DraftHandling.PUBLISH; gApi.changes().id(r.getChangeId()).current().review(input); assertThat(testCommentHelper.getPublishedComments(r.getChangeId())).hasSize(2); - assertThat(capture.getValues()).hasSize(1); + assertThat(capture.getAllValues()).hasSize(1); assertThat(capture.getValue()) .containsExactly( CommentForValidation.create( @@ -250,12 +236,10 @@ @Test public void validateCommentsInChangeMessage_messageOK() throws Exception { - EasyMock.expect( - mockCommentValidator.validateComments( - ImmutableList.of( - CommentForValidation.create(CommentType.CHANGE_MESSAGE, COMMENT_TEXT)))) - .andReturn(ImmutableList.of()); - EasyMock.replay(mockCommentValidator); + when(mockCommentValidator.validateComments( + ImmutableList.of( + CommentForValidation.create(CommentType.CHANGE_MESSAGE, COMMENT_TEXT)))) + .thenReturn(ImmutableList.of()); PushOneCommit.Result r = createChange(); ReviewInput input = new ReviewInput().message(COMMENT_TEXT); @@ -271,12 +255,10 @@ public void validateCommentsInChangeMessage_messageRejected() throws Exception { CommentForValidation commentForValidation = CommentForValidation.create(CommentType.CHANGE_MESSAGE, COMMENT_TEXT); - EasyMock.expect( - mockCommentValidator.validateComments( - ImmutableList.of( - CommentForValidation.create(CommentType.CHANGE_MESSAGE, COMMENT_TEXT)))) - .andReturn(ImmutableList.of(commentForValidation.failValidation("Oh no!"))); - EasyMock.replay(mockCommentValidator); + when(mockCommentValidator.validateComments( + ImmutableList.of( + CommentForValidation.create(CommentType.CHANGE_MESSAGE, COMMENT_TEXT)))) + .thenReturn(ImmutableList.of(commentForValidation.failValidation("Oh no!"))); PushOneCommit.Result r = createChange(); ReviewInput input = new ReviewInput().message(COMMENT_TEXT);
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java index d908ee5f..599a2b9 100644 --- a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java +++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
@@ -45,6 +45,7 @@ import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.acceptance.Sandboxed; import com.google.gerrit.acceptance.TestAccount; +import com.google.gerrit.acceptance.UseClockStep; import com.google.gerrit.acceptance.testsuite.account.AccountOperations; import com.google.gerrit.acceptance.testsuite.group.GroupOperations; import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; @@ -93,7 +94,6 @@ import com.google.gerrit.server.util.MagicBranch; import com.google.gerrit.server.util.time.TimeUtil; import com.google.gerrit.testing.GerritJUnit.ThrowingRunnable; -import com.google.gerrit.testing.TestTimeUtil; import com.google.inject.Inject; import java.io.IOException; import java.lang.annotation.Retention; @@ -104,7 +104,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.concurrent.TimeUnit; import java.util.stream.Stream; import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; import org.eclipse.jgit.junit.TestRepository; @@ -121,10 +120,10 @@ import org.eclipse.jgit.transport.PushResult; import org.eclipse.jgit.transport.RemoteRefUpdate; import org.junit.After; -import org.junit.Before; import org.junit.Test; @NoHttpd +@UseClockStep public class GroupsIT extends AbstractDaemonTest { @Inject @ServerInitiated private GroupsUpdate groupsUpdate; @Inject private AccountOperations accountOperations; @@ -140,16 +139,6 @@ @Inject private Sequences seq; @Inject private StalenessChecker stalenessChecker; - @Before - public void setTimeForTesting() { - TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS); - } - - @After - public void resetTime() { - TestTimeUtil.useSystemTime(); - } - @After public void consistencyCheck() throws Exception { if (description.getAnnotation(IgnoreGroupInconsistencies.class) == null) {
diff --git a/javatests/com/google/gerrit/acceptance/api/project/CommitIT.java b/javatests/com/google/gerrit/acceptance/api/project/CommitIT.java index 71ca289..f7902c4 100644 --- a/javatests/com/google/gerrit/acceptance/api/project/CommitIT.java +++ b/javatests/com/google/gerrit/acceptance/api/project/CommitIT.java
@@ -96,10 +96,30 @@ } @Test + public void cherryPickWithoutMessage() throws Exception { + String branch = "foo"; + + // Create change to cherry-pick + RevCommit revCommit = createChange().getCommit(); + + // Create target branch to cherry-pick to. + gApi.projects().name(project.get()).branch(branch).create(new BranchInput()); + + // Cherry-pick without message. + CherryPickInput input = new CherryPickInput(); + input.destination = branch; + String changeId = + gApi.projects().name(project.get()).commit(revCommit.name()).cherryPick(input).get().id; + + // Expect that the message of the cherry-picked commit was used for the cherry-pick change. + ChangeInfo changeInfo = gApi.changes().id(changeId).get(); + RevisionInfo revInfo = changeInfo.revisions.get(changeInfo.currentRevision); + assertThat(revInfo).isNotNull(); + assertThat(revInfo.commit.message).isEqualTo(revCommit.getFullMessage()); + } + + @Test public void cherryPickCommitWithoutChangeId() throws Exception { - // This test is a little superfluous, since the current cherry-pick code ignores - // the commit message of the to-be-cherry-picked change, using the one in - // CherryPickInput instead. CherryPickInput input = new CherryPickInput(); input.destination = "foo"; input.message = "it goes to foo branch";
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java index 22b8051..2087f939 100644 --- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java +++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -341,6 +341,34 @@ } @Test + public void cherryPickWithoutMessage() throws Exception { + String branch = "foo"; + + // Create change to cherry-pick + PushOneCommit.Result change = createChange(); + RevCommit revCommit = change.getCommit(); + + // Create target branch to cherry-pick to. + gApi.projects().name(project.get()).branch(branch).create(new BranchInput()); + + // Cherry-pick without message. + CherryPickInput input = new CherryPickInput(); + input.destination = branch; + String changeId = + gApi.changes() + .id(change.getChangeId()) + .revision(revCommit.name()) + .cherryPickAsInfo(input) + .id; + + // Expect that the message of the cherry-picked commit was used for the cherry-pick change. + ChangeInfo changeInfo = gApi.changes().id(changeId).get(); + RevisionInfo revInfo = changeInfo.revisions.get(changeInfo.currentRevision); + assertThat(revInfo).isNotNull(); + assertThat(revInfo.commit.message).isEqualTo(revCommit.getFullMessage()); + } + + @Test public void cherryPickSetChangeId() throws Exception { PushOneCommit.Result r = pushTo("refs/for/master"); CherryPickInput in = new CherryPickInput();
diff --git a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java index df4b6d6..2e331c4 100644 --- a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java +++ b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
@@ -27,7 +27,6 @@ import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; import static com.google.gerrit.testing.GerritJUnit.assertThrows; import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.stream.Collectors.toList; import com.google.common.collect.ImmutableList; @@ -37,6 +36,7 @@ import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.acceptance.RestResponse; import com.google.gerrit.acceptance.TestProjectInput; +import com.google.gerrit.acceptance.UseClockStep; import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; import com.google.gerrit.common.RawInputUtil; @@ -65,7 +65,6 @@ import com.google.gerrit.server.restapi.change.ChangeEdits.EditMessage; import com.google.gerrit.server.restapi.change.ChangeEdits.Post; import com.google.gerrit.server.restapi.change.ChangeEdits.Put; -import com.google.gerrit.testing.TestTimeUtil; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.inject.Inject; @@ -81,11 +80,10 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; -import org.junit.AfterClass; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; +@UseClockStep public class ChangeEditIT extends AbstractDaemonTest { private static final String FILE_NAME = "foo"; @@ -103,16 +101,6 @@ private String changeId2; private PatchSet ps; - @BeforeClass - public static void setTimeForTesting() { - TestTimeUtil.resetWithClockStep(1, SECONDS); - } - - @AfterClass - public static void restoreTime() { - TestTimeUtil.useSystemTime(); - } - @Before public void setUp() throws Exception { changeId = newChange(admin.newIdent());
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java index 27cc241..564a29b 100644 --- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java +++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -41,7 +41,6 @@ import static com.google.gerrit.server.project.testing.TestLabels.label; import static com.google.gerrit.server.project.testing.TestLabels.value; import static java.util.Comparator.comparing; -import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; @@ -58,6 +57,7 @@ import com.google.gerrit.acceptance.SkipProjectClone; import com.google.gerrit.acceptance.TestAccount; import com.google.gerrit.acceptance.TestProjectInput; +import com.google.gerrit.acceptance.UseClockStep; import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; import com.google.gerrit.common.data.GlobalCapability; @@ -105,7 +105,6 @@ import com.google.gerrit.server.project.testing.TestLabels; import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.testing.FakeEmailSender.Message; -import com.google.gerrit.testing.TestTimeUtil; import com.google.inject.Inject; import java.util.ArrayList; import java.util.Collection; @@ -134,12 +133,11 @@ import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.RemoteRefUpdate; import org.junit.After; -import org.junit.AfterClass; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; @SkipProjectClone +@UseClockStep public abstract class AbstractPushForReview extends AbstractDaemonTest { protected enum Protocol { // Only test protocols which are actually served by the Gerrit server, since each separate test @@ -160,16 +158,6 @@ @Inject private DynamicSet<CommitValidationListener> commitValidators; - @BeforeClass - public static void setTimeForTesting() { - TestTimeUtil.resetWithClockStep(1, SECONDS); - } - - @AfterClass - public static void restoreTime() { - TestTimeUtil.useSystemTime(); - } - @Before public void setUpPatchSetLock() throws Exception { try (ProjectConfigUpdate u = updateProject(project)) {
diff --git a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java index e7501e7..8a0238a 100644 --- a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java +++ b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java
@@ -16,16 +16,15 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.TruthJUnit.assume; -import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.collect.Iterables; import com.google.gerrit.acceptance.GerritConfig; import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.acceptance.PushOneCommit; +import com.google.gerrit.acceptance.UseClockStep; import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.testing.ConfigSuite; -import com.google.gerrit.testing.TestTimeUtil; import com.google.inject.Inject; import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; import org.eclipse.jgit.junit.TestRepository; @@ -480,127 +479,107 @@ } @Test + @UseClockStep public void superRepoCommitHasSameAuthorAsSubmoduleCommit() throws Exception { // Make sure that the commit is created at an earlier timestamp than the submit timestamp. - TestTimeUtil.resetWithClockStep(1, SECONDS); - try { - allowMatchingSubmoduleSubscription( - subKey, "refs/heads/master", superKey, "refs/heads/master"); - createSubmoduleSubscription(superRepo, "master", subKey, "master"); + allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master"); + createSubmoduleSubscription(superRepo, "master", subKey, "master"); - PushOneCommit.Result pushResult = - createChange(subRepo, "refs/heads/master", "Change", "a.txt", "some content", null); - approve(pushResult.getChangeId()); - gApi.changes().id(pushResult.getChangeId()).current().submit(); + PushOneCommit.Result pushResult = + createChange(subRepo, "refs/heads/master", "Change", "a.txt", "some content", null); + approve(pushResult.getChangeId()); + gApi.changes().id(pushResult.getChangeId()).current().submit(); - // Expect that the author name/email is preserved for the superRepo commit, but a new author - // timestamp is used. - PersonIdent authorIdent = getAuthor(superRepo, "master"); - assertThat(authorIdent.getName()).isEqualTo(admin.fullName()); - assertThat(authorIdent.getEmailAddress()).isEqualTo(admin.email()); - assertThat(authorIdent.getWhen()) - .isGreaterThan(pushResult.getCommit().getAuthorIdent().getWhen()); - } finally { - TestTimeUtil.useSystemTime(); - } + // Expect that the author name/email is preserved for the superRepo commit, but a new author + // timestamp is used. + PersonIdent authorIdent = getAuthor(superRepo, "master"); + assertThat(authorIdent.getName()).isEqualTo(admin.fullName()); + assertThat(authorIdent.getEmailAddress()).isEqualTo(admin.email()); + assertThat(authorIdent.getWhen()) + .isGreaterThan(pushResult.getCommit().getAuthorIdent().getWhen()); } @Test + @UseClockStep public void superRepoCommitHasSameAuthorAsSubmoduleCommits() throws Exception { assume().that(isSubmitWholeTopicEnabled()).isTrue(); - // Make sure that the commits are created at different timestamps and that the submit timestamp - // is afterwards. - TestTimeUtil.resetWithClockStep(1, SECONDS); - try { + Project.NameKey proj2 = createProjectForPush(getSubmitType()); - Project.NameKey proj2 = createProjectForPush(getSubmitType()); + TestRepository<?> subRepo2 = cloneProject(proj2); + allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master"); + allowMatchingSubmoduleSubscription(proj2, "refs/heads/master", superKey, "refs/heads/master"); - TestRepository<?> subRepo2 = cloneProject(proj2); - allowMatchingSubmoduleSubscription( - subKey, "refs/heads/master", superKey, "refs/heads/master"); - allowMatchingSubmoduleSubscription(proj2, "refs/heads/master", superKey, "refs/heads/master"); + Config config = new Config(); + prepareSubmoduleConfigEntry(config, subKey, subKey, "master"); + prepareSubmoduleConfigEntry(config, proj2, proj2, "master"); + pushSubmoduleConfig(superRepo, "master", config); - Config config = new Config(); - prepareSubmoduleConfigEntry(config, subKey, subKey, "master"); - prepareSubmoduleConfigEntry(config, proj2, proj2, "master"); - pushSubmoduleConfig(superRepo, "master", config); + String topic = "foo"; - String topic = "foo"; + PushOneCommit.Result pushResult1 = + createChange(subRepo, "refs/heads/master", "Change 1", "a.txt", "some content", topic); + approve(pushResult1.getChangeId()); - PushOneCommit.Result pushResult1 = - createChange(subRepo, "refs/heads/master", "Change 1", "a.txt", "some content", topic); - approve(pushResult1.getChangeId()); + PushOneCommit.Result pushResult2 = + createChange(subRepo2, "refs/heads/master", "Change 2", "b.txt", "other content", topic); + approve(pushResult2.getChangeId()); - PushOneCommit.Result pushResult2 = - createChange(subRepo2, "refs/heads/master", "Change 2", "b.txt", "other content", topic); - approve(pushResult2.getChangeId()); + // Submit the topic, 2 changes with the same author. + gApi.changes().id(pushResult1.getChangeId()).current().submit(); - // Submit the topic, 2 changes with the same author. - gApi.changes().id(pushResult1.getChangeId()).current().submit(); - - // Expect that the author name/email is preserved for the superRepo commit, but a new author - // timestamp is used. - PersonIdent authorIdent = getAuthor(superRepo, "master"); - assertThat(authorIdent.getName()).isEqualTo(admin.fullName()); - assertThat(authorIdent.getEmailAddress()).isEqualTo(admin.email()); - assertThat(authorIdent.getWhen()) - .isGreaterThan(pushResult1.getCommit().getAuthorIdent().getWhen()); - assertThat(authorIdent.getWhen()) - .isGreaterThan(pushResult2.getCommit().getAuthorIdent().getWhen()); - } finally { - TestTimeUtil.useSystemTime(); - } + // Expect that the author name/email is preserved for the superRepo commit, but a new author + // timestamp is used. + PersonIdent authorIdent = getAuthor(superRepo, "master"); + assertThat(authorIdent.getName()).isEqualTo(admin.fullName()); + assertThat(authorIdent.getEmailAddress()).isEqualTo(admin.email()); + assertThat(authorIdent.getWhen()) + .isGreaterThan(pushResult1.getCommit().getAuthorIdent().getWhen()); + assertThat(authorIdent.getWhen()) + .isGreaterThan(pushResult2.getCommit().getAuthorIdent().getWhen()); } @Test + @UseClockStep public void superRepoCommitHasGerritAsAuthorIfAuthorsOfSubmoduleCommitsDiffer() throws Exception { assume().that(isSubmitWholeTopicEnabled()).isTrue(); - // Make sure that the commits are created at different timestamps and that the submit timestamp - // is afterwards. - TestTimeUtil.resetWithClockStep(1, SECONDS); - try { - Project.NameKey proj2 = createProjectForPush(getSubmitType()); - TestRepository<InMemoryRepository> repo2 = cloneProject(proj2, user); + Project.NameKey proj2 = createProjectForPush(getSubmitType()); + TestRepository<InMemoryRepository> repo2 = cloneProject(proj2, user); - allowMatchingSubmoduleSubscription( - subKey, "refs/heads/master", superKey, "refs/heads/master"); - allowMatchingSubmoduleSubscription(proj2, "refs/heads/master", superKey, "refs/heads/master"); + allowMatchingSubmoduleSubscription(subKey, "refs/heads/master", superKey, "refs/heads/master"); + allowMatchingSubmoduleSubscription(proj2, "refs/heads/master", superKey, "refs/heads/master"); - Config config = new Config(); - prepareSubmoduleConfigEntry(config, subKey, subKey, "master"); - prepareSubmoduleConfigEntry(config, proj2, proj2, "master"); - pushSubmoduleConfig(superRepo, "master", config); + Config config = new Config(); + prepareSubmoduleConfigEntry(config, subKey, subKey, "master"); + prepareSubmoduleConfigEntry(config, proj2, proj2, "master"); + pushSubmoduleConfig(superRepo, "master", config); - String topic = "foo"; + String topic = "foo"; - // Create change as admin. - PushOneCommit.Result pushResult1 = - createChange(subRepo, "refs/heads/master", "Change 1", "a.txt", "some content", topic); - approve(pushResult1.getChangeId()); + // Create change as admin. + PushOneCommit.Result pushResult1 = + createChange(subRepo, "refs/heads/master", "Change 1", "a.txt", "some content", topic); + approve(pushResult1.getChangeId()); - // Create change as user. - PushOneCommit push = - pushFactory.create(user.newIdent(), repo2, "Change 2", "b.txt", "other content"); - PushOneCommit.Result pushResult2 = push.to("refs/for/master/" + name(topic)); - approve(pushResult2.getChangeId()); + // Create change as user. + PushOneCommit push = + pushFactory.create(user.newIdent(), repo2, "Change 2", "b.txt", "other content"); + PushOneCommit.Result pushResult2 = push.to("refs/for/master/" + name(topic)); + approve(pushResult2.getChangeId()); - // Submit the topic, 2 changes with the different author. - gApi.changes().id(pushResult1.getChangeId()).current().submit(); + // Submit the topic, 2 changes with the different author. + gApi.changes().id(pushResult1.getChangeId()).current().submit(); - // Expect that the Gerrit server identity is chosen as author for the superRepo commit and a - // new author timestamp is used. - PersonIdent authorIdent = getAuthor(superRepo, "master"); - assertThat(authorIdent.getName()).isEqualTo(serverIdent.get().getName()); - assertThat(authorIdent.getEmailAddress()).isEqualTo(serverIdent.get().getEmailAddress()); - assertThat(authorIdent.getWhen()) - .isGreaterThan(pushResult1.getCommit().getAuthorIdent().getWhen()); - assertThat(authorIdent.getWhen()) - .isGreaterThan(pushResult2.getCommit().getAuthorIdent().getWhen()); - } finally { - TestTimeUtil.useSystemTime(); - } + // Expect that the Gerrit server identity is chosen as author for the superRepo commit and a + // new author timestamp is used. + PersonIdent authorIdent = getAuthor(superRepo, "master"); + assertThat(authorIdent.getName()).isEqualTo(serverIdent.get().getName()); + assertThat(authorIdent.getEmailAddress()).isEqualTo(serverIdent.get().getEmailAddress()); + assertThat(authorIdent.getWhen()) + .isGreaterThan(pushResult1.getCommit().getAuthorIdent().getWhen()); + assertThat(authorIdent.getWhen()) + .isGreaterThan(pushResult2.getCommit().getAuthorIdent().getWhen()); } @Test
diff --git a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java index af4a22e..0e51208 100644 --- a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java +++ b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
@@ -36,6 +36,7 @@ import com.google.gerrit.extensions.registration.RegistrationHandle; import com.google.gerrit.httpd.restapi.ParameterParser; import com.google.gerrit.httpd.restapi.RestApiServlet; +import com.google.gerrit.server.ExceptionHook; import com.google.gerrit.server.events.CommitReceivedEvent; import com.google.gerrit.server.git.WorkQueue; import com.google.gerrit.server.git.validators.CommitValidationException; @@ -85,6 +86,7 @@ @Inject private DynamicSet<ChangeIndexedListener> changeIndexedListeners; @Inject private DynamicSet<PerformanceLogger> performanceLoggers; @Inject private DynamicSet<SubmitRule> submitRules; + @Inject private DynamicSet<ExceptionHook> exceptionHooks; @Inject private WorkQueue workQueue; private TraceValidatingProjectCreationValidationListener projectCreationListener; @@ -585,17 +587,18 @@ assertThat(projectCreationListener.tags.get("project")).containsExactly("new24"); } + @Test @GerritConfig(name = "retry.retryWithTraceOnFailure", value = "true") public void autoRetryWithTrace() throws Exception { String changeId = createChange().getChangeId(); approve(changeId); TraceSubmitRule traceSubmitRule = new TraceSubmitRule(); - traceSubmitRule.failOnce = true; + traceSubmitRule.failAlways = true; RegistrationHandle submitRuleRegistrationHandle = submitRules.add("gerrit", traceSubmitRule); try { RestResponse response = adminRestSession.post("/changes/" + changeId + "/submit"); - assertThat(response.getStatusCode()).isEqualTo(SC_OK); + assertThat(response.getStatusCode()).isEqualTo(SC_INTERNAL_SERVER_ERROR); assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).startsWith("retry-on-failure-"); assertThat(traceSubmitRule.traceId).startsWith("retry-on-failure-"); assertThat(traceSubmitRule.isLoggingForced).isTrue(); @@ -605,6 +608,36 @@ } @Test + @GerritConfig(name = "retry.retryWithTraceOnFailure", value = "true") + public void noAutoRetryIfExceptionCausesNormalRetrying() throws Exception { + String changeId = createChange().getChangeId(); + approve(changeId); + + TraceSubmitRule traceSubmitRule = new TraceSubmitRule(); + traceSubmitRule.failAlways = true; + RegistrationHandle submitRuleRegistrationHandle = submitRules.add("gerrit", traceSubmitRule); + RegistrationHandle exceptionHookRegistrationHandle = + exceptionHooks.add( + "gerrit", + new ExceptionHook() { + @Override + public boolean shouldRetry(Throwable t) { + return true; + } + }); + try { + RestResponse response = adminRestSession.post("/changes/" + changeId + "/submit"); + assertThat(response.getStatusCode()).isEqualTo(SC_INTERNAL_SERVER_ERROR); + assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull(); + assertThat(traceSubmitRule.traceId).isNull(); + assertThat(traceSubmitRule.isLoggingForced).isFalse(); + } finally { + submitRuleRegistrationHandle.remove(); + exceptionHookRegistrationHandle.remove(); + } + } + + @Test public void noAutoRetryWithTraceIfDisabled() throws Exception { String changeId = createChange().getChangeId(); approve(changeId); @@ -617,7 +650,7 @@ assertThat(response.getStatusCode()).isEqualTo(SC_INTERNAL_SERVER_ERROR); assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull(); assertThat(traceSubmitRule.traceId).isNull(); - assertThat(traceSubmitRule.isLoggingForced).isNull(); + assertThat(traceSubmitRule.isLoggingForced).isFalse(); } finally { submitRuleRegistrationHandle.remove(); } @@ -677,18 +710,19 @@ String traceId; Boolean isLoggingForced; boolean failOnce; + boolean failAlways; @Override public Optional<SubmitRecord> evaluate(ChangeData changeData) { - if (failOnce) { - failOnce = false; - throw new IllegalStateException("forced failure from test"); - } - this.traceId = Iterables.getFirst(LoggingContext.getInstance().getTagsAsMap().get("TRACE_ID"), null); this.isLoggingForced = LoggingContext.getInstance().shouldForceLogging(null, null, false); + if (failOnce || failAlways) { + failOnce = false; + throw new IllegalStateException("forced failure from test"); + } + SubmitRecord submitRecord = new SubmitRecord(); submitRecord.status = SubmitRecord.Status.OK; return Optional.of(submitRecord);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java index a756c97..c0083d2 100644 --- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java +++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -29,7 +29,6 @@ import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; import static com.google.gerrit.testing.GerritJUnit.assertThrows; import static java.util.Objects.requireNonNull; -import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.stream.Collectors.toList; import static org.eclipse.jgit.lib.Constants.EMPTY_TREE_ID; import static org.mockito.Mockito.atLeast; @@ -49,6 +48,8 @@ import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.acceptance.TestAccount; import com.google.gerrit.acceptance.TestProjectInput; +import com.google.gerrit.acceptance.UseClockStep; +import com.google.gerrit.acceptance.UseTimezone; import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; import com.google.gerrit.common.Nullable; @@ -95,7 +96,6 @@ import com.google.gerrit.server.validators.ValidationException; import com.google.gerrit.testing.ConfigSuite; import com.google.gerrit.testing.GerritJUnit.ThrowingRunnable; -import com.google.gerrit.testing.TestTimeUtil; import com.google.inject.Inject; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -119,10 +119,11 @@ import org.eclipse.jgit.transport.ReceiveCommand; import org.eclipse.jgit.transport.RefSpec; import org.junit.After; -import org.junit.Before; import org.junit.Test; @NoHttpd +@UseClockStep +@UseTimezone(timezone = "US/Eastern") public abstract class AbstractSubmit extends AbstractDaemonTest { @ConfigSuite.Config public static Config submitWholeTopicEnabled() { @@ -138,19 +139,6 @@ @Inject private Submit submitHandler; private RegistrationHandle onSubmitValidatorHandle; - private String systemTimeZone; - - @Before - public void setTimeForTesting() { - systemTimeZone = System.setProperty("user.timezone", "US/Eastern"); - TestTimeUtil.resetWithClockStep(1, SECONDS); - } - - @After - public void resetTime() { - TestTimeUtil.useSystemTime(); - System.setProperty("user.timezone", systemTimeZone); - } @After public void removeOnSubmitValidator() {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java index 39eb5cd..094bcd5 100644 --- a/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java +++ b/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
@@ -19,12 +19,12 @@ import static com.google.gerrit.extensions.client.ListChangesOption.DETAILED_LABELS; import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; import static com.google.gerrit.testing.GerritJUnit.assertThrows; -import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.collect.Iterables; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.acceptance.PushOneCommit; +import com.google.gerrit.acceptance.UseClockStep; import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; import com.google.gerrit.common.data.Permission; @@ -35,30 +35,18 @@ import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.server.account.AccountResolver.UnresolvableAccountException; import com.google.gerrit.testing.FakeEmailSender.Message; -import com.google.gerrit.testing.TestTimeUtil; import com.google.inject.Inject; import java.util.Iterator; import java.util.List; import org.eclipse.jgit.transport.RefSpec; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.junit.Test; @NoHttpd +@UseClockStep public class AssigneeIT extends AbstractDaemonTest { @Inject private ProjectOperations projectOperations; @Inject private RequestScopeOperations requestScopeOperations; - @BeforeClass - public static void setTimeForTesting() { - TestTimeUtil.resetWithClockStep(1, SECONDS); - } - - @AfterClass - public static void restoreTime() { - TestTimeUtil.useSystemTime(); - } - @Test public void getNoAssignee() throws Exception { PushOneCommit.Result r = createChange();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java index a55f17e..221e854 100644 --- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java +++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
@@ -23,7 +23,6 @@ import static com.google.gerrit.server.notedb.ChangeNoteUtil.parseCommitMessageRange; import static com.google.gerrit.server.restapi.change.DeleteChangeMessage.createNewChangeMessage; import static com.google.gerrit.testing.GerritJUnit.assertThrows; -import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.stream.Collectors.toSet; import static org.eclipse.jgit.util.RawParseUtils.decode; @@ -32,6 +31,8 @@ import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.acceptance.TestAccount; +import com.google.gerrit.acceptance.UseClockStep; +import com.google.gerrit.acceptance.UseTimezone; import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; import com.google.gerrit.common.data.GlobalCapability; @@ -45,7 +46,6 @@ import com.google.gerrit.reviewdb.client.Change; import com.google.gerrit.server.notedb.ChangeNoteUtil; import com.google.gerrit.testing.ConfigSuite; -import com.google.gerrit.testing.TestTimeUtil; import com.google.inject.Inject; import java.nio.charset.Charset; import java.util.ArrayList; @@ -54,30 +54,16 @@ import java.util.Optional; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.util.RawParseUtils; -import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +@UseClockStep +@UseTimezone(timezone = "US/Eastern") @RunWith(ConfigSuite.class) public class ChangeMessagesIT extends AbstractDaemonTest { @Inject private ProjectOperations projectOperations; @Inject private RequestScopeOperations requestScopeOperations; - private String systemTimeZone; - - @Before - public void setTimeForTesting() { - systemTimeZone = System.setProperty("user.timezone", "US/Eastern"); - TestTimeUtil.resetWithClockStep(1, SECONDS); - } - - @After - public void resetTime() { - TestTimeUtil.useSystemTime(); - System.setProperty("user.timezone", systemTimeZone); - } - @Test public void messagesNotReturnedByDefault() throws Exception { String changeId = createChange().getChangeId();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java index 54a50ce..a3285be 100644 --- a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java +++ b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
@@ -20,7 +20,6 @@ import static com.google.gerrit.reviewdb.client.RefNames.changeMetaRef; import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; import static com.google.gerrit.testing.GerritJUnit.assertThrows; -import static java.util.concurrent.TimeUnit.SECONDS; import static org.eclipse.jgit.lib.Constants.SIGNED_OFF_BY_TAG; import com.google.common.base.Strings; @@ -29,6 +28,8 @@ import com.google.gerrit.acceptance.PushOneCommit; import com.google.gerrit.acceptance.PushOneCommit.Result; import com.google.gerrit.acceptance.RestResponse; +import com.google.gerrit.acceptance.UseClockStep; +import com.google.gerrit.acceptance.UseSystemTime; import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; import com.google.gerrit.extensions.api.changes.ChangeApi; @@ -51,34 +52,28 @@ import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.server.submit.ChangeAlreadyMergedException; import com.google.gerrit.testing.FakeEmailSender.Message; -import com.google.gerrit.testing.TestTimeUtil; import com.google.inject.Inject; import java.util.List; import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.transport.RefSpec; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.junit.Test; +@UseClockStep public class CreateChangeIT extends AbstractDaemonTest { @Inject private ProjectOperations projectOperations; @Inject private RequestScopeOperations requestScopeOperations; - @BeforeClass - public static void setTimeForTesting() { - TestTimeUtil.resetWithClockStep(1, SECONDS); - } - - @AfterClass - public static void restoreTime() { - TestTimeUtil.useSystemTime(); - } - @Test public void createEmptyChange_MissingBranch() throws Exception { ChangeInput ci = new ChangeInput(); @@ -160,6 +155,31 @@ } @Test + public void cannotCreateChangeWithChangeIfOfExistingChangeOnSameBranch() throws Exception { + String changeId = createChange().getChangeId(); + + ChangeInput ci = newChangeInput(ChangeStatus.NEW); + ci.subject = "Subject\n\nChange-Id: " + changeId; + assertCreateFails( + ci, + ResourceConflictException.class, + "A change with Change-Id " + changeId + " already exists for this branch."); + } + + @Test + public void canCreateChangeWithChangeIfOfExistingChangeOnOtherBranch() throws Exception { + String changeId = createChange().getChangeId(); + + createBranch(BranchNameKey.create(project, "other")); + + ChangeInput ci = newChangeInput(ChangeStatus.NEW); + ci.subject = "Subject\n\nChange-Id: " + changeId; + ci.branch = "other"; + ChangeInfo info = assertCreateSucceeds(ci); + assertThat(info.changeId).isEqualTo(changeId); + } + + @Test public void notificationsOnChangeCreation() throws Exception { requestScopeOperations.setApiUser(user.id()); watch(project.get()); @@ -445,6 +465,7 @@ } @Test + @UseSystemTime public void sha1sOfTwoNewChangesDiffer() throws Exception { ChangeInput changeInput = newChangeInput(ChangeStatus.NEW); ChangeInfo info1 = assertCreateSucceeds(changeInput); @@ -452,6 +473,33 @@ assertThat(info1.currentRevision).isNotEqualTo(info2.currentRevision); } + @Test + @UseSystemTime + public void sha1sOfTwoNewChangesDifferIfCreatedConcurrently() throws Exception { + ExecutorService executor = Executors.newFixedThreadPool(2); + try { + for (int i = 0; i < 10; i++) { + ChangeInput changeInput = newChangeInput(ChangeStatus.NEW); + + CyclicBarrier sync = new CyclicBarrier(2); + Callable<ChangeInfo> createChange = + () -> { + requestScopeOperations.setApiUser(admin.id()); + sync.await(); + return assertCreateSucceeds(changeInput); + }; + + Future<ChangeInfo> changeInfo1 = executor.submit(createChange); + Future<ChangeInfo> changeInfo2 = executor.submit(createChange); + assertThat(changeInfo1.get().currentRevision) + .isNotEqualTo(changeInfo2.get().currentRevision); + } + } finally { + executor.shutdown(); + executor.awaitTermination(5, TimeUnit.SECONDS); + } + } + private ChangeInput newChangeInput(ChangeStatus status) { ChangeInput in = new ChangeInput(); in.project = project.get();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java index 542c6a9..c57a035 100644 --- a/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java +++ b/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java
@@ -20,7 +20,6 @@ import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; import static com.google.gerrit.testing.GerritJUnit.assertThrows; import static java.util.Objects.requireNonNull; -import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; @@ -28,6 +27,7 @@ import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.acceptance.PushOneCommit; +import com.google.gerrit.acceptance.UseClockStep; import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; import com.google.gerrit.common.data.Permission; @@ -35,26 +35,14 @@ import com.google.gerrit.extensions.common.ChangeMessageInfo; import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.BadRequestException; -import com.google.gerrit.testing.TestTimeUtil; import com.google.inject.Inject; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.junit.Test; @NoHttpd +@UseClockStep public class HashtagsIT extends AbstractDaemonTest { @Inject private ProjectOperations projectOperations; - @BeforeClass - public static void setTimeForTesting() { - TestTimeUtil.resetWithClockStep(1, SECONDS); - } - - @AfterClass - public static void restoreTime() { - TestTimeUtil.useSystemTime(); - } - @Inject private RequestScopeOperations requestScopeOperations; @Test
diff --git a/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java b/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java index 9882c77..58ec5eb 100644 --- a/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java +++ b/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
@@ -20,7 +20,6 @@ import static com.google.gerrit.acceptance.GitUtil.pushHead; import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability; import static com.google.gerrit.extensions.common.testing.EditInfoSubject.assertThat; -import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; @@ -29,6 +28,8 @@ import com.google.gerrit.acceptance.GerritConfig; import com.google.gerrit.acceptance.NoHttpd; import com.google.gerrit.acceptance.PushOneCommit; +import com.google.gerrit.acceptance.UseClockStep; +import com.google.gerrit.acceptance.UseTimezone; import com.google.gerrit.acceptance.testsuite.account.AccountOperations; import com.google.gerrit.acceptance.testsuite.group.GroupOperations; import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; @@ -52,7 +53,6 @@ import com.google.gerrit.server.update.ChangeContext; import com.google.gerrit.server.util.time.TimeUtil; import com.google.gerrit.testing.ConfigSuite; -import com.google.gerrit.testing.TestTimeUtil; import com.google.inject.Inject; import java.util.ArrayList; import java.util.Arrays; @@ -64,11 +64,11 @@ import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.revwalk.RevCommit; -import org.junit.After; -import org.junit.Before; import org.junit.Test; @NoHttpd +@UseClockStep +@UseTimezone(timezone = "US/Eastern") public class GetRelatedIT extends AbstractDaemonTest { private static final int MAX_TERMS = 10; @@ -84,20 +84,6 @@ @Inject private ProjectOperations projectOperations; @Inject private RequestScopeOperations requestScopeOperations; - private String systemTimeZone; - - @Before - public void setTimeForTesting() { - systemTimeZone = System.setProperty("user.timezone", "US/Eastern"); - TestTimeUtil.resetWithClockStep(1, SECONDS); - } - - @After - public void resetTime() { - TestTimeUtil.useSystemTime(); - System.setProperty("user.timezone", systemTimeZone); - } - @Inject private IndexConfig indexConfig; @Inject private ChangesCollection changes;
diff --git a/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsCommentValidationIT.java b/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsCommentValidationIT.java index cb5add3..6677583 100644 --- a/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsCommentValidationIT.java +++ b/javatests/com/google/gerrit/acceptance/server/git/receive/ReceiveCommitsCommentValidationIT.java
@@ -15,6 +15,10 @@ package com.google.gerrit.acceptance.server.git.receive; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; import com.google.common.collect.ImmutableList; import com.google.gerrit.acceptance.AbstractDaemonTest; @@ -30,11 +34,10 @@ import com.google.gerrit.testing.TestCommentHelper; import com.google.inject.Inject; import com.google.inject.Module; -import org.easymock.Capture; -import org.easymock.EasyMock; -import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; /** * Tests for comment validation when publishing drafts via the {@code --publish-comments} option. @@ -45,14 +48,14 @@ private static final String COMMENT_TEXT = "The comment text"; - private Capture<ImmutableList<CommentForValidation>> capture = new Capture<>(); + @Captor private ArgumentCaptor<ImmutableList<CommentForValidation>> capture; @Override public Module createModule() { return new FactoryModule() { @Override public void configure() { - CommentValidator mockCommentValidator = EasyMock.createMock(CommentValidator.class); + CommentValidator mockCommentValidator = mock(CommentValidator.class); bind(CommentValidator.class) .annotatedWith(Exports.named(mockCommentValidator.getClass())) .toInstance(mockCommentValidator); @@ -63,23 +66,17 @@ @Before public void resetMock() { - EasyMock.reset(mockCommentValidator); - } - - @After - public void verifyMock() { - EasyMock.verify(mockCommentValidator); + initMocks(this); + clearInvocations(mockCommentValidator); } @Test public void validateComments_commentOK() throws Exception { - EasyMock.expect( - mockCommentValidator.validateComments( - ImmutableList.of( - CommentForValidation.create( - CommentForValidation.CommentType.FILE_COMMENT, COMMENT_TEXT)))) - .andReturn(ImmutableList.of()); - EasyMock.replay(mockCommentValidator); + when(mockCommentValidator.validateComments( + ImmutableList.of( + CommentForValidation.create( + CommentForValidation.CommentType.FILE_COMMENT, COMMENT_TEXT)))) + .thenReturn(ImmutableList.of()); PushOneCommit.Result result = createChange(); String changeId = result.getChangeId(); String revId = result.getCommit().getName(); @@ -96,13 +93,11 @@ public void validateComments_commentRejected() throws Exception { CommentForValidation commentForValidation = CommentForValidation.create(CommentType.FILE_COMMENT, COMMENT_TEXT); - EasyMock.expect( - mockCommentValidator.validateComments( - ImmutableList.of( - CommentForValidation.create( - CommentForValidation.CommentType.FILE_COMMENT, COMMENT_TEXT)))) - .andReturn(ImmutableList.of(commentForValidation.failValidation("Oh no!"))); - EasyMock.replay(mockCommentValidator); + when(mockCommentValidator.validateComments( + ImmutableList.of( + CommentForValidation.create( + CommentForValidation.CommentType.FILE_COMMENT, COMMENT_TEXT)))) + .thenReturn(ImmutableList.of(commentForValidation.failValidation("Oh no!"))); PushOneCommit.Result result = createChange(); String changeId = result.getChangeId(); String revId = result.getCommit().getName(); @@ -117,9 +112,7 @@ @Test public void validateComments_inlineVsFileComments_allOK() throws Exception { - EasyMock.expect(mockCommentValidator.validateComments(EasyMock.capture(capture))) - .andReturn(ImmutableList.of()); - EasyMock.replay(mockCommentValidator); + when(mockCommentValidator.validateComments(capture.capture())).thenReturn(ImmutableList.of()); PushOneCommit.Result result = createChange(); String changeId = result.getChangeId(); String revId = result.getCommit().getName(); @@ -132,7 +125,7 @@ assertThat(testCommentHelper.getPublishedComments(result.getChangeId())).isEmpty(); amendChange(changeId, "refs/for/master%publish-comments", admin, testRepo); assertThat(testCommentHelper.getPublishedComments(result.getChangeId())).hasSize(2); - assertThat(capture.getValues()).hasSize(1); + assertThat(capture.getAllValues()).hasSize(1); assertThat(capture.getValue()) .containsExactly( CommentForValidation.create(
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java index 1386aec..0826c166 100644 --- a/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java +++ b/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
@@ -15,11 +15,12 @@ package com.google.gerrit.acceptance.server.mail; import static com.google.common.truth.Truth.assertThat; -import static java.util.concurrent.TimeUnit.SECONDS; import com.google.common.collect.Iterables; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.PushOneCommit; +import com.google.gerrit.acceptance.UseClockStep; +import com.google.gerrit.acceptance.UseTimezone; import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; import com.google.gerrit.extensions.api.changes.ReviewInput; import com.google.gerrit.extensions.common.ChangeMessageInfo; @@ -27,7 +28,6 @@ import com.google.gerrit.mail.MailProcessingUtil; import com.google.gerrit.server.query.change.ChangeData; import com.google.gerrit.testing.FakeEmailSender; -import com.google.gerrit.testing.TestTimeUtil; import com.google.inject.Inject; import java.sql.Timestamp; import java.time.ZoneId; @@ -37,28 +37,14 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.junit.After; -import org.junit.Before; import org.junit.Test; /** Tests the presence of required metadata in email headers, text and html. */ +@UseClockStep +@UseTimezone(timezone = "US/Eastern") public class MailMetadataIT extends AbstractDaemonTest { @Inject private RequestScopeOperations requestScopeOperations; - private String systemTimeZone; - - @Before - public void setTimeForTesting() { - systemTimeZone = System.setProperty("user.timezone", "US/Eastern"); - TestTimeUtil.resetWithClockStep(1, SECONDS); - } - - @After - public void resetTime() { - TestTimeUtil.useSystemTime(); - System.setProperty("user.timezone", systemTimeZone); - } - @Test public void metadataOnNewChange() throws Exception { PushOneCommit.Result newChange = createChange();
diff --git a/javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java b/javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java index 7d4b95e..8e0cd3d 100644 --- a/javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java +++ b/javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java
@@ -16,9 +16,9 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.gerrit.testing.GerritJUnit.assertThrows; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.resetToStrict; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.extensions.annotations.Exports; @@ -35,13 +35,12 @@ import com.google.inject.Inject; import com.google.inject.Module; import java.util.Collections; -import org.easymock.EasyMock; import org.junit.Before; import org.junit.Test; public class DefaultQuotaBackendIT extends AbstractDaemonTest { - private static final QuotaEnforcer quotaEnforcer = EasyMock.createStrictMock(QuotaEnforcer.class); + private static final QuotaEnforcer quotaEnforcer = mock(QuotaEnforcer.class); private IdentifiedUser identifiedAdmin; @Inject private QuotaBackend quotaBackend; @@ -61,14 +60,13 @@ @Before public void setUp() { identifiedAdmin = identifiedUserFactory.create(admin.id()); - resetToStrict(quotaEnforcer); + clearInvocations(quotaEnforcer); } @Test public void requestTokenForUser() { QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build(); - expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok()); - replay(quotaEnforcer); + when(quotaEnforcer.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.ok()); assertThat(quotaBackend.user(identifiedAdmin).requestToken("testGroup")) .isEqualTo(singletonAggregation(QuotaResponse.ok())); } @@ -77,8 +75,7 @@ public void requestTokenForUserAndAccount() { QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).account(user.id()).build(); - expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok()); - replay(quotaEnforcer); + when(quotaEnforcer.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.ok()); assertThat(quotaBackend.user(identifiedAdmin).account(user.id()).requestToken("testGroup")) .isEqualTo(singletonAggregation(QuotaResponse.ok())); } @@ -87,8 +84,7 @@ public void requestTokenForUserAndProject() { QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).project(project).build(); - expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok()); - replay(quotaEnforcer); + when(quotaEnforcer.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.ok()); assertThat(quotaBackend.user(identifiedAdmin).project(project).requestToken("testGroup")) .isEqualTo(singletonAggregation(QuotaResponse.ok())); } @@ -102,8 +98,7 @@ .change(changeId) .project(project) .build(); - expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok()); - replay(quotaEnforcer); + when(quotaEnforcer.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.ok()); assertThat( quotaBackend.user(identifiedAdmin).change(changeId, project).requestToken("testGroup")) .isEqualTo(singletonAggregation(QuotaResponse.ok())); @@ -112,8 +107,7 @@ @Test public void requestTokens() { QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build(); - expect(quotaEnforcer.requestTokens("testGroup", ctx, 123)).andReturn(QuotaResponse.ok()); - replay(quotaEnforcer); + when(quotaEnforcer.requestTokens("testGroup", ctx, 123)).thenReturn(QuotaResponse.ok()); assertThat(quotaBackend.user(identifiedAdmin).requestTokens("testGroup", 123)) .isEqualTo(singletonAggregation(QuotaResponse.ok())); } @@ -121,8 +115,7 @@ @Test public void dryRun() { QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build(); - expect(quotaEnforcer.dryRun("testGroup", ctx, 123)).andReturn(QuotaResponse.ok()); - replay(quotaEnforcer); + when(quotaEnforcer.dryRun("testGroup", ctx, 123)).thenReturn(QuotaResponse.ok()); assertThat(quotaBackend.user(identifiedAdmin).dryRun("testGroup", 123)) .isEqualTo(singletonAggregation(QuotaResponse.ok())); } @@ -132,8 +125,7 @@ QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).account(user.id()).build(); QuotaResponse r = QuotaResponse.ok(10L); - expect(quotaEnforcer.availableTokens("testGroup", ctx)).andReturn(r); - replay(quotaEnforcer); + when(quotaEnforcer.availableTokens("testGroup", ctx)).thenReturn(r); assertThat(quotaBackend.user(identifiedAdmin).account(user.id()).availableTokens("testGroup")) .isEqualTo(singletonAggregation(r)); } @@ -143,8 +135,7 @@ QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).project(project).build(); QuotaResponse r = QuotaResponse.ok(10L); - expect(quotaEnforcer.availableTokens("testGroup", ctx)).andReturn(r); - replay(quotaEnforcer); + when(quotaEnforcer.availableTokens("testGroup", ctx)).thenReturn(r); assertThat(quotaBackend.user(identifiedAdmin).project(project).availableTokens("testGroup")) .isEqualTo(singletonAggregation(r)); } @@ -159,8 +150,7 @@ .project(project) .build(); QuotaResponse r = QuotaResponse.ok(10L); - expect(quotaEnforcer.availableTokens("testGroup", ctx)).andReturn(r); - replay(quotaEnforcer); + when(quotaEnforcer.availableTokens("testGroup", ctx)).thenReturn(r); assertThat( quotaBackend .user(identifiedAdmin) @@ -173,8 +163,7 @@ public void availableTokens() { QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build(); QuotaResponse r = QuotaResponse.ok(10L); - expect(quotaEnforcer.availableTokens("testGroup", ctx)).andReturn(r); - replay(quotaEnforcer); + when(quotaEnforcer.availableTokens("testGroup", ctx)).thenReturn(r); assertThat(quotaBackend.user(identifiedAdmin).availableTokens("testGroup")) .isEqualTo(singletonAggregation(r)); } @@ -182,9 +171,8 @@ @Test public void requestTokenError() throws Exception { QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build(); - expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)) - .andReturn(QuotaResponse.error("failed")); - replay(quotaEnforcer); + when(quotaEnforcer.requestTokens("testGroup", ctx, 1)) + .thenReturn(QuotaResponse.error("failed")); QuotaResponse.Aggregated result = quotaBackend.user(identifiedAdmin).requestToken("testGroup"); assertThat(result).isEqualTo(singletonAggregation(QuotaResponse.error("failed"))); @@ -195,9 +183,7 @@ @Test public void availableTokensError() throws Exception { QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build(); - expect(quotaEnforcer.availableTokens("testGroup", ctx)) - .andReturn(QuotaResponse.error("failed")); - replay(quotaEnforcer); + when(quotaEnforcer.availableTokens("testGroup", ctx)).thenReturn(QuotaResponse.error("failed")); QuotaResponse.Aggregated result = quotaBackend.user(identifiedAdmin).availableTokens("testGroup"); assertThat(result).isEqualTo(singletonAggregation(QuotaResponse.error("failed"))); @@ -208,8 +194,7 @@ @Test public void requestTokenPluginThrowsAndRethrows() { QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build(); - expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)).andThrow(new NullPointerException()); - replay(quotaEnforcer); + when(quotaEnforcer.requestTokens("testGroup", ctx, 1)).thenThrow(new NullPointerException()); assertThrows( NullPointerException.class, @@ -219,8 +204,7 @@ @Test public void availableTokensPluginThrowsAndRethrows() { QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build(); - expect(quotaEnforcer.availableTokens("testGroup", ctx)).andThrow(new NullPointerException()); - replay(quotaEnforcer); + when(quotaEnforcer.availableTokens("testGroup", ctx)).thenThrow(new NullPointerException()); assertThrows( NullPointerException.class,
diff --git a/javatests/com/google/gerrit/acceptance/server/quota/RepositorySizeQuotaIT.java b/javatests/com/google/gerrit/acceptance/server/quota/RepositorySizeQuotaIT.java index 05b3b83..801288a 100644 --- a/javatests/com/google/gerrit/acceptance/server/quota/RepositorySizeQuotaIT.java +++ b/javatests/com/google/gerrit/acceptance/server/quota/RepositorySizeQuotaIT.java
@@ -15,15 +15,16 @@ package com.google.gerrit.acceptance.server.quota; import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assertWithMessage; import static com.google.gerrit.server.quota.QuotaGroupDefinitions.REPOSITORY_SIZE_GROUP; import static com.google.gerrit.server.quota.QuotaResponse.ok; -import static org.easymock.EasyMock.anyLong; -import static org.easymock.EasyMock.eq; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.resetToStrict; -import static org.easymock.EasyMock.verify; +import static com.google.gerrit.testing.GerritJUnit.assertThrows; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.acceptance.UseLocalDisk; @@ -33,7 +34,6 @@ import com.google.gerrit.server.quota.QuotaResponse; import com.google.inject.Module; import java.util.Collections; -import org.easymock.EasyMock; import org.eclipse.jgit.api.errors.TooLargeObjectInPackException; import org.eclipse.jgit.api.errors.TransportException; import org.junit.Before; @@ -42,9 +42,9 @@ @UseLocalDisk public class RepositorySizeQuotaIT extends AbstractDaemonTest { private static final QuotaBackend.WithResource quotaBackendWithResource = - EasyMock.createStrictMock(QuotaBackend.WithResource.class); + mock(QuotaBackend.WithResource.class); private static final QuotaBackend.WithUser quotaBackendWithUser = - EasyMock.createStrictMock(QuotaBackend.WithUser.class); + mock(QuotaBackend.WithUser.class); @Override public Module createModule() { @@ -70,64 +70,42 @@ @Before public void setUp() { - resetToStrict(quotaBackendWithResource); - resetToStrict(quotaBackendWithUser); + clearInvocations(quotaBackendWithResource); + clearInvocations(quotaBackendWithUser); } @Test public void pushWithAvailableTokens() throws Exception { - expect(quotaBackendWithResource.availableTokens(REPOSITORY_SIZE_GROUP)) - .andReturn(singletonAggregation(ok(276L))) - .times(2); - expect(quotaBackendWithResource.requestTokens(eq(REPOSITORY_SIZE_GROUP), anyLong())) - .andReturn(singletonAggregation(ok())); - expect(quotaBackendWithUser.project(project)).andReturn(quotaBackendWithResource).anyTimes(); - replay(quotaBackendWithResource); - replay(quotaBackendWithUser); + when(quotaBackendWithResource.availableTokens(REPOSITORY_SIZE_GROUP)) + .thenReturn(singletonAggregation(ok(276L))); + when(quotaBackendWithResource.requestTokens(eq(REPOSITORY_SIZE_GROUP), anyLong())) + .thenReturn(singletonAggregation(ok())); + when(quotaBackendWithUser.project(project)).thenReturn(quotaBackendWithResource); pushCommit(); - verify(quotaBackendWithUser); - verify(quotaBackendWithResource); + verify(quotaBackendWithResource, times(2)).availableTokens(REPOSITORY_SIZE_GROUP); } @Test public void pushWithNotSufficientTokens() throws Exception { long availableTokens = 1L; - expect(quotaBackendWithResource.availableTokens(REPOSITORY_SIZE_GROUP)) - .andReturn(singletonAggregation(ok(availableTokens))) - .anyTimes(); - expect(quotaBackendWithUser.project(project)).andReturn(quotaBackendWithResource).anyTimes(); - replay(quotaBackendWithResource); - replay(quotaBackendWithUser); - try { - pushCommit(); - assertWithMessage("expected TooLargeObjectInPackException").fail(); - } catch (TooLargeObjectInPackException e) { - String msg = e.getMessage(); - assertThat(msg).contains("Object too large"); - assertThat(msg) - .contains(String.format("Max object size limit is %d bytes.", availableTokens)); - } - verify(quotaBackendWithUser); - verify(quotaBackendWithResource); + when(quotaBackendWithResource.availableTokens(REPOSITORY_SIZE_GROUP)) + .thenReturn(singletonAggregation(ok(availableTokens))); + when(quotaBackendWithUser.project(project)).thenReturn(quotaBackendWithResource); + TooLargeObjectInPackException thrown = + assertThrows(TooLargeObjectInPackException.class, () -> pushCommit()); + assertThat(thrown).hasMessageThat().contains("Object too large"); + assertThat(thrown) + .hasMessageThat() + .contains(String.format("Max object size limit is %d bytes.", availableTokens)); } @Test public void errorGettingAvailableTokens() throws Exception { String msg = "quota error"; - expect(quotaBackendWithResource.availableTokens(REPOSITORY_SIZE_GROUP)) - .andReturn(singletonAggregation(QuotaResponse.error(msg))) - .anyTimes(); - expect(quotaBackendWithUser.project(project)).andReturn(quotaBackendWithResource).anyTimes(); - replay(quotaBackendWithResource); - replay(quotaBackendWithUser); - try { - pushCommit(); - assertWithMessage("expected TransportException").fail(); - } catch (TransportException e) { - // TransportException has not much info about the cause - } - verify(quotaBackendWithUser); - verify(quotaBackendWithResource); + when(quotaBackendWithResource.availableTokens(REPOSITORY_SIZE_GROUP)) + .thenReturn(singletonAggregation(QuotaResponse.error(msg))); + when(quotaBackendWithUser.project(project)).thenReturn(quotaBackendWithResource); + assertThrows(TransportException.class, () -> pushCommit()); } private void pushCommit() throws Exception {
diff --git a/javatests/com/google/gerrit/httpd/BUILD b/javatests/com/google/gerrit/httpd/BUILD index 6849d66..2254c4e 100644 --- a/javatests/com/google/gerrit/httpd/BUILD +++ b/javatests/com/google/gerrit/httpd/BUILD
@@ -19,11 +19,11 @@ "//lib:junit", "//lib:servlet-api-3_1-without-neverlink", "//lib:soy", - "//lib/easymock", "//lib/guice", "//lib/guice:guice-servlet", "//lib/jgit/org.eclipse.jgit:jgit", "//lib/jgit/org.eclipse.jgit.junit:junit", + "//lib/mockito", "//lib/truth", "//lib/truth:truth-java8-extension", ],
diff --git a/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java b/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java index 99835dd..77ab58b 100644 --- a/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java +++ b/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java
@@ -15,10 +15,8 @@ package com.google.gerrit.httpd.raw; import static com.google.common.truth.Truth.assertThat; -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableList; import com.google.gerrit.extensions.api.GerritApi; @@ -35,22 +33,22 @@ @Test public void renderTemplate() throws Exception { - Accounts accountsApi = createMock(Accounts.class); - expect(accountsApi.self()).andThrow(new AuthException("user needs to be authenticated")); + Accounts accountsApi = mock(Accounts.class); + when(accountsApi.self()).thenThrow(new AuthException("user needs to be authenticated")); - Server serverApi = createMock(Server.class); - expect(serverApi.getVersion()).andReturn("123"); - expect(serverApi.topMenus()).andReturn(ImmutableList.of()); + Server serverApi = mock(Server.class); + when(serverApi.getVersion()).thenReturn("123"); + when(serverApi.topMenus()).thenReturn(ImmutableList.of()); ServerInfo serverInfo = new ServerInfo(); serverInfo.defaultTheme = "my-default-theme"; - expect(serverApi.getInfo()).andReturn(serverInfo); + when(serverApi.getInfo()).thenReturn(serverInfo); - Config configApi = createMock(Config.class); - expect(configApi.server()).andReturn(serverApi); + Config configApi = mock(Config.class); + when(configApi.server()).thenReturn(serverApi); - GerritApi gerritApi = createMock(GerritApi.class); - expect(gerritApi.accounts()).andReturn(accountsApi); - expect(gerritApi.config()).andReturn(configApi); + GerritApi gerritApi = mock(GerritApi.class); + when(gerritApi.accounts()).thenReturn(accountsApi); + when(gerritApi.config()).thenReturn(configApi); String testCanonicalUrl = "foo-url"; String testCdnPath = "bar-cdn"; @@ -60,18 +58,8 @@ FakeHttpServletResponse response = new FakeHttpServletResponse(); - replay(gerritApi); - replay(configApi); - replay(serverApi); - replay(accountsApi); - servlet.doGet(new FakeHttpServletRequest(), response); - verify(gerritApi); - verify(configApi); - verify(serverApi); - verify(accountsApi); - String output = response.getActualBodyString(); assertThat(output).contains("<!DOCTYPE html>"); assertThat(output).contains("window.CANONICAL_PATH = '" + testCanonicalUrl);
diff --git a/javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java b/javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java index 8bac910..9f083b2 100644 --- a/javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java +++ b/javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java
@@ -17,18 +17,15 @@ import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS; import static com.google.gerrit.server.group.SystemGroupBackend.PROJECT_OWNERS; import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; -import static org.easymock.EasyMock.anyObject; -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.createNiceMock; -import static org.easymock.EasyMock.eq; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.getCurrentArguments; -import static org.easymock.EasyMock.not; -import static org.easymock.EasyMock.replay; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.AdditionalMatchers.not; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.reviewdb.client.AccountGroup; @@ -41,6 +38,8 @@ import org.eclipse.jgit.lib.Config; import org.junit.Before; import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; public class UniversalGroupBackendTest { private static final AccountGroup.UUID OTHER_UUID = AccountGroup.uuid("other"); @@ -52,8 +51,7 @@ @Before public void setup() { - user = createNiceMock(IdentifiedUser.class); - replay(user); + user = mock(IdentifiedUser.class); backends = new DynamicSet<>(); backends.add("gerrit", new SystemGroupBackend(new Config())); backend = @@ -103,23 +101,24 @@ public void otherMemberships() { final AccountGroup.UUID handled = AccountGroup.uuid("handled"); final AccountGroup.UUID notHandled = AccountGroup.uuid("not handled"); - final IdentifiedUser member = createNiceMock(IdentifiedUser.class); - final IdentifiedUser notMember = createNiceMock(IdentifiedUser.class); + final IdentifiedUser member = mock(IdentifiedUser.class); + final IdentifiedUser notMember = mock(IdentifiedUser.class); - GroupBackend backend = createMock(GroupBackend.class); - expect(backend.handles(handled)).andStubReturn(true); - expect(backend.handles(not(eq(handled)))).andStubReturn(false); - expect(backend.membershipsOf(anyObject(IdentifiedUser.class))) - .andStubAnswer( - () -> { - Object[] args = getCurrentArguments(); - GroupMembership membership = createMock(GroupMembership.class); - expect(membership.contains(eq(handled))).andStubReturn(args[0] == member); - expect(membership.contains(not(eq(notHandled)))).andStubReturn(false); - replay(membership); - return membership; + GroupBackend backend = mock(GroupBackend.class); + when(backend.handles(eq(handled))).thenReturn(true); + when(backend.handles(not(eq(handled)))).thenReturn(false); + when(backend.membershipsOf(any(IdentifiedUser.class))) + .thenAnswer( + new Answer<GroupMembership>() { + @Override + public GroupMembership answer(InvocationOnMock invocation) { + GroupMembership membership = mock(GroupMembership.class); + when(membership.contains(eq(handled))) + .thenReturn(invocation.getArguments()[0] == member); + when(membership.contains(eq(notHandled))).thenReturn(false); + return membership; + } }); - replay(member, notMember, backend); backends = new DynamicSet<>(); backends.add("gerrit", backend);
diff --git a/javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java b/javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java index c8df548..2174927 100644 --- a/javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java +++ b/javatests/com/google/gerrit/server/fixes/FixReplacementInterpreterTest.java
@@ -16,8 +16,8 @@ import static com.google.gerrit.server.edit.tree.TreeModificationSubject.assertThatList; import static com.google.gerrit.testing.GerritJUnit.assertThrows; -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.replay; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableList; import com.google.gerrit.extensions.restapi.BinaryResult; @@ -30,17 +30,16 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import org.easymock.EasyMock; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; import org.junit.Before; import org.junit.Test; public class FixReplacementInterpreterTest { - private final FileContentUtil fileContentUtil = createMock(FileContentUtil.class); - private final Repository repository = createMock(Repository.class); - private final ProjectState projectState = createMock(ProjectState.class); - private final ObjectId patchSetCommitId = createMock(ObjectId.class); + private final FileContentUtil fileContentUtil = mock(FileContentUtil.class); + private final Repository repository = mock(Repository.class); + private final ProjectState projectState = mock(ProjectState.class); + private final ObjectId patchSetCommitId = mock(ObjectId.class); private final String filePath1 = "an/arbitrary/file.txt"; private final String filePath2 = "another/arbitrary/file.txt"; @@ -68,7 +67,6 @@ new FixReplacement(filePath2, new Range(2, 0, 3, 0), "Another modified content"); mockFileContent(filePath2, "1st line\n2nd line\n3rd line\n"); - replay(fileContentUtil); List<TreeModification> treeModifications = toTreeModifications(fixReplacement, fixReplacement3, fixReplacement2); List<TreeModification> sortedTreeModifications = getSortedCopy(treeModifications); @@ -99,7 +97,6 @@ FixReplacement fixReplacement = new FixReplacement(filePath1, new Range(2, 0, 3, 0), ""); mockFileContent(filePath1, "First line\nSecond line\nThird line\n"); - replay(fileContentUtil); List<TreeModification> treeModifications = toTreeModifications(fixReplacement); assertThatList(treeModifications) .onlyElement() @@ -114,7 +111,6 @@ new FixReplacement(filePath1, new Range(2, 0, 2, 0), "A new line\n"); mockFileContent(filePath1, "First line\nSecond line\nThird line\n"); - replay(fileContentUtil); List<TreeModification> treeModifications = toTreeModifications(fixReplacement); assertThatList(treeModifications) .onlyElement() @@ -128,7 +124,6 @@ FixReplacement fixReplacement = new FixReplacement(filePath1, new Range(1, 6, 3, 1), "and t"); mockFileContent(filePath1, "First line\nSecond line\nThird line\n"); - replay(fileContentUtil); List<TreeModification> treeModifications = toTreeModifications(fixReplacement); assertThatList(treeModifications) .onlyElement() @@ -144,7 +139,6 @@ new FixReplacement(filePath1, new Range(2, 7, 2, 11), "modification"); mockFileContent(filePath1, "First line\nSecond line\nThird line\n"); - replay(fileContentUtil); List<TreeModification> treeModifications = toTreeModifications(fixReplacement1, fixReplacement2); assertThatList(treeModifications) @@ -162,7 +156,6 @@ new FixReplacement(filePath1, new Range(2, 7, 3, 5), "content"); mockFileContent(filePath1, "First line\nSecond line\nThird line\n"); - replay(fileContentUtil); List<TreeModification> treeModifications = toTreeModifications(fixReplacement1, fixReplacement2); assertThatList(treeModifications) @@ -178,7 +171,6 @@ new FixReplacement(filePath1, new Range(4, 0, 4, 0), "New content"); mockFileContent(filePath1, "First line\nSecond line\nThird line\n"); - replay(fileContentUtil); List<TreeModification> treeModifications = toTreeModifications(fixReplacement); assertThatList(treeModifications) .onlyElement() @@ -198,7 +190,6 @@ new FixReplacement(filePath2, new Range(3, 0, 4, 0), "Second modification\n"); mockFileContent(filePath2, "1st line\n2nd line\n3rd line\n"); - replay(fileContentUtil); List<TreeModification> treeModifications = toTreeModifications(fixReplacement3, fixReplacement1, fixReplacement2); List<TreeModification> sortedTreeModifications = getSortedCopy(treeModifications); @@ -219,7 +210,6 @@ FixReplacement fixReplacement = new FixReplacement(filePath1, new Range(2, 11, 3, 0), "\r"); mockFileContent(filePath1, "First line\nSecond line\nThird line\n"); - replay(fileContentUtil); List<TreeModification> treeModifications = toTreeModifications(fixReplacement); assertThatList(treeModifications) .onlyElement() @@ -238,7 +228,6 @@ new FixReplacement(filePath1, new Range(4, 0, 5, 0), "3rd modification\n"); mockFileContent(filePath1, "First line\nSecond line\nThird line\nFourth line\nFifth line\n"); - replay(fileContentUtil); List<TreeModification> treeModifications = toTreeModifications(fixReplacement2, fixReplacement1, fixReplacement3); assertThatList(treeModifications) @@ -255,7 +244,6 @@ new FixReplacement(filePath1, new Range(5, 0, 5, 0), "A new line\n"); mockFileContent(filePath1, "First line\nSecond line\nThird line\n"); - replay(fileContentUtil); assertThrows(ResourceConflictException.class, () -> toTreeModifications(fixReplacement)); } @@ -265,8 +253,6 @@ new FixReplacement(filePath1, new Range(0, 0, 0, 0), "A new line\n"); mockFileContent(filePath1, "First line\nSecond line\nThird line\n"); - replay(fileContentUtil); - assertThrows(ResourceConflictException.class, () -> toTreeModifications(fixReplacement)); } @@ -276,7 +262,6 @@ new FixReplacement(filePath1, new Range(1, 0, 1, 11), "modified"); mockFileContent(filePath1, "First line\nSecond line\nThird line\n"); - replay(fileContentUtil); assertThrows(ResourceConflictException.class, () -> toTreeModifications(fixReplacement)); } @@ -286,8 +271,6 @@ new FixReplacement(filePath1, new Range(3, 0, 3, 11), "modified"); mockFileContent(filePath1, "First line\nSecond line\nThird line\n"); - replay(fileContentUtil); - assertThrows(ResourceConflictException.class, () -> toTreeModifications(fixReplacement)); } @@ -297,14 +280,12 @@ new FixReplacement(filePath1, new Range(1, -1, 1, 5), "modified"); mockFileContent(filePath1, "First line\nSecond line\nThird line\n"); - replay(fileContentUtil); assertThrows(ResourceConflictException.class, () -> toTreeModifications(fixReplacement)); } private void mockFileContent(String filePath, String fileContent) throws Exception { - EasyMock.expect( - fileContentUtil.getContent(repository, projectState, patchSetCommitId, filePath)) - .andReturn(BinaryResult.create(fileContent)); + when(fileContentUtil.getContent(repository, projectState, patchSetCommitId, filePath)) + .thenReturn(BinaryResult.create(fileContent)); } private List<TreeModification> toTreeModifications(FixReplacement... fixReplacements)
diff --git a/javatests/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManagerTest.java b/javatests/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManagerTest.java index 491594b..cbef04a 100644 --- a/javatests/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManagerTest.java +++ b/javatests/com/google/gerrit/server/git/MultiBaseLocalDiskRepositoryManagerTest.java
@@ -16,10 +16,8 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.gerrit.testing.GerritJUnit.assertThrows; -import static org.easymock.EasyMock.createNiceMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.reset; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableList; import com.google.gerrit.reviewdb.client.Project; @@ -55,9 +53,8 @@ site.resolve("git").toFile().mkdir(); cfg = new Config(); cfg.setString("gerrit", null, "basePath", "git"); - configMock = createNiceMock(RepositoryConfig.class); - expect(configMock.getAllBasePaths()).andReturn(ImmutableList.of()).anyTimes(); - replay(configMock); + configMock = mock(RepositoryConfig.class); + when(configMock.getAllBasePaths()).thenReturn(ImmutableList.of()); repoManager = new MultiBaseLocalDiskRepositoryManager(site, cfg, configMock); } @@ -90,10 +87,8 @@ public void alternateRepositoryLocation() throws IOException { Path alternateBasePath = temporaryFolder.newFolder().toPath(); Project.NameKey someProjectKey = Project.nameKey("someProject"); - reset(configMock); - expect(configMock.getBasePath(someProjectKey)).andReturn(alternateBasePath).anyTimes(); - expect(configMock.getAllBasePaths()).andReturn(ImmutableList.of(alternateBasePath)).anyTimes(); - replay(configMock); + when(configMock.getBasePath(someProjectKey)).thenReturn(alternateBasePath); + when(configMock.getAllBasePaths()).thenReturn(ImmutableList.of(alternateBasePath)); Repository repo = repoManager.createRepository(someProjectKey); assertThat(repo.getDirectory()).isNotNull(); @@ -123,11 +118,9 @@ Path alternateBasePath = temporaryFolder.newFolder().toPath(); - reset(configMock); - expect(configMock.getBasePath(altPathProject)).andReturn(alternateBasePath).anyTimes(); - expect(configMock.getBasePath(misplacedProject2)).andReturn(alternateBasePath).anyTimes(); - expect(configMock.getAllBasePaths()).andReturn(ImmutableList.of(alternateBasePath)).anyTimes(); - replay(configMock); + when(configMock.getBasePath(altPathProject)).thenReturn(alternateBasePath); + when(configMock.getBasePath(misplacedProject2)).thenReturn(alternateBasePath); + when(configMock.getAllBasePaths()).thenReturn(ImmutableList.of(alternateBasePath)); repoManager.createRepository(basePathProject); repoManager.createRepository(altPathProject); @@ -155,11 +148,8 @@ assertThrows( IllegalStateException.class, () -> { - configMock = createNiceMock(RepositoryConfig.class); - expect(configMock.getAllBasePaths()) - .andReturn(ImmutableList.of(Paths.get("repos"))) - .anyTimes(); - replay(configMock); + configMock = mock(RepositoryConfig.class); + when(configMock.getAllBasePaths()).thenReturn(ImmutableList.of(Paths.get("repos"))); repoManager = new MultiBaseLocalDiskRepositoryManager(site, cfg, configMock); }); }
diff --git a/javatests/com/google/gerrit/server/group/db/AuditLogReaderTest.java b/javatests/com/google/gerrit/server/group/db/AuditLogReaderTest.java index a5b755e..81108ea 100644 --- a/javatests/com/google/gerrit/server/group/db/AuditLogReaderTest.java +++ b/javatests/com/google/gerrit/server/group/db/AuditLogReaderTest.java
@@ -37,7 +37,7 @@ @Before public void setUp() throws Exception { - auditLogReader = new AuditLogReader(SERVER_ID, allUsersName); + auditLogReader = new AuditLogReader(allUsersName); } @Test
diff --git a/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java b/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java index ff114fa7..6b0945c 100644 --- a/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java +++ b/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java
@@ -15,11 +15,11 @@ package com.google.gerrit.server.mail.send; import static com.google.common.truth.Truth.assertThat; -import static org.easymock.EasyMock.createStrictMock; -import static org.easymock.EasyMock.eq; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; import com.google.gerrit.mail.Address; import com.google.gerrit.reviewdb.client.Account; @@ -43,7 +43,7 @@ public void setUp() throws Exception { config = new Config(); ident = new PersonIdent("NAME", "e@email", 0, 0); - accountCache = createStrictMock(AccountCache.class); + accountCache = mock(AccountCache.class); } private FromAddressGenerator create() { @@ -83,12 +83,11 @@ final String email = "a.u.thor@test.example.com"; final Account.Id user = user(name, email); - replay(accountCache); final Address r = create().from(user); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo(name); assertThat(r.getEmail()).isEqualTo(email); - verify(accountCache); + verifyAccountCacheGet(user); } @Test @@ -98,12 +97,11 @@ final String email = "a.u.thor@test.example.com"; final Account.Id user = user(null, email); - replay(accountCache); final Address r = create().from(user); assertThat(r).isNotNull(); assertThat(r.getName()).isNull(); assertThat(r.getEmail()).isEqualTo(email); - verify(accountCache); + verifyAccountCacheGet(user); } @Test @@ -113,23 +111,21 @@ final String name = "A U. Thor"; final Account.Id user = user(name, null); - replay(accountCache); final Address r = create().from(user); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo(name + " (Code Review)"); assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress()); - verify(accountCache); + verifyAccountCacheGet(user); } @Test public void USER_NullUser() { setFrom("USER"); - replay(accountCache); final Address r = create().from(null); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo(ident.getName()); assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress()); - verify(accountCache); + verifyZeroInteractions(accountCache); } @Test @@ -140,12 +136,11 @@ final String email = "a.u.thor@test.example.com"; final Account.Id user = user(name, email); - replay(accountCache); final Address r = create().from(user); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo(name); assertThat(r.getEmail()).isEqualTo(email); - verify(accountCache); + verifyAccountCacheGet(user); } @Test @@ -156,12 +151,11 @@ final String email = "a.u.thor@test.com"; final Account.Id user = user(name, email); - replay(accountCache); final Address r = create().from(user); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo(name + " (Code Review)"); assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress()); - verify(accountCache); + verifyAccountCacheGet(user); } @Test @@ -173,12 +167,11 @@ final String email = "a.u.thor@test.com"; final Account.Id user = user(name, email); - replay(accountCache); final Address r = create().from(user); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo(name); assertThat(r.getEmail()).isEqualTo(email); - verify(accountCache); + verifyAccountCacheGet(user); } @Test @@ -190,12 +183,11 @@ final String email = "a.u.thor@test.com"; final Account.Id user = user(name, email); - replay(accountCache); final Address r = create().from(user); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo(name + " (Code Review)"); assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress()); - verify(accountCache); + verifyAccountCacheGet(user); } @Test @@ -206,12 +198,11 @@ final String email = "a.u.thor@test.com"; final Account.Id user = user(name, email); - replay(accountCache); final Address r = create().from(user); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo(name); assertThat(r.getEmail()).isEqualTo(email); - verify(accountCache); + verifyAccountCacheGet(user); } @Test @@ -234,23 +225,21 @@ final String email = "a.u.thor@test.example.com"; final Account.Id user = userNoLookup(name, email); - replay(accountCache); final Address r = create().from(user); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo(ident.getName()); assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress()); - verify(accountCache); + verifyZeroInteractions(accountCache); } @Test public void SERVER_NullUser() { setFrom("SERVER"); - replay(accountCache); final Address r = create().from(null); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo(ident.getName()); assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress()); - verify(accountCache); + verifyZeroInteractions(accountCache); } @Test @@ -273,12 +262,11 @@ final String email = "a.u.thor@test.example.com"; final Account.Id user = user(name, email); - replay(accountCache); final Address r = create().from(user); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo(name + " (Code Review)"); assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress()); - verify(accountCache); + verifyAccountCacheGet(user); } @Test @@ -288,12 +276,11 @@ final String email = "a.u.thor@test.example.com"; final Account.Id user = user(null, email); - replay(accountCache); final Address r = create().from(user); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo("Anonymous Coward (Code Review)"); assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress()); - verify(accountCache); + verifyAccountCacheGet(user); } @Test @@ -303,23 +290,21 @@ final String name = "A U. Thor"; final Account.Id user = user(name, null); - replay(accountCache); final Address r = create().from(user); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo(name + " (Code Review)"); assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress()); - verify(accountCache); + verifyAccountCacheGet(user); } @Test public void MIXED_NullUser() { setFrom("MIXED"); - replay(accountCache); final Address r = create().from(null); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo(ident.getName()); assertThat(r.getEmail()).isEqualTo(ident.getEmailAddress()); - verify(accountCache); + verifyZeroInteractions(accountCache); } @Test @@ -330,12 +315,11 @@ final String email = "a.u.thor@test.example.com"; final Account.Id user = user(name, email); - replay(accountCache); final Address r = create().from(user); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo("A " + name + " B"); assertThat(r.getEmail()).isEqualTo("my.server@email.address"); - verify(accountCache); + verifyAccountCacheGet(user); } @Test @@ -345,32 +329,32 @@ final String email = "a.u.thor@test.example.com"; final Account.Id user = user(null, email); - replay(accountCache); final Address r = create().from(user); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo("A Anonymous Coward B"); assertThat(r.getEmail()).isEqualTo("my.server@email.address"); - verify(accountCache); } @Test public void CUSTOM_NullUser() { setFrom("A ${user} B <my.server@email.address>"); - replay(accountCache); final Address r = create().from(null); assertThat(r).isNotNull(); assertThat(r.getName()).isEqualTo(ident.getName()); assertThat(r.getEmail()).isEqualTo("my.server@email.address"); - verify(accountCache); } private Account.Id user(String name, String email) { final AccountState s = makeUser(name, email); - expect(accountCache.get(eq(s.getAccount().id()))).andReturn(Optional.of(s)); + when(accountCache.get(eq(s.getAccount().id()))).thenReturn(Optional.of(s)); return s.getAccount().id(); } + private void verifyAccountCacheGet(Account.Id id) { + verify(accountCache).get(eq(id)); + } + private Account.Id userNoLookup(String name, String email) { final AccountState s = makeUser(name, email); return s.getAccount().id();
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java index 3e54863..a4602e19 100644 --- a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java +++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
@@ -56,6 +56,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.UUID; import org.eclipse.jgit.lib.ObjectId; import org.junit.Before; import org.junit.Test; @@ -66,6 +67,7 @@ ObjectId.fromString("1234567812345678123456781234567812345678"); private static final ByteString SHA_BYTES = ObjectIdConverter.create().toByteString(SHA); private static final String CHANGE_KEY = "Iabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"; + private static final String DEFAULT_SERVER_ID = UUID.randomUUID().toString(); private ChangeColumns cols; private ChangeColumnsProto colsProto; @@ -717,6 +719,7 @@ ImmutableMap.<String, Type>builder() .put("metaId", ObjectId.class) .put("changeId", Change.Id.class) + .put("serverId", String.class) .put("columns", ChangeColumns.class) .put("pastAssignees", new TypeLiteral<ImmutableSet<Account.Id>>() {}.getType()) .put("hashtags", new TypeLiteral<ImmutableSet<String>>() {}.getType()) @@ -918,6 +921,19 @@ .build()); } + @Test + public void serializeServerId() throws Exception { + assertRoundTrip( + newBuilder().serverId(DEFAULT_SERVER_ID).build(), + ChangeNotesStateProto.newBuilder() + .setMetaId(SHA_BYTES) + .setChangeId(ID.get()) + .setServerId(DEFAULT_SERVER_ID) + .setHasServerId(true) + .setColumns(colsProto.toBuilder()) + .build()); + } + private static ChangeNotesStateProto toProto(ChangeNotesState state) throws Exception { return ChangeNotesStateProto.parseFrom(Serializer.INSTANCE.serialize(state)); }
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html index 63f3eaf..8e57534 100644 --- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html +++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html
@@ -329,17 +329,8 @@ name: 'ldap/tests tests'}}); assert.equal(element._rules.length, 3); assert.equal(Object.keys(element._groupsWithRules).length, 3); - if (Polymer.Element) { - // Under Polymer 2 gr-rule-editor.js#_handleValueChange get's - // fully loaded before this change, thus `modified: true` get's managed - // to be added. Under Polymer 1 it was a mix hence why it was not - // added in time for when this test ran. - assert.deepEqual(element.permission.value.rules['ldap:CN=test test'], - {action: 'ALLOW', min: -2, max: 2, modified: true, added: true}); - } else { - assert.deepEqual(element.permission.value.rules['ldap:CN=test test'], - {action: 'ALLOW', min: -2, max: 2, added: true}); - } + assert.deepEqual(element.permission.value.rules['ldap:CN=test test'], + {action: 'ALLOW', min: -2, max: 2, added: true}); // New rule should be removed if cancel from editing. element.editing = false; assert.equal(element._rules.length, 2);
diff --git a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html index 53db6d5..f22c5a5 100644 --- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html +++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo_test.html
@@ -102,10 +102,12 @@ const SCHEMES = {http: {}, repo: {}, ssh: {}}; function getFormFields() { - const selects = Polymer.dom(element.root).querySelectorAll('select'); - const textareas = - Polymer.dom(element.root).querySelectorAll('iron-autogrow-textarea'); - const inputs = Polymer.dom(element.root).querySelectorAll('input'); + const selects = Array.from( + Polymer.dom(element.root).querySelectorAll('select')); + const textareas = Array.from( + Polymer.dom(element.root).querySelectorAll('iron-autogrow-textarea')); + const inputs = Array.from( + Polymer.dom(element.root).querySelectorAll('input')); return inputs.concat(textareas).concat(selects); }
diff --git a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js index f206514..df5a9f3 100644 --- a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js +++ b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
@@ -118,11 +118,20 @@ this._setupValues(this.rule); }, + attached() { + if (!this.rule) { return; } // Check needed for test purposes. + if (!this._originalRuleValues) { + // Observer _handleValueChange is called after the ready() + // method finishes. Original values must be set later to + // avoid set .modified flag to true + this._setOriginalRuleValues(this.rule.value); + } + }, + _setupValues(rule) { if (!rule.value) { this._setDefaultRuleValues(); } - this._setOriginalRuleValues(rule.value); }, _computeForce(permission, action) {
diff --git a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html index 17e8c6c..4ea3817 100644 --- a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html +++ b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor_test.html
@@ -202,7 +202,7 @@ }); suite('already existing generic rule', () => { - setup(() => { + setup(done => { element.group = 'Group Name'; element.permission = 'submit'; element.rule = { @@ -218,6 +218,10 @@ // by the parent element. element._setupValues(element.rule); flushAsynchronousOperations(); + flush(() => { + element.attached(); + done(); + }); }); test('_ruleValues and _originalRuleValues are set correctly', () => { @@ -313,7 +317,7 @@ }); suite('new edit rule', () => { - setup(() => { + setup(done => { element.group = 'Group Name'; element.permission = 'editTopicName'; element.rule = { @@ -323,6 +327,10 @@ element._setupValues(element.rule); flushAsynchronousOperations(); element.rule.value.added = true; + flush(() => { + element.attached(); + done(); + }); }); test('_ruleValues and _originalRuleValues are set correctly', () => { @@ -362,7 +370,7 @@ }); suite('already existing rule with labels', () => { - setup(() => { + setup(done => { element.label = {values: [ {value: -2, text: 'This shall not be merged'}, {value: -1, text: 'I would prefer this is not merged as is'}, @@ -384,6 +392,10 @@ element.section = 'refs/*'; element._setupValues(element.rule); flushAsynchronousOperations(); + flush(() => { + element.attached(); + done(); + }); }); test('_ruleValues and _originalRuleValues are set correctly', () => { @@ -416,7 +428,7 @@ }); suite('new rule with labels', () => { - setup(() => { + setup(done => { sandbox.spy(element, '_setDefaultRuleValues'); element.label = {values: [ {value: -2, text: 'This shall not be merged'}, @@ -434,6 +446,10 @@ element._setupValues(element.rule); flushAsynchronousOperations(); element.rule.value.added = true; + flush(() => { + element.attached(); + done(); + }); }); test('_ruleValues and _originalRuleValues are set correctly', () => { @@ -474,7 +490,7 @@ }); suite('already existing push rule', () => { - setup(() => { + setup(done => { element.group = 'Group Name'; element.permission = 'push'; element.rule = { @@ -487,6 +503,10 @@ element.section = 'refs/*'; element._setupValues(element.rule); flushAsynchronousOperations(); + flush(() => { + element.attached(); + done(); + }); }); test('_ruleValues and _originalRuleValues are set correctly', () => { @@ -515,7 +535,7 @@ }); suite('new push rule', () => { - setup(() => { + setup(done => { element.group = 'Group Name'; element.permission = 'push'; element.rule = { @@ -525,6 +545,10 @@ element._setupValues(element.rule); flushAsynchronousOperations(); element.rule.value.added = true; + flush(() => { + element.attached(); + done(); + }); }); test('_ruleValues and _originalRuleValues are set correctly', () => { @@ -555,7 +579,7 @@ }); suite('already existing edit rule', () => { - setup(() => { + setup(done => { element.group = 'Group Name'; element.permission = 'editTopicName'; element.rule = { @@ -568,6 +592,10 @@ element.section = 'refs/*'; element._setupValues(element.rule); flushAsynchronousOperations(); + flush(() => { + element.attached(); + done(); + }); }); test('_ruleValues and _originalRuleValues are set correctly', () => {
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html index 282d8de..de02923 100644 --- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html +++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html
@@ -146,7 +146,8 @@ }); test('anchors use download attribute', () => { - const anchors = Polymer.dom(element.root).querySelectorAll('a'); + const anchors = Array.from( + Polymer.dom(element.root).querySelectorAll('a')); assert.isTrue(!anchors.some(a => !a.hasAttribute('download'))); });
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js index 37beff4..9e6997c 100644 --- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js +++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -148,7 +148,7 @@ _shownFiles: { type: Array, - computed: '_computeFilesShown(numFilesShown, _files.*)', + computed: '_computeFilesShown(numFilesShown, _files)', }, /** @@ -473,7 +473,7 @@ this.set(['_files', index, 'isReviewed'], reviewed); if (index < this._shownFiles.length) { - this.set(['_shownFiles', index, 'isReviewed'], reviewed); + this.notifyPath(`_shownFiles.${index}.isReviewed`); } this._saveReviewedState(path, reviewed); @@ -855,7 +855,7 @@ const previousNumFilesShown = this._shownFiles ? this._shownFiles.length : 0; - const filesShown = files.base.slice(0, numFilesShown); + const filesShown = files.slice(0, numFilesShown); this.fire('files-shown-changed', {length: filesShown.length}); // Start the timer for the rendering work hwere because this is where the
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html index 9529d38..cc3e937 100644 --- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html +++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
@@ -1709,8 +1709,9 @@ // Commit message should not have edit controls. const editControls = - Polymer.dom(element.root).querySelectorAll('.row:not(.header)') - .map(row => row.querySelector('gr-edit-file-controls')); + Array.from( + Polymer.dom(element.root).querySelectorAll('.row:not(.header)')) + .map(row => row.querySelector('gr-edit-file-controls')); assert.isTrue(editControls[0].classList.contains('invisible')); });
diff --git a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.html b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.html index 4920e20..5f7ccbe 100644 --- a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.html +++ b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row_test.html
@@ -267,7 +267,7 @@ assert.isTrue(element.$$('iron-selector').hidden); }); - test('asymetrical labels', () => { + test('asymetrical labels', done => { element.permittedLabels = { 'Code-Review': [ '-2', @@ -281,30 +281,35 @@ '+1', ], }; - flushAsynchronousOperations(); - assert.strictEqual(element.$$('iron-selector') - .items.length, 2); - assert.strictEqual(Polymer.dom(element.root). - querySelectorAll('.placeholder').length, 3); + flush(() => { + assert.strictEqual(element.$$('iron-selector') + .items.length, 2); + assert.strictEqual( + Polymer.dom(element.root).querySelectorAll('.placeholder').length, + 3); - element.permittedLabels = { - 'Code-Review': [ - ' 0', - '+1', - ], - 'Verified': [ - '-2', - '-1', - ' 0', - '+1', - '+2', - ], - }; - flushAsynchronousOperations(); - assert.strictEqual(element.$$('iron-selector') - .items.length, 5); - assert.strictEqual(Polymer.dom(element.root). - querySelectorAll('.placeholder').length, 0); + element.permittedLabels = { + 'Code-Review': [ + ' 0', + '+1', + ], + 'Verified': [ + '-2', + '-1', + ' 0', + '+1', + '+2', + ], + }; + flush(() => { + assert.strictEqual(element.$$('iron-selector') + .items.length, 5); + assert.strictEqual( + Polymer.dom(element.root).querySelectorAll('.placeholder').length, + 0); + done(); + }); + }); }); test('default_value', () => {
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js index 13c1754..80115c6 100644 --- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js +++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
@@ -158,9 +158,13 @@ _expandedChanged(exp) { for (let i = 0; i < this._processedMessages.length; i++) { this._processedMessages[i].expanded = exp; - if (i < this._visibleMessages.length) { - this.set(['_visibleMessages', i, 'expanded'], exp); - } + } + // _visibleMessages is a subarray of _processedMessages + // _processedMessages contains all items from _visibleMessages + // At this point all _visibleMessages.expanded values are set, + // and notifyPath must be used to notify Polymer about changes. + for (let i = 0; i < this._visibleMessages.length; i++) { + this.notifyPath(`_visibleMessages.${i}.expanded`); } },
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html index 01f1f4d..6b738d8 100644 --- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html +++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
@@ -277,17 +277,19 @@ }); }); - test('setlabelValue', () => { + test('setlabelValue', done => { element._account = {_account_id: 1}; - flushAsynchronousOperations(); - const label = 'Verified'; - const value = '+1'; - element.setLabelValue(label, value); - flushAsynchronousOperations(); - const labels = element.$.labelScores.getLabelValues(); - assert.deepEqual(labels, { - 'Code-Review': 0, - 'Verified': 1, + flush(() => { + const label = 'Verified'; + const value = '+1'; + element.setLabelValue(label, value); + + const labels = element.$.labelScores.getLabelValues(); + assert.deepEqual(labels, { + 'Code-Review': 0, + 'Verified': 1, + }); + done(); }); }); @@ -310,6 +312,26 @@ }); } + function isFocusInsideElement(element) { + // In Polymer 2 focused element either <paper-input> or nested + // native input <input> element depending on the current focus + // in browser window. + // For example, the focus is changed if the developer console + // get a focus. + let activeElement = getActiveElement(); + while (activeElement) { + if (activeElement === element) { + return true; + } + if (activeElement.parentElement) { + activeElement = activeElement.parentElement; + } else { + activeElement = activeElement.getRootNode().host; + } + } + return false; + } + function testConfirmationDialog(done, cc) { const yesButton = element.$$('.reviewerConfirmationButtons gr-button:first-child'); @@ -363,7 +385,8 @@ assert.isFalse(isVisible(element.$.reviewerConfirmationOverlay)); // We should be focused on account entry input. - assert.equal(getActiveElement().id, 'input'); + assert.isTrue( + isFocusInsideElement(element.$.reviewers.$.entry.$.input.$.input)); // No reviewer/CC should have been added. assert.equal(element.$$('#ccs').additions().length, 0); @@ -408,7 +431,13 @@ ]); // We should be focused on account entry input. - assert.equal(getActiveElement().id, 'input'); + if (cc) { + assert.isTrue( + isFocusInsideElement(element.$.ccs.$.entry.$.input.$.input)); + } else { + assert.isTrue( + isFocusInsideElement(element.$.reviewers.$.entry.$.input.$.input)); + } }).then(done); } @@ -1239,4 +1268,4 @@ assert.equal(element.$.pluginMessage.textContent, 'foo'); }); }); -</script> \ No newline at end of file +</script>
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector.js b/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector.js deleted file mode 100644 index 28c46f4..0000000 --- a/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector.js +++ /dev/null
@@ -1,61 +0,0 @@ -/** - * @license - * Copyright (C) 2016 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. - */ -(function() { - 'use strict'; - - const JANK_SLEEP_TIME_MS = 1000; - - const GrJankDetector = { - // Slowdowns counter. - jank: 0, - fps: 0, - _lastFrameTime: 0, - - start() { - this._requestAnimationFrame(this._detect.bind(this)); - }, - - _requestAnimationFrame(callback) { - window.requestAnimationFrame(callback); - }, - - _detect(now) { - if (this._lastFrameTime === 0) { - this._lastFrameTime = now; - this.fps = 0; - this._requestAnimationFrame(this._detect.bind(this)); - return; - } - const fpsNow = 1000/(now - this._lastFrameTime); - this._lastFrameTime = now; - // Calculate moving average within last 3 measurements. - this.fps = this.fps === 0 ? fpsNow : ((this.fps * 2 + fpsNow) / 3); - if (this.fps > 10) { - this._requestAnimationFrame(this._detect.bind(this)); - } else { - this.jank++; - console.warn('JANK', this.jank); - this._lastFrameTime = 0; - window.setTimeout( - () => this._requestAnimationFrame(this._detect.bind(this)), - JANK_SLEEP_TIME_MS); - } - }, - }; - - window.GrJankDetector = GrJankDetector; -})();
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector_test.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector_test.html deleted file mode 100644 index 825a5fc..0000000 --- a/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector_test.html +++ /dev/null
@@ -1,80 +0,0 @@ -<!DOCTYPE html> -<!-- -@license -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. ---> - -<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> -<title>gr-jank-detector</title> -<script src="/test/common-test-setup.js"></script> -<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script> - -<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script> -<script src="/bower_components/web-component-tester/browser.js"></script> -<link rel="import" href="../../../test/common-test-setup.html"/> - -<script src="gr-jank-detector.js"></script> - -<script> - suite('gr-jank-detector tests', () => { - let sandbox; - let clock; - let instance; - - const NOW_TIME = 100; - - setup(() => { - sandbox = sinon.sandbox.create(); - clock = sinon.useFakeTimers(NOW_TIME); - instance = GrJankDetector; - instance._lastFrameTime = 0; - sandbox.stub(instance, '_requestAnimationFrame'); - }); - - teardown(() => { - sandbox.restore(); - }); - - test('start() installs frame callback', () => { - sandbox.stub(instance, '_detect'); - instance._requestAnimationFrame.callsArg(0); - instance.start(); - assert.isTrue(instance._detect.calledOnce); - }); - - test('measures fps', () => { - instance._detect(10); - instance._detect(30); - assert.equal(instance.fps, 50); - }); - - test('detects jank', () => { - let now = 10; - instance._detect(now); - const fastFrame = () => instance._detect(now += 20); - const slowFrame = () => instance._detect(now += 300); - fastFrame(); - assert.equal(instance.jank, 0); - _.times(4, slowFrame); - assert.equal(instance.jank, 0); - instance._requestAnimationFrame.reset(); - slowFrame(); - assert.equal(instance.jank, 1); - assert.isFalse(instance._requestAnimationFrame.called); - clock.tick(1000); - assert.isTrue(instance._requestAnimationFrame.called); - }); - }); -</script>
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html index 6588df4..0ba8a22 100644 --- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html +++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html
@@ -18,6 +18,5 @@ <link rel="import" href="/bower_components/polymer/polymer.html"> <dom-module id="gr-reporting"> - <script src="gr-jank-detector.js"></script> <script src="gr-reporting.js"></script> </dom-module>
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js index 2bd4a620..9ff425c 100644 --- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js +++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
@@ -49,14 +49,6 @@ STARTED_HIDDEN: 'hidden', }; - // Frame rate related constants. - const JANK = { - TYPE: 'lifecycle', - CATEGORY: 'UI Latency', - // Reported events - alphabetize below. - COUNT: 'Jank count', - }; - // Navigation reporting constants. const NAVIGATION = { TYPE: 'nav-report', @@ -150,8 +142,6 @@ }; catchErrors(); - GrJankDetector.start(); - // The Polymer pass of JSCompiler requires this to be reassignable // eslint-disable-next-line prefer-const let GrReporting = Polymer({ @@ -291,11 +281,6 @@ }, beforeLocationChanged() { - if (GrJankDetector.jank > 0) { - this.reporter( - JANK.TYPE, JANK.CATEGORY, JANK.COUNT, GrJankDetector.jank); - GrJankDetector.jank = 0; - } for (const prop of Object.keys(this._baselines)) { delete this._baselines[prop]; }
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html index 805cef0..ec24b19 100644 --- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html +++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
@@ -95,11 +95,7 @@ test('beforeLocationChanged', () => { element._baselines['garbage'] = 'monster'; sandbox.stub(element, 'time'); - GrJankDetector.jank = 42; element.beforeLocationChanged(); - assert.equal(GrJankDetector.jank, 0); - assert.isTrue(element.reporter.calledWithExactly( - 'lifecycle', 'UI Latency', 'Jank count', 42)); assert.isTrue(element.time.calledWithExactly('DashboardDisplayed')); assert.isTrue(element.time.calledWithExactly('ChangeDisplayed')); assert.isTrue(element.time.calledWithExactly('ChangeFullyLoaded'));
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js index ff7593b..9cd0527 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js +++ b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js
@@ -163,6 +163,7 @@ _handleCopy(e) { // Let the browser handle the copy event for polymer 2 // as selection across shadow DOM will be hard to process + // If you remove the following line, please remove it from tests also. if (window.POLYMER2) return; let commentSelected = false;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html index 0f5c6dd..a2696c2 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html +++ b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html
@@ -189,12 +189,18 @@ }); test('ignores copy for non-content Element', () => { + // See _handleCopy for explanation + if (window.POLYMER2) return; + sandbox.stub(element, '_getSelectedText'); emulateCopyOn(element.querySelector('.not-diff-row')); assert.isFalse(element._getSelectedText.called); }); test('asks for text for left side Elements', () => { + // See _handleCopy for explanation + if (window.POLYMER2) return; + element._cachedDiffBuilder.getSideByLineEl.returns('left'); sandbox.stub(element, '_getSelectedText'); emulateCopyOn(element.querySelector('div.contentText')); @@ -202,12 +208,18 @@ }); test('reacts to copy for content Elements', () => { + // See _handleCopy for explanation + if (window.POLYMER2) return; + sandbox.stub(element, '_getSelectedText'); emulateCopyOn(element.querySelector('div.contentText')); assert.isTrue(element._getSelectedText.called); }); test('copy event is prevented for content Elements', () => { + // See _handleCopy for explanation + if (window.POLYMER2) return; + sandbox.stub(element, '_getSelectedText'); element._cachedDiffBuilder.getSideByLineEl.returns('left'); element._getSelectedText.returns('test'); @@ -216,6 +228,9 @@ }); test('inserts text into clipboard on copy', () => { + // See _handleCopy for explanation + if (window.POLYMER2) return; + sandbox.stub(element, '_getSelectedText').returns('the text'); const event = emulateCopyOn(element.querySelector('div.contentText')); assert.deepEqual(
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html index b30cc5c..2b12932 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html +++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
@@ -333,9 +333,9 @@ user-select: text; } - /** Make comments selectable */ - .selected-left ::slotted(gr-comment-thread[comment-side=left]), - .selected-right ::slotted(gr-comment-thread[comment-side=right]) { + /** Make comments selectable when selected */ + .selected-left.selected-comment ::slotted(gr-comment-thread[comment-side=left]), + .selected-right.selected-comment ::slotted(gr-comment-thread[comment-side=right]) { -webkit-user-select: text; -moz-user-select: text; -ms-user-select: text;
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js index ece15bc..35b78ca 100644 --- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js +++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
@@ -168,7 +168,11 @@ // just make two separate queries. dialog.querySelectorAll('gr-autocomplete') .forEach(input => { input.text = ''; }); - dialog.querySelectorAll('input') + + // TODO: reveiw binding for input after drop Polymer 1 support + // All docs related to Polymer 2 set binding only for iron-input, + // and doesn't add binding to input. + dialog.querySelectorAll(window.POLYMER2 ? 'iron-input' : 'input') .forEach(input => { input.bindValue = ''; }); }
diff --git a/polygerrit-ui/app/elements/gr-app-element.html b/polygerrit-ui/app/elements/gr-app-element.html index 5c297e7..193f861 100644 --- a/polygerrit-ui/app/elements/gr-app-element.html +++ b/polygerrit-ui/app/elements/gr-app-element.html
@@ -198,12 +198,13 @@ <gr-endpoint-decorator name="footer-left"></gr-endpoint-decorator> </div> <div> - <a class="feedback" - href$="[[_feedbackUrl]]" - rel="noopener" - target="_blank" - hidden$="[[!_showFeedbackUrl(_feedbackUrl)]]">Report bug</a> - | Press “?” for keyboard shortcuts + <template is="dom-if" if="[[_feedbackUrl]]"> + <a class="feedback" + href$="[[_feedbackUrl]]" + rel="noopener" + target="_blank">Report bug</a> | + </template> + Press “?” for keyboard shortcuts <gr-endpoint-decorator name="footer-right"></gr-endpoint-decorator> </div> </footer>
diff --git a/polygerrit-ui/app/elements/gr-app-element.js b/polygerrit-ui/app/elements/gr-app-element.js index 3c146f8..5692969 100644 --- a/polygerrit-ui/app/elements/gr-app-element.js +++ b/polygerrit-ui/app/elements/gr-app-element.js
@@ -435,7 +435,9 @@ if (window.VERSION_INFO) { console.log(`UI Version Info: ${window.VERSION_INFO}`); } - console.log(`Please file bugs and feedback at: ${this._feedbackUrl}`); + if (this._feedbackUrl) { + console.log(`Please file bugs and feedback at: ${this._feedbackUrl}`); + } console.groupEnd(); }, @@ -453,14 +455,6 @@ this.mobileSearch = !this.mobileSearch; }, - _showFeedbackUrl(feedbackUrl) { - if (feedbackUrl) { - return feedbackUrl; - } - - return false; - }, - getThemeEndpoint() { // For now, we only have dark mode and light mode return window.localStorage.getItem('dark-theme') ?
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html index fa097ac..0883707 100644 --- a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html +++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html
@@ -88,7 +88,7 @@ test('decoration', () => { const element = container.querySelector('gr-endpoint-decorator[name="first"]'); - const modules = Polymer.dom(element.root).children.filter( + const modules = Array.from(Polymer.dom(element.root).children).filter( element => element.nodeName === 'SOME-MODULE'); assert.equal(modules.length, 1); const [module] = modules; @@ -105,7 +105,7 @@ test('replacement', () => { const element = container.querySelector('gr-endpoint-decorator[name="second"]'); - const module = Polymer.dom(element.root).children.find( + const module = Array.from(Polymer.dom(element.root).children).find( element => element.nodeName === 'OTHER-MODULE'); assert.isOk(module); assert.equal(module['someparam'], 'foofoo'); @@ -122,7 +122,7 @@ flush(() => { const element = container.querySelector('gr-endpoint-decorator[name="banana"]'); - const module = Polymer.dom(element.root).children.find( + const module = Array.from(Polymer.dom(element.root).children).find( element => element.nodeName === 'NOOB-NOOB'); assert.isOk(module); done(); @@ -135,10 +135,10 @@ flush(() => { const element = container.querySelector('gr-endpoint-decorator[name="banana"]'); - const module1 = Polymer.dom(element.root).children.find( + const module1 = Array.from(Polymer.dom(element.root).children).find( element => element.nodeName === 'MOD-ONE'); assert.isOk(module1); - const module2 = Polymer.dom(element.root).children.find( + const module2 = Array.from(Polymer.dom(element.root).children).find( element => element.nodeName === 'MOD-TWO'); assert.isOk(module2); done(); @@ -152,14 +152,14 @@ param['value'] = undefined; plugin.registerCustomComponent('banana', 'noob-noob'); flush(() => { - let module = Polymer.dom(element.root).children.find( + let module = Array.from(Polymer.dom(element.root).children).find( element => element.nodeName === 'NOOB-NOOB'); // Module waits for param to be defined. assert.isNotOk(module); const value = {abc: 'def'}; param.value = value; flush(() => { - module = Polymer.dom(element.root).children.find( + module = Array.from(Polymer.dom(element.root).children).find( element => element.nodeName === 'NOOB-NOOB'); assert.isOk(module); assert.strictEqual(module['someParam'], value); @@ -177,7 +177,7 @@ param.value = value1; plugin.registerCustomComponent('banana', 'noob-noob'); flush(() => { - const module = Polymer.dom(element.root).children.find( + const module = Array.from(Polymer.dom(element.root).children).find( element => element.nodeName === 'NOOB-NOOB'); assert.strictEqual(module['someParam'], value1); param.value = value2;
diff --git a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js index e8b7212..f865e77 100644 --- a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js +++ b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js
@@ -57,6 +57,7 @@ // Within <gr-external-style> itself the styles would have no effect. const topEl = document.getElementsByTagName('body')[0]; topEl.insertBefore(cs, topEl.firstChild); + Polymer.updateStyles(); }, _importAndApply() {
diff --git a/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api.js b/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api.js index 879b392..1de8283 100644 --- a/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api.js +++ b/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api.js
@@ -37,8 +37,11 @@ * @return {string} Appropriate class name for the element is returned */ GrStyleObject.prototype.getClassName = function(element) { - const rootNode = Polymer.Settings.useShadow + let rootNode = Polymer.Settings.useShadow ? element.getRootNode() : document.body; + if (rootNode === document) { + rootNode = document.head; + } if (!rootNode.__pg_js_api_style_tags) { rootNode.__pg_js_api_style_tags = {}; }
diff --git a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.html b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.html index f508288..b30bd52 100644 --- a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.html +++ b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.html
@@ -68,7 +68,7 @@ type="radio" on-change="_handlePreferredChange" name="preferred" - value="[[item.email]]" + bind-value="[[item.email]]" checked$="[[item.preferred]]"> <input is="iron-input"
diff --git a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html index e8ecd7a..8d3f2d2 100644 --- a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html +++ b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html
@@ -51,7 +51,7 @@ element = fixture('basic'); - element.loadData().then(done); + element.loadData().then(flush(done)); }); test('renders', () => {
diff --git a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html index ac17521..3c3ece36 100644 --- a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html +++ b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html
@@ -73,7 +73,8 @@ teardown(() => { sandbox.restore(); }); test('renders', () => { - const rows = Polymer.dom(element.root).querySelectorAll('tbody tr'); + const rows = Array.from( + Polymer.dom(element.root).querySelectorAll('tbody tr')); assert.equal(rows.length, 3);
diff --git a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.html b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.html index a663fd2..1277424 100644 --- a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.html +++ b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities_test.html
@@ -71,7 +71,8 @@ }); test('renders', () => { - const rows = Polymer.dom(element.root).querySelectorAll('tbody tr'); + const rows = Array.from( + Polymer.dom(element.root).querySelectorAll('tbody tr')); assert.equal(rows.length, 2); @@ -84,7 +85,8 @@ }); test('renders email', () => { - const rows = Polymer.dom(element.root).querySelectorAll('tbody tr'); + const rows = Array.from( + Polymer.dom(element.root).querySelectorAll('tbody tr')); assert.equal(rows.length, 2);
diff --git a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html index 917026a..134e018 100644 --- a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html +++ b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html
@@ -57,7 +57,7 @@ MockInteractions.tap(button); } - setup(() => { + setup(done => { element = fixture('basic'); menu = [ {url: '/first/url', name: 'first name', target: '_blank'}, @@ -66,6 +66,7 @@ ]; element.set('menuItems', menu); Polymer.dom.flush(); + flush(done); }); test('renders', () => {
diff --git a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html index 4193382..7a238ec 100644 --- a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html +++ b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html
@@ -189,6 +189,7 @@ element.$.newProject.value = {id: 'project b'}; element.$.newProject.setText('project b'); element.$.newFilter.bindValue = 'filter 1'; + element.$.newFilter.value = 'filter 1'; element._handleAddProject();
diff --git a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js index 9edc9c8..1be55d2 100644 --- a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js +++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list.js
@@ -253,8 +253,13 @@ console.warn('received remove event for missing account', toRemove); }, + _getNativeInput(paperInput) { + // In Polymer 2 inputElement isn't nativeInput anymore + return paperInput.$.nativeInput || paperInput.inputElement; + }, + _handleInputKeydown(e) { - const input = e.detail.input.inputElement; + const input = this._getNativeInput(e.detail.input); if (input.selectionStart !== input.selectionEnd || input.selectionStart !== 0) { return;
diff --git a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.html b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.html index 22e3a3d..6f265e7 100644 --- a/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.html +++ b/polygerrit-ui/app/elements/shared/gr-account-list/gr-account-list_test.html
@@ -417,43 +417,51 @@ }); suite('keyboard interactions', () => { - test('backspace at text input start removes last account', () => { + test('backspace at text input start removes last account', done => { const input = element.$.entry.$.input; sandbox.stub(input, '_updateSuggestions'); sandbox.stub(element, '_computeRemovable').returns(true); - // Next line is a workaround for Firefix not moving cursor - // on input field update - assert.equal(input.$.input.inputElement.selectionStart, 0); - input.text = 'test'; - MockInteractions.focus(input.$.input); - flushAsynchronousOperations(); - assert.equal(element.accounts.length, 2); - MockInteractions.pressAndReleaseKeyOn( - input.$.input.inputElement, 8); // Backspace - assert.equal(element.accounts.length, 2); - input.text = ''; - MockInteractions.pressAndReleaseKeyOn( - input.$.input.inputElement, 8); // Backspace - assert.equal(element.accounts.length, 1); + flush(() => { + // Next line is a workaround for Firefix not moving cursor + // on input field update + assert.equal( + element._getNativeInput(input.$.input).selectionStart, 0); + input.text = 'test'; + MockInteractions.focus(input.$.input); + flushAsynchronousOperations(); + assert.equal(element.accounts.length, 2); + MockInteractions.pressAndReleaseKeyOn( + element._getNativeInput(input.$.input), 8); // Backspace + assert.equal(element.accounts.length, 2); + input.text = ''; + MockInteractions.pressAndReleaseKeyOn( + element._getNativeInput(input.$.input), 8); // Backspace + flushAsynchronousOperations(); + assert.equal(element.accounts.length, 1); + done(); + }); }); - test('arrow key navigation', () => { + test('arrow key navigation', done => { const input = element.$.entry.$.input; input.text = ''; element.accounts = [makeAccount(), makeAccount()]; - MockInteractions.focus(input.$.input); - flushAsynchronousOperations(); - const chips = element.accountChips; - const chipsOneSpy = sandbox.spy(chips[1], 'focus'); - MockInteractions.pressAndReleaseKeyOn(input.$.input, 37); // Left - assert.isTrue(chipsOneSpy.called); - const chipsZeroSpy = sandbox.spy(chips[0], 'focus'); - MockInteractions.pressAndReleaseKeyOn(chips[1], 37); // Left - assert.isTrue(chipsZeroSpy.called); - MockInteractions.pressAndReleaseKeyOn(chips[0], 37); // Left - assert.isTrue(chipsZeroSpy.calledOnce); - MockInteractions.pressAndReleaseKeyOn(chips[0], 39); // Right - assert.isTrue(chipsOneSpy.calledTwice); + flush(() => { + MockInteractions.focus(input.$.input); + flushAsynchronousOperations(); + const chips = element.accountChips; + const chipsOneSpy = sandbox.spy(chips[1], 'focus'); + MockInteractions.pressAndReleaseKeyOn(input.$.input, 37); // Left + assert.isTrue(chipsOneSpy.called); + const chipsZeroSpy = sandbox.spy(chips[0], 'focus'); + MockInteractions.pressAndReleaseKeyOn(chips[1], 37); // Left + assert.isTrue(chipsZeroSpy.called); + MockInteractions.pressAndReleaseKeyOn(chips[0], 37); // Left + assert.isTrue(chipsZeroSpy.calledOnce); + MockInteractions.pressAndReleaseKeyOn(chips[0], 39); // Right + assert.isTrue(chipsOneSpy.calledTwice); + done(); + }); }); test('delete', done => {
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js index af31473..745cff1 100644 --- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js +++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
@@ -177,6 +177,11 @@ '_updateSuggestions(text, threshold, noDebounce)', ], + get _nativeInput() { + // In Polymer 2 inputElement isn't nativeInput anymore + return this.$.input.$.nativeInput || this.$.input.inputElement; + }, + attached() { this.listen(document.body, 'tap', '_handleBodyTap'); }, @@ -195,7 +200,7 @@ }, selectAll() { - const nativeInputElement = this.$.input.inputElement; + const nativeInputElement = this._nativeInput; if (!this.$.input.value) { return; } nativeInputElement.setSelectionRange(0, this.$.input.value.length); },
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html index cff35b4..ea1fd50 100644 --- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html +++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
@@ -82,16 +82,19 @@ }); }); - test('selectAll', () => { - const nativeInput = element.$.input.inputElement; - const selectionStub = sandbox.stub(nativeInput, 'setSelectionRange'); + test('selectAll', done => { + flush(() => { + const nativeInput = element._nativeInput; + const selectionStub = sandbox.stub(nativeInput, 'setSelectionRange'); - element.selectAll(); - assert.isFalse(selectionStub.called); + element.selectAll(); + assert.isFalse(selectionStub.called); - element.$.input.value = 'test'; - element.selectAll(); - assert.isTrue(selectionStub.called); + element.$.input.value = 'test'; + element.selectAll(); + assert.isTrue(selectionStub.called); + done(); + }); }); test('esc key behavior', done => {
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html index 42d7678..1c2afaa 100644 --- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html +++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
@@ -129,14 +129,31 @@ element.style.backgroundImage.includes('/accounts/123/avatar?s=64')); }); }); + }); + + suite('plugin has avatars', () => { + let element; + let sandbox; + + setup(() => { + sandbox = sinon.sandbox.create(); + + stub('gr-avatar', { + _getConfig: () => { + return Promise.resolve({plugin: {has_avatars: true}}); + }, + }); + + element = fixture('basic'); + }); + + teardown(() => { + sandbox.restore(); + }); test('dom for non available account', () => { assert.isFalse(element.hasAttribute('hidden')); - sandbox.stub(element, '_getConfig', () => { - return Promise.resolve({plugin: {has_avatars: true}}); - }); - // Emulate plugins loaded. Gerrit._setPluginsPending([]); @@ -149,45 +166,45 @@ assert.strictEqual(element.style.backgroundImage, ''); }); }); + }); - test('avatar config not set and account not set', () => { - assert.isFalse(element.hasAttribute('hidden')); + suite('config not set', () => { + let element; + let sandbox; - sandbox.stub(element, '_getConfig', () => { - return Promise.resolve({}); + setup(() => { + sandbox = sinon.sandbox.create(); + + stub('gr-avatar', { + _getConfig: () => { + return Promise.resolve({}); + }, }); - // Emulate plugins loaded. - Gerrit._setPluginsPending([]); - - return Promise.all([ - element.$.restAPI.getConfig(), - Gerrit.awaitPluginsLoaded(), - ]).then(() => { - assert.isTrue(element.hasAttribute('hidden')); - }); + element = fixture('basic'); }); - test('avatar config not set and account set', () => { - assert.isFalse(element.hasAttribute('hidden')); + teardown(() => { + sandbox.restore(); + }); - sandbox.stub(element, '_getConfig', () => { - return Promise.resolve({}); - }); + test('avatar hidden when account set', () => { + flush(() => { + assert.isFalse(element.hasAttribute('hidden')); - element.imageSize = 64; - element.account = { - _account_id: 123, - }; + element.imageSize = 64; + element.account = { + _account_id: 123, + }; + // Emulate plugins loaded. + Gerrit._setPluginsPending([]); - // Emulate plugins loaded. - Gerrit._setPluginsPending([]); - - return Promise.all([ - element.$.restAPI.getConfig(), - Gerrit.awaitPluginsLoaded(), - ]).then(() => { - assert.isTrue(element.hasAttribute('hidden')); + return Promise.all([ + element.$.restAPI.getConfig(), + Gerrit.awaitPluginsLoaded(), + ]).then(() => { + assert.isTrue(element.hasAttribute('hidden')); + }); }); }); });
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html index 76ff442..ad5c243 100644 --- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html +++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
@@ -84,6 +84,24 @@ padding: var(--padding, 4px 8px); @apply --gr-button; } + /* https://github.com/PolymerElements/paper-button/blob/2.x/paper-button.html */ + /* BEGIN: Copy from paper-button */ + paper-button[elevation="1"] { + @apply --paper-material-elevation-1; + } + paper-button[elevation="2"] { + @apply --paper-material-elevation-2; + } + paper-button[elevation="3"] { + @apply --paper-material-elevation-3; + } + paper-button[elevation="4"] { + @apply --paper-material-elevation-4; + } + paper-button[elevation="5"] { + @apply --paper-material-elevation-5; + } + /* END: Copy from paper-button */ paper-button:hover { background: linear-gradient( rgba(0, 0, 0, .12),
diff --git a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.html b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.html index c092b7c..574dc1e 100644 --- a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.html +++ b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard_test.html
@@ -39,12 +39,13 @@ let element; let sandbox; - setup(() => { + setup(done => { sandbox = sinon.sandbox.create(); element = fixture('basic'); element.text = `git fetch http://gerrit@localhost:8080/a/test-project refs/changes/05/5/1 && git checkout FETCH_HEAD`; flushAsynchronousOperations(); + flush(done); }); teardown(() => {
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.html b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.html index 3b7e8f8..85a5b1f 100644 --- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.html +++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands_test.html
@@ -67,12 +67,13 @@ }); suite('unauthenticated', () => { - setup(() => { + setup(done => { element = fixture('basic'); element.schemes = SCHEMES; element.commands = COMMANDS; element.selectedScheme = SELECTED_SCHEME; flushAsynchronousOperations(); + flush(done); }); test('focusOnCopy', () => { @@ -91,13 +92,13 @@ assert.isTrue(isHidden(element.$$('.commands'))); }); - test('tab selection', () => { + test('tab selection', done => { assert.equal(element.$.downloadTabs.selected, '0'); MockInteractions.tap(element.$$('[data-scheme="ssh"]')); flushAsynchronousOperations(); - assert.equal(element.selectedScheme, 'ssh'); assert.equal(element.$.downloadTabs.selected, '2'); + done(); }); test('loads scheme from preferences', done => {
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html index 5f07fc9..312cc34 100644 --- a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html +++ b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_test.html
@@ -94,77 +94,79 @@ ]; assert.equal(element.$$('paper-listbox').selected, element.value); assert.equal(element.text, 'Button Text 2'); - flushAsynchronousOperations(); - const items = Polymer.dom(element.root).querySelectorAll('paper-item'); - const mobileItems = Polymer.dom(element.root).querySelectorAll('option'); - assert.equal(items.length, 3); - assert.equal(mobileItems.length, 3); + flush(() => { + const items = Polymer.dom(element.root).querySelectorAll('paper-item'); + const mobileItems = Polymer.dom(element.root).querySelectorAll('option'); + assert.equal(items.length, 3); + assert.equal(mobileItems.length, 3); - // First Item - // The first item should be disabled, has no bottom text, and no date. - assert.isFalse(!!items[0].disabled); - assert.isFalse(mobileItems[0].disabled); - assert.isFalse(items[0].classList.contains('iron-selected')); - assert.isFalse(mobileItems[0].selected); + // First Item + // The first item should be disabled, has no bottom text, and no date. + assert.isFalse(!!items[0].disabled); + assert.isFalse(mobileItems[0].disabled); + assert.isFalse(items[0].classList.contains('iron-selected')); + assert.isFalse(mobileItems[0].selected); - assert.isNotOk(Polymer.dom(items[0]).querySelector('gr-date-formatter')); - assert.isNotOk(Polymer.dom(items[0]).querySelector('.bottomContent')); - assert.equal(items[0].value, element.items[0].value); - assert.equal(mobileItems[0].value, element.items[0].value); - assert.equal(Polymer.dom(items[0]).querySelector('.topContent div') - .innerText, element.items[0].text); + assert.isNotOk(Polymer.dom(items[0]).querySelector('gr-date-formatter')); + assert.isNotOk(Polymer.dom(items[0]).querySelector('.bottomContent')); + assert.equal(items[0].value, element.items[0].value); + assert.equal(mobileItems[0].value, element.items[0].value); + assert.equal(Polymer.dom(items[0]).querySelector('.topContent div') + .innerText, element.items[0].text); - // Since no mobile specific text, it should fall back to text. - assert.equal(mobileItems[0].text, element.items[0].text); + // Since no mobile specific text, it should fall back to text. + assert.equal(mobileItems[0].text, element.items[0].text); - // Second Item - // The second item should have top text, bottom text, and no date. - assert.isFalse(!!items[1].disabled); - assert.isFalse(mobileItems[1].disabled); - assert.isTrue(items[1].classList.contains('iron-selected')); - assert.isTrue(mobileItems[1].selected); + // Second Item + // The second item should have top text, bottom text, and no date. + assert.isFalse(!!items[1].disabled); + assert.isFalse(mobileItems[1].disabled); + assert.isTrue(items[1].classList.contains('iron-selected')); + assert.isTrue(mobileItems[1].selected); - assert.isNotOk(Polymer.dom(items[1]).querySelector('gr-date-formatter')); - assert.isOk(Polymer.dom(items[1]).querySelector('.bottomContent')); - assert.equal(items[1].value, element.items[1].value); - assert.equal(mobileItems[1].value, element.items[1].value); - assert.equal(Polymer.dom(items[1]).querySelector('.topContent div') - .innerText, element.items[1].text); + assert.isNotOk(Polymer.dom(items[1]).querySelector('gr-date-formatter')); + assert.isOk(Polymer.dom(items[1]).querySelector('.bottomContent')); + assert.equal(items[1].value, element.items[1].value); + assert.equal(mobileItems[1].value, element.items[1].value); + assert.equal(Polymer.dom(items[1]).querySelector('.topContent div') + .innerText, element.items[1].text); - // Since there is mobile specific text, it should that. - assert.equal(mobileItems[1].text, element.items[1].mobileText); + // Since there is mobile specific text, it should that. + assert.equal(mobileItems[1].text, element.items[1].mobileText); - // Since this item is selected, and it has triggerText defined, that - // should be used. - assert.equal(element.text, element.items[1].triggerText); + // Since this item is selected, and it has triggerText defined, that + // should be used. + assert.equal(element.text, element.items[1].triggerText); - // Third item - // The third item should be disabled, and have a date, and bottom content. - assert.isTrue(!!items[2].disabled); - assert.isTrue(mobileItems[2].disabled); - assert.isFalse(items[2].classList.contains('iron-selected')); - assert.isFalse(mobileItems[2].selected); + // Third item + // The third item should be disabled, and have a date, and bottom content. + assert.isTrue(!!items[2].disabled); + assert.isTrue(mobileItems[2].disabled); + assert.isFalse(items[2].classList.contains('iron-selected')); + assert.isFalse(mobileItems[2].selected); - assert.isOk(Polymer.dom(items[2]).querySelector('gr-date-formatter')); - assert.isOk(Polymer.dom(items[2]).querySelector('.bottomContent')); - assert.equal(items[2].value, element.items[2].value); - assert.equal(mobileItems[2].value, element.items[2].value); - assert.equal(Polymer.dom(items[2]).querySelector('.topContent div') - .innerText, element.items[2].text); + assert.isOk(Polymer.dom(items[2]).querySelector('gr-date-formatter')); + assert.isOk(Polymer.dom(items[2]).querySelector('.bottomContent')); + assert.equal(items[2].value, element.items[2].value); + assert.equal(mobileItems[2].value, element.items[2].value); + assert.equal(Polymer.dom(items[2]).querySelector('.topContent div') + .innerText, element.items[2].text); - // Since there is mobile specific text, it should that. - assert.equal(mobileItems[2].text, element.items[2].mobileText); + // Since there is mobile specific text, it should that. + assert.equal(mobileItems[2].text, element.items[2].mobileText); - // Select a new item. - MockInteractions.tap(items[0]); - flushAsynchronousOperations(); - assert.equal(element.value, 1); - assert.isTrue(items[0].classList.contains('iron-selected')); - assert.isTrue(mobileItems[0].selected); + // Select a new item. + MockInteractions.tap(items[0]); + flushAsynchronousOperations(); + assert.equal(element.value, 1); + assert.isTrue(items[0].classList.contains('iron-selected')); + assert.isTrue(mobileItems[0].selected); - // Since no triggerText, the fallback is used. - assert.equal(element.text, element.items[0].text); + // Since no triggerText, the fallback is used. + assert.equal(element.text, element.items[0].text); + done(); + }); }); }); </script>
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html index 5dad9a6..7ff0a14 100644 --- a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html +++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html
@@ -61,13 +61,17 @@ let label; let sandbox; - setup(() => { + setup(done => { element = fixture('basic'); elementNoPlaceholder = fixture('no-placeholder'); - input = element.$.input.$.input; label = element.$$('label'); sandbox = sinon.sandbox.create(); + flush(() => { + // In Polymer 2 inputElement isn't nativeInput anymore + input = element.$.input.$.nativeInput || element.$.input.inputElement; + done(); + }); }); teardown(() => { @@ -79,7 +83,7 @@ assert.isFalse(element.$.dropdown.opened); assert.isTrue(label.classList.contains('editable')); assert.equal(label.textContent, 'value text'); - const focusSpy = sandbox.spy(element.$.input.$.input, 'focus'); + const focusSpy = sandbox.spy(input, 'focus'); const showSpy = sandbox.spy(element, '_showDropdown'); MockInteractions.tap(label);
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.html index 2a6487e..3223636 100644 --- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.html +++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context_test.html
@@ -56,6 +56,7 @@ el.setAttribute('data-side', 'right'); lineNumberEl = document.createElement('td'); lineNumberEl.classList.add('right'); + document.body.appendChild(el); instance = new GrAnnotationActionsContext( el, lineNumberEl, line, 'dummy/path', '123', '1'); });
diff --git a/polygerrit-ui/app/elements/shared/gr-select/gr-select.js b/polygerrit-ui/app/elements/shared/gr-select/gr-select.js index ecf542f..333bede 100644 --- a/polygerrit-ui/app/elements/shared/gr-select/gr-select.js +++ b/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
@@ -66,7 +66,7 @@ ready() { // If not set via the property, set bind-value to the element value. - if (this.bindValue == undefined) { + if (this.bindValue == undefined && this.nativeSelect.options.length > 0) { this.bindValue = this.nativeSelect.value; } },
diff --git a/polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html b/polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html index 66ebb79..b3abe5f 100644 --- a/polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html +++ b/polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html
@@ -40,6 +40,15 @@ </template> </test-fixture> +<test-fixture id="noOptions"> + <template> + <gr-select> + <select> + </select> + </gr-select> + </template> +</test-fixture> + <script> suite('gr-select tests', () => { let element; @@ -48,6 +57,10 @@ element = fixture('basic'); }); + test('bindValue must be set to the first option value', () => { + assert.equal(element.bindValue, '1'); + }); + test('value of 0 should still trigger value updates', () => { element.bindValue = 0; assert.equal(element.nativeSelect.value, 0); @@ -90,4 +103,16 @@ assert.isTrue(changeStub.called); }); }); + + suite('gr-select no options tests', () => { + let element; + + setup(() => { + element = fixture('noOptions'); + }); + + test('bindValue must not be changed', () => { + assert.isUndefined(element.bindValue); + }); + }); </script>
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html index 8b6eff2..077f4b7 100644 --- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html +++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html
@@ -33,6 +33,18 @@ </template> </test-fixture> +<test-fixture id="monospace"> + <template> + <gr-textarea monospace="true"></gr-textarea> + </template> +</test-fixture> + +<test-fixture id="hideBorder"> + <template> + <gr-textarea hide-border="true"></gr-textarea> + </template> +</test-fixture> + <script> suite('gr-textarea tests', () => { let element; @@ -49,16 +61,10 @@ test('monospace is set properly', () => { assert.isFalse(element.classList.contains('monospace')); - element.monospace = true; - element.ready(); - assert.isTrue(element.classList.contains('monospace')); }); test('hideBorder is set properly', () => { assert.isFalse(element.$.textarea.classList.contains('noBorder')); - element.hideBorder = true; - element.ready(); - assert.isTrue(element.$.textarea.classList.contains('noBorder')); }); test('emoji selector is not open with the textarea lacks focus', () => { @@ -235,4 +241,52 @@ }); }); }); + + suite('gr-textarea monospace', () => { + // gr-textarea set monospace class in the ready() method. + // In Polymer2, ready() is called from the fixture(...) method, + // If ready() is called again later, some nested elements doesn't + // handle it correctly. A separate test-fixture is used to set + // properties before ready() is called. + + let element; + let sandbox; + + setup(() => { + sandbox = sinon.sandbox.create(); + element = fixture('monospace'); + }); + + teardown(() => { + sandbox.restore(); + }); + + test('monospace is set properly', () => { + assert.isTrue(element.classList.contains('monospace')); + }); + }); + + suite('gr-textarea hideBorder', () => { + // gr-textarea set noBorder class in the ready() method. + // In Polymer2, ready() is called from the fixture(...) method, + // If ready() is called again later, some nested elements doesn't + // handle it correctly. A separate test-fixture is used to set + // properties before ready() is called. + + let element; + let sandbox; + + setup(() => { + sandbox = sinon.sandbox.create(); + element = fixture('hideBorder'); + }); + + teardown(() => { + sandbox.restore(); + }); + + test('hideBorder is set properly', () => { + assert.isTrue(element.$.textarea.classList.contains('noBorder')); + }); + }); </script>
diff --git a/polygerrit-ui/app/scripts/util.js b/polygerrit-ui/app/scripts/util.js index a6ada82..672c43f 100644 --- a/polygerrit-ui/app/scripts/util.js +++ b/polygerrit-ui/app/scripts/util.js
@@ -95,5 +95,38 @@ return style; }; + /** + * Query selector on a dom element. + * + * This is shadow DOM compatible, but only works when selector is within + * one shadow host, won't work if your selector is crossing + * multiple shadow hosts. + * + */ + util.querySelector = (el, selector) => { + let nodes = [el]; + let element = null; + while (nodes.length) { + const node = nodes.pop(); + + // Skip if it's an invalid node. + if (!node || !node.querySelector) continue; + + // Try find it with native querySelector directly + element = node.querySelector(selector); + + if (element) { + break; + } else if (node.shadowRoot) { + // If shadowHost detected, add the host and its children + nodes = nodes.concat(Array.from(node.children)); + nodes.push(node.shadowRoot); + } else { + nodes = nodes.concat(Array.from(node.children)); + } + } + return element; + }; + window.util = util; })(window);
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html index f5a64dc..75380dd 100644 --- a/polygerrit-ui/app/test/index.html +++ b/polygerrit-ui/app/test/index.html
@@ -100,7 +100,6 @@ 'core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog_test.html', 'core/gr-main-header/gr-main-header_test.html', 'core/gr-navigation/gr-navigation_test.html', - 'core/gr-reporting/gr-jank-detector_test.html', 'core/gr-reporting/gr-reporting_test.html', 'core/gr-router/gr-router_test.html', 'core/gr-search-bar/gr-search-bar_test.html',
diff --git a/proto/cache.proto b/proto/cache.proto index 77b6908..7e6abcc 100644 --- a/proto/cache.proto +++ b/proto/cache.proto
@@ -186,6 +186,9 @@ // Number of updates to the change's meta ref. int32 update_count = 19; + + string server_id = 20; + bool has_server_id = 21; }
diff --git a/tools/BUILD b/tools/BUILD index 5531c3e..208a7fcb 100644 --- a/tools/BUILD +++ b/tools/BUILD
@@ -15,6 +15,19 @@ visibility = ["//visibility:public"], ) +# TODO(davido): Remove this workaround and switch to using toolchain_vanilla +# from this Bazel package again: @bazel_tools//tools/jdk:toolchain_vanilla, +# when this issue is fixed: https://github.com/bazelbuild/bazel/issues/9415. +default_java_toolchain( + name = "toolchain_vanilla", + forcibly_disable_header_compilation = True, + javabuilder = ["@bazel_tools//tools/jdk:vanillajavabuilder"], + jvm_opts = [], + source_version = "", + target_version = "", + visibility = ["//visibility:public"], +) + default_java_toolchain( name = "error_prone_warnings_toolchain", bootclasspath = ["@bazel_tools//tools/jdk:platformclasspath.jar"],