1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  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 */
16package tests.api.java.nio.charset;
17
18import dalvik.annotation.TestLevel;
19import dalvik.annotation.TestTargetClass;
20import dalvik.annotation.TestTargetNew;
21import dalvik.annotation.TestTargets;
22
23import java.nio.ByteBuffer;
24import java.nio.CharBuffer;
25import java.nio.charset.Charset;
26import java.nio.charset.CharsetDecoder;
27import java.nio.charset.CharsetEncoder;
28import java.nio.charset.CoderResult;
29import java.nio.charset.CodingErrorAction;
30import java.nio.charset.UnsupportedCharsetException;
31import java.util.Arrays;
32@TestTargetClass(CharsetEncoder.class)
33/**
34 * API unit test for java.nio.charset.CharsetEncoder
35 */
36public class CharsetEncoderTest extends AbstractCharsetEncoderTestCase {
37
38    static final int MAX_BYTES = 3;
39
40    static final float AVER_BYTES = 0.5f;
41
42    // default for Charset abstract class
43    byte[] defaultReplacement = new byte[] { 63 };
44
45    // specific for Charset implementation subclass
46    byte[] specifiedReplacement = new byte[] { 26 };
47
48
49    protected void setUp() throws Exception {
50        cs = new MockCharset("CharsetEncoderTest_mock", new String[0]);
51        unibytes = new byte[] { 32, 98, 117, 102, 102, 101, 114 };
52        super.setUp();
53    }
54
55
56    @TestTargets({
57        @TestTargetNew(
58            level = TestLevel.COMPLETE,
59            notes = "",
60            method = "averageBytesPerChar",
61            args = {}
62        ),
63        @TestTargetNew(
64            level = TestLevel.COMPLETE,
65            notes = "",
66            method = "maxBytesPerChar",
67            args = {}
68        )
69    })
70    public void testSpecificDefaultValue() {
71        assertTrue(encoder.averageBytesPerChar() == AVER_BYTES);
72        assertTrue(encoder.maxBytesPerChar() == MAX_BYTES);
73    }
74
75
76    @TestTargetNew(
77        level = TestLevel.COMPLETE,
78        notes = "",
79        method = "implOnMalformedInput",
80        args = {java.nio.charset.CodingErrorAction.class}
81    )
82    public void testImplOnMalformedInput() {
83        encoder = new MockCharsetEncoder(cs, 1, 3);
84        assertEquals(CoderResult.UNDERFLOW, ((MockCharsetEncoder) encoder)
85                .pubImplFlush(null));
86
87    }
88
89
90    @TestTargetNew(
91        level = TestLevel.COMPLETE,
92        notes = "",
93        method = "implOnUnmappableCharacter",
94        args = {java.nio.charset.CodingErrorAction.class}
95    )
96    public void testImplOnUnmappableCharacter() {
97        encoder = new MockCharsetEncoder(cs, 1, 3);
98        ((MockCharsetEncoder) encoder).pubImplOnUnmappableCharacter(null);
99    }
100
101    @TestTargets({
102        @TestTargetNew(
103            level = TestLevel.PARTIAL,
104            notes = "onMalformedInput & onUnmappableCharacter requires check for IllegalArgumentException",
105            method = "malformedInputAction",
106            args = {}
107        ),
108        @TestTargetNew(
109            level = TestLevel.PARTIAL,
110            notes = "onMalformedInput & onUnmappableCharacter requires check for IllegalArgumentException",
111            method = "unmappableCharacterAction",
112            args = {}
113        ),
114        @TestTargetNew(
115            level = TestLevel.PARTIAL,
116            notes = "onMalformedInput & onUnmappableCharacter requires check for IllegalArgumentException",
117            method = "onMalformedInput",
118            args = {java.nio.charset.CodingErrorAction.class}
119        ),
120        @TestTargetNew(
121            level = TestLevel.PARTIAL,
122            notes = "onMalformedInput & onUnmappableCharacter requires check for IllegalArgumentException",
123            method = "onUnmappableCharacter",
124            args = {java.nio.charset.CodingErrorAction.class}
125        ),
126        @TestTargetNew(
127            level = TestLevel.PARTIAL,
128            notes = "onMalformedInput & onUnmappableCharacter requires check for IllegalArgumentException",
129            method = "replacement",
130            args = {}
131        )
132    })
133    public void testDefaultValue() {
134        assertEquals(CodingErrorAction.REPORT, encoder.malformedInputAction());
135        assertEquals(CodingErrorAction.REPORT, encoder
136                .unmappableCharacterAction());
137        assertSame(encoder, encoder.onMalformedInput(CodingErrorAction.IGNORE));
138        assertSame(encoder, encoder
139                .onUnmappableCharacter(CodingErrorAction.IGNORE));
140        if (encoder instanceof MockCharsetEncoder) {
141            assertTrue(Arrays.equals(encoder.replacement(), defaultReplacement));
142        } else {
143            assertTrue(Arrays.equals(encoder.replacement(),
144                    specifiedReplacement));
145        }
146
147    }
148
149
150    @TestTargetNew(
151        level = TestLevel.COMPLETE,
152        notes = "",
153        method = "implReset",
154        args = {}
155    )
156    public void testImplReset() {
157        encoder = new MockCharsetEncoder(cs, 1, 3);
158        ((MockCharsetEncoder) encoder).pubImplReset();
159    }
160
161
162    @TestTargetNew(
163        level = TestLevel.COMPLETE,
164        notes = "",
165        method = "implFlush",
166        args = {ByteBuffer.class}
167    )
168    public void testImplFlush() {
169        encoder = new MockCharsetEncoder(cs, 1, 3);
170        assertEquals(CoderResult.UNDERFLOW, ((MockCharsetEncoder) encoder)
171                .pubImplFlush(null));
172    }
173
174
175    /*
176     * Class under test for constructor CharsetEncoder(Charset, float, float)
177     */
178    @TestTargets({
179        @TestTargetNew(
180            level = TestLevel.COMPLETE,
181            notes = "",
182            method = "charset",
183            args = {}
184        ),
185        @TestTargetNew(
186            level = TestLevel.COMPLETE,
187            notes = "",
188            method = "averageBytesPerChar",
189            args = {}
190        ),
191        @TestTargetNew(
192            level = TestLevel.COMPLETE,
193            notes = "",
194            method = "maxBytesPerChar",
195            args = {}
196        ),
197        @TestTargetNew(
198            level = TestLevel.COMPLETE,
199            notes = "",
200            method = "malformedInputAction",
201            args = {}
202        ),
203        @TestTargetNew(
204            level = TestLevel.COMPLETE,
205            notes = "",
206            method = "unmappableCharacterAction",
207            args = {}
208        ),
209        @TestTargetNew(
210            level = TestLevel.COMPLETE,
211            notes = "",
212            method = "CharsetEncoder",
213            args = {java.nio.charset.Charset.class, float.class, float.class}
214        ),
215        @TestTargetNew(
216            level = TestLevel.COMPLETE,
217            notes = "",
218            method = "replacement",
219            args = {}
220        )
221    })
222    public void testCharsetEncoderCharsetfloatfloat() {
223        // default value
224        encoder = new MockCharsetEncoder(cs, (float) AVER_BYTES, MAX_BYTES);
225        assertSame(encoder.charset(), cs);
226        assertTrue(encoder.averageBytesPerChar() == AVER_BYTES);
227        assertTrue(encoder.maxBytesPerChar() == MAX_BYTES);
228        assertEquals(CodingErrorAction.REPORT, encoder.malformedInputAction());
229        assertEquals(CodingErrorAction.REPORT, encoder
230                .unmappableCharacterAction());
231        assertEquals(new String(encoder.replacement()), new String(
232                defaultReplacement));
233        assertSame(encoder, encoder.onMalformedInput(CodingErrorAction.IGNORE));
234        assertSame(encoder, encoder
235                .onUnmappableCharacter(CodingErrorAction.IGNORE));
236
237        // normal case
238        CharsetEncoder ec = new MockCharsetEncoder(cs, 1, MAX_BYTES);
239        assertSame(ec.charset(), cs);
240        assertEquals(1.0, ec.averageBytesPerChar(), 0);
241        assertTrue(ec.maxBytesPerChar() == MAX_BYTES);
242
243        /*
244         * ------------------------ Exceptional cases -------------------------
245         */
246        // NullPointerException: null charset
247        try {
248            ec = new MockCharsetEncoder(null, 1, MAX_BYTES);
249            fail("should throw null pointer exception");
250        } catch (NullPointerException e) {
251        }
252
253        ec = new MockCharsetEncoder(new MockCharset("mock", new String[0]), 1,
254                MAX_BYTES);
255
256                // Commented out since the comment is wrong since MAX_BYTES > 1
257        // // OK: average length less than max length
258        // ec = new MockCharsetEncoder(cs, MAX_BYTES, 1);
259        // assertTrue(ec.averageBytesPerChar() == MAX_BYTES);
260        // assertTrue(ec.maxBytesPerChar() == 1);
261
262        // Illegal Argument: zero length
263        try {
264            ec = new MockCharsetEncoder(cs, 0, MAX_BYTES);
265            fail("should throw IllegalArgumentException");
266        } catch (IllegalArgumentException e) {
267        }
268        try {
269            ec = new MockCharsetEncoder(cs, 1, 0);
270            fail("should throw IllegalArgumentException");
271        } catch (IllegalArgumentException e) {
272        }
273
274        // Illegal Argument: negative length
275        try {
276            ec = new MockCharsetEncoder(cs, -1, MAX_BYTES);
277            fail("should throw IllegalArgumentException");
278        } catch (IllegalArgumentException e) {
279        }
280        try {
281            ec = new MockCharsetEncoder(cs, 1, -1);
282            fail("should throw IllegalArgumentException");
283        } catch (IllegalArgumentException e) {
284        }
285    }
286
287    /*
288     * Class under test for constructor CharsetEncoder(Charset, float, float,
289     * byte[])
290     */
291    @TestTargets({
292        @TestTargetNew(
293            level = TestLevel.COMPLETE,
294            notes = "",
295            method = "CharsetEncoder",
296            args = {java.nio.charset.Charset.class, float.class, float.class, byte[].class}
297        ),
298        @TestTargetNew(
299            level = TestLevel.COMPLETE,
300            notes = "",
301            method = "charset",
302            args = {}
303        ),
304        @TestTargetNew(
305            level = TestLevel.COMPLETE,
306            notes = "",
307            method = "averageBytesPerChar",
308            args = {}
309        ),
310        @TestTargetNew(
311            level = TestLevel.COMPLETE,
312            notes = "",
313            method = "maxBytesPerChar",
314            args = {}
315        ),
316        @TestTargetNew(
317            level = TestLevel.COMPLETE,
318            notes = "",
319            method = "replacement",
320            args = {}
321        )
322    })
323    public void testCharsetEncoderCharsetfloatfloatbyteArray() {
324        byte[] ba = getLegalByteArray();
325        // normal case
326        CharsetEncoder ec = new MockCharsetEncoder(cs, 1, MAX_BYTES, ba);
327        assertSame(ec.charset(), cs);
328        assertEquals(1.0, ec.averageBytesPerChar(), 0.0);
329        assertTrue(ec.maxBytesPerChar() == MAX_BYTES);
330        assertSame(ba, ec.replacement());
331
332        /*
333         * ------------------------ Exceptional cases -------------------------
334         */
335        // NullPointerException: null charset
336        try {
337            ec = new MockCharsetEncoder(null, 1, MAX_BYTES, ba);
338            fail("should throw null pointer exception");
339        } catch (NullPointerException e) {
340        }
341
342        // Illegal Argument: null byte array
343        try {
344            ec = new MockCharsetEncoder(cs, 1, MAX_BYTES, null);
345            fail("should throw IllegalArgumentException");
346        } catch (IllegalArgumentException e) {
347        }
348        // Illegal Argument: empty byte array
349        try {
350            ec = new MockCharsetEncoder(cs, 1, MAX_BYTES, new byte[0]);
351            fail("should throw IllegalArgumentException");
352        } catch (IllegalArgumentException e) {
353        }
354        // Illegal Argument: byte array is longer than max length
355        try {
356            ec = new MockCharsetEncoder(cs, 1, MAX_BYTES, new byte[] { 1, 2,
357                    MAX_BYTES, 4 });
358            fail("should throw IllegalArgumentException");
359        } catch (IllegalArgumentException e) {
360        }
361
362                // Commented out since the comment is wrong since MAX_BYTES > 1
363                // This test throws IllegalArgumentException on Harmony and RI
364        // // OK: average length less than max length
365        // ec = new MockCharsetEncoder(cs, MAX_BYTES, ba.length, ba);
366        // assertTrue(ec.averageBytesPerChar() == MAX_BYTES);
367        // assertTrue(ec.maxBytesPerChar() == ba.length);
368
369        // Illegal Argument: zero length
370        try {
371            ec = new MockCharsetEncoder(cs, 0, MAX_BYTES, ba);
372            fail("should throw IllegalArgumentException");
373        } catch (IllegalArgumentException e) {
374        }
375        try {
376            ec = new MockCharsetEncoder(cs, 1, 0, ba);
377            fail("should throw IllegalArgumentException");
378        } catch (IllegalArgumentException e) {
379        }
380
381        // Illegal Argument: negative length
382        try {
383            ec = new MockCharsetEncoder(cs, -1, MAX_BYTES, ba);
384            fail("should throw IllegalArgumentException");
385        } catch (IllegalArgumentException e) {
386        }
387        try {
388            ec = new MockCharsetEncoder(cs, 1, -1, ba);
389            fail("should throw IllegalArgumentException");
390        } catch (IllegalArgumentException e) {
391        }
392    }
393
394
395
396    /*
397     * Class under test for Charset charset()
398     */
399    @TestTargetNew(
400        level = TestLevel.PARTIAL,
401        notes = "",
402        method = "CharsetEncoder",
403        args = {java.nio.charset.Charset.class, float.class, float.class}
404    )
405    public void testCharset() {
406        try {
407            encoder = new MockCharsetEncoder(Charset.forName("gbk"), 1,
408                    MAX_BYTES);
409            // assertSame(encoder.charset(), Charset.forName("gbk"));
410        } catch (UnsupportedCharsetException e) {
411            // System.err
412            //         .println("Don't support GBK encoding, ignore current test");
413        }
414    }
415
416
417    boolean enCodeLoopCalled = false;
418
419    @TestTargetNew(
420        level = TestLevel.SUFFICIENT,
421        notes = "",
422        method = "encodeLoop",
423        args = { CharBuffer.class, ByteBuffer.class}
424    )
425    public void testEncodeLoop() throws Exception {
426        try {
427            encoder = new MockCharsetEncoder(Charset.forName("US-ASCII"), 1,
428                    MAX_BYTES) {
429                @Override
430                protected CoderResult encodeLoop(CharBuffer arg0,
431                        ByteBuffer arg1) {
432                    enCodeLoopCalled = true;
433                    return super.encodeLoop(arg0, arg1);
434                }
435            };
436            encoder.encode(CharBuffer.wrap("hallo"));
437        } catch (UnsupportedCharsetException e) {
438            fail("us-ascii not supported");
439        }
440        assertTrue(enCodeLoopCalled);
441    }
442
443
444    @TestTargetNew(
445        level = TestLevel.COMPLETE,
446        notes = "",
447        method = "implReplaceWith",
448        args = { byte[].class}
449    )
450    public void testImplReplaceWith() {
451        encoder = new MockCharsetEncoder(cs, 1, 3);
452        ((MockCharsetEncoder) encoder).pubImplReplaceWith(null);
453    }
454
455
456
457    protected byte[] getLegalByteArray() {
458        return new byte[] { 'a' };
459    }
460
461    protected byte[] getIllegalByteArray() {
462        return new byte[155];
463    }
464
465    /*
466     * Mock subclass of CharsetEncoder For protected method test
467     */
468    public static class MockCharsetEncoder extends CharsetEncoder {
469
470        boolean flushed = false;
471
472        public boolean isFlushed() {
473            boolean result = flushed;
474            flushed = false;
475            return result;
476        }
477
478        public boolean isLegalReplacement(byte[] ba) {
479            if (ba.length == 155) {// specified magic number, return false
480                return false;
481            }
482            return super.isLegalReplacement(ba);
483        }
484
485        public MockCharsetEncoder(Charset cs, float aver, float max) {
486            super(cs, aver, max);
487        }
488
489        public MockCharsetEncoder(Charset cs, float aver, float max,
490                byte[] replacement) {
491            super(cs, aver, max, replacement);
492        }
493
494        protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
495            int inPosition = in.position();
496            char[] input = new char[in.remaining()];
497            in.get(input);
498            String result = new String(input);
499            if (result.startsWith("malform")) {
500                // reset the cursor to the error position
501                in.position(inPosition);
502                // in.position(0);
503                // set the error length
504                return CoderResult.malformedForLength("malform".length());
505            } else if (result.startsWith("unmap")) {
506                // reset the cursor to the error position
507                in.position(inPosition);
508                // in.position(0);
509                // set the error length
510                return CoderResult.unmappableForLength("unmap".length());
511            } else if (result.startsWith("runtime")) {
512                // reset the cursor to the error position
513                in.position(0);
514                // set the error length
515                throw new RuntimeException("runtime");
516            }
517            int inLeft = input.length;
518            int outLeft = out.remaining();
519            CoderResult r = CoderResult.UNDERFLOW;
520            int length = inLeft;
521            if (outLeft < inLeft) {
522                r = CoderResult.OVERFLOW;
523                length = outLeft;
524                in.position(inPosition + outLeft);
525            }
526            for (int i = 0; i < length; i++) {
527                out.put((byte) input[i]);
528            }
529            return r;
530        }
531
532        protected CoderResult implFlush(ByteBuffer out) {
533            CoderResult result = super.implFlush(out);
534            int length = 0;
535            if (out.remaining() >= 5) {
536                length = 5;
537                result = CoderResult.UNDERFLOW;
538                flushed = true;
539                // for (int i = 0; i < length; i++) {
540                // out.put((byte)'f');
541                // }
542            } else {
543                length = out.remaining();
544                result = CoderResult.OVERFLOW;
545            }
546            return result;
547        }
548
549        protected void implReplaceWith(byte[] ba) {
550            assertSame(ba, replacement());
551        }
552
553        public void pubImplReplaceWith(byte[] newReplacement) {
554            super.implReplaceWith(newReplacement);
555        }
556
557        public CoderResult pubImplFlush(ByteBuffer out) {
558            return super.implFlush(out);
559        }
560
561        public void pubImplOnUnmappableCharacter(CodingErrorAction newAction) {
562            super.implOnUnmappableCharacter(newAction);
563        }
564
565        public void pubImplReset() {
566            super.implReset();
567        }
568
569    }
570
571    /*
572     * mock charset for test encoder initialization
573     */
574    public static class MockCharset extends Charset {
575        protected MockCharset(String arg0, String[] arg1) {
576            super(arg0, arg1);
577        }
578
579        public boolean contains(Charset arg0) {
580            return false;
581        }
582
583        public CharsetDecoder newDecoder() {
584            return new CharsetDecoderTest.MockCharsetDecoder(this,
585                    (float) AVER_BYTES, MAX_BYTES);
586        }
587
588        public CharsetEncoder newEncoder() {
589            return new MockCharsetEncoder(this, (float) AVER_BYTES, MAX_BYTES);
590        }
591    }
592
593}
594