| // From Apache Harmony tests: |
| // https://github.com/apache/harmony/blob/trunk/classlib/modules/concurrent/src/test/java/JSR166TestCase.java |
| using Lucene.Net.Util; |
| using System; |
| using System.Collections.Generic; |
| using System.Linq; |
| using System.Runtime.CompilerServices; |
| using System.Threading; |
| using System.Threading.Tasks; |
| using ThreadInterruptedException = System.Threading.ThreadInterruptedException; |
| |
| #nullable enable |
| |
| namespace Lucene.Net.Support.Threading |
| { |
| /* |
| * 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. |
| */ |
| |
| /// <summary> |
| /// LUCENENET NOTE: This class has been adapted from the Apache Harmony |
| /// tests. The original javadoc is included below, and adapted where necessary. |
| /// <para /> |
| /// |
| /// Base class for JSR166 Junit TCK tests. Defines some constants, |
| /// utility methods and classes, as well as a simple framework for |
| /// helping to make sure that assertions failing in generated threads |
| /// cause the associated test that generated them to itself fail (which |
| /// JUnit does not otherwise arrange). The rules for creating such |
| /// tests are: |
| /// |
| /// <list type="bullets"> |
| /// |
| /// <item> All assertions in code running in generated threads must use |
| /// the forms <see cref="threadFail"/>, <see cref="threadAssertTrue"/>, |
| /// <see cref="threadAssertEquals(long,long)"/>, <see cref="threadAssertEquals(object,object)"/> |
| /// or <see cref="threadAssertNull"/>, (not |
| /// <c>fail</c>, <c>assertTrue</c>, etc.) It is OK (but not |
| /// particularly recommended) for other code to use these forms too. |
| /// Only the most typically used JUnit assertion methods are defined |
| /// this way, but enough to live with.</item> |
| /// |
| /// <item> If you override <see cref="SetUp"/> or <see cref="TearDown"/>, make sure |
| /// to invoke <c>base.SetUp</c> and <c>base.TearDown</c> within |
| /// them. These methods are used to clear and check for thread |
| /// assertion failures.</item> |
| /// |
| /// <item>All delays and timeouts must use one of the constants |
| /// <see cref="SHORT_DELAY_MS"/>, <see cref="SMALL_DELAY_MS"/>, <see cref="MEDIUM_DELAY_MS"/>, |
| /// <see cref="LONG_DELAY_MS"/>. The idea here is that a SHORT is always |
| /// discriminable from zero time, and always allows enough time for the |
| /// small amounts of computation (creating a thread, calling a few |
| /// methods, etc) needed to reach a timeout point. Similarly, a SMALL |
| /// is always discriminable as larger than SHORT and smaller than |
| /// MEDIUM. And so on. These constants are set to conservative values, |
| /// but even so, if there is ever any doubt, they can all be increased |
| /// in one spot to rerun tests on slower platforms.</item> |
| /// |
| /// <item> All threads generated must be joined inside each test case |
| /// method (or <c>fail</c> to do so) before returning from the |
| /// method. The <see cref="joinPool"/> method can be used to do this when |
| /// using Executors.</item> |
| /// |
| /// </list> |
| /// |
| /// <para /> |
| /// <b>Other notes</b> |
| /// <list type="bullet"> |
| /// |
| /// <item> Usually, there is one testcase method per JSR166 method |
| /// covering "normal" operation, and then as many exception-testing |
| /// methods as there are exceptions the method can throw. Sometimes |
| /// there are multiple tests per JSR166 method when the different |
| /// "normal" behaviors differ significantly. And sometimes testcases |
| /// cover multiple methods when they cannot be tested in |
| /// isolation.</item> |
| /// |
| /// <item> The documentation style for testcases is to provide as javadoc |
| /// a simple sentence or two describing the property that the testcase |
| /// method purports to test. The javadocs do not say anything about how |
| /// the property is tested. To find out, read the code.</item> |
| /// |
| /// <item> These tests are "conformance tests", and do not attempt to |
| /// test throughput, latency, scalability or other performance factors |
| /// (see the separate "jtreg" tests for a set intended to check these |
| /// for the most central aspects of functionality.) So, most tests use |
| /// the smallest sensible numbers of threads, collection sizes, etc |
| /// needed to check basic conformance.</item> |
| /// |
| /// <item>The test classes currently do not declare inclusion in |
| /// any particular package to simplify things for people integrating |
| /// them in TCK test suites.</item> |
| /// |
| /// <!-- LUCENENET: not implemented |
| /// <item> As a convenience, the <c>main</c> of this class (JSR166TestCase) |
| /// runs all JSR166 unit tests.</item> |
| /// --> |
| /// |
| /// </list> |
| /// </summary> |
| public class JSR166TestCase : LuceneTestCase |
| { |
| // /** |
| // * Runs all JSR166 unit tests using junit.textui.TestRunner |
| // */ |
| //public static void main(String[] args) |
| //{ |
| // int iters = 1; |
| // if (args.length > 0) |
| // iters = Integer.parseInt(args[0]); |
| // Test s = suite(); |
| // for (int i = 0; i < iters; ++i) |
| // { |
| // junit.textui.TestRunner.run(s); |
| // System.gc(); |
| // System.runFinalization(); |
| // } |
| // System.exit(0); |
| //} |
| |
| ///** |
| // * Collects all JSR166 unit tests as one suite |
| // */ |
| //public static Test suite() |
| //{ |
| // TestSuite suite = new TestSuite("JSR166 Unit Tests"); |
| |
| // suite.addTest(new TestSuite(AbstractExecutorServiceTest.class)); |
| // suite.addTest(new TestSuite(AbstractQueueTest.class)); |
| // suite.addTest(new TestSuite(AbstractQueuedSynchronizerTest.class)); |
| // suite.addTest(new TestSuite(ArrayBlockingQueueTest.class)); |
| // suite.addTest(new TestSuite(AtomicBooleanTest.class)); |
| // suite.addTest(new TestSuite(AtomicIntegerArrayTest.class)); |
| // suite.addTest(new TestSuite(AtomicIntegerFieldUpdaterTest.class)); |
| // suite.addTest(new TestSuite(AtomicIntegerTest.class)); |
| // suite.addTest(new TestSuite(AtomicLongArrayTest.class)); |
| // suite.addTest(new TestSuite(AtomicLongFieldUpdaterTest.class)); |
| // suite.addTest(new TestSuite(AtomicLongTest.class)); |
| // suite.addTest(new TestSuite(AtomicMarkableReferenceTest.class)); |
| // suite.addTest(new TestSuite(AtomicReferenceArrayTest.class)); |
| // suite.addTest(new TestSuite(AtomicReferenceFieldUpdaterTest.class)); |
| // suite.addTest(new TestSuite(AtomicReferenceTest.class)); |
| // suite.addTest(new TestSuite(AtomicStampedReferenceTest.class)); |
| // suite.addTest(new TestSuite(ConcurrentHashMapTest.class)); |
| // suite.addTest(new TestSuite(ConcurrentLinkedQueueTest.class)); |
| // suite.addTest(new TestSuite(CopyOnWriteArrayListTest.class)); |
| // suite.addTest(new TestSuite(CopyOnWriteArraySetTest.class)); |
| // suite.addTest(new TestSuite(CountDownLatchTest.class)); |
| // suite.addTest(new TestSuite(CyclicBarrierTest.class)); |
| // suite.addTest(new TestSuite(DelayQueueTest.class)); |
| // suite.addTest(new TestSuite(ExchangerTest.class)); |
| // suite.addTest(new TestSuite(ExecutorsTest.class)); |
| // suite.addTest(new TestSuite(ExecutorCompletionServiceTest.class)); |
| // suite.addTest(new TestSuite(FutureTaskTest.class)); |
| // suite.addTest(new TestSuite(LinkedBlockingQueueTest.class)); |
| // suite.addTest(new TestSuite(LinkedListTest.class)); |
| // suite.addTest(new TestSuite(LockSupportTest.class)); |
| // suite.addTest(new TestSuite(PriorityBlockingQueueTest.class)); |
| // suite.addTest(new TestSuite(PriorityQueueTest.class)); |
| // suite.addTest(new TestSuite(ReentrantLockTest.class)); |
| // suite.addTest(new TestSuite(ReentrantReadWriteLockTest.class)); |
| // suite.addTest(new TestSuite(ScheduledExecutorTest.class)); |
| // suite.addTest(new TestSuite(SemaphoreTest.class)); |
| // suite.addTest(new TestSuite(SynchronousQueueTest.class)); |
| // suite.addTest(new TestSuite(SystemTest.class)); |
| // suite.addTest(new TestSuite(ThreadLocalTest.class)); |
| // suite.addTest(new TestSuite(ThreadPoolExecutorTest.class)); |
| // suite.addTest(new TestSuite(ThreadTest.class)); |
| // suite.addTest(new TestSuite(TimeUnitTest.class)); |
| |
| // return suite; |
| //} |
| |
| public static int SHORT_DELAY_MS; |
| public static int SMALL_DELAY_MS; |
| public static int MEDIUM_DELAY_MS; |
| public static int LONG_DELAY_MS; |
| |
| /** |
| * Returns the shortest timed delay. This could |
| * be reimplemented to use for example a Property. |
| */ |
| protected int getShortDelay() |
| { |
| return 50; |
| } |
| |
| |
| /** |
| * Sets delays as multiples of SHORT_DELAY. |
| */ |
| protected void setDelays() |
| { |
| SHORT_DELAY_MS = getShortDelay(); |
| SMALL_DELAY_MS = SHORT_DELAY_MS * 5; |
| MEDIUM_DELAY_MS = SHORT_DELAY_MS * 10; |
| LONG_DELAY_MS = SHORT_DELAY_MS * 50; |
| } |
| |
| /** |
| * Flag set true if any threadAssert methods fail |
| */ |
| internal volatile bool threadFailed; |
| |
| /** |
| * Initializes test to indicate that no thread assertions have failed |
| */ |
| public override void SetUp() |
| { |
| base.SetUp(); |
| setDelays(); |
| threadFailed = false; |
| } |
| |
| /** |
| * Triggers test case failure if any thread assertions have failed |
| */ |
| public override void TearDown() |
| { |
| assertFalse(threadFailed); |
| base.TearDown(); |
| } |
| |
| /** |
| * Fail, also setting status to indicate current testcase should fail |
| */ |
| public void threadFail(string reason) |
| { |
| threadFailed = true; |
| fail(reason); |
| } |
| |
| /** |
| * If expression not true, set status to indicate current testcase |
| * should fail |
| */ |
| public void threadAssertTrue(bool b) |
| { |
| if (!b) |
| { |
| threadFailed = true; |
| assertTrue(b); |
| } |
| } |
| |
| /** |
| * If expression not false, set status to indicate current testcase |
| * should fail |
| */ |
| public void threadAssertFalse(bool b) |
| { |
| if (b) |
| { |
| threadFailed = true; |
| assertFalse(b); |
| } |
| } |
| |
| /** |
| * If argument not null, set status to indicate current testcase |
| * should fail |
| */ |
| public void threadAssertNull(object? x) |
| { |
| if (x != null) |
| { |
| threadFailed = true; |
| assertNull(x); |
| } |
| } |
| |
| /** |
| * If arguments not equal, set status to indicate current testcase |
| * should fail |
| */ |
| public void threadAssertEquals(long x, long y) |
| { |
| if (x != y) |
| { |
| threadFailed = true; |
| assertEquals(x, y); |
| } |
| } |
| |
| /** |
| * If arguments not equal, set status to indicate current testcase |
| * should fail |
| */ |
| public void threadAssertEquals(object? x, object? y) |
| { |
| if (x != y && (x == null || !x.equals(y))) |
| { |
| threadFailed = true; |
| assertEquals(x, y); |
| } |
| } |
| |
| /** |
| * threadFail with message "should throw exception" |
| */ |
| public void threadShouldThrow() |
| { |
| //try |
| //{ |
| threadFailed = true; |
| fail("should throw exception"); |
| //} |
| //catch (AssertionFailedError e) |
| //{ |
| // e.printStackTrace(); |
| // throw e; |
| //} |
| } |
| |
| /** |
| * threadFail with message "Unexpected exception" |
| */ |
| public void threadUnexpectedException() |
| { |
| threadFailed = true; |
| fail("Unexpected exception"); |
| } |
| |
| /** |
| * threadFail with message "Unexpected exception", with argument |
| */ |
| public void threadUnexpectedException(Exception ex) |
| { |
| threadFailed = true; |
| ex.PrintStackTrace(); |
| fail("Unexpected exception: " + ex); |
| } |
| |
| /** |
| * Wait out termination of a thread pool or fail doing so |
| */ |
| public void joinPool(TaskScheduler exec) |
| { |
| try |
| { |
| exec.Shutdown(); |
| assertTrue(exec.AwaitTermination(TimeSpan.FromMilliseconds(LONG_DELAY_MS))); |
| } |
| // catch (SecurityException ok) // LUCENENET - not needed |
| // { |
| // // Allowed in case test doesn't have privs |
| // } |
| catch (ThreadInterruptedException /*ie*/) |
| { |
| fail("Unexpected exception"); |
| } |
| } |
| |
| |
| /** |
| * fail with message "should throw exception" |
| */ |
| public void shouldThrow() |
| { |
| fail("Should throw exception"); |
| } |
| |
| /** |
| * fail with message "Unexpected exception" |
| */ |
| public void unexpectedException() |
| { |
| fail("Unexpected exception"); |
| } |
| |
| protected static readonly int zero = 0; |
| protected static readonly int one = 1; |
| protected static readonly int two = 2; |
| protected static readonly int three = 3; |
| protected static readonly int four = 4; |
| protected static readonly int five = 5; |
| protected static readonly int six = 6; |
| protected static readonly int seven = 7; |
| protected static readonly int eight = 8; |
| protected static readonly int nine = 9; |
| protected static readonly int m1 = -1; |
| protected static readonly int m2 = -2; |
| protected static readonly int m3 = -3; |
| protected static readonly int m4 = -4; |
| protected static readonly int m5 = -5; |
| protected static readonly int m6 = -6; |
| protected static readonly int m10 = -10; |
| |
| internal void ShortRunnable() |
| { |
| try |
| { |
| Thread.Sleep(SHORT_DELAY_MS); |
| } |
| catch (Exception e) |
| { |
| threadUnexpectedException(e); |
| } |
| } |
| |
| internal void MediumRunnable() |
| { |
| try |
| { |
| Thread.Sleep(MEDIUM_DELAY_MS); |
| } |
| catch (Exception e) |
| { |
| threadUnexpectedException(e); |
| } |
| } |
| |
| // LUCENENET TODO: Complete port |
| } |
| |
| /// <summary> |
| /// LUCENENET specific - fake support for an API that feels like ThreadPoolExecutor. |
| /// </summary> |
| internal static class JSR166TestCaseExtensions |
| { |
| /// <summary> |
| /// LUCENENET specific - state to keep track of tasks. |
| /// <see cref="LimitedConcurrencyLevelTaskScheduler"/> removes tasks from the list when they complete, |
| /// so this class is needed to keep track of them. |
| /// </summary> |
| private class TaskState |
| { |
| private readonly TaskFactory _factory; |
| private readonly List<Task> _tasks = new(); |
| |
| public TaskState(TaskScheduler scheduler) |
| { |
| _factory = new TaskFactory(scheduler); |
| } |
| |
| public void NewTask(Action action) |
| { |
| var task = _factory.StartNew(action); |
| _tasks.Add(task); |
| } |
| |
| public int ActiveCount => _tasks.Count(t => t.Status == TaskStatus.Running); |
| |
| public int CompletedCount => _tasks.Count(t => t.IsCompleted); |
| |
| public int TaskCount => _tasks.Count; |
| |
| public bool AllCompleted => _tasks.All(t => t.IsCompleted); |
| |
| public bool JoinAll(TimeSpan timeout) => Task.WhenAll(_tasks).Wait(timeout); |
| } |
| |
| private static readonly ConditionalWeakTable<TaskScheduler, TaskState> _taskFactories = new(); |
| |
| public static void Execute(this TaskScheduler scheduler, Action action) |
| { |
| if (!_taskFactories.TryGetValue(scheduler, out TaskState? state)) |
| { |
| state = new TaskState(scheduler); |
| _taskFactories.Add(scheduler, state); |
| } |
| |
| state.NewTask(action); |
| } |
| |
| public static bool AwaitTermination(this TaskScheduler scheduler, TimeSpan timeout) |
| { |
| if (_taskFactories.TryGetValue(scheduler, out TaskState? state)) |
| { |
| return state.JoinAll(timeout); |
| } |
| |
| return true; |
| } |
| |
| public static int GetActiveCount(this TaskScheduler scheduler) |
| { |
| if (_taskFactories.TryGetValue(scheduler, out TaskState? state)) |
| { |
| // Approximate the number of running threads, which shouldn't exceed the concurrency level |
| return Math.Min(scheduler.MaximumConcurrencyLevel, state.ActiveCount); |
| } |
| |
| return 0; |
| } |
| |
| public static int GetCompletedTaskCount(this TaskScheduler scheduler) |
| { |
| if (_taskFactories.TryGetValue(scheduler, out TaskState? state)) |
| { |
| return state.CompletedCount; |
| } |
| |
| return 0; |
| } |
| |
| public static int GetTaskCount(this TaskScheduler scheduler) |
| { |
| if (_taskFactories.TryGetValue(scheduler, out TaskState? state)) |
| { |
| return state.TaskCount; |
| } |
| |
| return 0; |
| } |
| |
| public static void Shutdown(this TaskScheduler scheduler) |
| { |
| if (scheduler is LimitedConcurrencyLevelTaskScheduler lcl) |
| { |
| lcl.Shutdown(); |
| } |
| } |
| |
| public static bool IsTerminated(this TaskScheduler scheduler) |
| { |
| if (scheduler is LimitedConcurrencyLevelTaskScheduler lcl |
| && _taskFactories.TryGetValue(scheduler, out TaskState? state)) |
| { |
| return lcl.IsShutdown && state.AllCompleted; |
| } |
| |
| return false; // can't be shut down, so can't be terminated |
| } |
| } |
| } |