1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2009 Oracle.  All rights reserved.
5 *
6 */
7using System;
8using System.Collections.Generic;
9using System.IO;
10using System.Text;
11using System.Xml;
12using NUnit.Framework;
13using BerkeleyDB;
14
15namespace CsharpAPITest
16{
17	[TestFixture]
18	public class QueueDatabaseTest : DatabaseTest
19	{
20		private string testFixtureHome;
21		private string testFixtureName;
22		private string testName;
23		private string testHome;
24
25		[TestFixtureSetUp]
26		public void RunBeforeTests()
27		{
28			testFixtureName = "QueueDatabaseTest";
29			testFixtureHome = "./TestOut/" + testFixtureName;
30
31			Configuration.ClearDir(testFixtureHome);
32		}
33
34		[Test]
35		public void TestAppendWithoutTxn()
36		{
37			testName = "TestAppendWithoutTxn";
38			testHome = testFixtureHome + "/" + testName;
39			string queueDBFileName = testHome + "/" + testName + ".db";
40
41			Configuration.ClearDir(testHome);
42
43			QueueDatabaseConfig queueConfig = new QueueDatabaseConfig();
44			queueConfig.Creation = CreatePolicy.ALWAYS;
45			queueConfig.Length = 1000;
46			QueueDatabase queueDB = QueueDatabase.Open(
47			    queueDBFileName, queueConfig);
48
49			byte[] byteArr = new byte[4];
50			byteArr = BitConverter.GetBytes((int)1);
51			DatabaseEntry data = new DatabaseEntry(byteArr);
52			uint recno = queueDB.Append(data);
53
54			// Confirm that the recno is larger than 0.
55			Assert.AreNotEqual(0, recno);
56
57			// Confirm that the record exists in the database.
58			byteArr = BitConverter.GetBytes(recno);
59			DatabaseEntry key = new DatabaseEntry();
60			key.Data = byteArr;
61			Assert.IsTrue(queueDB.Exists(key));
62			queueDB.Close();
63		}
64
65		[Test]
66		public void TestAppendWithTxn()
67		{
68			testName = "TestAppendWithTxn";
69			testHome = testFixtureHome + "/" + testName;
70			string queueDBFileName = testHome + "/" + testName + ".db";
71			string queueDBName =
72			    Path.GetFileNameWithoutExtension(queueDBFileName);
73
74			Configuration.ClearDir(testHome);
75
76			DatabaseEnvironmentConfig envConfig =
77			    new DatabaseEnvironmentConfig();
78			envConfig.Create = true;
79			envConfig.UseTxns = true;
80			envConfig.UseMPool = true;
81
82			DatabaseEnvironment env = DatabaseEnvironment.Open(
83			    testHome, envConfig);
84			Transaction txn = env.BeginTransaction();
85
86			QueueDatabaseConfig queueConfig = new QueueDatabaseConfig();
87			queueConfig.Creation = CreatePolicy.ALWAYS;
88			queueConfig.Env = env;
89			queueConfig.Length = 1000;
90
91			/* If environmnet home is set, the file name in
92			 * Open() is the relative path.
93			 */
94			QueueDatabase queueDB = QueueDatabase.Open(
95			    queueDBName, queueConfig, txn);
96			DatabaseEntry data;
97			int i = 1000;
98			try
99			{
100				while (i > 0)
101				{
102					data = new DatabaseEntry(
103					    BitConverter.GetBytes(i));
104					queueDB.Append(data, txn);
105					i--;
106				}
107				txn.Commit();
108			}
109			catch
110			{
111				txn.Abort();
112			}
113			finally
114			{
115				queueDB.Close();
116				env.Close();
117			}
118
119		}
120
121		[Test, ExpectedException(typeof(ExpectedTestException))]
122		public void TestConsumeWithTxn()
123		{
124			testName = "TestConsumeWithTxn";
125			testHome = testFixtureHome + "/" + testName;
126			string queueDBFileName = testHome + "/" + testName + ".db";
127			string queueDBName = Path.GetFileName(queueDBFileName);
128
129			Configuration.ClearDir(testHome);
130
131			DatabaseEnvironmentConfig envConfig =
132			    new DatabaseEnvironmentConfig();
133			envConfig.Create = true;
134			envConfig.UseTxns = true;
135			envConfig.UseMPool = true;
136
137			DatabaseEnvironment env = DatabaseEnvironment.Open(
138			    testHome, envConfig);
139			Transaction txn = env.BeginTransaction();
140
141			QueueDatabaseConfig queueConfig =
142			    new QueueDatabaseConfig();
143			queueConfig.Creation = CreatePolicy.ALWAYS;
144			queueConfig.Env = env;
145			queueConfig.Length = 1000;
146			QueueDatabase queueDB = QueueDatabase.Open(
147			    queueDBName, queueConfig, txn);
148
149			int i = 1;
150			DatabaseEntry data;
151			DatabaseEntry getData = new DatabaseEntry();
152			while (i <= 10)
153			{
154				data = new DatabaseEntry(
155				    ASCIIEncoding.ASCII.GetBytes(i.ToString()));
156				queueDB.Append(data, txn);
157				if (i == 5)
158				{
159					getData = data;
160				}
161				i++;
162			}
163
164			KeyValuePair<uint, DatabaseEntry> pair = queueDB.Consume(false, txn);
165
166			queueDB.Close();
167			txn.Commit();
168			env.Close();
169
170			Database db = Database.Open(queueDBFileName,
171			    new QueueDatabaseConfig());
172			try
173			{
174				DatabaseEntry key =
175					new DatabaseEntry(BitConverter.GetBytes(pair.Key));
176				db.Get(key);
177			}
178			catch (NotFoundException)
179			{
180				throw new ExpectedTestException();
181			}
182			finally
183			{
184				db.Close();
185			}
186		}
187
188		[Test, ExpectedException(typeof(ExpectedTestException))]
189		public void TestConsumeWithoutTxn()
190		{
191			testName = "TestConsumeWithoutTxn";
192			testHome = testFixtureHome + "/" + testName;
193			string queueDBFileName = testHome + "/" +
194			    testName + ".db";
195
196			Configuration.ClearDir(testHome);
197
198			QueueDatabaseConfig queueConfig =
199			    new QueueDatabaseConfig();
200			queueConfig.Creation = CreatePolicy.ALWAYS;
201			queueConfig.ErrorPrefix = testName;
202			queueConfig.Length = 1000;
203
204			QueueDatabase queueDB = QueueDatabase.Open(
205			    queueDBFileName, queueConfig);
206			DatabaseEntry data = new DatabaseEntry(
207			    ASCIIEncoding.ASCII.GetBytes("data"));
208			queueDB.Append(data);
209
210			DatabaseEntry consumeData = new DatabaseEntry();
211			KeyValuePair<uint, DatabaseEntry> pair = queueDB.Consume(false);
212			try
213			{
214				DatabaseEntry key =
215					new DatabaseEntry(BitConverter.GetBytes(pair.Key));
216				queueDB.Get(key);
217			}
218			catch (NotFoundException)
219			{
220				throw new ExpectedTestException();
221			}
222			finally
223			{
224				queueDB.Close();
225			}
226		}
227
228		public void TestCursor()
229		{
230			testName = "TestCursor";
231			testHome = testFixtureHome + "/" + testName;
232
233			Configuration.ClearDir(testHome);
234
235			GetCursur(testHome + "/" + testName + ".db", false);
236		}
237
238		public void TestCursorWithConfig()
239		{
240			testName = "TestCursorWithConfig";
241			testHome = testFixtureHome + "/" + testName;
242
243			Configuration.ClearDir(testHome);
244
245			GetCursur(testHome + "/" + testName + ".db", true);
246		}
247
248		public void GetCursur(string dbFileName, bool ifConfig)
249		{
250			QueueDatabaseConfig dbConfig = new QueueDatabaseConfig();
251			dbConfig.Creation = CreatePolicy.IF_NEEDED;
252			dbConfig.Length = 100;
253			QueueDatabase db = QueueDatabase.Open(dbFileName, dbConfig);
254			Cursor cursor;
255			if (ifConfig == false)
256				cursor = db.Cursor();
257			else
258				cursor = db.Cursor(new CursorConfig());
259			cursor.Close();
260			db.Close();
261		}
262
263        //[Test]
264        //public void TestDupCompare()
265        //{
266        //    testName = "TestDupCompare";
267        //    testHome = testFixtureHome + "/" + testName;
268        //    string dbFileName = testHome + "/" + testName + ".db";
269
270        //    Configuration.ClearDir(testHome);
271
272        //    QueueDatabaseConfig dbConfig = new QueueDatabaseConfig();
273        //    dbConfig.Creation = CreatePolicy.IF_NEEDED;
274        //    dbConfig.DuplicateCompare = new EntryComparisonDelegate(dbIntCompare);
275        //    dbConfig.Length = 10;
276        //    dbConfig.PageSize = 40860;
277        //    try
278        //    {
279        //        QueueDatabase db = QueueDatabase.Open(dbFileName, dbConfig);
280        //        int ret = db.DupCompare(new DatabaseEntry(BitConverter.GetBytes(255)),
281        //            new DatabaseEntry(BitConverter.GetBytes(257)));
282        //        Assert.Greater(0, ret);
283        //        db.Close();
284        //    }
285        //    catch (DatabaseException e)
286        //    {
287        //        Console.WriteLine(e.Message);
288        //    }
289        //}
290
291		private int dbIntCompare(DatabaseEntry dbt1,
292		    DatabaseEntry dbt2)
293		{
294			int a, b;
295			a = BitConverter.ToInt16(dbt1.Data, 0);
296			b = BitConverter.ToInt16(dbt2.Data, 0);
297			return a - b;
298		}
299
300		[Test, ExpectedException(typeof(ExpectedTestException))]
301		public void TestKeyEmptyException()
302		{
303			testName = "TestKeyEmptyException";
304			testHome = testFixtureHome + "/" + testName;
305
306			Configuration.ClearDir(testHome);
307
308			DatabaseEnvironmentConfig envConfig =
309			    new DatabaseEnvironmentConfig();
310			envConfig.Create = true;
311			envConfig.UseLocking = true;
312			envConfig.UseLogging = true;
313			envConfig.UseMPool = true;
314			envConfig.UseTxns = true;
315			DatabaseEnvironment env = DatabaseEnvironment.Open(
316			    testHome, envConfig);
317
318			QueueDatabase db;
319			try
320			{
321				Transaction openTxn = env.BeginTransaction();
322				try
323				{
324					QueueDatabaseConfig queueConfig =
325					    new QueueDatabaseConfig();
326					queueConfig.Creation = CreatePolicy.IF_NEEDED;
327					queueConfig.Length = 10;
328					queueConfig.Env = env;
329					db = QueueDatabase.Open(testName + ".db",
330					    queueConfig, openTxn);
331					openTxn.Commit();
332				}
333				catch (DatabaseException e)
334				{
335					openTxn.Abort();
336					throw e;
337				}
338
339				Transaction cursorTxn = env.BeginTransaction();
340				Cursor cursor;
341				try
342				{
343					/*
344					 * Put a record into queue database with
345					 * cursor and abort the operation.
346					 */
347					cursor = db.Cursor(cursorTxn);
348					KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
349					pair = new KeyValuePair<DatabaseEntry, DatabaseEntry>(
350					    new DatabaseEntry(BitConverter.GetBytes((int)10)),
351					    new DatabaseEntry(ASCIIEncoding.ASCII.GetBytes("data")));
352					cursor.Add(pair);
353					cursor.Close();
354					cursorTxn.Abort();
355				}
356				catch (DatabaseException e)
357				{
358					cursorTxn.Abort();
359					db.Close();
360					throw e;
361				}
362
363				Transaction delTxn = env.BeginTransaction();
364				try
365				{
366					/*
367					 * The put operation is aborted in the queue
368					 * database so querying if the record still exists
369					 * throws KeyEmptyException.
370					 */
371					db.Exists(new DatabaseEntry(
372					    BitConverter.GetBytes((int)10)), delTxn);
373					delTxn.Commit();
374				}
375				catch (DatabaseException e)
376				{
377					delTxn.Abort();
378					throw e;
379				}
380				finally
381				{
382					db.Close();
383				}
384			}
385			catch (KeyEmptyException)
386			{
387				throw new ExpectedTestException();
388			}
389			finally
390			{
391				env.Close();
392			}
393		}
394
395		[Test]
396		public void TestOpenExistingQueueDB()
397		{
398			testName = "TestOpenExistingQueueDB";
399			testHome = testFixtureHome + "/" + testName;
400			string queueDBFileName = testHome + "/" + testName + ".db";
401
402			Configuration.ClearDir(testHome);
403
404			QueueDatabaseConfig queueConfig = new QueueDatabaseConfig();
405			queueConfig.Creation = CreatePolicy.ALWAYS;
406			QueueDatabase queueDB = QueueDatabase.Open(
407			    queueDBFileName, queueConfig);
408			queueDB.Close();
409
410			DatabaseConfig dbConfig = new DatabaseConfig();
411			Database db = Database.Open(queueDBFileName, dbConfig);
412			Assert.AreEqual(db.Type, DatabaseType.QUEUE);
413			db.Close();
414		}
415
416		[Test]
417		public void TestOpenNewQueueDB()
418		{
419			testName = "TestOpenNewQueueDB";
420			testHome = testFixtureHome + "/" + testName;
421			string queueDBFileName = testHome + "/" + testName + ".db";
422
423			Configuration.ClearDir(testHome);
424
425			// Configure all fields/properties in queue database.
426			XmlElement xmlElem = Configuration.TestSetUp(
427			    testFixtureName, testName);
428			QueueDatabaseConfig queueConfig = new QueueDatabaseConfig();
429			QueueDatabaseConfigTest.Config(xmlElem, ref queueConfig, true);
430			queueConfig.Feedback = new DatabaseFeedbackDelegate(DbFeedback);
431
432			// Open the queue database with above configuration.
433			QueueDatabase queueDB = QueueDatabase.Open(
434			    queueDBFileName, queueConfig);
435
436			// Check the fields/properties in opened queue database.
437			Confirm(xmlElem, queueDB, true);
438
439			queueDB.Close();
440		}
441
442		private void DbFeedback(DatabaseFeedbackEvent opcode, int percent)
443		{
444			if (opcode == DatabaseFeedbackEvent.UPGRADE)
445				Console.WriteLine("Update for %d%", percent);
446
447			if (opcode == DatabaseFeedbackEvent.VERIFY)
448				Console.WriteLine("Vertify for %d", percent);
449		}
450
451		[Test, ExpectedException(typeof(NotFoundException))]
452		public void TestPutToQueue()
453		{
454			KeyValuePair<DatabaseEntry, DatabaseEntry> pair;
455
456			testName = "TestPutQueue";
457			testHome = testFixtureHome + "/" + testName;
458			string queueDBFileName = testHome + "/" +
459			    testName + ".db";
460
461			Configuration.ClearDir(testHome);
462
463			QueueDatabaseConfig queueConfig =
464			    new QueueDatabaseConfig();
465			queueConfig.Length = 512;
466			queueConfig.Creation = CreatePolicy.ALWAYS;
467			using (QueueDatabase queueDB = QueueDatabase.Open(
468			    queueDBFileName, queueConfig))
469			{
470				DatabaseEntry key = new DatabaseEntry();
471				key.Data = BitConverter.GetBytes((int)100);
472				DatabaseEntry data = new DatabaseEntry(
473				    BitConverter.GetBytes((int)1));
474				queueDB.Put(key, data);
475				pair = queueDB.GetBoth(key, data);
476			}
477		}
478
479		[Test]
480		public void TestStats()
481		{
482			testName = "TestStats";
483			testHome = testFixtureHome + "/" + testName;
484			string dbFileName = testHome + "/" +
485			    testName + ".db";
486			Configuration.ClearDir(testHome);
487
488			QueueDatabaseConfig dbConfig =
489			    new QueueDatabaseConfig();
490			ConfigCase1(dbConfig);
491			QueueDatabase db = QueueDatabase.Open(dbFileName, dbConfig);
492
493			QueueStats stats = db.Stats();
494			ConfirmStatsPart1Case1(stats);
495			db.PrintFastStats(true);
496
497			// Put 500 records into the database.
498			PutRecordCase1(db, null);
499
500			stats = db.Stats();
501			ConfirmStatsPart2Case1(stats);
502			db.PrintFastStats();
503
504			db.Close();
505		}
506
507		[Test]
508		public void TestStatsInTxn()
509		{
510			testName = "TestStatsInTxn";
511			testHome = testFixtureHome + "/" + testName;
512			Configuration.ClearDir(testHome);
513
514			StatsInTxn(testHome, testName, false);
515		}
516
517		[Test]
518		public void TestStatsWithIsolation()
519		{
520			testName = "TestStatsWithIsolation";
521			testHome = testFixtureHome + "/" + testName;
522			Configuration.ClearDir(testHome);
523
524			StatsInTxn(testHome, testName, true);
525		}
526
527		public void StatsInTxn(string home, string name, bool ifIsolation)
528		{
529			DatabaseEnvironmentConfig envConfig =
530			    new DatabaseEnvironmentConfig();
531			EnvConfigCase1(envConfig);
532			DatabaseEnvironment env = DatabaseEnvironment.Open(
533			    home, envConfig);
534
535			Transaction openTxn = env.BeginTransaction();
536			QueueDatabaseConfig dbConfig =
537			    new QueueDatabaseConfig();
538			ConfigCase1(dbConfig);
539			dbConfig.Env = env;
540			QueueDatabase db = QueueDatabase.Open(name + ".db",
541			    dbConfig, openTxn);
542			openTxn.Commit();
543
544			Transaction statsTxn = env.BeginTransaction();
545			QueueStats stats;
546			if (ifIsolation == false)
547				stats = db.Stats(statsTxn);
548			else
549				stats = db.Stats(statsTxn, Isolation.DEGREE_ONE);
550
551			ConfirmStatsPart1Case1(stats);
552			db.PrintStats(true);
553
554			// Put 500 records into the database.
555			PutRecordCase1(db, statsTxn);
556
557			if (ifIsolation == false)
558				stats = db.Stats(statsTxn);
559			else
560				stats = db.Stats(statsTxn, Isolation.DEGREE_TWO);
561			ConfirmStatsPart2Case1(stats);
562			db.PrintStats();
563
564			statsTxn.Commit();
565			db.Close();
566			env.Close();
567		}
568
569		public void EnvConfigCase1(DatabaseEnvironmentConfig cfg)
570		{
571			cfg.Create = true;
572			cfg.UseTxns = true;
573			cfg.UseMPool = true;
574			cfg.UseLogging = true;
575		}
576
577		public void ConfigCase1(QueueDatabaseConfig dbConfig)
578		{
579			dbConfig.Creation = CreatePolicy.IF_NEEDED;
580			dbConfig.PageSize = 4096;
581			dbConfig.ExtentSize = 1024;
582			dbConfig.Length = 4000;
583			dbConfig.PadByte = 32;
584		}
585
586		public void PutRecordCase1(QueueDatabase db, Transaction txn)
587		{
588			byte[] bigArray = new byte[4000];
589			for (int i = 1; i <= 100; i++)
590			{
591				if (txn == null)
592					db.Put(new DatabaseEntry(BitConverter.GetBytes(i)),
593					    new DatabaseEntry(BitConverter.GetBytes(i)));
594				else
595					db.Put(new DatabaseEntry(BitConverter.GetBytes(i)),
596					    new DatabaseEntry(BitConverter.GetBytes(i)), txn);
597			}
598			DatabaseEntry key = new DatabaseEntry(BitConverter.GetBytes((int)100));
599			for (int i = 100; i <= 500; i++)
600			{
601				if (txn == null)
602					db.Put(key, new DatabaseEntry(bigArray));
603				else
604					db.Put(key, new DatabaseEntry(bigArray), txn);
605			}
606		}
607
608		public void ConfirmStatsPart1Case1(QueueStats stats)
609		{
610			Assert.AreEqual(1, stats.FirstRecordNumber);
611			Assert.AreNotEqual(0, stats.MagicNumber);
612			Assert.AreEqual(1, stats.NextRecordNumber);
613			Assert.AreEqual(4096, stats.PageSize);
614			Assert.AreEqual(1024, stats.PagesPerExtent);
615			Assert.AreEqual(4000, stats.RecordLength);
616			Assert.AreEqual(32, stats.RecordPadByte);
617			Assert.AreEqual(4, stats.Version);
618		}
619
620		public void ConfirmStatsPart2Case1(QueueStats stats)
621		{
622			Assert.AreNotEqual(0, stats.DataPages);
623			Assert.AreEqual(0, stats.DataPagesBytesFree);
624			Assert.AreEqual(0, stats.MetadataFlags);
625			Assert.AreEqual(100, stats.nData);
626			Assert.AreEqual(100, stats.nKeys);
627		}
628
629		public static void Confirm(XmlElement xmlElem,
630		    QueueDatabase queueDB, bool compulsory)
631		{
632			DatabaseTest.Confirm(xmlElem, queueDB, compulsory);
633
634			// Confirm queue database specific field/property
635			Configuration.ConfirmUint(xmlElem, "ExtentSize",
636			    queueDB.ExtentSize, compulsory);
637			Configuration.ConfirmBool(xmlElem, "ConsumeInOrder",
638			    queueDB.InOrder, compulsory);
639			Configuration.ConfirmInt(xmlElem, "PadByte",
640			    queueDB.PadByte, compulsory);
641			Assert.AreEqual(DatabaseType.QUEUE, queueDB.Type);
642			string type = queueDB.Type.ToString();
643			Assert.IsNotNull(type);
644		}
645	}
646}
647