提交 727d77a4 编写于 作者: T tfennelly

Removed more advanced search options as requested by @daniel-beck

上级 2ec3ffcd
...@@ -27,9 +27,6 @@ import hudson.model.Job; ...@@ -27,9 +27,6 @@ import hudson.model.Job;
import hudson.model.Queue; import hudson.model.Queue;
import hudson.model.Run; import hudson.model.Run;
import hudson.widgets.HistoryWidget; import hudson.widgets.HistoryWidget;
import jenkins.widgets.buildsearch.BuildSearchParamProcessor;
import jenkins.widgets.buildsearch.BuildSearchParamProcessorList;
import jenkins.widgets.buildsearch.BuildSearchParams;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -47,7 +44,7 @@ public class HistoryPageFilter<T> { ...@@ -47,7 +44,7 @@ public class HistoryPageFilter<T> {
private final int maxEntries; private final int maxEntries;
private Long newerThan; private Long newerThan;
private Long olderThan; private Long olderThan;
private BuildSearchParamProcessor searchProcessor; private String searchString;
// Need to use different Lists for Queue.Items and Runs because // Need to use different Lists for Queue.Items and Runs because
// we need access to them separately in the jelly files for rendering. // we need access to them separately in the jelly files for rendering.
...@@ -98,8 +95,7 @@ public class HistoryPageFilter<T> { ...@@ -98,8 +95,7 @@ public class HistoryPageFilter<T> {
* @param searchString The search string. * @param searchString The search string.
*/ */
public void setSearchString(@Nonnull String searchString) { public void setSearchString(@Nonnull String searchString) {
BuildSearchParams searchParams = new BuildSearchParams(searchString); this.searchString = searchString;
this.searchProcessor = new BuildSearchParamProcessorList(searchParams);
} }
/** /**
...@@ -250,14 +246,14 @@ public class HistoryPageFilter<T> { ...@@ -250,14 +246,14 @@ public class HistoryPageFilter<T> {
// to the page initially, newerThan then cutting it back down to size using cutLeading() // to the page initially, newerThan then cutting it back down to size using cutLeading()
if (entry instanceof Queue.Item) { if (entry instanceof Queue.Item) {
Queue.Item item = (Queue.Item) entry; Queue.Item item = (Queue.Item) entry;
if (searchProcessor != null && !searchProcessor.fitsSearchParams(item)) { if (searchString != null && !fitsSearchParams(item)) {
return false; return false;
} }
addQueueItem(item); addQueueItem(item);
return true; return true;
} else if (entry instanceof Run) { } else if (entry instanceof Run) {
Run run = (Run) entry; Run run = (Run) entry;
if (searchProcessor != null && !searchProcessor.fitsSearchParams(run)) { if (searchString != null && !fitsSearchParams(run)) {
return false; return false;
} }
addRun(run); addRun(run);
...@@ -278,4 +274,51 @@ public class HistoryPageFilter<T> { ...@@ -278,4 +274,51 @@ public class HistoryPageFilter<T> {
private int getFillCount() { private int getFillCount() {
return Math.max(0, (maxEntries - size())); return Math.max(0, (maxEntries - size()));
} }
private boolean fitsSearchParams(@Nonnull Queue.Item item) {
if (fitsSearchString(item.getDisplayName())) {
return true;
} else if (fitsSearchString(item.getId())) {
return true;
}
// Non of the fuzzy matches "liked" the search term.
return false;
}
private boolean fitsSearchParams(@Nonnull Run run) {
if (searchString == null) {
return true;
}
if (fitsSearchString(run.getDisplayName())) {
return true;
} else if (fitsSearchString(run.getDescription())) {
return true;
} else if (fitsSearchString(run.getNumber())) {
return true;
} else if (fitsSearchString(run.getQueueId())) {
return true;
} else if (fitsSearchString(run.getResult())) {
return true;
}
// Non of the fuzzy matches "liked" the search term.
return false;
}
private boolean fitsSearchString(Object data) {
if (searchString == null) {
return true;
}
if (data != null) {
if (data instanceof Number) {
return data.toString().equals(searchString);
} else {
return data.toString().toLowerCase().contains(searchString);
}
}
return false;
}
} }
/*
* The MIT License
*
* Copyright (c) 2013-2014, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.widgets.buildsearch;
import hudson.model.Queue;
import hudson.model.Run;
/**
* Search param/term processor.
*
* <p>
* Implementations created through a {@link BuildSearchParamProcessorFactory} implementation.
*
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public abstract class BuildSearchParamProcessor<T> {
/**
* Does the supplied {@link Queue.Item} fit the {@link BuildSearchParams} used to create this
*
* <p>
* Implementations should call {@link #fitsSearchParams(Object)} after extracting the appropriate data from
* the {@link Queue.Item}.
*
* {@link BuildSearchParamProcessor} instance.
* @param item The {@link Queue.Item} to test.
* @return {@code true} if the {@link Queue.Item} fits, otherwise {@code false}.
*/
public abstract boolean fitsSearchParams(Queue.Item item);
/**
* Does the supplied {@link Run} fit the {@link BuildSearchParams} used to create this
*
* <p>
* Implementations should call {@link #fitsSearchParams(Object)} after extracting the appropriate data from
* the {@link Run}.
*
* {@link BuildSearchParamProcessor} instance.
* @param run The {@link Run} to test.
* @return {@code true} if the {@link Run} fits, otherwise {@code false}.
*/
public abstract boolean fitsSearchParams(Run run);
/**
* Does the supplied "data" (extracted from a {@link Queue.Item} or {@link Run}) fit the {@link BuildSearchParams}
* used to create this {@link BuildSearchParamProcessor} instance.
*
* <p>
* This method implementation should do all of the work in terms of testing the extracted {@link Queue.Item}
* or {@link Run} data against the search criteria used to create the instance. The
* {@link #fitsSearchParams(hudson.model.Queue.Item)} and {@link #fitsSearchParams(hudson.model.Run)}
* implementations should call this function after extracting the appropriate data from the {@link Queue.Item}
* or {@link Run}.
*
* <p>
* This method makes unit testing of the {@link BuildSearchParamProcessor} implementation a little easier in
* that it allows the implementation to be hidden in the parent {@link BuildSearchParamProcessorFactory factory}
* class, plus allows testing without the need to create {@link Queue.Item} or {@link Run} instances, which can't
* be created without spinning a {@link jenkins.model.Jenkins} instance via a JenkinsRule (which in turn requires
* test classes to be in the test harness etc).
*
*
* @param data The data to test.
* @return {@code true} if the {@link Run} fits, otherwise {@code false}.
*/
public abstract boolean fitsSearchParams(T data);
}
/*
* The MIT License
*
* Copyright (c) 2013-2014, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.widgets.buildsearch;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import jenkins.widgets.buildsearch.processors.DateProcessorFactory;
import jenkins.widgets.buildsearch.processors.DescriptionProcessorFactory;
import jenkins.widgets.buildsearch.processors.NameProcessorFactory;
import jenkins.widgets.buildsearch.processors.ResultProcessorFactory;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* Implementations of this class create {@link BuildSearchParamProcessor} instances from a set
* of {@link BuildSearchParams}. The set of {@link BuildSearchParamProcessor} instances are then used
* by the {@link jenkins.widgets.HistoryPageFilter} class to filter the build history (via an instance of
* {@link BuildSearchParamProcessorList}).
*
* <p>
* Each {@link BuildSearchParamProcessor} implementation processes one or more search terms e.g. the
* {@link BuildSearchParamProcessor} instance created by the {@link NameProcessorFactory} checks the
* build {@link hudson.model.Queue.Item} or {@link hudson.model.Run} name using the "name:" search token(s)
* provided in the search, while the {@link DateProcessorFactory} checks date using one or both of the
* "date-from:" and "date-to:" tokens.
*
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public abstract class BuildSearchParamProcessorFactory implements ExtensionPoint {
private static List<BuildSearchParamProcessorFactory> coreParamProcessorFactories = new ArrayList<BuildSearchParamProcessorFactory>();
static {
// Statically adding the core BuildSearchParamProcessorFactory impls Vs adding via @Extension.
// Makes testing easier coz no need to create a JenkinsRule instance + put in test harness etc.
// Plugins etc can still contribute via @Extension.
coreParamProcessorFactories.add(new NameProcessorFactory());
coreParamProcessorFactories.add(new DescriptionProcessorFactory());
coreParamProcessorFactories.add(new ResultProcessorFactory());
coreParamProcessorFactories.add(new DateProcessorFactory());
}
/**
* Get the list of search terms that this processor is interested in.
*
* @return List of search terms.
*/
public abstract String[] getSearchTerms();
/**
* Create the {@link BuildSearchParamProcessor} instance using the supplied set of search parameters.
*
* @param searchParams The parsed search parameters.
* @return The {@link BuildSearchParamProcessor} instance to be used for searching, or {@code null} if the search
* parameters are such that {@link BuildSearchParamProcessor} instance of this type is not needed.
*/
public abstract @CheckForNull BuildSearchParamProcessor createProcessor(@Nonnull BuildSearchParams searchParams);
public static List<BuildSearchParamProcessorFactory> all() {
// TODO: Not convinced we want to bother with @Extension
ExtensionList<BuildSearchParamProcessorFactory> extensions = ExtensionList.lookup(BuildSearchParamProcessorFactory.class);
if (extensions.isEmpty()) {
// No BuildSearchParamProcessorFactory defined by @Extension
return coreParamProcessorFactories;
} else {
// One or more BuildSearchParamProcessorFactory defined by @Extension.
List<BuildSearchParamProcessorFactory> combined = new ArrayList<BuildSearchParamProcessorFactory>(coreParamProcessorFactories);
combined.addAll(extensions);
return combined;
}
}
public static Set<String> getAllSearchTerms() {
Set<String> allSearchTerms = new LinkedHashSet<String>();
for (BuildSearchParamProcessorFactory paramProcessorFactory : all()) {
allSearchTerms.addAll(Arrays.asList(paramProcessorFactory.getSearchTerms()));
}
return allSearchTerms;
}
}
/*
* The MIT License
*
* Copyright (c) 2013-2014, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.widgets.buildsearch;
import hudson.model.Queue;
import hudson.model.Run;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Wrapper class for the list of {@link jenkins.widgets.buildsearch.BuildSearchParamProcessor} needed to process/apply
* a set of {@link jenkins.widgets.buildsearch.BuildSearchParams}.
*
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public class BuildSearchParamProcessorList extends BuildSearchParamProcessor {
private final String searchString;
private final List<BuildSearchParamProcessor> processors;
/**
* Create a {@link BuildSearchParamProcessorList} instance from a set of {@link jenkins.widgets.buildsearch.BuildSearchParams}.
* @param searchParams The search parameters to use for creating the {@link BuildSearchParamProcessorList}.
*/
public BuildSearchParamProcessorList(@Nonnull BuildSearchParams searchParams) {
this.searchString = searchParams.getSearchString().toLowerCase();
if (searchParams.size() != 0) {
processors = new ArrayList();
for (BuildSearchParamProcessorFactory factory : BuildSearchParamProcessorFactory.all()) {
BuildSearchParamProcessor processor = factory.createProcessor(searchParams);
if (processor != null) {
processors.add(processor);
}
}
} else {
processors = Collections.emptyList();
}
}
public List<BuildSearchParamProcessor> getProcessors() {
return processors;
}
/**
* {@inheritDoc}
*/
@Override
public boolean fitsSearchParams(@Nonnull Queue.Item item) {
if (!processors.isEmpty()) {
for (BuildSearchParamProcessor processor : processors) {
if (!processor.fitsSearchParams(item)) {
return false;
}
}
// All the selected processors "liked" the search term.
return true;
} else {
if (fitsSearchParams(item.getDisplayName())) {
return true;
} else if (fitsSearchParams(item.getId())) {
return true;
}
// Non of the fuzzy matches "liked" the search term.
return false;
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean fitsSearchParams(@Nonnull Run run) {
if (!processors.isEmpty()) {
for (BuildSearchParamProcessor processor : processors) {
if (!processor.fitsSearchParams(run)) {
return false;
}
}
// All the selected processors "liked" the search term.
return true;
} else {
if (fitsSearchParams(run.getDisplayName())) {
return true;
} else if (fitsSearchParams(run.getDescription())) {
return true;
} else if (fitsSearchParams(run.getNumber())) {
return true;
} else if (fitsSearchParams(run.getQueueId())) {
return true;
} else if (fitsSearchParams(run.getResult())) {
return true;
}
// Non of the fuzzy matches "liked" the search term.
return false;
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean fitsSearchParams(Object data) {
if (data != null) {
if (data instanceof Number) {
return data.toString().equals(searchString);
} else {
return data.toString().toLowerCase().contains(searchString);
}
}
return false;
}
}
/*
* The MIT License
*
* Copyright (c) 2013-2014, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.widgets.buildsearch;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public class BuildSearchParams {
private final String searchString;
private final Map<String, List<BuildSearchParam>> searchParamsSets = newParamMap();
private boolean parsingComplete = false;
private static Map<String, List<BuildSearchParam>> newParamMap() {
Map<String, List<BuildSearchParam>> map = new LinkedHashMap<String, List<BuildSearchParam>>();
for (String searchTerm: BuildSearchParamProcessorFactory.getAllSearchTerms()) {
map.put(searchTerm, new ArrayList<BuildSearchParam>());
}
return Collections.unmodifiableMap(map);
}
public class BuildSearchParam {
public String searchTerm;
public String searchTermToken;
private String param;
// The start index (in this.searchString) of the search parameter (including the searchTermToken itself).
int startIndex;
// The end index (in this.searchString) of the search parameter, -1 being the end of the string.
int endIndex = -1;
BuildSearchParam(String searchTerm, String searchTermToken, int startIndex) {
this.searchTerm = searchTerm;
this.searchTermToken = searchTermToken;
this.startIndex = startIndex;
}
public String get() {
if (this.param != null) {
return this.param;
}
String param = searchString.substring(getEndOfTokenIndex(), getParamEndIndex());
param = param.trim();
if (parsingComplete) {
// We can cache the value if parsing is complete.
this.param = param;
}
return param;
}
private boolean isWhitespace() {
int paramStartIndex = getEndOfTokenIndex();
int paramEndIndex = getParamEndIndex();
for (int i = paramStartIndex; i < paramEndIndex; i++) {
if (!Character.isWhitespace(searchString.charAt(i))) {
return false;
}
}
return true;
}
private int getEndOfTokenIndex() {
return (startIndex + searchTermToken.length());
}
private int getParamEndIndex() {
return (endIndex != -1 ? endIndex : searchString.length());
}
@Override
public String toString() {
return searchTerm + ":'" + get() + "'";
}
}
public BuildSearchParams(@Nonnull String searchString) {
this.searchString = searchString;
if (searchString.trim().length() > 0) {
parseSearchString();
}
}
/**
* Get the underlying/raw search string as entered by the user.
* @return The underlying/raw search string as entered by the user.
*/
public String getSearchString() {
return searchString;
}
/**
* Get the total parameter count across all search terms.
* @return The total parameter count across all search terms.
*/
public int size() {
int paramCount = 0;
Collection<List<BuildSearchParam>> searchParams = searchParamsSets.values();
for (List<BuildSearchParam> searchParam : searchParams) {
paramCount += searchParam.size();
}
return paramCount;
}
public List<BuildSearchParam> getParams(@Nonnull String searchTerm) {
List<BuildSearchParam> params = searchParamsSets.get(searchTerm);
if (params == null) {
throw new IllegalArgumentException(String.format("Unknown search term '%s'.", searchTerm));
}
return params;
}
@Override
public String toString() {
return searchParamsSets.toString();
}
private void parseSearchString() {
// Step 1: find all search params in the searchString, recording their startIndex
List<BuildSearchParam> allSearchParams = new ArrayList<BuildSearchParam>();
String searchStringLower = searchString.toLowerCase();
for (String searchTerm : BuildSearchParamProcessorFactory.getAllSearchTerms()) {
String searchTermToken = searchTerm + ":";
int startIndex = 0;
while((startIndex = searchStringLower.indexOf(searchTermToken, startIndex)) != -1) {
allSearchParams.add(new BuildSearchParam(searchTerm, searchTermToken, startIndex));
startIndex += searchTerm.length();
}
}
// Step 2: Remove all empty/whitespace params i.e. token defined but no param
// Step 2: sort all search params by their startIndex. This will then make it easy to find the endIndexes (step 3)
Collections.sort(allSearchParams, new Comparator<BuildSearchParam>() {
@Override
public int compare(BuildSearchParam param1, BuildSearchParam param2) {
return param1.startIndex - param2.startIndex;
}
});
// Step 3: find the endIndex for each search param.
// That's easy: it's the startIndex of the next search param, or the end of the searchString for the last.
int numParams = allSearchParams.size();
for (int i = 0; i < numParams; i++) {
BuildSearchParam searchParam = allSearchParams.get(i);
if (i == numParams - 1) {
// last one, so leave endIndex as -1
} else {
BuildSearchParam nextSearchParam = allSearchParams.get(i + 1);
searchParam.endIndex = nextSearchParam.startIndex;
}
// And add it to the top level searchParamsSets Map, but only if it's not whitespace.
if (!searchParam.isWhitespace()) {
searchParamsSets.get(searchParam.searchTerm).add(searchParam);
}
}
parsingComplete = true;
}
}
__TODO__: Move to jenkins-ci web/wiki if changes accepted.
# Fuzzy Search
This is obviously the easiest form of build item search. Just enter a string in the "find" box and build
items/runs will be matched across a number of predefined fields (number, name, status description etc).
For more control, see the next section.
# Search Terms
* [__name:__] : Is contained in the name of the build.
* [__desc:__] : Is contained in the description of the build.d.
* [__result:__] : Is the "result" of the build (SUCCESS, UNSTABLE, FAILURE, NOT_BUILT, ABORTED).
* [__date-from:__] and/or [__date-to:__] : Build date is before, after or between. Date format is *yyyy-MM-yy*. Date is start of day i.e. 00:00. Also supports some shorthands e.g. "today", "yesterday", "1 day", "2 days", "2 weeks", "2 months".
Different search terms are __AND__ together to result in a matching Build, while multiples of the same search term are __OR__'d together e.g.
* `result: FAILURE desc: staging desc: production`
Is interpreted as: "(result: FAILURE) AND (desc: staging OR desc: production)"
# Examples
Builds that contain the words "staging" or "production" in the description:
* `desc: staging desc: production`
Failed builds in last week (7 days):
* `result: FAILURE date-from: 1 week`
Failed builds since a specific date:
* `result: FAILURE date-from: 2015-03-31`
Unstable builds in March:
* `result: UNSTABLE date-from: 2015-03-01 date-to: 2015-04-01`
/*
* The MIT License
*
* Copyright (c) 2013-2014, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.widgets.buildsearch.processors;
import hudson.model.Queue;
import hudson.model.Run;
import jenkins.widgets.buildsearch.BuildSearchParamProcessor;
import jenkins.widgets.buildsearch.BuildSearchParamProcessorFactory;
import jenkins.widgets.buildsearch.BuildSearchParams;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* Search build history by {@link hudson.model.Queue.Item#getInQueueSince()}
* or {@link hudson.model.Run#getTimeInMillis()} using one or both of the
* "date-from:" and "date-to:" tokens. Also supports some shorthands such as "today"
*
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public class DateProcessorFactory extends BuildSearchParamProcessorFactory {
public static String LONG_DATE_FORMAT = "yyyy-MM-dd";
public static String MEDIUM_DATE_FORMAT = "yy-MM-dd";
public static String SHORT_DATE_FORMAT = "MM-dd";
public static final long DAY = 1000 * 60 * 60 * 24;
public static final long WEEK = DAY * 7;
public static final long MONTH = WEEK * 4;
/**
* "date-from" search term.
*/
public static final String DATE_FROM_ST = "date-from";
/**
* "date-to" search term.
*/
public static final String DATE_TO_ST = "date-to";
private static final String[] SEARCH_TERMS = new String[] {DATE_FROM_ST, DATE_TO_ST};
/**
* {@inheritDoc}
*/
@Override
public String[] getSearchTerms() {
return SEARCH_TERMS;
}
/**
* {@inheritDoc}
*/
@Override
public BuildSearchParamProcessor createProcessor(BuildSearchParams searchParams) {
final DateFromTo dateFromTo = new DateFromTo(searchParams);
if (dateFromTo.dateFrom == null && dateFromTo.dateTo == null) {
return null;
}
return new BuildSearchParamProcessor<Long>() {
@Override
public boolean fitsSearchParams(Long timeInMillis) {
if (dateFromTo.dateFrom != null && timeInMillis < dateFromTo.dateFrom) {
return false;
}
if (dateFromTo.dateTo != null && timeInMillis > dateFromTo.dateTo) {
return false;
}
return true;
}
@Override
public boolean fitsSearchParams(Queue.Item item) {
return fitsSearchParams(item.getInQueueSince());
}
@Override
public boolean fitsSearchParams(Run run) {
return fitsSearchParams(run.getTimeInMillis());
}
};
}
private class DateFromTo {
private Long dateFrom;
private Long dateTo;
private DateFromTo(BuildSearchParams searchParams) {
final List<BuildSearchParams.BuildSearchParam> dateFromParams = searchParams.getParams(DATE_FROM_ST);
final List<BuildSearchParams.BuildSearchParam> dateToParams = searchParams.getParams(DATE_TO_ST);
if (dateFromParams.isEmpty() && dateToParams.isEmpty()) {
// none of the date search terms are specified
return;
}
// Only supports spec of a single pair of "date-from:" and "date-to:" search params.
if (!dateFromParams.isEmpty()) {
dateFrom = toTimeInMillis(dateFromParams.get(0).get());
} else {
dateFrom = null;
}
if (!dateToParams.isEmpty()) {
dateTo = toTimeInMillis(dateToParams.get(0).get());
} else {
dateTo = null;
}
if (dateFrom != null && dateTo != null && dateFrom > dateTo) {
dateTo = null;
}
}
}
// make this available to test code too
public static Long toTimeInMillis(String date) {
date = date.toLowerCase();
if (date.equals("today")) {
return getToday();
} else if (date.equals("yesterday")) {
return getYesterday();
} else if (date.endsWith("day") || date.endsWith("days")) {
return getToday() - (extractMultiple(date) * DAY);
} else if (date.endsWith("week") || date.endsWith("weeks")) {
return getToday() - (extractMultiple(date) * WEEK);
} else if (date.endsWith("month") || date.endsWith("months")) {
return getToday() - (extractMultiple(date) * MONTH);
}
try {
// SimpleDateFormat is not thread safe. What a PITA !!!
date = normalize(date);
if (date.length() == LONG_DATE_FORMAT.length()) {
return new SimpleDateFormat(LONG_DATE_FORMAT).parse(date).getTime();
} else if (date.length() == MEDIUM_DATE_FORMAT.length()) {
return new SimpleDateFormat(MEDIUM_DATE_FORMAT).parse(date).getTime();
} else {
return null;
}
} catch (ParseException e) {
return null;
}
}
private static long extractMultiple(String date) {
if (Character.isDigit(date.charAt(0))) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < date.length(); i++) {
char c = date.charAt(i);
if (!Character.isDigit(c)) {
break;
}
builder.append(c);
}
return new Long(builder.toString());
}
return 1;
}
// make this available to test code too
public static String normalize(String date) {
// In case the user forgot the format and used '/' instead of '-' as the delimiter.
date = date.replace('/', '-');
// In case there are spaces
date = date.replace(" ", "");
date = date.toLowerCase();
if (date.length() == SHORT_DATE_FORMAT.length()) {
date = getCurrentYearString() + "-" + date;
}
return date;
}
public static String getCurrentYearString() {
return new SimpleDateFormat("yyyy").format(new Date());
}
public static long getToday() {
long now = System.currentTimeMillis();
long partOfToday = now % DAY;
long startOfToday = now - partOfToday;
return startOfToday;
}
public static long getTomorrow() {
return getToday() + DAY;
}
public static long getYesterday() {
return getToday() - DAY;
}
}
/*
* The MIT License
*
* Copyright (c) 2013-2014, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.widgets.buildsearch.processors;
import hudson.model.Queue;
import hudson.model.Run;
import jenkins.widgets.buildsearch.BuildSearchParamProcessor;
import jenkins.widgets.buildsearch.BuildSearchParamProcessorFactory;
import jenkins.widgets.buildsearch.BuildSearchParams;
import java.util.List;
/**
* Search build history by {@link hudson.model.Run} description.
*
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public class DescriptionProcessorFactory extends BuildSearchParamProcessorFactory {
/**
* "desc" (description) search term.
*/
public static final String DESC_ST = "desc";
private static final String[] SEARCH_TERMS = new String[] {DESC_ST}; // Build description
/**
* {@inheritDoc}
*/
@Override
public String[] getSearchTerms() {
return SEARCH_TERMS;
}
/**
* {@inheritDoc}
*/
@Override
public BuildSearchParamProcessor createProcessor(BuildSearchParams searchParams) {
final List<BuildSearchParams.BuildSearchParam> descParams = searchParams.getParams(DESC_ST);
if (descParams.isEmpty()) {
// "desc" search term not specified in search
return null;
}
return new BuildSearchParamProcessor<String>() {
@Override
public boolean fitsSearchParams(String description) {
if (description == null) {
return false;
}
// It fits if it contains any of the specified "desc" search terms.
for (BuildSearchParams.BuildSearchParam nameParam : descParams) {
if (description.contains(nameParam.get())) {
return true;
}
}
return false;
}
@Override
public boolean fitsSearchParams(Queue.Item item) {
// Queue items don't have a description.
return false;
}
@Override
public boolean fitsSearchParams(Run run) {
return fitsSearchParams(run.getDescription());
}
};
}
}
/*
* The MIT License
*
* Copyright (c) 2013-2014, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.widgets.buildsearch.processors;
import hudson.model.Queue;
import hudson.model.Run;
import jenkins.widgets.buildsearch.BuildSearchParamProcessor;
import jenkins.widgets.buildsearch.BuildSearchParamProcessorFactory;
import jenkins.widgets.buildsearch.BuildSearchParams;
import java.util.List;
/**
* Search build history by {@link hudson.model.Queue.Item} or {@link hudson.model.Run} name.
*
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public class NameProcessorFactory extends BuildSearchParamProcessorFactory {
/**
* "name" search term.
*/
public static final String NAME_ST = "name";
private static final String[] SEARCH_TERMS = new String[] {NAME_ST}; // Build name
/**
* {@inheritDoc}
*/
@Override
public String[] getSearchTerms() {
return SEARCH_TERMS;
}
/**
* {@inheritDoc}
*/
@Override
public BuildSearchParamProcessor createProcessor(BuildSearchParams searchParams) {
final List<BuildSearchParams.BuildSearchParam> nameParams = searchParams.getParams(NAME_ST);
if (nameParams.isEmpty()) {
// "name" search term not specified in search
return null;
}
return new BuildSearchParamProcessor<String>() {
@Override
public boolean fitsSearchParams(String name) {
if (name == null) {
return false;
}
// It fits if it contains any of the specified "name" search terms.
for (BuildSearchParams.BuildSearchParam nameParam : nameParams) {
if (name.contains(nameParam.get())) {
return true;
}
}
return false;
}
@Override
public boolean fitsSearchParams(Queue.Item item) {
return fitsSearchParams(item.getDisplayName());
}
@Override
public boolean fitsSearchParams(Run run) {
return fitsSearchParams(run.getDisplayName());
}
};
}
}
/*
* The MIT License
*
* Copyright (c) 2013-2014, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.widgets.buildsearch.processors;
import hudson.model.Queue;
import hudson.model.Result;
import hudson.model.Run;
import jenkins.widgets.buildsearch.BuildSearchParamProcessor;
import jenkins.widgets.buildsearch.BuildSearchParamProcessorFactory;
import jenkins.widgets.buildsearch.BuildSearchParams;
import java.util.ArrayList;
import java.util.List;
/**
* Search build history by {@link hudson.model.Run} result.
*
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public class ResultProcessorFactory extends BuildSearchParamProcessorFactory {
/**
* "result" search term.
*/
public static final String RESULT_ST = "result";
private static final String[] SEARCH_TERMS = new String[] {RESULT_ST}; // TODO: Could do "status" too or maybe that's different
/**
* {@inheritDoc}
*/
@Override
public String[] getSearchTerms() {
return SEARCH_TERMS;
}
/**
* {@inheritDoc}
*/
@Override
public BuildSearchParamProcessor createProcessor(BuildSearchParams searchParams) {
List<BuildSearchParams.BuildSearchParam> resultParams = searchParams.getParams(RESULT_ST);
if (resultParams.isEmpty()) {
// "result" search term not specified in search
return null;
}
final List<Result> interestingResults = new ArrayList<Result>();
for (BuildSearchParams.BuildSearchParam resultParam : resultParams) {
Result result = Result.fromString(resultParam.get());
// Result.fromString returns Result.FAILURE if there's no match. We don't want that.
if (result.toString().equalsIgnoreCase(resultParam.get())) {
interestingResults.add(result);
}
}
return new BuildSearchParamProcessor<Result>() {
@Override
public boolean fitsSearchParams(Result result) {
if (result == null) {
return false;
}
// It fits if it's any of the specified "result" search terms.
return interestingResults.contains(result);
}
@Override
public boolean fitsSearchParams(Queue.Item item) {
// Queue items don't have a Result.
return false;
}
@Override
public boolean fitsSearchParams(Run run) {
return fitsSearchParams(run.getResult());
}
};
}
}
...@@ -68,9 +68,6 @@ THE SOFTWARE. ...@@ -68,9 +68,6 @@ THE SOFTWARE.
<span class="clear" title="${%Clear}">x</span> <span class="clear" title="${%Clear}">x</span>
<input placeholder="${%find}"></input> <input placeholder="${%find}"></input>
</td> </td>
<td class="build-search-help">
<a href="https://wiki.jenkins-ci.org/display/JENKINS/Build+History+Search" target="_blank"><l:icon class="icon-help icon-sm"/></a>
</td>
</tr> </tr>
</table> </table>
</td> </td>
...@@ -120,9 +117,8 @@ THE SOFTWARE. ...@@ -120,9 +117,8 @@ THE SOFTWARE.
</tr> </tr>
</j:if> </j:if>
</l:pane> </l:pane>
<j:if test="!empty(it.owner.nextBuildNumber)"> </div>
<script defer="true"> <script defer="true">
updateBuildHistory("${it.baseUrl}/buildHistory/ajax",${it.owner.nextBuildNumber}); updateBuildHistory("${it.baseUrl}/buildHistory/ajax",${it.owner.nextBuildNumber});
</script> </script>
</j:if>
</j:jelly> </j:jelly>
/*
* The MIT License
*
* Copyright (c) 2013-2014, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.widgets.buildsearch;
import hudson.model.Result;
import jenkins.widgets.buildsearch.processors.DateProcessorFactory;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
/**
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public class BuildSearchParamProcessorTest {
@Test
public void test_name() {
BuildSearchParams searchParams = new BuildSearchParams(" name: Build1 name: Build2 ");
List<BuildSearchParamProcessor> processors = new BuildSearchParamProcessorList(searchParams).getProcessors();
Assert.assertEquals(1, processors.size());
BuildSearchParamProcessor<String> processor = processors.get(0);
Assert.assertTrue(processor.fitsSearchParams("Build1 was good"));
Assert.assertTrue(processor.fitsSearchParams("The Build2 not so good"));
Assert.assertFalse(processor.fitsSearchParams("Build3"));
}
@Test
public void test_description() {
BuildSearchParams searchParams = new BuildSearchParams(" desc: Build1 desc: Build2 ");
List<BuildSearchParamProcessor> processors = new BuildSearchParamProcessorList(searchParams).getProcessors();
Assert.assertEquals(1, processors.size());
BuildSearchParamProcessor<String> processor = processors.get(0);
Assert.assertTrue(processor.fitsSearchParams("Build1 was good"));
Assert.assertTrue(processor.fitsSearchParams("The Build2 not so good"));
Assert.assertFalse(processor.fitsSearchParams("Build3"));
}
@Test
public void test_result() {
BuildSearchParams searchParams = new BuildSearchParams(" result: UNSTABLE result: aborted result: BLAH ");
List<BuildSearchParamProcessor> processors = new BuildSearchParamProcessorList(searchParams).getProcessors();
Assert.assertEquals(1, processors.size());
BuildSearchParamProcessor<Result> processor = processors.get(0);
Assert.assertTrue(processor.fitsSearchParams(Result.UNSTABLE));
Assert.assertTrue(processor.fitsSearchParams(Result.ABORTED));
Assert.assertFalse(processor.fitsSearchParams(Result.SUCCESS));
Assert.assertFalse(processor.fitsSearchParams(Result.FAILURE)); // need to make sure "BLAH" is not interpreted as FAILURE. See Result.fromString.
}
@Test
public void test_date_from_and_to_ok() {
BuildSearchParams searchParams = new BuildSearchParams("date-from: 2015-02-20 date-to: 2015-03-20");
List<BuildSearchParamProcessor> processors = new BuildSearchParamProcessorList(searchParams).getProcessors();
Assert.assertEquals(1, processors.size());
BuildSearchParamProcessor<Long> processor = processors.get(0);
Assert.assertFalse(processor.fitsSearchParams(DateProcessorFactory.toTimeInMillis("2015-02-19")));
Assert.assertTrue(processor.fitsSearchParams(DateProcessorFactory.toTimeInMillis("2015-02-20")));
Assert.assertTrue(processor.fitsSearchParams(DateProcessorFactory.toTimeInMillis("2015-02-21")));
Assert.assertTrue(processor.fitsSearchParams(DateProcessorFactory.toTimeInMillis("2015-03-19")));
Assert.assertFalse(processor.fitsSearchParams(DateProcessorFactory.toTimeInMillis("2015-03-21")));
}
/**
* date-to: set to a date before date-from: should result in the date-to: being ignored.
*/
@Test
public void test_date_from_and_to_with_to_before_from() {
BuildSearchParams searchParams = new BuildSearchParams("date-from:2015-02-20 date-to:2015-02-10");
List<BuildSearchParamProcessor> processors = new BuildSearchParamProcessorList(searchParams).getProcessors();
Assert.assertEquals(1, processors.size());
BuildSearchParamProcessor<Long> processor = processors.get(0);
Assert.assertFalse(processor.fitsSearchParams(DateProcessorFactory.toTimeInMillis("2015-02-19")));
Assert.assertTrue(processor.fitsSearchParams(DateProcessorFactory.toTimeInMillis("2015-02-20")));
Assert.assertTrue(processor.fitsSearchParams(DateProcessorFactory.toTimeInMillis("2015-02-21")));
Assert.assertTrue(processor.fitsSearchParams(DateProcessorFactory.toTimeInMillis("2015-03-21")));
}
@Test
public void test_date_short() {
List<BuildSearchParamProcessor> processors = new BuildSearchParamProcessorList(new BuildSearchParams("date-from: 02-20")).getProcessors();
BuildSearchParamProcessor<Long> processor = processors.get(0);
Assert.assertFalse(processor.fitsSearchParams(DateProcessorFactory.toTimeInMillis(DateProcessorFactory.getCurrentYearString() + "-02-19")));
Assert.assertTrue(processor.fitsSearchParams(DateProcessorFactory.toTimeInMillis(DateProcessorFactory.getCurrentYearString() + "-02-20")));
}
@Test
public void test_date_today() {
List<BuildSearchParamProcessor> processors = new BuildSearchParamProcessorList(new BuildSearchParams("date-from: today")).getProcessors();
BuildSearchParamProcessor<Long> processor = processors.get(0);
long now = System.currentTimeMillis();
Assert.assertFalse(processor.fitsSearchParams(now - DateProcessorFactory.DAY));
Assert.assertTrue(processor.fitsSearchParams(now));
Assert.assertTrue(processor.fitsSearchParams(now + DateProcessorFactory.DAY));
}
@Test
public void test_date_last_week_to_today() {
List<BuildSearchParamProcessor> processors = new BuildSearchParamProcessorList(new BuildSearchParams("date-from: week date-to: today")).getProcessors();
BuildSearchParamProcessor<Long> processor = processors.get(0);
long now = System.currentTimeMillis();
Assert.assertFalse(processor.fitsSearchParams(now - (DateProcessorFactory.DAY * 8)));
Assert.assertTrue(processor.fitsSearchParams(now - (DateProcessorFactory.DAY * 7)));
Assert.assertTrue(processor.fitsSearchParams(now - (DateProcessorFactory.DAY * 1)));
Assert.assertFalse(processor.fitsSearchParams(now));
}
@Test
public void test_date_days() {
List<BuildSearchParamProcessor> processors = new BuildSearchParamProcessorList(new BuildSearchParams("date-from: 5 days date-to: 1 day")).getProcessors();
BuildSearchParamProcessor<Long> processor = processors.get(0);
long now = System.currentTimeMillis();
Assert.assertFalse(processor.fitsSearchParams(now - (DateProcessorFactory.DAY * 6)));
Assert.assertTrue(processor.fitsSearchParams(now - (DateProcessorFactory.DAY * 5)));
Assert.assertFalse(processor.fitsSearchParams(now));
}
@Test
public void test_date_weeks() {
List<BuildSearchParamProcessor> processors = new BuildSearchParamProcessorList(new BuildSearchParams("date-from: 5 weeks date-to: 1 week")).getProcessors();
BuildSearchParamProcessor<Long> processor = processors.get(0);
long now = System.currentTimeMillis();
Assert.assertFalse(processor.fitsSearchParams(now - (DateProcessorFactory.WEEK * 6)));
Assert.assertTrue(processor.fitsSearchParams(now - (DateProcessorFactory.WEEK * 5)));
Assert.assertFalse(processor.fitsSearchParams(now - (DateProcessorFactory.DAY * 6)));
}
@Test
public void test_date_months() {
List<BuildSearchParamProcessor> processors = new BuildSearchParamProcessorList(new BuildSearchParams("date-from: 5 months date-to: 1 month")).getProcessors();
BuildSearchParamProcessor<Long> processor = processors.get(0);
long now = System.currentTimeMillis();
Assert.assertFalse(processor.fitsSearchParams(now - (DateProcessorFactory.MONTH * 6)));
Assert.assertTrue(processor.fitsSearchParams(now - (DateProcessorFactory.MONTH * 5)));
Assert.assertFalse(processor.fitsSearchParams(now - (DateProcessorFactory.MONTH - DateProcessorFactory.DAY)));
}
}
/*
* The MIT License
*
* Copyright (c) 2013-2014, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.widgets.buildsearch;
import org.junit.Assert;
import org.junit.Test;
/**
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public class BuildSearchParamsTest {
@Test
public void test_no_params() {
Assert.assertEquals("{name=[], desc=[], result=[], date-from=[], date-to=[]}", new BuildSearchParams("").toString());
Assert.assertEquals("{name=[], desc=[], result=[], date-from=[], date-to=[]}", new BuildSearchParams("blah").toString());
Assert.assertEquals("{name=[], desc=[], result=[], date-from=[], date-to=[]}", new BuildSearchParams("name: desc: ").toString()); // whitespace only params should be ignored
}
@Test
public void test_unknown_search_term() {
try {
new BuildSearchParams("").getParams("blah");
Assert.fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
Assert.assertEquals("Unknown search term 'blah'.", e.getMessage());
}
}
@Test
public void test_with_params_one_of_each() {
Assert.assertEquals(
"{name=[name:'Build1'], desc=[desc:'something big'], result=[result:'FAILED'], date-from=[], date-to=[]}",
new BuildSearchParams(" result: FAILED Name: Build1 desc: something big ").toString());
}
@Test
public void test_with_params_multiples_of_each() {
BuildSearchParams searchParams =
new BuildSearchParams(" result: FAILED name: Build1 desc: something big result:SUCCESS desc: something middle ");
Assert.assertEquals("[name:'Build1']",
searchParams.getParams("name").toString());
Assert.assertEquals("[desc:'something big', desc:'something middle']",
searchParams.getParams("desc").toString());
Assert.assertEquals("[result:'FAILED', result:'SUCCESS']",
searchParams.getParams("result").toString());
Assert.assertEquals(
"{name=[name:'Build1'], desc=[desc:'something big', desc:'something middle'], result=[result:'FAILED', result:'SUCCESS'], date-from=[], date-to=[]}",
searchParams.toString());
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册