1 /*
2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test
26  * @summary This exercises String#repeat patterns and limits.
27  * @run main/othervm -Xmx4G StringRepeat
28  */
29 package test.java.lang.String;
30 
31 // Android-added: support for wrapper to avoid d8 backporting of String.isBlank (b/191859202).
32 import android.platform.test.annotations.LargeTest;
33 
34 import java.lang.invoke.MethodHandle;
35 import java.lang.invoke.MethodHandles;
36 import java.lang.invoke.MethodType;
37 
38 import org.testng.Assert;
39 import org.testng.annotations.Test;
40 
41 
42 public class StringRepeat {
43     /*
44      * Varitions of repeat count.
45      */
46     static int[] REPEATS = {
47         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
48         32, 64, 128, 256, 512, 1024, 64 * 1024, 1024 * 1024,
49         16 * 1024 * 1024
50     };
51 
52     /*
53      * Varitions of Strings.
54      */
55     static String[] STRINGS = new String[] {
56             "", "\0",  " ", "a", "$", "\u2022",
57             "ab", "abc", "abcd", "abcde",
58             "The quick brown fox jumps over the lazy dog."
59     };
60 
61     /*
62      * Repeat String function tests.
63      */
64     @LargeTest
65     @Test
test1()66     public void test1() {
67         for (int repeat : REPEATS) {
68             for (String string : STRINGS) {
69                 long limit = (long)string.length() * (long)repeat;
70 
71                 // Android-changed: lowered max length limit
72                 // if ((long)(Integer.MAX_VALUE >> 1) <= limit) {
73                 if ((long)(Integer.MAX_VALUE >> 5) <= limit) {
74                     break;
75                 }
76 
77                 // Android-changed: call wrappered repeat() to avoid d8 backport (b/191859202).
78                 // verify(string.repeat(repeat), string, repeat);
79                 verify(String_repeat(string, repeat), string, repeat);
80             }
81         }
82     }
83 
84     /*
85      * Repeat String exception tests.
86      */
87     @Test
test2()88     public void test2() {
89         try {
90             // Android-changed: call wrappered repeat() to avoid d8 backport (b/191859202).
91             // "abc".repeat(-1);
92             String_repeat("abc", -1);
93             throw new RuntimeException("No exception for negative repeat count");
94         } catch (IllegalArgumentException ex) {
95             // Correct
96         }
97 
98         try {
99             // Android-changed: call wrappered repeat() to avoid d8 backport (b/191859202).
100             // "abc".repeat(Integer.MAX_VALUE - 1);
101             String_repeat("abc", Integer.MAX_VALUE - 1);
102             throw new RuntimeException("No exception for large repeat count");
103         } catch (OutOfMemoryError ex) {
104             // Correct
105         }
106     }
107 
108     // Android-added: more tests
109     @Test
testEdgeCases()110     public void testEdgeCases() {
111         // Android-changed: call wrappered repeat() to avoid d8 backport (b/191859202).
112         // Assert.assertThrows(IllegalArgumentException.class, () -> "a".repeat(-1));
113         // Assert.assertThrows(IllegalArgumentException.class, () -> "\u03B1".repeat(-1));
114         // Assert.assertThrows(OutOfMemoryError.class, () -> "\u03B1\u03B2".repeat(Integer.MAX_VALUE));
115         Assert.assertThrows(IllegalArgumentException.class, () -> String_repeat("a", -1));
116         Assert.assertThrows(IllegalArgumentException.class, () -> String_repeat("\u03B1", -1));
117         Assert.assertThrows(OutOfMemoryError.class,
118                             () -> String_repeat("\u03B1\u03B2", Integer.MAX_VALUE));
119     }
120 
121     @Test
testCompressed()122     public void testCompressed() {
123         // Android-changed: call wrappered repeat() to avoid d8 backport (b/191859202).
124         // Assert.assertEquals("a".repeat(0), "");
125         // Assert.assertEquals("a".repeat(1), "a");
126         // Assert.assertEquals("a".repeat(5), "aaaaa");
127         Assert.assertEquals(String_repeat("a", 0), "");
128         Assert.assertEquals(String_repeat("a", 1), "a");
129         Assert.assertEquals(String_repeat("a", 5), "aaaaa");
130 
131         // Android-changed: call wrappered repeat() to avoid d8 backport (b/191859202).
132         // Assert.assertEquals("abc".repeat(0), "");
133         // Assert.assertEquals("abc".repeat(1), "abc");
134         // Assert.assertEquals("abc".repeat(5), "abcabcabcabcabc");
135         Assert.assertEquals(String_repeat("abc", 0), "");
136         Assert.assertEquals(String_repeat("abc", 1), "abc");
137         Assert.assertEquals(String_repeat("abc", 5), "abcabcabcabcabc");
138     }
139 
140     @Test
testUncompressed()141     public void testUncompressed() {
142         // Android-changed: call wrappered repeat() to avoid d8 backport (b/191859202).
143         // Assert.assertEquals("\u2022".repeat(0), "");
144         // Assert.assertEquals("\u2022".repeat(1), "\u2022");
145         // Assert.assertEquals("\u2022".repeat(5), "\u2022\u2022\u2022\u2022\u2022");
146         Assert.assertEquals(String_repeat("\u2022", 0), "");
147         Assert.assertEquals(String_repeat("\u2022", 1), "\u2022");
148         Assert.assertEquals(String_repeat("\u2022", 5), "\u2022\u2022\u2022\u2022\u2022");
149 
150         // Android-changed: call wrappered repeat() to avoid d8 backport (b/191859202).
151         // Assert.assertEquals("\u03B1\u03B2\u03B3".repeat(0), "");
152         // Assert.assertEquals("\u03B1\u03B2\u03B3".repeat(1), "αβγ");
153         // Assert.assertEquals("\u03B1\u03B2\u03B3".repeat(5), "αβγαβγαβγαβγαβγ");
154         Assert.assertEquals(String_repeat("\u03B1\u03B2\u03B3", 0), "");
155         Assert.assertEquals(String_repeat("\u03B1\u03B2\u03B3", 1), "αβγ");
156         Assert.assertEquals(String_repeat("\u03B1\u03B2\u03B3", 5), "αβγαβγαβγαβγαβγ");
157     }
158 
truncate(String string)159     static String truncate(String string) {
160         if (string.length() < 80) {
161             return string;
162         }
163         return string.substring(0, 80) + "...";
164     }
165 
166     /*
167      * Verify string repeat patterns.
168      */
verify(String result, String string, int repeat)169     static void verify(String result, String string, int repeat) {
170         if (string.isEmpty() || repeat == 0) {
171             if (!result.isEmpty()) {
172                 String message = String.format("\"%s\".repeat(%d)%n", truncate(string), repeat) +
173                         String.format("Result \"%s\"%n", truncate(result)) +
174                         String.format("Result expected to be empty, found string of length %d%n", result.length());
175                 Assert.fail(message);
176             }
177         } else {
178             int expected = 0;
179             int count = 0;
180             for (int offset = result.indexOf(string, expected);
181                  0 <= offset;
182                  offset = result.indexOf(string, expected)) {
183                 count++;
184                 if (offset != expected) {
185                     String message = String.format("\"%s\".repeat(%d)%n", truncate(string), repeat) +
186                             String.format("Result \"%s\"%n", truncate(result)) +
187                             String.format("Repeat expected at %d, found at = %d%n", expected, offset);
188                     Assert.fail(message);
189                 }
190                 expected += string.length();
191             }
192             if (count != repeat) {
193                 String message = String.format("\"%s\".repeat(%d)%n", truncate(string), repeat) +
194                         String.format("Result \"%s\"%n", truncate(result)) +
195                         String.format("Repeat count expected to be %d, found %d%n", repeat, count);
196                 Assert.fail(message);
197             }
198         }
199     }
200 
201     // Android-added: wrapper to avoid d8 backporting of String.isBlank (b/191859202).
String_repeat(String input, int count)202     private static String String_repeat(String input, int count) {
203         try {
204             MethodType type = MethodType.methodType(String.class, int.class);
205             MethodHandle repeat = MethodHandles.lookup().findVirtual(String.class, "repeat", type);
206             return (String) repeat.invokeExact(input, count);
207         } catch (IllegalArgumentException | OutOfMemoryError expected) {
208             throw expected;
209         } catch (Throwable t) {
210             throw new RuntimeException(t);
211         }
212     }
213 }
214