void (TextChangeImpl ... changes) { assertEquals(asList(changes), myStorage.getChanges()); assertEquals(changes.length > 0, !myStorage.isEmpty()); if (changes.length <= 0) { return; } int length = changes[changes.length - 1].getEnd() + 2; char[] input = new char[length]; char c = 'A'; for (int i = 0; i < input.length; i++) { input[i] = c++; } char[] output = BulkChangesMerger.INSTANCE.mergeToCharArray(input, input.length, asList(changes)); // charAt(). for (int i = 0; i < output.length; i++) { if (output[i] != myStorage.charAt(input, i)) { fail(String.format( "Detected incorrect charAt() processing. Original text: '%s', changes: %s, index: %d, expected: %c, actual: %c", new String(input), Arrays.asList(changes), i, output[i], myStorage.charAt(input, i) )); } } // substring(). for (int start = 0; start < output.length; start++) { for( int end = start; end < output.length; end++) { String expected = new String(output, start, end - start); String actual = myStorage.substring(input, start, end).toString(); if (!expected.equals(actual)) { fail(String.format( "Detected incorrect substring() processing. Original text: '%s', changes: %s, client text: '%s', range: %d-%d, " + "expected: '%s', actual: '%s'", new String(input), Arrays.asList(changes), new String(output), start, end, expected, actual )); } } } }