Florian Weikert | d7b64bd | 2015-09-29 14:10:16 +0000 | [diff] [blame] | 1 | // Copyright 2015 The Bazel Authors. All Rights Reserved. |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 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 | |
| 15 | package com.google.devtools.build.lib.syntax; |
| 16 | |
| 17 | import static com.google.common.truth.Truth.assertThat; |
brandjon | a9cafcb | 2018-04-04 12:24:33 -0700 | [diff] [blame] | 18 | import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows; |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 19 | import static org.junit.Assert.fail; |
| 20 | |
Florian Weikert | 2591e19 | 2015-10-05 14:24:51 +0000 | [diff] [blame] | 21 | import com.google.common.base.Joiner; |
| 22 | import com.google.common.base.Strings; |
Klaus Aehlig | 26c86f8 | 2018-05-29 06:57:03 -0700 | [diff] [blame] | 23 | import com.google.common.collect.ImmutableList; |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 24 | import com.google.common.collect.ImmutableMap; |
Lukacs Berki | 6e91eb9 | 2015-09-21 09:12:37 +0000 | [diff] [blame] | 25 | import com.google.devtools.build.lib.cmdline.Label; |
vladmos | 6ff634d | 2017-07-05 10:25:01 -0400 | [diff] [blame] | 26 | import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter; |
| 27 | import com.google.devtools.build.lib.skylarkinterface.SkylarkValue; |
Francois-Rene Rideau | a2c9ac6 | 2016-01-22 10:54:38 +0000 | [diff] [blame] | 28 | import com.google.devtools.build.lib.syntax.SkylarkList.MutableList; |
| 29 | import com.google.devtools.build.lib.syntax.SkylarkList.Tuple; |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 30 | import java.util.Arrays; |
| 31 | import java.util.IllegalFormatException; |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 32 | import java.util.LinkedHashMap; |
Florian Weikert | 2591e19 | 2015-10-05 14:24:51 +0000 | [diff] [blame] | 33 | import java.util.LinkedList; |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 34 | import java.util.List; |
| 35 | import java.util.Map; |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 36 | import org.junit.Test; |
| 37 | import org.junit.runner.RunWith; |
| 38 | import org.junit.runners.JUnit4; |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 39 | |
| 40 | /** |
| 41 | * Test properties of the evaluator's datatypes and utility functions |
| 42 | * without actually creating any parse trees. |
| 43 | */ |
| 44 | @RunWith(JUnit4.class) |
| 45 | public class PrinterTest { |
| 46 | |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 47 | @Test |
| 48 | public void testPrinter() throws Exception { |
| 49 | // Note that prettyPrintValue and printValue only differ on behaviour of |
| 50 | // labels and strings at toplevel. |
vladmos | cd6d8ae | 2017-10-12 15:35:17 +0200 | [diff] [blame] | 51 | assertThat(Printer.str(createObjWithStr())).isEqualTo("<str marker>"); |
| 52 | assertThat(Printer.repr(createObjWithStr())).isEqualTo("<repr marker>"); |
| 53 | |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 54 | assertThat(Printer.str("foo\nbar")).isEqualTo("foo\nbar"); |
| 55 | assertThat(Printer.repr("foo\nbar")).isEqualTo("\"foo\\nbar\""); |
| 56 | assertThat(Printer.str("'")).isEqualTo("'"); |
| 57 | assertThat(Printer.repr("'")).isEqualTo("\"'\""); |
| 58 | assertThat(Printer.str("\"")).isEqualTo("\""); |
| 59 | assertThat(Printer.repr("\"")).isEqualTo("\"\\\"\""); |
| 60 | assertThat(Printer.str(3)).isEqualTo("3"); |
| 61 | assertThat(Printer.repr(3)).isEqualTo("3"); |
| 62 | assertThat(Printer.repr(Runtime.NONE)).isEqualTo("None"); |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 63 | |
dannark | 90e2b4b | 2018-06-27 13:35:04 -0700 | [diff] [blame] | 64 | assertThat(Printer.str(Label.parseAbsolute("//x", ImmutableMap.of()))).isEqualTo("//x:x"); |
| 65 | assertThat(Printer.repr(Label.parseAbsolute("//x", ImmutableMap.of()))) |
| 66 | .isEqualTo("Label(\"//x:x\")"); |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 67 | |
Francois-Rene Rideau | a2c9ac6 | 2016-01-22 10:54:38 +0000 | [diff] [blame] | 68 | List<?> list = MutableList.of(null, "foo", "bar"); |
| 69 | List<?> tuple = Tuple.of("foo", "bar"); |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 70 | |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 71 | assertThat(Printer.str(Tuple.of(1, list, 3))).isEqualTo("(1, [\"foo\", \"bar\"], 3)"); |
| 72 | assertThat(Printer.repr(Tuple.of(1, list, 3))).isEqualTo("(1, [\"foo\", \"bar\"], 3)"); |
| 73 | assertThat(Printer.str(MutableList.of(null, 1, tuple, 3))) |
| 74 | .isEqualTo("[1, (\"foo\", \"bar\"), 3]"); |
| 75 | assertThat(Printer.repr(MutableList.of(null, 1, tuple, 3))) |
| 76 | .isEqualTo("[1, (\"foo\", \"bar\"), 3]"); |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 77 | |
Lukacs Berki | cc9ac3f | 2015-06-16 06:42:41 +0000 | [diff] [blame] | 78 | Map<Object, Object> dict = ImmutableMap.<Object, Object>of( |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 79 | 1, tuple, |
| 80 | 2, list, |
Francois-Rene Rideau | a2c9ac6 | 2016-01-22 10:54:38 +0000 | [diff] [blame] | 81 | "foo", MutableList.of(null)); |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 82 | assertThat(Printer.str(dict)) |
| 83 | .isEqualTo("{1: (\"foo\", \"bar\"), 2: [\"foo\", \"bar\"], \"foo\": []}"); |
| 84 | assertThat(Printer.repr(dict)) |
| 85 | .isEqualTo("{1: (\"foo\", \"bar\"), 2: [\"foo\", \"bar\"], \"foo\": []}"); |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 86 | } |
| 87 | |
Francois-Rene Rideau | 8d5cce3 | 2015-06-16 23:12:04 +0000 | [diff] [blame] | 88 | private void checkFormatPositionalFails(String errorMessage, String format, Object... arguments) { |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 89 | try { |
Francois-Rene Rideau | 8d5cce3 | 2015-06-16 23:12:04 +0000 | [diff] [blame] | 90 | Printer.format(format, arguments); |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 91 | fail(); |
| 92 | } catch (IllegalFormatException e) { |
| 93 | assertThat(e).hasMessage(errorMessage); |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | @Test |
Vladimir Moskva | 2b690e3 | 2017-02-17 13:01:02 +0000 | [diff] [blame] | 98 | public void testOutputOrderOfMap() throws Exception { |
| 99 | Map<Object, Object> map = new LinkedHashMap<>(); |
| 100 | map.put(5, 5); |
| 101 | map.put(3, 3); |
| 102 | map.put("foo", 42); |
| 103 | map.put(7, "bar"); |
| 104 | assertThat(Printer.str(map)).isEqualTo("{5: 5, 3: 3, \"foo\": 42, 7: \"bar\"}"); |
Florian Weikert | f31b947 | 2015-08-04 16:36:58 +0000 | [diff] [blame] | 105 | } |
| 106 | |
| 107 | @Test |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 108 | public void testFormatPositional() throws Exception { |
vladmos | 4690793 | 2017-06-30 14:01:45 +0200 | [diff] [blame] | 109 | assertThat(Printer.formatWithList("%s %d", Tuple.of("foo", 3))).isEqualTo("foo 3"); |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 110 | assertThat(Printer.format("%s %d", "foo", 3)).isEqualTo("foo 3"); |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 111 | |
brandjon | f107a53 | 2017-08-16 22:46:02 +0200 | [diff] [blame] | 112 | assertThat(Printer.format("%s %s %s", 1, null, 3)).isEqualTo("1 null 3"); |
| 113 | |
Francois-Rene Rideau | 304e195 | 2015-08-25 13:30:10 +0000 | [diff] [blame] | 114 | // Note: formatToString doesn't perform scalar x -> (x) conversion; |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 115 | // The %-operator is responsible for that. |
vladmos | 4690793 | 2017-06-30 14:01:45 +0200 | [diff] [blame] | 116 | assertThat(Printer.formatWithList("", Tuple.of())).isEmpty(); |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 117 | assertThat(Printer.format("%s", "foo")).isEqualTo("foo"); |
| 118 | assertThat(Printer.format("%s", 3.14159)).isEqualTo("3.14159"); |
Francois-Rene Rideau | 8d5cce3 | 2015-06-16 23:12:04 +0000 | [diff] [blame] | 119 | checkFormatPositionalFails("not all arguments converted during string formatting", |
| 120 | "%s", 1, 2, 3); |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 121 | assertThat(Printer.format("%%%s", "foo")).isEqualTo("%foo"); |
Francois-Rene Rideau | 8d5cce3 | 2015-06-16 23:12:04 +0000 | [diff] [blame] | 122 | checkFormatPositionalFails("not all arguments converted during string formatting", |
| 123 | "%%s", "foo"); |
| 124 | checkFormatPositionalFails("unsupported format character \" \" at index 1 in \"% %s\"", |
| 125 | "% %s", "foo"); |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 126 | assertThat(Printer.format("%s", MutableList.of(null, 1, 2, 3))).isEqualTo("[1, 2, 3]"); |
| 127 | assertThat(Printer.format("%s", Tuple.of(1, 2, 3))).isEqualTo("(1, 2, 3)"); |
| 128 | assertThat(Printer.format("%s", MutableList.of(null))).isEqualTo("[]"); |
| 129 | assertThat(Printer.format("%s", Tuple.of())).isEqualTo("()"); |
| 130 | assertThat(Printer.format("%% %d %r %s", 1, "2", "3")).isEqualTo("% 1 \"2\" 3"); |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 131 | |
Francois-Rene Rideau | 8d5cce3 | 2015-06-16 23:12:04 +0000 | [diff] [blame] | 132 | checkFormatPositionalFails( |
| 133 | "invalid argument \"1\" for format pattern %d", |
| 134 | "%d", "1"); |
| 135 | checkFormatPositionalFails("unsupported format character \".\" at index 1 in \"%.3g\"", |
| 136 | "%.3g"); |
| 137 | checkFormatPositionalFails("unsupported format character \".\" at index 1 in \"%.3g\"", |
| 138 | "%.3g", 1, 2); |
| 139 | checkFormatPositionalFails("unsupported format character \".\" at index 1 in \"%.s\"", |
| 140 | "%.s"); |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 141 | } |
| 142 | |
Klaus Aehlig | 26c86f8 | 2018-05-29 06:57:03 -0700 | [diff] [blame] | 143 | @Test |
| 144 | public void testPrettyPrinter() throws Exception { |
| 145 | assertThat(Printer.getPrettyPrinter().repr(ImmutableList.of(1, 2, 3)).toString()) |
| 146 | .isEqualTo( |
| 147 | "[\n" + |
| 148 | " 1,\n" + |
| 149 | " 2,\n" + |
| 150 | " 3\n" + |
| 151 | "]"); |
Klaus Aehlig | 3695591 | 2018-06-14 08:17:33 -0700 | [diff] [blame] | 152 | assertThat(Printer.getPrettyPrinter().repr(ImmutableList.<String>of()).toString()) |
| 153 | .isEqualTo("[]"); |
| 154 | assertThat(Printer.getPrettyPrinter().repr(ImmutableList.of("foo")).toString()) |
| 155 | .isEqualTo("[\n \"foo\"\n]"); |
Klaus Aehlig | 26c86f8 | 2018-05-29 06:57:03 -0700 | [diff] [blame] | 156 | assertThat( |
| 157 | Printer.getPrettyPrinter() |
| 158 | .repr(ImmutableMap.<Object, Object>of("foo", "bar", "baz", ImmutableList.of(1, 2))) |
| 159 | .toString()) |
| 160 | .isEqualTo( |
| 161 | "{\n" + |
| 162 | " \"foo\": \"bar\",\n" + |
| 163 | " \"baz\": [\n" + |
| 164 | " 1,\n" + |
| 165 | " 2\n" + |
| 166 | " ]\n" + |
| 167 | "}"); |
Klaus Aehlig | 3695591 | 2018-06-14 08:17:33 -0700 | [diff] [blame] | 168 | assertThat( |
| 169 | Printer.getPrettyPrinter() |
| 170 | .repr(ImmutableMap.<Object, Object>of( |
| 171 | "foo", "bar", "empty", ImmutableList.of(), "a", "b")) |
| 172 | .toString()) |
| 173 | .isEqualTo( |
| 174 | "{\n" + |
| 175 | " \"foo\": \"bar\",\n" + |
| 176 | " \"empty\": [],\n" + |
| 177 | " \"a\": \"b\"\n" + |
| 178 | "}"); |
Klaus Aehlig | 26c86f8 | 2018-05-29 06:57:03 -0700 | [diff] [blame] | 179 | } |
| 180 | |
brandjon | a9cafcb | 2018-04-04 12:24:33 -0700 | [diff] [blame] | 181 | private SkylarkPrinter makeSimplifiedFormatPrinter() { |
| 182 | return new Printer.BasePrinter(new StringBuilder(), /*simplifiedFormatStrings=*/ true); |
| 183 | } |
| 184 | |
| 185 | @Test |
| 186 | public void testSimplifiedDisallowsPlaceholdersBesidesPercentS() { |
| 187 | assertThat(makeSimplifiedFormatPrinter().format("Allowed: %%").toString()) |
| 188 | .isEqualTo("Allowed: %"); |
| 189 | assertThat(makeSimplifiedFormatPrinter().format("Allowed: %s", "abc").toString()) |
| 190 | .isEqualTo("Allowed: abc"); |
| 191 | assertThrows( |
| 192 | IllegalFormatException.class, |
| 193 | () -> makeSimplifiedFormatPrinter().format("Disallowed: %r", "abc")); |
| 194 | assertThrows( |
| 195 | IllegalFormatException.class, |
| 196 | () -> makeSimplifiedFormatPrinter().format("Disallowed: %d", 5)); |
| 197 | } |
| 198 | |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 199 | @Test |
Florian Weikert | 2591e19 | 2015-10-05 14:24:51 +0000 | [diff] [blame] | 200 | public void testListLimitStringLength() throws Exception { |
| 201 | int lengthDivisibleByTwo = Printer.SUGGESTED_CRITICAL_LIST_ELEMENTS_STRING_LENGTH; |
| 202 | if (lengthDivisibleByTwo % 2 == 1) { |
| 203 | ++lengthDivisibleByTwo; |
| 204 | } |
| 205 | String limit = Strings.repeat("x", lengthDivisibleByTwo); |
| 206 | String half = Strings.repeat("x", lengthDivisibleByTwo / 2); |
| 207 | |
| 208 | List<String> list = Arrays.asList(limit + limit); |
| 209 | |
| 210 | // String is way too long -> shorten. |
| 211 | assertThat(printListWithLimit(list)).isEqualTo("[\"" + limit + "...\"]"); |
| 212 | |
| 213 | LinkedList<List<String>> nestedList = new LinkedList<>(); |
| 214 | nestedList.add(list); |
| 215 | |
| 216 | // Same as above, but with one additional level of indirection. |
| 217 | assertThat(printListWithLimit(nestedList)).isEqualTo("[[\"" + limit + "...\"]]"); |
| 218 | |
| 219 | // The inner list alone would meet the limit, but because of the first element, it has to be |
| 220 | // shortened. |
| 221 | assertThat(printListWithLimit(Arrays.asList(half, Arrays.asList(limit)))) |
| 222 | .isEqualTo("[\"" + half + "\", [\"" + half + "...\"]]"); |
| 223 | |
| 224 | // String is too long, but the ellipsis make it even longer. |
| 225 | assertThat(printListWithLimit(Arrays.asList(limit + "x"))).isEqualTo("[\"" + limit + "...\"]"); |
| 226 | |
| 227 | // We hit the limit exactly -> everything is printed. |
| 228 | assertThat(printListWithLimit(Arrays.asList(limit))).isEqualTo("[\"" + limit + "\"]"); |
| 229 | |
| 230 | // Exact hit, but with two arguments -> everything is printed. |
| 231 | assertThat(printListWithLimit(Arrays.asList(half, half))) |
| 232 | .isEqualTo("[\"" + half + "\", \"" + half + "\"]"); |
| 233 | |
| 234 | // First argument hits the limit -> remaining argument is shortened. |
| 235 | assertThat(printListWithLimit(Arrays.asList(limit, limit))) |
| 236 | .isEqualTo("[\"" + limit + "\", \"...\"]"); |
| 237 | |
| 238 | String limitMinusOne = limit.substring(0, limit.length() - 1); |
| 239 | |
| 240 | // First arguments is one below the limit -> print first character of remaining argument. |
| 241 | assertThat(printListWithLimit(Arrays.asList(limitMinusOne, limit))) |
| 242 | .isEqualTo("[\"" + limitMinusOne + "\", \"x...\"]"); |
| 243 | |
| 244 | // First argument hits the limit -> we skip the remaining two arguments. |
| 245 | assertThat(printListWithLimit(Arrays.asList(limit, limit, limit))) |
| 246 | .isEqualTo("[\"" + limit + "\", <2 more arguments>]"); |
| 247 | } |
| 248 | |
| 249 | @Test |
| 250 | public void testListLimitTooManyArgs() throws Exception { |
| 251 | StringBuilder builder = new StringBuilder(); |
| 252 | List<Integer> maxLength = new LinkedList<>(); |
| 253 | |
| 254 | int next; |
| 255 | for (next = 0; next < Printer.SUGGESTED_CRITICAL_LIST_ELEMENTS_COUNT; ++next) { |
| 256 | maxLength.add(next); |
| 257 | if (next > 0) { |
| 258 | builder.append(", "); |
| 259 | } |
| 260 | builder.append(next); |
| 261 | } |
| 262 | |
| 263 | // There is one too many, but we print every argument nonetheless. |
| 264 | maxLength.add(next); |
| 265 | assertThat(printListWithLimit(maxLength)).isEqualTo("[" + builder + ", " + next + "]"); |
| 266 | |
| 267 | // There are two too many, hence we don't print them. |
| 268 | ++next; |
| 269 | maxLength.add(next); |
| 270 | assertThat(printListWithLimit(maxLength)).isEqualTo("[" + builder + ", <2 more arguments>]"); |
| 271 | } |
| 272 | |
| 273 | @Test |
| 274 | public void testPrintListDefaultNoLimit() throws Exception { |
| 275 | List<Integer> list = new LinkedList<>(); |
| 276 | // Make sure that the resulting string is longer than the suggestion. This should also lead to |
| 277 | // way more items than suggested. |
| 278 | for (int i = 0; i < Printer.SUGGESTED_CRITICAL_LIST_ELEMENTS_STRING_LENGTH * 2; ++i) { |
| 279 | list.add(i); |
| 280 | } |
| 281 | assertThat(Printer.str(list)).isEqualTo(String.format("[%s]", Joiner.on(", ").join(list))); |
| 282 | } |
| 283 | |
| 284 | private String printListWithLimit(List<?> list) { |
| 285 | return printList(list, Printer.SUGGESTED_CRITICAL_LIST_ELEMENTS_COUNT, |
| 286 | Printer.SUGGESTED_CRITICAL_LIST_ELEMENTS_STRING_LENGTH); |
| 287 | } |
| 288 | |
| 289 | private String printList(List<?> list, int criticalElementsCount, int criticalStringLength) { |
vladmos | 4690793 | 2017-06-30 14:01:45 +0200 | [diff] [blame] | 290 | return Printer.printAbbreviatedList( |
| 291 | list, "[", ", ", "]", "", criticalElementsCount, criticalStringLength); |
Florian Weikert | 2591e19 | 2015-10-05 14:24:51 +0000 | [diff] [blame] | 292 | } |
vladmos | 6ff634d | 2017-07-05 10:25:01 -0400 | [diff] [blame] | 293 | |
| 294 | private SkylarkValue createObjWithStr() { |
| 295 | return new SkylarkValue() { |
| 296 | @Override |
| 297 | public void repr(SkylarkPrinter printer) { |
| 298 | printer.append("<repr marker>"); |
| 299 | } |
| 300 | |
| 301 | @Override |
vladmos | 6ff634d | 2017-07-05 10:25:01 -0400 | [diff] [blame] | 302 | public void str(SkylarkPrinter printer) { |
| 303 | printer.append("<str marker>"); |
| 304 | } |
vladmos | 6ff634d | 2017-07-05 10:25:01 -0400 | [diff] [blame] | 305 | }; |
| 306 | } |
Francois-Rene Rideau | d61f531 | 2015-06-13 03:34:47 +0000 | [diff] [blame] | 307 | } |