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