blob: 5ac0d3932105320b4e48ee509a483e03b765059d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010-2014 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* SAP AG - initial API and implementation
*******************************************************************************/
package org.eclipse.skalli.core.rest;
import static org.junit.Assert.fail;
import java.text.MessageFormat;
import java.util.Set;
import java.util.regex.Pattern;
import org.custommonkey.xmlunit.Difference;
import org.custommonkey.xmlunit.DifferenceConstants;
import org.custommonkey.xmlunit.DifferenceListener;
import org.custommonkey.xmlunit.NodeDetail;
import org.eclipse.skalli.commons.CollectionUtils;
import org.w3c.dom.Node;
@SuppressWarnings("nls")
public class ProjectDetailsV1V2Diff extends ProjectsV1V2Diff implements DifferenceListener {
private final Pattern MEMBER_XPATH_PATTERN = getPattern("/members\\[1\\]/member\\[\\d+\\]/link\\[1\\]/@href");
private final Pattern PEOPLE_EXT_LEADS_XPATH_PATTERN = getExtPattern("/people\\[1\\]/leads\\[1\\]/lead\\[\\d+\\]/link\\[1\\]/@href");
private final Pattern PEOPLE_EXT_MEMBERS_XPATH_PATTERN = getExtPattern("/people\\[1\\]/members\\[1\\]/member\\[\\d+\\]/link\\[1\\]/@href");
private final Pattern INFO_EXT_XPATH_PATTERN = getExtPattern("/info\\[1\\]");
private final Pattern INFO_EXT_MAILINGLISTS_XPATH_PATTERN = getExtPattern("/info\\[1\\]/mailingLists\\[1\\]");
private final Pattern DEVINF_EXT_XPATH_PATTERN = getExtPattern("/devInf\\[1\\]");
private final Pattern DEVINF_EXT_JAVADOCS_XPATH_PATTERN = getExtPattern("/devInf\\[1\\]/javadocs\\[1\\]");
private final Pattern DEVINF_EXT_SCMLOCATIONS_XPATH_PATTERN = getExtPattern("/devInf\\[1\\]/scmLocations\\[1\\]");
private final Pattern MAVEN_EXT_COORDINATE_XPATH_PATTERN = getExtPattern("/mavenReactor\\[1\\]/coordinate\\[1\\]");
private final Pattern MAVEN_EXT_VERSIONS_XPATH_PATTERN = getExtPattern("/mavenReactor\\[1\\]/coordinate\\[1\\]/versions\\[1\\]");
private final Pattern MAVEN_EXT_PACKAGING_XPATH_PATTERN = getExtPattern("/mavenReactor\\[1\\]/coordinate\\[1\\]/packaging\\[1\\]");
private final Pattern MAVEN_EXT_MODULE_XPATH_PATTERN = getExtPattern("/mavenReactor\\[1\\]/modules\\[1\\]/module\\[\\d+\\]");
private final Pattern MAVEN_EXT_MODULE_VERSIONS_PATTERN = getExtPattern("/mavenReactor\\[1\\]/modules\\[1\\]/module\\[\\d+\\]/versions\\[1\\]");
private final Pattern MAVEN_EXT_MODULE_PACKAGING_XPATH_PATTERN = getExtPattern("/mavenReactor\\[1\\]/modules\\[1\\]/module\\[\\d+\\]/packaging\\[1\\]");
private final Pattern RELATED_EXT_XPATH_PATTERN = getPattern("/extensions\\[1\\]/relatedProjects\\[1\\]");
private final Pattern RELATED_EXT_CALCULATED_XPATH_PATTERN = getExtPattern("/relatedProjects\\[1\\]/calculated\\[1\\]");
private final Pattern RELATED_EXT_LINK_XPATH_PATTERN = getExtPattern("/relatedProjects\\[1\\]/link\\[\\d+\\]");
private final Pattern REVIEWS_EXT_XPATH_PATTERN = getExtPattern("/reviews\\[1\\]");
private final Pattern REVIEWS_EXT_REVIEW_XPATH_PATTERN = getExtPattern("/reviews\\[1\\]/review\\[\\d+\\]");
private final Pattern REVIEWS_EXT_STYLE_XPATH_PATTERN = getExtPattern("/reviews\\[1\\]/ratingStyle\\[1\\]");
private final Pattern REVIEWS_EXT_ANONYMOUS_XPATH_PATTERN = getExtPattern("/reviews\\[1\\]/allowAnonymous\\[1\\]");
private final Pattern REVIEWS_EXT_NUMBERVOTES_XPATH_PATTERN = getExtPattern("/reviews\\[1\\]/numberVotes\\[1\\]");
private final Pattern REVIEWS_EXT_NUMBERUPS_XPATH_PATTERN = getExtPattern("/reviews\\[1\\]/numberThumbsUp\\[1\\]");
private final Pattern REVIEWS_EXT_NUMBERDNS_XPATH_PATTERN = getExtPattern("/reviews\\[1\\]/numberThumbsDown\\[1\\]");
private final Pattern REVIEWS_EXT_AVGRATING_XPATH_PATTERN = getExtPattern("/reviews\\[1\\]/averageRating\\[1\\]");
private final Pattern REVIEWS_EXT_COMMENT_TEXT_XPATH_PATTERN = getExtPattern("/reviews\\[1\\]/review\\[\\d+\\]/comment\\[1\\]/text\\(\\)\\[1\\]");
private final Pattern SCRUM_EXT_XPATH_PATTERN = getExtPattern("/scrum\\[1\\]");
private final Pattern SCRUM_EXT_SCRUMMASTERAXPATH_PATTERN = getExtPattern("/scrum\\[1\\]/scrumMasters\\[1\\]");
private final Pattern SCRUM_EXT_PRODUCTOWNERS_XPATH_PATTERN = getExtPattern("/scrum\\[1\\]/productOwners\\[1\\]");
private static final Set<String> ADDITIONAL_REVIEW_TAGS = CollectionUtils.asSet("ratingStyle", "allowAnonymous",
"numberVotes", "numberThumbsUp", "numberThumbsDown", "averageRating");
private final Pattern USER_PATTERN;
private final Pattern USERS_PATTERN;
public ProjectDetailsV1V2Diff(String webLocator) {
super(webLocator);
USER_PATTERN = Pattern.compile("^" + webLocator + "/api/user/.+$");
USERS_PATTERN = Pattern.compile("^" + webLocator + "/api/users/.+$");
}
@Override
public int differenceFound(Difference difference) {
int result = super.differenceFound(difference);
if (result == RETURN_ACCEPT_DIFFERENCE) {
NodeDetail expected = difference.getControlNodeDetail();
NodeDetail actual = difference.getTestNodeDetail();
switch (difference.getId()) {
case DifferenceConstants.ATTR_VALUE_ID:
// path prefix of the user API has been changed from /api/user to /api/users:
// all <link> tags to the user API have changed from v1 to v2; this affects
// the <members> section and the <people> extension
if (equalsAndMatchesAnyXPath(expected, actual, MEMBER_XPATH_PATTERN,
PEOPLE_EXT_LEADS_XPATH_PATTERN, PEOPLE_EXT_MEMBERS_XPATH_PATTERN)
&& USER_PATTERN.matcher(expected.getValue()).matches()
&& USERS_PATTERN.matcher(actual.getValue()).matches()) {
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
}
break;
case DifferenceConstants.HAS_CHILD_NODES_ID:
if (equalsAndMatchesAnyXPath(expected, actual, INFO_EXT_XPATH_PATTERN)
&& hasEmptyChildNode(actual, "mailingLists")) {
// collection-like tags are always rendered in the v2 API even if empty;
// in the v1 API they were not rendered at all
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (equalsAndMatchesAnyXPath(expected, actual, DEVINF_EXT_XPATH_PATTERN)
&& hasEmptyChildNode(actual, "scmLocations")) {
// collection-like tags are always rendered in the v2 API even if empty;
// in the v1 API they were not rendered at all
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (equalsAndMatchesAnyXPath(expected, actual, SCRUM_EXT_XPATH_PATTERN)
&& hasAnyChildNode(actual, CollectionUtils.asSet("scrumMasters", "productOwners"))) {
// the v1 API did not rendered the scrum masters and product owners at all;
// the v2 API renders these lists similiar to the <people> extension (userId + link)
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (equalsAndMatchesAnyXPath(expected, actual, RELATED_EXT_XPATH_PATTERN)
&& hasBooleanChildNode(actual, "calculated")) {
// the v1 API rendered the <calculated> tag only in the case the value is true;
// the v2 API renders this tag always with the corresponding value
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (equalsAndMatchesAnyXPath(expected, actual, REVIEWS_EXT_XPATH_PATTERN)
&& hasAnyChildNode(actual, ADDITIONAL_REVIEW_TAGS)) {
// the v2 API renders several additional tags (see ADDITIONAL_REVIEW_TAGS)
// that are not present in the v1 API
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
}
break;
case DifferenceConstants.CHILD_NODELIST_LENGTH_ID:
if (equalsAndMatchesAnyXPath(expected, actual, INFO_EXT_XPATH_PATTERN)
&& hasEmptyChildNode(actual, "mailingLists")) {
// collection-like tags are always rendered in the v2 API even if empty;
// in the v1 API they were not rendered at all
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (equalsAndMatchesAnyXPath(expected, actual, DEVINF_EXT_XPATH_PATTERN)
&& hasEmptyChildNode(actual, "javadocs")) {
// collection-like tags are always rendered in the v2 API even if empty;
// in the v1 API they were not rendered at all
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (equalsAndMatchesAnyXPath(expected, actual, MAVEN_EXT_COORDINATE_XPATH_PATTERN, MAVEN_EXT_MODULE_XPATH_PATTERN)
&& hasEmptyChildNode(actual, "versions")) {
// collection-like tags are always rendered in the v2 API even if empty;
// in the v1 API they were not rendered at all
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (equalsAndMatchesAnyXPath(expected, actual, SCRUM_EXT_XPATH_PATTERN)
&& hasAnyChildNode(actual, CollectionUtils.asSet("scrumMasters", "productOwners"))) {
// the v1 API did not rendered the scrum masters and product owners at all;
// the v2 API renders these lists similiar to the <people> extension (userId + link)
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (equalsAndMatchesAnyXPath(expected, actual, RELATED_EXT_XPATH_PATTERN)
&& hasBooleanChildNode(actual, "calculated")) {
// the v1 API rendered the <calculated> tag only in the case the value is true;
// the v2 API renders this tag always with the corresponding value
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (equalsAndMatchesAnyXPath(expected, actual, REVIEWS_EXT_XPATH_PATTERN)
&& hasAnyChildNode(actual, ADDITIONAL_REVIEW_TAGS)) {
// the v2 API renders several additional tags (see ADDITIONAL_REVIEW_TAGS)
// that are not present in the v1 API
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
}
break;
case DifferenceConstants.CHILD_NODE_NOT_FOUND_ID:
if (matchesAnyXPath(actual, INFO_EXT_MAILINGLISTS_XPATH_PATTERN)
&& "mailingLists".equals(actual.getValue())) {
// collection-like tags are always rendered in the v2 API even if empty;
// in the v1 API they were not rendered at all
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (matchesAnyXPath(actual, DEVINF_EXT_JAVADOCS_XPATH_PATTERN)
&& "javadocs".equals(actual.getValue())) {
// collection-like tags are always rendered in the v2 API even if empty;
// in the v1 API they were not rendered at all
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (matchesAnyXPath(actual, DEVINF_EXT_SCMLOCATIONS_XPATH_PATTERN)
&& "scmLocations".equals(actual.getValue())) {
// collection-like tags are always rendered in the v2 API even if empty;
// in the v1 API they were not rendered at all
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (matchesAnyXPath(actual, MAVEN_EXT_VERSIONS_XPATH_PATTERN, MAVEN_EXT_MODULE_VERSIONS_PATTERN)
&& "versions".equals(actual.getValue())) {
// collection-like tags are always rendered in the v2 API even if empty;
// in the v1 API they were not rendered at all
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (matchesAnyXPath(actual, SCRUM_EXT_SCRUMMASTERAXPATH_PATTERN)
&& "scrumMasters".equals(actual.getValue())) {
// the v1 API did not rendered the scrum masters and product owners at all;
// the v2 API renders these lists similiar to the <people> extension (userId + link)
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (matchesAnyXPath(actual, SCRUM_EXT_PRODUCTOWNERS_XPATH_PATTERN)
&& "productOwners".equals(actual.getValue())) {
// the v1 API did not rendered the scrum masters and product owners at all;
// the v2 API renders these lists similiar to the <people> extension (userId + link)
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (matchesAnyXPath(actual, RELATED_EXT_CALCULATED_XPATH_PATTERN)
&& "calculated".equals(actual.getValue())) {
// the v1 API rendered the <calculated> tag only in the case the value is true;
// the v2 API renders this tag always with the corresponding value
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (matchesAnyXPath(actual, REVIEWS_EXT_STYLE_XPATH_PATTERN, REVIEWS_EXT_ANONYMOUS_XPATH_PATTERN,
REVIEWS_EXT_NUMBERVOTES_XPATH_PATTERN, REVIEWS_EXT_NUMBERUPS_XPATH_PATTERN,
REVIEWS_EXT_NUMBERDNS_XPATH_PATTERN, REVIEWS_EXT_AVGRATING_XPATH_PATTERN)
&& ADDITIONAL_REVIEW_TAGS.contains(actual.getValue())) {
// the v2 API renders several additional tags (see ADDITIONAL_REVIEW_TAGS)
// that are not present in the v1 API
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
}
break;
case DifferenceConstants.CHILD_NODELIST_SEQUENCE_ID:
if (equalsAndMatchesAnyXPath(expected, actual, MAVEN_EXT_PACKAGING_XPATH_PATTERN,
MAVEN_EXT_MODULE_PACKAGING_XPATH_PATTERN, RELATED_EXT_LINK_XPATH_PATTERN)) {
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
} else if (equalsAndMatchesAnyXPath(expected, actual, REVIEWS_EXT_REVIEW_XPATH_PATTERN)) {
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
}
break;
case DifferenceConstants.TEXT_VALUE_ID:
// old and new API render different file endings: the new API renders a single \n #xA),
// while the old API preferred \r\n (#xD #xA)
if (equalsAndMatchesAnyXPath(expected, actual, REVIEWS_EXT_COMMENT_TEXT_XPATH_PATTERN)
&& equalsValueIgnoreLineEndings(expected, actual)) {
result = RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
};
break;
default:
result = RETURN_ACCEPT_DIFFERENCE;
}
}
return result;
}
@Override
public void skippedComparison(Node expected, Node actual) {
fail(MessageFormat.format(
"comparison skipped because the node types are not comparable: {0} (type: {1}) - {2} (type: {3})",
expected.getNodeName(), expected.getNodeType(), actual.getNodeName(), actual.getNodeType()));
}
@Override
protected String getRootPath() {
return "";
}
}