From d4ca8b446886bf1abce8d9175618361f0454924c Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Thu, 11 Nov 2021 19:03:42 +0700 Subject: [PATCH 01/28] Lucene.Net.TestFramework.Util.LuceneTestCase: Added message to the test result on how to set a fixed random seed to reproduce the test result (pass or fail). --- src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs index 33f0b377e8..a0ababf182 100644 --- a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs +++ b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs @@ -1033,6 +1033,19 @@ public virtual void TearDown() /* LUCENENET TODO: Not sure how to convert these ParentChainCallRule.TeardownCalled = true; */ +#if TESTFRAMEWORK_NUNIT + TestResult result = TestExecutionContext.CurrentContext.CurrentResult; + string message = result.Message + $"\n\nTo reproduce this test result, apply the [RandomSeed({Randomizer.InitialSeed})]" + + $" attribute to the test class or use the following .runsettings file.\n\n" + + $"\n" + + $" \n" + + $" \n" + + $" \n" + + $"\n\nSee the .runsettings documentation at: https://docs.microsoft.com/en-us/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file."; + + string stackTrace = result.StackTrace; + result.SetResult(result.ResultState, message, stackTrace); +#endif } #if TESTFRAMEWORK_MSTEST From fa7c735342365c5eede8b1d9da9fc75e5bfc490c Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Thu, 11 Nov 2021 20:06:44 +0700 Subject: [PATCH 02/28] Lucene.Net.TestFramework.Util: Added customized TestFixtureAttribute, and NUnitTestFixtureBuilder along with RandomSeedAttribute and LuceneRandomSeedInitializer to fix the broken randomized seed functionality so we can repeat test runs reliably. --- .../Util/LuceneRandomSeedInitializer.cs | 58 +++ .../LuceneTestCase.RandomSeedAttribute.cs | 47 ++ .../LuceneTestCase.TestFixtureAttribute.cs | 265 ++++++++++++ .../Support/Util/NUnitTestFixtureBuilder.cs | 409 ++++++++++++++++++ .../Util/LuceneTestCase.cs | 16 +- 5 files changed, 790 insertions(+), 5 deletions(-) create mode 100644 src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs create mode 100644 src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.RandomSeedAttribute.cs create mode 100644 src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.TestFixtureAttribute.cs create mode 100644 src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs diff --git a/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs b/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs new file mode 100644 index 0000000000..2a22af42ac --- /dev/null +++ b/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs @@ -0,0 +1,58 @@ +using NUnit.Framework.Internal; +using System; +using System.Linq; + +namespace Lucene.Net.Util +{ + /* + * 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. + */ + + internal class LuceneRandomSeedInitializer + { + #region Messages + + const string RANDOM_SEED_PARAMS_MSG = + "RandomSeed parameter must be a valid int value or the word 'random'."; + + #endregion + + public void InitializeRandomSeed(TestFixture fixture) + { + if (fixture.TypeInfo.IsDefined(inherit: true)) + { + var randomSeedAttribute = fixture.TypeInfo.GetCustomAttributes(inherit: true).First(); + Randomizer.InitialSeed = randomSeedAttribute.RandomSeed; + } + // HACK: NUnit3TestAdapter seems to be supplying the seed from nunit_random_seed.tmp whether + // or not there is a RandomSeed set by the user. This is a workaorund until that is fixed. + else if (NUnit.Framework.TestContext.Parameters.Exists("RandomSeed")) + { + if (NUnit.Framework.TestContext.Parameters["RandomSeed"].Equals("random", StringComparison.OrdinalIgnoreCase)) + Randomizer.InitialSeed = new Random().Next(); + else if (int.TryParse(NUnit.Framework.TestContext.Parameters["RandomSeed"], out int initialSeed)) + Randomizer.InitialSeed = initialSeed; + else + fixture.MakeInvalid(RANDOM_SEED_PARAMS_MSG); + } + else + { + // For now, ignore anything NUnit3TestAdapter does, because it is messing up repeatable runs. + Randomizer.InitialSeed = SystemProperties.GetPropertyAsInt32("tests:seed", new Random().Next()); + } + } + } +} diff --git a/src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.RandomSeedAttribute.cs b/src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.RandomSeedAttribute.cs new file mode 100644 index 0000000000..25cd045967 --- /dev/null +++ b/src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.RandomSeedAttribute.cs @@ -0,0 +1,47 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Lucene.Net.Util +{ + /* + * 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. + */ + + public abstract partial class LuceneTestCase + { + /// + /// Specifies a random seed to use when running tests. This allows specific test conditions to be repeated for debugging purposes. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)] + [SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "API looks better with this nested.")] + public sealed class RandomSeedAttribute : System.Attribute + { + /// + /// Construct a with a specific random seed value. + /// + /// + public RandomSeedAttribute(int randomSeed) + { + RandomSeed = randomSeed; + } + + /// + /// The random seed value. + /// + public int RandomSeed { get; private set; } + } + } +} diff --git a/src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.TestFixtureAttribute.cs b/src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.TestFixtureAttribute.cs new file mode 100644 index 0000000000..988b7d035d --- /dev/null +++ b/src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.TestFixtureAttribute.cs @@ -0,0 +1,265 @@ +using Lucene.Net.Support; +using NUnit.Framework; +using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Lucene.Net.Util +{ + #region Copyright (c) 2021 Charlie Poole, Rob Prouse + + // Copyright (c) 2021 Charlie Poole, Rob Prouse + // + // Permission is hereby granted, free of charge, to any person obtaining a copy + // of this software and associated documentation files (the "Software"), to deal + // in the Software without restriction, including without limitation the rights + // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + // copies of the Software, and to permit persons to whom the Software is + // furnished to do so, subject to the following conditions: + // + // The above copyright notice and this permission notice shall be included in + // all copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + // THE SOFTWARE. + + #endregion + + public abstract partial class LuceneTestCase + { + /// + /// Marks the class as a TestFixture. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] + public class TestFixtureAttribute : NUnitAttribute, IFixtureBuilder2, ITestFixtureData + { + private readonly NUnitTestFixtureBuilder _builder = new NUnitTestFixtureBuilder(); + + #region Constructors + + /// + /// Default constructor + /// + public TestFixtureAttribute() : this(new object[0]) { } + + /// + /// Construct with a object[] representing a set of arguments. + /// The arguments may later be separated into type arguments and constructor arguments. + /// + /// + public TestFixtureAttribute(params object[] arguments) + { + RunState = RunState.Runnable; + Arguments = arguments ?? new object[] { null }; + TypeArgs = new Type[0]; + Properties = new PropertyBag(); + } + + #endregion + + #region ITestData Members + + /// + /// Gets or sets the name of the test. + /// + /// The name of the test. + public string TestName { get; set; } + + /// + /// Gets or sets the RunState of this test fixture. + /// + public RunState RunState { get; private set; } + + /// + /// The arguments originally provided to the attribute + /// + [WritableArray] + public object[] Arguments { get; } + + /// + /// Properties pertaining to this fixture + /// + public IPropertyBag Properties { get; } + + #endregion + + #region ITestFixtureData Members + + /// + /// Get or set the type arguments. If not set + /// explicitly, any leading arguments that are + /// Types are taken as type arguments. + /// + [WritableArray] + public Type[] TypeArgs { get; set; } + + #endregion + + #region Other Properties + + /// + /// Descriptive text for this fixture + /// + public string Description + { + get { return Properties.Get(PropertyNames.Description) as string; } + set { Properties.Set(PropertyNames.Description, value); } + } + + /// + /// The author of this fixture + /// + public string Author + { + get { return Properties.Get(PropertyNames.Author) as string; } + set { Properties.Set(PropertyNames.Author, value); } + } + + /// + /// The type that this fixture is testing + /// + public Type TestOf + { + get { return _testOf; } + set + { + _testOf = value; + Properties.Set(PropertyNames.TestOf, value.FullName); + } + } + private Type _testOf; + + /// + /// Gets or sets the ignore reason. May set RunState as a side effect. + /// + /// The ignore reason. + public string Ignore + { + get { return IgnoreReason; } + set { IgnoreReason = value; } + } + + /// + /// Gets or sets the reason for not running the fixture. + /// + /// The reason. + public string Reason + { + get { return this.Properties.Get(PropertyNames.SkipReason) as string; } + set { this.Properties.Set(PropertyNames.SkipReason, value); } + } + + /// + /// Gets or sets the ignore reason. When set to a non-null + /// non-empty value, the test is marked as ignored. + /// + /// The ignore reason. + public string IgnoreReason + { + get { return Reason; } + set + { + RunState = RunState.Ignored; + Reason = value; + } + } + + /// + /// Gets or sets a value indicating whether this is explicit. + /// + /// + /// true if explicit; otherwise, false. + /// + public bool Explicit + { + get { return RunState == RunState.Explicit; } + set { RunState = value ? RunState.Explicit : RunState.Runnable; } + } + + /// + /// Gets and sets the category for this fixture. + /// May be a comma-separated list of categories. + /// + public string Category + { + get + { + //return Properties.Get(PropertyNames.Category) as string; + var catList = Properties[PropertyNames.Category]; + if (catList == null) + return null; + + switch (catList.Count) + { + case 0: + return null; + case 1: + return catList[0] as string; + default: + var cats = new string[catList.Count]; + int index = 0; + foreach (string cat in catList) + cats[index++] = cat; + + return string.Join(",", cats); + } + } + set + { + foreach (string cat in value.Split(new char[] { ',' })) + Properties.Add(PropertyNames.Category, cat); + } + } + + #endregion + + #region IFixtureBuilder Members + + /// + /// Builds a single test fixture from the specified type. + /// + public IEnumerable BuildFrom(ITypeInfo typeInfo) + { + yield return _builder.BuildFrom(typeInfo, AlwaysMatchPreFilter.Instance, this); + } + + #endregion + + #region IFixtureBuilder2 Members + + /// + /// Builds a single test fixture from the specified type. + /// + /// The type info of the fixture to be used. + /// Filter used to select methods as tests. + public IEnumerable BuildFrom(ITypeInfo typeInfo, IPreFilter filter) + { + yield return _builder.BuildFrom(typeInfo, filter, this); + } + + #endregion + } + } + + internal class AlwaysMatchPreFilter : IPreFilter + { + public static IPreFilter Instance { get; } = new AlwaysMatchPreFilter(); + + public bool IsMatch(Type type) + { + return true; + } + + public bool IsMatch(Type type, MethodInfo method) + { + return true; + } + } +} diff --git a/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs b/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs new file mode 100644 index 0000000000..02df873e7c --- /dev/null +++ b/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs @@ -0,0 +1,409 @@ +using NUnit.Framework; +using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; +using NUnit.Framework.Internal.Builders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Lucene.Net.Util +{ + #region Copyright (c) 2021 Charlie Poole, Rob Prouse + + // Copyright (c) 2021 Charlie Poole, Rob Prouse + // + // Permission is hereby granted, free of charge, to any person obtaining a copy + // of this software and associated documentation files (the "Software"), to deal + // in the Software without restriction, including without limitation the rights + // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + // copies of the Software, and to permit persons to whom the Software is + // furnished to do so, subject to the following conditions: + // + // The above copyright notice and this permission notice shall be included in + // all copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + // THE SOFTWARE. + + #endregion + + /// + /// NUnitTestFixtureBuilder is able to build a fixture given + /// a class marked with a TestFixtureAttribute or an unmarked + /// class containing test methods. In the first case, it is + /// called by the attribute and in the second directly by + /// NUnitSuiteBuilder. + /// + internal class NUnitTestFixtureBuilder + { + #region Messages + + const string NO_TYPE_ARGS_MSG = + "Fixture type contains generic parameters. You must either provide Type arguments or specify constructor arguments that allow NUnit to deduce the Type arguments."; + + const string PARALLEL_NOT_ALLOWED_MSG = + "ParallelizableAttribute is only allowed on test methods and fixtures"; + + #endregion + + #region Instance Fields + + private readonly ITestCaseBuilder _testBuilder = new DefaultTestCaseBuilder(); + private readonly LuceneRandomSeedInitializer _randomSeedInitializer = new LuceneRandomSeedInitializer(); + + #endregion + + #region Public Methods + + /// + /// Build a TestFixture from type provided. A non-null TestSuite + /// must always be returned, since the method is generally called + /// because the user has marked the target class as a fixture. + /// If something prevents the fixture from being used, it should + /// be returned nonetheless, labelled as non-runnable. + /// + /// An ITypeInfo for the fixture to be used. + /// Filter used to select methods as tests. + /// A TestSuite object or one derived from TestSuite. + // TODO: This should really return a TestFixture, but that requires changes to the Test hierarchy. + public TestSuite BuildFrom(ITypeInfo typeInfo, IPreFilter filter) + { + var fixture = new TestFixture(typeInfo); + + // HACK: NUnit3TestAdapter seems to be supplying the seed from nunit_random_seed.tmp whether + // or not there is a RandomSeed set by the user. This is a workaorund until that is fixed. + _randomSeedInitializer.InitializeRandomSeed(fixture); + + if (fixture.RunState != RunState.NotRunnable) + CheckTestFixtureIsValid(fixture); + + fixture.ApplyAttributesToTest(typeInfo.Type.GetTypeInfo()); + + AddTestCasesToFixture(fixture, filter); + + return fixture; + } + + /// + /// Overload of BuildFrom called by tests that have arguments. + /// Builds a fixture using the provided type and information + /// in the ITestFixtureData object. + /// + /// The TypeInfo for which to construct a fixture. + /// Filter used to select methods as tests. + /// An object implementing ITestFixtureData or null. + /// + public TestSuite BuildFrom(ITypeInfo typeInfo, IPreFilter filter, ITestFixtureData testFixtureData) + { + //Guard.ArgumentNotNull(testFixtureData, nameof(testFixtureData)); + if (testFixtureData is null) + throw new ArgumentNullException(nameof(testFixtureData)); + + object[] arguments = testFixtureData.Arguments; + + if (typeInfo.ContainsGenericParameters) + { + Type[] typeArgs = testFixtureData.TypeArgs; + if (typeArgs == null || typeArgs.Length == 0) + { + int cnt = 0; + foreach (object o in arguments) + if (o is Type) cnt++; + else break; + + typeArgs = new Type[cnt]; + for (int i = 0; i < cnt; i++) + typeArgs[i] = (Type)arguments[i]; + + if (cnt > 0) + { + object[] args = new object[arguments.Length - cnt]; + for (int i = 0; i < args.Length; i++) + args[i] = arguments[cnt + i]; + + arguments = args; + } + } + + if (typeArgs.Length > 0 || + TypeHelper.CanDeduceTypeArgsFromArgs(typeInfo.Type, arguments, ref typeArgs)) + { + typeInfo = typeInfo.MakeGenericType(typeArgs); + } + } + + var fixture = new TestFixture(typeInfo, arguments); + + string name = fixture.Name; + + if (testFixtureData.TestName != null) + { + fixture.Name = testFixtureData.TestName; + } + else + { + //var argDisplayNames = (testFixtureData as NUnit.Framework.Internal.TestParameters)?.ArgDisplayNames; + var testParameters = testFixtureData as NUnit.Framework.Internal.TestParameters; + string[] argDisplayNames = null; + if (testParameters != null) + { + // Hack so we can call the same internal field that NUnit does + BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + FieldInfo field = typeof(NUnit.Framework.Internal.TestParameters).GetField("_argDisplayNames", bindFlags); + argDisplayNames = (string[])field.GetValue(testFixtureData); + } + if (argDisplayNames != null) + { + fixture.Name = typeInfo.GetDisplayName(); + if (argDisplayNames.Length != 0) + fixture.Name += '(' + string.Join(", ", argDisplayNames) + ')'; + } + else if (arguments != null && arguments.Length > 0) + { + fixture.Name = typeInfo.GetDisplayName(arguments); + } + } + + if (fixture.Name != name) // name was changed + { + string nspace = typeInfo.Namespace; + fixture.FullName = nspace != null && nspace != "" + ? nspace + "." + fixture.Name + : fixture.Name; + } + + if (fixture.RunState != RunState.NotRunnable) + fixture.RunState = testFixtureData.RunState; + + foreach (string key in testFixtureData.Properties.Keys) + foreach (object val in testFixtureData.Properties[key]) + fixture.Properties.Add(key, val); + + // HACK: NUnit3TestAdapter seems to be supplying the seed from nunit_random_seed.tmp whether + // or not there is a RandomSeed set by the user. This is a workaorund until that is fixed. + _randomSeedInitializer.InitializeRandomSeed(fixture); + + if (fixture.RunState != RunState.NotRunnable) + CheckTestFixtureIsValid(fixture); + + fixture.ApplyAttributesToTest(typeInfo.Type.GetTypeInfo()); + + AddTestCasesToFixture(fixture, filter); + + return fixture; + } + + #endregion + + #region Helper Methods + + /// + /// Method to add test cases to the newly constructed fixture. + /// + private void AddTestCasesToFixture(TestFixture fixture, IPreFilter filter) + { + // TODO: Check this logic added from Neil's build. + if (fixture.TypeInfo.ContainsGenericParameters) + { + fixture.MakeInvalid(NO_TYPE_ARGS_MSG); + return; + } + + // We sort the methods in a deterministic order, since BuildTestCase() will invoke the + // Randomizer to create seeds for each test. We want those seeds to be deterministically repeatable + // so we can re-run the same conditions by manually fixing the Randomizer.InitialSeed. + var methods = new SortedSet(fixture.TypeInfo.GetMethods( + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static), + MethodInfoComparer.Default); + + foreach (IMethodInfo method in methods) + { + // Generate the seed whether or not we use a filter to ensure we invoke the Randomizer to + // move to the next random test seed (a test should always get the same seed). + Test test = BuildTestCase(method, fixture); + + if (filter.IsMatch(fixture.TypeInfo.Type, method.MethodInfo)) + { + if (test != null) + fixture.Add(test); + else // it's not a test, check for disallowed attributes + if (method.MethodInfo.HasAttribute(false)) + fixture.MakeInvalid(PARALLEL_NOT_ALLOWED_MSG); + } + } + } + + /// + /// Method to create a test case from a MethodInfo and add + /// it to the fixture being built. It first checks to see if + /// any global TestCaseBuilder addin wants to build the + /// test case. If not, it uses the internal builder + /// collection maintained by this fixture builder. + /// + /// The default implementation has no test case builders. + /// Derived classes should add builders to the collection + /// in their constructor. + /// + /// The method for which a test is to be created + /// The test suite being built. + /// A newly constructed Test + private Test BuildTestCase(IMethodInfo method, TestSuite suite) + { + return _testBuilder.CanBuildFrom(method, suite) + ? _testBuilder.BuildFrom(method, suite) + : null; + } + + private static void CheckTestFixtureIsValid(TestFixture fixture) + { + if (fixture.TypeInfo.ContainsGenericParameters) + { + fixture.MakeInvalid(NO_TYPE_ARGS_MSG); + } + else if (!fixture.TypeInfo.IsStaticClass) + { + Type[] argTypes = /*Reflect.*/GetTypeArray(fixture.Arguments); + + if (!/*Reflect.*/GetConstructors(fixture.TypeInfo.Type, argTypes).Any()) + { + fixture.MakeInvalid("No suitable constructor was found"); + } + } + } + + /// + /// Returns an array of types from an array of objects. + /// Differs from by returning + /// for null elements rather than throwing . + /// + internal static Type[] GetTypeArray(object[] objects) + { + Type[] types = new Type[objects.Length]; + int index = 0; + foreach (object o in objects) + { + types[index++] = o?.GetType(); + } + return types; + } + + /// + /// Gets the constructors to which the specified argument types can be coerced. + /// + internal static IEnumerable GetConstructors(Type type, Type[] matchingTypes) + { + return type + .GetConstructors() + .Where(c => c.GetParameters().ParametersMatch(matchingTypes)); + } + + #endregion + } + + internal class MethodInfoComparer : IComparer + { + public static IComparer Default { get; } = new MethodInfoComparer(); + + public int Compare(IMethodInfo x, IMethodInfo y) + { + StringComparer stringComparer = StringComparer.Ordinal; + + int nameCompare = stringComparer.Compare(x.Name, y.Name); + if (nameCompare != 0) + return nameCompare; + + var xParameters = x.GetParameters(); + var yParameters = y.GetParameters(); + + if (xParameters.Length > yParameters.Length) + return 1; + if (xParameters.Length < yParameters.Length) + return -1; + + for (int i = 0; i < xParameters.Length; i++) + { + var px = xParameters[i]; + var py = xParameters[i]; + + int parameterTypeCompare = stringComparer.Compare(px.ParameterType.FullName, py.ParameterType.FullName); + if (parameterTypeCompare != 0) + return parameterTypeCompare; + } + + return 0; + } + } + + internal static class Extensions + { + // From NUnit's Reflect class + + /// + /// Determines if the given types can be coerced to match the given parameters. + /// + internal static bool ParametersMatch(this ParameterInfo[] pinfos, Type[] ptypes) + { + if (pinfos.Length != ptypes.Length) + return false; + + for (int i = 0; i < pinfos.Length; i++) + { + if (!ptypes[i].CanImplicitlyConvertTo(pinfos[i].ParameterType)) + return false; + } + return true; + } + + // §6.1.2 (Implicit numeric conversions) of the specification + private static readonly Dictionary> convertibleValueTypes = new Dictionary>() { + { typeof(decimal), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char) } }, + { typeof(double), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } }, + { typeof(float), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } }, + { typeof(ulong), new List { typeof(byte), typeof(ushort), typeof(uint), typeof(char) } }, + { typeof(long), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(char) } }, + { typeof(uint), new List { typeof(byte), typeof(ushort), typeof(char) } }, + { typeof(int), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(char) } }, + { typeof(ushort), new List { typeof(byte), typeof(char) } }, + { typeof(short), new List { typeof(byte) } } + }; + + /// + /// Determines whether the current type can be implicitly converted to the specified type. + /// + internal static bool CanImplicitlyConvertTo(this Type from, Type to) + { + if (to.IsAssignableFrom(from)) + return true; + + // Look for the marker that indicates from was null + if (from == null && (to.GetTypeInfo().IsClass || to.FullName.StartsWith("System.Nullable"))) + return true; + + if (convertibleValueTypes.ContainsKey(to) && convertibleValueTypes[to].Contains(from)) + return true; + + return from + .GetMethods(BindingFlags.Public | BindingFlags.Static) + .Any(m => m.ReturnType == to && m.Name == "op_Implicit"); + } + + + // From NUnit's Extensions class + public static bool HasAttribute(this ICustomAttributeProvider attributeProvider, bool inherit) + { + return attributeProvider.IsDefined(typeof(T), inherit); + } + + public static bool HasAttribute(this Type type, bool inherit) + { + return ((ICustomAttributeProvider)type.GetTypeInfo()).HasAttribute(inherit); + } + } + +} diff --git a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs index a0ababf182..ab957e22da 100644 --- a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs +++ b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs @@ -48,7 +48,7 @@ using OneTimeSetUp = NUnit.Framework.OneTimeSetUpAttribute; using OneTimeTearDown = NUnit.Framework.OneTimeTearDownAttribute; using Test = NUnit.Framework.TestAttribute; -using TestFixture = NUnit.Framework.TestFixtureAttribute; +//using TestFixture = Lucene.Net.Util.LuceneTestCase.TestFixtureAttribute; using AssumptionViolatedException = NUnit.Framework.InconclusiveException; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; @@ -1199,6 +1199,7 @@ public virtual void AfterClass() { TempLineDocsFile = null; } + random = null; // Remove our random - random numbers after this point will be generated by LuceneTestFrameworkInitializer.random } catch (Exception ex) { @@ -1237,18 +1238,23 @@ public static Random Random get { #if TESTFRAMEWORK_NUNIT + if (TestExecutionContext.CurrentContext.CurrentTest.IsSuite) + { + // This value is set to null at the end of OneTimeTearDown to prevent polluting the next instance. + return random ?? (random = new Random(Randomizer.InitialSeed)); + } + + // If we are here, this is a test, and it has a valid seed that was generated for it specifically. return NUnit.Framework.TestContext.CurrentContext.Random; #else - return _random ?? (_random = new Random(/* LUCENENET TODO seed */)); + return random ?? (random = new Random(/* LUCENENET TODO seed */)); //return RandomizedContext.Current.Random; #endif } } -#if !TESTFRAMEWORK_NUNIT [ThreadStatic] - private static Random _random; -#endif + private static Random random; /////// /////// Registers a resource that should be closed after the test From 2c74f2d4b550f7d4199c3f3f6732d04b20c8df06 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Sun, 7 Nov 2021 16:55:08 +0700 Subject: [PATCH 03/28] BREAKING: Lucene.Net.Util.StringHelper.GOOD_FAST_HASH_SEED: converted from a static field to a property and marked obsolete. Added a new property GoodFastHashSeed. Removed SystemProperties call to populate the value of the field, since NUnit only allows us to generate a seed per test, and we need a way to inject the seed value for repeatability. --- .../Configuration/TestSystemProperties.cs | 4 +- src/Lucene.Net/Util/BytesRef.cs | 4 +- src/Lucene.Net/Util/BytesRefHash.cs | 2 +- src/Lucene.Net/Util/StringHelper.cs | 39 +++++++------------ 4 files changed, 18 insertions(+), 31 deletions(-) diff --git a/src/Lucene.Net.Tests.TestFramework/Configuration/TestSystemProperties.cs b/src/Lucene.Net.Tests.TestFramework/Configuration/TestSystemProperties.cs index 5cb21229ee..2858b91855 100644 --- a/src/Lucene.Net.Tests.TestFramework/Configuration/TestSystemProperties.cs +++ b/src/Lucene.Net.Tests.TestFramework/Configuration/TestSystemProperties.cs @@ -151,9 +151,9 @@ public virtual void TestHashCodeReadProperty() Assert.AreEqual(0xf6a5c420, (uint)StringHelper.Murmurhash3_x86_32(new BytesRef("foo"), 0)); - Assert.AreEqual(16, StringHelper.GOOD_FAST_HASH_SEED); + Assert.AreEqual(16, StringHelper.GoodFastHashSeed); // Hashes computed using murmur3_32 from https://code.google.com/p/pyfasthash - Assert.AreEqual(0xcd018ef6, (uint)StringHelper.Murmurhash3_x86_32(new BytesRef("foo"), StringHelper.GOOD_FAST_HASH_SEED)); + Assert.AreEqual(0xcd018ef6, (uint)StringHelper.Murmurhash3_x86_32(new BytesRef("foo"), StringHelper.GoodFastHashSeed)); } [Test] diff --git a/src/Lucene.Net/Util/BytesRef.cs b/src/Lucene.Net/Util/BytesRef.cs index 23a80f4d4f..87bb54ed7f 100644 --- a/src/Lucene.Net/Util/BytesRef.cs +++ b/src/Lucene.Net/Util/BytesRef.cs @@ -204,13 +204,13 @@ public object Clone() /// Calculates the hash code as required by during indexing. /// This is currently implemented as MurmurHash3 (32 /// bit), using the seed from - /// , but is subject to + /// , but is subject to /// change from release to release. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { - return StringHelper.Murmurhash3_x86_32(this, StringHelper.GOOD_FAST_HASH_SEED); + return StringHelper.Murmurhash3_x86_32(this, StringHelper.GoodFastHashSeed); } public override bool Equals(object other) diff --git a/src/Lucene.Net/Util/BytesRefHash.cs b/src/Lucene.Net/Util/BytesRefHash.cs index 4ff425dacc..19fba5f835 100644 --- a/src/Lucene.Net/Util/BytesRefHash.cs +++ b/src/Lucene.Net/Util/BytesRefHash.cs @@ -525,7 +525,7 @@ private void Rehash(int newSize, bool hashOnData) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int DoHash(byte[] bytes, int offset, int length) { - return StringHelper.Murmurhash3_x86_32(bytes, offset, length, StringHelper.GOOD_FAST_HASH_SEED); + return StringHelper.Murmurhash3_x86_32(bytes, offset, length, StringHelper.GoodFastHashSeed); } /// diff --git a/src/Lucene.Net/Util/StringHelper.cs b/src/Lucene.Net/Util/StringHelper.cs index 50aff05fc0..8826b3ce9c 100644 --- a/src/Lucene.Net/Util/StringHelper.cs +++ b/src/Lucene.Net/Util/StringHelper.cs @@ -31,38 +31,25 @@ namespace Lucene.Net.Util /// public static class StringHelper // LUCENENET specific - marked static and removed private constructor { - /// - /// Pass this as the seed to . - - //Singleton-esque member. Only created once - public static readonly int GOOD_FAST_HASH_SEED = InitializeHashSeed(); - // Poached from Guava: set a different salt/seed // for each JVM instance, to frustrate hash key collision // denial of service attacks, and to catch any places that // somehow rely on hash function/order across JVM // instances: - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int InitializeHashSeed() - { - // LUCENENET specific - reformatted with : - string prop = SystemProperties.GetProperty("tests:seed", null); - if (prop != null) - { - // So if there is a test failure that relied on hash - // order, we remain reproducible based on the test seed: - if (prop.Length > 8) - { - prop = prop.Substring(prop.Length - 8); - } - return Convert.ToInt32(prop, 16); - } - else - { - return (int)J2N.Time.CurrentTimeMilliseconds(); - } - } + // LUCENENET specific - Since NUnit considers hash seed a per-test setting, we make the + // backing field internal and writable so it can be set by the test framework. + // The tests:seed system property is only applicable to the test environment, as it has no + // useful purpose in production. + internal static int goodFastHashSeed = (int)J2N.Time.CurrentTimeMilliseconds(); + + /// + /// Pass this as the seed to . + //Singleton-esque member. Only created once + public static int GoodFastHashSeed => goodFastHashSeed; + + [Obsolete("Use GoodFastHashSeed instead. This field will be removed in 4.8.0 release candidate.")] + public static int GOOD_FAST_HASH_SEED => goodFastHashSeed; /// /// Compares two , element by element, and returns the From 90bcf5561b21029367f803018d3480b053d084d2 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Thu, 11 Nov 2021 20:14:43 +0700 Subject: [PATCH 04/28] Lucene.Net.TestFramework.Support.Util.LuceneRandomSeedInitializer: Added line to set StringHelper.goodFastHashSeed when the test framework is attached. --- .../Support/Util/LuceneRandomSeedInitializer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs b/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs index 2a22af42ac..04b448bfb1 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs @@ -53,6 +53,8 @@ public void InitializeRandomSeed(TestFixture fixture) // For now, ignore anything NUnit3TestAdapter does, because it is messing up repeatable runs. Randomizer.InitialSeed = SystemProperties.GetPropertyAsInt32("tests:seed", new Random().Next()); } + + StringHelper.goodFastHashSeed = Randomizer.InitialSeed * 31; // LUCENENET: Multiplying 31 to remove the possility of a collision with the test framework while still using a deterministic number. } } } From d150553e8af096c11f5dddfcb28d386901610d89 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Fri, 12 Nov 2021 01:43:51 +0700 Subject: [PATCH 05/28] Lucene.Net.TestFramework.Util.LuceneTestCase: Stow Random instance in test properties if we are in a suite context --- .../Util/LuceneTestCase.cs | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs index ab957e22da..7564cee188 100644 --- a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs +++ b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs @@ -1199,7 +1199,6 @@ public virtual void AfterClass() { TempLineDocsFile = null; } - random = null; // Remove our random - random numbers after this point will be generated by LuceneTestFrameworkInitializer.random } catch (Exception ex) { @@ -1238,10 +1237,29 @@ public static Random Random get { #if TESTFRAMEWORK_NUNIT - if (TestExecutionContext.CurrentContext.CurrentTest.IsSuite) + var currentTest = TestExecutionContext.CurrentContext.CurrentTest; + if (currentTest.IsSuite) { - // This value is set to null at the end of OneTimeTearDown to prevent polluting the next instance. - return random ?? (random = new Random(Randomizer.InitialSeed)); + // We may get here if this property is called in OneTimeSetUp or OneTimeTearDown + // of either a TextFixture or TestSetupFixture, or somewhere else outside of + // a test's context like direct calls from external code and field initializers. + + const string SuiteRandomizer = "SuiteRandomizer"; + + UninterruptableMonitor.Enter(randomCreationLock); + try + { + if (currentTest.Properties.ContainsKey(SuiteRandomizer)) + return (Random)currentTest.Properties[SuiteRandomizer]; + + var random = new Random(Randomizer.InitialSeed); + currentTest.Properties.Add(SuiteRandomizer, random); + return random; + } + finally + { + UninterruptableMonitor.Exit(randomCreationLock); + } } // If we are here, this is a test, and it has a valid seed that was generated for it specifically. @@ -1253,8 +1271,12 @@ public static Random Random } } + private static readonly object randomCreationLock = new object(); + +#if !TESTFRAMEWORK_NUNIT [ThreadStatic] private static Random random; +#endif /////// /////// Registers a resource that should be closed after the test From f63ed4de2655a16810da610e2a1e33d540c308e1 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Fri, 12 Nov 2021 22:57:52 +0700 Subject: [PATCH 06/28] Lucene.Net.TestFramework: Reworked LuceneRandomSeedIntiailizer and changed RandomSeedAttribute to be an assembly-level attribute --- .../Util/LuceneRandomSeedInitializer.cs | 103 ++++++++++++++++-- .../LuceneTestCase.RandomSeedAttribute.cs | 47 -------- .../Support/Util/NUnitTestFixtureBuilder.cs | 8 +- .../Support/Util/RandomSeedAttribute.cs | 42 +++++++ .../Util/LuceneTestCase.cs | 32 +----- 5 files changed, 142 insertions(+), 90 deletions(-) delete mode 100644 src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.RandomSeedAttribute.cs create mode 100644 src/Lucene.Net.TestFramework/Support/Util/RandomSeedAttribute.cs diff --git a/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs b/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs index 04b448bfb1..5bc8fe7786 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs @@ -1,4 +1,5 @@ -using NUnit.Framework.Internal; +using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; using System; using System.Linq; @@ -30,31 +31,113 @@ internal class LuceneRandomSeedInitializer #endregion - public void InitializeRandomSeed(TestFixture fixture) + private Random random; + private int seed; + + private bool TryGetRandomSeedFromContext(Test test, out int seed) { - if (fixture.TypeInfo.IsDefined(inherit: true)) + var randomSeedAttribute = (RandomSeedAttribute)test.TypeInfo.Assembly + .GetCustomAttributes(typeof(RandomSeedAttribute), inherit: false) + .FirstOrDefault(); + if (randomSeedAttribute != null) { - var randomSeedAttribute = fixture.TypeInfo.GetCustomAttributes(inherit: true).First(); - Randomizer.InitialSeed = randomSeedAttribute.RandomSeed; + seed = randomSeedAttribute.RandomSeed; } // HACK: NUnit3TestAdapter seems to be supplying the seed from nunit_random_seed.tmp whether // or not there is a RandomSeed set by the user. This is a workaorund until that is fixed. else if (NUnit.Framework.TestContext.Parameters.Exists("RandomSeed")) { if (NUnit.Framework.TestContext.Parameters["RandomSeed"].Equals("random", StringComparison.OrdinalIgnoreCase)) - Randomizer.InitialSeed = new Random().Next(); + seed = -1; else if (int.TryParse(NUnit.Framework.TestContext.Parameters["RandomSeed"], out int initialSeed)) - Randomizer.InitialSeed = initialSeed; + seed = initialSeed; else - fixture.MakeInvalid(RANDOM_SEED_PARAMS_MSG); + { + seed = -1; + test.MakeInvalid(RANDOM_SEED_PARAMS_MSG); + } } else { // For now, ignore anything NUnit3TestAdapter does, because it is messing up repeatable runs. - Randomizer.InitialSeed = SystemProperties.GetPropertyAsInt32("tests:seed", new Random().Next()); + seed = SystemProperties.GetPropertyAsInt32("tests:seed", -1); + } + + if (seed == -1) + { + seed = new Random().Next(); + return false; } + return true; + } + + public void EnsureInitialized(Test fixture) + { + TryGetRandomSeedFromContext(fixture, out int seed); + random = new Random(seed); + + if (Randomizer.InitialSeed != seed) + Randomizer.InitialSeed = seed; + int goodFastHashSeed = seed * 31; // LUCENENET: Multiplying 31 to remove the possility of a collision with the test framework while still using a deterministic number. + if (StringHelper.goodFastHashSeed != goodFastHashSeed) + StringHelper.goodFastHashSeed = goodFastHashSeed; + + // Now we need to generate the first seed for our test fixture + // which will be used during OneTimeSetUp and OneTimeTearDown. + fixture.Seed = random.Next(); + } + + //public void EnsureInitialized(Test test) + //{ + // if (!TryGetRandomSeedFromContext(test, out int contextSeed) || seed != contextSeed) + // { + // seed = contextSeed; + // random = new Random(seed); + // } + + // if (Randomizer.InitialSeed != seed) + // Randomizer.InitialSeed = seed; + // int goodFastHashSeed = seed * 31; // LUCENENET: Multiplying 31 to remove the possility of a collision with the test framework while still using a deterministic number. + // if (StringHelper.goodFastHashSeed != goodFastHashSeed) + // StringHelper.goodFastHashSeed = goodFastHashSeed; + //} - StringHelper.goodFastHashSeed = Randomizer.InitialSeed * 31; // LUCENENET: Multiplying 31 to remove the possility of a collision with the test framework while still using a deterministic number. + //private void ResetRandomizer(TestFixture fixture) + //{ + // TryGetRandomSeedFromContext(fixture, out int seed); + // random = new Random(seed); + + // if (Randomizer.InitialSeed != seed) + // Randomizer.InitialSeed = seed; + // int goodFastHashSeed = seed * 31; // LUCENENET: Multiplying 31 to remove the possility of a collision with the test framework while still using a deterministic number. + // if (StringHelper.goodFastHashSeed != goodFastHashSeed) + // StringHelper.goodFastHashSeed = goodFastHashSeed; + //} + + //public void SetNextFixtureSeed(TestFixture fixture) + //{ + // ResetRandomizer(fixture); + // random.Next(); + //} + + public void GenerateRandomSeeds(Test test) + { + SetRandomSeeds(test); + } + + private void SetRandomSeeds(Test test) + { + if (test is null) + return; + + test.Seed = random.Next(); + + if (test.HasChildren) + { + foreach (ITest child in test.Tests) + if (child is Test testChild) + SetRandomSeeds(testChild); + } } } } diff --git a/src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.RandomSeedAttribute.cs b/src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.RandomSeedAttribute.cs deleted file mode 100644 index 25cd045967..0000000000 --- a/src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.RandomSeedAttribute.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; - -namespace Lucene.Net.Util -{ - /* - * 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. - */ - - public abstract partial class LuceneTestCase - { - /// - /// Specifies a random seed to use when running tests. This allows specific test conditions to be repeated for debugging purposes. - /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)] - [SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "API looks better with this nested.")] - public sealed class RandomSeedAttribute : System.Attribute - { - /// - /// Construct a with a specific random seed value. - /// - /// - public RandomSeedAttribute(int randomSeed) - { - RandomSeed = randomSeed; - } - - /// - /// The random seed value. - /// - public int RandomSeed { get; private set; } - } - } -} diff --git a/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs b/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs index 02df873e7c..c139e02779 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs @@ -78,7 +78,7 @@ public TestSuite BuildFrom(ITypeInfo typeInfo, IPreFilter filter) // HACK: NUnit3TestAdapter seems to be supplying the seed from nunit_random_seed.tmp whether // or not there is a RandomSeed set by the user. This is a workaorund until that is fixed. - _randomSeedInitializer.InitializeRandomSeed(fixture); + _randomSeedInitializer.EnsureInitialized(fixture); if (fixture.RunState != RunState.NotRunnable) CheckTestFixtureIsValid(fixture); @@ -187,7 +187,7 @@ public TestSuite BuildFrom(ITypeInfo typeInfo, IPreFilter filter, ITestFixtureDa // HACK: NUnit3TestAdapter seems to be supplying the seed from nunit_random_seed.tmp whether // or not there is a RandomSeed set by the user. This is a workaorund until that is fixed. - _randomSeedInitializer.InitializeRandomSeed(fixture); + _randomSeedInitializer.EnsureInitialized(fixture); if (fixture.RunState != RunState.NotRunnable) CheckTestFixtureIsValid(fixture); @@ -225,9 +225,11 @@ private void AddTestCasesToFixture(TestFixture fixture, IPreFilter filter) foreach (IMethodInfo method in methods) { // Generate the seed whether or not we use a filter to ensure we invoke the Randomizer to - // move to the next random test seed (a test should always get the same seed). + // move to the next random test seed (a test should always get the same seed in the sequence). Test test = BuildTestCase(method, fixture); + _randomSeedInitializer.GenerateRandomSeeds(test); + if (filter.IsMatch(fixture.TypeInfo.Type, method.MethodInfo)) { if (test != null) diff --git a/src/Lucene.Net.TestFramework/Support/Util/RandomSeedAttribute.cs b/src/Lucene.Net.TestFramework/Support/Util/RandomSeedAttribute.cs new file mode 100644 index 0000000000..8f2279a145 --- /dev/null +++ b/src/Lucene.Net.TestFramework/Support/Util/RandomSeedAttribute.cs @@ -0,0 +1,42 @@ +using System; + +namespace Lucene.Net.Util +{ + /* + * 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. + */ + + /// + /// Specifies a random seed to use when running tests. This allows specific test conditions to be repeated for debugging purposes. + /// + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = true)] + public sealed class RandomSeedAttribute : System.Attribute + { + /// + /// Construct a with a specific random seed value. + /// + /// + public RandomSeedAttribute(int randomSeed) + { + RandomSeed = randomSeed; + } + + /// + /// The random seed value. + /// + public int RandomSeed { get; private set; } + } +} diff --git a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs index 7564cee188..19e5cd96d9 100644 --- a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs +++ b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs @@ -1035,8 +1035,8 @@ public virtual void TearDown() */ #if TESTFRAMEWORK_NUNIT TestResult result = TestExecutionContext.CurrentContext.CurrentResult; - string message = result.Message + $"\n\nTo reproduce this test result, apply the [RandomSeed({Randomizer.InitialSeed})]" + - $" attribute to the test class or use the following .runsettings file.\n\n" + + string message = result.Message + $"\n\nTo reproduce this test result, apply the [assembly: RandomSeed({Randomizer.InitialSeed})]" + + $" attribute to the test assembly or use the following .runsettings file.\n\n" + $"\n" + $" \n" + $" \n" + @@ -1237,32 +1237,6 @@ public static Random Random get { #if TESTFRAMEWORK_NUNIT - var currentTest = TestExecutionContext.CurrentContext.CurrentTest; - if (currentTest.IsSuite) - { - // We may get here if this property is called in OneTimeSetUp or OneTimeTearDown - // of either a TextFixture or TestSetupFixture, or somewhere else outside of - // a test's context like direct calls from external code and field initializers. - - const string SuiteRandomizer = "SuiteRandomizer"; - - UninterruptableMonitor.Enter(randomCreationLock); - try - { - if (currentTest.Properties.ContainsKey(SuiteRandomizer)) - return (Random)currentTest.Properties[SuiteRandomizer]; - - var random = new Random(Randomizer.InitialSeed); - currentTest.Properties.Add(SuiteRandomizer, random); - return random; - } - finally - { - UninterruptableMonitor.Exit(randomCreationLock); - } - } - - // If we are here, this is a test, and it has a valid seed that was generated for it specifically. return NUnit.Framework.TestContext.CurrentContext.Random; #else return random ?? (random = new Random(/* LUCENENET TODO seed */)); @@ -1271,8 +1245,6 @@ public static Random Random } } - private static readonly object randomCreationLock = new object(); - #if !TESTFRAMEWORK_NUNIT [ThreadStatic] private static Random random; From a4d75d35f41eca6f690571706f8e24810a9e4071 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Sat, 13 Nov 2021 01:31:19 +0700 Subject: [PATCH 07/28] Lucene.Net.TestFramework: Removed support for Randomizer.InitialSeed and store the seed in a property of the test fixture (instance-based rather than static) --- .../Util/LuceneRandomSeedInitializer.cs | 50 ++++--------------- .../Util/LuceneTestCase.cs | 10 ++-- 2 files changed, 17 insertions(+), 43 deletions(-) diff --git a/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs b/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs index 5bc8fe7786..5f2c4845ba 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs @@ -24,6 +24,8 @@ namespace Lucene.Net.Util internal class LuceneRandomSeedInitializer { + const string RandomSeed = "RandomSeed"; + #region Messages const string RANDOM_SEED_PARAMS_MSG = @@ -32,7 +34,6 @@ internal class LuceneRandomSeedInitializer #endregion private Random random; - private int seed; private bool TryGetRandomSeedFromContext(Test test, out int seed) { @@ -45,11 +46,11 @@ private bool TryGetRandomSeedFromContext(Test test, out int seed) } // HACK: NUnit3TestAdapter seems to be supplying the seed from nunit_random_seed.tmp whether // or not there is a RandomSeed set by the user. This is a workaorund until that is fixed. - else if (NUnit.Framework.TestContext.Parameters.Exists("RandomSeed")) + else if (NUnit.Framework.TestContext.Parameters.Exists(RandomSeed)) { - if (NUnit.Framework.TestContext.Parameters["RandomSeed"].Equals("random", StringComparison.OrdinalIgnoreCase)) + if (NUnit.Framework.TestContext.Parameters[RandomSeed].Equals("random", StringComparison.OrdinalIgnoreCase)) seed = -1; - else if (int.TryParse(NUnit.Framework.TestContext.Parameters["RandomSeed"], out int initialSeed)) + else if (int.TryParse(NUnit.Framework.TestContext.Parameters[RandomSeed], out int initialSeed)) seed = initialSeed; else { @@ -71,13 +72,15 @@ private bool TryGetRandomSeedFromContext(Test test, out int seed) return true; } - public void EnsureInitialized(Test fixture) + public void EnsureInitialized(TestFixture fixture) { TryGetRandomSeedFromContext(fixture, out int seed); random = new Random(seed); - if (Randomizer.InitialSeed != seed) - Randomizer.InitialSeed = seed; + // Set a prpoperty on the test fixture. This is where we will later + // report the seed in the TearDown method (which sets the TestResult message). + fixture.Properties.Set(RandomSeed, seed); + int goodFastHashSeed = seed * 31; // LUCENENET: Multiplying 31 to remove the possility of a collision with the test framework while still using a deterministic number. if (StringHelper.goodFastHashSeed != goodFastHashSeed) StringHelper.goodFastHashSeed = goodFastHashSeed; @@ -87,39 +90,6 @@ public void EnsureInitialized(Test fixture) fixture.Seed = random.Next(); } - //public void EnsureInitialized(Test test) - //{ - // if (!TryGetRandomSeedFromContext(test, out int contextSeed) || seed != contextSeed) - // { - // seed = contextSeed; - // random = new Random(seed); - // } - - // if (Randomizer.InitialSeed != seed) - // Randomizer.InitialSeed = seed; - // int goodFastHashSeed = seed * 31; // LUCENENET: Multiplying 31 to remove the possility of a collision with the test framework while still using a deterministic number. - // if (StringHelper.goodFastHashSeed != goodFastHashSeed) - // StringHelper.goodFastHashSeed = goodFastHashSeed; - //} - - //private void ResetRandomizer(TestFixture fixture) - //{ - // TryGetRandomSeedFromContext(fixture, out int seed); - // random = new Random(seed); - - // if (Randomizer.InitialSeed != seed) - // Randomizer.InitialSeed = seed; - // int goodFastHashSeed = seed * 31; // LUCENENET: Multiplying 31 to remove the possility of a collision with the test framework while still using a deterministic number. - // if (StringHelper.goodFastHashSeed != goodFastHashSeed) - // StringHelper.goodFastHashSeed = goodFastHashSeed; - //} - - //public void SetNextFixtureSeed(TestFixture fixture) - //{ - // ResetRandomizer(fixture); - // random.Next(); - //} - public void GenerateRandomSeeds(Test test) { SetRandomSeeds(test); diff --git a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs index 19e5cd96d9..6a56ba9c9d 100644 --- a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs +++ b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs @@ -1034,12 +1034,16 @@ public virtual void TearDown() ParentChainCallRule.TeardownCalled = true; */ #if TESTFRAMEWORK_NUNIT - TestResult result = TestExecutionContext.CurrentContext.CurrentResult; - string message = result.Message + $"\n\nTo reproduce this test result, apply the [assembly: RandomSeed({Randomizer.InitialSeed})]" + + var context = TestExecutionContext.CurrentContext; + int seed = 0; + if (context.CurrentTest.Parent.Properties.ContainsKey("RandomSeed")) + seed = (int)context.CurrentTest.Parent.Properties.Get("RandomSeed"); + TestResult result = context.CurrentResult; + string message = result.Message + $"\n\nTo reproduce this test result, apply the [assembly: RandomSeed({seed})]" + $" attribute to the test assembly or use the following .runsettings file.\n\n" + $"\n" + $" \n" + - $" \n" + + $" \n" + $" \n" + $"\n\nSee the .runsettings documentation at: https://docs.microsoft.com/en-us/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file."; From 80504cf25901ab7d3dfa7d16a235fc1fbb324c26 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Sat, 13 Nov 2021 14:23:06 +0700 Subject: [PATCH 08/28] Lucene.Net.Tests.TestFramework.Support.TestApiConsistency: Added exclusion for RandomSeed constant --- .../Support/TestApiConsistency.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Lucene.Net.Tests.TestFramework/Support/TestApiConsistency.cs b/src/Lucene.Net.Tests.TestFramework/Support/TestApiConsistency.cs index a17e51b43f..929ebe497d 100644 --- a/src/Lucene.Net.Tests.TestFramework/Support/TestApiConsistency.cs +++ b/src/Lucene.Net.Tests.TestFramework/Support/TestApiConsistency.cs @@ -39,7 +39,7 @@ public override void TestProtectedFieldNames(Type typeFromTargetAssembly) [TestCase(typeof(Lucene.Net.RandomExtensions))] public override void TestPrivateFieldNames(Type typeFromTargetAssembly) { - base.TestPrivateFieldNames(typeFromTargetAssembly, @"ApiScanTestBase|TestUtil.MaxRecursionBound|Assert.FailureFormat"); + base.TestPrivateFieldNames(typeFromTargetAssembly, @"ApiScanTestBase|TestUtil\.MaxRecursionBound|Assert\.FailureFormat|LuceneRandomSeedInitializer\.RandomSeed"); } [Test, LuceneNetSpecific] From aa3d72733c0bfdcad27a6486503596222e60e92d Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Mon, 15 Nov 2021 04:34:00 +0700 Subject: [PATCH 09/28] Lucene.Net.TestFramework.Util.TestRuleSetupAndRestoreClassEnv: Changed system property to prefer "tests:culture" instead of "tests:locale" to set the culture, but left "tests:locale" in for backward/java compatibility. --- .../Util/TestRuleSetupAndRestoreClassEnv.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs b/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs index f8c6c52400..35e5fe8e0d 100644 --- a/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs +++ b/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs @@ -281,7 +281,8 @@ public override void Before(LuceneTestCase testInstance) Codec.Default = codec; // Initialize locale/ timezone. - string testLocale = SystemProperties.GetProperty("tests:locale", "random"); // LUCENENET specific - reformatted with : + // LUCENENET: Accept either tests:culture or tests:locale (culture preferred). + string testLocale = SystemProperties.GetProperty("tests:culture", SystemProperties.GetProperty("tests:locale", "random")); // LUCENENET specific - reformatted with : string testTimeZone = SystemProperties.GetProperty("tests:timezone", "random"); // LUCENENET specific - reformatted with : // Always pick a random one for consistency (whether tests.locale was specified or not). From cf685cca62ac92b81f03b9fe5307cc002efa9944 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Mon, 15 Nov 2021 04:42:00 +0700 Subject: [PATCH 10/28] Lucene.Net.TestFramework: Created RandomizedContext class to store as an NUnit test property for each test. This allows us to add additional strongly-typed properties to the test without wrapping the class. Changed to use J2N.Randomizer and changed all seeds to long rather than int. Converted the number for setting the random seed to hexadecimal and updated the message to add the setting for the current culture to the attributes/.runsettings file. --- .../Util/LuceneRandomSeedInitializer.cs | 78 ++++++++----- .../Support/Util/NUnitTestFixtureBuilder.cs | 10 +- .../Support/Util/RandomSeedAttribute.cs | 16 ++- .../Support/Util/RandomizedContext.cs | 103 ++++++++++++++++++ .../Util/LuceneTestCase.cs | 29 +++-- 5 files changed, 186 insertions(+), 50 deletions(-) create mode 100644 src/Lucene.Net.TestFramework/Support/Util/RandomizedContext.cs diff --git a/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs b/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs index 5f2c4845ba..8bf90b6d86 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs @@ -24,72 +24,89 @@ namespace Lucene.Net.Util internal class LuceneRandomSeedInitializer { - const string RandomSeed = "RandomSeed"; - #region Messages const string RANDOM_SEED_PARAMS_MSG = - "RandomSeed parameter must be a valid int value or the word 'random'."; + "\"tests:seed\" parameter must be a valid long hexadecimal value or the word \"random\"."; #endregion - private Random random; + private J2N.Randomizer random; + private long initialSeed; - private bool TryGetRandomSeedFromContext(Test test, out int seed) + private bool TryGetRandomSeedFromContext(Test test, out long seed) { + // Setup the factories + LuceneTestFrameworkInitializer.EnsureInitialized(); + + bool generate; + seed = 0; var randomSeedAttribute = (RandomSeedAttribute)test.TypeInfo.Assembly .GetCustomAttributes(typeof(RandomSeedAttribute), inherit: false) .FirstOrDefault(); if (randomSeedAttribute != null) { seed = randomSeedAttribute.RandomSeed; + generate = false; } - // HACK: NUnit3TestAdapter seems to be supplying the seed from nunit_random_seed.tmp whether - // or not there is a RandomSeed set by the user. This is a workaorund until that is fixed. - else if (NUnit.Framework.TestContext.Parameters.Exists(RandomSeed)) + else { - if (NUnit.Framework.TestContext.Parameters[RandomSeed].Equals("random", StringComparison.OrdinalIgnoreCase)) - seed = -1; - else if (int.TryParse(NUnit.Framework.TestContext.Parameters[RandomSeed], out int initialSeed)) - seed = initialSeed; + // For now, ignore anything NUnit3TestAdapter does, because it is messing up repeatable runs. + var seedAsString = SystemProperties.GetProperty("tests:seed", null); + if (seedAsString is null || "random".Equals(seedAsString, StringComparison.OrdinalIgnoreCase)) + { + generate = true; + } + else if (J2N.Numerics.Int64.TryParse(seedAsString, 16, out seed)) + { + generate = false; + } else { - seed = -1; + generate = true; test.MakeInvalid(RANDOM_SEED_PARAMS_MSG); } } - else - { - // For now, ignore anything NUnit3TestAdapter does, because it is messing up repeatable runs. - seed = SystemProperties.GetPropertyAsInt32("tests:seed", -1); - } - if (seed == -1) + if (generate) { - seed = new Random().Next(); + seed = new J2N.Randomizer().NextInt64(); return false; } return true; } - public void EnsureInitialized(TestFixture fixture) + /// + /// Initializes the randomized context and seed for the test fixture. + /// + /// The test fixture. + /// Offset that will be added to the initial seed. This should be different for SetUpFixture and TestFixture attributes + /// so they have different seeds that are deterministically based on the initial seed. + public void InitializeTestFixture(TestFixture fixture, int seedOffset = 0) { - TryGetRandomSeedFromContext(fixture, out int seed); - random = new Random(seed); + if (fixture is null) + throw new ArgumentNullException(nameof(fixture)); - // Set a prpoperty on the test fixture. This is where we will later - // report the seed in the TearDown method (which sets the TestResult message). - fixture.Properties.Set(RandomSeed, seed); + TryGetRandomSeedFromContext(fixture, out initialSeed); // NOTE: This sets the initialSeed field for this class. + random = new J2N.Randomizer(initialSeed + seedOffset); - int goodFastHashSeed = seed * 31; // LUCENENET: Multiplying 31 to remove the possility of a collision with the test framework while still using a deterministic number. + int goodFastHashSeed = (int)initialSeed * 31; // LUCENENET: Multiplying 31 to remove the possility of a collision with the test framework while still using a deterministic number. if (StringHelper.goodFastHashSeed != goodFastHashSeed) StringHelper.goodFastHashSeed = goodFastHashSeed; // Now we need to generate the first seed for our test fixture // which will be used during OneTimeSetUp and OneTimeTearDown. - fixture.Seed = random.Next(); + // Assumption: The passed in fixture doesn't have any tests added. + // The tests are added in a later step to prevent differences in the + // result when there are filters applied. + SetRandomSeeds(fixture); } + /// + /// Generates random seeds for the given test and all of its children that + /// can be cast to . + /// + /// public void GenerateRandomSeeds(Test test) { SetRandomSeeds(test); @@ -100,7 +117,10 @@ private void SetRandomSeeds(Test test) if (test is null) return; - test.Seed = random.Next(); + test.Properties.Set( + RandomizedContext.RandomizedContextPropertyName, + // Generate a new long value that is the seed for this specific test. + new RandomizedContext(test, initialSeed, random.NextInt64())); if (test.HasChildren) { diff --git a/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs b/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs index c139e02779..5036c6283c 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs @@ -76,9 +76,8 @@ public TestSuite BuildFrom(ITypeInfo typeInfo, IPreFilter filter) { var fixture = new TestFixture(typeInfo); - // HACK: NUnit3TestAdapter seems to be supplying the seed from nunit_random_seed.tmp whether - // or not there is a RandomSeed set by the user. This is a workaorund until that is fixed. - _randomSeedInitializer.EnsureInitialized(fixture); + // Set the seed for the test fixture with an offset of 2 + _randomSeedInitializer.InitializeTestFixture(fixture, seedOffset: 2); if (fixture.RunState != RunState.NotRunnable) CheckTestFixtureIsValid(fixture); @@ -185,9 +184,8 @@ public TestSuite BuildFrom(ITypeInfo typeInfo, IPreFilter filter, ITestFixtureDa foreach (object val in testFixtureData.Properties[key]) fixture.Properties.Add(key, val); - // HACK: NUnit3TestAdapter seems to be supplying the seed from nunit_random_seed.tmp whether - // or not there is a RandomSeed set by the user. This is a workaorund until that is fixed. - _randomSeedInitializer.EnsureInitialized(fixture); + // Set the seed for the test fixture with an offset of 2 + _randomSeedInitializer.InitializeTestFixture(fixture, seedOffset: 2); if (fixture.RunState != RunState.NotRunnable) CheckTestFixtureIsValid(fixture); diff --git a/src/Lucene.Net.TestFramework/Support/Util/RandomSeedAttribute.cs b/src/Lucene.Net.TestFramework/Support/Util/RandomSeedAttribute.cs index 8f2279a145..9b15af17e4 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/RandomSeedAttribute.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/RandomSeedAttribute.cs @@ -28,15 +28,25 @@ public sealed class RandomSeedAttribute : System.Attribute /// /// Construct a with a specific random seed value. /// - /// - public RandomSeedAttribute(int randomSeed) + /// A value that represents the initial random seed to use to run the tests. + public RandomSeedAttribute(long randomSeed) { RandomSeed = randomSeed; } + /// + /// Construct a with a specific random seed value. + /// + /// A value that represents the initial random seed to use to run the tests. + [CLSCompliant(false)] + public RandomSeedAttribute(ulong randomSeed) + { + RandomSeed = unchecked((long)randomSeed); + } + /// /// The random seed value. /// - public int RandomSeed { get; private set; } + public long RandomSeed { get; private set; } } } diff --git a/src/Lucene.Net.TestFramework/Support/Util/RandomizedContext.cs b/src/Lucene.Net.TestFramework/Support/Util/RandomizedContext.cs new file mode 100644 index 0000000000..53afbfb664 --- /dev/null +++ b/src/Lucene.Net.TestFramework/Support/Util/RandomizedContext.cs @@ -0,0 +1,103 @@ +using J2N; +using NUnit.Framework.Internal; +using System; + +namespace Lucene.Net.Util +{ + /* + * 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. + */ + + /// + /// Extensions to NUnit's test context for randomized testing. + /// + internal class RandomizedContext + { + internal const string RandomizedContextPropertyName = "RandomizedContext"; + + private Random randomGenerator; + private readonly long randomSeed; + private readonly string randomSeedAsHex; + private readonly long testSeed; + private readonly Test currentTest; + + public RandomizedContext(Test currentTest, long randomSeed, long testSeed) + { + this.currentTest = currentTest ?? throw new ArgumentNullException(nameof(currentTest)); + this.randomSeed = randomSeed; + this.randomSeedAsHex = "0x" + randomSeed.ToHexString(); + this.testSeed = testSeed; + } + + /// + /// Gets the initial seed that was used to initialize the current context. + /// + public long RandomSeed => randomSeed; + + /// + /// Gets the initial seed as a hexadecimal string for display/configuration purposes. + /// + public string RandomSeedAsHex => randomSeedAsHex; + + /// + /// The current test for this context. + /// + public Test CurrentTest => currentTest; + + /// + /// The random seed for this test's . + /// + public long TestSeed => testSeed; + + /// + /// Gets the RandomGenerator specific to this Test. This random generator implementatation + /// is not platform specific, so random numbers generated on one operating system will work on another. + /// + /// NOTE: NUnit doesn't currently set the property for the test fixtures + /// when using their built-in or + /// . This means the seed will always be 0 + /// when using this property from or + /// , so it cannot be relied upon to create + /// random test data in these cases. Using the + /// will set the seed properly and make it possible to repeat the result. + /// + public Random RandomGenerator + { + get + { + if (randomGenerator == null) + randomGenerator = new J2N.Randomizer(testSeed); + return randomGenerator; + } + } + + /// + /// Gets the randomized context for the current test or test fixture. + /// + public static RandomizedContext CurrentContext + { + get + { + var currentTest = TestExecutionContext.CurrentContext.CurrentTest; + + if (currentTest.Properties.ContainsKey(RandomizedContextPropertyName)) + return (RandomizedContext)currentTest.Properties.Get(RandomizedContextPropertyName); + + return null; // We are out of random context and cannot respond with results that are repeatable. + } + } + } +} diff --git a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs index 6a56ba9c9d..46e34498da 100644 --- a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs +++ b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs @@ -1034,21 +1034,23 @@ public virtual void TearDown() ParentChainCallRule.TeardownCalled = true; */ #if TESTFRAMEWORK_NUNIT - var context = TestExecutionContext.CurrentContext; - int seed = 0; - if (context.CurrentTest.Parent.Properties.ContainsKey("RandomSeed")) - seed = (int)context.CurrentTest.Parent.Properties.Get("RandomSeed"); - TestResult result = context.CurrentResult; - string message = result.Message + $"\n\nTo reproduce this test result, apply the [assembly: RandomSeed({seed})]" + - $" attribute to the test assembly or use the following .runsettings file.\n\n" + + TestResult result = TestExecutionContext.CurrentContext.CurrentResult; + string message = result.Message + $"\n\nTo reproduce this test result:\n\n" + + $"Option 1:\n\n" + + $" Apply the following assembly-level attributes:\n\n" + + $"[assembly: Lucene.Net.Util.RandomSeed({RandomizedContext.CurrentContext.RandomSeedAsHex}L)]\n" + + $"[assembly: NUnit.Framework.Property(\"tests:culture\", \"{Thread.CurrentThread.CurrentCulture.Name}\")]\n\n\n" + + $"Option 2:\n\n" + + $" Use the following .runsettings file.\n\n" + $"\n" + $" \n" + - $" \n" + + $" \n" + + $" \n" + $" \n" + - $"\n\nSee the .runsettings documentation at: https://docs.microsoft.com/en-us/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file."; + $"\n\n" + + $"See the .runsettings documentation at: https://docs.microsoft.com/en-us/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file."; - string stackTrace = result.StackTrace; - result.SetResult(result.ResultState, message, stackTrace); + result.SetResult(result.ResultState, message, result.StackTrace); #endif } @@ -1241,7 +1243,10 @@ public static Random Random get { #if TESTFRAMEWORK_NUNIT - return NUnit.Framework.TestContext.CurrentContext.Random; + var context = RandomizedContext.CurrentContext; + if (context is null) + Assert.Fail("LuceneTestCase.Random may only be used within tests/setup/teardown context in subclasses of LuceneTestCase or LuceneTestFrameworkInitializer."); + return context.RandomGenerator; #else return random ?? (random = new Random(/* LUCENENET TODO seed */)); //return RandomizedContext.Current.Random; From 8633f671780177a0625281788b239a051e89d864 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Tue, 16 Nov 2021 06:02:47 +0700 Subject: [PATCH 11/28] BREAKING: Added LuceneSetUpFixtureBuilder class to load either a subclass or our default instance of LuceneTestFrameworkInitializer. Also added LuceneTestCase.SetUpFixture to control initialization of LuceneTestFrameworkInitializer so it is only called on setup and teardown for the assembly. Added Initialize() method to LuceneTestFrameworkInitializer that must be used to set the factories. --- .../Util/DefaultNamespaceTypeWrapper.cs | 294 ++++++++++++++++ .../Util/LuceneRandomSeedInitializer.cs | 11 +- .../Support/Util/LuceneSetUpFixtureBuilder.cs | 87 +++++ .../Util/LuceneTestCase.SetUpFixture.cs | 198 +++++++++++ .../LuceneTestCase.TestFixtureAttribute.cs | 2 +- .../Util/LuceneTestFrameworkInitializer.cs | 322 ++++++++++++------ .../Support/Util/NUnitTestFixtureBuilder.cs | 79 ++++- .../Util/LuceneTestCase.cs | 8 - .../Startup.cs | 2 +- src/Lucene.Net.Tests.TestFramework/Startup.cs | 2 +- .../Support/TestApiConsistency.cs | 2 +- 11 files changed, 884 insertions(+), 123 deletions(-) create mode 100644 src/Lucene.Net.TestFramework/Support/Util/DefaultNamespaceTypeWrapper.cs create mode 100644 src/Lucene.Net.TestFramework/Support/Util/LuceneSetUpFixtureBuilder.cs create mode 100644 src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.SetUpFixture.cs diff --git a/src/Lucene.Net.TestFramework/Support/Util/DefaultNamespaceTypeWrapper.cs b/src/Lucene.Net.TestFramework/Support/Util/DefaultNamespaceTypeWrapper.cs new file mode 100644 index 0000000000..4a3de06d18 --- /dev/null +++ b/src/Lucene.Net.TestFramework/Support/Util/DefaultNamespaceTypeWrapper.cs @@ -0,0 +1,294 @@ +using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; +using System; +using System.Linq; +using System.Reflection; + +namespace Lucene.Net.Util +{ + #region Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License. + + // Copyright (c) 2021 Charlie Poole, Rob Prouse + // + // Permission is hereby granted, free of charge, to any person obtaining a copy + // of this software and associated documentation files (the "Software"), to deal + // in the Software without restriction, including without limitation the rights + // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + // copies of the Software, and to permit persons to whom the Software is + // furnished to do so, subject to the following conditions: + // + // The above copyright notice and this permission notice shall be included in + // all copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + // THE SOFTWARE. + + #endregion + + /// + /// The TypeWrapper class wraps a Type so it may be used in + /// a platform-independent manner. This class overrides the + /// namespace to return null so it is only run once per + /// assembly. + /// + public class DefaultNamespaceTypeWrapper : ITypeInfo + { + /// + /// Construct a TypeWrapper for a specified Type. + /// + public DefaultNamespaceTypeWrapper(Type type) + { + Type = type ?? throw new ArgumentNullException(nameof(type)); + } + + /// + /// Gets the underlying Type on which this TypeWrapper is based. + /// + public Type Type { get; private set; } + + /// + /// Gets the base type of this type as an ITypeInfo + /// + public ITypeInfo BaseType + { + get + { + var baseType = Type.GetTypeInfo().BaseType; + + return baseType != null + ? new TypeWrapper(baseType) + : null; + } + } + + /// + /// Gets the Name of the Type + /// + public string Name + { + get { return Type.Name; } + } + + /// + /// Gets the FullName of the Type + /// + public string FullName + { + get { return Type.FullName; } + } + + /// + /// Gets the assembly in which the type is declared + /// + public Assembly Assembly + { + get { return Type.GetTypeInfo().Assembly; } + } + + /// + /// Gets the namespace of the Type + /// + public string Namespace + { + get { return null; } // LUCENENET: Force this type to be the + } + + /// + /// Gets a value indicating whether the type is abstract. + /// + public bool IsAbstract + { + get { return Type.GetTypeInfo().IsAbstract; } + } + + /// + /// Gets a value indicating whether the Type is a generic Type + /// + public bool IsGenericType + { + get { return Type.GetTypeInfo().IsGenericType; } + } + + /// + /// Returns true if the Type wrapped is T + /// + public bool IsType(Type type) + { + return Type == type; + } + + /// + /// Gets a value indicating whether the Type has generic parameters that have not been replaced by specific Types. + /// + public bool ContainsGenericParameters + { + get { return Type.GetTypeInfo().ContainsGenericParameters; } + } + + /// + /// Gets a value indicating whether the Type is a generic Type definition + /// + public bool IsGenericTypeDefinition + { + get { return Type.GetTypeInfo().IsGenericTypeDefinition; } + } + + /// + /// Gets a value indicating whether the type is sealed. + /// + public bool IsSealed + { + get { return Type.GetTypeInfo().IsSealed; } + } + + /// + /// Gets a value indicating whether this type represents a static class. + /// + public bool IsStaticClass => Type.IsStatic(); + + /// + /// Get the display name for this type + /// + public string GetDisplayName() + { + return TypeHelper.GetDisplayName(Type); + } + + /// + /// Get the display name for an object of this type, constructed with the specified args. + /// + public string GetDisplayName(object[] args) + { + return TypeHelper.GetDisplayName(Type, args); + } + + /// + /// Returns a new ITypeInfo representing an instance of this generic Type using the supplied Type arguments + /// + public ITypeInfo MakeGenericType(Type[] typeArgs) + { + return new TypeWrapper(Type.MakeGenericType(typeArgs)); + } + + /// + /// Returns a Type representing a generic type definition from which this Type can be constructed. + /// + public Type GetGenericTypeDefinition() + { + return Type.GetGenericTypeDefinition(); + } + + /// + /// Returns an array of custom attributes of the specified type applied to this type + /// + public T[] GetCustomAttributes(bool inherit) where T : class + { + return Type.GetAttributes(inherit); + } + + /// + /// Returns a value indicating whether the type has an attribute of the specified type. + /// + /// + /// + /// + public bool IsDefined(bool inherit) where T : class + { + return Type.HasAttribute(inherit); + } + + /// + /// Returns a flag indicating whether this type has a method with an attribute of the specified type. + /// + /// + /// + public bool HasMethodWithAttribute(Type attributeType) + { + return Reflect.HasMethodWithAttribute(Type, attributeType); + } + + /// + /// Returns an array of IMethodInfos for methods of this Type + /// that match the specified flags. + /// + public IMethodInfo[] GetMethods(BindingFlags flags) + { + var methods = Type.GetMethods(flags); + var result = new MethodWrapper[methods.Length]; + + for (int i = 0; i < methods.Length; i++) + result[i] = new MethodWrapper(Type, methods[i]); + + return result; + } + + /// + /// Gets the public constructor taking the specified argument Types + /// + public ConstructorInfo GetConstructor(Type[] argTypes) + { + return Type.GetConstructor(argTypes); + } + + /// + /// Returns a value indicating whether this Type has a public constructor taking the specified argument Types. + /// + public bool HasConstructor(Type[] argTypes) + { + return GetConstructor(argTypes) != null; + } + + /// + /// Construct an object of this Type, using the specified arguments. + /// + public object Construct(object[] args) + { + return Reflect.Construct(Type, args); + } + + /// + /// Override ToString() so that error messages in NUnit's own tests make sense + /// + public override string ToString() + { + return Type.ToString(); + } + + /// + /// Returns all methods declared by this type that have the specified attribute, optionally + /// including base classes. Methods from a base class are always returned before methods from a class that + /// inherits from it. + /// + /// Specifies whether to search the fixture type inheritance chain. + public IMethodInfo[] GetMethodsWithAttribute(bool inherit) where T : class + { + if (!inherit) + { + return Type + .GetMethods(/*Reflect.*/AllMembers | BindingFlags.DeclaredOnly) + .Where(method => method.IsDefined(typeof(T), inherit: false)) + .Select(method => new MethodWrapper(Type, method)) + .ToArray(); + } + + var methodsByDeclaringType = Type + .GetMethods(/*Reflect.*/AllMembers | BindingFlags.FlattenHierarchy) // FlattenHierarchy is complex to replicate by looping over base types with DeclaredOnly. + .Where(method => method.IsDefined(typeof(T), inherit: true)) + .ToLookup(method => method.DeclaringType); + + return Type.TypeAndBaseTypes() + .Reverse() + .SelectMany(declaringType => methodsByDeclaringType[declaringType].Select(method => new MethodWrapper(declaringType, method))) + .ToArray(); + } + + // From NUnit's Reflect class + + internal static readonly BindingFlags AllMembers = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; + } +} diff --git a/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs b/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs index 8bf90b6d86..cf61a5065c 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs @@ -36,9 +36,6 @@ internal class LuceneRandomSeedInitializer private bool TryGetRandomSeedFromContext(Test test, out long seed) { - // Setup the factories - LuceneTestFrameworkInitializer.EnsureInitialized(); - bool generate; seed = 0; var randomSeedAttribute = (RandomSeedAttribute)test.TypeInfo.Assembly @@ -82,7 +79,7 @@ private bool TryGetRandomSeedFromContext(Test test, out long seed) /// The test fixture. /// Offset that will be added to the initial seed. This should be different for SetUpFixture and TestFixture attributes /// so they have different seeds that are deterministically based on the initial seed. - public void InitializeTestFixture(TestFixture fixture, int seedOffset = 0) + public RandomizedContext InitializeTestFixture(Test fixture, int seedOffset = 0) { if (fixture is null) throw new ArgumentNullException(nameof(fixture)); @@ -99,7 +96,11 @@ public void InitializeTestFixture(TestFixture fixture, int seedOffset = 0) // Assumption: The passed in fixture doesn't have any tests added. // The tests are added in a later step to prevent differences in the // result when there are filters applied. - SetRandomSeeds(fixture); + + // Generate a new long value that is the seed for this specific test. + var randomizedContext = new RandomizedContext(fixture, initialSeed, random.NextInt64()); + fixture.Properties.Set(RandomizedContext.RandomizedContextPropertyName, randomizedContext); + return randomizedContext; } /// diff --git a/src/Lucene.Net.TestFramework/Support/Util/LuceneSetUpFixtureBuilder.cs b/src/Lucene.Net.TestFramework/Support/Util/LuceneSetUpFixtureBuilder.cs new file mode 100644 index 0000000000..81eccfa19f --- /dev/null +++ b/src/Lucene.Net.TestFramework/Support/Util/LuceneSetUpFixtureBuilder.cs @@ -0,0 +1,87 @@ +using NUnit.Framework; +using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; +using System; +using System.Linq; +using System.Reflection; +using System.Threading; + +namespace Lucene.Net.Util +{ + /* + * 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. + */ + + /// + /// Custom class to build the assembly-wide setup/teardown. NUnit doesn't scan attributes from dependent assemblies, + /// so this is a workaround to inject our initializer so we can get a teardown for the assembly. + /// + internal class LuceneSetUpFixtureBuilder + { + public TestSuite BuildFrom(ITypeInfo typeInfo) + { + var setUpFixtureType = new DefaultNamespaceTypeWrapper(typeof(LuceneTestCase.SetUpFixture)); + SetUpFixture fixture = new SetUpFixture(setUpFixtureType); + + if (fixture.RunState != RunState.NotRunnable) + { + string reason = null; + if (!IsValidFixtureType(setUpFixtureType, ref reason)) + fixture.MakeInvalid(reason); + } + + fixture.ApplyAttributesToTest(setUpFixtureType.Type.GetTypeInfo()); + + return fixture; + } + + #region Helper Methods + + private static bool IsValidFixtureType(ITypeInfo typeInfo, ref string reason) + { + if (!typeInfo.IsStaticClass) + { + if (typeInfo.IsAbstract) + { + reason = string.Format("{0} is an abstract class", typeInfo.FullName); + return false; + } + + if (!typeInfo.HasConstructor(new Type[0])) + { + reason = string.Format("{0} does not have a default constructor", typeInfo.FullName); + return false; + } + } + + var invalidAttributes = new Type[] { + typeof(SetUpAttribute), + typeof(TearDownAttribute) + }; + + foreach (Type invalidType in invalidAttributes) + if (typeInfo.HasMethodWithAttribute(invalidType)) + { + reason = invalidType.Name + $" attribute not allowed in a {nameof(LuceneTestCase.SetUpFixture)}."; + return false; + } + + return true; + } + + #endregion + } +} diff --git a/src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.SetUpFixture.cs b/src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.SetUpFixture.cs new file mode 100644 index 0000000000..1f57513fc1 --- /dev/null +++ b/src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.SetUpFixture.cs @@ -0,0 +1,198 @@ +using J2N.Threading.Atomic; +using NUnit.Framework; +using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; +using System; +using System.Linq; +using System.Reflection; +using System.Threading; + +namespace Lucene.Net.Util +{ + /* + * 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. + */ + + public abstract partial class LuceneTestCase + { + /// + /// Captures all of the OneTimeSetUp and OneTimeTearDown calls from NUnit and uses reference counting to ensure + /// a single instance is shared between all + /// test wrappers and that each of its , + /// + /// methods are only executed once. + /// + internal class SetUpFixture + { + /// + /// The singleton instance of LuceneTestFrameworkInitializer that is called before and after all tests. + /// + private static LuceneTestFrameworkInitializer initializer; + private readonly static AtomicInt32 stackCount = new AtomicInt32(0); + + /// + /// + /// + /// The setup wrapper fixture for the current context. This is the top level parent class. It is used to report exceptions. + /// The setup fixture for the current tests. This is the original NUnit test fixture. + /// We use it's assembly to scan for subclasses. + public static void EnsureInitialized(Test setUpFixture, Test testFixture) + { + LazyInitializer.EnsureInitialized(ref initializer, () => Initialize(setUpFixture, testFixture)); + } + + private static LuceneTestFrameworkInitializer Initialize(Test setUpFixture, Test testFixture) + { + var initializer = CreateInitializer(setUpFixture, testFixture); + + // Allow the factories to be instantiated first. We do this to allow a + // dependency injection step, which can only be done once. No random context + // is availble here because we need to load the SystemProperties setup here before + // we can read them. + initializer.DoInitialize(); + + return initializer; + } + + [OneTimeSetUp] + public void OneTimeSetUpWrapper() + { + //var fixture = TestExecutionContext.CurrentContext.CurrentTest; + if (stackCount.GetAndIncrement() == 0) + { + // Set up for assembly + + // This is where a global application setup can be done that includes a randomized context, + // therefore LuceneTestCase.Random calls are allowed here and are repeatable. + initializer.DoTestFrameworkSetUp(); + } + } + + [OneTimeTearDown] + public void OneTimeTearDownWrapper() + { + if (stackCount.DecrementAndGet() == 0) + { + // Tear down for assembly + initializer.DoTestFrameworkTearDown(); + } + } + + private static LuceneTestFrameworkInitializer CreateInitializer(Test wrapperSetUpFixture, Test testFixture) + { + if (TryGetSetUpFixtureType(testFixture.TypeInfo.Assembly, out ITypeInfo setUpFixture, out Type[] candidateTypes)) + { + // No need for Reflection in this case. + if (setUpFixture.Type.Equals(typeof(LuceneTestFrameworkInitializer.DefaultLuceneTestFrameworkInitializer))) + return new LuceneTestFrameworkInitializer.DefaultLuceneTestFrameworkInitializer(); + + if (!IsValidFixtureType(new TypeWrapper(setUpFixture.Type), out string reason)) + { + wrapperSetUpFixture.MakeInvalid(reason); + return new LuceneTestFrameworkInitializer.DefaultLuceneTestFrameworkInitializer(); + } + + try + { + return (LuceneTestFrameworkInitializer)Reflect.Construct(setUpFixture.Type); + } + catch (Exception ex) + { + var exception = ex is TargetInvocationException ? ex.InnerException : ex; + + wrapperSetUpFixture.MakeInvalid("The LuceneTestFrameworkInitializer subclass could not be instantiated.\n\n" + exception.ToString()); + return new LuceneTestFrameworkInitializer.DefaultLuceneTestFrameworkInitializer(); + } + } + else + { + wrapperSetUpFixture.MakeInvalid($"Multiple subclasses of {typeof(LuceneTestFrameworkInitializer).FullName} were found in {testFixture.TypeInfo.Assembly.FullName}. " + + $"Only 1 non-abstract subclass is allowed per assembly.\n\n" + + $"Types Found:\n" + + $"{string.Join("\n ", candidateTypes.Select(t => t.FullName))}"); + return new LuceneTestFrameworkInitializer.DefaultLuceneTestFrameworkInitializer(); + } + } + + private static bool TryGetSetUpFixtureType(Assembly assembly, out ITypeInfo setUpFixtureType, out Type[] candidateTypes) + { + candidateTypes = (from assemblyType in assembly.GetTypes() + where typeof(LuceneTestFrameworkInitializer).IsAssignableFrom(assemblyType) + && !assemblyType.GetTypeInfo().IsAbstract + select assemblyType).ToArray(); + + bool result; + switch (candidateTypes.Length) + { + case 0: // The user didn't subclass, so use ours + candidateTypes = new Type[] { typeof(LuceneTestFrameworkInitializer.DefaultLuceneTestFrameworkInitializer) }; + result = true; + break; + case 1: // The user created 1 subclass, so use theirs + result = true; + break; + default: // The user created multiple subclasses - this is invalid. Return the first type so we can mark it invalid. + result = false; + break; + } + + setUpFixtureType = new DefaultNamespaceTypeWrapper(candidateTypes[0]); + return result; + } + + private static bool IsValidFixtureType(ITypeInfo typeInfo, out string reason) + { + if (!typeInfo.IsStaticClass) + { + if (typeInfo.IsAbstract) + { + reason = string.Format("{0} is an abstract class", typeInfo.FullName); + return false; + } + + if (!typeInfo.HasConstructor(new Type[0])) + { + reason = string.Format("{0} does not have a default constructor", typeInfo.FullName); + return false; + } + } + + var invalidAttributes = new Type[] { + typeof(OneTimeSetUpAttribute), + typeof(OneTimeTearDownAttribute), + typeof(SetUpAttribute), + typeof(TearDownAttribute) + }; + + foreach (Type invalidType in invalidAttributes) + if (typeInfo.HasMethodWithAttribute(invalidType)) + { + reason = invalidType.Name + $" not allowed in a {nameof(LuceneTestFrameworkInitializer)} subclass."; + return false; + } + + if (typeInfo.IsDefined(inherit: true)) + { + reason = $"{nameof(SetUpFixtureAttribute)} not allowed on a {nameof(LuceneTestFrameworkInitializer)} subclass."; + return false; + } + + reason = null; + return true; + } + } + } +} diff --git a/src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.TestFixtureAttribute.cs b/src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.TestFixtureAttribute.cs index 988b7d035d..89a7b36d5a 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.TestFixtureAttribute.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/LuceneTestCase.TestFixtureAttribute.cs @@ -8,7 +8,7 @@ namespace Lucene.Net.Util { - #region Copyright (c) 2021 Charlie Poole, Rob Prouse + #region Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License. // Copyright (c) 2021 Charlie Poole, Rob Prouse // diff --git a/src/Lucene.Net.TestFramework/Support/Util/LuceneTestFrameworkInitializer.cs b/src/Lucene.Net.TestFramework/Support/Util/LuceneTestFrameworkInitializer.cs index 2ad8ccd1c6..9842e31a52 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/LuceneTestFrameworkInitializer.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/LuceneTestFrameworkInitializer.cs @@ -3,7 +3,6 @@ using NUnit.Framework; using System; using System.IO; -using System.Threading; namespace Lucene.Net.Util { @@ -25,10 +24,11 @@ namespace Lucene.Net.Util */ /// - /// may be subclassed by end users in order to + /// may be subclassed in order to /// use standard dependency injection techniques for classes under test. A subclass of - /// will be executed automatically by NUnit. To work properly with the Lucene.NET test framework, the subclass - /// must be placed outside of all namespaces, which ensures it is only executed 1 time per test assembly. + /// will be executed automatically. + /// + /// Only one subclass per assembly is allowed, and by convention these subclasses are usually named "Startup". /// /// The following abstract factories can be overridden for testing purposes: /// @@ -59,51 +59,124 @@ namespace Lucene.Net.Util /// /// /// + /// Methods are executed one time per assembly, and are executed in the following order: + /// + /// + /// Method + /// Description + /// + /// + /// + /// Used to set the factories in the above table. In general, dependency injection for the test assembly is + /// setup in this method. No randomized context is available. + /// + /// + /// + /// Used to set assembly-level test setup. Executed before all tests and class-level setup in the assembly. + /// Repeatable randomized content can be generated using the property. + /// + /// + /// + /// Used to tear down assembly-level test setup. Executed after all tests and class-level tear down in the assembly. + /// Repeatable randomized content can be generated using the property. + /// + /// + /// /// Example: /// - /// // IMPORTANT: Do not put the initializer in a namespace - /// public class MyTestFrameworkInitializer : LuceneTestFrameworkInitializer + /// using RandomizedTesting.Generators; + /// + /// public class Startup : LuceneTestFrameworkInitializer /// { - /// protected override IConfigurationFactory LoadConfigurationFactory() - /// { - /// // Inject a custom configuration factory - /// return new MyConfigurationFactory(); - /// } - /// - /// protected override void TestFrameworkSetUp() + /// // Run first + /// protected override void Initialize() /// { /// // Inject a custom configuration factory /// ConfigurationFactory = new MyConfigurationFactory(); /// /// // Do any additional dependency injection setup here /// } + /// + /// // Run before all tests in the assembly and before any class-level setup + /// protected overide void TestFrameworkSetUp() + /// { + /// // Get the random instance for the current context + /// var random = Random; + /// + /// // Generate random content + /// string content = random.NextSimpleString(); + /// + /// // Use randomization from LuceneTestCase + /// int numberOfDocuments = LuceneTestCase.AtLeast(30); + /// } + /// + /// // Run after all tests in the assembly and after any class-level setup + /// protected override void TestFrameworkTearDown() + /// { + /// // Tear down everything here + /// } /// } /// /// - [SetUpFixture] public abstract class LuceneTestFrameworkInitializer { - // Initialization target must be static to ensure the initializer only fires one - // time per application run (even when the static EnsureInitialized() method is called). - private static object initializationTarget; + private bool isInitializing = false; + private ICodecFactory codecFactory; + private IDocValuesFormatFactory docValuesFormatFactory; + private IPostingsFormatFactory postingsFormatFactory; + private IConfigurationFactory configurationFactory; + protected LuceneTestFrameworkInitializer() + { + codecFactory = new TestCodecFactory(); + docValuesFormatFactory = new TestDocValuesFormatFactory(); + postingsFormatFactory = new TestPostingsFormatFactory(); + configurationFactory = new TestConfigurationFactory(); + } // LUCENENET specific factory methods to scan the test framework for codecs/docvaluesformats/postingsformats only once /// /// The implementation to use to load codecs during testing. /// - protected static ICodecFactory CodecFactory { get; set; } = new TestCodecFactory(); + protected ICodecFactory CodecFactory + { + get => codecFactory; + set + { + if (!isInitializing) + throw new InvalidOperationException("CodecFactory must be set in the Initialize() method."); + codecFactory = value ?? throw new ArgumentNullException(nameof(CodecFactory)); + } + } /// /// The implementation to use to load doc values during testing. /// - protected static IDocValuesFormatFactory DocValuesFormatFactory { get; set; } = new TestDocValuesFormatFactory(); + protected IDocValuesFormatFactory DocValuesFormatFactory + { + get => docValuesFormatFactory; + set + { + if (!isInitializing) + throw new InvalidOperationException("DocValuesFormatFactory must be set in the Initialize() method."); + docValuesFormatFactory = value ?? throw new ArgumentNullException(nameof(DocValuesFormatFactory)); + } + } /// /// The implementation to use to load postings formats during testing. /// - protected static IPostingsFormatFactory PostingsFormatFactory { get; set; } = new TestPostingsFormatFactory(); + protected IPostingsFormatFactory PostingsFormatFactory + { + get => postingsFormatFactory; + set + { + if (!isInitializing) + throw new InvalidOperationException("PostingsFormatFactory must be set in the Initialize() method."); + postingsFormatFactory = value ?? throw new ArgumentNullException(nameof(PostingsFormatFactory)); + } + } /// /// The implementation to use to load configuration settings during testing. @@ -111,65 +184,64 @@ public abstract class LuceneTestFrameworkInitializer /// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/. /// [CLSCompliant(false)] - protected static IConfigurationFactory ConfigurationFactory { get; set; } = new TestConfigurationFactory(); - - /// - /// Overridden in a derived class, can be used to initialize factory instances that are - /// injected into Lucene.NET during test operations, or perform other one-time initialization - /// of the test framework setup. - /// - /// NOTE: To initialize 1 time for your entire test library, only create a single derived class - /// and place it outside of any namespace. - /// - protected virtual void TestFrameworkSetUp() + protected IConfigurationFactory ConfigurationFactory { - // Runs only once per test framework run before all tests + get => configurationFactory; + set + { + if (!isInitializing) + throw new InvalidOperationException("ConfigurationFactory must be set in the Initialize() method."); + configurationFactory = value ?? throw new ArgumentNullException(nameof(ConfigurationFactory)); + } } - /// - /// Overridden in a derived class, can be used to tear down classes one-time - /// for the test framework. + /// Called by to run the initialization phase for the test assembly. /// - protected virtual void TestFrameworkTearDown() + internal void DoInitialize() { - // Runs only once per test framework run after all tests + try + { + isInitializing = true; + Initialize(); + isInitializing = false; + InitializeStaticState(); + AfterInitialization(); // Hook for our tests to check the result + } + catch (Exception ex) + { + // Write the stack trace so we have something to go on if an error occurs here. + throw RuntimeException.Create($"An exception occurred during initialization:\n{ex}", ex); + } } /// - /// When this class is derived in a test assembly, this method is automatically called - /// by NUnit one time before all tests run. + /// Called by to run the one time set up phase for the test assembly. /// - [OneTimeSetUp] - protected void OneTimeSetUpBeforeTests() + internal void DoTestFrameworkSetUp() { + // If we can cleanup, unzip the LineDocsFile 1 time for the entire test run, + // which will dramatically improve performance. + var temp = LineFileDocs.MaybeCreateTempFile(removeAfterClass: false); + if (null != temp) + LuceneTestCase.TestLineDocsFile = temp; + try { TestFrameworkSetUp(); - - // Initialize of the test factories here, so that doesn't happen - // when EnsureInitialized() is called. - Initialize(); - - // If we can cleanup, unzip the LineDocsFile 1 time for the entire test run, - // which will dramatically improve performance. - var temp = LineFileDocs.MaybeCreateTempFile(removeAfterClass: false); - if (null != temp) - LuceneTestCase.TestLineDocsFile = temp; } catch (Exception ex) { // Write the stack trace so we have something to go on if an error occurs here. - throw RuntimeException.Create($"An exception occurred during OneTimeSetUpBeforeTests:\n{ex}", ex); + throw RuntimeException.Create($"An exception occurred during TestFrameworkSetUp:\n{ex}", ex); } } + /// - /// When this class is derived in a test assembly, this method is automatically called - /// by NUnit one time after all tests run. + /// Called by to run the one time tear down phase for the test assembly. /// - [OneTimeTearDown] - protected void OneTimeTearDownAfterTests() + internal void DoTestFrameworkTearDown() { try { @@ -178,7 +250,7 @@ protected void OneTimeTearDownAfterTests() catch (Exception ex) { // Write the stack trace so we have something to go on if an error occurs here. - throw RuntimeException.Create($"An exception occurred during OneTimeTearDownAfterTests:\n{ex}", ex); + throw RuntimeException.Create($"An exception occurred during TestFrameworkTearDown:\n{ex}", ex); } // Cleanup our LineDocsFile and reset LuceneTestCase back to its original state. @@ -196,62 +268,116 @@ protected void OneTimeTearDownAfterTests() } /// - /// The default implementation of the initializer that is simply used to initialize with the - /// default test factory implementations. + /// Access to the current instance. It is safe to use + /// this method from multiple threads, etc., but it should be called while within a runner's + /// scope (so no static initializers). The returned instance will be + /// different when this method is called inside a hook (static + /// suite scope) and within / hooks or test methods. + /// + /// The returned instance must not be shared with other threads or cross a single scope's + /// boundary. For example, a acquired within a test method shouldn't be reused + /// for another test case. + /// + /// There is an overhead connected with getting the for a particular context + /// and thread. It is better to cache the locally if tight loops with multiple + /// invocations are present or create a derivative local for millions of calls + /// like this: + /// + /// Random random = new Random(Random.Next()); + /// // tight loop with many invocations. + /// /// - private class DefaultLuceneTestFrameworkInitializer : LuceneTestFrameworkInitializer { } + protected Random Random => LuceneTestCase.Random; + + /// + /// Overridden in a derived class, provides a way to set , + /// , and as well as + /// other dependency injection setup. + /// + /// This method is called only one time per assembly. + /// + /// Using the property here will result in a test failure. To build randomized global setup, + /// use instead. + /// + protected virtual void Initialize() + { + // Runs only once per test framework before OneTimeSetUp on any tests, and before TestFrameworkSetUp + } /// - /// Can be called by an external source to ensure that the test framework has been initialized. - /// only occurs if it hasn't already been called by NUnit in a subclass - /// of this class. + /// Overridden in a derived class, can be used to perform one-time initialization + /// of the test framework setup. + /// + /// Repeatable random setup can be done by calling or by using methods of . + /// + /// It is not possible to set , , + /// and + /// from this method. Those must be called in . /// - internal static void EnsureInitialized() + protected virtual void TestFrameworkSetUp() { - new DefaultLuceneTestFrameworkInitializer().Initialize(); + // Runs only once per test framework run before all tests } - private void Initialize() + /// + /// Overridden in a derived class, can be used to perform one-time tear down + /// of the test framework setup (whether the setup was done in + /// or doesn't matter). + /// + /// Repeatable random setup can be done by calling or by using methods of . + /// + protected virtual void TestFrameworkTearDown() { - LazyInitializer.EnsureInitialized(ref initializationTarget, () => - { - // Setup the factories - ConfigurationSettings.SetConfigurationFactory(ConfigurationFactory); - Codec.SetCodecFactory(CodecFactory); - DocValuesFormat.SetDocValuesFormatFactory(DocValuesFormatFactory); - PostingsFormat.SetPostingsFormatFactory(PostingsFormatFactory); + // Runs only once per test framework run after all tests + } - // Enable "asserts" for tests. In Java, these were actual asserts, - // but in .NET we simply mock this as a boolean static setting that can be - // toggled on and off, even in release mode. Note this must be done after - // the ConfigurationFactory is set. - Lucene.Net.Diagnostics.Debugging.AssertsEnabled = SystemProperties.GetPropertyAsBoolean("assert", true); + /// + /// The default implementation of the initializer that is simply used to initialize with the + /// default test factory implementations. + /// + internal class DefaultLuceneTestFrameworkInitializer : LuceneTestFrameworkInitializer { } - // Identify NUnit exceptions down in Lucene.Net so they can be ignored in catch blocks that - // catch Java "Exception" types that do subclass Error (for the ExceptionExtensions.IsException() method). - Lucene.ExceptionExtensions.NUnitResultStateExceptionType = typeof(NUnit.Framework.ResultStateException); - Lucene.ExceptionExtensions.NUnitAssertionExceptionType = typeof(NUnit.Framework.AssertionException); - Lucene.ExceptionExtensions.NUnitMultipleAssertExceptionType = typeof(NUnit.Framework.MultipleAssertException); - Lucene.ExceptionExtensions.NUnitInconclusiveExceptionType = typeof(NUnit.Framework.InconclusiveException); - Lucene.ExceptionExtensions.NUnitSuccessExceptionType = typeof(NUnit.Framework.SuccessException); - Lucene.ExceptionExtensions.NUnitInvalidPlatformException = Type.GetType("NUnit.Framework.Internal.InvalidPlatformException, NUnit.Framework"); + /// + /// Called after the factory properties have been set (possibly by using dependency injection) which allows customization + /// of codecs and system properties during testing. + /// + internal void InitializeStaticState() + { + // Setup the factories + ConfigurationSettings.SetConfigurationFactory(ConfigurationFactory); + Codec.SetCodecFactory(CodecFactory); + DocValuesFormat.SetDocValuesFormatFactory(DocValuesFormatFactory); + PostingsFormat.SetPostingsFormatFactory(PostingsFormatFactory); - // Identify the Debug.Assert() exception so it can be excluded from being swallowed by catch blocks. - // These types are internal, so we can identify them using Reflection. - Lucene.ExceptionExtensions.DebugAssertExceptionType = - // .NET 5/.NET Core 3.x - Type.GetType("System.Diagnostics.DebugProvider+DebugAssertException, System.Private.CoreLib") - // .NET Core 2.x - ?? Type.GetType("System.Diagnostics.Debug+DebugAssertException, System.Private.CoreLib"); - // .NET Framework doesn't throw in this case + // Enable "asserts" for tests. In Java, these were actual asserts, + // but in .NET we simply mock this as a boolean static setting that can be + // toggled on and off, even in release mode. Note this must be done after + // the ConfigurationFactory is set. + Lucene.Net.Diagnostics.Debugging.AssertsEnabled = SystemProperties.GetPropertyAsBoolean("assert", true); - AfterInitialization(); + // Identify NUnit exceptions down in Lucene.Net so they can be ignored in catch blocks that + // catch Java "Exception" types that do subclass Error (for the ExceptionExtensions.IsException() method). + Lucene.ExceptionExtensions.NUnitResultStateExceptionType = typeof(NUnit.Framework.ResultStateException); + Lucene.ExceptionExtensions.NUnitAssertionExceptionType = typeof(NUnit.Framework.AssertionException); + Lucene.ExceptionExtensions.NUnitMultipleAssertExceptionType = typeof(NUnit.Framework.MultipleAssertException); + Lucene.ExceptionExtensions.NUnitInconclusiveExceptionType = typeof(NUnit.Framework.InconclusiveException); + Lucene.ExceptionExtensions.NUnitSuccessExceptionType = typeof(NUnit.Framework.SuccessException); + Lucene.ExceptionExtensions.NUnitInvalidPlatformException = Type.GetType("NUnit.Framework.Internal.InvalidPlatformException, NUnit.Framework"); - return new object(); // Placeholder to indicate our initializer has been run already - }); + // Identify the Debug.Assert() exception so it can be excluded from being swallowed by catch blocks. + // These types are internal, so we can identify them using Reflection. + Lucene.ExceptionExtensions.DebugAssertExceptionType = + // .NET 5/.NET Core 3.x + Type.GetType("System.Diagnostics.DebugProvider+DebugAssertException, System.Private.CoreLib") + // .NET Core 2.x + ?? Type.GetType("System.Diagnostics.Debug+DebugAssertException, System.Private.CoreLib"); + // .NET Framework doesn't throw in this case } - internal virtual void AfterInitialization() // LUCENENET TODO: Consider making this public + /// + /// Checkpoint to allow tests to check the results of . + /// + internal virtual void AfterInitialization() // Called only by tests { } } diff --git a/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs b/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs index 5036c6283c..1dbb114bdc 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs @@ -9,7 +9,7 @@ namespace Lucene.Net.Util { - #region Copyright (c) 2021 Charlie Poole, Rob Prouse + #region Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License. // Copyright (c) 2021 Charlie Poole, Rob Prouse // @@ -42,6 +42,11 @@ namespace Lucene.Net.Util /// internal class NUnitTestFixtureBuilder { + const int SETUP_FIXTURE_SEED_OFFSET = 7; + const int TEST_FIXTURE_SEED_OFFSET = 3; + + private static RandomizedContext setUpFixtureRandomizedContext = null; + #region Messages const string NO_TYPE_ARGS_MSG = @@ -55,6 +60,7 @@ internal class NUnitTestFixtureBuilder #region Instance Fields private readonly ITestCaseBuilder _testBuilder = new DefaultTestCaseBuilder(); + private readonly LuceneSetUpFixtureBuilder _setUpFixtureBuilder = new LuceneSetUpFixtureBuilder(); private readonly LuceneRandomSeedInitializer _randomSeedInitializer = new LuceneRandomSeedInitializer(); #endregion @@ -74,10 +80,12 @@ internal class NUnitTestFixtureBuilder // TODO: This should really return a TestFixture, but that requires changes to the Test hierarchy. public TestSuite BuildFrom(ITypeInfo typeInfo, IPreFilter filter) { + // Build our custom SetUpFixture to get the NUnit runner to initialize us + // even though we don't own the test assembly. + var setUpFixture = _setUpFixtureBuilder.BuildFrom(typeInfo); var fixture = new TestFixture(typeInfo); - // Set the seed for the test fixture with an offset of 2 - _randomSeedInitializer.InitializeTestFixture(fixture, seedOffset: 2); + SetUpRandomizedContext(setUpFixture, fixture); if (fixture.RunState != RunState.NotRunnable) CheckTestFixtureIsValid(fixture); @@ -86,7 +94,8 @@ public TestSuite BuildFrom(ITypeInfo typeInfo, IPreFilter filter) AddTestCasesToFixture(fixture, filter); - return fixture; + setUpFixture.Add(fixture); + return setUpFixture; } /// @@ -137,8 +146,13 @@ public TestSuite BuildFrom(ITypeInfo typeInfo, IPreFilter filter, ITestFixtureDa } } + // Build our custom SetUpFixture to get the NUnit runner to initialize us + // even though we don't own the test assembly. + var setUpFixture = _setUpFixtureBuilder.BuildFrom(typeInfo); var fixture = new TestFixture(typeInfo, arguments); + SetUpRandomizedContext(setUpFixture, fixture); + string name = fixture.Name; if (testFixtureData.TestName != null) @@ -184,9 +198,6 @@ public TestSuite BuildFrom(ITypeInfo typeInfo, IPreFilter filter, ITestFixtureDa foreach (object val in testFixtureData.Properties[key]) fixture.Properties.Add(key, val); - // Set the seed for the test fixture with an offset of 2 - _randomSeedInitializer.InitializeTestFixture(fixture, seedOffset: 2); - if (fixture.RunState != RunState.NotRunnable) CheckTestFixtureIsValid(fixture); @@ -194,13 +205,36 @@ public TestSuite BuildFrom(ITypeInfo typeInfo, IPreFilter filter, ITestFixtureDa AddTestCasesToFixture(fixture, filter); - return fixture; + setUpFixture.Add(fixture); + return setUpFixture; } #endregion #region Helper Methods + /// + /// Sets up the randomized context for both the set up fixture and the test fixture. + /// We use the same instance for every set up fixture in the assembly, but each test + /// fixture has its own distinct randomized context instance. + /// + /// The setup fixture. + /// The test fixture. + private void SetUpRandomizedContext(Test setUpFixture, Test testFixture) + { + // Setup the factories so we can read the random seed from the system properties + LuceneTestCase.SetUpFixture.EnsureInitialized(setUpFixture, testFixture); + + // Reuse the same randomized context for each setup fixture instance, since these all need to report + // the same seed. Note that setUpFixtureRandomizedContext is static, so we do this once per assembly. + if (setUpFixtureRandomizedContext is null) + setUpFixtureRandomizedContext = _randomSeedInitializer.InitializeTestFixture(setUpFixture, SETUP_FIXTURE_SEED_OFFSET); + else + setUpFixture.Properties.Add(RandomizedContext.RandomizedContextPropertyName, setUpFixtureRandomizedContext); + + _randomSeedInitializer.InitializeTestFixture(testFixture, TEST_FIXTURE_SEED_OFFSET); + } + /// /// Method to add test cases to the newly constructed fixture. /// @@ -393,8 +427,22 @@ internal static bool CanImplicitlyConvertTo(this Type from, Type to) .Any(m => m.ReturnType == to && m.Name == "op_Implicit"); } + internal static IEnumerable TypeAndBaseTypes(this Type type) + { + for (; type != null; type = type.GetTypeInfo().BaseType) + { + yield return type; + } + } + // From NUnit's Extensions class + + public static bool IsStatic(this Type type) + { + return type.GetTypeInfo().IsAbstract && type.GetTypeInfo().IsSealed; + } + public static bool HasAttribute(this ICustomAttributeProvider attributeProvider, bool inherit) { return attributeProvider.IsDefined(typeof(T), inherit); @@ -404,6 +452,21 @@ public static bool HasAttribute(this Type type, bool inherit) { return ((ICustomAttributeProvider)type.GetTypeInfo()).HasAttribute(inherit); } + + public static T[] GetAttributes(this ICustomAttributeProvider attributeProvider, bool inherit) where T : class + { + return (T[])attributeProvider.GetCustomAttributes(typeof(T), inherit); + } + + public static T[] GetAttributes(this Assembly assembly) where T : class + { + return assembly.GetAttributes(inherit: false); + } + + public static T[] GetAttributes(this Type type, bool inherit) where T : class + { + return ((ICustomAttributeProvider)type.GetTypeInfo()).GetAttributes(inherit); + } } } diff --git a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs index 46e34498da..112ec6fd20 100644 --- a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs +++ b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs @@ -548,8 +548,6 @@ private class SystemPropertyData // LUCENENET specific { public SystemPropertyData() { - // LUCENENET: Always ensure the system properties are initailized before any attempt to read them. - LuceneTestFrameworkInitializer.EnsureInitialized(); Verbose = SystemProperties.GetPropertyAsBoolean("tests:verbose", // LUCENENET specific - reformatted with : #if DEBUG true @@ -1099,9 +1097,6 @@ public static void BeforeClass(Microsoft.VisualStudio.TestTools.UnitTesting.Test try { - // Setup the factories - LuceneTestFrameworkInitializer.EnsureInitialized(); - ClassEnvRule.Before(null); // LUCENENET: Generate the info once so it can be printed out for each test @@ -1139,9 +1134,6 @@ public virtual void BeforeClass() { try { - // Setup the factories - LuceneTestFrameworkInitializer.EnsureInitialized(); - ClassEnvRule.Before(this); // LUCENENET: Generate the info once so it can be printed out for each test diff --git a/src/Lucene.Net.Tests.TestFramework.DependencyInjection/Startup.cs b/src/Lucene.Net.Tests.TestFramework.DependencyInjection/Startup.cs index 437796acf1..0edb611cd9 100644 --- a/src/Lucene.Net.Tests.TestFramework.DependencyInjection/Startup.cs +++ b/src/Lucene.Net.Tests.TestFramework.DependencyInjection/Startup.cs @@ -29,7 +29,7 @@ /// public class Startup : LuceneTestFrameworkInitializer { - protected override void TestFrameworkSetUp() + protected override void Initialize() { // Composition root diff --git a/src/Lucene.Net.Tests.TestFramework/Startup.cs b/src/Lucene.Net.Tests.TestFramework/Startup.cs index d53a98404c..f06f0e8c48 100644 --- a/src/Lucene.Net.Tests.TestFramework/Startup.cs +++ b/src/Lucene.Net.Tests.TestFramework/Startup.cs @@ -39,7 +39,7 @@ public Startup() initilizationReset = initializationCount.GetAndSet(0) != 0; } - protected override void TestFrameworkSetUp() + protected override void Initialize() { // Decorate the existing configuration factory with mock settings // so we don't interfere with the operation of the test framework. diff --git a/src/Lucene.Net.Tests.TestFramework/Support/TestApiConsistency.cs b/src/Lucene.Net.Tests.TestFramework/Support/TestApiConsistency.cs index 929ebe497d..078d665a2a 100644 --- a/src/Lucene.Net.Tests.TestFramework/Support/TestApiConsistency.cs +++ b/src/Lucene.Net.Tests.TestFramework/Support/TestApiConsistency.cs @@ -39,7 +39,7 @@ public override void TestProtectedFieldNames(Type typeFromTargetAssembly) [TestCase(typeof(Lucene.Net.RandomExtensions))] public override void TestPrivateFieldNames(Type typeFromTargetAssembly) { - base.TestPrivateFieldNames(typeFromTargetAssembly, @"ApiScanTestBase|TestUtil\.MaxRecursionBound|Assert\.FailureFormat|LuceneRandomSeedInitializer\.RandomSeed"); + base.TestPrivateFieldNames(typeFromTargetAssembly, @"ApiScanTestBase|TestUtil\.MaxRecursionBound|Assert\.FailureFormat|Lucene\.Net\.Util\.RandomizedContext\.RandomizedContextPropertyName|Lucene\.Net\.Util\.DefaultNamespaceTypeWrapper\.AllMembers"); } [Test, LuceneNetSpecific] From 7ee4b3f4fdc0e70d1891ecd9ec0aeadf9661eb7f Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Tue, 16 Nov 2021 06:11:00 +0700 Subject: [PATCH 12/28] Lucene.Net.TestFramework.Util.LuceneTestCase: Only print out the detailed random repeat message on failure or error --- .../Util/LuceneTestCase.cs | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs index 112ec6fd20..28ae9275cb 100644 --- a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs +++ b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs @@ -983,6 +983,9 @@ public virtual void SetUp() // LUCENENET: Printing out randomized context regardless // of whether verbose is enabled (since we need it for debugging, // but the verbose output can crash tests). + Console.Write("RandomSeed: "); + Console.WriteLine(RandomizedContext.CurrentContext.RandomSeedAsHex); + Console.Write("Culture: "); Console.WriteLine(ClassEnvRule.locale.Name); @@ -1033,22 +1036,25 @@ public virtual void TearDown() */ #if TESTFRAMEWORK_NUNIT TestResult result = TestExecutionContext.CurrentContext.CurrentResult; - string message = result.Message + $"\n\nTo reproduce this test result:\n\n" + - $"Option 1:\n\n" + - $" Apply the following assembly-level attributes:\n\n" + - $"[assembly: Lucene.Net.Util.RandomSeed({RandomizedContext.CurrentContext.RandomSeedAsHex}L)]\n" + - $"[assembly: NUnit.Framework.Property(\"tests:culture\", \"{Thread.CurrentThread.CurrentCulture.Name}\")]\n\n\n" + - $"Option 2:\n\n" + - $" Use the following .runsettings file.\n\n" + - $"\n" + - $" \n" + - $" \n" + - $" \n" + - $" \n" + - $"\n\n" + - $"See the .runsettings documentation at: https://docs.microsoft.com/en-us/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file."; - - result.SetResult(result.ResultState, message, result.StackTrace); + string message; + if (result.ResultState == ResultState.Failure || result.ResultState == ResultState.Error) + { + message = result.Message + $"\n\nTo reproduce this test result:\n\n" + + $"Option 1:\n\n" + + $" Apply the following assembly-level attributes:\n\n" + + $"[assembly: Lucene.Net.Util.RandomSeed({RandomizedContext.CurrentContext.RandomSeedAsHex}L)]\n" + + $"[assembly: NUnit.Framework.Property(\"tests:culture\", \"{Thread.CurrentThread.CurrentCulture.Name}\")]\n\n\n" + + $"Option 2:\n\n" + + $" Use the following .runsettings file.\n\n" + + $"\n" + + $" \n" + + $" \n" + + $" \n" + + $" \n" + + $"\n\n" + + $"See the .runsettings documentation at: https://docs.microsoft.com/en-us/visualstudio/test/configure-unit-tests-by-using-a-dot-runsettings-file."; + result.SetResult(result.ResultState, message, result.StackTrace); + } #endif } From d826cc42ff194971d506953e1ed59db7a9036a05 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Tue, 16 Nov 2021 17:09:35 +0700 Subject: [PATCH 13/28] BREAKING: Lucene.Net.TestFramework.Util.LuceneTestCase: Deprecated GetClassType() method and added TestType property --- src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs | 7 +++++++ .../Util/TestRuleSetupAndRestoreClassEnv.cs | 2 +- .../ByTask/TestPerfTasksParse.cs | 2 +- src/Lucene.Net.Tests/Util/TestFieldCacheSanityChecker.cs | 4 ++-- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs index 28ae9275cb..2ef99212c2 100644 --- a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs +++ b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs @@ -1277,9 +1277,16 @@ public static Random Random //// return RandomizedContext.Current.CloseAtEnd(resource, LifecycleScope.SUITE); ////}*/ + /// + /// Gets the current type being tested. + /// + public static Type TestType + => TestExecutionContext.CurrentContext.CurrentTest.Fixture?.GetType(); + /// /// Return the current type being tested. /// + [Obsolete("Use TestType instead. This method will be removed in 4.8.0 release candidate.")] public static Type GetTestClass() { #if !TESTFRAMEWORK_XUNIT diff --git a/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs b/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs index 35e5fe8e0d..6efc7fb1fe 100644 --- a/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs +++ b/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs @@ -150,7 +150,7 @@ public override void Before(LuceneTestCase testInstance) InfoStream.Default = new NullInfoStream(); } - Type targetClass = testInstance?.GetType() ?? LuceneTestCase.GetTestClass(); + Type targetClass = LuceneTestCase.TestType; avoidCodecs = new JCG.HashSet(); var suppressCodecsAttribute = targetClass.GetCustomAttribute(); if (suppressCodecsAttribute != null) diff --git a/src/Lucene.Net.Tests.Benchmark/ByTask/TestPerfTasksParse.cs b/src/Lucene.Net.Tests.Benchmark/ByTask/TestPerfTasksParse.cs index 5be1307d2f..0fc4dfecd9 100644 --- a/src/Lucene.Net.Tests.Benchmark/ByTask/TestPerfTasksParse.cs +++ b/src/Lucene.Net.Tests.Benchmark/ByTask/TestPerfTasksParse.cs @@ -139,7 +139,7 @@ public void TestParseExamples() if (Type.GetType(contentSource) == null) throw ClassNotFoundException.Create(contentSource); } - config.Set("work.dir", CreateTempDir(LuceneTestCase.GetTestClass().Name).FullName); + config.Set("work.dir", CreateTempDir(LuceneTestCase.TestType.Name).FullName); config.Set("content.source", typeof(MockContentSource).AssemblyQualifiedName); String dir = config.Get("content.source", null); if (dir != null) diff --git a/src/Lucene.Net.Tests/Util/TestFieldCacheSanityChecker.cs b/src/Lucene.Net.Tests/Util/TestFieldCacheSanityChecker.cs index 5bd612b411..99e9bd43cd 100644 --- a/src/Lucene.Net.Tests/Util/TestFieldCacheSanityChecker.cs +++ b/src/Lucene.Net.Tests/Util/TestFieldCacheSanityChecker.cs @@ -1,4 +1,4 @@ -using Lucene.Net.Documents; +using Lucene.Net.Documents; using Lucene.Net.Search; using NUnit.Framework; using System; @@ -139,7 +139,7 @@ public virtual void TestSanity() if (0 < insanity.Length) { - DumpArray(GetTestClass().Name + "#" + TestName + " INSANITY", insanity, Console.Error); + DumpArray(TestType.Name + "#" + TestName + " INSANITY", insanity, Console.Error); } Assert.AreEqual(0, insanity.Length, "shouldn't be any cache insanity"); From 0bc77dd5fb8137e2e9685b7c53379ad43ed4807a Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Tue, 16 Nov 2021 17:26:50 +0700 Subject: [PATCH 14/28] BREAKING: Lucene.Net.TestFramework.Util.AbstractBeforeAfterRule: Removed LuceneTestCase parameter from Before() and After() methods. --- .../Util/AbstractBeforeAfterRule.cs | 6 +++--- src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs | 8 ++++---- .../Util/TestRuleSetupAndRestoreClassEnv.cs | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Lucene.Net.TestFramework/Util/AbstractBeforeAfterRule.cs b/src/Lucene.Net.TestFramework/Util/AbstractBeforeAfterRule.cs index 5fe37d8991..89348c5031 100644 --- a/src/Lucene.Net.TestFramework/Util/AbstractBeforeAfterRule.cs +++ b/src/Lucene.Net.TestFramework/Util/AbstractBeforeAfterRule.cs @@ -1,4 +1,4 @@ -namespace Lucene.Net.Util +namespace Lucene.Net.Util { /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -23,11 +23,11 @@ namespace Lucene.Net.Util /// public abstract class AbstractBeforeAfterRule { - public virtual void Before(LuceneTestCase testInstance) + public virtual void Before() { } - public virtual void After(LuceneTestCase testInstance) + public virtual void After() { } } diff --git a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs index 2ef99212c2..0d552ec793 100644 --- a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs +++ b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs @@ -1103,7 +1103,7 @@ public static void BeforeClass(Microsoft.VisualStudio.TestTools.UnitTesting.Test try { - ClassEnvRule.Before(null); + ClassEnvRule.Before(); // LUCENENET: Generate the info once so it can be printed out for each test codecType = ClassEnvRule.codec.GetType().Name; @@ -1140,7 +1140,7 @@ public virtual void BeforeClass() { try { - ClassEnvRule.Before(this); + ClassEnvRule.Before(); // LUCENENET: Generate the info once so it can be printed out for each test codecType = ClassEnvRule.codec.GetType().Name; @@ -1168,7 +1168,7 @@ public static void ClassCleanup() { try { - ClassEnvRule.After(null); + ClassEnvRule.After(); CleanupTemporaryFiles(); if (UseTempLineDocsFile) { @@ -1197,7 +1197,7 @@ public virtual void AfterClass() { try { - ClassEnvRule.After(this); + ClassEnvRule.After(); CleanupTemporaryFiles(); if (UseTempLineDocsFile) { diff --git a/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs b/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs index 6efc7fb1fe..714c7763ed 100644 --- a/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs +++ b/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs @@ -108,7 +108,7 @@ public override void Message(string component, string message) } } - public override void Before(LuceneTestCase testInstance) + public override void Before() { // LUCENENET specific - SOLR setup code removed @@ -369,7 +369,7 @@ private void CheckCodecRestrictions(Codec codec) /// /// After suite cleanup (always invoked). /// - public override void After(LuceneTestCase testInstance) + public override void After() { // LUCENENT specific - Not used in .NET //foreach (KeyValuePair e in restoreProperties) From d8a44405041cd060a14b5c38ca4aa2d0871f0071 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Tue, 16 Nov 2021 23:06:31 +0700 Subject: [PATCH 15/28] Lucene.Net.Util.LuceneRandomSeedInitializer: Added overload of InitializeTestFixture that accepts RandomizedContext so NUnitTestFixtureBuilder doesn't have to deal with setting NUnit properties. Also added RandomizedContext.CurrentTestAssembly property. --- .../Util/LuceneRandomSeedInitializer.cs | 31 +++++++++++++++++-- .../Support/Util/NUnitTestFixtureBuilder.cs | 6 ++-- .../Support/Util/RandomizedContext.cs | 19 ++++++++++-- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs b/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs index cf61a5065c..c715aabe9c 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/LuceneRandomSeedInitializer.cs @@ -2,6 +2,7 @@ using NUnit.Framework.Internal; using System; using System.Linq; +using System.Reflection; namespace Lucene.Net.Util { @@ -34,6 +35,14 @@ internal class LuceneRandomSeedInitializer private J2N.Randomizer random; private long initialSeed; + /// + /// Tries to get the random seed from either a or the "tests:seed" system property. + /// If niether of these exist, a random seed will be generated and this method returns false; + /// + /// The test fixture. + /// The random seed for a new instance. + /// Note this is a subclass of , since the default doesn't produce consistent results across platforms. + /// true if the seed was found in context; false if the seed was generated. private bool TryGetRandomSeedFromContext(Test test, out long seed) { bool generate; @@ -79,7 +88,8 @@ private bool TryGetRandomSeedFromContext(Test test, out long seed) /// The test fixture. /// Offset that will be added to the initial seed. This should be different for SetUpFixture and TestFixture attributes /// so they have different seeds that are deterministically based on the initial seed. - public RandomizedContext InitializeTestFixture(Test fixture, int seedOffset = 0) + /// The randomized context. + public RandomizedContext InitializeTestFixture(Test fixture, Assembly testAssembly, int seedOffset = 0) { if (fixture is null) throw new ArgumentNullException(nameof(fixture)); @@ -98,7 +108,20 @@ public RandomizedContext InitializeTestFixture(Test fixture, int seedOffset = 0) // result when there are filters applied. // Generate a new long value that is the seed for this specific test. - var randomizedContext = new RandomizedContext(fixture, initialSeed, random.NextInt64()); + return InitializeTestFixture(fixture, new RandomizedContext(fixture, testAssembly, initialSeed, random.NextInt64())); + } + + /// + /// Initializes the randomized context for the fixture. + /// + /// The test fixture. + /// The randomized context to associate with the fixture. + /// The randomized context. + public RandomizedContext InitializeTestFixture(Test fixture, RandomizedContext randomizedContext) + { + if (fixture is null) + throw new ArgumentNullException(nameof(fixture)); + fixture.Properties.Set(RandomizedContext.RandomizedContextPropertyName, randomizedContext); return randomizedContext; } @@ -118,10 +141,12 @@ private void SetRandomSeeds(Test test) if (test is null) return; + var testAssembly = test is ParameterizedMethodSuite ? test.Tests[0].TypeInfo.Assembly : test.TypeInfo.Assembly; + test.Properties.Set( RandomizedContext.RandomizedContextPropertyName, // Generate a new long value that is the seed for this specific test. - new RandomizedContext(test, initialSeed, random.NextInt64())); + new RandomizedContext(test, testAssembly, initialSeed, random.NextInt64())); if (test.HasChildren) { diff --git a/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs b/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs index 1dbb114bdc..852eb67505 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs @@ -228,11 +228,11 @@ private void SetUpRandomizedContext(Test setUpFixture, Test testFixture) // Reuse the same randomized context for each setup fixture instance, since these all need to report // the same seed. Note that setUpFixtureRandomizedContext is static, so we do this once per assembly. if (setUpFixtureRandomizedContext is null) - setUpFixtureRandomizedContext = _randomSeedInitializer.InitializeTestFixture(setUpFixture, SETUP_FIXTURE_SEED_OFFSET); + setUpFixtureRandomizedContext = _randomSeedInitializer.InitializeTestFixture(setUpFixture, testFixture.TypeInfo.Assembly, SETUP_FIXTURE_SEED_OFFSET); else - setUpFixture.Properties.Add(RandomizedContext.RandomizedContextPropertyName, setUpFixtureRandomizedContext); + _randomSeedInitializer.InitializeTestFixture(setUpFixture, setUpFixtureRandomizedContext); - _randomSeedInitializer.InitializeTestFixture(testFixture, TEST_FIXTURE_SEED_OFFSET); + _randomSeedInitializer.InitializeTestFixture(testFixture, testFixture.TypeInfo.Assembly, TEST_FIXTURE_SEED_OFFSET); } /// diff --git a/src/Lucene.Net.TestFramework/Support/Util/RandomizedContext.cs b/src/Lucene.Net.TestFramework/Support/Util/RandomizedContext.cs index 53afbfb664..8e68126502 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/RandomizedContext.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/RandomizedContext.cs @@ -1,6 +1,7 @@ using J2N; using NUnit.Framework.Internal; using System; +using System.Reflection; namespace Lucene.Net.Util { @@ -29,14 +30,23 @@ internal class RandomizedContext internal const string RandomizedContextPropertyName = "RandomizedContext"; private Random randomGenerator; + private readonly Test currentTest; + private readonly Assembly currentTestAssembly; private readonly long randomSeed; private readonly string randomSeedAsHex; private readonly long testSeed; - private readonly Test currentTest; - public RandomizedContext(Test currentTest, long randomSeed, long testSeed) + /// + /// Initializes the randomized context. + /// + /// The test or test fixture for the context. + /// The test assembly. This can be used in to scan the test assembly for attributes. + /// The initial random seed that can be used to regenerate this context. + /// The individual test's seed to regenerate the test. + public RandomizedContext(Test currentTest, Assembly currentTestAssembly, long randomSeed, long testSeed) { this.currentTest = currentTest ?? throw new ArgumentNullException(nameof(currentTest)); + this.currentTestAssembly = currentTestAssembly ?? throw new ArgumentNullException(nameof(currentTestAssembly)); this.randomSeed = randomSeed; this.randomSeedAsHex = "0x" + randomSeed.ToHexString(); this.testSeed = testSeed; @@ -57,6 +67,11 @@ public RandomizedContext(Test currentTest, long randomSeed, long testSeed) /// public Test CurrentTest => currentTest; + /// + /// The current test assembly, which may be used to scan the assembly for custom attributes. + /// + public Assembly CurrentTestAssembly => currentTestAssembly; + /// /// The random seed for this test's . /// From 973f525eba202dcdd7006ce1842a5a1bae8ab1bc Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Tue, 16 Nov 2021 23:50:31 +0700 Subject: [PATCH 16/28] Lucene.Net.TestFramework.Util: Added UseTempLineDocsFileAttribute and a UseTempLineDocFileRule to scan for it and execute the file decompression and cleanup. Removed the unnecessary Startup.cs files that only served the purpose of getting NUnit to recognize LuceneTestFrameworkInitializer in the assembly scan. --- .../Analysis/BaseTokenStreamTestCase.cs | 9 +- .../ThreadedIndexingAndSearchingTestCase.cs | 9 +- .../Search/ShardSearchingTestBase.cs | 9 +- .../Util/LuceneTestFrameworkInitializer.cs | 23 ++--- .../Util/UseTempLineDocsFileAttribute.cs | 30 +++++++ .../Support/Util/UseTempLineDocsFileRule.cs | 84 +++++++++++++++++++ .../Util/LineFileDocs.cs | 4 +- .../Util/LuceneTestCase.cs | 30 +------ .../Startup.cs | 3 - .../Startup.cs | 25 ------ .../Startup.cs | 25 ------ .../Startup.cs | 25 ------ .../Startup.cs | 25 ------ .../Startup.cs | 25 ------ .../Startup.cs | 25 ------ src/Lucene.Net.Tests._A-D/Startup.cs | 25 ------ src/Lucene.Net.Tests._I-J/Startup.cs | 25 ------ src/Lucene.Net.Tests._J-S/Startup.cs | 25 ------ .../Codecs/Lucene40/TestReuseDocsEnum.cs | 5 +- .../Index/TestFlushByRamOrCountsPolicy.cs | 7 +- 20 files changed, 136 insertions(+), 302 deletions(-) create mode 100644 src/Lucene.Net.TestFramework/Support/Util/UseTempLineDocsFileAttribute.cs create mode 100644 src/Lucene.Net.TestFramework/Support/Util/UseTempLineDocsFileRule.cs delete mode 100644 src/Lucene.Net.Tests.Analysis.Kuromoji/Startup.cs delete mode 100644 src/Lucene.Net.Tests.Analysis.Morfologik/Startup.cs delete mode 100644 src/Lucene.Net.Tests.Analysis.OpenNLP/Startup.cs delete mode 100644 src/Lucene.Net.Tests.Analysis.Phonetic/Startup.cs delete mode 100644 src/Lucene.Net.Tests.Analysis.SmartCn/Startup.cs delete mode 100644 src/Lucene.Net.Tests.Analysis.Stempel/Startup.cs delete mode 100644 src/Lucene.Net.Tests._A-D/Startup.cs delete mode 100644 src/Lucene.Net.Tests._I-J/Startup.cs delete mode 100644 src/Lucene.Net.Tests._J-S/Startup.cs diff --git a/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs b/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs index 266a5c1574..c989051f89 100644 --- a/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs +++ b/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs @@ -95,6 +95,8 @@ public override void CopyTo(IAttribute target) /// /// /// + // LUCENENET specific - Specify to unzip the line file docs + [UseTempLineDocsFile] public abstract class BaseTokenStreamTestCase : LuceneTestCase #if TESTFRAMEWORK_XUNIT , Xunit.IClassFixture @@ -126,13 +128,6 @@ public BaseTokenStreamTestCase(BeforeAfterClass beforeAfter) // LUCENENET specific - de-nested CheckClearAttributesAttribute - // LUCENENET specific - Specify to unzip the line file docs - public override void BeforeClass() - { - UseTempLineDocsFile = true; - base.BeforeClass(); - } - // offsetsAreCorrect also validates: // - graph offsets are correct (all tokens leaving from // pos X have the same startOffset; all tokens diff --git a/src/Lucene.Net.TestFramework/Index/ThreadedIndexingAndSearchingTestCase.cs b/src/Lucene.Net.TestFramework/Index/ThreadedIndexingAndSearchingTestCase.cs index 9cb64efb23..cd909a5b32 100644 --- a/src/Lucene.Net.TestFramework/Index/ThreadedIndexingAndSearchingTestCase.cs +++ b/src/Lucene.Net.TestFramework/Index/ThreadedIndexingAndSearchingTestCase.cs @@ -49,6 +49,8 @@ namespace Lucene.Net.Index /// Utility class that spawns multiple indexing and /// searching threads. /// + // LUCENENET specific - Specify to unzip the line file docs + [UseTempLineDocsFile] public abstract class ThreadedIndexingAndSearchingTestCase : LuceneTestCase #if TESTFRAMEWORK_XUNIT , Xunit.IClassFixture @@ -81,13 +83,6 @@ public SubDocs(string packID, IList subIDs) } } - // LUCENENET specific - Specify to unzip the line file docs - public override void BeforeClass() - { - UseTempLineDocsFile = true; - base.BeforeClass(); - } - // Called per-search protected abstract IndexSearcher GetCurrentSearcher(); diff --git a/src/Lucene.Net.TestFramework/Search/ShardSearchingTestBase.cs b/src/Lucene.Net.TestFramework/Search/ShardSearchingTestBase.cs index 6e8b7983fb..727ae2c7f2 100644 --- a/src/Lucene.Net.TestFramework/Search/ShardSearchingTestBase.cs +++ b/src/Lucene.Net.TestFramework/Search/ShardSearchingTestBase.cs @@ -72,6 +72,8 @@ protected SearcherExpiredException(SerializationInfo info, StreamingContext cont /// /// Base test class for simulating distributed search across multiple shards. /// + // LUCENENET specific - Specify to unzip the line file docs + [UseTempLineDocsFile] public abstract class ShardSearchingTestBase : LuceneTestCase #if TESTFRAMEWORK_XUNIT , Xunit.IClassFixture @@ -83,13 +85,6 @@ public ShardSearchingTestBase(BeforeAfterClass beforeAfter) #else { #endif - // LUCENENET specific - Specify to unzip the line file docs - public override void BeforeClass() - { - UseTempLineDocsFile = true; - base.BeforeClass(); - } - // LUCENENET specific - de-nested SearcherExpiredException diff --git a/src/Lucene.Net.TestFramework/Support/Util/LuceneTestFrameworkInitializer.cs b/src/Lucene.Net.TestFramework/Support/Util/LuceneTestFrameworkInitializer.cs index 9842e31a52..f1ac4429b9 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/LuceneTestFrameworkInitializer.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/LuceneTestFrameworkInitializer.cs @@ -126,12 +126,16 @@ public abstract class LuceneTestFrameworkInitializer private IPostingsFormatFactory postingsFormatFactory; private IConfigurationFactory configurationFactory; + private readonly AbstractBeforeAfterRule useTempLineDocsFileRule; + protected LuceneTestFrameworkInitializer() { codecFactory = new TestCodecFactory(); docValuesFormatFactory = new TestDocValuesFormatFactory(); postingsFormatFactory = new TestPostingsFormatFactory(); configurationFactory = new TestConfigurationFactory(); + + useTempLineDocsFileRule = new UseTempLineDocsFileRule(); } // LUCENENET specific factory methods to scan the test framework for codecs/docvaluesformats/postingsformats only once @@ -220,11 +224,7 @@ internal void DoInitialize() /// internal void DoTestFrameworkSetUp() { - // If we can cleanup, unzip the LineDocsFile 1 time for the entire test run, - // which will dramatically improve performance. - var temp = LineFileDocs.MaybeCreateTempFile(removeAfterClass: false); - if (null != temp) - LuceneTestCase.TestLineDocsFile = temp; + useTempLineDocsFileRule.Before(); try { @@ -253,18 +253,7 @@ internal void DoTestFrameworkTearDown() throw RuntimeException.Create($"An exception occurred during TestFrameworkTearDown:\n{ex}", ex); } - // Cleanup our LineDocsFile and reset LuceneTestCase back to its original state. - var originalLineDocsFile = SystemProperties.GetProperty("tests:linedocsfile", LuceneTestCase.DEFAULT_LINE_DOCS_FILE); - if (!originalLineDocsFile.Equals(LuceneTestCase.TestLineDocsFile)) - { - try - { - if (!string.IsNullOrEmpty(LuceneTestCase.TestLineDocsFile) && File.Exists(LuceneTestCase.TestLineDocsFile)) - File.Delete(LuceneTestCase.TestLineDocsFile); - } - catch { } - LuceneTestCase.TestLineDocsFile = originalLineDocsFile; - } + useTempLineDocsFileRule.After(); } /// diff --git a/src/Lucene.Net.TestFramework/Support/Util/UseTempLineDocsFileAttribute.cs b/src/Lucene.Net.TestFramework/Support/Util/UseTempLineDocsFileAttribute.cs new file mode 100644 index 0000000000..fe805bbdbe --- /dev/null +++ b/src/Lucene.Net.TestFramework/Support/Util/UseTempLineDocsFileAttribute.cs @@ -0,0 +1,30 @@ +using System; + +namespace Lucene.Net.Util +{ + /* + * 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. + */ + + /// + /// Specifies to extract the to a temp directory and use the same file for the entire assembly. + /// The file path can be obtained from the to property. + /// + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = false, Inherited = true)] + public class UseTempLineDocsFileAttribute : System.Attribute + { + } +} diff --git a/src/Lucene.Net.TestFramework/Support/Util/UseTempLineDocsFileRule.cs b/src/Lucene.Net.TestFramework/Support/Util/UseTempLineDocsFileRule.cs new file mode 100644 index 0000000000..3f87cb82f1 --- /dev/null +++ b/src/Lucene.Net.TestFramework/Support/Util/UseTempLineDocsFileRule.cs @@ -0,0 +1,84 @@ +using System.IO; +using System.Reflection; + +namespace Lucene.Net.Util +{ + /* + * 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. + */ + + /// + /// LUCENENET specific rule for extracting the LineFileDocs file (or embedded resource) only once + /// per assembly if the . + /// + internal class UseTempLineDocsFileRule : AbstractBeforeAfterRule + { + private bool lineFileDocsExtracted; + + private bool IsEnabled() + { + bool enabled = false; + Assembly assembly = RandomizedContext.CurrentContext.CurrentTestAssembly; + + if (assembly.HasAttribute(inherit: true)) + enabled = true; + else + { + foreach (var type in assembly.GetTypes()) + { + if (!type.IsClass || type.IsAbstract) + continue; + if (type.HasAttribute(inherit: true)) + { + enabled = true; + break; + } + } + } + return enabled; + } + + public override void Before() + { + if (IsEnabled()) + { + // If we can cleanup, unzip the LineDocsFile 1 time for the entire test run, + // which will dramatically improve performance. + var temp = LineFileDocs.MaybeCreateTempFile(removeAfterClass: false); + if (null != temp) + { + lineFileDocsExtracted = true; + LuceneTestCase.TestLineDocsFile = temp; + } + } + } + + public override void After() + { + if (lineFileDocsExtracted) + { + // Cleanup our LineDocsFile and reset LuceneTestCase back to its original state. + try + { + if (!string.IsNullOrEmpty(LuceneTestCase.TestLineDocsFile) && File.Exists(LuceneTestCase.TestLineDocsFile)) + File.Delete(LuceneTestCase.TestLineDocsFile); + } + catch { } + LuceneTestCase.TestLineDocsFile = SystemProperties.GetProperty("tests:linedocsfile", LuceneTestCase.DEFAULT_LINE_DOCS_FILE); + } + } + } +} diff --git a/src/Lucene.Net.TestFramework/Util/LineFileDocs.cs b/src/Lucene.Net.TestFramework/Util/LineFileDocs.cs index 79057d358a..209cac0d26 100644 --- a/src/Lucene.Net.TestFramework/Util/LineFileDocs.cs +++ b/src/Lucene.Net.TestFramework/Util/LineFileDocs.cs @@ -60,12 +60,12 @@ public LineFileDocs(Random random, string path, bool useDocValues) } public LineFileDocs(Random random) - : this(random, LuceneTestCase.TempLineDocsFile ?? LuceneTestCase.TestLineDocsFile, true) + : this(random, LuceneTestCase.TestLineDocsFile, true) { } public LineFileDocs(Random random, bool useDocValues) - : this(random, LuceneTestCase.TempLineDocsFile ?? LuceneTestCase.TestLineDocsFile, useDocValues) + : this(random, LuceneTestCase.TestLineDocsFile, useDocValues) { } diff --git a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs index 0d552ec793..43a1cbdf05 100644 --- a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs +++ b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs @@ -694,19 +694,9 @@ private static bool LoadLeaveTemorary() public static string TestLineDocsFile // LUCENENET specific - changed from field to property, and renamed { get => TestProperties.TestLineDocsFile; - set => TestProperties.TestLineDocsFile = value; + internal set => TestProperties.TestLineDocsFile = value; } - /// - /// The line file used by to override if it exists (points to an unzipped file). - // LUCENENET specific - used to unzip the LineDocsFile at the class level so we don't have to do it in every test. - internal static string TempLineDocsFile { get; set; } - - /// - /// Whether to use the temporary line docs file. This should be set to true when a subclass requires . - // LUCENENET specific - used to unzip the LineDocsFile at the class level so we don't have to do it in every test. - internal bool UseTempLineDocsFile { get; set; } - /// /// Whether or not tests should run. public static bool TestNightly => TestProperties.TestNightly; // LUCENENET specific - changed from field to property, and renamed @@ -1111,11 +1101,6 @@ public static void BeforeClass(Microsoft.VisualStudio.TestTools.UnitTesting.Test // LUCENENET TODO: Scan for a custom attribute and setup ordering to // initialize data from this class to the top class - - if (UseTempLineDocsFile) - { - TempLineDocsFile = LineFileDocs.MaybeCreateTempFile(); - } } catch (Exception ex) { @@ -1145,11 +1130,6 @@ public virtual void BeforeClass() // LUCENENET: Generate the info once so it can be printed out for each test codecType = ClassEnvRule.codec.GetType().Name; similarityName = ClassEnvRule.similarity.ToString(); - - if (UseTempLineDocsFile) - { - TempLineDocsFile = LineFileDocs.MaybeCreateTempFile(); - } } catch (Exception ex) { @@ -1170,10 +1150,6 @@ public static void ClassCleanup() { ClassEnvRule.After(); CleanupTemporaryFiles(); - if (UseTempLineDocsFile) - { - TempLineDocsFile = null; - } } catch (Exception ex) { @@ -1199,10 +1175,6 @@ public virtual void AfterClass() { ClassEnvRule.After(); CleanupTemporaryFiles(); - if (UseTempLineDocsFile) - { - TempLineDocsFile = null; - } } catch (Exception ex) { diff --git a/src/Lucene.Net.Tests.Analysis.Common/Startup.cs b/src/Lucene.Net.Tests.Analysis.Common/Startup.cs index 7cdc10dc3c..07033b1b05 100644 --- a/src/Lucene.Net.Tests.Analysis.Common/Startup.cs +++ b/src/Lucene.Net.Tests.Analysis.Common/Startup.cs @@ -18,9 +18,6 @@ using Lucene.Net.Util; -// LUCENENET: With this class in place, the LineDocsFile is exported to a temp file 1 time per test run, -// which makes the tests run faster. This class is simply here to facilitate the call from NUnit which -// would not occur if it were not here. public class Startup : LuceneTestFrameworkInitializer { protected override void TestFrameworkSetUp() diff --git a/src/Lucene.Net.Tests.Analysis.Kuromoji/Startup.cs b/src/Lucene.Net.Tests.Analysis.Kuromoji/Startup.cs deleted file mode 100644 index 9233cacfe6..0000000000 --- a/src/Lucene.Net.Tests.Analysis.Kuromoji/Startup.cs +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 Lucene.Net.Util; - -// LUCENENET: With this class in place, the LineDocsFile is exported to a temp file 1 time per test run, -// which makes the tests run faster. This class is simply here to facilitate the call from NUnit which -// would not occur if it were not here. -public class Startup : LuceneTestFrameworkInitializer -{ -} diff --git a/src/Lucene.Net.Tests.Analysis.Morfologik/Startup.cs b/src/Lucene.Net.Tests.Analysis.Morfologik/Startup.cs deleted file mode 100644 index 9233cacfe6..0000000000 --- a/src/Lucene.Net.Tests.Analysis.Morfologik/Startup.cs +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 Lucene.Net.Util; - -// LUCENENET: With this class in place, the LineDocsFile is exported to a temp file 1 time per test run, -// which makes the tests run faster. This class is simply here to facilitate the call from NUnit which -// would not occur if it were not here. -public class Startup : LuceneTestFrameworkInitializer -{ -} diff --git a/src/Lucene.Net.Tests.Analysis.OpenNLP/Startup.cs b/src/Lucene.Net.Tests.Analysis.OpenNLP/Startup.cs deleted file mode 100644 index 9233cacfe6..0000000000 --- a/src/Lucene.Net.Tests.Analysis.OpenNLP/Startup.cs +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 Lucene.Net.Util; - -// LUCENENET: With this class in place, the LineDocsFile is exported to a temp file 1 time per test run, -// which makes the tests run faster. This class is simply here to facilitate the call from NUnit which -// would not occur if it were not here. -public class Startup : LuceneTestFrameworkInitializer -{ -} diff --git a/src/Lucene.Net.Tests.Analysis.Phonetic/Startup.cs b/src/Lucene.Net.Tests.Analysis.Phonetic/Startup.cs deleted file mode 100644 index 9233cacfe6..0000000000 --- a/src/Lucene.Net.Tests.Analysis.Phonetic/Startup.cs +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 Lucene.Net.Util; - -// LUCENENET: With this class in place, the LineDocsFile is exported to a temp file 1 time per test run, -// which makes the tests run faster. This class is simply here to facilitate the call from NUnit which -// would not occur if it were not here. -public class Startup : LuceneTestFrameworkInitializer -{ -} diff --git a/src/Lucene.Net.Tests.Analysis.SmartCn/Startup.cs b/src/Lucene.Net.Tests.Analysis.SmartCn/Startup.cs deleted file mode 100644 index 9233cacfe6..0000000000 --- a/src/Lucene.Net.Tests.Analysis.SmartCn/Startup.cs +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 Lucene.Net.Util; - -// LUCENENET: With this class in place, the LineDocsFile is exported to a temp file 1 time per test run, -// which makes the tests run faster. This class is simply here to facilitate the call from NUnit which -// would not occur if it were not here. -public class Startup : LuceneTestFrameworkInitializer -{ -} diff --git a/src/Lucene.Net.Tests.Analysis.Stempel/Startup.cs b/src/Lucene.Net.Tests.Analysis.Stempel/Startup.cs deleted file mode 100644 index 9233cacfe6..0000000000 --- a/src/Lucene.Net.Tests.Analysis.Stempel/Startup.cs +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 Lucene.Net.Util; - -// LUCENENET: With this class in place, the LineDocsFile is exported to a temp file 1 time per test run, -// which makes the tests run faster. This class is simply here to facilitate the call from NUnit which -// would not occur if it were not here. -public class Startup : LuceneTestFrameworkInitializer -{ -} diff --git a/src/Lucene.Net.Tests._A-D/Startup.cs b/src/Lucene.Net.Tests._A-D/Startup.cs deleted file mode 100644 index 9233cacfe6..0000000000 --- a/src/Lucene.Net.Tests._A-D/Startup.cs +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 Lucene.Net.Util; - -// LUCENENET: With this class in place, the LineDocsFile is exported to a temp file 1 time per test run, -// which makes the tests run faster. This class is simply here to facilitate the call from NUnit which -// would not occur if it were not here. -public class Startup : LuceneTestFrameworkInitializer -{ -} diff --git a/src/Lucene.Net.Tests._I-J/Startup.cs b/src/Lucene.Net.Tests._I-J/Startup.cs deleted file mode 100644 index 9233cacfe6..0000000000 --- a/src/Lucene.Net.Tests._I-J/Startup.cs +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 Lucene.Net.Util; - -// LUCENENET: With this class in place, the LineDocsFile is exported to a temp file 1 time per test run, -// which makes the tests run faster. This class is simply here to facilitate the call from NUnit which -// would not occur if it were not here. -public class Startup : LuceneTestFrameworkInitializer -{ -} diff --git a/src/Lucene.Net.Tests._J-S/Startup.cs b/src/Lucene.Net.Tests._J-S/Startup.cs deleted file mode 100644 index 9233cacfe6..0000000000 --- a/src/Lucene.Net.Tests._J-S/Startup.cs +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 Lucene.Net.Util; - -// LUCENENET: With this class in place, the LineDocsFile is exported to a temp file 1 time per test run, -// which makes the tests run faster. This class is simply here to facilitate the call from NUnit which -// would not occur if it were not here. -public class Startup : LuceneTestFrameworkInitializer -{ -} diff --git a/src/Lucene.Net.Tests/Codecs/Lucene40/TestReuseDocsEnum.cs b/src/Lucene.Net.Tests/Codecs/Lucene40/TestReuseDocsEnum.cs index 9ceac85115..78fd27b7df 100644 --- a/src/Lucene.Net.Tests/Codecs/Lucene40/TestReuseDocsEnum.cs +++ b/src/Lucene.Net.Tests/Codecs/Lucene40/TestReuseDocsEnum.cs @@ -1,6 +1,7 @@ using J2N.Runtime.CompilerServices; using Lucene.Net.Index; using Lucene.Net.Index.Extensions; +using Lucene.Net.Util; using NUnit.Framework; using RandomizedTesting.Generators; using System; @@ -46,13 +47,13 @@ namespace Lucene.Net.Codecs.Lucene40 using TestUtil = Lucene.Net.Util.TestUtil; // TODO: really this should be in BaseTestPF or somewhere else? useful test! - [TestFixture] + // LUCENENET specific - Specify to unzip the line file docs + [UseTempLineDocsFile] public class TestReuseDocsEnum : LuceneTestCase { [OneTimeSetUp] public override void BeforeClass() { - UseTempLineDocsFile = true; base.BeforeClass(); OldFormatImpersonationIsActive = true; // explicitly instantiates ancient codec } diff --git a/src/Lucene.Net.Tests/Index/TestFlushByRamOrCountsPolicy.cs b/src/Lucene.Net.Tests/Index/TestFlushByRamOrCountsPolicy.cs index 9029af1ff9..7340d4fa90 100644 --- a/src/Lucene.Net.Tests/Index/TestFlushByRamOrCountsPolicy.cs +++ b/src/Lucene.Net.Tests/Index/TestFlushByRamOrCountsPolicy.cs @@ -9,6 +9,7 @@ using JCG = J2N.Collections.Generic; using Assert = Lucene.Net.TestFramework.Assert; using Console = Lucene.Net.Util.SystemConsole; +using Lucene.Net.Util; namespace Lucene.Net.Index { @@ -37,9 +38,10 @@ namespace Lucene.Net.Index using MockDirectoryWrapper = Lucene.Net.Store.MockDirectoryWrapper; using TestUtil = Lucene.Net.Util.TestUtil; using ThreadState = Lucene.Net.Index.DocumentsWriterPerThreadPool.ThreadState; - - [TestFixture] + + // LUCENENET specific - Specify to unzip the line file docs + [UseTempLineDocsFile] [Timeout(900000)] public class TestFlushByRamOrCountsPolicy : LuceneTestCase { @@ -49,7 +51,6 @@ public class TestFlushByRamOrCountsPolicy : LuceneTestCase [OneTimeSetUp] public override void BeforeClass() { - UseTempLineDocsFile = true; // LUCENENET specific - Specify to unzip the line file docs base.BeforeClass(); lineDocFile = new LineFileDocs(Random, DefaultCodecSupportsDocValues); } From a6b88fda70d9d296ec6dc4be947fe659e5222cea Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Fri, 19 Nov 2021 03:43:04 +0700 Subject: [PATCH 17/28] Lucene.Net.TestFramework.Util.TestRuleSetupAndRestoreClassEnv: Allow NUnit's [SetCulture] attribute to be used to set the culture --- .../Util/TestRuleSetupAndRestoreClassEnv.cs | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs b/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs index 714c7763ed..f3b90cb8ad 100644 --- a/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs +++ b/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs @@ -281,19 +281,35 @@ public override void Before() Codec.Default = codec; // Initialize locale/ timezone. - // LUCENENET: Accept either tests:culture or tests:locale (culture preferred). - string testLocale = SystemProperties.GetProperty("tests:culture", SystemProperties.GetProperty("tests:locale", "random")); // LUCENENET specific - reformatted with : + string testTimeZone = SystemProperties.GetProperty("tests:timezone", "random"); // LUCENENET specific - reformatted with : - // Always pick a random one for consistency (whether tests.locale was specified or not). - savedLocale = CultureInfo.CurrentCulture; + // LUCENENET: We need to ensure our random generator stays consistent here so we can repeat the session exactly, + // so always call this whether the return value is useful or not. CultureInfo randomLocale = LuceneTestCase.RandomCulture(random); - locale = testLocale.Equals("random", StringComparison.Ordinal) ? randomLocale : LuceneTestCase.CultureForName(testLocale); + + // LUCENENET: Allow NUnit properties to set the culture and respect that culture over our random one. + // Note that SetCultureAttribute may also be on the test method, but we don't need to deal with that here. + if (targetClass.Assembly.HasAttribute(inherit: true) + || targetClass.HasAttribute(inherit: true)) + { + locale = CultureInfo.CurrentCulture; + } + else + { + // LUCENENET: Accept either tests:culture or tests:locale (culture preferred). + string testLocale = SystemProperties.GetProperty("tests:culture", SystemProperties.GetProperty("tests:locale", "random")); // LUCENENET specific - reformatted with : + + // Always pick a random one for consistency (whether tests.locale was specified or not). + savedLocale = CultureInfo.CurrentCulture; + + locale = testLocale.Equals("random", StringComparison.Ordinal) ? randomLocale : LuceneTestCase.CultureForName(testLocale); #if FEATURE_CULTUREINFO_CURRENTCULTURE_SETTER - CultureInfo.CurrentCulture = locale; + CultureInfo.CurrentCulture = locale; #else - Thread.CurrentThread.CurrentCulture = locale; + Thread.CurrentThread.CurrentCulture = locale; #endif + } // TimeZone.getDefault will set user.timezone to the default timezone of the user's locale. // So store the original property value and restore it at end. From b512b1eedd0fe9da3272b085712b863c12e0577d Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Fri, 19 Nov 2021 03:46:46 +0700 Subject: [PATCH 18/28] Lucene.Net.TestFramework.Util.LuceneTestCase: Updated repeat instructions to use NUnit's SetCulture attribute, since NUnit property attributes are not currently read by the test framework --- src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs index 43a1cbdf05..1a0d4eed94 100644 --- a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs +++ b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs @@ -1033,9 +1033,9 @@ public virtual void TearDown() $"Option 1:\n\n" + $" Apply the following assembly-level attributes:\n\n" + $"[assembly: Lucene.Net.Util.RandomSeed({RandomizedContext.CurrentContext.RandomSeedAsHex}L)]\n" + - $"[assembly: NUnit.Framework.Property(\"tests:culture\", \"{Thread.CurrentThread.CurrentCulture.Name}\")]\n\n\n" + + $"[assembly: NUnit.Framework.SetCulture(\"{Thread.CurrentThread.CurrentCulture.Name}\")]\n\n" + $"Option 2:\n\n" + - $" Use the following .runsettings file.\n\n" + + $" Use the following .runsettings file:\n\n" + $"\n" + $" \n" + $" \n" + From 6446eb6fd5349af4c17713694426ff554a237b0b Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Thu, 18 Nov 2021 07:04:06 +0700 Subject: [PATCH 19/28] Lucene.Net.TestFramework.Analysis.BaseTokenStreamTestCase: Switched from using System.Random to J2N.Randomizer --- .../Analysis/BaseTokenStreamTestCase.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs b/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs index c989051f89..1218c403f4 100644 --- a/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs +++ b/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs @@ -710,7 +710,7 @@ public override void Run() if (latch != null) latch.Wait(); // see the part in checkRandomData where it replays the same text again // to verify reproducability/reuse: hopefully this would catch thread hazards. - CheckRandomData(new Random((int)seed), a, iterations, maxWordLength, useCharFilter, simple, offsetsAreCorrect, iw); + CheckRandomData(new J2N.Randomizer(seed), a, iterations, maxWordLength, useCharFilter, simple, offsetsAreCorrect, iw); success = true; } catch (Exception e) when (e.IsException()) // LUCENENET TODO: This catch block can be removed because Rethrow.rethrow() simply does what its name says, but need to get rid of the FirstException functionality and fix ThreadJob so it re-throws ThreadInterruptedExcepetion first. @@ -749,7 +749,7 @@ public void CheckRandomData(Random random, Analyzer a, int iterations, int maxWo #endif { CheckResetException(a, "best effort"); - long seed = random.Next(); + long seed = random.NextInt64(); bool useCharFilter = random.NextBoolean(); Directory dir = null; RandomIndexWriter iw = null; @@ -764,13 +764,13 @@ public void CheckRandomData(Random random, Analyzer a, int iterations, int maxWo #if FEATURE_INSTANCE_TESTDATA_INITIALIZATION this, #endif - new Random((int)seed), dir, a); + new J2N.Randomizer(seed), dir, a); } bool success = false; try { - CheckRandomData(new Random((int)seed), a, iterations, maxWordLength, useCharFilter, simple, offsetsAreCorrect, iw); + CheckRandomData(new J2N.Randomizer(seed), a, iterations, maxWordLength, useCharFilter, simple, offsetsAreCorrect, iw); // now test with multiple threads: note we do the EXACT same thing we did before in each thread, // so this should only really fail from another thread if its an actual thread problem int numThreads = TestUtil.NextInt32(random, 2, 4); @@ -780,7 +780,7 @@ public void CheckRandomData(Random random, Analyzer a, int iterations, int maxWo { threads[i] = new AnalysisThread(seed, startingGun, a, iterations, maxWordLength, useCharFilter, simple, offsetsAreCorrect, iw); } - + foreach (AnalysisThread thread in threads) { thread.Start(); @@ -943,7 +943,7 @@ private static void CheckRandomData(Random random, Analyzer a, int iterations, i { // TODO: really we should pass a random seed to // checkAnalysisConsistency then print it here too: - Console.Error.WriteLine("TEST FAIL: useCharFilter=" + useCharFilter + " text='" + Escape(text) + "'"); + Console.Error.WriteLine($"TEST FAIL (iteration {i}): useCharFilter=" + useCharFilter + " text='" + Escape(text) + "'"); throw; // LUCENENET: CA2200: Rethrow to preserve stack details (https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2200-rethrow-to-preserve-stack-details) } } @@ -1196,8 +1196,8 @@ private static void CheckAnalysisConsistency(Random random, Analyzer a, bool use } reader = new StringReader(text); - long seed = random.Next(); - random = new Random((int)seed); + long seed = random.NextInt64(); + random = new J2N.Randomizer(seed); if (random.Next(30) == 7) { if (Verbose) @@ -1243,7 +1243,7 @@ private static void CheckAnalysisConsistency(Random random, Analyzer a, bool use if (field != null) { reader = new StringReader(text); - random = new Random((int)seed); + random = new J2N.Randomizer(seed); if (random.Next(30) == 7) { if (Verbose) From acb5efdb700be4806578ba9c395f3477f9d50d7a Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Fri, 19 Nov 2021 04:08:46 +0700 Subject: [PATCH 20/28] Lucene.Net.Tests.Analysis.Common.Analysis.Core.TestRandomChains: Switched from using System.Random to J2N.Randomizer. Also added feature to export a random SynonymMap as source code. --- .../Analysis/Core/TestRandomChains.cs | 85 ++++++++++++++----- 1 file changed, 63 insertions(+), 22 deletions(-) diff --git a/src/Lucene.Net.Tests.Analysis.Common/Analysis/Core/TestRandomChains.cs b/src/Lucene.Net.Tests.Analysis.Common/Analysis/Core/TestRandomChains.cs index 72d80e6c47..704ff4ab01 100644 --- a/src/Lucene.Net.Tests.Analysis.Common/Analysis/Core/TestRandomChains.cs +++ b/src/Lucene.Net.Tests.Analysis.Common/Analysis/Core/TestRandomChains.cs @@ -1,5 +1,6 @@ // Lucene version compatibility level 4.8.1 +using J2N; using J2N.Runtime.CompilerServices; using J2N.Text; using Lucene.Net.Analysis.CharFilters; @@ -25,6 +26,7 @@ using Lucene.Net.Util; using Lucene.Net.Util.Automaton; using NUnit.Framework; +using RandomizedTesting.Generators; using System; using System.Collections.Generic; using System.Globalization; @@ -108,7 +110,7 @@ static TestRandomChains() typeof(CrankyTokenFilter), // Not broken: we forcefully add this, so we shouldn't // also randomly pick it: - typeof(ValidatingTokenFilter) + typeof(ValidatingTokenFilter), }) { foreach (ConstructorInfo ctor in c.GetConstructors()) @@ -692,16 +694,45 @@ public object Create(Random random) private class SynonymMapArgProducer : IArgProducer { + // LUCENENET specific: Added functionality to export the source code of the builder with all of the escaped text. + // + // USAGE + // + // This is intended to be used in conjunction with a fixed random seed after a failure. You should confirm + // first that you can reproduce a test failure before exporting. + // + // 1. Change exportSourceCode to true to export the source code + // 2. Update the exportPath to a folder on your local system. Note that it won't automatically create directories. + // The {0} token will be replaced with a map number. Normally, you would get the contents of the highest number, + // which is the last successful map that occurred before there was a test failure. + + private StringBuilder sb = new StringBuilder(); + private static bool exportSourceCode = false; // Change to enable source code export. + private static string exportPath = @"F:\synonym-map-{0}.txt"; // Change as necessary for your system. + private int mapCount = 0; public object Create(Random random) { - SynonymMap.Builder b = new SynonymMap.Builder(random.nextBoolean()); + if (exportSourceCode) sb.Clear(); + bool dedup = random.NextBoolean(); + SynonymMap.Builder b = new SynonymMap.Builder(dedup); + + if (exportSourceCode) sb.AppendLine($"SynonymMap.Builder b = new SynonymMap.Builder({dedup.ToString().ToLowerInvariant()});"); + int numEntries = AtLeast(10); for (int j = 0; j < numEntries; j++) { - AddSyn(b, RandomNonEmptyString(random), RandomNonEmptyString(random), random.nextBoolean()); + AddSyn(b, RandomNonEmptyString(random), RandomNonEmptyString(random), random.NextBoolean()); } try { + if (exportSourceCode) + { + sb.AppendLine("SynonymMap synonymMap = b.Build();"); + mapCount++; + string path = string.Format(exportPath, mapCount); + File.WriteAllText(path, sb.ToString()); + } + return b.Build(); } catch (Exception ex) when (ex.IsException()) @@ -713,11 +744,24 @@ public object Create(Random random) } } + private static readonly Regex whiteSpace = new Regex(" +", RegexOptions.Compiled); + private void AddSyn(SynonymMap.Builder b, string input, string output, bool keepOrig) { - b.Add(new CharsRef(input.Replace(" +", "\u0000")), - new CharsRef(output.Replace(" +", "\u0000")), - keepOrig); + string inputNoSpaces = whiteSpace.Replace(input, "\u0000"); + string outputNoSpaces = whiteSpace.Replace(output, "\u0000"); + + if (exportSourceCode) + { + sb.AppendLine($"b.Add(new CharsRef(\"{Escape(inputNoSpaces)}\"),"); + sb.AppendLine($" new CharsRef(\"{Escape(outputNoSpaces)}\"),"); + sb.AppendLine($" {keepOrig.ToString().ToLowerInvariant()});"); + sb.AppendLine(); + } + + b.Add(new CharsRef(inputNoSpaces), + new CharsRef(outputNoSpaces), + keepOrig); } private string RandomNonEmptyString(Random random) @@ -814,10 +858,9 @@ static object[] NewFilterArgs(Random random, TokenStream stream, Type[] paramTyp private class MockRandomAnalyzer : Analyzer { - internal readonly int seed; - + internal readonly long seed; - public MockRandomAnalyzer(int seed) + public MockRandomAnalyzer(long seed) { this.seed = seed; } @@ -827,7 +870,7 @@ public bool OffsetsAreCorrect get { // TODO: can we not do the full chain here!? - Random random = new Random(seed); + Random random = new Randomizer(seed); TokenizerSpec tokenizerSpec = NewTokenizer(random, new StringReader("")); TokenFilterSpec filterSpec = NewFilterChain(random, tokenizerSpec.tokenizer, tokenizerSpec.offsetsAreCorrect); return filterSpec.offsetsAreCorrect; @@ -836,7 +879,7 @@ public bool OffsetsAreCorrect protected internal override TokenStreamComponents CreateComponents(string fieldName, TextReader reader) { - Random random = new Random(seed); + Random random = new Randomizer(seed); TokenizerSpec tokenizerSpec = NewTokenizer(random, reader); //System.out.println("seed=" + seed + ",create tokenizer=" + tokenizerSpec.toString); TokenFilterSpec filterSpec = NewFilterChain(random, tokenizerSpec.tokenizer, tokenizerSpec.offsetsAreCorrect); @@ -846,7 +889,7 @@ protected internal override TokenStreamComponents CreateComponents(string fieldN protected internal override TextReader InitReader(string fieldName, TextReader reader) { - Random random = new Random(seed); + Random random = new Randomizer(seed); CharFilterSpec charfilterspec = NewCharFilterChain(random, reader); return charfilterspec.reader; } @@ -854,13 +897,13 @@ protected internal override TextReader InitReader(string fieldName, TextReader r public override string ToString() { - Random random = new Random(seed); + Random random = new Randomizer(seed); StringBuilder sb = new StringBuilder(); CharFilterSpec charFilterSpec = NewCharFilterChain(random, new StringReader("")); sb.Append("\ncharfilters="); sb.Append(charFilterSpec.toString); // intentional: initReader gets its own separate random - random = new Random(seed); + random = new Randomizer(seed); TokenizerSpec tokenizerSpec = NewTokenizer(random, charFilterSpec.reader); sb.Append("\n"); sb.Append("tokenizer="); @@ -1119,15 +1162,14 @@ internal class CharFilterSpec } [Test] - [Slow] - [AwaitsFix(BugUrl = "https://github.com/apache/lucenenet/issues/269")] // LUCENENET TODO: this test occasionally fails + [AwaitsFix(BugUrl = "https://github.com/apache/lucenenet/issues/271#issuecomment-973005744")] // LUCENENET TODO: this test occasionally fails public void TestRandomChains_() { int numIterations = AtLeast(20); Random random = Random; for (int i = 0; i < numIterations; i++) { - MockRandomAnalyzer a = new MockRandomAnalyzer(random.Next()); + MockRandomAnalyzer a = new MockRandomAnalyzer(random.NextInt64()); if (Verbose) { Console.WriteLine("Creating random analyzer:" + a); @@ -1139,7 +1181,7 @@ public void TestRandomChains_() } catch (Exception e) when (e.IsThrowable()) { - Console.WriteLine("Exception from random analyzer: " + a); + Console.WriteLine("Exception from random analyzer (iteration {i}): " + a); throw; // LUCENENET: CA2200: Rethrow to preserve stack details (https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2200-rethrow-to-preserve-stack-details) } } @@ -1147,15 +1189,14 @@ public void TestRandomChains_() // we might regret this decision... [Test] - [Slow] - [AwaitsFix(BugUrl = "https://github.com/apache/lucenenet/issues/269")] // LUCENENET TODO: this test occasionally fails + [AwaitsFix(BugUrl = "https://github.com/apache/lucenenet/issues/271#issuecomment-973005744")] // LUCENENET TODO: this test occasionally fails public void TestRandomChainsWithLargeStrings() { int numIterations = AtLeast(20); Random random = Random; for (int i = 0; i < numIterations; i++) { - MockRandomAnalyzer a = new MockRandomAnalyzer(random.Next()); + MockRandomAnalyzer a = new MockRandomAnalyzer(random.NextInt64()); if (Verbose) { Console.WriteLine("Creating random analyzer:" + a); @@ -1167,7 +1208,7 @@ public void TestRandomChainsWithLargeStrings() } catch (Exception e) when (e.IsThrowable()) { - Console.WriteLine("Exception from random analyzer: " + a); + Console.WriteLine($"Exception from random analyzer (iteration {i}): " + a); throw; // LUCENENET: CA2200: Rethrow to preserve stack details (https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2200-rethrow-to-preserve-stack-details) } } From cbf0fd08dea7eeed463e9ba826d83c2ae4c0fe98 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Sun, 7 Nov 2021 01:23:21 +0700 Subject: [PATCH 21/28] azure-pipelines.yml: set maximumAllowedFailures to 0 --- azure-pipelines.yml | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 63cd18f94e..e929bbc2b2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -336,17 +336,17 @@ stages: osName: 'Windows' imageName: 'windows-2019' maximumParallelJobs: 8 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build Linux: osName: 'Linux' imageName: 'ubuntu-latest' maximumParallelJobs: 7 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build macOS: osName: 'macOS' imageName: 'macOS-latest' maximumParallelJobs: 7 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build displayName: 'Test net6.0,x64 on' pool: vmImage: $(imageName) @@ -370,17 +370,17 @@ stages: osName: 'Windows' imageName: 'windows-2019' maximumParallelJobs: 8 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build Linux: osName: 'Linux' imageName: 'ubuntu-latest' maximumParallelJobs: 7 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build macOS: osName: 'macOS' imageName: 'macOS-latest' maximumParallelJobs: 7 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build displayName: 'Test net6.0,x86 on' pool: vmImage: $(imageName) @@ -404,17 +404,17 @@ stages: osName: 'Windows' imageName: 'windows-2019' maximumParallelJobs: 8 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build Linux: osName: 'Linux' imageName: 'ubuntu-latest' maximumParallelJobs: 7 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build macOS: osName: 'macOS' imageName: 'macOS-latest' maximumParallelJobs: 7 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build displayName: 'Test net5.0,x64 on' pool: vmImage: $(imageName) @@ -438,17 +438,17 @@ stages: osName: 'Windows' imageName: 'windows-2019' maximumParallelJobs: 8 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build Linux: osName: 'Linux' imageName: 'ubuntu-latest' maximumParallelJobs: 7 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build macOS: osName: 'macOS' imageName: 'macOS-latest' maximumParallelJobs: 7 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build displayName: 'Test net5.0,x86 on' pool: vmImage: $(imageName) @@ -472,17 +472,17 @@ stages: osName: 'Windows' imageName: 'windows-2019' maximumParallelJobs: 8 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build Linux: osName: 'Linux' imageName: 'ubuntu-latest' maximumParallelJobs: 7 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build macOS: osName: 'macOS' imageName: 'macOS-10.15' # macOS-latest should not be used here because we may get OS Darwin 19.6.0, which isn't supported fully (we get PlatformNotSupportedException). maximumParallelJobs: 7 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build displayName: 'Test netcoreapp3.1,x64 on' pool: vmImage: $(imageName) @@ -506,17 +506,17 @@ stages: osName: 'Windows' imageName: 'windows-2019' maximumParallelJobs: 8 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build Linux: osName: 'Linux' imageName: 'ubuntu-latest' maximumParallelJobs: 7 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build macOS: osName: 'macOS' imageName: 'macOS-10.15' # macOS-latest should not be used here because we may get OS Darwin 19.6.0, which isn't supported fully (we get PlatformNotSupportedException). maximumParallelJobs: 7 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build displayName: 'Test netcoreapp3.1,x86 on' pool: vmImage: $(imageName) @@ -540,7 +540,7 @@ stages: osName: 'Windows' imageName: 'windows-2019' maximumParallelJobs: 8 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build displayName: 'Test net461,x64 on' pool: vmImage: $(imageName) @@ -564,7 +564,7 @@ stages: osName: 'Windows' imageName: 'windows-2019' maximumParallelJobs: 8 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build displayName: 'Test net461,x86 on' pool: vmImage: $(imageName) @@ -595,7 +595,7 @@ stages: nugetArtifactName: '$(NuGetArtifactName)' testResultsArtifactName: '$(TestResultsArtifactName)' maximumParallelJobs: 8 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build - job: Test_net48_Windows_x86 # Only run Nightly or if explicitly enabled with RunX86Tests condition: and(succeeded(), ne(variables['RunTests'], 'false'), or(eq(variables['IsNightly'], 'true'), eq(variables['RunX86Tests'], 'true'))) @@ -612,7 +612,7 @@ stages: nugetArtifactName: '$(NuGetArtifactName)' testResultsArtifactName: '$(TestResultsArtifactName)' maximumParallelJobs: 8 - maximumAllowedFailures: 2 # Maximum allowed failures for a successful build + maximumAllowedFailures: 0 # Maximum allowed failures for a successful build - stage: Publish_Stage From 987a63f70cdecc68815886f296a3789007d84676 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Fri, 19 Nov 2021 07:34:58 +0700 Subject: [PATCH 22/28] SWEEP: Marked all of the latest test failures with [AwaitsFix] attribute and an issue URL. --- src/Lucene.Net.Tests/Index/TestDuelingCodecs.cs | 3 ++- src/Lucene.Net.Tests/Index/TestIndexWriter.cs | 1 + src/Lucene.Net.Tests/Search/TestSimpleExplanations.cs | 5 ++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Lucene.Net.Tests/Index/TestDuelingCodecs.cs b/src/Lucene.Net.Tests/Index/TestDuelingCodecs.cs index 4710120f47..69e5d5e88b 100644 --- a/src/Lucene.Net.Tests/Index/TestDuelingCodecs.cs +++ b/src/Lucene.Net.Tests/Index/TestDuelingCodecs.cs @@ -1,4 +1,4 @@ -using J2N.Text; +using J2N.Text; using Lucene.Net.Documents; using Lucene.Net.Index.Extensions; using NUnit.Framework; @@ -171,6 +171,7 @@ public static void CreateRandomIndex(int numdocs, RandomIndexWriter writer, long /// checks the two indexes are equivalent /// [Test] + [AwaitsFix(BugUrl = "https://github.com/apache/lucenenet/issues/545")] // LUCENENET TODO: This test occasionally fails public virtual void TestEquals() { AssertReaderEquals(info, leftReader, rightReader); diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs index b4cda089c7..42726ebbf4 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs @@ -1516,6 +1516,7 @@ public virtual void TestThreadInterruptDeadlock() /// testThreadInterruptDeadlock but with 2 indexer threads [Test] [Slow] + [AwaitsFix(BugUrl = "https://github.com/apache/lucenenet/issues/544")] // LUCENENET TODO: This test occasionally fails public virtual void TestTwoThreadsInterruptDeadlock() { IndexerThreadInterrupt t1 = new IndexerThreadInterrupt(this); diff --git a/src/Lucene.Net.Tests/Search/TestSimpleExplanations.cs b/src/Lucene.Net.Tests/Search/TestSimpleExplanations.cs index 0e36eb852a..f325a03af5 100644 --- a/src/Lucene.Net.Tests/Search/TestSimpleExplanations.cs +++ b/src/Lucene.Net.Tests/Search/TestSimpleExplanations.cs @@ -1,4 +1,4 @@ -using NUnit.Framework; +using NUnit.Framework; namespace Lucene.Net.Search { @@ -274,6 +274,9 @@ public virtual void TestDMQ7() } [Test] +#if NETFRAMEWORK + [AwaitsFix(BugUrl = "https://github.com/apache/lucenenet/issues/546")] // LUCENENET TODO: This test fails on x86 .NET Framework in Release mode only +#endif public virtual void TestDMQ8() { DisjunctionMaxQuery q = new DisjunctionMaxQuery(0.5f); From 38c026e1341567b6372f5c71ba5b30629eb7779d Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Fri, 19 Nov 2021 07:37:31 +0700 Subject: [PATCH 23/28] Lucene.Net.Classification.KNearestNeighborClassifierTest::TestPerformance(): Ignore in net461, since it runs a bit slow in that environment. --- .../KNearestNeighborClassifierTest.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Lucene.Net.Tests.Classification/KNearestNeighborClassifierTest.cs b/src/Lucene.Net.Tests.Classification/KNearestNeighborClassifierTest.cs index 87f3f4f6e7..93238f9ebc 100644 --- a/src/Lucene.Net.Tests.Classification/KNearestNeighborClassifierTest.cs +++ b/src/Lucene.Net.Tests.Classification/KNearestNeighborClassifierTest.cs @@ -1,4 +1,4 @@ -/* +/* * 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. @@ -43,10 +43,14 @@ public void TestBasicUsageWithQuery() CheckCorrectClassification(new KNearestNeighborClassifier(1), TECHNOLOGY_INPUT, TECHNOLOGY_RESULT, new MockAnalyzer(Random), textFieldName, categoryFieldName, new TermQuery(new Term(textFieldName, "it"))); } +#if NET461 + [Ignore("LUCENENET: We don't care if this fails on .NET Standard 2.0/.NET 4.6.1.")] +#endif [Test] public void TestPerformance() { CheckPerformance(new KNearestNeighborClassifier(100), new MockAnalyzer(Random), categoryFieldName); } + } } \ No newline at end of file From f133e82b52ef876e8441d0813307d09772bbb604 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Sun, 21 Nov 2021 13:00:07 +0700 Subject: [PATCH 24/28] Lucene.Net.Index.TestIndexWriter::TestThreadInterruptDeadlock(): Added [AwaitsFix] attribute, as this is sometimes failing (see #544) --- src/Lucene.Net.Tests/Index/TestIndexWriter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs index 42726ebbf4..8243b63dfa 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs @@ -1476,6 +1476,7 @@ private string GetToStringFrom(Exception exception) [Test] [Slow] + [AwaitsFix(BugUrl = "https://github.com/apache/lucenenet/issues/544")] // LUCENENET TODO: This test occasionally fails public virtual void TestThreadInterruptDeadlock() { IndexerThreadInterrupt t = new IndexerThreadInterrupt(this); From 8ded01ec1495d47a7df9aefe5ca554e42b4fa488 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Sat, 20 Nov 2021 02:46:09 +0700 Subject: [PATCH 25/28] Lucene.Net.Tests.Join.TestJoinUtil::TestMultiValueRandomJoin(): Added [AwaitsFix] attribute, since this test is failing on .NET Framework x86 in Release mode. --- src/Lucene.Net.Tests.Join/Support/TestJoinUtil.cs | 3 +++ src/Lucene.Net.Tests.Join/TestJoinUtil.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/Lucene.Net.Tests.Join/Support/TestJoinUtil.cs b/src/Lucene.Net.Tests.Join/Support/TestJoinUtil.cs index dea1ecf15e..f4526fe713 100644 --- a/src/Lucene.Net.Tests.Join/Support/TestJoinUtil.cs +++ b/src/Lucene.Net.Tests.Join/Support/TestJoinUtil.cs @@ -393,6 +393,9 @@ public void TestSingleValueRandomJoin() [Test] // [Slow] // LUCENENET specific - Not slow in .NET +#if NETFRAMEWORK + [AwaitsFix(BugUrl = "https://github.com/apache/lucenenet/issues/549")] // LUCENENET TODO: This test fails on x86 .NET Framework in Release mode only +#endif public void TestMultiValueRandomJoin() // this test really takes more time, that is why the number of iterations are smaller. { diff --git a/src/Lucene.Net.Tests.Join/TestJoinUtil.cs b/src/Lucene.Net.Tests.Join/TestJoinUtil.cs index 104bcb5ada..50d60f221b 100644 --- a/src/Lucene.Net.Tests.Join/TestJoinUtil.cs +++ b/src/Lucene.Net.Tests.Join/TestJoinUtil.cs @@ -390,6 +390,9 @@ public void TestSingleValueRandomJoin() [Test] // [Slow] // LUCENENET specific - Not slow in .NET +#if NETFRAMEWORK + [AwaitsFix(BugUrl = "https://github.com/apache/lucenenet/issues/549")] // LUCENENET TODO: This test fails on x86 .NET Framework in Release mode only +#endif public void TestMultiValueRandomJoin() // this test really takes more time, that is why the number of iterations are smaller. { From 6d3c85912337a96a5f01fba815062eedd635d18e Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Sun, 21 Nov 2021 10:33:38 +0700 Subject: [PATCH 26/28] SWEEP: Changed all test instances of System.Random to J2N.Randomizer. APIs remain as System.Random, since J2N.Randomizer is a subclass. --- .../ByTask/Feeds/DocMaker.cs | 2 +- .../ByTask/Feeds/RandomFacetSource.cs | 2 +- .../ByTask/Feeds/SortableSingleDocSource.cs | 2 +- .../ByTask/Feeds/SpatialDocMaker.cs | 2 +- .../JS/JavascriptCompiler.cs | 2 +- .../Suggest/UnsortedInputIterator.cs | 2 +- .../Analysis/BaseTokenStreamTestCase.cs | 2 +- .../Analysis/CrankyTokenFilter.cs | 2 +- .../Analysis/MockAnalyzer.cs | 3 +- .../Analysis/MockGraphTokenFilter.cs | 7 ++-- .../Analysis/MockHoleInjectingTokenFilter.cs | 9 +++-- .../MockRandomLookaheadTokenFilter.cs | 13 +++--- .../Analysis/MockTokenizer.cs | 5 ++- .../MockRandom/MockRandomPostingsFormat.cs | 40 +++++++++---------- .../Index/BasePostingsFormatTestCase.cs | 4 +- .../Index/MockRandomMergePolicy.cs | 3 +- .../Index/RandomCodec.cs | 5 ++- .../Index/RandomIndexWriter.cs | 4 +- .../ThreadedIndexingAndSearchingTestCase.cs | 2 +- .../Search/AssertingIndexSearcher.cs | 8 ++-- .../Search/AssertingQuery.cs | 18 ++++----- .../Search/AssertingWeight.cs | 6 +-- .../Search/QueryUtils.cs | 6 +-- .../Store/BaseDirectoryTestCase.cs | 6 +-- .../Store/MockDirectoryWrapper.cs | 2 +- .../Util/LuceneTestFrameworkInitializer.cs | 2 +- .../Util/LuceneTestCase.cs | 6 +-- .../Util/TestRuleSetupAndRestoreClassEnv.cs | 2 +- .../Analysis/Core/TestRandomChains.cs | 2 +- .../Pattern/TestPatternReplaceCharFilter.cs | 7 ++-- .../ClassificationTestBase.cs | 2 +- .../SlowRAMDirectory.cs | 2 +- .../Index/Sorter/TestEarlyTermination.cs | 6 +-- .../Index/Sorter/TestSortingMergePolicy.cs | 8 ++-- .../Util/Fst/TestFSTsMisc.cs | 5 ++- .../CommonTermsQueryTest.cs | 6 +-- .../IndexInputStreamTest.cs | 16 ++++---- .../Suggest/Fst/WFSTCompletionTest.cs | 5 ++- .../Suggest/LookupBenchmarkTest.cs | 2 +- .../Lucene41/TestBlockPostingsFormat3.cs | 4 +- .../PerField/TestPerFieldDocValuesFormat.cs | 5 ++- .../PerField/TestPerFieldPostingsFormat.cs | 5 ++- .../Index/TestBagOfPositions.cs | 2 +- .../Index/TestCompoundFile.cs | 8 ++-- .../Index/TestDocValuesWithThreads.cs | 2 +- .../Index/TestDuelingCodecs.cs | 15 +++---- src/Lucene.Net.Tests/Index/TestIndexWriter.cs | 2 +- .../Index/TestIndexWriterExceptions.cs | 4 +- .../Index/TestIndexWriterReader.cs | 2 +- .../Index/TestNRTReaderWithThreads.cs | 3 +- .../Index/TestPerSegmentDeletes.cs | 3 +- .../Index/TestRollingUpdates.cs | 2 +- .../Index/TestStressIndexing2.cs | 2 +- src/Lucene.Net.Tests/Index/TestStressNRT.cs | 4 +- .../Index/TestTermVectorsReader.cs | 2 +- src/Lucene.Net.Tests/Index/TestTermdocPerf.cs | 3 +- src/Lucene.Net.Tests/Index/TestTermsEnum.cs | 2 +- src/Lucene.Net.Tests/Search/TestBoolean2.cs | 2 +- .../Search/TestBooleanMinShouldMatch.cs | 8 ++-- .../Search/TestLiveFieldValues.cs | 3 +- .../Search/TestSloppyPhraseQuery2.cs | 9 +++-- src/Lucene.Net.Tests/Search/TestSortRandom.cs | 2 +- src/Lucene.Net.Tests/Util/Fst/Test2BFST.cs | 23 ++++++----- src/Lucene.Net.Tests/Util/Fst/TestFSTs.cs | 8 ++-- .../Util/Packed/TestPackedInts.cs | 2 +- src/Lucene.Net.Tests/Util/Test2BPagedBytes.cs | 4 +- src/Lucene.Net.Tests/Util/TestFixedBitSet.cs | 2 +- src/Lucene.Net.Tests/Util/TestLongBitSet.cs | 2 +- .../Util/TestMergedIterator.cs | 5 ++- src/Lucene.Net.Tests/Util/TestOpenBitSet.cs | 2 +- src/Lucene.Net.Tests/Util/TestSetOnce.cs | 5 +-- .../Util/TestWeakIdentityMap.cs | 2 +- 72 files changed, 195 insertions(+), 182 deletions(-) diff --git a/src/Lucene.Net.Benchmark/ByTask/Feeds/DocMaker.cs b/src/Lucene.Net.Benchmark/ByTask/Feeds/DocMaker.cs index 6b2e7af628..9983a21b0b 100644 --- a/src/Lucene.Net.Benchmark/ByTask/Feeds/DocMaker.cs +++ b/src/Lucene.Net.Benchmark/ByTask/Feeds/DocMaker.cs @@ -505,7 +505,7 @@ public virtual void SetConfig(Config config, ContentSource source) updateDocIDLimit = config.Get("doc.random.id.limit", -1); if (updateDocIDLimit != -1) { - r = new Random(179); + r = new J2N.Randomizer(179); } } } diff --git a/src/Lucene.Net.Benchmark/ByTask/Feeds/RandomFacetSource.cs b/src/Lucene.Net.Benchmark/ByTask/Feeds/RandomFacetSource.cs index 467180c2a3..cd42950a61 100644 --- a/src/Lucene.Net.Benchmark/ByTask/Feeds/RandomFacetSource.cs +++ b/src/Lucene.Net.Benchmark/ByTask/Feeds/RandomFacetSource.cs @@ -95,7 +95,7 @@ protected override void Dispose(bool disposing) public override void SetConfig(Config config) { base.SetConfig(config); - random = new Random(config.Get("rand.seed", 13)); + random = new J2N.Randomizer(config.Get("rand.seed", 13)); maxDocFacets = config.Get("max.doc.facets", 10); maxDims = config.Get("max.doc.facets.dims", 5); maxFacetDepth = config.Get("max.facet.depth", 3); diff --git a/src/Lucene.Net.Benchmark/ByTask/Feeds/SortableSingleDocSource.cs b/src/Lucene.Net.Benchmark/ByTask/Feeds/SortableSingleDocSource.cs index 1c91e81588..ac7a40c9dc 100644 --- a/src/Lucene.Net.Benchmark/ByTask/Feeds/SortableSingleDocSource.cs +++ b/src/Lucene.Net.Benchmark/ByTask/Feeds/SortableSingleDocSource.cs @@ -109,7 +109,7 @@ public override void SetConfig(Config config) { base.SetConfig(config); sortRange = config.Get("sort.rng", 20000); - r = new Random(config.Get("rand.seed", 13)); + r = new J2N.Randomizer(config.Get("rand.seed", 13)); } } } diff --git a/src/Lucene.Net.Benchmark/ByTask/Feeds/SpatialDocMaker.cs b/src/Lucene.Net.Benchmark/ByTask/Feeds/SpatialDocMaker.cs index a54aa333c7..df8e4cc84b 100644 --- a/src/Lucene.Net.Benchmark/ByTask/Feeds/SpatialDocMaker.cs +++ b/src/Lucene.Net.Benchmark/ByTask/Feeds/SpatialDocMaker.cs @@ -219,7 +219,7 @@ public IShape Convert(IShape shape) double radius = radiusDegrees; if (plusMinus > 0.0) { - Random random = new Random(point.GetHashCode());//use hashCode so it's reproducibly random + Random random = new J2N.Randomizer(point.GetHashCode());//use hashCode so it's reproducibly random radius += random.NextDouble() * 2 * plusMinus - plusMinus; radius = Math.Abs(radius);//can happen if configured plusMinus > radiusDegrees } diff --git a/src/Lucene.Net.Expressions/JS/JavascriptCompiler.cs b/src/Lucene.Net.Expressions/JS/JavascriptCompiler.cs index d2f444350c..afdcf7393a 100644 --- a/src/Lucene.Net.Expressions/JS/JavascriptCompiler.cs +++ b/src/Lucene.Net.Expressions/JS/JavascriptCompiler.cs @@ -194,7 +194,7 @@ private Expression CompileExpression() private void BeginCompile() { - var assemblyName = new AssemblyName("Lucene.Net.Expressions.Dynamic" + new Random().Next()); + var assemblyName = new AssemblyName("Lucene.Net.Expressions.Dynamic" + Math.Abs(new J2N.Randomizer().Next())); asmBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect); modBuilder = asmBuilder.DefineDynamicModule(assemblyName.Name + ".dll"); diff --git a/src/Lucene.Net.Suggest/Suggest/UnsortedInputIterator.cs b/src/Lucene.Net.Suggest/Suggest/UnsortedInputIterator.cs index 6b1707fda6..ec60223d09 100644 --- a/src/Lucene.Net.Suggest/Suggest/UnsortedInputIterator.cs +++ b/src/Lucene.Net.Suggest/Suggest/UnsortedInputIterator.cs @@ -43,7 +43,7 @@ public UnsortedInputEnumerator(IInputEnumerator source) : base(source) { ords = new int[m_entries.Length]; - Random random = new Random(); + Random random = new J2N.Randomizer(); for (int i = 0; i < ords.Length; i++) { ords[i] = i; diff --git a/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs b/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs index 1218c403f4..67e0fc0eb0 100644 --- a/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs +++ b/src/Lucene.Net.TestFramework/Analysis/BaseTokenStreamTestCase.cs @@ -1292,7 +1292,7 @@ internal static int[] ToIntArray(IList list) /// Returns a random impl public static AttributeFactory NewAttributeFactory(Random random) { - switch (random.nextInt(2)) + switch (random.Next(2)) { case 0: return Token.TOKEN_ATTRIBUTE_FACTORY; diff --git a/src/Lucene.Net.TestFramework/Analysis/CrankyTokenFilter.cs b/src/Lucene.Net.TestFramework/Analysis/CrankyTokenFilter.cs index 3fec138918..d1c29d78e0 100644 --- a/src/Lucene.Net.TestFramework/Analysis/CrankyTokenFilter.cs +++ b/src/Lucene.Net.TestFramework/Analysis/CrankyTokenFilter.cs @@ -62,7 +62,7 @@ public override void End() public override void Reset() { base.Reset(); - thingToDo = random.nextInt(100); + thingToDo = random.Next(100); if (thingToDo == 2 && random.nextBoolean()) { throw new IOException("Fake IOException from TokenStream.Reset()"); diff --git a/src/Lucene.Net.TestFramework/Analysis/MockAnalyzer.cs b/src/Lucene.Net.TestFramework/Analysis/MockAnalyzer.cs index cae547aa99..567ec5c7c8 100644 --- a/src/Lucene.Net.TestFramework/Analysis/MockAnalyzer.cs +++ b/src/Lucene.Net.TestFramework/Analysis/MockAnalyzer.cs @@ -1,6 +1,7 @@ using Lucene.Net.Support.Threading; using Lucene.Net.Util; using Lucene.Net.Util.Automaton; +using RandomizedTesting.Generators; using System; using System.Collections.Generic; using System.IO; @@ -72,7 +73,7 @@ public MockAnalyzer(Random random, CharacterRunAutomaton runAutomaton, bool lowe : base(PER_FIELD_REUSE_STRATEGY) { // TODO: this should be solved in a different way; Random should not be shared (!). - this.random = new Random(random.Next()); + this.random = new J2N.Randomizer(random.NextInt64()); this.runAutomaton = runAutomaton; this.lowerCase = lowerCase; this.filter = filter; diff --git a/src/Lucene.Net.TestFramework/Analysis/MockGraphTokenFilter.cs b/src/Lucene.Net.TestFramework/Analysis/MockGraphTokenFilter.cs index c248a72a5b..318f19768a 100644 --- a/src/Lucene.Net.TestFramework/Analysis/MockGraphTokenFilter.cs +++ b/src/Lucene.Net.TestFramework/Analysis/MockGraphTokenFilter.cs @@ -1,5 +1,6 @@ using Lucene.Net.Analysis.TokenAttributes; using Lucene.Net.Util; +using RandomizedTesting.Generators; using System; using Console = Lucene.Net.Util.SystemConsole; @@ -38,13 +39,13 @@ public sealed class MockGraphTokenFilter : LookaheadTokenFilter(); } @@ -111,7 +112,7 @@ public override void Reset() // NOTE: must be "deterministically random" because // baseTokenStreamTestCase pulls tokens twice on the // same input and asserts they are the same: - this.random = new Random(seed); + this.random = new J2N.Randomizer(seed); } protected override void Dispose(bool disposing) diff --git a/src/Lucene.Net.TestFramework/Analysis/MockHoleInjectingTokenFilter.cs b/src/Lucene.Net.TestFramework/Analysis/MockHoleInjectingTokenFilter.cs index ed5e4b54b9..16c4d54e67 100644 --- a/src/Lucene.Net.TestFramework/Analysis/MockHoleInjectingTokenFilter.cs +++ b/src/Lucene.Net.TestFramework/Analysis/MockHoleInjectingTokenFilter.cs @@ -1,5 +1,6 @@ -using Lucene.Net.Analysis.TokenAttributes; +using Lucene.Net.Analysis.TokenAttributes; using Lucene.Net.Util; +using RandomizedTesting.Generators; using System; namespace Lucene.Net.Analysis @@ -30,7 +31,7 @@ namespace Lucene.Net.Analysis /// public sealed class MockHoleInjectingTokenFilter : TokenFilter { - private readonly int randomSeed; // LUCENENET specific - changed from long to int, as in .NET a System.Random seed is int + private readonly long randomSeed; private Random random; private readonly IPositionIncrementAttribute posIncAtt; private readonly IPositionLengthAttribute posLenAtt; @@ -40,7 +41,7 @@ public sealed class MockHoleInjectingTokenFilter : TokenFilter public MockHoleInjectingTokenFilter(Random random, TokenStream @in) : base(@in) { - randomSeed = random.Next(); + randomSeed = random.NextInt64(); posIncAtt = AddAttribute(); posLenAtt = AddAttribute(); } @@ -48,7 +49,7 @@ public MockHoleInjectingTokenFilter(Random random, TokenStream @in) public override void Reset() { base.Reset(); - random = new Random(randomSeed); + random = new J2N.Randomizer(randomSeed); maxPos = -1; pos = -1; } diff --git a/src/Lucene.Net.TestFramework/Analysis/MockRandomLookaheadTokenFilter.cs b/src/Lucene.Net.TestFramework/Analysis/MockRandomLookaheadTokenFilter.cs index 3aa0339674..1bdcd7be8d 100644 --- a/src/Lucene.Net.TestFramework/Analysis/MockRandomLookaheadTokenFilter.cs +++ b/src/Lucene.Net.TestFramework/Analysis/MockRandomLookaheadTokenFilter.cs @@ -1,4 +1,5 @@ -using Lucene.Net.Analysis.TokenAttributes; +using Lucene.Net.Analysis.TokenAttributes; +using RandomizedTesting.Generators; using System; using System.Threading; using Console = Lucene.Net.Util.SystemConsole; @@ -28,15 +29,15 @@ namespace Lucene.Net.Analysis public sealed class MockRandomLookaheadTokenFilter : LookaheadTokenFilter { private readonly ICharTermAttribute termAtt; - private Random random; // LUCENENET: not readonly to reset seed later - private readonly int seed; // LUCENENET specific: changed to int, since .NET random seed is int, not long + private readonly J2N.Randomizer random; + private readonly long seed; public MockRandomLookaheadTokenFilter(Random random, TokenStream @in) : base(@in) { this.termAtt = AddAttribute(); - this.seed = random.Next(); - this.random = new Random(seed); + this.seed = random.NextInt64(); + this.random = new J2N.Randomizer(seed); } protected override Position NewPosition() @@ -110,7 +111,7 @@ public override bool IncrementToken() public override void Reset() { base.Reset(); - random = new Random(seed); + random.Seed = seed; } } } \ No newline at end of file diff --git a/src/Lucene.Net.TestFramework/Analysis/MockTokenizer.cs b/src/Lucene.Net.TestFramework/Analysis/MockTokenizer.cs index 05eda512ba..3b72dbbd95 100644 --- a/src/Lucene.Net.TestFramework/Analysis/MockTokenizer.cs +++ b/src/Lucene.Net.TestFramework/Analysis/MockTokenizer.cs @@ -1,7 +1,8 @@ -using J2N; +using J2N; using Lucene.Net.Analysis.TokenAttributes; using Lucene.Net.Diagnostics; using Lucene.Net.Util; +using RandomizedTesting.Generators; using System; using System.Globalization; using System.IO; @@ -99,7 +100,7 @@ public class MockTokenizer : Tokenizer private bool enableChecks = true; // evil: but we don't change the behavior with this random, we only switch up how we read - private readonly Random random = new Random(LuceneTestCase.Random.Next() /*RandomizedContext.Current.Random.nextLong()*/); // LUCENENET TODO: Random seed synchronization + private readonly Random random = new J2N.Randomizer(LuceneTestCase.Random.NextInt64()); // LUCENENET: LuceneTestCase.Random instead of using RandomizedContext.CurrentContext.RandomGenerator, since the latter could be null public MockTokenizer(AttributeFactory factory, TextReader input, CharacterRunAutomaton runAutomaton, bool lowerCase, int maxTokenLength) : base(factory, input) diff --git a/src/Lucene.Net.TestFramework/Codecs/MockRandom/MockRandomPostingsFormat.cs b/src/Lucene.Net.TestFramework/Codecs/MockRandom/MockRandomPostingsFormat.cs index 15deb78581..12e6d14f58 100644 --- a/src/Lucene.Net.TestFramework/Codecs/MockRandom/MockRandomPostingsFormat.cs +++ b/src/Lucene.Net.TestFramework/Codecs/MockRandom/MockRandomPostingsFormat.cs @@ -43,7 +43,7 @@ public sealed class MockRandomPostingsFormat : PostingsFormat private readonly Random seedRandom; private const string SEED_EXT = "sd"; - private class RandomAnonymousClassHelper : Random + private class RandomAnonymousClassHelper : J2N.Randomizer { public RandomAnonymousClassHelper() : base(0) @@ -67,7 +67,7 @@ public MockRandomPostingsFormat(Random random) if (random == null) this.seedRandom = new RandomAnonymousClassHelper(); else - this.seedRandom = new Random(random.Next()); + this.seedRandom = new J2N.Randomizer(random.NextInt64()); } // Chooses random IntStreamFactory depending on file's extension @@ -78,7 +78,7 @@ private class MockInt32StreamFactory : Int32StreamFactory public MockInt32StreamFactory(Random random) { - salt = random.nextInt(); + salt = random.Next(); delegates.Add(new MockSingleInt32Factory()); int blockSize = TestUtil.NextInt32(random, 1, 2000); delegates.Add(new MockFixedInt32BlockPostingsFormat.MockInt32Factory(blockSize)); @@ -98,7 +98,7 @@ public override Int32IndexInput OpenInput(Directory dir, string fileName, IOCont { // Must only use extension, because IW.addIndexes can // rename segment! - Int32StreamFactory f = delegates[(Math.Abs(salt ^ GetExtension(fileName).GetHashCode())) % delegates.size()]; + Int32StreamFactory f = delegates[(Math.Abs(salt ^ GetExtension(fileName).GetHashCode())) % delegates.Count]; if (LuceneTestCase.Verbose) { Console.WriteLine("MockRandomCodec: read using int factory " + f + " from fileName=" + fileName); @@ -108,7 +108,7 @@ public override Int32IndexInput OpenInput(Directory dir, string fileName, IOCont public override Int32IndexOutput CreateOutput(Directory dir, string fileName, IOContext context) { - Int32StreamFactory f = delegates[(Math.Abs(salt ^ GetExtension(fileName).GetHashCode())) % delegates.size()]; + Int32StreamFactory f = delegates[(Math.Abs(salt ^ GetExtension(fileName).GetHashCode())) % delegates.Count]; if (LuceneTestCase.Verbose) { Console.WriteLine("MockRandomCodec: write using int factory " + f + " to fileName=" + fileName); @@ -121,9 +121,9 @@ private class IndexTermSelectorAnonymousClass : VariableGapTermsIndexWriter.Inde { private readonly Random rand; private readonly int gap; - public IndexTermSelectorAnonymousClass(int seed, int gap) + public IndexTermSelectorAnonymousClass(long seed, int gap) { - rand = new Random(seed); + rand = new J2N.Randomizer(seed); this.gap = gap; } public override bool IsIndexTerm(BytesRef term, TermStats stats) @@ -158,7 +158,7 @@ public override FieldsConsumer FieldsConsumer(SegmentWriteState state) Console.WriteLine("MockRandomCodec: skipInterval=" + skipInterval); } - long seed = seedRandom.nextLong(); + long seed = seedRandom.NextInt64(); if (LuceneTestCase.Verbose) { @@ -176,9 +176,9 @@ public override FieldsConsumer FieldsConsumer(SegmentWriteState state) @out.Dispose(); } - Random random = new Random((int)seed); + Random random = new J2N.Randomizer(seed); - random.nextInt(); // consume a random for buffersize + random.Next(); // consume a random for buffersize PostingsWriterBase postingsWriter; if (random.nextBoolean()) @@ -195,7 +195,7 @@ public override FieldsConsumer FieldsConsumer(SegmentWriteState state) postingsWriter = new Lucene41PostingsWriter(state, skipInterval); } - if (random.nextBoolean()) + if (random.NextBoolean()) { int totTFCutoff = TestUtil.NextInt32(random, 1, 20); if (LuceneTestCase.Verbose) @@ -206,7 +206,7 @@ public override FieldsConsumer FieldsConsumer(SegmentWriteState state) } FieldsConsumer fields; - int t1 = random.nextInt(4); + int t1 = random.Next(4); if (t1 == 0) { @@ -252,7 +252,7 @@ public override FieldsConsumer FieldsConsumer(SegmentWriteState state) // TODO: would be nice to allow 1 but this is very // slow to write int minTermsInBlock = TestUtil.NextInt32(random, 2, 100); - int maxTermsInBlock = Math.Max(2, (minTermsInBlock - 1) * 2 + random.nextInt(100)); + int maxTermsInBlock = Math.Max(2, (minTermsInBlock - 1) * 2 + random.Next(100)); bool success = false; try @@ -281,7 +281,7 @@ public override FieldsConsumer FieldsConsumer(SegmentWriteState state) TermsIndexWriterBase indexWriter; try { - if (random.nextBoolean()) + if (random.NextBoolean()) { state.TermIndexInterval = TestUtil.NextInt32(random, 1, 100); if (LuceneTestCase.Verbose) @@ -293,7 +293,7 @@ public override FieldsConsumer FieldsConsumer(SegmentWriteState state) else { VariableGapTermsIndexWriter.IndexTermSelector selector; - int n2 = random.nextInt(3); + int n2 = random.Next(3); if (n2 == 0) { int tii = TestUtil.NextInt32(random, 1, 100); @@ -311,7 +311,7 @@ public override FieldsConsumer FieldsConsumer(SegmentWriteState state) } else { - int seed2 = random.Next(); + long seed2 = random.NextInt64(); int gap = TestUtil.NextInt32(random, 2, 40); if (LuceneTestCase.Verbose) { @@ -368,7 +368,7 @@ public override FieldsProducer FieldsProducer(SegmentReadState state) } @in.Dispose(); - Random random = new Random((int)seed); + Random random = new J2N.Randomizer(seed); int readBufferSize = TestUtil.NextInt32(random, 1, 4096); if (LuceneTestCase.Verbose) @@ -378,7 +378,7 @@ public override FieldsProducer FieldsProducer(SegmentReadState state) PostingsReaderBase postingsReader; - if (random.nextBoolean()) + if (random.NextBoolean()) { if (LuceneTestCase.Verbose) { @@ -396,7 +396,7 @@ public override FieldsProducer FieldsProducer(SegmentReadState state) postingsReader = new Lucene41PostingsReader(state.Directory, state.FieldInfos, state.SegmentInfo, state.Context, state.SegmentSuffix); } - if (random.nextBoolean()) + if (random.NextBoolean()) { int totTFCutoff = TestUtil.NextInt32(random, 1, 20); if (LuceneTestCase.Verbose) @@ -407,7 +407,7 @@ public override FieldsProducer FieldsProducer(SegmentReadState state) } FieldsProducer fields; - int t1 = random.nextInt(4); + int t1 = random.Next(4); if (t1 == 0) { bool success = false; diff --git a/src/Lucene.Net.TestFramework/Index/BasePostingsFormatTestCase.cs b/src/Lucene.Net.TestFramework/Index/BasePostingsFormatTestCase.cs index 36d6814c78..1fe19d3d3f 100644 --- a/src/Lucene.Net.TestFramework/Index/BasePostingsFormatTestCase.cs +++ b/src/Lucene.Net.TestFramework/Index/BasePostingsFormatTestCase.cs @@ -142,8 +142,8 @@ private class SeedPostings : DocsAndPositionsEnum public SeedPostings(long seed, int minDocFreq, int maxDocFreq, IBits liveDocs, IndexOptions options) { - random = new Random((int)seed); - docRandom = new Random(random.Next()); + random = new J2N.Randomizer(seed); + docRandom = new J2N.Randomizer(random.NextInt64()); DocFreq = TestUtil.NextInt32(random, minDocFreq, maxDocFreq); this.liveDocs = liveDocs; diff --git a/src/Lucene.Net.TestFramework/Index/MockRandomMergePolicy.cs b/src/Lucene.Net.TestFramework/Index/MockRandomMergePolicy.cs index 3a811ffc1c..5f1fca62c6 100644 --- a/src/Lucene.Net.TestFramework/Index/MockRandomMergePolicy.cs +++ b/src/Lucene.Net.TestFramework/Index/MockRandomMergePolicy.cs @@ -1,6 +1,7 @@ using J2N.Collections.Generic.Extensions; using Lucene.Net.Diagnostics; using Lucene.Net.Util; +using RandomizedTesting.Generators; using System; using System.Collections.Generic; using JCG = J2N.Collections.Generic; @@ -35,7 +36,7 @@ public MockRandomMergePolicy(Random random) { // fork a private random, since we are called // unpredictably from threads: - this.random = new Random(random.Next()); + this.random = new J2N.Randomizer(random.NextInt64()); } public override MergeSpecification FindMerges(MergeTrigger mergeTrigger, SegmentInfos segmentInfos) diff --git a/src/Lucene.Net.TestFramework/Index/RandomCodec.cs b/src/Lucene.Net.TestFramework/Index/RandomCodec.cs index 0f41316b06..5b517aa889 100644 --- a/src/Lucene.Net.TestFramework/Index/RandomCodec.cs +++ b/src/Lucene.Net.TestFramework/Index/RandomCodec.cs @@ -16,6 +16,7 @@ using Lucene.Net.Diagnostics; using Lucene.Net.Support; using Lucene.Net.Util; +using RandomizedTesting.Generators; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -157,8 +158,8 @@ public RandomCodec(Random random, ISet avoidCodecs) new Lucene41WithOrds(), new SimpleTextPostingsFormat(), new AssertingPostingsFormat(), - new MemoryPostingsFormat(true, random.nextFloat()), - new MemoryPostingsFormat(false, random.nextFloat()) + new MemoryPostingsFormat(true, random.NextSingle()), + new MemoryPostingsFormat(false, random.NextSingle()) ); AddDocValues(avoidCodecs, diff --git a/src/Lucene.Net.TestFramework/Index/RandomIndexWriter.cs b/src/Lucene.Net.TestFramework/Index/RandomIndexWriter.cs index f34829a0f6..f9776fce76 100644 --- a/src/Lucene.Net.TestFramework/Index/RandomIndexWriter.cs +++ b/src/Lucene.Net.TestFramework/Index/RandomIndexWriter.cs @@ -49,7 +49,7 @@ public class RandomIndexWriter : IDisposable public static IndexWriter MockIndexWriter(Directory dir, IndexWriterConfig conf, Random r) { // Randomly calls Thread.yield so we mixup thread scheduling - Random random = new Random(r.Next()); + Random random = new J2N.Randomizer(r.NextInt64()); return MockIndexWriter(dir, conf, new TestPointAnonymousClass(random)); } @@ -162,7 +162,7 @@ public RandomIndexWriter(LuceneTestCase luceneTestCase, Random r, Directory dir, public RandomIndexWriter(Random r, Directory dir, IndexWriterConfig c) { // TODO: this should be solved in a different way; Random should not be shared (!). - this.r = new Random(r.Next()); + this.r = new J2N.Randomizer(r.NextInt64()); IndexWriter = MockIndexWriter(dir, c, r); flushAt = TestUtil.NextInt32(r, 10, 1000); codec = IndexWriter.Config.Codec; diff --git a/src/Lucene.Net.TestFramework/Index/ThreadedIndexingAndSearchingTestCase.cs b/src/Lucene.Net.TestFramework/Index/ThreadedIndexingAndSearchingTestCase.cs index cd909a5b32..7eeea39d73 100644 --- a/src/Lucene.Net.TestFramework/Index/ThreadedIndexingAndSearchingTestCase.cs +++ b/src/Lucene.Net.TestFramework/Index/ThreadedIndexingAndSearchingTestCase.cs @@ -565,7 +565,7 @@ public virtual void RunTest(string testName) long t0 = J2N.Time.NanoTime() / J2N.Time.MillisecondsPerNanosecond; // LUCENENET: Use NanoTime() rather than CurrentTimeMilliseconds() for more accurate/reliable results - Random random = new Random(Random.Next()); + Random random = new J2N.Randomizer(Random.NextInt64()); LineFileDocs docs = new LineFileDocs(random, DefaultCodecSupportsDocValues); DirectoryInfo tempDir = CreateTempDir(testName); m_dir = GetDirectory(NewMockFSDirectory(tempDir)); // some subclasses rely on this being MDW diff --git a/src/Lucene.Net.TestFramework/Search/AssertingIndexSearcher.cs b/src/Lucene.Net.TestFramework/Search/AssertingIndexSearcher.cs index 907a59b3a6..3ef8c83c4e 100644 --- a/src/Lucene.Net.TestFramework/Search/AssertingIndexSearcher.cs +++ b/src/Lucene.Net.TestFramework/Search/AssertingIndexSearcher.cs @@ -35,25 +35,25 @@ public class AssertingIndexSearcher : IndexSearcher public AssertingIndexSearcher(Random random, IndexReader r) : base(r) { - this.random = new Random(random.Next()); + this.random = new J2N.Randomizer(random.NextInt64()); } public AssertingIndexSearcher(Random random, IndexReaderContext context) : base(context) { - this.random = new Random(random.Next()); + this.random = new J2N.Randomizer(random.NextInt64()); } public AssertingIndexSearcher(Random random, IndexReader r, TaskScheduler ex) : base(r, ex) { - this.random = new Random(random.Next()); + this.random = new J2N.Randomizer(random.NextInt64()); } public AssertingIndexSearcher(Random random, IndexReaderContext context, TaskScheduler ex) : base(context, ex) { - this.random = new Random(random.Next()); + this.random = new J2N.Randomizer(random.NextInt64()); } /// diff --git a/src/Lucene.Net.TestFramework/Search/AssertingQuery.cs b/src/Lucene.Net.TestFramework/Search/AssertingQuery.cs index 6ac72595ea..e73bfb754a 100644 --- a/src/Lucene.Net.TestFramework/Search/AssertingQuery.cs +++ b/src/Lucene.Net.TestFramework/Search/AssertingQuery.cs @@ -1,4 +1,5 @@ -using Lucene.Net.Index; +using Lucene.Net.Index; +using RandomizedTesting.Generators; using System; using System.Collections.Generic; @@ -41,13 +42,12 @@ public AssertingQuery(Random random, Query @in) /// Wrap a query if necessary. public static Query Wrap(Random random, Query query) { - var aq = query as AssertingQuery; - return aq ?? new AssertingQuery(random, query); + return query is AssertingQuery ? query : new AssertingQuery(random, query); } public override Weight CreateWeight(IndexSearcher searcher) { - return AssertingWeight.Wrap(new Random(random.Next()), @in.CreateWeight(searcher)); + return AssertingWeight.Wrap(new J2N.Randomizer(random.NextInt64()), @in.CreateWeight(searcher)); } public override void ExtractTerms(ISet terms) @@ -74,7 +74,7 @@ public override int GetHashCode() public override object Clone() { - return Wrap(new Random(random.Next()), (Query)@in.Clone()); + return Wrap(new J2N.Randomizer(random.NextInt64()), (Query)@in.Clone()); } public override Query Rewrite(IndexReader reader) @@ -86,18 +86,14 @@ public override Query Rewrite(IndexReader reader) } else { - return Wrap(new Random(random.Next()), rewritten); + return Wrap(new J2N.Randomizer(random.NextInt64()), rewritten); } } public override float Boost { get => @in.Boost; - set - { - if (@in != null) - @in.Boost = value; - } + set => @in.Boost = value; } } } \ No newline at end of file diff --git a/src/Lucene.Net.TestFramework/Search/AssertingWeight.cs b/src/Lucene.Net.TestFramework/Search/AssertingWeight.cs index 98422b2698..3cf978c98a 100644 --- a/src/Lucene.Net.TestFramework/Search/AssertingWeight.cs +++ b/src/Lucene.Net.TestFramework/Search/AssertingWeight.cs @@ -58,7 +58,7 @@ public override Scorer GetScorer(AtomicReaderContext context, IBits acceptDocs) // if the caller asks for in-order scoring or if the weight does not support // out-of order scoring then collection will have to happen in-order. Scorer inScorer = @in.GetScorer(context, acceptDocs); - return AssertingScorer.Wrap(new Random(random.Next()), inScorer); + return AssertingScorer.Wrap(new J2N.Randomizer(random.NextInt64()), inScorer); } public override BulkScorer GetBulkScorer(AtomicReaderContext context, bool scoreDocsInOrder, IBits acceptDocs) @@ -75,7 +75,7 @@ public override BulkScorer GetBulkScorer(AtomicReaderContext context, bool score { // The incoming scorer already has a specialized // implementation for BulkScorer, so we should use it: - inScorer = AssertingBulkScorer.Wrap(new Random(random.Next()), inScorer); + inScorer = AssertingBulkScorer.Wrap(new J2N.Randomizer(random.NextInt64()), inScorer); } else if (random.NextBoolean()) { @@ -89,7 +89,7 @@ public override BulkScorer GetBulkScorer(AtomicReaderContext context, bool score // The caller claims it can handle out-of-order // docs; let's confirm that by pulling docs and // randomly shuffling them before collection: - inScorer = new AssertingBulkOutOfOrderScorer(new Random(random.Next()), inScorer); + inScorer = new AssertingBulkOutOfOrderScorer(new J2N.Randomizer(random.NextInt64()), inScorer); } return inScorer; } diff --git a/src/Lucene.Net.TestFramework/Search/QueryUtils.cs b/src/Lucene.Net.TestFramework/Search/QueryUtils.cs index 128f5e184b..94cc7c7347 100644 --- a/src/Lucene.Net.TestFramework/Search/QueryUtils.cs +++ b/src/Lucene.Net.TestFramework/Search/QueryUtils.cs @@ -281,9 +281,9 @@ private static IndexReader[] LoadEmptyReaders() // LUCENENET: Avoid static const try { emptyReaders[0] = new MultiReader(); - emptyReaders[4] = MakeEmptyIndex(new Random(0), 4); - emptyReaders[5] = MakeEmptyIndex(new Random(0), 5); - emptyReaders[7] = MakeEmptyIndex(new Random(0), 7); + emptyReaders[4] = MakeEmptyIndex(new J2N.Randomizer(0), 4); + emptyReaders[5] = MakeEmptyIndex(new J2N.Randomizer(0), 5); + emptyReaders[7] = MakeEmptyIndex(new J2N.Randomizer(0), 7); } catch (Exception ex) when (ex.IsIOException()) { diff --git a/src/Lucene.Net.TestFramework/Store/BaseDirectoryTestCase.cs b/src/Lucene.Net.TestFramework/Store/BaseDirectoryTestCase.cs index 552fe889ca..14ec3bd018 100644 --- a/src/Lucene.Net.TestFramework/Store/BaseDirectoryTestCase.cs +++ b/src/Lucene.Net.TestFramework/Store/BaseDirectoryTestCase.cs @@ -445,7 +445,7 @@ public virtual void TestVInt64() // LUCENENET: Renamed from TestVLong public virtual void TestChecksum() { CRC32 expected = new CRC32(); - int numBytes = Random.nextInt(20000); + int numBytes = Random.Next(20000); byte[] bytes = new byte[numBytes]; Random.NextBytes(bytes); expected.Update(bytes); @@ -494,7 +494,7 @@ public virtual void TestDetectClose() // { // try // { - // Random rnd = new Random(Random.Next() + 1); // LUCENENET: Changed from Long to Int32 + // Random rnd = new J2N.Randomizer(Random.NextInt64() + 1); // for (int i = 0, max = RandomInts.RandomInt32Between(Random, 500, 1000); i < max; i++) // { // string fileName = "file-" + i; @@ -534,7 +534,7 @@ public virtual void TestDetectClose() // { // try // { - // Random rnd = new Random(Random.Next()); // LUCENENET: Changed from Long to Int32 + // Random rnd = new J2N.Randomizer(Random.NextInt64()); // while (!stop.Get()) // { // string[] files = dir.ListAll() diff --git a/src/Lucene.Net.TestFramework/Store/MockDirectoryWrapper.cs b/src/Lucene.Net.TestFramework/Store/MockDirectoryWrapper.cs index ead37aa816..c34133419f 100644 --- a/src/Lucene.Net.TestFramework/Store/MockDirectoryWrapper.cs +++ b/src/Lucene.Net.TestFramework/Store/MockDirectoryWrapper.cs @@ -166,7 +166,7 @@ public MockDirectoryWrapper(Random random, Directory @delegate) // must make a private random since our methods are // called from different threads; else test failures may // not be reproducible from the original seed - this.randomState = new Random(random.Next()); + this.randomState = new J2N.Randomizer(random.NextInt64()); this.throttledOutput = new ThrottledIndexOutput(ThrottledIndexOutput.MBitsToBytes(40 + randomState.Next(10)), 5 + randomState.Next(5), null); // force wrapping of lockfactory this.m_lockFactory = new MockLockFactoryWrapper(this, @delegate.LockFactory); diff --git a/src/Lucene.Net.TestFramework/Support/Util/LuceneTestFrameworkInitializer.cs b/src/Lucene.Net.TestFramework/Support/Util/LuceneTestFrameworkInitializer.cs index f1ac4429b9..167d7f5177 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/LuceneTestFrameworkInitializer.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/LuceneTestFrameworkInitializer.cs @@ -272,7 +272,7 @@ internal void DoTestFrameworkTearDown() /// invocations are present or create a derivative local for millions of calls /// like this: /// - /// Random random = new Random(Random.Next()); + /// Random random = new J2N.Randomizer(Random.NextInt64()); /// // tight loop with many invocations. /// /// diff --git a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs index 1a0d4eed94..99b4d818b8 100644 --- a/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs +++ b/src/Lucene.Net.TestFramework/Util/LuceneTestCase.cs @@ -1203,7 +1203,7 @@ public virtual void AfterClass() /// invocations are present or create a derivative local for millions of calls /// like this: /// - /// Random random = new Random(Random.Next()); + /// Random random = new J2N.Randomizer(Random.NextInt64()); /// // tight loop with many invocations. /// /// @@ -1218,7 +1218,7 @@ public static Random Random Assert.Fail("LuceneTestCase.Random may only be used within tests/setup/teardown context in subclasses of LuceneTestCase or LuceneTestFrameworkInitializer."); return context.RandomGenerator; #else - return random ?? (random = new Random(/* LUCENENET TODO seed */)); + return random ?? (random = new J2N.Randomizer(/* LUCENENET TODO seed */)); //return RandomizedContext.Current.Random; #endif } @@ -1750,7 +1750,7 @@ public static AlcoholicMergePolicy NewAlcoholicMergePolicy(TimeZoneInfo timeZone public static AlcoholicMergePolicy NewAlcoholicMergePolicy(Random random, TimeZoneInfo timeZone) { - return new AlcoholicMergePolicy(timeZone, new Random(random.Next())); + return new AlcoholicMergePolicy(timeZone, new J2N.Randomizer(random.NextInt64())); } public static LogMergePolicy NewLogMergePolicy(Random random) diff --git a/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs b/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs index f3b90cb8ad..ac4ea921a7 100644 --- a/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs +++ b/src/Lucene.Net.TestFramework/Util/TestRuleSetupAndRestoreClassEnv.cs @@ -226,7 +226,7 @@ public override void Before() } else if ("MockRandom".Equals(LuceneTestCase.TestPostingsFormat, StringComparison.Ordinal)) { - format = new MockRandomPostingsFormat(new Random(random.Next())); + format = new MockRandomPostingsFormat(new J2N.Randomizer(random.NextInt64())); } else { diff --git a/src/Lucene.Net.Tests.Analysis.Common/Analysis/Core/TestRandomChains.cs b/src/Lucene.Net.Tests.Analysis.Common/Analysis/Core/TestRandomChains.cs index 704ff4ab01..a0a20b7f94 100644 --- a/src/Lucene.Net.Tests.Analysis.Common/Analysis/Core/TestRandomChains.cs +++ b/src/Lucene.Net.Tests.Analysis.Common/Analysis/Core/TestRandomChains.cs @@ -417,7 +417,7 @@ private class RandomArgProducer : IArgProducer { public object Create(Random random) { - return new Random(random.Next()); + return new J2N.Randomizer(random.NextInt64()); } } diff --git a/src/Lucene.Net.Tests.Analysis.Common/Analysis/Pattern/TestPatternReplaceCharFilter.cs b/src/Lucene.Net.Tests.Analysis.Common/Analysis/Pattern/TestPatternReplaceCharFilter.cs index aa3dc1c11f..d940ed37ca 100644 --- a/src/Lucene.Net.Tests.Analysis.Common/Analysis/Pattern/TestPatternReplaceCharFilter.cs +++ b/src/Lucene.Net.Tests.Analysis.Common/Analysis/Pattern/TestPatternReplaceCharFilter.cs @@ -2,6 +2,7 @@ using J2N; using Lucene.Net.Util; using NUnit.Framework; +using RandomizedTesting.Generators; using System; using System.IO; using System.Text; @@ -251,11 +252,11 @@ public virtual void TestNastyPattern() [Slow] public virtual void TestRandomStrings() { - int numPatterns = 10 + LuceneTestCase.Random.Next(20); - Random random = new Random(Random.nextInt(int.MaxValue)); + int numPatterns = 10 + Random.Next(20); + Random random = new J2N.Randomizer(Random.NextInt64()); for (int i = 0; i < numPatterns; i++) { - Regex p = TestUtil.RandomRegex(LuceneTestCase.Random); + Regex p = TestUtil.RandomRegex(Random); string replacement = TestUtil.RandomSimpleString(random); Analyzer a = Analyzer.NewAnonymous(createComponents: (fieldName, reader) => diff --git a/src/Lucene.Net.Tests.Classification/ClassificationTestBase.cs b/src/Lucene.Net.Tests.Classification/ClassificationTestBase.cs index 5d0a22ff95..2e46605de3 100644 --- a/src/Lucene.Net.Tests.Classification/ClassificationTestBase.cs +++ b/src/Lucene.Net.Tests.Classification/ClassificationTestBase.cs @@ -240,7 +240,7 @@ private void PopulatePerformanceIndex(Analyzer analyzer) ft.StoreTermVectorOffsets = true; ft.StoreTermVectorPositions = true; int docs = 1000; - Random random = new Random(); + Random random = Random; for (int i = 0; i < docs; i++) { Boolean b = random.NextBoolean(); diff --git a/src/Lucene.Net.Tests.Facet/SlowRAMDirectory.cs b/src/Lucene.Net.Tests.Facet/SlowRAMDirectory.cs index 0600431db0..25e7cebc4c 100644 --- a/src/Lucene.Net.Tests.Facet/SlowRAMDirectory.cs +++ b/src/Lucene.Net.Tests.Facet/SlowRAMDirectory.cs @@ -96,7 +96,7 @@ internal virtual Random ForkRandom() { return null; } - return new Random((int)random.NextInt64()); + return new J2N.Randomizer(random.NextInt64()); } /// diff --git a/src/Lucene.Net.Tests.Misc/Index/Sorter/TestEarlyTermination.cs b/src/Lucene.Net.Tests.Misc/Index/Sorter/TestEarlyTermination.cs index 9acd9f9b5d..1339cbb022 100644 --- a/src/Lucene.Net.Tests.Misc/Index/Sorter/TestEarlyTermination.cs +++ b/src/Lucene.Net.Tests.Misc/Index/Sorter/TestEarlyTermination.cs @@ -65,10 +65,10 @@ private void CreateRandomIndexes(int maxSegments) randomTerms.add(TestUtil.RandomSimpleString(Random)); } terms = new JCG.List(randomTerms); - int seed = Random.Next(); - IndexWriterConfig iwc = NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(new Random(seed))); + long seed = Random.NextInt64(); + IndexWriterConfig iwc = NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(new J2N.Randomizer(seed))); iwc.SetMergePolicy(TestSortingMergePolicy.NewSortingMergePolicy(sort)); - iw = new RandomIndexWriter(new Random(seed), dir, iwc); + iw = new RandomIndexWriter(new J2N.Randomizer(seed), dir, iwc); for (int i = 0; i < numDocs; ++i) { Document doc = RandomDocument(); diff --git a/src/Lucene.Net.Tests.Misc/Index/Sorter/TestSortingMergePolicy.cs b/src/Lucene.Net.Tests.Misc/Index/Sorter/TestSortingMergePolicy.cs index 97d9648421..9b28f31208 100644 --- a/src/Lucene.Net.Tests.Misc/Index/Sorter/TestSortingMergePolicy.cs +++ b/src/Lucene.Net.Tests.Misc/Index/Sorter/TestSortingMergePolicy.cs @@ -89,11 +89,11 @@ private void CreateRandomIndexes() } terms = new JCG.List(randomTerms); long seed = Random.NextInt64(); - IndexWriterConfig iwc1 = NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(new Random((int)seed))); - IndexWriterConfig iwc2 = NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(new Random((int)seed))); + IndexWriterConfig iwc1 = NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(new J2N.Randomizer(seed))); + IndexWriterConfig iwc2 = NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(new J2N.Randomizer(seed))); iwc2.SetMergePolicy(NewSortingMergePolicy(sort)); - RandomIndexWriter iw1 = new RandomIndexWriter(new Random((int)seed), dir1, iwc1); - RandomIndexWriter iw2 = new RandomIndexWriter(new Random((int)seed), dir2, iwc2); + RandomIndexWriter iw1 = new RandomIndexWriter(new J2N.Randomizer(seed), dir1, iwc1); + RandomIndexWriter iw2 = new RandomIndexWriter(new J2N.Randomizer(seed), dir2, iwc2); for (int i = 0; i < numDocs; ++i) { if (Random.nextInt(5) == 0 && i != numDocs - 1) diff --git a/src/Lucene.Net.Tests.Misc/Util/Fst/TestFSTsMisc.cs b/src/Lucene.Net.Tests.Misc/Util/Fst/TestFSTsMisc.cs index f1e74c958d..b79bdf0b2d 100644 --- a/src/Lucene.Net.Tests.Misc/Util/Fst/TestFSTsMisc.cs +++ b/src/Lucene.Net.Tests.Misc/Util/Fst/TestFSTsMisc.cs @@ -1,6 +1,7 @@ -using J2N.Collections.Generic.Extensions; +using J2N.Collections.Generic.Extensions; using Lucene.Net.Store; using NUnit.Framework; +using RandomizedTesting.Generators; using System; using System.Collections.Generic; using System.Linq; @@ -53,7 +54,7 @@ public void TestRandomWords() private void TestRandomWords(int maxNumWords, int numIter) { - Random random = new Random(Random.Next()); + Random random = new J2N.Randomizer(Random.NextInt64()); for (int iter = 0; iter < numIter; iter++) { if (Verbose) diff --git a/src/Lucene.Net.Tests.Queries/CommonTermsQueryTest.cs b/src/Lucene.Net.Tests.Queries/CommonTermsQueryTest.cs index 6ff07d9cfe..4835728a99 100644 --- a/src/Lucene.Net.Tests.Queries/CommonTermsQueryTest.cs +++ b/src/Lucene.Net.Tests.Queries/CommonTermsQueryTest.cs @@ -131,7 +131,7 @@ public void TestEqualsHashCode() RandomOccur(Random), Random.NextSingle(), Random.NextBoolean()), query); { long seed = Random.NextInt64(); - Random r = new Random((int)seed); + Random r = new J2N.Randomizer(seed); CommonTermsQuery left = new CommonTermsQuery(RandomOccur(r), RandomOccur(r), r.NextSingle(), r.NextBoolean()); int leftTerms = AtLeast(r, 2); @@ -143,7 +143,7 @@ public void TestEqualsHashCode() left.HighFreqMinimumNumberShouldMatch = r.nextInt(4); left.LowFreqMinimumNumberShouldMatch = r.nextInt(4); - r = new Random((int)seed); + r = new J2N.Randomizer(seed); CommonTermsQuery right = new CommonTermsQuery(RandomOccur(r), RandomOccur(r), r.NextSingle(), r.NextBoolean()); int rightTerms = AtLeast(r, 2); @@ -593,7 +593,7 @@ public int CompareTo(TermAndFreq other) /// public static void CreateRandomIndex(int numdocs, RandomIndexWriter writer, long seed) { - Random random = new Random((int)seed); + Random random = new J2N.Randomizer(seed); // primary source for our data is from linefiledocs, its realistic. LineFileDocs lineFileDocs = new LineFileDocs(random, false); // no docvalues in 4x diff --git a/src/Lucene.Net.Tests.Replicator/IndexInputStreamTest.cs b/src/Lucene.Net.Tests.Replicator/IndexInputStreamTest.cs index a38c06cc32..f060eba98a 100644 --- a/src/Lucene.Net.Tests.Replicator/IndexInputStreamTest.cs +++ b/src/Lucene.Net.Tests.Replicator/IndexInputStreamTest.cs @@ -1,12 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Lucene.Net.Attributes; +using Lucene.Net.Attributes; using Lucene.Net.Index; using Lucene.Net.Replicator; -using Lucene.Net.Store; +using Lucene.Net.Util; using NUnit.Framework; +using System; +using System.Linq; using Assert = Lucene.Net.TestFramework.Assert; namespace Lucene.Net.Tests.Replicator @@ -30,7 +28,7 @@ namespace Lucene.Net.Tests.Replicator //Note: LUCENENET specific [LuceneNetSpecific] - public class IndexInputStreamTest + public class IndexInputStreamTest : LuceneTestCase { [Test] @@ -38,7 +36,7 @@ public class IndexInputStreamTest public void Read_RemainingIndexInputLargerThanReadCount_ReturnsReadCount() { byte[] buffer = new byte[8.KiloBytes()]; - new Random().NextBytes(buffer); + Random.NextBytes(buffer); IndexInputStream stream = new IndexInputStream(new MockIndexInput(buffer)); int readBytes = 2.KiloBytes(); @@ -51,7 +49,7 @@ public void Read_RemainingIndexInputLargerThanReadCount_ReturnsReadCount() public void Read_RemainingIndexInputLargerThanReadCount_ReturnsExpectedSection([Range(1,8)]int section) { byte[] buffer = new byte[8.KiloBytes()]; - new Random().NextBytes(buffer); + Random.NextBytes(buffer); IndexInputStream stream = new IndexInputStream(new MockIndexInput(buffer)); int readBytes = 1.KiloBytes(); diff --git a/src/Lucene.Net.Tests.Suggest/Suggest/Fst/WFSTCompletionTest.cs b/src/Lucene.Net.Tests.Suggest/Suggest/Fst/WFSTCompletionTest.cs index c7d9f81f0d..3f1b32048d 100644 --- a/src/Lucene.Net.Tests.Suggest/Suggest/Fst/WFSTCompletionTest.cs +++ b/src/Lucene.Net.Tests.Suggest/Suggest/Fst/WFSTCompletionTest.cs @@ -1,6 +1,7 @@ using Lucene.Net.Support; using Lucene.Net.Util; using NUnit.Framework; +using RandomizedTesting.Generators; using System; using System.Collections.Generic; using JCG = J2N.Collections.Generic; @@ -36,7 +37,7 @@ public void TestBasic() new Input("barbara", 6) }; - Random random = new Random(Random.Next()); + Random random = new J2N.Randomizer(Random.NextInt64()); WFSTCompletionLookup suggester = new WFSTCompletionLookup(); suggester.Build(new InputArrayEnumerator(keys)); @@ -171,7 +172,7 @@ public void TestRandom() suggester.Build(new InputArrayEnumerator(keys)); assertEquals(numWords, suggester.Count); - Random random = new Random(Random.Next()); + Random random = new J2N.Randomizer(Random.NextInt64()); foreach (String prefix in allPrefixes) { int topN = TestUtil.NextInt32(random, 1, 10); diff --git a/src/Lucene.Net.Tests.Suggest/Suggest/LookupBenchmarkTest.cs b/src/Lucene.Net.Tests.Suggest/Suggest/LookupBenchmarkTest.cs index 17a79ebaac..9b1442f098 100644 --- a/src/Lucene.Net.Tests.Suggest/Suggest/LookupBenchmarkTest.cs +++ b/src/Lucene.Net.Tests.Suggest/Suggest/LookupBenchmarkTest.cs @@ -58,7 +58,7 @@ public class LookupBenchmarkTest : LuceneTestCase internal readonly int num = 7; internal readonly bool onlyMorePopular = false; - private readonly static Random random = new Random(0xdeadbee); // LUCENENET NOTE: Changed seed so it would fit in an int + private readonly static Random random = new J2N.Randomizer(0xdeadbeef); /** * Input term/weight pairs. diff --git a/src/Lucene.Net.Tests/Codecs/Lucene41/TestBlockPostingsFormat3.cs b/src/Lucene.Net.Tests/Codecs/Lucene41/TestBlockPostingsFormat3.cs index 98ee4cf766..d02e5104a3 100644 --- a/src/Lucene.Net.Tests/Codecs/Lucene41/TestBlockPostingsFormat3.cs +++ b/src/Lucene.Net.Tests/Codecs/Lucene41/TestBlockPostingsFormat3.cs @@ -84,12 +84,12 @@ public virtual void Test() Tokenizer tokenizer = new MockTokenizer(reader); if (fieldName.Contains("payloadsFixed")) { - TokenFilter filter = new MockFixedLengthPayloadFilter(new Random(0), tokenizer, 1); + TokenFilter filter = new MockFixedLengthPayloadFilter(new J2N.Randomizer(0), tokenizer, 1); return new TokenStreamComponents(tokenizer, filter); } else if (fieldName.Contains("payloadsVariable")) { - TokenFilter filter = new MockVariableLengthPayloadFilter(new Random(0), tokenizer); + TokenFilter filter = new MockVariableLengthPayloadFilter(new J2N.Randomizer(0), tokenizer); return new TokenStreamComponents(tokenizer, filter); } else diff --git a/src/Lucene.Net.Tests/Codecs/PerField/TestPerFieldDocValuesFormat.cs b/src/Lucene.Net.Tests/Codecs/PerField/TestPerFieldDocValuesFormat.cs index cf3abc6aa0..25950ab30c 100644 --- a/src/Lucene.Net.Tests/Codecs/PerField/TestPerFieldDocValuesFormat.cs +++ b/src/Lucene.Net.Tests/Codecs/PerField/TestPerFieldDocValuesFormat.cs @@ -1,9 +1,10 @@ -using Lucene.Net.Diagnostics; +using Lucene.Net.Diagnostics; using Lucene.Net.Documents; using Lucene.Net.Index; using Lucene.Net.Index.Extensions; using Lucene.Net.Support; using NUnit.Framework; +using RandomizedTesting.Generators; using System; using Assert = Lucene.Net.TestFramework.Assert; @@ -61,7 +62,7 @@ public class TestPerFieldDocValuesFormat : BaseDocValuesFormatTestCase [SetUp] public override void SetUp() { - codec = new RandomCodec(new Random(Random.Next()), Collections.EmptySet()); + codec = new RandomCodec(new J2N.Randomizer(Random.NextInt64()), Collections.EmptySet()); base.SetUp(); } diff --git a/src/Lucene.Net.Tests/Codecs/PerField/TestPerFieldPostingsFormat.cs b/src/Lucene.Net.Tests/Codecs/PerField/TestPerFieldPostingsFormat.cs index 35b39d3522..f267f1a3d6 100644 --- a/src/Lucene.Net.Tests/Codecs/PerField/TestPerFieldPostingsFormat.cs +++ b/src/Lucene.Net.Tests/Codecs/PerField/TestPerFieldPostingsFormat.cs @@ -1,5 +1,6 @@ -using Lucene.Net.Support; +using Lucene.Net.Support; using NUnit.Framework; +using RandomizedTesting.Generators; using System; namespace Lucene.Net.Codecs.PerField @@ -32,7 +33,7 @@ public class TestPerFieldPostingsFormat : BasePostingsFormatTestCase { protected override Codec GetCodec() { - return new RandomCodec(new Random(Random.Next()), Collections.EmptySet()); + return new RandomCodec(new J2N.Randomizer(Random.NextInt64()), Collections.EmptySet()); } [Test] diff --git a/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs b/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs index 19738a6bbe..6719790349 100644 --- a/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs +++ b/src/Lucene.Net.Tests/Index/TestBagOfPositions.cs @@ -124,7 +124,7 @@ public virtual void Test() for (int threadID = 0; threadID < threadCount; threadID++) { - Random threadRandom = new Random(Random.Next()); + Random threadRandom = new J2N.Randomizer(Random.NextInt64()); Document document = new Document(); Field field = new Field("field", "", fieldType); document.Add(field); diff --git a/src/Lucene.Net.Tests/Index/TestCompoundFile.cs b/src/Lucene.Net.Tests/Index/TestCompoundFile.cs index 50861e0fd7..941248aee3 100644 --- a/src/Lucene.Net.Tests/Index/TestCompoundFile.cs +++ b/src/Lucene.Net.Tests/Index/TestCompoundFile.cs @@ -64,8 +64,8 @@ private void CreateRandomFile(Directory dir, string name, int size) IndexOutput os = dir.CreateOutput(name, NewIOContext(Random)); for (int i = 0; i < size; i++) { - var b = unchecked((sbyte)(new Random(1).NextDouble() * 256)); - os.WriteByte((byte)b); + var b = (byte)(Random.NextDouble() * 256); // LUCENENET: Using Random, since Math.random() doesn't exist in .NET, and it seems to make sense to make this repeatable. + os.WriteByte(b); } os.Dispose(); } @@ -611,11 +611,11 @@ public virtual void TestReadPastEOF() public virtual void TestLargeWrites() { IndexOutput os = dir.CreateOutput("testBufferStart.txt", NewIOContext(Random)); - + var largeBuf = new byte[2048]; for (int i = 0; i < largeBuf.Length; i++) { - largeBuf[i] = (byte)(new Random(1).NextDouble() * 256); + largeBuf[i] = (byte)(Random.NextDouble() * 256); // LUCENENET: Using Random, since Math.random() doesn't exist in .NET, and it seems to make sense to make this repeatable. } long currentPos = os.Position; // LUCENENET specific: Renamed from getFilePointer() to match FileStream diff --git a/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs b/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs index b160376de2..f1b65ab05d 100644 --- a/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs +++ b/src/Lucene.Net.Tests/Index/TestDocValuesWithThreads.cs @@ -82,7 +82,7 @@ public virtual void Test() CountdownEvent startingGun = new CountdownEvent(1); for (int t = 0; t < numThreads; t++) { - Random threadRandom = new Random(Random.Next()); + Random threadRandom = new J2N.Randomizer(Random.NextInt64()); ThreadJob thread = new ThreadAnonymousClass(this, numbers, binary, sorted, numDocs, ar, startingGun, threadRandom); thread.Start(); threads.Add(thread); diff --git a/src/Lucene.Net.Tests/Index/TestDuelingCodecs.cs b/src/Lucene.Net.Tests/Index/TestDuelingCodecs.cs index 69e5d5e88b..8e889432b9 100644 --- a/src/Lucene.Net.Tests/Index/TestDuelingCodecs.cs +++ b/src/Lucene.Net.Tests/Index/TestDuelingCodecs.cs @@ -2,6 +2,7 @@ using Lucene.Net.Documents; using Lucene.Net.Index.Extensions; using NUnit.Framework; +using RandomizedTesting.Generators; using System; using System.Text.RegularExpressions; @@ -66,13 +67,13 @@ public override void SetUp() leftDir = NewDirectory(); rightDir = NewDirectory(); - long seed = Random.Next(); + long seed = Random.NextInt64(); // must use same seed because of random payloads, etc int maxTermLength = TestUtil.NextInt32(Random, 1, IndexWriter.MAX_TERM_LENGTH); - MockAnalyzer leftAnalyzer = new MockAnalyzer(new Random((int)seed)); + MockAnalyzer leftAnalyzer = new MockAnalyzer(new J2N.Randomizer(seed)); leftAnalyzer.MaxTokenLength = maxTermLength; - MockAnalyzer rightAnalyzer = new MockAnalyzer(new Random((int)seed)); + MockAnalyzer rightAnalyzer = new MockAnalyzer(new J2N.Randomizer(seed)); rightAnalyzer.MaxTokenLength = maxTermLength; // but these can be different @@ -88,8 +89,8 @@ public override void SetUp() rightConfig.SetMergePolicy(NewLogMergePolicy()); // must use same seed because of random docvalues fields, etc - RandomIndexWriter leftWriter = new RandomIndexWriter(new Random((int)seed), leftDir, leftConfig); - RandomIndexWriter rightWriter = new RandomIndexWriter(new Random((int)seed), rightDir, rightConfig); + RandomIndexWriter leftWriter = new RandomIndexWriter(new J2N.Randomizer(seed), leftDir, leftConfig); + RandomIndexWriter rightWriter = new RandomIndexWriter(new J2N.Randomizer(seed), rightDir, rightConfig); int numdocs = AtLeast(100); CreateRandomIndex(numdocs, leftWriter, seed); @@ -136,7 +137,7 @@ public override void TearDown() /// public static void CreateRandomIndex(int numdocs, RandomIndexWriter writer, long seed) { - Random random = new Random((int)seed); + Random random = new J2N.Randomizer(seed); // primary source for our data is from linefiledocs, its realistic. LineFileDocs lineFileDocs = new LineFileDocs(random); @@ -159,7 +160,7 @@ public static void CreateRandomIndex(int numdocs, RandomIndexWriter writer, long document.RemoveFields("sparsenumeric"); if (random.Next(4) == 2) { - document.Add(new NumericDocValuesField("sparsenumeric", random.Next())); + document.Add(new NumericDocValuesField("sparsenumeric", random.NextInt64())); } writer.AddDocument(document); } diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs index 8243b63dfa..7b2f2a2cef 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriter.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriter.cs @@ -1163,7 +1163,7 @@ private class IndexerThreadInterrupt : ThreadJob internal IndexerThreadInterrupt(TestIndexWriter outerInstance) { this.outerInstance = outerInstance; - this.random = new Random(LuceneTestCase.Random.Next()); + this.random = new J2N.Randomizer(Random.NextInt64()); // make a little directory for addIndexes // LUCENE-2239: won't work with NIOFS/MMAP adder = new MockDirectoryWrapper(this.random, new RAMDirectory()); diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterExceptions.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterExceptions.cs index 78712bda27..3ca892bcda 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterExceptions.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterExceptions.cs @@ -167,7 +167,7 @@ private class IndexerThread : ThreadJob internal IndexWriter writer; - internal readonly Random r = new Random(Random.Next()); + internal readonly Random r = new J2N.Randomizer(Random.NextInt64()); internal volatile Exception failure = null; public IndexerThread(TestIndexWriterExceptions outerInstance, int i, IndexWriter writer) @@ -294,7 +294,7 @@ public TestPoint1(TestIndexWriterExceptions outerInstance) this.outerInstance = outerInstance; } - internal Random r = new Random(Random.Next()); + internal Random r = new J2N.Randomizer(Random.NextInt64()); public void Apply(string name) { diff --git a/src/Lucene.Net.Tests/Index/TestIndexWriterReader.cs b/src/Lucene.Net.Tests/Index/TestIndexWriterReader.cs index 463a918055..6d3a73f402 100644 --- a/src/Lucene.Net.Tests/Index/TestIndexWriterReader.cs +++ b/src/Lucene.Net.Tests/Index/TestIndexWriterReader.cs @@ -1070,7 +1070,7 @@ public ThreadAnonymousClass2(IndexWriter writer, long endTime, ConcurrentQueue b = new Builder(FST.INPUT_TYPE.BYTE1, 0, 0, true, true, int.MaxValue, outputs, null, doPack, PackedInt32s.COMPACT, true, 15); int count = 0; - Random r = new Random(seed); + Random r = new J2N.Randomizer(seed); int[] ints2 = new int[200]; Int32sRef input2 = new Int32sRef(ints2, 0, ints2.Length); while (true) @@ -89,7 +90,7 @@ public virtual void Test() Console.WriteLine("\nTEST: now verify [fst size=" + fst.GetSizeInBytes() + "; nodeCount=" + fst.NodeCount + "; arcCount=" + fst.ArcCount + "]"); Arrays.Fill(ints2, 0); - r = new Random(seed); + r = new J2N.Randomizer(seed); for (int i = 0; i < count; i++) { @@ -109,7 +110,7 @@ public virtual void Test() Int32sRefFSTEnum fstEnum = new Int32sRefFSTEnum(fst); Arrays.Fill(ints2, 0); - r = new Random(seed); + r = new J2N.Randomizer(seed); int upto = 0; while (true) { @@ -157,7 +158,7 @@ public virtual void Test() BytesRef output = new BytesRef(outputBytes); Arrays.Fill(ints, 0); int count = 0; - Random r = new Random(seed); + Random r = new J2N.Randomizer(seed); while (true) { r.NextBytes(outputBytes); @@ -180,7 +181,7 @@ public virtual void Test() { Console.WriteLine("\nTEST: now verify [fst size=" + fst.GetSizeInBytes() + "; nodeCount=" + fst.NodeCount + "; arcCount=" + fst.ArcCount + "]"); - r = new Random(seed); + r = new J2N.Randomizer(seed); Arrays.Fill(ints, 0); for (int i = 0; i < count; i++) @@ -198,7 +199,7 @@ public virtual void Test() Int32sRefFSTEnum fstEnum = new Int32sRefFSTEnum(fst); Arrays.Fill(ints, 0); - r = new Random(seed); + r = new J2N.Randomizer(seed); int upto = 0; while (true) { @@ -243,7 +244,7 @@ public virtual void Test() Arrays.Fill(ints, 0); int count = 0; - Random r = new Random(seed); + Random r = new J2N.Randomizer(seed); while (true) { //System.out.println("add: " + input + " -> " + output); @@ -270,7 +271,7 @@ public virtual void Test() Arrays.Fill(ints, 0); output = 1; - r = new Random(seed); + r = new J2N.Randomizer(seed); for (int i = 0; i < count; i++) { if (i % 1000000 == 0) @@ -290,7 +291,7 @@ public virtual void Test() Int32sRefFSTEnum fstEnum = new Int32sRefFSTEnum(fst); Arrays.Fill(ints, 0); - r = new Random(seed); + r = new J2N.Randomizer(seed); int upto = 0; output = 1; while (true) diff --git a/src/Lucene.Net.Tests/Util/Fst/TestFSTs.cs b/src/Lucene.Net.Tests/Util/Fst/TestFSTs.cs index 014ec0b890..2a189a62b9 100644 --- a/src/Lucene.Net.Tests/Util/Fst/TestFSTs.cs +++ b/src/Lucene.Net.Tests/Util/Fst/TestFSTs.cs @@ -291,7 +291,7 @@ internal virtual string InputModeToString(int mode) private void TestRandomWords(int maxNumWords, int numIter) { - Random random = new Random(Random.Next()); + Random random = new J2N.Randomizer(Random.NextInt64()); for (int iter = 0; iter < numIter; iter++) { if (Verbose) @@ -426,7 +426,7 @@ public virtual void TestRealTerms() if (ord > 0) { - Random random = new Random(Random.Next()); + Random random = new J2N.Randomizer(Random.NextInt64()); // Now confirm BytesRefFSTEnum and TermsEnum act the // same: BytesRefFSTEnum fstEnum = new BytesRefFSTEnum(fst); @@ -829,7 +829,7 @@ protected internal override Pair GetOutput(Int32sRef input, int ord) { if (ord == 0) { - rand = new Random(17); + rand = new J2N.Randomizer(17); } return outputs.NewPair(ord, TestUtil.NextInt32(rand, 1, 5000)); } @@ -860,7 +860,7 @@ public VisitTermsAnonymousClass3(string dirOut, string wordsFileIn, int inputMod { if (ord == 0) { - rand = new Random(17); + rand = new J2N.Randomizer(17); } return (long)TestUtil.NextInt32(rand, 1, 5000); } diff --git a/src/Lucene.Net.Tests/Util/Packed/TestPackedInts.cs b/src/Lucene.Net.Tests/Util/Packed/TestPackedInts.cs index 7ed4fffa21..bd60e05544 100644 --- a/src/Lucene.Net.Tests/Util/Packed/TestPackedInts.cs +++ b/src/Lucene.Net.Tests/Util/Packed/TestPackedInts.cs @@ -431,7 +431,7 @@ private static void AssertRandomEquality(int valueCount, int bitsPerValue, long private static void Fill(PackedInt32s.Mutable packedInt, long maxValue, long randomSeed) { - Random rnd2 = new Random((int)randomSeed); + Random rnd2 = new J2N.Randomizer(randomSeed); for (int i = 0; i < packedInt.Count; i++) { long value = TestUtil.NextInt64(rnd2, 0, maxValue); diff --git a/src/Lucene.Net.Tests/Util/Test2BPagedBytes.cs b/src/Lucene.Net.Tests/Util/Test2BPagedBytes.cs index d70d43be64..9e0723cebc 100644 --- a/src/Lucene.Net.Tests/Util/Test2BPagedBytes.cs +++ b/src/Lucene.Net.Tests/Util/Test2BPagedBytes.cs @@ -50,7 +50,7 @@ public virtual void Test() long netBytes = 0; long seed = Random.NextInt64(); long lastFP = 0; - Random r2 = new Random((int)seed); + Random r2 = new J2N.Randomizer(seed); while (netBytes < 1.1 * int.MaxValue) { int numBytes = TestUtil.NextInt32(r2, 1, 32768); @@ -68,7 +68,7 @@ public virtual void Test() input.Dispose(); PagedBytes.Reader reader = pb.Freeze(true); - r2 = new Random((int)seed); + r2 = new J2N.Randomizer(seed); netBytes = 0; while (netBytes < 1.1 * int.MaxValue) { diff --git a/src/Lucene.Net.Tests/Util/TestFixedBitSet.cs b/src/Lucene.Net.Tests/Util/TestFixedBitSet.cs index 297b6a56e1..e622c477a8 100644 --- a/src/Lucene.Net.Tests/Util/TestFixedBitSet.cs +++ b/src/Lucene.Net.Tests/Util/TestFixedBitSet.cs @@ -298,10 +298,10 @@ public void TestClearSmall() [Test, LuceneNetSpecific] public void TestClearLarge() { + Random random = Random; int iters = AtLeast(1000); for (int it = 0; it < iters; it++) { - Random random = new Random(); int sz = AtLeast(1200); FixedBitSet a = new FixedBitSet(sz); FixedBitSet b = new FixedBitSet(sz); diff --git a/src/Lucene.Net.Tests/Util/TestLongBitSet.cs b/src/Lucene.Net.Tests/Util/TestLongBitSet.cs index 829592ee5e..badd258166 100644 --- a/src/Lucene.Net.Tests/Util/TestLongBitSet.cs +++ b/src/Lucene.Net.Tests/Util/TestLongBitSet.cs @@ -243,10 +243,10 @@ public void TestClearSmall() [Test, LuceneNetSpecific] public void TestClearLarge() { + Random random = Random; int iters = AtLeast(1000); for (int it = 0; it < iters; it++) { - Random random = new Random(); int sz = AtLeast(1200); Int64BitSet a = new Int64BitSet(sz); Int64BitSet b = new Int64BitSet(sz); diff --git a/src/Lucene.Net.Tests/Util/TestMergedIterator.cs b/src/Lucene.Net.Tests/Util/TestMergedIterator.cs index a7ef2e9849..55e9c06907 100644 --- a/src/Lucene.Net.Tests/Util/TestMergedIterator.cs +++ b/src/Lucene.Net.Tests/Util/TestMergedIterator.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using JCG = J2N.Collections.Generic; using Assert = Lucene.Net.TestFramework.Assert; +using RandomizedTesting.Generators; namespace Lucene.Net.Util { @@ -123,7 +124,7 @@ private void TestCase(int itrsWithVal, int specifiedValsOnItr, bool removeDups) { // Build a random number of lists IList expected = new JCG.List(); - Random random = new Random(Random.Next()); + Random random = new J2N.Randomizer(Random.NextInt64()); int numLists = itrsWithVal + random.Next(1000 - itrsWithVal); IList[] lists = new IList[numLists]; for (int i = 0; i < numLists; i++) @@ -292,7 +293,7 @@ private void TestCaseIterator(int itrsWithVal, int specifiedValsOnItr, bool remo { // Build a random number of lists IList expected = new JCG.List(); - Random random = new Random(Random.Next()); + Random random = new J2N.Randomizer(Random.NextInt64()); int numLists = itrsWithVal + random.Next(1000 - itrsWithVal); IList[] lists = new IList[numLists]; for (int i = 0; i < numLists; i++) diff --git a/src/Lucene.Net.Tests/Util/TestOpenBitSet.cs b/src/Lucene.Net.Tests/Util/TestOpenBitSet.cs index 2bf549c139..cb48bfa3f8 100644 --- a/src/Lucene.Net.Tests/Util/TestOpenBitSet.cs +++ b/src/Lucene.Net.Tests/Util/TestOpenBitSet.cs @@ -356,10 +356,10 @@ public void TestClearSmall() [Test, LuceneNetSpecific] public void TestClearLarge() { + Random random = Random; int iters = AtLeast(1000); for (int it = 0; it < iters; it++) { - Random random = new Random(); int sz = AtLeast(1200); OpenBitSet a = new OpenBitSet(sz); OpenBitSet b = new OpenBitSet(sz); diff --git a/src/Lucene.Net.Tests/Util/TestSetOnce.cs b/src/Lucene.Net.Tests/Util/TestSetOnce.cs index 6ef60bc52c..2f572eb540 100644 --- a/src/Lucene.Net.Tests/Util/TestSetOnce.cs +++ b/src/Lucene.Net.Tests/Util/TestSetOnce.cs @@ -1,9 +1,8 @@ using J2N.Threading; -using Lucene.Net.Support.Threading; using NUnit.Framework; +using RandomizedTesting.Generators; using System; using System.Globalization; -using System.Threading; using Assert = Lucene.Net.TestFramework.Assert; namespace Lucene.Net.Util @@ -46,7 +45,7 @@ private sealed class SetOnceThread : ThreadJob public SetOnceThread(Random random) { - RAND = new Random(random.Next()); + RAND = new J2N.Randomizer(random.NextInt64()); } public override void Run() diff --git a/src/Lucene.Net.Tests/Util/TestWeakIdentityMap.cs b/src/Lucene.Net.Tests/Util/TestWeakIdentityMap.cs index 0fd57a1864..10dae67bad 100644 --- a/src/Lucene.Net.Tests/Util/TestWeakIdentityMap.cs +++ b/src/Lucene.Net.Tests/Util/TestWeakIdentityMap.cs @@ -215,7 +215,7 @@ // { // for (int t = 0; t < threadCount; t++) // { -// Random rnd = new Random(Random.Next()); +// Random rnd = new J2N.Randomizer(Random.NextInt64()); // var worker = new RunnableAnonymousClass(this, keyCount, map, keys, rnd); // workers[t] = worker; // worker.Start(); From 5c63995f01c4f914f5183dce1adebfbbc22a8c38 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Sat, 20 Nov 2021 04:19:18 +0700 Subject: [PATCH 27/28] Lucene.Net.TestFramework.Util.MethodInfoComparer: Made into singleton --- .../Support/Util/NUnitTestFixtureBuilder.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs b/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs index 852eb67505..b3734a0ed5 100644 --- a/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs +++ b/src/Lucene.Net.TestFramework/Support/Util/NUnitTestFixtureBuilder.cs @@ -342,6 +342,8 @@ internal static IEnumerable GetConstructors(Type type, Type[] m internal class MethodInfoComparer : IComparer { + private MethodInfoComparer() { } // LUCENENT: Made into singleton + public static IComparer Default { get; } = new MethodInfoComparer(); public int Compare(IMethodInfo x, IMethodInfo y) From 08fbeec5f231095b7495c42516a1dc8a4f3a61a4 Mon Sep 17 00:00:00 2001 From: Shad Storhaug Date: Tue, 23 Nov 2021 03:20:24 +0700 Subject: [PATCH 28/28] Lucene.Net.Classification.KNearestNeighborClassifierTest::TestPerformance(): Adjusted limit from 2 min to 2.5 min for .NET Framework, since it runs a bit slow in CI. --- .../ClassificationTestBase.cs | 5 +++++ .../KNearestNeighborClassifierTest.cs | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Lucene.Net.Tests.Classification/ClassificationTestBase.cs b/src/Lucene.Net.Tests.Classification/ClassificationTestBase.cs index 2e46605de3..314c79dd79 100644 --- a/src/Lucene.Net.Tests.Classification/ClassificationTestBase.cs +++ b/src/Lucene.Net.Tests.Classification/ClassificationTestBase.cs @@ -221,7 +221,12 @@ protected void CheckPerformance(IClassifier classifier, Analyzer analyzer, St classifier.Train(atomicReader, textFieldName, classFieldName, analyzer); long trainEnd = J2N.Time.NanoTime() / J2N.Time.MillisecondsPerNanosecond; // LUCENENET: Use NanoTime() rather than CurrentTimeMilliseconds() for more accurate/reliable results long trainTime = trainEnd - trainStart; + // LUCENENET: This test is running slow on .NET Framework in CI, so we are giving it a little more time to complete. +#if NETFRAMEWORK + Assert.IsTrue(trainTime < 150000, "training took more than 2.5 mins : " + trainTime / 1000 + "s"); +#else Assert.IsTrue(trainTime < 120000, "training took more than 2 mins : " + trainTime / 1000 + "s"); +#endif } finally { diff --git a/src/Lucene.Net.Tests.Classification/KNearestNeighborClassifierTest.cs b/src/Lucene.Net.Tests.Classification/KNearestNeighborClassifierTest.cs index 93238f9ebc..fbfed0930b 100644 --- a/src/Lucene.Net.Tests.Classification/KNearestNeighborClassifierTest.cs +++ b/src/Lucene.Net.Tests.Classification/KNearestNeighborClassifierTest.cs @@ -43,9 +43,6 @@ public void TestBasicUsageWithQuery() CheckCorrectClassification(new KNearestNeighborClassifier(1), TECHNOLOGY_INPUT, TECHNOLOGY_RESULT, new MockAnalyzer(Random), textFieldName, categoryFieldName, new TermQuery(new Term(textFieldName, "it"))); } -#if NET461 - [Ignore("LUCENENET: We don't care if this fails on .NET Standard 2.0/.NET 4.6.1.")] -#endif [Test] public void TestPerformance() {