Merge branch 'stable-3.3'
* stable-3.3:
Fix test due to change in Gerrit core
Index change when combined state changes
ChecksSubmitRule.Module: Demote base class to AbstractModule
Change-Id: Ie14245216b2a73f6c82ab242b89c4234e9bb408f
diff --git a/gr-checks/gr-checks-all-statuses.js b/gr-checks/gr-checks-all-statuses.js
index bdaa06f..2aaed0b 100644
--- a/gr-checks/gr-checks-all-statuses.js
+++ b/gr-checks/gr-checks-all-statuses.js
@@ -44,7 +44,15 @@
* @returns {boolean} if status is Unevaluated.
*/
export function isUnevaluated(status) {
- return isStatus(status, [Statuses.NOT_STARTED, Statuses.NOT_RELEVANT]);
+ return isStatus(status, [Statuses.NOT_STARTED]);
+}
+
+/**
+ * @param {string} status
+ * @returns {boolean} if status is Not Relevant
+ */
+export function isNotRelevant(status) {
+ return isStatus(status, [Statuses.NOT_RELEVANT]);
}
/**
diff --git a/gr-checks/gr-checks-item.js b/gr-checks/gr-checks-item.js
index 494a330..40c4d77 100644
--- a/gr-checks/gr-checks-item.js
+++ b/gr-checks/gr-checks-item.js
@@ -225,21 +225,21 @@
if (secondsAgo % 60 !== 0) {
durationSegments.push(`${secondsAgo % 60} sec`);
}
- const minutesAgo = Math.round(secondsAgo / 60);
+ const minutesAgo = Math.floor(secondsAgo / 60);
if (minutesAgo % 60 !== 0) {
durationSegments.push(`${minutesAgo % 60} min`);
}
- const hoursAgo = Math.round(minutesAgo / 60);
+ const hoursAgo = Math.floor(minutesAgo / 60);
if (hoursAgo % 24 !== 0) {
const hours = pluralize(hoursAgo % 24, 'hour', 'hours');
durationSegments.push(`${hoursAgo % 24} ${hours}`);
}
- const daysAgo = Math.round(hoursAgo / 24);
+ const daysAgo = Math.floor(hoursAgo / 24);
if (daysAgo % 30 !== 0) {
const days = pluralize(daysAgo % 30, 'day', 'days');
durationSegments.push(`${daysAgo % 30} ${days}`);
}
- const monthsAgo = Math.round(daysAgo / 30);
+ const monthsAgo = Math.floor(daysAgo / 30);
if (monthsAgo > 0) {
const months = pluralize(monthsAgo, 'month', 'months');
durationSegments.push(`${monthsAgo} ${months}`);
@@ -255,4 +255,4 @@
*/
function pluralize(unit, singular, plural) {
return unit === 1 ? singular : plural;
-}
\ No newline at end of file
+}
diff --git a/gr-checks/gr-checks-item_test.html b/gr-checks/gr-checks-item_test.html
index e9ce401..305d965 100644
--- a/gr-checks/gr-checks-item_test.html
+++ b/gr-checks/gr-checks-item_test.html
@@ -79,6 +79,42 @@
.querySelector(`td:nth-of-type(${idx})`);
assert.equal(name.textContent.trim(), '0 sec');
});
+
+ test('renders > 1m correctly', () => {
+ element.check = {
+ checkId: 'test-check-id',
+ started: '2019-02-06T22:25:19.269Z',
+ finished: '2019-02-06T22:45:29.269Z',
+ };
+ const idx = CHECKS_ITEM.DURATION;
+ const name = element.shadowRoot
+ .querySelector(`td:nth-of-type(${idx})`);
+ assert.equal(name.textContent.trim(), '20 min 10 sec');
+ });
+
+ test('renders > 1h correctly', () => {
+ element.check = {
+ checkId: 'test-check-id',
+ started: '2019-02-06T22:25:19.269Z',
+ finished: '2019-02-06T23:45:29.269Z',
+ };
+ const idx = CHECKS_ITEM.DURATION;
+ const name = element.shadowRoot
+ .querySelector(`td:nth-of-type(${idx})`);
+ assert.equal(name.textContent.trim(), '1 hour 20 min');
+ });
+
+ test('renders > 1d correctly', () => {
+ element.check = {
+ checkId: 'test-check-id',
+ started: '2019-02-06T22:25:19.269Z',
+ finished: '2019-02-07T23:45:29.269Z',
+ };
+ const idx = CHECKS_ITEM.DURATION;
+ const name = element.shadowRoot
+ .querySelector(`td:nth-of-type(${idx})`);
+ assert.equal(name.textContent.trim(), '1 day 1 hour');
+ });
});
test('renders a link to the log', () => {
diff --git a/gr-checks/gr-checks-reboot.js b/gr-checks/gr-checks-reboot.js
new file mode 100644
index 0000000..1e7bdc2
--- /dev/null
+++ b/gr-checks/gr-checks-reboot.js
@@ -0,0 +1,164 @@
+/**
+ * @license
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Heads up! Everything in this file is still in flux. The new reboot checks API
+ * is still in development. So everything in this file can change. And it is
+ * expected that the amount of comments and tests is limited for the time being.
+ */
+
+function pluralize(count, noun) {
+ if (count === 0) return '';
+ return `${count} ${noun}` + (count > 1 ? 's' : '');
+}
+
+function generateDurationString(startTime, endTime) {
+ const secondsAgo = Math.round((endTime - startTime) / 1000);
+
+ if (secondsAgo === 0) {
+ return ZERO_SECONDS;
+ }
+
+ const durationSegments = [];
+ if (secondsAgo % 60 !== 0) {
+ durationSegments.push(`${secondsAgo % 60} sec`);
+ }
+ const minutesAgo = Math.floor(secondsAgo / 60);
+ if (minutesAgo % 60 !== 0) {
+ durationSegments.push(`${minutesAgo % 60} min`);
+ }
+ const hoursAgo = Math.floor(minutesAgo / 60);
+ if (hoursAgo % 24 !== 0) {
+ const hours = pluralize(hoursAgo % 24, 'hour', 'hours');
+ durationSegments.push(`${hours}`);
+ }
+ const daysAgo = Math.floor(hoursAgo / 24);
+ if (daysAgo % 30 !== 0) {
+ const days = pluralize(daysAgo % 30, 'day', 'days');
+ durationSegments.push(`${days}`);
+ }
+ const monthsAgo = Math.floor(daysAgo / 30);
+ if (monthsAgo > 0) {
+ const months = pluralize(monthsAgo, 'month', 'months');
+ durationSegments.push(`${months}`);
+ }
+ return durationSegments.reverse().slice(0, 2).join(' ');
+}
+
+function computeDuration(check) {
+ if (!check.started || !check.finished) {
+ return '-';
+ }
+ const startTime = new Date(check.started);
+ const finishTime = check.finished ? new Date(check.finished) : new Date();
+ return generateDurationString(startTime, finishTime);
+}
+
+export class RebootFetcher {
+ constructor(restApi) {
+ this.restApi = restApi;
+ }
+
+ async fetchCurrent() {
+ return this.fetch(this.changeNumber, this.patchsetNumber);
+ }
+
+ async fetch(changeData) {
+ const {changeNumber, patchsetNumber} = changeData;
+ this.changeNumber = changeNumber;
+ this.patchsetNumber = patchsetNumber;
+ const checks = await this.apiGet('?o=CHECKER');
+ return {
+ responseCode: 'OK',
+ runs: checks.map(check => this.convert(check)),
+ };
+ }
+
+ async apiGet(suffix) {
+ return this.restApi.get(
+ '/changes/' + this.changeNumber + '/revisions/' + this.patchsetNumber
+ + '/checks' + suffix);
+ }
+
+ async apiPost(suffix) {
+ return this.restApi.post(
+ '/changes/' + this.changeNumber + '/revisions/' + this.patchsetNumber
+ + '/checks' + suffix);
+ }
+
+ /**
+ * Converts a Checks Plugin CheckInfo object into a Reboot Checks API Run
+ * object.
+ *
+ * TODO(brohlfs): Refine this conversion and add tests.
+ */
+ convert(check) {
+ let status = 'RUNNABLE';
+ if (check.state === 'RUNNING' || check.state === 'SCHEDULED') {
+ status = 'RUNNING';
+ } else if (check.state === 'FAILED' || check.state === 'SUCCESSFUL') {
+ status = 'COMPLETED';
+ }
+ const run = {
+ checkName: check.checker_name,
+ checkDescription: check.checker_description,
+ externalId: check.checker_uuid,
+ status,
+ };
+ if (check.started) run.startedTimestamp = new Date(check.started);
+ if (check.finished) run.finishedTimestamp = new Date(check.finished);
+ if (status === 'RUNNING') {
+ run.statusDescription = check.message;
+ if (check.url) {
+ run.statusLink = check.url;
+ }
+ } else if (check.state === 'SUCCESSFUL') {
+ run.statusDescription =
+ check.message || `Passed (${computeDuration(check)})`;
+ if (check.url) {
+ run.statusLink = check.url;
+ }
+ } else if (check.state === 'FAILED') {
+ run.results = [{
+ category: 'ERROR',
+ summary: check.message || `Failed (${computeDuration(check)})`,
+ }];
+ if (check.url) {
+ run.results[0].links = [{
+ url: check.url,
+ primary: true,
+ icon: 'EXTERNAL',
+ }];
+ }
+ }
+ if (status !== 'RUNNING') {
+ run.actions = [{
+ name: 'Run',
+ primary: true,
+ callback: () => this.run(check.checker_uuid),
+ }];
+ }
+ return run;
+ }
+
+ run(uuid) {
+ return this.apiPost('/' + uuid + '/rerun')
+ .catch(e => {
+ return {errorMessage: `Triggering the run failed: ${e.message}`};
+ });
+ }
+}
diff --git a/gr-checks/gr-checks-status.js b/gr-checks/gr-checks-status.js
index 3a22f2e..557e81c 100644
--- a/gr-checks/gr-checks-status.js
+++ b/gr-checks/gr-checks-status.js
@@ -15,7 +15,7 @@
* limitations under the License.
*/
import {htmlTemplate} from './gr-checks-status_html.js';
-import {isUnevaluated, isInProgress, isRunning, isScheduled, isSuccessful, isFailed} from './gr-checks-all-statuses.js';
+import {isNotRelevant, isUnevaluated, isInProgress, isRunning, isScheduled, isSuccessful, isFailed} from './gr-checks-all-statuses.js';
class GrChecksStatus extends Polymer.GestureEventListeners(
Polymer.LegacyElementMixin(
@@ -43,6 +43,10 @@
};
}
+ _isNotRelevant(status) {
+ return isNotRelevant(status);
+ }
+
_isUnevaluated(status) {
return isUnevaluated(status);
}
diff --git a/gr-checks/gr-checks-status_html.js b/gr-checks/gr-checks-status_html.js
index 0325b95..5cca639 100644
--- a/gr-checks/gr-checks-status_html.js
+++ b/gr-checks/gr-checks-status_html.js
@@ -36,6 +36,19 @@
}
</style>
<span>
+ <template is="dom-if" if="[[_isNotRelevant(status)]]">
+ <svg width="18" height="18" xmlns="http://www.w3.org/2000/svg">
+ <g fill="none" fill-rule="evenodd">
+ <path d="M0 0h18v18H0z"/>
+ <path d="M9 11.8a2.8 2.8 0 1 1 0-5.6 2.8 2.8 0 0 1 0 5.6M9 2a7 7 0 1 0 0 14A7 7 0 0 0 9 2" fill="#9E9E9E"/>
+ </g>
+ </svg>
+ <template is="dom-if" if="[[showText]]">
+ <span>
+ Not relevant
+ </span>
+ </template>
+ </template>
<template is="dom-if" if="[[_isUnevaluated(status)]]">
<svg width="18" height="18" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd">
diff --git a/gr-checks/gr-checks.js b/gr-checks/gr-checks.js
index b8c2920..39152f5 100644
--- a/gr-checks/gr-checks.js
+++ b/gr-checks/gr-checks.js
@@ -21,8 +21,9 @@
import './gr-checks-change-list-item-cell-view.js';
import './gr-checks-item.js';
import './gr-checks-status.js';
+import {RebootFetcher} from './gr-checks-reboot.js';
-Gerrit.install(plugin => {
+function installChecksLegacy(plugin) {
const getChecks = (change, revision) => {
return plugin.restApi().get(
'/changes/' + change + '/revisions/' + revision + '/checks?o=CHECKER');
@@ -54,4 +55,24 @@
view['isConfigured'] = repository => Promise.resolve(true);
view['getChecks'] = getChecks;
});
-});
\ No newline at end of file
+}
+
+function installChecksReboot(plugin) {
+ const checksApi = plugin.checks();
+ const fetcher = new RebootFetcher(plugin.restApi());
+ checksApi.register({
+ fetch: (data) => fetcher.fetch(data)
+ });
+}
+
+Gerrit.install(plugin => {
+ const experiments = window.ENABLED_EXPERIMENTS || [];
+ if (experiments.includes("UiFeature__ci_reboot_checks_checks")) {
+ // Until end of 2020 this is only interesting for developing purposes. So
+ // no real user is affected for the time being.
+ console.log('Installing checks REBOOT plugin.');
+ installChecksReboot(plugin);
+ } else {
+ installChecksLegacy(plugin);
+ }
+});
diff --git a/java/com/google/gerrit/plugins/checks/Module.java b/java/com/google/gerrit/plugins/checks/Module.java
index 3431ec1..4787df6 100644
--- a/java/com/google/gerrit/plugins/checks/Module.java
+++ b/java/com/google/gerrit/plugins/checks/Module.java
@@ -31,8 +31,8 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.UserInitiated;
-import com.google.gerrit.server.change.ChangeAttributeFactory;
import com.google.gerrit.server.change.ChangeETagComputation;
+import com.google.gerrit.server.change.ChangePluginDefinedInfoFactory;
import com.google.gerrit.server.git.validators.CommitValidationListener;
import com.google.gerrit.server.git.validators.MergeValidationListener;
import com.google.gerrit.server.git.validators.RefOperationValidationListener;
@@ -67,7 +67,8 @@
.to(ChecksETagComputation.class)
.in(SINGLETON);
- DynamicSet.bind(binder(), ChangeAttributeFactory.class).to(ChangeCheckAttributeFactory.class);
+ DynamicSet.bind(binder(), ChangePluginDefinedInfoFactory.class)
+ .to(ChangeCheckAttributeFactory.class);
bind(DynamicOptions.DynamicBean.class)
.annotatedWith(Exports.named(GetChange.class))
.to(GetChangeOptions.class);
diff --git a/java/com/google/gerrit/plugins/checks/api/ChangeCheckAttributeFactory.java b/java/com/google/gerrit/plugins/checks/api/ChangeCheckAttributeFactory.java
index 9c48851..f456279 100644
--- a/java/com/google/gerrit/plugins/checks/api/ChangeCheckAttributeFactory.java
+++ b/java/com/google/gerrit/plugins/checks/api/ChangeCheckAttributeFactory.java
@@ -14,13 +14,22 @@
package com.google.gerrit.plugins.checks.api;
+import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.extensions.common.PluginDefinedInfo;
import com.google.gerrit.plugins.checks.CombinedCheckStateCache;
import com.google.gerrit.server.DynamicOptions.BeanProvider;
import com.google.gerrit.server.DynamicOptions.DynamicBean;
-import com.google.gerrit.server.change.ChangeAttributeFactory;
+import com.google.gerrit.server.change.ChangePluginDefinedInfoFactory;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+import java.util.AbstractMap;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Collection;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
import org.kohsuke.args4j.Option;
/**
@@ -28,7 +37,7 @@
* a {@code ChangeInfo}.
*/
@Singleton
-public class ChangeCheckAttributeFactory implements ChangeAttributeFactory {
+public class ChangeCheckAttributeFactory implements ChangePluginDefinedInfoFactory {
private static final String COMBINED_OPTION_NAME = "--combined";
private static final String COMBINED_OPTION_USAGE = "include combined check state";
@@ -50,19 +59,37 @@
}
@Override
- public ChangeCheckInfo create(ChangeData cd, BeanProvider beanProvider, String plugin) {
+ public Map<Change.Id, PluginDefinedInfo> createPluginDefinedInfos(
+ Collection<ChangeData> cds, BeanProvider beanProvider, String plugin) {
DynamicBean opts = beanProvider.getDynamicBean(plugin);
if (opts == null) {
- return null;
+ return ImmutableMap.of();
}
if (opts instanceof GetChangeOptions) {
- return forGetChange(cd, (GetChangeOptions) opts);
+ return evalAndCollect(
+ cds,
+ cd ->
+ new AbstractMap.SimpleImmutableEntry<>(
+ cd.getId(), forGetChange(cd, (GetChangeOptions) opts)));
} else if (opts instanceof QueryChangesOptions) {
- return forQueryChanges(cd, (QueryChangesOptions) opts);
+ return evalAndCollect(
+ cds,
+ cd ->
+ new AbstractMap.SimpleImmutableEntry<>(
+ cd.getId(), forQueryChanges(cd, (QueryChangesOptions) opts)));
}
throw new IllegalStateException("unexpected options type: " + opts);
}
+ private Map<Change.Id, PluginDefinedInfo> evalAndCollect(
+ Collection<ChangeData> cds,
+ Function<ChangeData, SimpleImmutableEntry<Change.Id, ChangeCheckInfo>> transformFn) {
+ return cds.stream()
+ .map(transformFn)
+ .filter(e -> e.getKey() != null && e.getValue() != null)
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+
private ChangeCheckInfo forGetChange(ChangeData cd, GetChangeOptions opts) {
if (opts == null || !opts.combined) {
return null;
diff --git a/javatests/com/google/gerrit/plugins/checks/acceptance/CheckerRefsIT.java b/javatests/com/google/gerrit/plugins/checks/acceptance/CheckerRefsIT.java
index abb015a..df918fa 100644
--- a/javatests/com/google/gerrit/plugins/checks/acceptance/CheckerRefsIT.java
+++ b/javatests/com/google/gerrit/plugins/checks/acceptance/CheckerRefsIT.java
@@ -30,7 +30,6 @@
import com.google.gerrit.entities.Permission;
import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.ChangeStatus;
-import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.plugins.checks.CheckerRef;
import com.google.gerrit.plugins.checks.CheckerUuid;
@@ -305,52 +304,6 @@
r.assertOkStatus();
}
- @Test
- public void createChangeForCheckerRefsViaApiIsDisabled() throws Exception {
- CheckerUuid checkerUuid = checkerOperations.newChecker().create();
- String checkerRef = checkerUuid.toRefName();
-
- TestRepository<InMemoryRepository> repo = cloneProject(allProjects, admin);
- fetch(repo, checkerRef + ":checkerRef");
- repo.reset("checkerRef");
- RevCommit head = getHead(repo.getRepository(), "HEAD");
-
- ChangeInput input = new ChangeInput();
- input.project = allProjects.get();
- input.branch = checkerRef;
- input.baseCommit = head.name();
- input.subject = "A change.";
-
- ResourceConflictException thrown =
- assertThrows(ResourceConflictException.class, () -> gApi.changes().create(input));
- assertThat(thrown).hasMessageThat().contains("creating change for checker ref not allowed");
- }
-
- @Test
- public void createChangeForCheckerLikeRefViaApi() throws Exception {
- String checkerRef = CheckerUuid.parse("foo:bar").toRefName();
-
- projectOperations
- .project(project)
- .forUpdate()
- .add(allow(Permission.CREATE).ref(checkerRef).group(adminGroupUuid()))
- .update();
- createBranch(BranchNameKey.create(project, checkerRef));
-
- TestRepository<InMemoryRepository> repo = cloneProject(project, admin);
- fetch(repo, checkerRef + ":checkerRef");
- repo.reset("checkerRef");
- RevCommit head = getHead(repo.getRepository(), "HEAD");
-
- // creating a change on a checker ref via API should work in any project except All-Projects
- ChangeInput input = new ChangeInput();
- input.project = project.get();
- input.branch = checkerRef;
- input.baseCommit = head.name();
- input.subject = "A change.";
- assertThat(gApi.changes().create(input).get()).isNotNull();
- }
-
private String createChangeWithoutCommitValidation(Project.NameKey project, String targetRef)
throws Exception {
try (Repository git = repoManager.openRepository(project);
diff --git a/javatests/com/google/gerrit/plugins/checks/acceptance/api/CreateCheckIT.java b/javatests/com/google/gerrit/plugins/checks/acceptance/api/CreateCheckIT.java
index b1e0273..9c554ed 100644
--- a/javatests/com/google/gerrit/plugins/checks/acceptance/api/CreateCheckIT.java
+++ b/javatests/com/google/gerrit/plugins/checks/acceptance/api/CreateCheckIT.java
@@ -383,21 +383,6 @@
assertThat(newETag).isNotEqualTo(oldETag);
}
- @Test
- public void creationOfCheckChangesETagOfRevisionActions() throws Exception {
- CheckerUuid checkerUuid = checkerOperations.newChecker().repository(project).create();
-
- String oldETag = gApi.changes().id(patchSetId.changeId().toString()).current().etag();
-
- CheckInput input = new CheckInput();
- input.checkerUuid = checkerUuid.get();
- input.state = CheckState.RUNNING;
- checksApiFactory.revision(patchSetId).create(input).get();
-
- String newETag = gApi.changes().id(patchSetId.changeId().toString()).current().etag();
- assertThat(newETag).isNotEqualTo(oldETag);
- }
-
// TODO(gerrit-team) More tests, especially for multiple checkers and PS and how commits behave
private Check getCheck(Project.NameKey project, PatchSet.Id patchSetId, CheckerUuid checkerUuid)
diff --git a/javatests/com/google/gerrit/plugins/checks/acceptance/api/UpdateCheckIT.java b/javatests/com/google/gerrit/plugins/checks/acceptance/api/UpdateCheckIT.java
index c763abc..3e16afc 100644
--- a/javatests/com/google/gerrit/plugins/checks/acceptance/api/UpdateCheckIT.java
+++ b/javatests/com/google/gerrit/plugins/checks/acceptance/api/UpdateCheckIT.java
@@ -443,30 +443,4 @@
String newETag = parseChangeResource(patchSetId.changeId().toString()).getETag();
assertThat(newETag).isEqualTo(oldETag);
}
-
- @Test
- public void updateOfCheckChangesETagOfRevisionActions() throws Exception {
- String oldETag = gApi.changes().id(patchSetId.changeId().toString()).current().etag();
-
- CheckInput input = new CheckInput();
- input.state = CheckState.FAILED;
- checksApiFactory.revision(patchSetId).id(checkKey.checkerUuid()).update(input);
-
- String newETag = gApi.changes().id(patchSetId.changeId().toString()).current().etag();
- assertThat(newETag).isNotEqualTo(oldETag);
- }
-
- @Test
- public void noOpUpdateOfCheckDoesNotChangeETagOfRevisionActions() throws Exception {
- CheckInput input = new CheckInput();
- input.state = CheckState.FAILED;
- checksApiFactory.revision(patchSetId).id(checkKey.checkerUuid()).update(input);
-
- String oldETag = gApi.changes().id(patchSetId.changeId().toString()).current().etag();
-
- checksApiFactory.revision(patchSetId).id(checkKey.checkerUuid()).update(input);
-
- String newETag = gApi.changes().id(patchSetId.changeId().toString()).current().etag();
- assertThat(newETag).isEqualTo(oldETag);
- }
}
diff --git a/resources/com/google/gerrit/plugins/checks/email/CombinedCheckStateUpdated.soy b/resources/com/google/gerrit/plugins/checks/email/CombinedCheckStateUpdated.soy
index 5e914ed..cd57453 100644
--- a/resources/com/google/gerrit/plugins/checks/email/CombinedCheckStateUpdated.soy
+++ b/resources/com/google/gerrit/plugins/checks/email/CombinedCheckStateUpdated.soy
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-{namespace com.google.gerrit.server.mail.template}
+{namespace com.google.gerrit.server.mail.template.CombinedCheckStateUpdated}
/**
* The .CombinedCheckStateUpdated template will determine the contents of the email related to a
diff --git a/resources/com/google/gerrit/plugins/checks/email/CombinedCheckStateUpdatedHtml.soy b/resources/com/google/gerrit/plugins/checks/email/CombinedCheckStateUpdatedHtml.soy
index 8feb6ff..0f35c40 100644
--- a/resources/com/google/gerrit/plugins/checks/email/CombinedCheckStateUpdatedHtml.soy
+++ b/resources/com/google/gerrit/plugins/checks/email/CombinedCheckStateUpdatedHtml.soy
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-{namespace com.google.gerrit.server.mail.template}
+{namespace com.google.gerrit.server.mail.template.CombinedCheckStateUpdatedHtml}
+
+import * as mailTemplate from 'com/google/gerrit/server/mail/Private.soy';
/**
* The .CombinedCheckStateUpdatedHtml template will determine the contents of the email related to
@@ -91,7 +93,7 @@
{if $email.changeUrl}
<p>
- {call .ViewChangeButton data="all" /}
+ {call mailTemplate.ViewChangeButton data="all" /}
</p>
{/if}
{/template}