/*
* 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.
*/
using System;
using NUnit.Framework;
using ConcurrentMergeScheduler = Lucene.Net.Index.ConcurrentMergeScheduler;
using Insanity = Lucene.Net.Util.FieldCacheSanityChecker.Insanity;
using FieldCache = Lucene.Net.Search.FieldCache;
using CacheEntry = Lucene.Net.Search.CacheEntry;
namespace Lucene.Net.Util
{
/// Base class for all Lucene unit tests.
///
/// Currently the
/// only added functionality over JUnit's TestCase is
/// asserting that no unhandled exceptions occurred in
/// threads launched by ConcurrentMergeScheduler and asserting sane
/// FieldCache usage athe moment of tearDown.
///
/// If you
/// override either setUp() or
/// tearDown() in your unit test, make sure you
/// call super.setUp() and
/// super.tearDown()
///
///
///
///
[Serializable]
public abstract class LuceneTestCase
{
public static System.IO.FileInfo TEMP_DIR;
static LuceneTestCase()
{
String directory = Paths.TempDirectory;
TEMP_DIR = new System.IO.FileInfo(directory);
}
bool allowDocsOutOfOrder = true;
public LuceneTestCase() : base()
{
}
public LuceneTestCase(System.String name)
{
}
[SetUp]
public virtual void SetUp()
{
ConcurrentMergeScheduler.SetTestMode();
}
/// Forcible purges all cache entries from the FieldCache.
///
/// This method will be called by tearDown to clean up FieldCache.DEFAULT.
/// If a (poorly written) test has some expectation that the FieldCache
/// will persist across test methods (ie: a static IndexReader) this
/// method can be overridden to do nothing.
///
///
///
///
protected internal virtual void PurgeFieldCache(FieldCache fc)
{
fc.PurgeAllCaches();
}
protected internal virtual System.String GetTestLabel()
{
return NUnit.Framework.TestContext.CurrentContext.Test.FullName;
}
[TearDown]
public virtual void TearDown()
{
try
{
// this isn't as useful as calling directly from the scope where the
// index readers are used, because they could be gc'ed just before
// tearDown is called.
// But it's better then nothing.
AssertSaneFieldCaches(GetTestLabel());
if (ConcurrentMergeScheduler.AnyUnhandledExceptions())
{
// Clear the failure so that we don't just keep
// failing subsequent test cases
ConcurrentMergeScheduler.ClearUnhandledExceptions();
Assert.Fail("ConcurrentMergeScheduler hit unhandled exceptions");
}
}
finally
{
PurgeFieldCache(Lucene.Net.Search.FieldCache_Fields.DEFAULT);
}
//base.TearDown(); // {{Aroush-2.9}}
this.seed = null;
}
/// Asserts that FieldCacheSanityChecker does not detect any
/// problems with FieldCache.DEFAULT.
///
/// If any problems are found, they are logged to System.err
/// (allong with the msg) when the Assertion is thrown.
///
/// This method is called by tearDown after every test method,
/// however IndexReaders scoped inside test methods may be garbage
/// collected prior to this method being called, causing errors to
/// be overlooked. Tests are encouraged to keep their IndexReaders
/// scoped at the class level, or to explicitly call this method
/// directly in the same scope as the IndexReader.
///
///
///
///
protected internal virtual void AssertSaneFieldCaches(System.String msg)
{
CacheEntry[] entries = Lucene.Net.Search.FieldCache_Fields.DEFAULT.GetCacheEntries();
Insanity[] insanity = null;
try
{
try
{
insanity = FieldCacheSanityChecker.CheckSanity(entries);
}
catch (System.SystemException e)
{
System.IO.StreamWriter temp_writer;
temp_writer = new System.IO.StreamWriter(System.Console.OpenStandardError(), System.Console.Error.Encoding);
temp_writer.AutoFlush = true;
DumpArray(msg + ": FieldCache", entries, temp_writer);
throw e;
}
Assert.AreEqual(0, insanity.Length, msg + ": Insane FieldCache usage(s) found");
insanity = null;
}
finally
{
// report this in the event of any exception/failure
// if no failure, then insanity will be null anyway
if (null != insanity)
{
System.IO.StreamWriter temp_writer2;
temp_writer2 = new System.IO.StreamWriter(System.Console.OpenStandardError(), System.Console.Error.Encoding);
temp_writer2.AutoFlush = true;
DumpArray(msg + ": Insane FieldCache usage(s)", insanity, temp_writer2);
}
}
}
/// Convinience method for logging an iterator.
/// String logged before/after the items in the iterator
///
/// Each next() is toString()ed and logged on it's own line. If iter is null this is logged differnetly then an empty iterator.
///
/// Stream to log messages to.
///
public static void DumpIterator(System.String label, System.Collections.IEnumerator iter, System.IO.StreamWriter stream)
{
stream.WriteLine("*** BEGIN " + label + " ***");
if (null == iter)
{
stream.WriteLine(" ... NULL ...");
}
else
{
while (iter.MoveNext())
{
stream.WriteLine(iter.Current.ToString());
}
}
stream.WriteLine("*** END " + label + " ***");
}
/// Convinience method for logging an array. Wraps the array in an iterator and delegates
///
///
public static void DumpArray(System.String label, System.Object[] objs, System.IO.StreamWriter stream)
{
System.Collections.IEnumerator iter = (null == objs)?null:new System.Collections.ArrayList(objs).GetEnumerator();
DumpIterator(label, iter, stream);
}
/// Returns a {@link Random} instance for generating random numbers during the test.
/// The random seed is logged during test execution and printed to System.out on any failure
/// for reproducing the test using {@link #NewRandom(long)} with the recorded seed
/// .
///
public virtual System.Random NewRandom()
{
if (this.seed != null)
{
throw new System.SystemException("please call LuceneTestCase.newRandom only once per test");
}
return NewRandom(seedRnd.Next(System.Int32.MinValue, System.Int32.MaxValue));
}
/// Returns a {@link Random} instance for generating random numbers during the test.
/// If an error occurs in the test that is not reproducible, you can use this method to
/// initialize the number generator with the seed that was printed out during the failing test.
///
public virtual System.Random NewRandom(int seed)
{
if (this.seed != null)
{
throw new System.SystemException("please call LuceneTestCase.newRandom only once per test");
}
this.seed = seed;
return new System.Random(seed);
}
// recorded seed
[NonSerialized]
protected internal int? seed = null;
//protected internal bool seed_init = false;
// static members
[NonSerialized]
private static readonly System.Random seedRnd = new System.Random();
#region Java porting shortcuts
protected static void assertEquals(string msg, object obj1, object obj2)
{
Assert.AreEqual(obj1, obj2, msg);
}
protected static void assertEquals(object obj1, object obj2)
{
Assert.AreEqual(obj1, obj2);
}
protected static void assertEquals(double d1, double d2, double delta)
{
Assert.AreEqual(d1, d2, delta);
}
protected static void assertEquals(string msg, double d1, double d2, double delta)
{
Assert.AreEqual(d1, d2, delta, msg);
}
protected static void assertTrue(bool cnd)
{
Assert.IsTrue(cnd);
}
protected static void assertTrue(string msg, bool cnd)
{
Assert.IsTrue(cnd, msg);
}
protected static void assertNotNull(object o)
{
Assert.NotNull(o);
}
protected static void assertNotNull(string msg, object o)
{
Assert.NotNull(o, msg);
}
protected static void assertNull(object o)
{
Assert.Null(o);
}
protected static void assertNull(string msg, object o)
{
Assert.Null(o, msg);
}
#endregion
}
}