/*************************GO-LICENSE-START*********************************
* Copyright 2014 ThoughtWorks, Inc.
*
* 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.
*************************GO-LICENSE-END***********************************/
package com.thoughtworks.go.server.dao.sparql;
import java.util.Arrays;
import java.util.List;
import com.thoughtworks.go.domain.JobIdentifier;
import com.thoughtworks.go.domain.StageIdentifier;
import com.thoughtworks.go.domain.testinfo.FailureDetails;
import com.thoughtworks.go.domain.testinfo.StageTestRuns;
import com.thoughtworks.go.domain.testinfo.TestStatus;
import com.thoughtworks.go.domain.testinfo.TestSuite;
import com.thoughtworks.go.i18n.LocalizedMessage;
import com.thoughtworks.go.server.service.PipelineInstanceLoader;
import com.thoughtworks.go.server.service.StageService;
import com.thoughtworks.go.server.service.result.LocalizedOperationResult;
import com.thoughtworks.studios.shine.cruise.GoOntology;
import com.thoughtworks.studios.shine.cruise.stage.StagesQuery;
import com.thoughtworks.studios.shine.semweb.BoundVariables;
import com.thoughtworks.studios.shine.xunit.XUnitOntology;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
/**
* @understands how to get data out of shine
*/
public class ShineDao {
private static final Logger LOGGER = Logger.getLogger(ShineDao.class);
private StagesQuery stagesQuery;
private final StageService stageService;
public ShineDao(StagesQuery stagesQuery, StageService stageService, PipelineInstanceLoader pipelineInstanceLoader) {
this.stagesQuery = stagesQuery;
this.stageService = stageService;
}
public List<TestSuite> failedTestsFor(final StageIdentifier stageId) {
StageTestRuns stageTestRuns = getTestCount(stageId);
final List<StageIdentifier> stageIdentifierList = Arrays.asList(stageId);
populateFailingTests(stageTestRuns, getFailedTests(stageIdentifierList));
populateUsers(stageTestRuns, getCommitters(stageIdentifierList));
return stageTestRuns.failingTestSuitesForNthPipelineRun(0);
}
public StageTestRuns failedBuildHistoryForStage(StageIdentifier stageId, LocalizedOperationResult result) {
try {
StageTestRuns stageTestRuns = getTestCount(stageId);
List<StageIdentifier> failedStageIds = stageService.findRunForStage(stageId);
populateFailingTests(stageTestRuns, getFailedTests(failedStageIds));
populateUsers(stageTestRuns, getCommitters(failedStageIds));
stageTestRuns.removeDuplicateTestEntries();
return stageTestRuns;
}
catch (RuntimeException e) {
LOGGER.error("can not retrieve shine test history!", e);
result.connectionError(LocalizedMessage.unableToRetrieveFailureResults());
return new StageTestRuns(0, 0, 0);
}
}
public FailureDetails failureDetailsForTest(JobIdentifier jobId, String suiteName, String testCaseName, LocalizedOperationResult result) {
String selectTestCase = "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n"
+ "PREFIX xunit: <" + XUnitOntology.URI + "> "
+ "PREFIX cruise: <" + GoOntology.URI + "> "
+ "\n"
+ "SELECT ?failureMessage ?stackTrace {\n"
+ " ?pipeline cruise:pipelineName " + s(jobId.getPipelineName()) + " .\n"
+ " ?pipeline cruise:pipelineCounter " + i(jobId.getPipelineCounter()) + " .\n"
+ " ?pipeline cruise:hasStage ?stage .\n"
+ " ?stage cruise:stageName " + s(jobId.getStageName()) + " .\n"
+ " ?stage cruise:stageCounter " + i(jobId.getStageCounter()) + " .\n"
+ " ?stage cruise:hasJob ?job .\n"
+ " ?job cruise:jobName " + s(jobId.getBuildName()) + " .\n"
+ " ?job xunit:hasTestCase ?testCase .\n"
+ " ?testCase a xunit:TestCase .\n"
+ " ?testCase xunit:testCaseName " + s(testCaseName) + " .\n"
+ " ?testCase xunit:testSuiteName " + s(suiteName) + " .\n"
+ " ?testCase xunit:hasFailure ?failure .\n"
+ " ?failure a xunit:Failure .\n"
+ " ?failure xunit:failureMessage ?failureMessage .\n"
+ " ?failure xunit:failureStackTrace ?stackTrace "
+ "}";
try {
return stagesQuery.select(selectTestCase, Arrays.asList(jobId.getStageIdentifier()), new RdfResultMapper<FailureDetails>() {
public FailureDetails map(BoundVariables aRow) {
return new FailureDetails(aRow.getAsString("failureMessage"), aRow.getAsString("stackTrace"));
}
}).get(0);
} catch (RuntimeException e) {
LOGGER.error("can not retrieve shine test history!", e);
result.connectionError(LocalizedMessage.unableToRetrieveFailureResults());
return FailureDetails.nullFailureDetails();
}
}
private StageTestRuns getTestCount(StageIdentifier stageId) {
String selectTestCase =
"PREFIX xunit: <" + XUnitOntology.URI + "> "
+ "\n"
+ "SELECT ?testCase ?failure ?error {\n"
+ " ?testCase a xunit:TestCase .\n"
+ " OPTIONAL {\n"
+ " ?testCase xunit:hasFailure ?failure .\n"
+ " ?failure xunit:isError ?error .\n"
+ " }"
+ "}";
List<TestCaseResultModel> testCounts = stagesQuery.select(selectTestCase, Arrays.asList(stageId), new RdfResultMapper<TestCaseResultModel>() {
public TestCaseResultModel map(BoundVariables aRow) {
boolean error = Boolean.TRUE.equals(aRow.getBoolean("error"));
return new TestCaseResultModel(aRow.getAsString("failure") != null, error);
}
});
int totalCount = testCounts.size();
int failuresCount = computeFailureCounts(testCounts);
int errorsCount = computeErrorCounts(testCounts);
return new StageTestRuns(totalCount, failuresCount, errorsCount);
}
private int computeErrorCounts(List<TestCaseResultModel> testCounts) {
int count = 0;
for (TestCaseResultModel testCaseResultModel : testCounts) {
if (testCaseResultModel.hasFailure() && testCaseResultModel.hasError()) {
count++;
}
}
return count;
}
private int computeFailureCounts(List<TestCaseResultModel> testCaseResultModels) {
int count = 0;
for (TestCaseResultModel testCaseResult : testCaseResultModels) {
if (testCaseResult.hasFailure() && !testCaseResult.hasError()) {
count++;
}
}
return count;
}
private List<PipelineCommiter> getCommitters(List<StageIdentifier> failedStageIds) {
String committersForStage =
"PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n"
+ "PREFIX xunit: <" + XUnitOntology.URI + "> "
+ "PREFIX cruise: <" + GoOntology.URI + "> "
+ "\n"
+ "SELECT DISTINCT ?failedPipelineCounter ?failedPipelineLabel ?user {\n"
+ " ?pipeline cruise:pipelineCounter ?failedPipelineCounter .\n"
+ " ?pipeline cruise:pipelineLabel ?failedPipelineLabel .\n"
+ " ?pipelineTrigger a cruise:ChangeSet .\n"
+ " OPTIONAL {"
+ " ?pipelineTrigger cruise:user ?user .\n"
+ " }"
+ "} ORDER BY ?failedPipelineCounter ?user";
return stagesQuery.select(committersForStage, failedStageIds, new RdfResultMapper<PipelineCommiter>() {
public PipelineCommiter map(BoundVariables aRow) {
String userName = aRow.getString("user");
Integer failedPipelineCounter = aRow.getInt("failedPipelineCounter");
String failedPipelineLabel = aRow.getString("failedPipelineLabel");
return new PipelineCommiter(userName, failedPipelineCounter, failedPipelineLabel);
}
});
}
private void populateUsers(StageTestRuns stageTestRuns, List<PipelineCommiter> commiters) {
for (PipelineCommiter commiter : commiters) {
if (!StringUtils.isEmpty(commiter.getUserName())) {
stageTestRuns.addUser(commiter.getFailedPipelineCounter(), commiter.getFailedPipelineLabel(), commiter.getUserName());
}
}
}
private List<TestCaseModel> getFailedTests(List<StageIdentifier> failedStageIds) {
String selectFailingTests =
"PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n"
+ "PREFIX xunit: <" + XUnitOntology.URI + "> "
+ "PREFIX cruise: <" + GoOntology.URI + "> "
+ "\n"
+ "SELECT DISTINCT ?testSuiteName ?testCaseName ?isError ?jobName ?failedPipelineName ?failedPipelineCounter ?failedPipelineLabel ?failedStageName ?failedStageCounter {\n"
+ " ?pipeline cruise:pipelineName ?failedPipelineName . "
+ " ?pipeline cruise:pipelineCounter ?failedPipelineCounter . "
+ " ?pipeline cruise:pipelineLabel ?failedPipelineLabel . "
+ " ?pipeline cruise:hasStage ?stage . "
+ " ?stage cruise:stageName ?failedStageName . "
+ " ?stage cruise:stageCounter ?failedStageCounter . "
+ " ?stage cruise:hasJob ?job . "
+ " ?job cruise:jobName ?jobName .\n"
+ " OPTIONAL {\n"
+ " ?job xunit:hasTestCase ?testCase .\n"
+ " ?testCase xunit:testCaseName ?testCaseName .\n"
+ " ?testCase xunit:testSuiteName ?testSuiteName .\n"
+ " ?testCase xunit:hasFailure ?failure .\n"
+ " ?failure xunit:isError ?isError.\n"
+ " }\n"
+ "} ORDER BY ?jobName ?testSuiteName ?testCaseName";
return stagesQuery.select(selectFailingTests, failedStageIds, new RdfResultMapper<TestCaseModel>() {
public TestCaseModel map(BoundVariables aRow) {
String failedPipelineName = aRow.getString("failedPipelineName");
int failedPipelineCounter = aRow.getInt("failedPipelineCounter");
String failedPipelineLabel = aRow.getString("failedPipelineLabel");
String failedStageName = aRow.getString("failedStageName");
String failedStageCounter = aRow.getString("failedStageCounter");
String jobName = aRow.getString("jobName");
return new TestCaseModel(new JobIdentifier(failedPipelineName, failedPipelineCounter, failedPipelineLabel, failedStageName, failedStageCounter, jobName),
aRow.getString("testSuiteName"), aRow.getString("testCaseName"), aRow.getBoolean("isError"));
}
});
}
private void populateFailingTests(StageTestRuns stageTestRuns, List<TestCaseModel> testCaseModels) {
for (TestCaseModel testCaseModel : testCaseModels) {
JobIdentifier jobIdentifier = testCaseModel.getJobIdentifier();
if (!StringUtils.isEmpty(testCaseModel.getTestName())) {
stageTestRuns.add(jobIdentifier.getPipelineCounter(), jobIdentifier.getPipelineLabel(), testCaseModel.getTestSuiteName(), testCaseModel.getTestName(), TestStatus.fromURLType(
testCaseModel.isError()), jobIdentifier);
} else {
stageTestRuns.add(jobIdentifier.getPipelineCounter(), jobIdentifier.getPipelineLabel());
}
}
}
private static String s(String str) {
return String.format("\"%s\"^^xsd:string", str);
}
private static String i(int integer) {
return i(String.valueOf(integer));
}
private static String i(String integer) {
return String.format("\"%s\"^^xsd:integer", integer);
}
}