blob: 2807b1b26e27457fd441921af87c59010ea65a69 [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;
18import static org.junit.Assert.assertEquals;
19import 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;
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000023import com.google.common.collect.ImmutableMap;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000024import com.google.devtools.build.lib.cmdline.Label;
Francois-Rene Rideaua2c9ac62016-01-22 10:54:38 +000025import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
26import com.google.devtools.build.lib.syntax.SkylarkList.Tuple;
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000027
28import org.junit.Test;
29import org.junit.runner.RunWith;
30import org.junit.runners.JUnit4;
31
32import java.util.Arrays;
Florian Weikertf31b9472015-08-04 16:36:58 +000033import java.util.HashMap;
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000034import java.util.IllegalFormatException;
Florian Weikert2591e192015-10-05 14:24:51 +000035import java.util.LinkedList;
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000036import java.util.List;
37import java.util.Map;
38
39/**
40 * Test properties of the evaluator's datatypes and utility functions
41 * without actually creating any parse trees.
42 */
43@RunWith(JUnit4.class)
44public class PrinterTest {
45
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000046 @Test
47 public void testPrinter() throws Exception {
48 // Note that prettyPrintValue and printValue only differ on behaviour of
49 // labels and strings at toplevel.
50 assertEquals("foo\nbar", Printer.str("foo\nbar"));
51 assertEquals("\"foo\\nbar\"", Printer.repr("foo\nbar"));
52 assertEquals("'", Printer.str("'"));
53 assertEquals("\"'\"", Printer.repr("'"));
54 assertEquals("\"", Printer.str("\""));
55 assertEquals("\"\\\"\"", Printer.repr("\""));
56 assertEquals("3", Printer.str(3));
57 assertEquals("3", Printer.repr(3));
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000058 assertEquals("None", Printer.repr(Runtime.NONE));
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000059
Lukacs Berki6e91eb92015-09-21 09:12:37 +000060 assertEquals("//x:x", Printer.str(
61 Label.parseAbsolute("//x")));
62 assertEquals("\"//x:x\"", Printer.repr(
63 Label.parseAbsolute("//x")));
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000064
Francois-Rene Rideaua2c9ac62016-01-22 10:54:38 +000065 List<?> list = MutableList.of(null, "foo", "bar");
66 List<?> tuple = Tuple.of("foo", "bar");
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000067
68 assertEquals("(1, [\"foo\", \"bar\"], 3)",
Francois-Rene Rideaua2c9ac62016-01-22 10:54:38 +000069 Printer.str(Tuple.of(1, list, 3)));
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000070 assertEquals("(1, [\"foo\", \"bar\"], 3)",
Francois-Rene Rideaua2c9ac62016-01-22 10:54:38 +000071 Printer.repr(Tuple.of(1, list, 3)));
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000072 assertEquals("[1, (\"foo\", \"bar\"), 3]",
Francois-Rene Rideaua2c9ac62016-01-22 10:54:38 +000073 Printer.str(MutableList.of(null, 1, tuple, 3)));
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000074 assertEquals("[1, (\"foo\", \"bar\"), 3]",
Francois-Rene Rideaua2c9ac62016-01-22 10:54:38 +000075 Printer.repr(MutableList.of(null, 1, tuple, 3)));
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000076
Lukacs Berkicc9ac3f2015-06-16 06:42:41 +000077 Map<Object, Object> dict = ImmutableMap.<Object, Object>of(
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000078 1, tuple,
79 2, list,
Francois-Rene Rideaua2c9ac62016-01-22 10:54:38 +000080 "foo", MutableList.of(null));
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000081 assertEquals("{1: (\"foo\", \"bar\"), 2: [\"foo\", \"bar\"], \"foo\": []}",
82 Printer.str(dict));
83 assertEquals("{1: (\"foo\", \"bar\"), 2: [\"foo\", \"bar\"], \"foo\": []}",
84 Printer.repr(dict));
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000085 }
86
Francois-Rene Rideau8d5cce32015-06-16 23:12:04 +000087 private void checkFormatPositionalFails(String errorMessage, String format, Object... arguments) {
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000088 try {
Francois-Rene Rideau8d5cce32015-06-16 23:12:04 +000089 Printer.format(format, arguments);
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +000090 fail();
91 } catch (IllegalFormatException e) {
92 assertThat(e).hasMessage(errorMessage);
93 }
94 }
95
96 @Test
Florian Weikertf31b9472015-08-04 16:36:58 +000097 public void testSortedOutputOfUnsortedMap() throws Exception {
98 Map<Integer, Integer> map = new HashMap<>();
99 int[] data = {5, 7, 3};
100
101 for (int current : data) {
102 map.put(current, current);
103 }
104 assertThat(Printer.str(map)).isEqualTo("{3: 3, 5: 5, 7: 7}");
105 }
106
107 @Test
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +0000108 public void testFormatPositional() throws Exception {
Francois-Rene Rideaua2c9ac62016-01-22 10:54:38 +0000109 assertEquals("foo 3", Printer.formatToString("%s %d", Tuple.of("foo", 3)));
Francois-Rene Rideau8d5cce32015-06-16 23:12:04 +0000110 assertEquals("foo 3", Printer.format("%s %d", "foo", 3));
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +0000111
Francois-Rene Rideau304e1952015-08-25 13:30:10 +0000112 // Note: formatToString doesn't perform scalar x -> (x) conversion;
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +0000113 // The %-operator is responsible for that.
Francois-Rene Rideaua2c9ac62016-01-22 10:54:38 +0000114 assertThat(Printer.formatToString("", Tuple.of())).isEmpty();
Francois-Rene Rideau8d5cce32015-06-16 23:12:04 +0000115 assertEquals("foo", Printer.format("%s", "foo"));
116 assertEquals("3.14159", Printer.format("%s", 3.14159));
117 checkFormatPositionalFails("not all arguments converted during string formatting",
118 "%s", 1, 2, 3);
119 assertEquals("%foo", Printer.format("%%%s", "foo"));
120 checkFormatPositionalFails("not all arguments converted during string formatting",
121 "%%s", "foo");
122 checkFormatPositionalFails("unsupported format character \" \" at index 1 in \"% %s\"",
123 "% %s", "foo");
Francois-Rene Rideaua2c9ac62016-01-22 10:54:38 +0000124 assertEquals("[1, 2, 3]", Printer.format("%s", MutableList.of(null, 1, 2, 3)));
125 assertEquals("(1, 2, 3)", Printer.format("%s", Tuple.of(1, 2, 3)));
126 assertEquals("[]", Printer.format("%s", MutableList.of(null)));
127 assertEquals("()", Printer.format("%s", Tuple.of()));
Francois-Rene Rideau8d5cce32015-06-16 23:12:04 +0000128 assertEquals("% 1 \"2\" 3", Printer.format("%% %d %r %s", 1, "2", "3"));
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +0000129
Francois-Rene Rideau8d5cce32015-06-16 23:12:04 +0000130 checkFormatPositionalFails(
131 "invalid argument \"1\" for format pattern %d",
132 "%d", "1");
133 checkFormatPositionalFails("unsupported format character \".\" at index 1 in \"%.3g\"",
134 "%.3g");
135 checkFormatPositionalFails("unsupported format character \".\" at index 1 in \"%.3g\"",
136 "%.3g", 1, 2);
137 checkFormatPositionalFails("unsupported format character \".\" at index 1 in \"%.s\"",
138 "%.s");
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +0000139 }
140
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +0000141 @Test
Florian Weikertf07e5442015-07-01 13:08:43 +0000142 public void testSingleQuotes() throws Exception {
143 assertThat(Printer.str("test", '\'')).isEqualTo("test");
144 assertThat(Printer.repr("test", '\'')).isEqualTo("'test'");
145
146 assertEquals("'\\''", Printer.repr("'", '\''));
147 assertEquals("\"", Printer.str("\"", '\''));
148 assertEquals("'\"'", Printer.repr("\"", '\''));
149
Francois-Rene Rideaua2c9ac62016-01-22 10:54:38 +0000150 List<?> list = MutableList.of(null, "foo", "bar");
151 List<?> tuple = Tuple.of("foo", "bar");
Florian Weikertf07e5442015-07-01 13:08:43 +0000152
Francois-Rene Rideaua2c9ac62016-01-22 10:54:38 +0000153 assertThat(Printer.str(Tuple.of(1, list, 3), '\'')).isEqualTo("(1, ['foo', 'bar'], 3)");
154 assertThat(Printer.repr(Tuple.of(1, list, 3), '\'')).isEqualTo("(1, ['foo', 'bar'], 3)");
155 assertThat(Printer.str(MutableList.of(null, 1, tuple, 3), '\''))
156 .isEqualTo("[1, ('foo', 'bar'), 3]");
157 assertThat(Printer.repr(MutableList.of(null, 1, tuple, 3), '\''))
158 .isEqualTo("[1, ('foo', 'bar'), 3]");
Florian Weikertf07e5442015-07-01 13:08:43 +0000159
160 Map<Object, Object> dict =
Francois-Rene Rideaua2c9ac62016-01-22 10:54:38 +0000161 ImmutableMap.<Object, Object>of(1, tuple, 2, list, "foo", MutableList.of(null));
Florian Weikertf07e5442015-07-01 13:08:43 +0000162
163 assertThat(Printer.str(dict, '\''))
164 .isEqualTo("{1: ('foo', 'bar'), 2: ['foo', 'bar'], 'foo': []}");
165 assertThat(Printer.repr(dict, '\''))
166 .isEqualTo("{1: ('foo', 'bar'), 2: ['foo', 'bar'], 'foo': []}");
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +0000167 }
Florian Weikert2591e192015-10-05 14:24:51 +0000168
169 @Test
170 public void testListLimitStringLength() throws Exception {
171 int lengthDivisibleByTwo = Printer.SUGGESTED_CRITICAL_LIST_ELEMENTS_STRING_LENGTH;
172 if (lengthDivisibleByTwo % 2 == 1) {
173 ++lengthDivisibleByTwo;
174 }
175 String limit = Strings.repeat("x", lengthDivisibleByTwo);
176 String half = Strings.repeat("x", lengthDivisibleByTwo / 2);
177
178 List<String> list = Arrays.asList(limit + limit);
179
180 // String is way too long -> shorten.
181 assertThat(printListWithLimit(list)).isEqualTo("[\"" + limit + "...\"]");
182
183 LinkedList<List<String>> nestedList = new LinkedList<>();
184 nestedList.add(list);
185
186 // Same as above, but with one additional level of indirection.
187 assertThat(printListWithLimit(nestedList)).isEqualTo("[[\"" + limit + "...\"]]");
188
189 // The inner list alone would meet the limit, but because of the first element, it has to be
190 // shortened.
191 assertThat(printListWithLimit(Arrays.asList(half, Arrays.asList(limit))))
192 .isEqualTo("[\"" + half + "\", [\"" + half + "...\"]]");
193
194 // String is too long, but the ellipsis make it even longer.
195 assertThat(printListWithLimit(Arrays.asList(limit + "x"))).isEqualTo("[\"" + limit + "...\"]");
196
197 // We hit the limit exactly -> everything is printed.
198 assertThat(printListWithLimit(Arrays.asList(limit))).isEqualTo("[\"" + limit + "\"]");
199
200 // Exact hit, but with two arguments -> everything is printed.
201 assertThat(printListWithLimit(Arrays.asList(half, half)))
202 .isEqualTo("[\"" + half + "\", \"" + half + "\"]");
203
204 // First argument hits the limit -> remaining argument is shortened.
205 assertThat(printListWithLimit(Arrays.asList(limit, limit)))
206 .isEqualTo("[\"" + limit + "\", \"...\"]");
207
208 String limitMinusOne = limit.substring(0, limit.length() - 1);
209
210 // First arguments is one below the limit -> print first character of remaining argument.
211 assertThat(printListWithLimit(Arrays.asList(limitMinusOne, limit)))
212 .isEqualTo("[\"" + limitMinusOne + "\", \"x...\"]");
213
214 // First argument hits the limit -> we skip the remaining two arguments.
215 assertThat(printListWithLimit(Arrays.asList(limit, limit, limit)))
216 .isEqualTo("[\"" + limit + "\", <2 more arguments>]");
217 }
218
219 @Test
220 public void testListLimitTooManyArgs() throws Exception {
221 StringBuilder builder = new StringBuilder();
222 List<Integer> maxLength = new LinkedList<>();
223
224 int next;
225 for (next = 0; next < Printer.SUGGESTED_CRITICAL_LIST_ELEMENTS_COUNT; ++next) {
226 maxLength.add(next);
227 if (next > 0) {
228 builder.append(", ");
229 }
230 builder.append(next);
231 }
232
233 // There is one too many, but we print every argument nonetheless.
234 maxLength.add(next);
235 assertThat(printListWithLimit(maxLength)).isEqualTo("[" + builder + ", " + next + "]");
236
237 // There are two too many, hence we don't print them.
238 ++next;
239 maxLength.add(next);
240 assertThat(printListWithLimit(maxLength)).isEqualTo("[" + builder + ", <2 more arguments>]");
241 }
242
243 @Test
244 public void testPrintListDefaultNoLimit() throws Exception {
245 List<Integer> list = new LinkedList<>();
246 // Make sure that the resulting string is longer than the suggestion. This should also lead to
247 // way more items than suggested.
248 for (int i = 0; i < Printer.SUGGESTED_CRITICAL_LIST_ELEMENTS_STRING_LENGTH * 2; ++i) {
249 list.add(i);
250 }
251 assertThat(Printer.str(list)).isEqualTo(String.format("[%s]", Joiner.on(", ").join(list)));
252 }
253
254 private String printListWithLimit(List<?> list) {
255 return printList(list, Printer.SUGGESTED_CRITICAL_LIST_ELEMENTS_COUNT,
256 Printer.SUGGESTED_CRITICAL_LIST_ELEMENTS_STRING_LENGTH);
257 }
258
259 private String printList(List<?> list, int criticalElementsCount, int criticalStringLength) {
260 StringBuilder builder = new StringBuilder();
261 Printer.printList(
262 builder, list, "[", ", ", "]", "", '"', criticalElementsCount, criticalStringLength);
263 return builder.toString();
264 }
Francois-Rene Rideaud61f5312015-06-13 03:34:47 +0000265}