blob: ee53e94064c4dc3bf887dee0288e56167f9c15af [file] [log] [blame]
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001// Copyright 2014 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.packages;
16
17import com.google.common.base.Splitter;
18import com.google.common.collect.Maps;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000019import com.google.devtools.build.lib.syntax.Type;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import com.google.devtools.common.options.Converter;
21import com.google.devtools.common.options.OptionsParsingException;
22
23import java.util.ArrayList;
24import java.util.EnumMap;
25import java.util.List;
26import java.util.Map;
27import java.util.Set;
28
29/**
30 * Symbolic labels of test timeout. Borrows heavily from {@link TestSize}.
31 */
32public enum TestTimeout {
33
34 // These symbolic labels are used in the build files.
35 SHORT(0, 60, 60),
36 MODERATE(30, 300, 300),
37 LONG(300, 900, 900),
38 ETERNAL(900, 365 * 24 * 60 /* One year */, 3600);
39
40 /**
41 * Default --test_timeout flag, used when collecting code coverage.
42 */
Lukacs Berkide2bba72015-04-16 11:16:38 +000043 public static final String COVERAGE_CMD_TIMEOUT = "--test_timeout=300,600,1200,3600";
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010044
45 private final Integer rangeMin;
46 private final Integer rangeMax;
47 private final Integer timeout;
48
49 private TestTimeout(Integer rangeMin, Integer rangeMax, Integer timeout) {
50 this.rangeMin = rangeMin;
51 this.rangeMax = rangeMax;
52 this.timeout = timeout;
53 }
54
55 /**
56 * Returns the enum associated with a test's timeout or null if the tag is
57 * not lower case or an unknown size.
58 */
59 public static TestTimeout getTestTimeout(String attr) {
60 if (!attr.equals(attr.toLowerCase())) {
61 return null;
62 }
63 try {
64 return TestTimeout.valueOf(attr.toUpperCase());
65 } catch (IllegalArgumentException e) {
66 return null;
67 }
68 }
69
70 @Override
71 public String toString() {
72 return super.toString().toLowerCase();
73 }
74
75 /**
76 * We print to upper case to make the test timeout warnings more readable.
77 */
78 public String prettyPrint() {
79 return super.toString().toUpperCase();
80 }
81
82 public Integer getTimeout() {
83 return timeout;
84 }
85 /**
86 * Returns true iff the given time in seconds is exactly in the range of valid
87 * execution times for this TestSize.
88 */
89 public boolean isInRangeExact(Integer timeInSeconds) {
90 return timeInSeconds >= rangeMin && timeInSeconds < rangeMax;
91 }
92
93 /**
94 * Returns true iff the given time in seconds is approximately (+/- 75%) in the range of valid
95 * execution times for this TestSize.
96 */
97 public boolean isInRangeFuzzy(Integer timeInSeconds) {
98 return timeInSeconds >= rangeMin - (rangeMin * .75)
99 && (this == ETERNAL || timeInSeconds <= rangeMax + (rangeMax * .75));
100 }
101
102 /**
103 * Returns suggested test size for the given time in seconds.
104 */
105 public static TestTimeout getSuggestedTestTimeout(Integer timeInSeconds) {
106 for (TestTimeout testTimeout : values()) {
107 if (testTimeout.isInRangeExact(timeInSeconds)) {
108 return testTimeout;
109 }
110 }
111 return ETERNAL;
112 }
113
114 /**
115 * Returns test timeout of the given test target using explicitly specified timeout
116 * or default through to the size label's associated default.
117 */
118 public static TestTimeout getTestTimeout(Rule testTarget) {
119 String attr = NonconfigurableAttributeMapper.of(testTarget).get("timeout", Type.STRING);
120 if (!attr.equals(attr.toLowerCase())) {
121 return null; // attribute values must be lowercase
122 }
123 try {
124 return TestTimeout.valueOf(attr.toUpperCase());
125 } catch (IllegalArgumentException e) {
126 return null;
127 }
128 }
129
130 /**
131 * Converter for the --test_timeout option.
132 */
133 public static class TestTimeoutConverter implements Converter<Map<TestTimeout, Integer>> {
134 public TestTimeoutConverter() {}
135
136 @Override
137 public Map<TestTimeout, Integer> convert(String input) throws OptionsParsingException {
138 List<Integer> values = new ArrayList<>();
139 for (String token : Splitter.on(',').limit(6).split(input)) {
140 // Handle the case of "2," which is accepted as legal... Because Splitter.split is lazy,
141 // there's no way of knowing if an empty string is a trailing or an intermediate one,
142 // so we can't fully emulate String.split(String, 0).
143 if (!token.isEmpty() || values.size() > 1) {
144 try {
145 values.add(Integer.valueOf(token));
146 } catch (NumberFormatException e) {
147 throw new OptionsParsingException("'" + input + "' is not an int");
148 }
149 }
150 }
151 EnumMap<TestTimeout, Integer> timeouts = Maps.newEnumMap(TestTimeout.class);
152 if (values.size() == 1) {
153 timeouts.put(SHORT, values.get(0));
154 timeouts.put(MODERATE, values.get(0));
155 timeouts.put(LONG, values.get(0));
156 timeouts.put(ETERNAL, values.get(0));
157 } else if (values.size() == 4) {
158 timeouts.put(SHORT, values.get(0));
159 timeouts.put(MODERATE, values.get(1));
160 timeouts.put(LONG, values.get(2));
161 timeouts.put(ETERNAL, values.get(3));
162 } else {
163 throw new OptionsParsingException("Invalid number of comma-separated entries");
164 }
165 for (TestTimeout label : values()) {
166 if (!timeouts.containsKey(label) || timeouts.get(label) <= 0) {
167 timeouts.put(label, label.getTimeout());
168 }
169 }
170 return timeouts;
171 }
172
173 @Override
174 public String getTypeDescription() {
175 return "a single integer or comma-separated list of 4 integers";
176 }
177 }
178
179 /**
180 * Converter for the --test_timeout_filters option.
181 */
182 public static class TestTimeoutFilterConverter extends EnumFilterConverter<TestTimeout> {
183 public TestTimeoutFilterConverter() {
184 super(TestTimeout.class, "test timeout");
185 }
186
187 /**
188 * {@inheritDoc}
189 *
190 * <p>This override is necessary to prevent OptionsData
191 * from throwing a "must be assignable from the converter return type" exception.
192 * OptionsData doesn't recognize the generic type and actual type are the same.
193 */
194 @Override
195 public final Set<TestTimeout> convert(String input) throws OptionsParsingException {
196 return super.convert(input);
197 }
198 }
199}