/**
 * LOGICAL-PARADOX.ORG b-tree library
 * Copyright (C)2004 satoshi akabane(akabane@logical-paradox.org)
 *
 * Release 1.0
 * ・初期パージョン
 */
package org.logical_paradox.common.btree;

import java.util.Properties;

import org.logical_paradox.common.io.MemoryMappedRandomAccessFile;
import org.logical_paradox.common.io.RandomAccessFileDelegator;

import junit.framework.TestCase;

/**
 * BtreeFileのテスト
 * @author satoshi akabane
 * @version 1.0
 */
public class BtreeFileTest extends TestCase {
	private static final int ORDER = 32;
	private BtreeFactory _factory;

	// プロパティ指定のコンストラクタのテスト
	public void testSetProperty() throws Exception {
		// わざと例外が発生しやすいようなコーディングにしている(checkの一環)
		Properties prop = new Properties();

		// プロパティ必須項目指定なしの場合は例外が発生する
		try {
			BtreeFile btf1 = new BtreeFileMoc(_factory, prop);
			fail("プロパティの内容が不十分なのに例外が発生しなかった");
		} catch(BtreeException e1) {
		}

		// 必要十分項目が埋まっている場合は例外は発生しない
		prop.setProperty(BtreeFile.FILENAME, "btree.idx");
		prop.setProperty(BtreeFile.MAGIC_NUMBER, "ABCD");

		prop.setProperty(BtreeFile.MAJOR_VERSION, "100");
		prop.setProperty(BtreeFile.MINOR_VERSION, "200");
		prop.setProperty(BtreeFile.HEADER_SIZE, "256");
		prop.setProperty(BtreeFile.ORDER, "64");
		prop.setProperty(BtreeFile.KEYSIZE, "32");

		try {
			BtreeFile btf2 = new BtreeFileMoc(_factory, prop);
		} catch(BtreeException e2) {
			fail("プロパティの内容が十分なのに例外が発生した1");
		}

		// 完全指定の場合も例外は発生しない
		prop.setProperty(BtreeFile.ROOT_PAGE, "100");
		prop.setProperty(BtreeFile.MAX_ASSIGNED_PAGE, "500");
		try {
			BtreeFile btf3 = new BtreeFileMoc(_factory, prop);
		} catch(BtreeException e3) {
			fail("プロパティの内容が十分なのに例外が発生した2");
		}

	}
	// ヘッダブロックの書き出しテスト
	public void testflush() throws Exception {
		BtreeFile btf1 = _factory.getFileHandle("1");
		BtreeFile btf2 = _factory.getFileHandle("1");

		// ランダムアクセスファイルのインスタンスを新しく作成してセットする
		RandomAccessFileDelegator file = new MemoryMappedRandomAccessFile();
		btf1._btfile = file;

		byte[] magicno = new byte[4];
		magicno[0] = (byte)'B';
		magicno[1] = (byte)'Y';
		magicno[2] = (byte)'T';
		magicno[3] = (byte)'E';
		btf1.setMagicNumber(magicno);
		btf1.setVersion(5,2);

		// 書き込み
		btf1.flush();
		file.seek(0);

		// btf1で書き込んだファイルのインスタンスをbtf2へセット
		btf2._btfile = file;
		// ロード
		btf2.reload();

		assertEquals("マジックナンバーのセットに問題がある", "BYTE", btf2.getMagicNumber());
		assertEquals("メジャーバージョンのセットに問題がある", 5, btf2.getMajorVersion());
		assertEquals("マイナーバージョンのセットに問題がある", 2, btf2.getMinorVersion());
	}
	// マジックナンバーの設定テスト
	public void testSetMagicNumber() throws Exception {
		BtreeFile btf1 = _factory.getFileHandle("1");

		byte[] magicno = new byte[4];
		magicno[0] = (byte)'B';
		magicno[1] = (byte)'Y';
		magicno[2] = (byte)'T';
		magicno[3] = (byte)'E';

		// 正常系
		btf1.setMagicNumber(magicno);

		// 0である
		byte[] magicno4 = new byte[0];
		try {
			btf1.setMagicNumber(magicno4);
			fail("バイト配列0なのに例外が発生しなかった");
		} catch(IllegalArgumentException i3) {
		}
		// 足りない
		byte[] magicno2 = new byte[3];
		try {
			btf1.setMagicNumber(magicno2);
			fail("バイト配列が少ないのに例外が発生しなかった");
		} catch(IllegalArgumentException i1) {
		}

		// 多い
		byte[] magicno3 = new byte[5];
		try  {
			btf1.setMagicNumber(magicno3);
			fail("バイト配列が多いのに例外が発生しなかった");
		} catch(IllegalArgumentException i2) {
		}
	}
	// ルートページ番号の変更テスト
	public void testReplaceRootPageNo() throws Exception {
		BtreeFile btf1 = _factory.getFileHandle("1");
		BtreeFile btf2 = _factory.getFileHandle("1");

		// ランダムアクセスファイルのインスタンスを新しく作成してセットする
		RandomAccessFileDelegator file = new MemoryMappedRandomAccessFile();
		btf1._btfile = file;

		// ルートページ番号を設定してコミットする
		btf1.replaceRootPageNo(12393);
		btf1.flush();

		// 別のBtreeFileのインスタンスでさっきのファイルを読み取って比較する
		btf2._btfile = file;
		btf2.reload();

		assertEquals("ルートページ番号が正しく読み書きできなかった", 12393, btf2.getRootPageNo());
	}
	// ヘッダブロックのサイズが標準値でない場合のフラッシュのテスト
	public void testFlush() throws Exception {
		BtreeFile btf1 = _factory.getFileHandle("1");
		btf1._btfile = new MemoryMappedRandomAccessFile();
		btf1._sizeOfHeader = 256;
		btf1.flush();
	}
	// 新規ページ作成のテスト
	public void testNewPage() throws Exception {
		BtreeFile btf1 = _factory.getFileHandle("1");
		BtreeFile btf2 = _factory.getFileHandle("1");

		// ランダムアクセスファイルのインスタンスを新しく作成してセットする
		RandomAccessFileDelegator file = new MemoryMappedRandomAccessFile();
		btf1._btfile = file;

		btf1._maxAssignedPageNo = 0;
		BtreePage btp = btf1.newPage();
		btf1.flush();
		assertEquals("最大ページ番号が更新されない", 1, btf1.getMaxAssignedPageNo());
		assertNotNull("ページが作成されなかった", btp);

		// btf2でファイルをロードする
		btf2._btfile = file;
		btf2.reload();
		assertEquals("ファイル上の最大ページが更新されていない", 1, btf2.getMaxAssignedPageNo());
	}
	// 指定された番号のページを取得するテスト
	public void testGetPage() throws Exception {
		BtreeFile btf1 = _factory.getFileHandle("1");
		// ランダムアクセスファイルのインスタンスを新しく作成してセットする
		RandomAccessFileDelegator file = new MemoryMappedRandomAccessFile();
//		RandomAccessFileDelegator file = new CommonRandomAccessFileDelegator("a.out", "rw");

		btf1._btfile = file;

		// 新しいページを作成してキーを挿入する
		BtreePage btp1 = btf1.newPage();
		btp1.insert(_factory.newKey("あああ"), 102);
		btp1.insert(_factory.newKey("いいい"), -1);
		btf1.replacePage(btp1);
		btf1.flush();

		BtreePage btp2 = btf1.getPage(btp1.getPageNo());

		// ダンプの出力
		/*
		CharacterEncoder encoder = new HexDumpEncoder();
		encoder.encode(btp2.getByteStream(), System.out);
		*/

		assertEquals(btp1.getPageNo(), btp2.getPageNo());
		assertEquals(btp2.find("あああ"), _factory.newKey("あああ"));
		assertEquals(btp2.find("いいい"), _factory.newKey("いいい"));
		assertEquals(-1,btp2.leftChildOf(_factory.newKey("あああ")));
		assertEquals(102,btp2.rightChildOf(_factory.newKey("あああ")));
		assertEquals(102,btp2.leftChildOf(_factory.newKey("いいい")));
		assertEquals(-1,btp2.rightChildOf(_factory.newKey("いいい")));
	}
	// ページ無効化のテスト
	public void testDisablePage() throws Exception {
		BtreeFile btf1 = _factory.getFileHandle("1");
		RandomAccessFileDelegator file = new MemoryMappedRandomAccessFile();
		btf1._btfile = file;

		// 新しいページを追加する
		BtreePage btp1 = btf1.newPage();
		btp1.insert(_factory.newKey("あああ"), 102);
		btp1.insert(_factory.newKey("いいい"), -1);
		btf1.replacePage(btp1);
		btf1.flush();

		// ページを無効化する
		long pagenoOfBtp1 = btp1.getPageNo();
		btp1.setEnabled(false);
		btf1.replacePage(btp1);

		// ページを復元して比較
		BtreePage btp2 = btf1.getPage(pagenoOfBtp1);
		assertEquals(false, btp2.isEnabled());
	}
	// 無効化ページの再利用処理のテスト
	public void testRecycleDisabledPage() throws Exception {
		BtreeFile btf1 = _factory.getFileHandle("1");
		RandomAccessFileDelegator file = new MemoryMappedRandomAccessFile();
		btf1._btfile = file;

		//新しいページを追加する
		BtreePage btp1 = btf1.newPage();
		btp1.insert(_factory.newKey("あああ"), 102);
		btp1.insert(_factory.newKey("いいい"), -1);
		btf1.replacePage(btp1);
		btf1.flush();
		long pagenoOfBtp1 = btp1.getPageNo();

		// ページを無効化する処理
		btp1.setEnabled(false);
		btf1.replacePage(btp1);
		btf1.flush();

		// 新しいページを追加する
		BtreePage btp2 = btf1.newPage();

		// ページ番号の再利用を確認する
		assertEquals(true, btp2.isEnabled());
		assertEquals(pagenoOfBtp1, btp2.getPageNo());
		// 削除されたページのページ番号が-1になっていることを確認する
		assertEquals(-1,btp1.getPageNo());
	}
	// セットアップ
	public void setUp() throws Exception {
		_factory = BtreeFactory.getInstance("org.logical_paradox.common.btree.BtreeFactoryTestImpl");
	}
}
