1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package libcore.libcore.net; 18 19 import junit.framework.TestCase; 20 21 import java.net.URISyntaxException; 22 import java.nio.charset.StandardCharsets; 23 import libcore.net.UriCodec; 24 25 /** 26 * Tests for {@link UriCodec} 27 */ 28 public class UriCodecTest extends TestCase { 29 private static final UriCodec CODEC = new UriCodec() { 30 @Override 31 protected boolean isRetained(char c) { 32 return c == '$'; 33 } 34 }; 35 36 private static final String VALID_ENCODED_STRING = "a0b$CD%01a%23b%45c%67%89%abd%cd%efq"; 37 38 public void testValidate_stringOK_passes() throws Exception { 39 assertEquals( 40 VALID_ENCODED_STRING, 41 CODEC.validate( 42 VALID_ENCODED_STRING, 0, VALID_ENCODED_STRING.length(), "test OK string")); 43 } 44 45 // Hex codes in upper case are valid as well. 46 public void testValidate_stringUppercaseOK_passes() throws Exception { 47 String stringOKUpperCase = VALID_ENCODED_STRING.toUpperCase(); 48 CODEC.validate(stringOKUpperCase, 0, stringOKUpperCase.length(), "test OK UC string"); 49 } 50 51 // Characters before the start index are ignored. 52 public void testValidate_wrongCharsBeforeStart_passes() throws Exception { 53 assertEquals(VALID_ENCODED_STRING, CODEC.validate( 54 "%p" + VALID_ENCODED_STRING, 55 2, 56 VALID_ENCODED_STRING.length() + 2, 57 "test string")); 58 } 59 60 // Fails with character 'p', invalid after '%' 61 public void testValidate_wrongCharsAtStart_fails() throws Exception { 62 try { 63 CODEC.validate( 64 "%p" + VALID_ENCODED_STRING, 65 0, 66 VALID_ENCODED_STRING.length() + 2, 67 "test string"); 68 fail("Expected URISyntaxException"); 69 } catch (URISyntaxException expected) { 70 // Expected. 71 } 72 } 73 74 // Fails with character 'p', invalid after '%' 75 public void testValidate_wrongCharsBeyondEnd_passes() throws Exception { 76 assertEquals(VALID_ENCODED_STRING, CODEC.validate( 77 VALID_ENCODED_STRING + "%p", 78 0, 79 VALID_ENCODED_STRING.length(), 80 "test string")); 81 } 82 83 // Fails with character 'p', invalid after '%' 84 public void testValidate_wrongCharsAtEnd_fails() throws Exception { 85 try { 86 CODEC.validate( 87 VALID_ENCODED_STRING + "%p", 88 0, 89 VALID_ENCODED_STRING.length() + 2, 90 "test string"); 91 fail("Expected URISyntaxException"); 92 } catch (URISyntaxException expected) { 93 // Expected. 94 } 95 } 96 97 public void testValidate_secondDigitWrong_fails() throws Exception { 98 try { 99 CODEC.validate( 100 VALID_ENCODED_STRING + "%1p", 101 0, 102 VALID_ENCODED_STRING.length() + 2, 103 "test string"); 104 fail("Expected URISyntaxException"); 105 } catch (URISyntaxException expected) { 106 // Expected. 107 } 108 } 109 110 public void testValidate_emptyString_passes() throws Exception { 111 assertEquals("", CODEC.validate("", 0, 0, "empty string")); 112 } 113 114 public void testValidate_stringEndingWithPercent_fails() throws Exception { 115 try { 116 CODEC.validate("a%", 0, 0, "a% string"); 117 } catch (URISyntaxException expected) { 118 // Expected. 119 } 120 } 121 122 public void testValidate_stringEndingWithPercentAndSingleDigit_fails() throws Exception { 123 try { 124 CODEC.validate("a%1", 0, 0, "a%1 string"); 125 } catch (URISyntaxException expected) { 126 // Expected. 127 } 128 } 129 130 public void testValidateSimple_stringOK_passes() throws Exception { 131 UriCodec.validateSimple(VALID_ENCODED_STRING, "$%"); 132 } 133 134 // Hex codes in upper case are valid as well. 135 public void testValidateSimple_stringUppercaseOK_passes() throws Exception { 136 UriCodec.validateSimple(VALID_ENCODED_STRING.toUpperCase(), "$%"); 137 } 138 139 // Fails with character 'p', invalid after '%' 140 public void testValidateSimple_wrongCharsAtStart_fails() throws Exception { 141 try { 142 UriCodec.validateSimple("%/" + VALID_ENCODED_STRING, "$%"); 143 fail("Expected URISyntaxException"); 144 } catch (URISyntaxException expected) { 145 // Expected. 146 } 147 } 148 149 // Fails with character 'p', invalid after '%' 150 public void testValidateSimple_wrongCharsAtEnd_fails() throws Exception { 151 try { 152 UriCodec.validateSimple(VALID_ENCODED_STRING + "%/", "$%"); 153 fail("Expected URISyntaxException"); 154 } catch (URISyntaxException expected) { 155 // Expected. 156 } 157 } 158 159 public void testValidateSimple_emptyString_passes() throws Exception { 160 UriCodec.validateSimple("", "$%"); 161 } 162 163 public void testValidateSimple_stringEndingWithPercent_passes() throws Exception { 164 UriCodec.validateSimple("a%", "$%"); 165 } 166 167 public void testValidateSimple_stringEndingWithPercentAndSingleDigit_passes() throws Exception { 168 UriCodec.validateSimple("a%1", "$%"); 169 } 170 171 public void testEncode_emptyString_returnsEmptyString() { 172 assertEquals("", CODEC.encode("", StandardCharsets.UTF_8)); 173 } 174 175 public void testEncode() { 176 assertEquals("ab%2F$%C4%82%2512", CODEC.encode("ab/$\u0102%12", StandardCharsets.UTF_8)); 177 } 178 179 public void testEncode_convertWhitespace() { 180 // Whitespace is not retained, output %20. 181 assertEquals("ab%2F$%C4%82%2512%20", 182 CODEC.encode("ab/$\u0102%12 ", StandardCharsets.UTF_8)); 183 184 UriCodec withWhitespaceRetained = new UriCodec() { 185 @Override 186 protected boolean isRetained(char c) { 187 return c == '$' || c == ' '; 188 } 189 }; 190 // Whitespace is retained, convert to plus. 191 assertEquals("ab%2F$%C4%82%2512+", 192 withWhitespaceRetained.encode("ab/$\u0102%12 ", StandardCharsets.UTF_8)); 193 } 194 195 /** Confirm that '%' can be retained, disabling '%' encoding. http://b/24806835 */ 196 public void testEncode_percentRetained() { 197 UriCodec withPercentRetained = new UriCodec() { 198 @Override 199 protected boolean isRetained(char c) { 200 return c == '%'; 201 } 202 }; 203 // Percent is retained 204 assertEquals("ab%34%20", withPercentRetained.encode("ab%34 ", StandardCharsets.UTF_8)); 205 } 206 207 public void testEncode_partially_returnsPercentUnchanged() { 208 StringBuilder stringBuilder = new StringBuilder(); 209 // Check it's really appending instead of returning a new builder. 210 stringBuilder.append("pp"); 211 CODEC.appendPartiallyEncoded(stringBuilder, "ab/$\u0102%"); 212 // Returns % at the end instead of %25. 213 assertEquals("ppab%2F$%C4%82%", stringBuilder.toString()); 214 } 215 216 public void testEncode_partially_returnsCharactersAfterPercentEncoded() { 217 StringBuilder stringBuilder = new StringBuilder(); 218 // Check it's really appending instead of returning a new builder. 219 stringBuilder.append("pp"); 220 CODEC.appendPartiallyEncoded(stringBuilder, "ab/$\u0102%\u0102"); 221 // Returns %C4%82 at the end. 222 assertEquals("ppab%2F$%C4%82%%C4%82", stringBuilder.toString()); 223 } 224 225 public void testEncode_partially_returnsDigitsAfterPercentUnchanged() { 226 StringBuilder stringBuilder = new StringBuilder(); 227 // Check it's really appending instead of returning a new builder. 228 stringBuilder.append("pp"); 229 CODEC.appendPartiallyEncoded(stringBuilder, "ab/$\u0102%38"); 230 // Returns %38 at the end. 231 assertEquals("ppab%2F$%C4%82%38", stringBuilder.toString()); 232 } 233 234 // Last character needs encoding (make sure we are flushing the buffer with chars to encode). 235 public void testEncode_lastCharacter() { 236 assertEquals("ab%2F$%C4%82%25%E0%A1%80", 237 CODEC.encode("ab/$\u0102%\u0840", StandardCharsets.UTF_8)); 238 } 239 240 // Last character needs encoding (make sure we are flushing the buffer with chars to encode). 241 public void testEncode_flushBufferBeforePlusFromSpace() { 242 UriCodec withSpaceRetained = new UriCodec() { 243 @Override 244 protected boolean isRetained(char c) { 245 return c == ' '; 246 } 247 }; 248 assertEquals("%2F+", 249 withSpaceRetained.encode("/ ", StandardCharsets.UTF_8)); 250 } 251 252 public void testDecode_emptyString_returnsEmptyString() { 253 assertEquals("", UriCodec.decode("")); 254 } 255 256 public void testDecode_wrongHexDigit_fails() { 257 try { 258 // %p in the end. 259 UriCodec.decode("ab%2f$%C4%82%25%e0%a1%80%p"); 260 fail("Expected URISyntaxException"); 261 } catch (IllegalArgumentException expected) { 262 // Expected. 263 } 264 } 265 266 public void testDecode_secondHexDigitWrong_fails() { 267 try { 268 // %1p in the end. 269 UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80%1p"); 270 fail("Expected URISyntaxException"); 271 } catch (IllegalArgumentException expected) { 272 // Expected. 273 } 274 } 275 276 public void testDecode_endsWithPercent_fails() { 277 try { 278 // % in the end. 279 UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80%"); 280 fail("Expected URISyntaxException"); 281 } catch (IllegalArgumentException expected) { 282 // Expected. 283 } 284 } 285 286 public void testDecode_dontThrowException_appendsUnknownCharacter() { 287 assertEquals("ab/$\u0102%\u0840\ufffd", 288 UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80%", 289 false /* convertPlus */, 290 StandardCharsets.UTF_8, 291 false /* throwOnFailure */)); 292 } 293 294 public void testDecode_convertPlus() { 295 assertEquals("ab/$\u0102% \u0840", 296 UriCodec.decode("ab%2f$%c4%82%25+%e0%a1%80", 297 true /* convertPlus */, 298 StandardCharsets.UTF_8, 299 false /* throwOnFailure */)); 300 } 301 302 // Last character needs decoding (make sure we are flushing the buffer with chars to decode). 303 public void testDecode_lastCharacter() { 304 assertEquals("ab/$\u0102%\u0840", 305 UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80")); 306 } 307 308 // Check that a second row of encoded characters is decoded properly (internal buffers are 309 // reset properly). 310 public void testDecode_secondRowOfEncoded() { 311 assertEquals("ab/$\u0102%\u0840aa\u0840", 312 UriCodec.decode("ab%2f$%c4%82%25%e0%a1%80aa%e0%a1%80")); 313 } 314 } 315