/* * Copyright 2000-2018 JetBrains s.r.o. * * 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 jetbrains.buildServer.server.rest.model.problem; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; import jetbrains.buildServer.server.rest.data.Locator; import jetbrains.buildServer.server.rest.data.problem.TestOccurrenceFinder; import jetbrains.buildServer.server.rest.model.Fields; import jetbrains.buildServer.server.rest.model.build.Build; import jetbrains.buildServer.server.rest.request.TestOccurrenceRequest; import jetbrains.buildServer.server.rest.util.BeanContext; import jetbrains.buildServer.server.rest.util.ValueWithDefault; import jetbrains.buildServer.serverSide.*; import jetbrains.buildServer.serverSide.mute.MuteInfo; import org.jetbrains.annotations.NotNull; import static jetbrains.buildServer.serverSide.BuildStatisticsOptions.ALL_TESTS_NO_DETAILS; /** * @author Yegor.Yarko */ @SuppressWarnings({"PublicField", "WeakerAccess"}) @XmlRootElement(name = "testOccurrence") @XmlType(name = "testOccurrence", propOrder = {"id", "name", "status", "ignored", "duration", "runOrder"/*experimental*/, "muted", "currentlyMuted", "currentlyInvestigated", "href", "ignoreDetails", "details", "test", "mute", "build", "firstFailed", "nextFixed", "invocations", "metadata"}) public class TestOccurrence { @XmlAttribute public String id; @XmlAttribute public String name; @XmlAttribute public String runOrder; /*experimental*/ @XmlAttribute public String status; @XmlAttribute public Boolean ignored; @XmlAttribute public String href; @XmlAttribute public Integer duration;//test run duration in milliseconds /** * Experimental! "true" is the test occurrence was muted, not present otherwise */ @XmlAttribute public Boolean muted; /** * Experimental! "true" is the test has investigation at the moment of request, not present otherwise */ @XmlAttribute public Boolean currentlyInvestigated; /** * Experimental! "true" is the test is muted at the moment of request, not present otherwise */ @XmlAttribute public Boolean currentlyMuted; @XmlElement public String ignoreDetails; @XmlElement public String details; //todo: consider using CDATA output here @XmlElement public Test test; @XmlElement public Mute mute; @XmlElement public Build build; @XmlElement public TestOccurrence firstFailed; @XmlElement public TestOccurrence nextFixed; @XmlElement public TestOccurrences invocations; /** * Experimental! Exposes test run metadata */ @XmlElement public TestRunMetadata metadata; public TestOccurrence() { } public TestOccurrence(final @NotNull STestRun testRun, final @NotNull BeanContext beanContext, @NotNull final Fields fields) { final STest sTest = testRun.getTest(); //STestRun.getTestRunId() can be the same between different builds id = ValueWithDefault.decideDefault(fields.isIncluded("id"), TestOccurrenceFinder.getTestRunLocator(testRun)); name = ValueWithDefault.decideDefault(fields.isIncluded("name"), sTest.getName().getAsString()); status = ValueWithDefault.decideDefault(fields.isIncluded("status"), testRun.getStatus().getText()); href = ValueWithDefault.decideDefault(fields.isIncluded("href"), beanContext.getApiUrlBuilder().transformRelativePath(TestOccurrenceRequest.getHref(testRun))); duration = ValueWithDefault.decideDefault(fields.isIncluded("duration"), testRun.getDuration()); runOrder = ValueWithDefault.decideDefault(fields.isIncluded("runOrder", false, false), String.valueOf(testRun.getOrderId())); ignored = ValueWithDefault.decideDefault(fields.isIncluded("ignored"), testRun.isIgnored()); final MuteInfo muteInfo = testRun.getMuteInfo(); muted = ValueWithDefault.decideDefault(fields.isIncluded("muted"), muteInfo != null); final TestOccurrenceFinder testOccurrenceFinder = beanContext.getSingletonService(TestOccurrenceFinder.class); currentlyInvestigated = ValueWithDefault.decideDefault(fields.isIncluded("currentlyInvestigated"), new ValueWithDefault.Value() { public Boolean get() { return testOccurrenceFinder.isCurrentlyInvestigated(testRun); } }); currentlyMuted = ValueWithDefault.decideDefault(fields.isIncluded("currentlyMuted"), new ValueWithDefault.Value() { public Boolean get() { return testOccurrenceFinder.isCurrentlyMuted(testRun); } }); details = ValueWithDefault.decideDefault(fields.isIncluded("details", false), new ValueWithDefault.Value() { public String get() { return testRun.getFullText(); } }); //consider providing separate stacktrace, stdout and stderr, see implementation of jetbrains.buildServer.serverSide.stat.TestFullTextBuilderImpl.getFullText() ignoreDetails = ValueWithDefault.decideDefault(fields.isIncluded("ignoreDetails", false), new ValueWithDefault.Value() { public String get() { return testRun.getIgnoreComment(); } }); test = ValueWithDefault.decideDefault(fields.isIncluded("test", false), new ValueWithDefault.Value() { public Test get() { return new Test(sTest, beanContext, fields.getNestedField("test")); } }); mute = muteInfo == null ? null : ValueWithDefault.decideDefault(fields.isIncluded("mute", false), new ValueWithDefault.Value() { public Mute get() { return new Mute(muteInfo, fields.getNestedField("mute", Fields.NONE, Fields.LONG), beanContext); } }); build = ValueWithDefault.decideDefault(fields.isIncluded("build", false), new ValueWithDefault.Value() { public Build get() { return new Build(testRun.getBuild(), fields.getNestedField("build"), beanContext); } }); firstFailed = ValueWithDefault.decideDefault(fields.isIncluded("firstFailed", false), new ValueWithDefault.Value() { public TestOccurrence get() { final SBuild firstFailedInBuild = testRun.getFirstFailed(); return firstFailedInBuild == null ? null : new TestOccurrence(getTestRun(firstFailedInBuild, testRun), beanContext, fields.getNestedField("firstFailed")); } }); nextFixed = ValueWithDefault.decideDefault(fields.isIncluded("nextFixed", false), new ValueWithDefault.Value() { public TestOccurrence get() { final SBuild fixedInBuild = testRun.getFixedIn(); return fixedInBuild == null ? null : new TestOccurrence(getTestRun(fixedInBuild, testRun), beanContext, fields.getNestedField("firstFailed")); } }); invocations = ValueWithDefault.decideDefault(fields.isIncluded("invocations", false, false), new ValueWithDefault.Value() { public TestOccurrences get() { if (!(testRun instanceof MultiTestRun)) return null; Fields nestedField = fields.getNestedField("invocations"); String invocationsLocator = Locator.merge(TestOccurrenceFinder.getTestInvocationsLocator(testRun), nestedField.getLocator()); return new TestOccurrences(testOccurrenceFinder.getItems(invocationsLocator).myEntries, testRun.getInvocationCount(), null, testRun.getFailedInvocationCount(), null, null, null, null, null, nestedField, beanContext); } }); metadata = ValueWithDefault.decideDefaultIgnoringAccessDenied(fields.isIncluded("metadata", false, false), () -> new TestRunMetadata(((TestRunEx)testRun).getMetadata(), fields.getNestedField("metadata", Fields.SHORT, Fields.LONG))); } @NotNull private STestRun getTestRun(@NotNull final SBuild build, @NotNull final STestRun sampleTestRun) { final STestRun testRun = build.getBuildStatistics(ALL_TESTS_NO_DETAILS).findTestByTestNameId(sampleTestRun.getTest().getTestNameId()); if (testRun == null) { throw new IllegalArgumentException("Cannot find test with name " + sampleTestRun.getFullText() + " in build " + build); } return testRun; } }