001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.testtools.common; 017 018import org.apache.commons.lang3.exception.ExceptionUtils; 019 020import java.io.IOException; 021import java.util.Iterator; 022import java.util.Properties; 023import java.util.regex.Matcher; 024import java.util.regex.Pattern; 025 026/** 027 * <p> 028 * Link test failures to existing Jiras as a html link in Jenkins. 029 * </p><p> 030 * The more failures the more useful it is to not have to keep tracking down the same Jiras. 031 * </p><p> 032 * Set -Djira.aware.regex.failures.location and -Djira.aware.contains.failures.location to define file locations, else 033 * @{code JiraAwareRegexFailures.properties} and {@code JiraAwareContainsFailures.properties} will be read as a resource stream. To 034 * override the Jira browse url set -Djira.aware.browse.url 035 * </p><p> 036 * To make use of JiraAwareFailureUtils implement {@see JiraAwareFailable} and call {@code JiraAwareFailureUtils.fail(contents, message, failable);} instead of 037 * asserts or Assert.fail(). 038 * </p><p> 039 * TODO: 040 * <ol> 041 * <li>Integration Test integration. ITs often fail by the 10s tracking down existing Jiras is a huge time sink.</li> 042 * </ol> 043 * </p> 044 * @author Kuali Rice Team (rice.collab@kuali.org) 045 */ 046public class JiraAwareFailureUtils { 047 048 /** 049 * <p> 050 * Set -Djira.aware.base.url to point your Jira url base (include trailing slash), defaults to 051 * https://jira.kuali.org/browse/ 052 * </p> 053 */ 054 public static final String JIRA_BROWSE_URL_PROPERTY = "jira.aware.browse.url"; 055 056 private static String JIRA_BROWSE_URL = System.getProperty(JIRA_BROWSE_URL_PROPERTY,"https://jira.kuali.org/browse/"); 057 058 private static String REGEX_DEFAULT_PROPS_LOCATION = "JiraAwareRegexFailures.properties"; 059 060 /** 061 * <p> 062 * Set -Djira.aware.regex.failures.location to point to the the regex failures properties, defaults to 063 * JiraAwareRegexFailures.properties 064 * </p> 065 */ 066 public static final String REGEX_LOCATION_POPERTY = "jira.aware.regex.failures.location"; 067 068 private static String REGEX_PROPS_LOCATION = System.getProperty(REGEX_LOCATION_POPERTY, REGEX_DEFAULT_PROPS_LOCATION); 069 070 private static String CONTAINS_DEFAULT_PROPS_LOCATION = "JiraAwareContainsFailures.properties"; 071 072 /** 073 * <p> 074 * Set -Djira.aware.contains.failures.location to point to the the regex failures properties, defaults to 075 * JiraAwareContainsFailures.properties 076 * </p> 077 */ 078 public static final String CONTAINS_LOCATION_PROERTY = "jira.aware.contains.failures.location"; 079 080 private static String CONTAINS_PROPS_LOCATION = System.getProperty(CONTAINS_LOCATION_PROERTY, CONTAINS_DEFAULT_PROPS_LOCATION); 081 082 static Properties regexJiraMatches; // for more powerful matching 083 084 static Properties jiraMatches; // simple contains matching 085 086 static { 087 try { 088 regexJiraMatches = new PropertiesUtils().loadProperties(REGEX_PROPS_LOCATION, REGEX_DEFAULT_PROPS_LOCATION); 089 } catch (IOException e) { 090 e.printStackTrace(); 091 } 092 093 try { 094 jiraMatches = new PropertiesUtils().loadProperties(CONTAINS_PROPS_LOCATION, CONTAINS_DEFAULT_PROPS_LOCATION); 095 } catch (IOException e) { 096 e.printStackTrace(); 097 } 098 } 099 100 private JiraAwareFailureUtils() {} 101 102 /** 103 * <p> 104 * Calls {@see #failOnMatchedJira(String, JiraAwareFailable)} and calls fail on the {@see JiraAwareFailable#fail} if no matched jira failures. 105 * </p> 106 * 107 * @param message to pass to fail 108 * @param failable {@see JiraAwareFailable#fail} 109 */ 110 public static void fail(String message, JiraAwareFailable failable) { 111 failOnMatchedJira(message, failable); 112 failable.fail(message); 113 } 114 115 /** 116 * <p> 117 * Calls {@see #failOnMatchedJira(String, String, JiraAwareFailable)} and calls fail on the {@see JiraAwareFailable#fail} fails if no matched jira failures. 118 * </p> 119 * 120 * @param contents to check for jira matches on 121 * @param message to pass to fail, also checked for jira matches 122 * @param failable {@see JiraAwareFailable#fail} 123 */ 124 public static void fail(String contents, String message, JiraAwareFailable failable) { 125 failOnMatchedJira(contents, message, failable); 126 failable.fail(contents + " " + message); 127 } 128 129 /** 130 * <p> 131 * Calls {@see #failOnMatchedJira(String, String, JiraAwareFailable)} and calls fail on the {@see JiraAwareFailable#fail} fails if no matched jira failures. 132 * </p> 133 * 134 * @param contents to check for jira matches on 135 * @param message to pass to fail, also checked for jira matches 136 * @param failable {@see JiraAwareFailable#fail} 137 */ 138 public static void fail(String contents, String message, Throwable throwable, JiraAwareFailable failable) { 139 failOnMatchedJira(contents, message, failable); 140 if (throwable != null) { 141 failOnMatchedJira(ExceptionUtils.getStackTrace(throwable), throwable.getMessage(), failable); 142 failable.fail(contents + " " + message + " " + throwable.getMessage() + "\n\t" + ExceptionUtils.getStackTrace(throwable)); 143 } 144 failable.fail(message + "\n" + contents); 145 } 146 147 /** 148 * <p> 149 * Calls {@see #failOnMatchedJira(String, JiraAwareFailable)} with the contents and if no match is detected then the message. 150 * </p> 151 * 152 * @param contents to check for containing of the jiraMatches keys. 153 * @param message to check for containing of the jiraMatches keys if contents doesn't 154 * @param failable to fail with the jiraMatches value if the contents or message is detected 155 */ 156 public static void failOnMatchedJira(String contents, String message, JiraAwareFailable failable) { 157 if (message == null) { 158 message = ""; // prevent NPEs 159 } 160 161 String match = findMatchedJiraContains(message); 162 if (match != null && !match.equals("")) { 163 failable.fail(match); 164 } 165 166 match = findMatchedJiraContains(contents); 167 if (match != null && !match.equals("")) { 168 failable.fail(match); 169 } 170 171 match = findMatchedJiraRegex(message); 172 if (match != null && !match.equals("")) { 173 failable.fail(match); 174 } 175 176 match = findMatchedJiraRegex(contents); 177 if (match != null && !match.equals("")) { 178 failable.fail(match); 179 } 180 } 181 182 /** 183 * <p> 184 * If the contents contains the jiraMatches key, calls fail on the {@see JiraAwareFailable#fail} passing in the jiraMatches value for the matched key. 185 * </p> 186 * 187 * @param contents to check for containing of the jiraMatches keys. 188 * @param failable to fail with the jiraMatches value if the jiraMatches key is contained in the contents 189 */ 190 public static void failOnMatchedJira(String contents, JiraAwareFailable failable) { 191 String match = findMatchedJira(contents); 192 if (match != null && !match.equals("")) { 193 failable.fail(match); 194 } 195 } 196 197 /** 198 * <p> 199 * Returns the value from the properties files for the key matching the contents or an empty string if no match is found. 200 * </p> 201 * 202 * @param contents to search for key in from properties file 203 * @return value for key which matches contents 204 */ 205 public static String findMatchedJira(String contents) { 206 String match = findMatchedJiraContains(contents); 207 if (match != null && !"".equals(match)) { 208 return match; 209 } 210 211 return findMatchedJiraRegex(contents); 212 } 213 214 protected static String findMatchedJiraContains(String contents) { 215 if (jiraMatches == null || jiraMatches.keySet() == null) { 216 System.out.println("WARNING JiraAwareFailureUtils contains properties empty, findMatchesJira contains not available."); 217 return ""; 218 } 219 String key = null; 220 221 Iterator iter = jiraMatches.keySet().iterator(); 222 223 while (iter.hasNext()) { 224 key = (String)iter.next(); 225 if (contents.contains(key)) { 226 return("\n" + JIRA_BROWSE_URL + jiraMatches.get(key) + "\n\n" + contents); 227 } 228 } 229 230 return ""; 231 } 232 233 protected static String findMatchedJiraRegex(String contents) { 234 if (regexJiraMatches == null || regexJiraMatches.keySet() == null) { 235 System.out.println("WARNING JiraAwareFailureUtils Regex properties empty, findMatchesJiraRegex not available."); 236 return ""; 237 } 238 String key = null; 239 Pattern pattern = null; 240 Matcher matcher = null; 241 242 Iterator iter = regexJiraMatches.keySet().iterator(); 243 244 while (iter.hasNext()) { 245 key = (String)iter.next(); 246 pattern = Pattern.compile(key, Pattern.DOTALL); // match across line terminators 247 matcher = pattern.matcher(contents); 248 249 if (matcher.find()) { 250 return("\n" + JIRA_BROWSE_URL + regexJiraMatches.get(key) + "\n\n" + contents); 251 } 252 } 253 254 return ""; 255 } 256 257}