/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* 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 org.apache.jdbm;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* This class contains all Unit tests for {@link PhysicalRowIdManager}.
*/
public class PhysicalRowIdManagerTest extends TestCaseWithTestFile {
private byte[] data = new byte[100000];
/**
* Test constructor
*/
public void testCtor() throws Exception {
PageFile f = newRecordFile();
PageManager pm = new PageManager(f);
PageFile free = newRecordFile();
PageManager pmfree = new PageManager(free);
PhysicalRowIdManager physMgr = new PhysicalRowIdManager(f, pm);
f.forceClose();
}
/**
* Test basics
*/
public void testBasics() throws Exception {
PageFile f = newRecordFile();
PageManager pm = new PageManager(f);
PageFile free = newRecordFile();
PageManager pmfree = new PageManager(free);
PhysicalRowIdManager physMgr = new PhysicalRowIdManager(f, pm);
// insert a 10,000 byte record.
byte[] data = UtilTT.makeRecord(10000, (byte) 1);
long loc = physMgr.insert(data, 0, data.length);
DataInputOutput a1 = new DataInputOutput();
physMgr.fetch(a1, loc);
assertTrue("check data1",
UtilTT.checkRecord(a1.toByteArray(), 10000, (byte) 1));
// update it as a 20,000 byte record.
data = UtilTT.makeRecord(20000, (byte) 2);
long loc2 = physMgr.update(loc, data, 0, data.length);
DataInputOutput a2 = new DataInputOutput();
physMgr.fetch(a2, loc2);
assertTrue("check data2",
UtilTT.checkRecord(a2.toByteArray(), 20000, (byte) 2));
// insert a third record. This'll effectively page the first one
// from growing
data = UtilTT.makeRecord(20, (byte) 3);
long loc3 = physMgr.insert(data, 0, data.length);
DataInputOutput a3 = new DataInputOutput();
physMgr.fetch(a3, loc3);
assertTrue("check data3",
UtilTT.checkRecord(a3.toByteArray(), 20, (byte) 3));
// now, grow the first record again
data = UtilTT.makeRecord(30000, (byte) 4);
loc2 = physMgr.update(loc2, data, 0, data.length);
DataInputOutput a4 = new DataInputOutput();
physMgr.fetch(a4, loc2);
assertTrue("check data4",
UtilTT.checkRecord(a4.toByteArray(), 30000, (byte) 4));
// delete the record
physMgr.free(loc2);
f.forceClose();
}
public void testTwoRecords() throws IOException {
PageFile f = newRecordFile();
PageManager pm = new PageManager(f);
PhysicalRowIdManager physmgr = new PhysicalRowIdManager(f, pm);
physmgr.insert(new byte[1024],0,1024);
physmgr.insert(new byte[100],0,100);
assertEquals(listRecords(pm),arrayList(1024,100));
}
public void testDeleteRecord() throws IOException {
PageFile f = newRecordFile();
PageManager pm = new PageManager(f);
PhysicalRowIdManager physmgr = new PhysicalRowIdManager(f, pm);
physmgr.insert(data,0,1024);
long recid = physmgr.insert(data,0,100);
physmgr.insert(data,0,700);
physmgr.free(recid);
assertEquals(listRecords(pm), arrayList(1024, -100, 700));
}
public void testTwoLargeRecord() throws IOException {
PageFile f = newRecordFile();
PageManager pm = new PageManager(f);
PhysicalRowIdManager physmgr = new PhysicalRowIdManager(f, pm);
physmgr.insert(data,0,5000);
physmgr.insert(data,0,5000);
assertEquals(listRecords(pm), arrayList(5000,5000));
}
public void testManyLargeRecord() throws IOException {
PageFile f = newRecordFile();
PageManager pm = new PageManager(f);
PhysicalRowIdManager physmgr = new PhysicalRowIdManager(f, pm);
physmgr.insert(data,0,5002);
long id1 = physmgr.insert(data,0,5003);
physmgr.insert(data,0,5005);
long id2 = physmgr.insert(data,0,5006);
physmgr.insert(data,0,5007);
physmgr.insert(data,0,5008);
physmgr.free(id1);
physmgr.free(id2);
assertEquals(listRecords(pm), arrayList(5002,-5003,5005,-5006,5007,5008));
}
public void testSplitRecordAcrossPage() throws IOException {
PageFile f = newRecordFile();
PageManager pm = new PageManager(f);
PhysicalRowIdManager physmgr = new PhysicalRowIdManager(f, pm);
physmgr.insert(data,0,3000);
long id = physmgr.insert(data,0,3000);
physmgr.insert(data,0,1000);
physmgr.free(id);
//record which crosses page should be sliced to two, so it does not cross the page
int firstSize = Storage.PAGE_SIZE - Magic.DATA_PAGE_O_DATA - RecordHeader.SIZE - 3000 - RecordHeader.SIZE;
int secondSize = 3000-firstSize - RecordHeader.SIZE;
//TODO decide about this
//assertEquals(listRecords(pm), arrayList(3000,-firstSize,-secondSize, 1000));
}
public void testFreeMidPages() throws IOException {
PageFile f = newRecordFile();
PageManager pm = new PageManager(f);
PhysicalRowIdManager physmgr = new PhysicalRowIdManager(f, pm);
physmgr.insert(data,0,3000);
long id = physmgr.insert(data,0,30000);
physmgr.insert(data,0,1000);
physmgr.free(id);
//if record occupies multiple pages, mid pages should be freed and record trimmed.
int newSize = 30000;
while(newSize>Storage.PAGE_SIZE - Magic.DATA_PAGE_O_DATA)
newSize = newSize - (Storage.PAGE_SIZE - Magic.DATA_PAGE_O_DATA);
assertEquals(listRecords(pm), arrayList(3000, -newSize, 1000));
}
/** return list of records in pageman, negative numbers are free records*/
List<Integer> listRecords(PageManager pageman) throws IOException {
int pos = Magic.DATA_PAGE_O_DATA;
List<Integer> ret =new ArrayList<Integer>();
for(
long pageid = pageman.getFirst(Magic.USED_PAGE);
pageid!=0;
pageid = pageman.getNext(pageid)){
PageIo page = pageman.file.get(pageid);
while(pos < Storage.PAGE_SIZE -RecordHeader.SIZE){
int size = RecordHeader.getAvailableSize(page, (short) pos);
if(size == 0)
break;
int currSize =RecordHeader.getCurrentSize(page, (short) pos);
pos+=size+RecordHeader.SIZE;
if(currSize==0)
size = -size;
ret.add(size);
}
pos = pos +Magic.DATA_PAGE_O_DATA - Storage.PAGE_SIZE;
pageman.file.release(page);
}
return ret;
}
List<Integer> arrayList(Integer... args){
ArrayList<Integer> ret = new ArrayList<Integer>();
for(Integer i:args)
ret.add(i);
return ret;
}
}