blob: 3609c0c8bec7ae381753885a0cac77f205096165 [file] [log] [blame]
Florian Weikertd7b64bd2015-09-29 14:10:16 +00001// Copyright 2015 The Bazel Authors. All Rights Reserved.
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +00002//
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.syntax;
16
17import static com.google.common.truth.Truth.assertThat;
brandjona9cafcb2018-04-04 12:24:33 -070018import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000019import static org.junit.Assert.fail;
20
Florian Weikert2591e192015-10-05 14:24:51 +000021import com.google.common.base.Joiner;
22import com.google.common.base.Strings;
Klaus Aehlig26c86f82018-05-29 06:57:03 -070023import com.google.common.collect.ImmutableList;
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000024import com.google.common.collect.ImmutableMap;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000025import com.google.devtools.build.lib.cmdline.Label;
vladmos6ff634d2017-07-05 10:25:01 -040026import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
27import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
Francois-Rene Rideaua2c9ac62016-01-22 10:54:38 +000028import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
29import com.google.devtools.build.lib.syntax.SkylarkList.Tuple;
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000030import java.util.Arrays;
31import java.util.IllegalFormatException;
lberkiaea56b32017-05-30 12:35:33 +020032import java.util.LinkedHashMap;
Florian Weikert2591e192015-10-05 14:24:51 +000033import java.util.LinkedList;
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000034import java.util.List;
35import java.util.Map;
lberkiaea56b32017-05-30 12:35:33 +020036import org.junit.Test;
37import org.junit.runner.RunWith;
38import org.junit.runners.JUnit4;
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000039
40/**
41 * Test properties of the evaluator's datatypes and utility functions
42 * without actually creating any parse trees.
43 */
44@RunWith(JUnit4.class)
45public class PrinterTest {
46
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000047 @Test
48 public void testPrinter() throws Exception {
49 // Note that prettyPrintValue and printValue only differ on behaviour of
50 // labels and strings at toplevel.
vladmoscd6d8ae2017-10-12 15:35:17 +020051 assertThat(Printer.str(createObjWithStr())).isEqualTo("<str marker>");
52 assertThat(Printer.repr(createObjWithStr())).isEqualTo("<repr marker>");
53
lberkiaea56b32017-05-30 12:35:33 +020054 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 Rideaud61f5312015-06-13 03:34:47 +000063
dannark90e2b4b2018-06-27 13:35:04 -070064 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 Rideaud61f5312015-06-13 03:34:47 +000067
Francois-Rene Rideaua2c9ac62016-01-22 10:54:38 +000068 List<?> list = MutableList.of(null, "foo", "bar");
69 List<?> tuple = Tuple.of("foo", "bar");
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000070
lberkiaea56b32017-05-30 12:35:33 +020071 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 Rideaud61f5312015-06-13 03:34:47 +000077
Lukacs Berkicc9ac3f2015-06-16 06:42:41 +000078 Map<Object, Object> dict = ImmutableMap.<Object, Object>of(
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000079 1, tuple,
80 2, list,
Francois-Rene Rideaua2c9ac62016-01-22 10:54:38 +000081 "foo", MutableList.of(null));
lberkiaea56b32017-05-30 12:35:33 +020082 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 Rideaud61f5312015-06-13 03:34:47 +000086 }
87
Francois-Rene Rideau8d5cce32015-06-16 23:12:04 +000088 private void checkFormatPositionalFails(String errorMessage, String format, Object... arguments) {
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000089 try {
Francois-Rene Rideau8d5cce32015-06-16 23:12:04 +000090 Printer.format(format, arguments);
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000091 fail();
92 } catch (IllegalFormatException e) {
93 assertThat(e).hasMessage(errorMessage);
94 }
95 }
96
97 @Test
Vladimir Moskva2b690e32017-02-17 13:01:02 +000098 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 Weikertf31b9472015-08-04 16:36:58 +0000105 }
106
107 @Test
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +0000108 public void testFormatPositional() throws Exception {
vladmos46907932017-06-30 14:01:45 +0200109 assertThat(Printer.formatWithList("%s %d", Tuple.of("foo", 3))).isEqualTo("foo 3");
lberkiaea56b32017-05-30 12:35:33 +0200110 assertThat(Printer.format("%s %d", "foo", 3)).isEqualTo("foo 3");
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +0000111
brandjonf107a532017-08-16 22:46:02 +0200112 assertThat(Printer.format("%s %s %s", 1, null, 3)).isEqualTo("1 null 3");
113
Francois-Rene Rideau304e1952015-08-25 13:30:10 +0000114 // Note: formatToString doesn't perform scalar x -> (x) conversion;
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +0000115 // The %-operator is responsible for that.
vladmos46907932017-06-30 14:01:45 +0200116 assertThat(Printer.formatWithList("", Tuple.of())).isEmpty();
lberkiaea56b32017-05-30 12:35:33 +0200117 assertThat(Printer.format("%s", "foo")).isEqualTo("foo");
118 assertThat(Printer.format("%s", 3.14159)).isEqualTo("3.14159");
Francois-Rene Rideau8d5cce32015-06-16 23:12:04 +0000119 checkFormatPositionalFails("not all arguments converted during string formatting",
120 "%s", 1, 2, 3);
lberkiaea56b32017-05-30 12:35:33 +0200121 assertThat(Printer.format("%%%s", "foo")).isEqualTo("%foo");
Francois-Rene Rideau8d5cce32015-06-16 23:12:04 +0000122 checkFormatPositionalFails("not all arguments converted during string formatting",
123 "%%s", "foo");
124 checkFormatPositionalFails("unsupported format character \" \" at index 1 in \"% %s\"",
125 "% %s", "foo");
lberkiaea56b32017-05-30 12:35:33 +0200126 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 Rideaud61f5312015-06-13 03:34:47 +0000131
Francois-Rene Rideau8d5cce32015-06-16 23:12:04 +0000132 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 Rideaud61f5312015-06-13 03:34:47 +0000141 }
142
Klaus Aehlig26c86f82018-05-29 06:57:03 -0700143 @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 Aehlig36955912018-06-14 08:17:33 -0700152 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 Aehlig26c86f82018-05-29 06:57:03 -0700156 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 Aehlig36955912018-06-14 08:17:33 -0700168 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 Aehlig26c86f82018-05-29 06:57:03 -0700179 }
180
brandjona9cafcb2018-04-04 12:24:33 -0700181 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 Rideaud61f5312015-06-13 03:34:47 +0000199 @Test
Florian Weikert2591e192015-10-05 14:24:51 +0000200 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) {
vladmos46907932017-06-30 14:01:45 +0200290 return Printer.printAbbreviatedList(
291 list, "[", ", ", "]", "", criticalElementsCount, criticalStringLength);
Florian Weikert2591e192015-10-05 14:24:51 +0000292 }
vladmos6ff634d2017-07-05 10:25:01 -0400293
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
vladmos6ff634d2017-07-05 10:25:01 -0400302 public void str(SkylarkPrinter printer) {
303 printer.append("<str marker>");
304 }
vladmos6ff634d2017-07-05 10:25:01 -0400305 };
306 }
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +0000307}