// 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.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.model.CapabilityIntersectionData;
import com.google.testing.testify.risk.frontend.model.Checkin;
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;
import java.util.Set;
/**
* Risk provider showing risk due to code churn. It works by analyzing individual changes which
* each contain a list of 'directories they care about' and then scanning all checkins to see
* if they touched any directories of interest.
*
* Note that if a checkin is in "foo\bar\baz", Components which care about "foo" and "foo\bar" will
* both have the risk associated with the checkin. To facilitate risk cascading down a directory
* hierarchy, a tree will be created where each node is a directory name.
*
* @author chrsmith@google.com (Chris Smith)
*/
public class CodeChurnRiskProvider implements RiskProvider {
// TODO(chrsmith): While time-efficient, creating a checkin lookup tree is not the simplest
// solution. Perhaps we can use a Multimap<String, Checkin> instead. Note however that we
// would need to walk every Key and see if the Key starts with the target directory in the case
// that the Component cares about "alpha\beta" and a checkin touches "alpha\beta\gamma".
/** Tree used to quickly lookup checkins based on a code directory. */
CheckinDirectoryTreeNode treeRoot = new CheckinDirectoryTreeNode();
@Override
public double calculateRisk(CapabilityIntersectionData targetCell) {
Set<Checkin> relevantCheckins = getCheckinsRealtedToRiskCell(targetCell);
return relevantCheckins.size() * 0.20;
}
@Override
public String getName() {
return "Code churn";
}
@Override
public void initialize(List<CapabilityIntersectionData> projectData) {
DataRpcAsync dataService = GWT.create(DataRpc.class);
long projectId = projectData.get(0).getParentComponent().getParentProjectId();
dataService.getProjectCheckinsById(projectId,
new TaCallback<List<Checkin>>("Querying Checkins") {
@Override
public void onSuccess(List<Checkin> result) {
initializeCheckinLookup(result);
}
});
}
@Override
public Widget onClick(CapabilityIntersectionData targetCell) {
VerticalPanel checkinsPanel = new VerticalPanel();
Set<Checkin> relevantCheckins = getCheckinsRealtedToRiskCell(targetCell);
if (relevantCheckins.size() == 0) {
checkinsPanel.add(new Label("No checkins are associated with this cell."));
} else {
for (Checkin checkin : relevantCheckins) {
String linkText = Long.toString(checkin.getExternalId()) + ": " + checkin.getSummary();
// Display only 100 characters of a checkin summary.
if (linkText.length() > 100) {
linkText = linkText.substring(0, 97) + "...";
}
checkinsPanel.add(new Anchor(linkText, checkin.getChangeUrl()));
}
}
return checkinsPanel;
}
/**
* Initialize the checkin directory tree data structure based on the directories touched by all
* known checkins.
*/
private void initializeCheckinLookup(List<Checkin> checkins) {
treeRoot = new CheckinDirectoryTreeNode();
for (Checkin checkin : checkins) {
for (String directoryTouched : checkin.getDirectoriesTouched()) {
// NOTE: This means that if a checkin touches directory A and directory B, then
// the checkin will be double-counted if the component looks for checkins in both A and B.
treeRoot.addCheckin(directoryTouched, checkin);
}
}
}
/**
* @return the checkins relevant to the risk provider cell. (Based on the current checkin
* directory tree in memory.)
*/
private Set<Checkin> getCheckinsRealtedToRiskCell(CapabilityIntersectionData cell) {
// TODO(chrsmith): Implement this by relying on ComponentSuperLabels.
return new HashSet<Checkin>();
}
}