Small style stuff
[zxing.git] / core / test / src / com / google / zxing / qrcode / encoder / EncoderTestCase.java
1 /*
2  * Copyright 2008 ZXing authors
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 com.google.zxing.qrcode.encoder;
18
19 import com.google.zxing.WriterException;
20 import com.google.zxing.common.BitArray;
21 import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
22 import com.google.zxing.qrcode.decoder.Mode;
23 import org.junit.Assert;
24 import org.junit.Test;
25
26 import java.io.UnsupportedEncodingException;
27
28 /**
29  * @author satorux@google.com (Satoru Takabayashi) - creator
30  * @author mysen@google.com (Chris Mysen) - ported from C++
31  */
32 public final class EncoderTestCase extends Assert {
33
34   @Test
35   public void testGetAlphanumericCode() {
36     // The first ten code points are numbers.
37     for (int i = 0; i < 10; ++i) {
38       assertEquals(i, Encoder.getAlphanumericCode('0' + i));
39     }
40
41     // The next 26 code points are capital alphabet letters.
42     for (int i = 10; i < 36; ++i) {
43       assertEquals(i, Encoder.getAlphanumericCode('A' + i - 10));
44     }
45
46     // Others are symbol letters
47     assertEquals(36, Encoder.getAlphanumericCode(' '));
48     assertEquals(37, Encoder.getAlphanumericCode('$'));
49     assertEquals(38, Encoder.getAlphanumericCode('%'));
50     assertEquals(39, Encoder.getAlphanumericCode('*'));
51     assertEquals(40, Encoder.getAlphanumericCode('+'));
52     assertEquals(41, Encoder.getAlphanumericCode('-'));
53     assertEquals(42, Encoder.getAlphanumericCode('.'));
54     assertEquals(43, Encoder.getAlphanumericCode('/'));
55     assertEquals(44, Encoder.getAlphanumericCode(':'));
56
57     // Should return -1 for other letters;
58     assertEquals(-1, Encoder.getAlphanumericCode('a'));
59     assertEquals(-1, Encoder.getAlphanumericCode('#'));
60     assertEquals(-1, Encoder.getAlphanumericCode('\0'));
61   }
62
63   @Test
64   public void testChooseMode() throws WriterException {
65     // Numeric mode.
66     assertSame(Mode.NUMERIC, Encoder.chooseMode("0"));
67     assertSame(Mode.NUMERIC, Encoder.chooseMode("0123456789"));
68     // Alphanumeric mode.
69     assertSame(Mode.ALPHANUMERIC, Encoder.chooseMode("A"));
70     assertSame(Mode.ALPHANUMERIC,
71                Encoder.chooseMode("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"));
72     // 8-bit byte mode.
73     assertSame(Mode.BYTE, Encoder.chooseMode("a"));
74     assertSame(Mode.BYTE, Encoder.chooseMode("#"));
75     assertSame(Mode.BYTE, Encoder.chooseMode(""));
76     // Kanji mode.  We used to use MODE_KANJI for these, but we stopped
77     // doing that as we cannot distinguish Shift_JIS from other encodings
78     // from data bytes alone.  See also comments in qrcode_encoder.h.
79
80     // AIUE in Hiragana in Shift_JIS
81     assertSame(Mode.BYTE,
82                Encoder.chooseMode(shiftJISString(new byte[]{0x8, 0xa, 0x8, 0xa, 0x8, 0xa, 0x8, (byte) 0xa6})));
83
84     // Nihon in Kanji in Shift_JIS.
85     assertSame(Mode.BYTE, Encoder.chooseMode(shiftJISString(new byte[]{0x9, 0xf, 0x9, 0x7b})));
86
87     // Sou-Utsu-Byou in Kanji in Shift_JIS.
88     assertSame(Mode.BYTE, Encoder.chooseMode(shiftJISString(new byte[]{0xe, 0x4, 0x9, 0x5, 0x9, 0x61})));
89   }
90
91   @Test
92   public void testEncode() throws WriterException {
93     QRCode qrCode = new QRCode();
94     Encoder.encode("ABCDEF", ErrorCorrectionLevel.H, qrCode);
95     // The following is a valid QR Code that can be read by cell phones.
96     String expected =
97       "<<\n" +
98       " mode: ALPHANUMERIC\n" +
99       " ecLevel: H\n" +
100       " version: 1\n" +
101       " matrixWidth: 21\n" +
102       " maskPattern: 0\n" +
103       " numTotalBytes: 26\n" +
104       " numDataBytes: 9\n" +
105       " numECBytes: 17\n" +
106       " numRSBlocks: 1\n" +
107       " matrix:\n" +
108       " 1 1 1 1 1 1 1 0 1 1 1 1 0 0 1 1 1 1 1 1 1\n" +
109       " 1 0 0 0 0 0 1 0 0 1 1 1 0 0 1 0 0 0 0 0 1\n" +
110       " 1 0 1 1 1 0 1 0 0 1 0 1 1 0 1 0 1 1 1 0 1\n" +
111       " 1 0 1 1 1 0 1 0 1 1 1 0 1 0 1 0 1 1 1 0 1\n" +
112       " 1 0 1 1 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1\n" +
113       " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 1\n" +
114       " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" +
115       " 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0\n" +
116       " 0 0 1 0 1 1 1 0 1 1 0 0 1 1 0 0 0 1 0 0 1\n" +
117       " 1 0 1 1 1 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0\n" +
118       " 0 0 1 1 0 0 1 0 1 0 0 0 1 0 1 0 1 0 1 1 0\n" +
119       " 1 1 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0\n" +
120       " 0 0 1 1 0 1 1 1 1 0 0 0 1 0 1 0 1 1 1 1 0\n" +
121       " 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 1 0 1 0 0 0\n" +
122       " 1 1 1 1 1 1 1 0 0 0 1 0 1 0 1 1 0 0 0 0 1\n" +
123       " 1 0 0 0 0 0 1 0 1 1 1 1 0 1 0 1 1 1 1 0 1\n" +
124       " 1 0 1 1 1 0 1 0 1 0 1 1 0 1 0 1 0 0 0 0 1\n" +
125       " 1 0 1 1 1 0 1 0 0 1 1 0 1 1 1 1 0 1 0 1 0\n" +
126       " 1 0 1 1 1 0 1 0 1 0 0 0 1 0 1 0 1 1 1 0 1\n" +
127       " 1 0 0 0 0 0 1 0 0 1 1 0 1 1 0 1 0 0 0 1 1\n" +
128       " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 1 0 1\n" +
129       ">>\n";
130     assertEquals(expected, qrCode.toString());
131   }
132
133   @Test
134   public void testAppendModeInfo() {
135     BitArray bits = new BitArray();
136     Encoder.appendModeInfo(Mode.NUMERIC, bits);
137     assertEquals(" ...X", bits.toString());
138   }
139
140   @Test
141   public void testAppendLengthInfo() throws WriterException {
142     {
143       BitArray bits = new BitArray();
144       Encoder.appendLengthInfo(1,  // 1 letter (1/1).
145                                1,  // version 1.
146                                Mode.NUMERIC,
147                                bits);
148       assertEquals(" ........ .X", bits.toString());  // 10 bits.
149     }
150     {
151       BitArray bits = new BitArray();
152       Encoder.appendLengthInfo(2,  // 2 letters (2/1).
153                                10,  // version 10.
154                                Mode.ALPHANUMERIC,
155                                bits);
156       assertEquals(" ........ .X.", bits.toString());  // 11 bits.
157     }
158     {
159       BitArray bits = new BitArray();
160       Encoder.appendLengthInfo(255,  // 255 letter (255/1).
161                                27,  // version 27.
162                                Mode.BYTE,
163                                bits);
164       assertEquals(" ........ XXXXXXXX", bits.toString());  // 16 bits.
165     }
166     {
167       BitArray bits = new BitArray();
168       Encoder.appendLengthInfo(512,  // 512 letters (1024/2).
169                                40,  // version 40.
170                                Mode.KANJI,
171                                bits);
172       assertEquals(" ..X..... ....", bits.toString());  // 12 bits.
173     }
174   }
175
176   @Test
177   public void testAppendBytes() throws WriterException {
178     {
179       // Should use appendNumericBytes.
180       // 1 = 01 = 0001 in 4 bits.
181       BitArray bits = new BitArray();
182       Encoder.appendBytes("1", Mode.NUMERIC, bits, Encoder.DEFAULT_BYTE_MODE_ENCODING);
183       assertEquals(" ...X" , bits.toString());
184     }
185     {
186       // Should use appendAlphanumericBytes.
187       // A = 10 = 0xa = 001010 in 6 bits
188       BitArray bits = new BitArray();
189       Encoder.appendBytes("A", Mode.ALPHANUMERIC, bits, Encoder.DEFAULT_BYTE_MODE_ENCODING);
190       assertEquals(" ..X.X." , bits.toString());
191       // Lower letters such as 'a' cannot be encoded in MODE_ALPHANUMERIC.
192       try {
193         Encoder.appendBytes("a", Mode.ALPHANUMERIC, bits, Encoder.DEFAULT_BYTE_MODE_ENCODING);
194       } catch (WriterException we) {
195         // good
196       }
197     }
198     {
199       // Should use append8BitBytes.
200       // 0x61, 0x62, 0x63
201       BitArray bits = new BitArray();
202       Encoder.appendBytes("abc", Mode.BYTE, bits, Encoder.DEFAULT_BYTE_MODE_ENCODING);
203       assertEquals(" .XX....X .XX...X. .XX...XX", bits.toString());
204       // Anything can be encoded in QRCode.MODE_8BIT_BYTE.
205       Encoder.appendBytes("\0", Mode.BYTE, bits, Encoder.DEFAULT_BYTE_MODE_ENCODING);
206     }
207     {
208       // Should use appendKanjiBytes.
209       // 0x93, 0x5f
210       BitArray bits = new BitArray();
211       Encoder.appendBytes(shiftJISString(new byte[] {(byte)0x93,0x5f}), Mode.KANJI, bits, Encoder.DEFAULT_BYTE_MODE_ENCODING);
212       assertEquals(" .XX.XX.. XXXXX", bits.toString());
213     }
214   }
215
216   @Test
217   public void testTerminateBits() throws WriterException {
218     {
219       BitArray v = new BitArray();
220       Encoder.terminateBits(0, v);
221       assertEquals("", v.toString());
222     }
223     {
224       BitArray v = new BitArray();
225       Encoder.terminateBits(1, v);
226       assertEquals(" ........", v.toString());
227     }
228     {
229       BitArray v = new BitArray();
230       v.appendBits(0, 3);  // Append 000
231       Encoder.terminateBits(1, v);
232       assertEquals(" ........", v.toString());
233     }
234     {
235       BitArray v = new BitArray();
236       v.appendBits(0, 5);  // Append 00000
237       Encoder.terminateBits(1, v);
238       assertEquals(" ........", v.toString());
239     }
240     {
241       BitArray v = new BitArray();
242       v.appendBits(0, 8);  // Append 00000000
243       Encoder.terminateBits(1, v);
244       assertEquals(" ........", v.toString());
245     }
246     {
247       BitArray v = new BitArray();
248       Encoder.terminateBits(2, v);
249       assertEquals(" ........ XXX.XX..", v.toString());
250     }
251     {
252       BitArray v = new BitArray();
253       v.appendBits(0, 1);  // Append 0
254       Encoder.terminateBits(3, v);
255       assertEquals(" ........ XXX.XX.. ...X...X", v.toString());
256     }
257   }
258
259   @Test
260   public void testGetNumDataBytesAndNumECBytesForBlockID() throws WriterException {
261     int[] numDataBytes = new int[1];
262     int[] numEcBytes = new int[1];
263     // Version 1-H.
264     Encoder.getNumDataBytesAndNumECBytesForBlockID(26, 9, 1, 0, numDataBytes, numEcBytes);
265     assertEquals(9, numDataBytes[0]);
266     assertEquals(17, numEcBytes[0]);
267
268     // Version 3-H.  2 blocks.
269     Encoder.getNumDataBytesAndNumECBytesForBlockID(70, 26, 2, 0, numDataBytes, numEcBytes);
270     assertEquals(13, numDataBytes[0]);
271     assertEquals(22, numEcBytes[0]);
272     Encoder.getNumDataBytesAndNumECBytesForBlockID(70, 26, 2, 1, numDataBytes, numEcBytes);
273     assertEquals(13, numDataBytes[0]);
274     assertEquals(22, numEcBytes[0]);
275
276     // Version 7-H. (4 + 1) blocks.
277     Encoder.getNumDataBytesAndNumECBytesForBlockID(196, 66, 5, 0, numDataBytes, numEcBytes);
278     assertEquals(13, numDataBytes[0]);
279     assertEquals(26, numEcBytes[0]);
280     Encoder.getNumDataBytesAndNumECBytesForBlockID(196, 66, 5, 4, numDataBytes, numEcBytes);
281     assertEquals(14, numDataBytes[0]);
282     assertEquals(26, numEcBytes[0]);
283
284     // Version 40-H. (20 + 61) blocks.
285     Encoder.getNumDataBytesAndNumECBytesForBlockID(3706, 1276, 81, 0, numDataBytes, numEcBytes);
286     assertEquals(15, numDataBytes[0]);
287     assertEquals(30, numEcBytes[0]);
288     Encoder.getNumDataBytesAndNumECBytesForBlockID(3706, 1276, 81, 20, numDataBytes, numEcBytes);
289     assertEquals(16, numDataBytes[0]);
290     assertEquals(30, numEcBytes[0]);
291     Encoder.getNumDataBytesAndNumECBytesForBlockID(3706, 1276, 81, 80, numDataBytes, numEcBytes);
292     assertEquals(16, numDataBytes[0]);
293     assertEquals(30, numEcBytes[0]);
294   }
295
296   @Test
297   public void testInterleaveWithECBytes() throws WriterException {
298     {
299       byte[] dataBytes = {32, 65, (byte)205, 69, 41, (byte)220, 46, (byte)128, (byte)236};
300       BitArray in = new BitArray();
301       for (byte dataByte: dataBytes) {
302         in.appendBits(dataByte, 8);
303       }
304       BitArray out = new BitArray();
305       Encoder.interleaveWithECBytes(in, 26, 9, 1, out);
306       byte[] expected = {
307           // Data bytes.
308           32, 65, (byte)205, 69, 41, (byte)220, 46, (byte)128, (byte)236,
309           // Error correction bytes.
310           42, (byte)159, 74, (byte)221, (byte)244, (byte)169, (byte)239, (byte)150, (byte)138, 70,
311           (byte)237, 85, (byte)224, 96, 74, (byte)219, 61,
312       };
313       assertEquals(expected.length, out.getSizeInBytes());
314       byte[] outArray = new byte[expected.length];
315       out.toBytes(0, outArray, 0, expected.length);
316       // Can't use Arrays.equals(), because outArray may be longer than out.sizeInBytes()
317       for (int x = 0; x < expected.length; x++) {
318         assertEquals(expected[x], outArray[x]);
319       }
320     }
321     // Numbers are from http://www.swetake.com/qr/qr8.html
322     {
323       byte[] dataBytes = {
324           67, 70, 22, 38, 54, 70, 86, 102, 118, (byte)134, (byte)150, (byte)166, (byte)182,
325           (byte)198, (byte)214, (byte)230, (byte)247, 7, 23, 39, 55, 71, 87, 103, 119, (byte)135,
326           (byte)151, (byte)166, 22, 38, 54, 70, 86, 102, 118, (byte)134, (byte)150, (byte)166,
327           (byte)182, (byte)198, (byte)214, (byte)230, (byte)247, 7, 23, 39, 55, 71, 87, 103, 119,
328           (byte)135, (byte)151, (byte)160, (byte)236, 17, (byte)236, 17, (byte)236, 17, (byte)236,
329           17
330       };
331       BitArray in = new BitArray();
332       for (byte dataByte: dataBytes) {
333         in.appendBits(dataByte, 8);
334       }
335       BitArray out = new BitArray();
336       Encoder.interleaveWithECBytes(in, 134, 62, 4, out);
337       byte[] expected = {
338           // Data bytes.
339           67, (byte)230, 54, 55, 70, (byte)247, 70, 71, 22, 7, 86, 87, 38, 23, 102, 103, 54, 39,
340           118, 119, 70, 55, (byte)134, (byte)135, 86, 71, (byte)150, (byte)151, 102, 87, (byte)166,
341           (byte)160, 118, 103, (byte)182, (byte)236, (byte)134, 119, (byte)198, 17, (byte)150,
342           (byte)135, (byte)214, (byte)236, (byte)166, (byte)151, (byte)230, 17, (byte)182,
343           (byte)166, (byte)247, (byte)236, (byte)198, 22, 7, 17, (byte)214, 38, 23, (byte)236, 39,
344           17,
345           // Error correction bytes.
346           (byte)175, (byte)155, (byte)245, (byte)236, 80, (byte)146, 56, 74, (byte)155, (byte)165,
347           (byte)133, (byte)142, 64, (byte)183, (byte)132, 13, (byte)178, 54, (byte)132, 108, 45,
348           113, 53, 50, (byte)214, 98, (byte)193, (byte)152, (byte)233, (byte)147, 50, 71, 65,
349           (byte)190, 82, 51, (byte)209, (byte)199, (byte)171, 54, 12, 112, 57, 113, (byte)155, 117,
350           (byte)211, (byte)164, 117, 30, (byte)158, (byte)225, 31, (byte)190, (byte)242, 38,
351           (byte)140, 61, (byte)179, (byte)154, (byte)214, (byte)138, (byte)147, 87, 27, 96, 77, 47,
352           (byte)187, 49, (byte)156, (byte)214,
353       };
354       assertEquals(expected.length, out.getSizeInBytes());
355       byte[] outArray = new byte[expected.length];
356       out.toBytes(0, outArray, 0, expected.length);
357       for (int x = 0; x < expected.length; x++) {
358         assertEquals(expected[x], outArray[x]);
359       }
360     }
361   }
362
363   @Test
364   public void testAppendNumericBytes() {
365     {
366       // 1 = 01 = 0001 in 4 bits.
367       BitArray bits = new BitArray();
368       Encoder.appendNumericBytes("1", bits);
369       assertEquals(" ...X" , bits.toString());
370     }
371     {
372       // 12 = 0xc = 0001100 in 7 bits.
373       BitArray bits = new BitArray();
374       Encoder.appendNumericBytes("12", bits);
375       assertEquals(" ...XX.." , bits.toString());
376     }
377     {
378       // 123 = 0x7b = 0001111011 in 10 bits.
379       BitArray bits = new BitArray();
380       Encoder.appendNumericBytes("123", bits);
381       assertEquals(" ...XXXX. XX" , bits.toString());
382     }
383     {
384       // 1234 = "123" + "4" = 0001111011 + 0100
385       BitArray bits = new BitArray();
386       Encoder.appendNumericBytes("1234", bits);
387       assertEquals(" ...XXXX. XX.X.." , bits.toString());
388     }
389     {
390       // Empty.
391       BitArray bits = new BitArray();
392       Encoder.appendNumericBytes("", bits);
393       assertEquals("" , bits.toString());
394     }
395   }
396
397   @Test
398   public void testAppendAlphanumericBytes() throws WriterException {
399     {
400       // A = 10 = 0xa = 001010 in 6 bits
401       BitArray bits = new BitArray();
402       Encoder.appendAlphanumericBytes("A", bits);
403       assertEquals(" ..X.X." , bits.toString());
404     }
405     {
406       // AB = 10 * 45 + 11 = 461 = 0x1cd = 00111001101 in 11 bits
407       BitArray bits = new BitArray();
408       Encoder.appendAlphanumericBytes("AB", bits);
409       assertEquals(" ..XXX..X X.X", bits.toString());
410     }
411     {
412       // ABC = "AB" + "C" = 00111001101 + 001100
413       BitArray bits = new BitArray();
414       Encoder.appendAlphanumericBytes("ABC", bits);
415       assertEquals(" ..XXX..X X.X..XX. ." , bits.toString());
416     }
417     {
418       // Empty.
419       BitArray bits = new BitArray();
420       Encoder.appendAlphanumericBytes("", bits);
421       assertEquals("" , bits.toString());
422     }
423     {
424       // Invalid data.
425       BitArray bits = new BitArray();
426       try {
427         Encoder.appendAlphanumericBytes("abc", bits);
428       } catch (WriterException we) {
429         // good
430       }
431     }
432   }
433
434   @Test
435   public void testAppend8BitBytes() throws WriterException {
436     {
437       // 0x61, 0x62, 0x63
438       BitArray bits = new BitArray();
439       Encoder.append8BitBytes("abc", bits, Encoder.DEFAULT_BYTE_MODE_ENCODING);
440       assertEquals(" .XX....X .XX...X. .XX...XX", bits.toString());
441     }
442     {
443       // Empty.
444       BitArray bits = new BitArray();
445       Encoder.append8BitBytes("", bits, Encoder.DEFAULT_BYTE_MODE_ENCODING);
446       assertEquals("", bits.toString());
447     }
448   }
449
450   // Numbers are from page 21 of JISX0510:2004
451   @Test
452   public void testAppendKanjiBytes() throws WriterException {
453     BitArray bits = new BitArray();
454       Encoder.appendKanjiBytes(shiftJISString(new byte[] {(byte)0x93,0x5f}), bits);
455       assertEquals(" .XX.XX.. XXXXX", bits.toString());
456       Encoder.appendKanjiBytes(shiftJISString(new byte[] {(byte)0xe4,(byte)0xaa}), bits);
457       assertEquals(" .XX.XX.. XXXXXXX. X.X.X.X. X.", bits.toString());
458   }
459
460   // Numbers are from http://www.swetake.com/qr/qr3.html and
461   // http://www.swetake.com/qr/qr9.html
462   @Test
463   public void testGenerateECBytes() {
464     {
465       byte[] dataBytes = {32, 65, (byte)205, 69, 41, (byte)220, 46, (byte)128, (byte)236};
466       byte[] ecBytes = Encoder.generateECBytes(dataBytes, 17);
467       int[] expected = {
468           42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61
469       };
470       assertEquals(expected.length, ecBytes.length);
471       for (int x = 0; x < expected.length; x++) {
472         assertEquals(expected[x], ecBytes[x] & 0xFF);
473       }
474     }
475     {
476       byte[] dataBytes = {67, 70, 22, 38, 54, 70, 86, 102, 118,
477           (byte)134, (byte)150, (byte)166, (byte)182, (byte)198, (byte)214};
478       byte[] ecBytes = Encoder.generateECBytes(dataBytes, 18);
479       int[] expected = {
480           175, 80, 155, 64, 178, 45, 214, 233, 65, 209, 12, 155, 117, 31, 140, 214, 27, 187
481       };
482       assertEquals(expected.length, ecBytes.length);
483       for (int x = 0; x < expected.length; x++) {
484         assertEquals(expected[x], ecBytes[x] & 0xFF);
485       }
486     }
487     {
488       // High-order zero coefficient case.
489       byte[] dataBytes = {32, 49, (byte)205, 69, 42, 20, 0, (byte)236, 17};
490       byte[] ecBytes = Encoder.generateECBytes(dataBytes, 17);
491       int[] expected = {
492           0, 3, 130, 179, 194, 0, 55, 211, 110, 79, 98, 72, 170, 96, 211, 137, 213
493       };
494       assertEquals(expected.length, ecBytes.length);
495       for (int x = 0; x < expected.length; x++) {
496         assertEquals(expected[x], ecBytes[x] & 0xFF);
497       }
498     }
499   }
500
501   @Test
502   public void testBugInBitVectorNumBytes() throws WriterException {
503     // There was a bug in BitVector.sizeInBytes() that caused it to return a
504     // smaller-by-one value (ex. 1465 instead of 1466) if the number of bits
505     // in the vector is not 8-bit aligned.  In QRCodeEncoder::InitQRCode(),
506     // BitVector::sizeInBytes() is used for finding the smallest QR Code
507     // version that can fit the given data.  Hence there were corner cases
508     // where we chose a wrong QR Code version that cannot fit the given
509     // data.  Note that the issue did not occur with MODE_8BIT_BYTE, as the
510     // bits in the bit vector are always 8-bit aligned.
511     //
512     // Before the bug was fixed, the following test didn't pass, because:
513     //
514     // - MODE_NUMERIC is chosen as all bytes in the data are '0'
515     // - The 3518-byte numeric data needs 1466 bytes
516     //   - 3518 / 3 * 10 + 7 = 11727 bits = 1465.875 bytes
517     //   - 3 numeric bytes are encoded in 10 bits, hence the first
518     //     3516 bytes are encoded in 3516 / 3 * 10 = 11720 bits.
519     //   - 2 numeric bytes can be encoded in 7 bits, hence the last
520     //     2 bytes are encoded in 7 bits.
521     // - The version 27 QR Code with the EC level L has 1468 bytes for data.
522     //   - 1828 - 360 = 1468
523     // - In InitQRCode(), 3 bytes are reserved for a header.  Hence 1465 bytes
524     //   (1468 -3) are left for data.
525     // - Because of the bug in BitVector::sizeInBytes(), InitQRCode() determines
526     //   the given data can fit in 1465 bytes, despite it needs 1466 bytes.
527     // - Hence QRCodeEncoder.encode() failed and returned false.
528     //   - To be precise, it needs 11727 + 4 (getMode info) + 14 (length info) =
529     //     11745 bits = 1468.125 bytes are needed (i.e. cannot fit in 1468
530     //     bytes).
531     StringBuilder builder = new StringBuilder(3518);
532     for (int x = 0; x < 3518; x++) {
533       builder.append('0');
534     }
535     QRCode qrCode = new QRCode();
536     Encoder.encode(builder.toString(), ErrorCorrectionLevel.L, qrCode);
537   }
538
539   private static String shiftJISString(byte[] bytes) throws WriterException {
540     try {
541       return new String(bytes, "Shift_JIS");
542     } catch (UnsupportedEncodingException uee) {
543       throw new WriterException(uee.toString());
544     }
545   }
546
547 }