| /* ==================================================================== |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You 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 org.apache.poi.hssf.record; |
| |
| |
| import static org.junit.Assert.assertArrayEquals; |
| import junit.framework.AssertionFailedError; |
| import junit.framework.TestCase; |
| |
| import org.apache.poi.util.HexRead; |
| import org.apache.poi.util.LittleEndian; |
| |
| /** |
| * Tests Subrecord components of an OBJ record. Test data taken directly |
| * from a real Excel file. |
| * |
| * @author Michael Zalewski (zalewski@optonline.net) |
| */ |
| public final class TestSubRecord extends TestCase { |
| /* |
| The following is a dump of the OBJ record corresponding to an auto-filter |
| drop-down list. The 3rd subrecord beginning at offset 0x002e (type=0x0013) |
| does not conform to the documentation, because the length field is 0x1fee, |
| which is longer than the entire OBJ record. |
| |
| 00000000 15 00 12 00 14 00 01 00 01 21 00 00 00 00 3C 13 .........!....<. Type=0x15 Len=0x0012 ftCmo |
| 00000010 F4 03 00 00 00 00 |
| 0C 00 14 00 00 00 00 00 00 00 ................ Type=0x0c Len=0x0014 ftSbs |
| 00000020 00 00 00 00 01 00 08 00 00 00 10 00 00 00 |
| 13 00 ................ Type=0x13 Len=0x1FEE ftLbsData |
| 00000030 EE 1F 00 00 08 00 08 00 01 03 00 00 0A 00 14 00 ................ |
| 00000040 6C 00 |
| 00 00 00 00 l..... Type=0x00 Len=0x0000 ftEnd |
| */ |
| |
| private static final byte[] dataAutoFilter |
| = HexRead.readFromString("" |
| + "5D 00 46 00 " // ObjRecord.sid, size=70 |
| // ftCmo |
| + "15 00 12 00 " |
| + "14 00 01 00 01 00 01 21 00 00 3C 13 F4 03 00 00 00 00 " |
| // ftSbs (currently UnknownSubrecord) |
| + "0C 00 14 00 " |
| + "00 00 00 00 00 00 00 00 00 00 01 00 08 00 00 00 10 00 00 00 " |
| // ftLbsData (currently UnknownSubrecord) |
| + "13 00 EE 1F 00 00 " |
| + "08 00 08 00 01 03 00 00 0A 00 14 00 6C 00 " |
| // ftEnd |
| + "00 00 00 00" |
| ); |
| |
| |
| /** |
| * Make sure that ftLbsData (which has abnormal size info) is parsed correctly. |
| * If the size field is interpreted incorrectly, the resulting ObjRecord becomes way too big. |
| * At the time of fixing (Oct-2008 svn r707447) {@link RecordInputStream} allowed buffer |
| * read overruns, so the bug was mostly silent. |
| */ |
| public void testReadAll_bug45778() { |
| RecordInputStream in = TestcaseRecordInputStream.create(dataAutoFilter); |
| ObjRecord or = new ObjRecord(in); |
| byte[] data2 = or.serialize(); |
| if (data2.length == 8228) { |
| throw new AssertionFailedError("Identified bug 45778"); |
| } |
| assertEquals(74, data2.length); |
| assertArrayEquals(dataAutoFilter, data2); |
| } |
| |
| public void testReadManualComboWithFormula() { |
| byte[] data = HexRead.readFromString("" |
| + "5D 00 66 00 " |
| + "15 00 12 00 14 00 02 00 11 20 00 00 00 00 " |
| + "20 44 C6 04 00 00 00 00 0C 00 14 00 04 F0 C6 04 " |
| + "00 00 00 00 00 00 01 00 06 00 00 00 10 00 00 00 " |
| + "0E 00 0C 00 05 00 80 44 C6 04 24 09 00 02 00 02 " |
| + "13 00 DE 1F 10 00 09 00 80 44 C6 04 25 0A 00 0F " |
| + "00 02 00 02 00 02 06 00 03 00 08 00 00 00 00 00 " |
| + "08 00 00 00 00 00 00 00 " // TODO sometimes last byte is non-zero |
| ); |
| |
| RecordInputStream in = TestcaseRecordInputStream.create(data); |
| ObjRecord or = new ObjRecord(in); |
| byte[] data2 = or.serialize(); |
| if (data2.length == 8228) { |
| throw new AssertionFailedError("Identified bug 45778"); |
| } |
| assertEquals("Encoded length", data.length, data2.length); |
| for (int i = 0; i < data.length; i++) { |
| if (data[i] != data2[i]) { |
| throw new AssertionFailedError("Encoded data differs at index " + i); |
| } |
| } |
| assertArrayEquals(data, data2); |
| } |
| |
| /** |
| * Some versions of POI (e.g. 3.1 - prior to svn r707450 / bug 45778) interpreted the ftLbs |
| * subrecord second short (0x1FEE) as a length, and hence read lots of extra padding. This |
| * buffer-overrun in {@link RecordInputStream} happened silently due to problems later fixed |
| * in svn 707778. When the ObjRecord is written, the extra padding is written too, making the |
| * record 8224 bytes long instead of 70. |
| * (An aside: It seems more than a coincidence that this problem creates a record of exactly |
| * {@link RecordInputStream#MAX_RECORD_DATA_SIZE} but not enough is understood about |
| * subrecords to explain this.)<br/> |
| * |
| * Excel reads files with this excessive padding OK. It also truncates the over-sized |
| * ObjRecord back to the proper size. POI should do the same. |
| */ |
| public void testWayTooMuchPadding_bug46545() { |
| byte[] data = HexRead.readFromString("" |
| + "15 00 12 00 14 00 13 00 01 21 00 00 00" |
| + "00 98 0B 5B 09 00 00 00 00 0C 00 14 00 00 00 00 00 00 00 00" |
| + "00 00 00 01 00 01 00 00 00 10 00 00 00 " |
| // ftLbs |
| + "13 00 EE 1F 00 00 " |
| + "01 00 00 00 01 06 00 00 02 00 08 00 75 00 " |
| // ftEnd |
| + "00 00 00 00" |
| ); |
| final int LBS_START_POS = 0x002E; |
| final int WRONG_LBS_SIZE = 0x1FEE; |
| assertEquals(0x0013, LittleEndian.getShort(data, LBS_START_POS+0)); |
| assertEquals(WRONG_LBS_SIZE, LittleEndian.getShort(data, LBS_START_POS+2)); |
| int wrongTotalSize = LBS_START_POS + 4 + WRONG_LBS_SIZE; |
| byte[] wrongData = new byte[wrongTotalSize]; |
| System.arraycopy(data, 0, wrongData, 0, data.length); |
| // wrongData has the ObjRecord data as would have been written by v3.1 |
| |
| RecordInputStream in = TestcaseRecordInputStream.create(ObjRecord.sid, wrongData); |
| ObjRecord or; |
| try { |
| or = new ObjRecord(in); |
| } catch (RecordFormatException e) { |
| if (e.getMessage().startsWith("Leftover 8154 bytes in subrecord data")) { |
| throw new AssertionFailedError("Identified bug 46545"); |
| } |
| throw e; |
| } |
| // make sure POI properly truncates the ObjRecord data |
| byte[] data2 = or.serialize(); |
| TestcaseRecordInputStream.confirmRecordEncoding(ObjRecord.sid, data, data2); |
| } |
| } |