// Copyright 2010 Google Inc. All Rights Reseved.
//
// 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.testing.testify.risk.frontend.client.riskprovider.impl;
import com.google.common.base.Predicate;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.testing.testify.risk.frontend.client.TaCallback;
import com.google.testing.testify.risk.frontend.client.riskprovider.RiskProvider;
import com.google.testing.testify.risk.frontend.client.util.NotificationUtil;
import com.google.testing.testify.risk.frontend.model.Capability;
import com.google.testing.testify.risk.frontend.model.CapabilityIntersectionData;
import com.google.testing.testify.risk.frontend.model.TestCase;
import com.google.testing.testify.risk.frontend.shared.rpc.DataRpc;
import com.google.testing.testify.risk.frontend.shared.rpc.DataRpcAsync;
import java.util.HashSet;
import java.util.List;
/**
* Provides the mitigation contributed by existing test coverage. (Testcases alone, not code
* coverage or recent test results.)
*
* @author chrsmith@google.com (Chris Smith)
*/
public class TestCoverageRiskProvider implements RiskProvider {
private final Multimap<Long, TestCase> testcaseByAttributeLookup = HashMultimap.create();
private final Multimap<Long, TestCase> testcaseByComponentLookup = HashMultimap.create();
private final Multimap<Long, TestCase> testcaseByCapabilityLookup = HashMultimap.create();
@Override
public String getName() {
return "Test coverage";
}
@Override
public void initialize(List<CapabilityIntersectionData> projectData) {
DataRpcAsync dataService = GWT.create(DataRpc.class);
long projectId = projectData.get(0).getParentComponent().getParentProjectId();
dataService.getProjectTestCasesById(projectId,
new TaCallback<List<TestCase>>("Querying TestCases") {
@Override
public void onSuccess(List<TestCase> result) {
initializeTestCaseLookups(result);
}
});
}
/**
* Initializes class fields related to looking up testcases by Attribute, Component, or
* Capability IDs.
*/
private void initializeTestCaseLookups(List<TestCase> testCases) {
for (TestCase test : testCases) {
for (String testcaseTag : test.getTags()) {
// Search test case tags for IDs prefixed with descriptors such as "Component:13452"
String[] parts = testcaseTag.split(":");
if (parts.length != 2) {
continue;
}
try {
if (parts[0].equals("Attribute")) {
testcaseByAttributeLookup.put(Long.parseLong(parts[1]), test);
} else if (parts[0].equals("Capability")) {
testcaseByCapabilityLookup.put(Long.parseLong(parts[1]), test);
} else if (parts[0].equals("Component")) {
testcaseByComponentLookup.put(Long.parseLong(parts[1]), test);
}
} catch (NumberFormatException nfe) {
// Notify the user of a malformed testcase tag.
StringBuilder errorMessage = new StringBuilder();
errorMessage.append("Error processing a testcase with a malformed tag. ");
errorMessage.append("Testcase tag: '");
errorMessage.append(testcaseTag);
errorMessage.append("' found on testcase '");
errorMessage.append(test.getTitle());
errorMessage.append("'");
NotificationUtil.displayErrorMessage(errorMessage.toString());
}
}
}
}
/**
* Returns the list of test cases associated with a given risk cell.
*/
public List<TestCase> getCellTestCases(CapabilityIntersectionData targetCell) {
List<TestCase> testCases = Lists.newArrayList();
long attributeId = targetCell.getParentAttribute().getAttributeId();
if (testcaseByAttributeLookup.containsKey(attributeId)) {
testCases.addAll(testcaseByAttributeLookup.get(attributeId));
}
long componentId = targetCell.getParentComponent().getComponentId();
if (testcaseByComponentLookup.containsKey(componentId)) {
testCases.addAll(testcaseByComponentLookup.get(componentId));
}
if (targetCell.getCapabilities() != null) {
for (Capability capability : targetCell.getCapabilities()) {
long capabilityId = capability.getCapabilityId();
if (testcaseByCapabilityLookup.containsKey(capabilityId)) {
testCases.addAll(testcaseByCapabilityLookup.get(capabilityId));
}
}
}
// Remove any duplicate entries.
final HashSet<Long> testCaseIds = new HashSet<Long>();
return Lists.newArrayList(Iterables.filter(testCases,
new Predicate<TestCase>() {
@Override
public boolean apply(TestCase input) {
if (testCaseIds.contains(input.getExternalId())) {
return false;
} else {
testCaseIds.add(input.getExternalId());
return true;
}
}
}));
}
@Override
public double calculateRisk(CapabilityIntersectionData targetCell) {
List<TestCase> testCases = getCellTestCases(targetCell);
// Test coverage mitigates risk, so the value returned is negative.
return testCases.size() * -0.15;
}
@Override
public Widget onClick(CapabilityIntersectionData targetCell) {
List<TestCase> testCases = getCellTestCases(targetCell);
VerticalPanel panel = new VerticalPanel();
if (testCases.size() == 0) {
panel.add(new Label("No test cases are associated with this cell."));
} else {
for (TestCase testCase : testCases) {
panel.add(new Anchor(testCase.getTitle(), testCase.getTestCaseUrl()));
}
}
return panel;
}
}