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}