/* * Copyright 2016-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.charset; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import com.google.common.io.BaseEncoding; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.CoderResult; import java.nio.charset.StandardCharsets; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; public class NulTerminatedCharsetDecoderTest { @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void nonNulTerminatedEmptyBufferDecodesWithUnderflow() { NulTerminatedCharsetDecoder decoder = new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()); ByteBuffer in = ByteBuffer.allocate(0); CharBuffer out = CharBuffer.allocate(0); assertThat(in.position(), is(equalTo(0))); assertThat(in.limit(), is(equalTo(0))); assertThat(out.position(), is(equalTo(0))); assertThat(out.limit(), is(equalTo(0))); NulTerminatedCharsetDecoder.Result result = decoder.decode(in, out, true); assertThat(in.position(), is(equalTo(0))); assertThat(in.limit(), is(equalTo(0))); assertThat(out.position(), is(equalTo(0))); assertThat(out.limit(), is(equalTo(0))); assertThat( result, is(equalTo(new NulTerminatedCharsetDecoder.Result(false, CoderResult.UNDERFLOW)))); } @Test public void nulTerminatedEmptyBufferDecodesToEmptyBuffer() { NulTerminatedCharsetDecoder decoder = new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()); ByteBuffer in = decodeHex("00"); CharBuffer out = CharBuffer.allocate(0); assertThat(in.position(), is(equalTo(0))); assertThat(in.limit(), is(equalTo(1))); assertThat(out.position(), is(equalTo(0))); assertThat(out.limit(), is(equalTo(0))); NulTerminatedCharsetDecoder.Result result = decoder.decode(in, out, true); assertThat(in.position(), is(equalTo(1))); assertThat(in.limit(), is(equalTo(1))); assertThat(out.position(), is(equalTo(0))); assertThat(out.limit(), is(equalTo(0))); assertThat( result, is(equalTo(new NulTerminatedCharsetDecoder.Result(true, CoderResult.UNDERFLOW)))); } @Test public void nulTerminatedBufferDecodesContentsToBuffer() { NulTerminatedCharsetDecoder decoder = new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()); ByteBuffer in = decodeHex("F09F92A900"); // U+1F4A9 in UTF-8 CharBuffer out = CharBuffer.allocate(2); assertThat(in.position(), is(equalTo(0))); assertThat(in.limit(), is(equalTo(5))); assertThat(out.position(), is(equalTo(0))); assertThat(out.limit(), is(equalTo(2))); NulTerminatedCharsetDecoder.Result result = decoder.decode(in, out, true); assertThat( result, is(equalTo(new NulTerminatedCharsetDecoder.Result(true, CoderResult.UNDERFLOW)))); assertThat(in.position(), is(equalTo(5))); assertThat(in.limit(), is(equalTo(5))); assertThat(out.position(), is(equalTo(2))); assertThat(out.limit(), is(equalTo(2))); out.flip(); assertThat(out.toString(), is(equalTo("\uD83D\uDCA9"))); // U+1F4A9 in Java } @Test public void splittingCodePointAcrossByteBuffersDecodesContentToBuffer() { NulTerminatedCharsetDecoder decoder = new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()); ByteBuffer in = decodeHex("F09F92A900"); // U+1F4A9 in UTF-8 // first half of U+1F4A9 in UTF-8 in.limit(2); CharBuffer out = CharBuffer.allocate(2); assertThat(in.position(), is(equalTo(0))); assertThat(in.limit(), is(equalTo(2))); assertThat(out.position(), is(equalTo(0))); assertThat(out.limit(), is(equalTo(2))); NulTerminatedCharsetDecoder.Result firstHalfResult = decoder.decode(in, out, false); assertThat( firstHalfResult, is(equalTo(new NulTerminatedCharsetDecoder.Result(false, CoderResult.UNDERFLOW)))); assertThat(in.position(), is(equalTo(0))); assertThat(in.limit(), is(equalTo(2))); assertThat(out.position(), is(equalTo(0))); assertThat(out.limit(), is(equalTo(2))); // second half of U+1F4A9 in UTF-8 in.limit(5); NulTerminatedCharsetDecoder.Result secondHalfResult = decoder.decode(in, out, true); assertThat( secondHalfResult, is(equalTo(new NulTerminatedCharsetDecoder.Result(true, CoderResult.UNDERFLOW)))); assertThat(in.position(), is(equalTo(5))); assertThat(in.limit(), is(equalTo(5))); assertThat(out.position(), is(equalTo(2))); assertThat(out.limit(), is(equalTo(2))); out.flip(); assertThat(out.toString(), is(equalTo("\uD83D\uDCA9"))); // U+1F4A9 in Java } @Test public void tooSmallCharBufferResultsInOverflow() { NulTerminatedCharsetDecoder decoder = new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()); ByteBuffer in = decodeHex("F09F92A900"); // U+1F4A9 in UTF-8 CharBuffer out = CharBuffer.allocate(1); // Too small to hold U+1F419 (need 2 UTF-16 code units) assertThat(in.position(), is(equalTo(0))); assertThat(in.limit(), is(equalTo(5))); assertThat(out.position(), is(equalTo(0))); assertThat(out.limit(), is(equalTo(1))); NulTerminatedCharsetDecoder.Result result = decoder.decode(in, out, true); assertThat( result, is(equalTo(new NulTerminatedCharsetDecoder.Result(false, CoderResult.OVERFLOW)))); assertThat(in.position(), is(equalTo(0))); assertThat(in.limit(), is(equalTo(5))); assertThat(out.position(), is(equalTo(0))); assertThat(out.limit(), is(equalTo(1))); } @Test public void invalidUTF8BufferReturnsMalformedResult() { NulTerminatedCharsetDecoder decoder = new NulTerminatedCharsetDecoder(StandardCharsets.UTF_8.newDecoder()); ByteBuffer in = decodeHex("C0FFEE00"); CharBuffer out = CharBuffer.allocate(4); assertThat(in.position(), is(equalTo(0))); assertThat(in.limit(), is(equalTo(4))); assertThat(out.position(), is(equalTo(0))); assertThat(out.limit(), is(equalTo(4))); NulTerminatedCharsetDecoder.Result result = decoder.decode(in, out, true); assertThat( result, is( equalTo( new NulTerminatedCharsetDecoder.Result(false, CoderResult.malformedForLength(1))))); assertThat(in.position(), is(equalTo(0))); assertThat(in.limit(), is(equalTo(4))); assertThat(out.position(), is(equalTo(0))); assertThat(out.limit(), is(equalTo(4))); } @Test public void decodeValidUTF8String() throws Exception { assertThat( NulTerminatedCharsetDecoder.decodeUTF8String(decodeHex("F09F92A900")), is(equalTo("\uD83D\uDCA9"))); } @Test public void decodeInvalidValidUTF8StringThrows() throws Exception { thrown.expect(CharacterCodingException.class); NulTerminatedCharsetDecoder.decodeUTF8String(decodeHex("C0FFEE00")); } @Test public void decodeNonNulTerminatedUTF8StringThrows() throws Exception { thrown.expect(BufferUnderflowException.class); NulTerminatedCharsetDecoder.decodeUTF8String(decodeHex("F09F92A9")); } private static ByteBuffer decodeHex(String hex) { return ByteBuffer.wrap(BaseEncoding.base16().decode(hex)); } }