/*******************************************************************************
* Copyright (c) 2005, 2009 QNX Software Systems
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* QNX Software Systems - initial API and implementation
* Andrew Ferguson (Symbian)
* Markus Schorn (Wind River Systems)
*******************************************************************************/
package org.eclipse.cdt.internal.pdom.tests;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import junit.framework.Test;
import org.eclipse.cdt.core.testplugin.CTestPlugin;
import org.eclipse.cdt.core.testplugin.util.BaseTestCase;
import org.eclipse.cdt.internal.core.pdom.db.BTree;
import org.eclipse.cdt.internal.core.pdom.db.ChunkCache;
import org.eclipse.cdt.internal.core.pdom.db.Database;
import org.eclipse.cdt.internal.core.pdom.db.IBTreeComparator;
import org.eclipse.cdt.internal.core.pdom.db.IBTreeVisitor;
import org.eclipse.cdt.internal.core.pdom.db.IString;
import org.eclipse.cdt.internal.core.pdom.db.ShortString;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
public class DBTest extends BaseTestCase {
protected Database db;
@Override
protected void setUp() throws Exception {
super.setUp();
db = new Database(getTestDir().append(getName()+System.currentTimeMillis()+".dat").toFile(),
new ChunkCache(), 0, false);
db.setExclusiveLock();
}
public static Test suite() {
return suite(DBTest.class);
}
protected IPath getTestDir() {
IPath path = CTestPlugin.getDefault().getStateLocation().append("tests/");
File file = path.toFile();
if (!file.exists())
file.mkdir();
return path;
}
@Override
protected void tearDown() throws Exception {
db.close();
if(!db.getLocation().delete()) {
db.getLocation().deleteOnExit();
}
db= null;
}
public void testBlockSizeAndFirstBlock() throws Exception {
assertEquals(0, db.getVersion());
final int realsize = 42;
final int deltas = (realsize+Database.BLOCK_HEADER_SIZE + Database.BLOCK_SIZE_DELTA - 1) / Database.BLOCK_SIZE_DELTA;
final int blocksize = deltas * Database.BLOCK_SIZE_DELTA;
final int freeDeltas= Database.CHUNK_SIZE/Database.BLOCK_SIZE_DELTA-deltas;
long mem = db.malloc(realsize);
assertEquals(-blocksize, db.getShort(mem - Database.BLOCK_HEADER_SIZE));
db.free(mem);
assertEquals(blocksize, db.getShort(mem - Database.BLOCK_HEADER_SIZE));
assertEquals(mem, db.getRecPtr((deltas-Database.MIN_BLOCK_DELTAS+1) * Database.INT_SIZE));
assertEquals(mem + blocksize, db.getRecPtr((freeDeltas-Database.MIN_BLOCK_DELTAS+1) * Database.INT_SIZE));
}
public void testBug192437() throws IOException {
File tmp= File.createTempFile("readOnlyEmpty", ".db");
try {
tmp.setReadOnly();
/* check opening a readonly file for rw access fails */
try {
new Database(tmp, ChunkCache.getSharedInstance(), 0, false);
fail("A readonly file should not be openable with write-access");
} catch(CoreException ioe) {
// we expect to get a failure here
}
/* check opening a readonly file for read access does not fail */
try {
new Database(tmp, ChunkCache.getSharedInstance(), 0, true);
} catch(CoreException ce) {
fail("A readonly file should be readable by a permanently readonly database "+ce);
}
} finally {
tmp.delete(); // this may be pointless on some platforms
}
}
public void testFreeBlockLinking() throws Exception {
final int realsize = 42;
final int deltas = (realsize+Database.BLOCK_HEADER_SIZE + Database.BLOCK_SIZE_DELTA - 1) / Database.BLOCK_SIZE_DELTA;
final int blocksize = deltas * Database.BLOCK_SIZE_DELTA;
final int freeDeltas= Database.MIN_BLOCK_DELTAS-deltas;
long mem1 = db.malloc(realsize);
long mem2 = db.malloc(realsize);
db.free(mem1);
db.free(mem2);
assertEquals(mem2, db.getRecPtr((deltas-Database.MIN_BLOCK_DELTAS+1) * Database.INT_SIZE));
assertEquals(0, db.getRecPtr(mem2));
assertEquals(mem1, db.getRecPtr(mem2 + Database.INT_SIZE));
assertEquals(mem2, db.getRecPtr(mem1));
assertEquals(0, db.getRecPtr(mem1 + Database.INT_SIZE));
}
public void testSimpleAllocationLifecycle() throws Exception {
long mem1 = db.malloc(42);
db.free(mem1);
long mem2 = db.malloc(42);
assertEquals(mem2, mem1);
}
private static class FindVisitor implements IBTreeVisitor {
private Database db;
private String key;
private long record;
public FindVisitor(Database db, String key) {
this.db = db;
this.key = key;
}
public int compare(long record) throws CoreException {
return db.getString(db.getRecPtr(record + 4)).compare(key, true);
}
public boolean visit(long record) throws CoreException {
this.record = record;
return false;
}
public long getRecord() {
return record;
}
}
public void testStringsInBTree() throws Exception {
// Tests inserting and retrieving strings
File f = getTestDir().append("testStrings.dat").toFile();
f.delete();
final Database db = new Database(f, new ChunkCache(), 0, false);
db.setExclusiveLock();
String[] names = {
"ARLENE",
"BRET",
"CINDY",
"DENNIS",
"EMILY",
"FRANKLIN",
"GERT",
"HARVEY",
"IRENE",
"JOSE",
"KATRINA",
"LEE",
"MARIA",
"NATE",
"OPHELIA",
"PHILIPPE",
"RITA",
"STAN",
"TAMMY",
"VINCE",
"WILMA",
"ALPHA",
"BETA"
};
IBTreeComparator comparator = new IBTreeComparator() {
public int compare(long record1, long record2) throws CoreException {
IString string1 = db.getString(db.getRecPtr(record1 + 4));
IString string2 = db.getString(db.getRecPtr(record2 + 4));
return string1.compare(string2, true);
}
};
BTree btree = new BTree(db, Database.DATA_AREA, comparator);
for (int i = 0; i < names.length; ++i) {
String name = names[i];
long record = db.malloc(8);
db.putInt(record + 0, i);
IString string = db.newString(name);
db.putRecPtr(record + 4, string.getRecord());
btree.insert(record);
}
for (int i = 0; i < names.length; ++i) {
String name = names[i];
FindVisitor finder = new FindVisitor(db, name);
btree.accept(finder);
long record = finder.getRecord();
assertTrue(record != 0);
assertEquals(i, db.getInt(record));
IString rname = db.getString(db.getRecPtr(record + 4));
assertTrue(rname.equals(name));
}
}
private final int GT = 1, LT = -1, EQ = 0;
public void testShortStringComparison() throws CoreException {
Random r= new Random(90210);
assertCMP("", EQ, "", true);
assertCMP("", EQ, "", false);
doTrials(1000, 1, ShortString.MAX_LENGTH, r, true);
doTrials(1000, 1, ShortString.MAX_LENGTH, r, false);
assertCMP("a", LT, "b", true);
assertCMP("aa", LT, "ab", true);
assertCMP("a", EQ, "a", true);
assertCMP("a", GT, "A", true);
assertCMP("aa", GT, "aA", true);
assertCMP("a", GT, "B", true);
assertCMP("a", EQ, "a", false);
assertCMP("a", EQ, "A", false);
}
public void testLongStringComparison() throws CoreException {
Random r= new Random(314159265);
doTrials(100, ShortString.MAX_LENGTH+1, ShortString.MAX_LENGTH*2, r, true);
doTrials(100, ShortString.MAX_LENGTH+1, ShortString.MAX_LENGTH*2, r, false);
}
private void doTrials(int n, int min, int max, Random r, boolean caseSensitive) throws CoreException {
long start = System.currentTimeMillis();
for(int i=0; i<n; i++) {
String a = randomString(min, max, r);
String b = randomString(min, max, r);
int expected = caseSensitive ? a.compareTo(b) : a.compareToIgnoreCase(b);
assertCMP(a, expected, b, caseSensitive);
}
System.out.print("Trials: "+n+" Max length: "+max+" ignoreCase: "+!caseSensitive);
System.out.println(" Time: "+(System.currentTimeMillis()-start));
}
private String randomString(int min, int max, Random r) {
StringBuffer result = new StringBuffer();
int len = min + r.nextInt(max-min);
for(int i=0; i<len; i++) {
result.append(randomChar(r));
}
return result.toString();
}
private char randomChar(Random r) {
// we only match String.compareToIgnoreCase behaviour within this limited range
return (char) (32 + r.nextInt(40));
}
private void assertCMP(String a, int expected, String b, boolean caseSensitive)
throws CoreException
{
char[] acs = a.toCharArray();
char[] bcs = b.toCharArray();
IString aiss = db.newString(a);
IString biss = db.newString(b);
IString aisc = db.newString(acs);
IString bisc = db.newString(bcs);
assertEquals(a.hashCode(), aiss.hashCode());
assertEquals(a.hashCode(), aisc.hashCode());
assertEquals(b.hashCode(), biss.hashCode());
assertEquals(b.hashCode(), bisc.hashCode());
assertEquals(aiss, a);
assertEquals(aisc, a);
assertEquals(biss, b);
assertEquals(bisc, b);
assertSignEquals(expected, aiss.compare(bcs, caseSensitive));
assertSignEquals(expected, aiss.compare(biss, caseSensitive));
assertSignEquals(expected, aiss.compare(bisc, caseSensitive));
assertSignEquals(expected, aiss.compare(b, caseSensitive));
assertSignEquals(expected, aiss.comparePrefix(bcs, caseSensitive));
assertSignEquals(expected, -biss.compare(acs, caseSensitive));
assertSignEquals(expected, -biss.compare(aiss, caseSensitive));
assertSignEquals(expected, -biss.compare(aisc, caseSensitive));
assertSignEquals(expected, -biss.compare(a, caseSensitive));
assertSignEquals(expected, -biss.comparePrefix(acs, caseSensitive));
if (!caseSensitive && expected != 0) {
assertSignEquals(expected, aiss.compareCompatibleWithIgnoreCase(bcs));
assertSignEquals(expected, aiss.compareCompatibleWithIgnoreCase(biss));
assertSignEquals(expected, aiss.compareCompatibleWithIgnoreCase(bisc));
assertSignEquals(expected, -biss.compareCompatibleWithIgnoreCase(acs));
assertSignEquals(expected, -biss.compareCompatibleWithIgnoreCase(aiss));
assertSignEquals(expected, -biss.compareCompatibleWithIgnoreCase(aisc));
}
}
private void assertSignEquals(int a, int b) {
a= a<0 ? -1 : (a>0 ? 1 : 0);
b= b<0 ? -1 : (b>0 ? 1 : 0);
assertEquals(a, b);
}
}