Skip to content

Commit

Permalink
ExceptionExtensions: Added support and tests for filtering types that…
Browse files Browse the repository at this point in the history
… correspond to RuntimeException in Java (see #446).
  • Loading branch information
NightOwl888 committed Apr 26, 2021
1 parent f032f22 commit 0a3ceed
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ internal static class ExceptionExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsIllegalArgumentException(this Exception e)
{
if (e is null || e.IsAlwaysIgnored()) return false;

return e is ArgumentException &&
!(e is ArgumentNullException) && // Corresponds to NullPointerException, so we don't catch it here.
!(e is ArgumentOutOfRangeException); // Corresponds to IndexOutOfBoundsException (and subclasses), so we don't catch it here.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,8 @@ private void Initialize()
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");
// 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ public void TestAbuseClosedIndexInput()
} // @out.close();
IndexInput @in = dir.OpenInput("foo", IOContext.DEFAULT);
@in.Dispose();
Assert.Throws<LuceneException>(() => @in.ReadByte());
Assert.Throws<LuceneSystemException>(() => @in.ReadByte());
}

// LUCENENET: This test compiles, but is not compatible with 4.8.0 (tested in Java Lucene), as it was ported from 8.2.0
Expand Down
2 changes: 1 addition & 1 deletion src/Lucene.Net.Tests/Analysis/TestGraphTokenizers.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using J2N.Text;
using J2N.Text;
using Lucene.Net.Analysis.TokenAttributes;
using Lucene.Net.Diagnostics;
using NUnit.Framework;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using J2N.Text;
using Lucene.Net.Attributes;
using Lucene.Net.Index;
using Lucene.Net.Queries.Function.DocValues;
using Lucene.Net.Search;
using Lucene.Net.Util;
using NUnit.Framework;
using NUnit.Framework.Interfaces;
Expand All @@ -9,6 +12,9 @@
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Security;
using System.Threading;
using Assert = Lucene.Net.TestFramework.Assert;
using JCG = J2N.Collections.Generic;

Expand Down Expand Up @@ -53,6 +59,9 @@ public class TestExceptionExtensions : LuceneTestCase
// .NET Core 2.1 Only
Type.GetType("System.CrossAppDomainMarshaledException, System.Private.CoreLib");

private static readonly Type NUnitFrameworkInternalInvalidPlatformExceptionType =
Type.GetType("NUnit.Framework.Internal.InvalidPlatformException, NUnit.Framework");


// Load exception types from all assemblies
private static readonly Assembly[] LuceneAssemblies = new Assembly[]
Expand Down Expand Up @@ -158,12 +167,26 @@ private static IEnumerable<Type> LoadKnownErrorExceptionTypes()
typeof(Lucene.Net.QueryParsers.Flexible.Core.QueryNodeError),
typeof(Lucene.Net.QueryParsers.Flexible.Standard.Parser.TokenMgrError),
typeof(Lucene.Net.QueryParsers.Surround.Parser.TokenMgrError),

typeof(NUnit.Framework.SuccessException), // Not sure about this, but it seems reasonable to ignore it in most cases because it is NUnit result state
};
}

private static readonly IEnumerable<Type> KnownExceptionTypes = AllExceptionTypes
// Exceptions in Java exclude Errors
.Except(KnownErrorExceptionTypes)
// Special Case: We never want to catch this NUnit exception
.Where(t => !Type.Equals(t, NUnitFrameworkInternalInvalidPlatformExceptionType));

private static readonly IEnumerable<Type> KnownThrowableExceptionTypes = AllExceptionTypes
// Special Case: We never want to catch this NUnit exception
.Where(t => !Type.Equals(t, NUnitFrameworkInternalInvalidPlatformExceptionType));


private static readonly IEnumerable<Type> KnownIOExceptionTypes = new Type[] {
typeof(UnauthorizedAccessException),
typeof(ObjectDisposedException),
typeof(Lucene.AlreadyClosedException),
}.Union(AllIOExceptionTypes);

private static readonly IEnumerable<Type> KnownIndexOutOfBoundsExceptionTypes = new Type[] {
Expand All @@ -190,16 +213,177 @@ private static IEnumerable<Type> LoadKnownErrorExceptionTypes()
typeof(ArgumentOutOfRangeException),

// Types for use as Java Aliases in .NET
typeof(IllegalArgumentException),
typeof(Lucene.IllegalArgumentException),
typeof(Lucene.ArrayIndexOutOfBoundsException),
typeof(Lucene.IndexOutOfBoundsException),
typeof(Lucene.NullPointerException), // ArgumentNullException subclass
typeof(Lucene.StringIndexOutOfBoundsException),

// Subclasses
typeof(System.DuplicateWaitObjectException),
typeof(System.Globalization.CultureNotFoundException),
typeof(System.Text.DecoderFallbackException),
typeof(System.Text.EncoderFallbackException),
};

private static readonly IEnumerable<Type> KnownIllegalArgumentExceptionTypes_TestEnvironment = new Type[] {
typeof(ArgumentException),

// Types for use as Java Aliases in .NET
typeof(IllegalArgumentException),

// Subclasses
typeof(System.DuplicateWaitObjectException),
typeof(System.Globalization.CultureNotFoundException),
typeof(System.Text.DecoderFallbackException),
typeof(System.Text.EncoderFallbackException),
};

private static readonly IEnumerable<Type> KnownRuntimeExceptionTypes = LoadKnownRuntimeExceptionTypes();

private static IEnumerable<Type> LoadKnownRuntimeExceptionTypes()
{
var result = new HashSet<Type>
{

// ******************************************************************************************
// CONFIRMED TYPES - these are for sure mapping to a type in Java that we want to catch
// ******************************************************************************************

typeof(SystemException), // Roughly corresponds to RuntimeException

// Corresponds to IndexOutOfBoundsException, StringIndexOutOfBoundsException, and ArrayIndexOutOfBoundsException
typeof(IndexOutOfRangeException),
typeof(ArgumentOutOfRangeException),
typeof(Lucene.ArrayIndexOutOfBoundsException),
typeof(Lucene.IndexOutOfBoundsException),
typeof(Lucene.StringIndexOutOfBoundsException),

// Corresponds to NullPointerException
typeof(NullReferenceException),
typeof(ArgumentNullException),
typeof(Lucene.NullPointerException),

// Corresponds to IllegalArgumentException
typeof(ArgumentException),
typeof(Lucene.IllegalArgumentException),

// Corresponds to UnsupportedOperationException
typeof(NotSupportedException),
typeof(Lucene.UnsupportedOperationException),

// Corresponds to Lucene's ThreadInterruptedException
typeof(ThreadInterruptedException),

// Corresponds to SecurityException
typeof(SecurityException),

// Corresponds to ClassCastException
typeof(InvalidCastException),

// Corresponds to IllegalStateException
typeof(InvalidOperationException),
typeof(Lucene.IllegalStateException),

// Corresponds to MissingResourceException
typeof(MissingManifestResourceException),

// Corresponds to NumberFormatException
typeof(FormatException),
typeof(Lucene.NumberFormatException),

// Corresponds to ArithmeticException
typeof(ArithmeticException),

// Corresponds to IllformedLocaleException
typeof(CultureNotFoundException),

// Corresponds to JUnit's AssumptionViolatedException
typeof(NUnit.Framework.InconclusiveException),

// Known implementations of IRuntimeException

typeof(RuntimeException),
typeof(LuceneSystemException),

typeof(BytesRefHash.MaxBytesLengthExceededException),
typeof(CollectionTerminatedException),
typeof(DocTermsIndexDocValues.DocTermsIndexException),
typeof(MergePolicy.MergeException),
typeof(SearcherExpiredException),
typeof(TimeLimitingCollector.TimeExceededException),
typeof(BooleanQuery.TooManyClausesException),

// Other known runtime exceptions
typeof(AlreadySetException), // Subclasses InvalidOperationException
typeof(J2N.IO.BufferUnderflowException),
typeof(J2N.IO.BufferOverflowException),
typeof(J2N.IO.InvalidMarkException),
typeof(Lucene.Net.Spatial.Queries.UnsupportedSpatialOperation), // Subclasses NotSupportedException

//typeof(NUnit.Framework.Internal.InvalidPlatformException),

// ******************************************************************************************
// UNCONFIRMED TYPES - these are SystemException types that are included, but require more
// research to determine whether they actually are something we don't want to catch as a RuntimeException.
// ******************************************************************************************

typeof(AccessViolationException),
typeof(AppDomainUnloadedException),
typeof(ArrayTypeMismatchException),
typeof(BadImageFormatException),
typeof(CannotUnloadAppDomainException),
typeof(KeyNotFoundException),
typeof(ContextMarshalException),
typeof(DataMisalignedException),
typeof(DivideByZeroException), // Subclasses ArithmeticException, so probably okay
typeof(DllNotFoundException),
typeof(DuplicateWaitObjectException),
typeof(EntryPointNotFoundException),
typeof(ExecutionEngineException),
typeof(InsufficientExecutionStackException),
typeof(InvalidProgramException),
typeof(InvalidDataException),
typeof(MulticastNotSupportedException),
typeof(NotFiniteNumberException), // Subclasses ArithmeticException, so probably okay
typeof(NotImplementedException),
typeof(OperationCanceledException),
typeof(OverflowException), // Subclasses ArithmeticException, so probably okay
typeof(PlatformNotSupportedException),
typeof(RankException),
typeof(System.Reflection.CustomAttributeFormatException), // Maybe like AnnotationTypeMismatchException in Java...?
typeof(System.Resources.MissingSatelliteAssemblyException),
typeof(System.Runtime.CompilerServices.SwitchExpressionException),
typeof(System.Runtime.InteropServices.COMException),
typeof(System.Runtime.InteropServices.ExternalException),
typeof(System.Runtime.InteropServices.InvalidComObjectException),
typeof(System.Runtime.InteropServices.InvalidOleVariantTypeException),
typeof(System.Runtime.InteropServices.MarshalDirectiveException),
typeof(System.Runtime.InteropServices.SafeArrayRankMismatchException),
typeof(System.Runtime.InteropServices.SafeArrayTypeMismatchException),
typeof(System.Runtime.InteropServices.SEHException),
typeof(System.Runtime.Serialization.SerializationException),
typeof(System.Security.Cryptography.CryptographicException),
typeof(System.Security.VerificationException),
typeof(System.Text.DecoderFallbackException), // LUCENENET TODO: Need to be sure about this one
typeof(System.Text.EncoderFallbackException), // LUCENENET TODO: Need to be sure about this one
typeof(System.Threading.AbandonedMutexException),
typeof(System.Threading.SemaphoreFullException),
typeof(System.Threading.SynchronizationLockException),
typeof(System.Threading.Tasks.TaskCanceledException),
typeof(System.Threading.ThreadAbortException),
typeof(System.Threading.ThreadStartException),
typeof(System.Threading.ThreadStateException),
typeof(System.TimeoutException),
typeof(System.TypeAccessException),
typeof(System.TypeInitializationException),
typeof(System.TypeLoadException),
typeof(System.TypeUnloadedException),
};

return result;
}

#endregion Known types of exception families

#region Special case constructors
Expand Down Expand Up @@ -365,9 +549,9 @@ public static IEnumerable<TestCaseData> ThrowableTypeExpressions
{
// expectedToThrow is true when we expect the error to be thrown and false when we expect it to be caught
yield return new TestCaseData(
exceptionType, // exception type (to make NUnit display them all)
true, // expectedToThrow
new Action(() => ThrowException(exceptionType))); // throw exception expression
exceptionType, // exception type (to make NUnit display them all)
!KnownThrowableExceptionTypes.Contains(exceptionType), // expectedToThrow
new Action(() => ThrowException(exceptionType))); // throw exception expression
}
}
}
Expand Down Expand Up @@ -397,12 +581,27 @@ public static IEnumerable<TestCaseData> ExceptionTypeExpressions
// expectedToThrow is true when we expect the error to be thrown and false when we expect it to be caught
yield return new TestCaseData(
exceptionType, // exception type (to make NUnit display them all)
KnownErrorExceptionTypes.Contains(exceptionType), // expectedToThrow
!KnownExceptionTypes.Contains(exceptionType), // expectedToThrow
new Action(() => ThrowException(exceptionType))); // throw exception expression
}
}
}

public static IEnumerable<TestCaseData> RuntimeExceptionTypeExpressions
{
get
{
foreach (var exceptionType in AllExceptionTypes)
{
// expectedToThrow is true when we expect the error to be thrown and false when we expect it to be caught
yield return new TestCaseData(
exceptionType, // exception type (to make NUnit display them all)
!KnownRuntimeExceptionTypes.Contains(exceptionType),// expectedToThrow
new Action(() => ThrowException(exceptionType))); // throw exception expression
}
}
}

public static IEnumerable<TestCaseData> IOExceptionTypeExpressions
{
get
Expand Down Expand Up @@ -579,6 +778,24 @@ public void TestIsException(Type exceptionType, bool expectedToThrow, Action exp
}
}

// This test ensures that all known Error types from Java are not caught by
// our IsRuntimeException() handler.
[Test]
[TestCaseSource("RuntimeExceptionTypeExpressions")]
public void TestIsRuntimeException(Type exceptionType, bool expectedToThrow, Action expression) // LUCENENET NOTE: exceptionType is only here to make NUnit display them all
{
static bool extensionMethod(Exception e) => e.IsRuntimeException();

if (expectedToThrow)
{
AssertDoesNotCatch(expression, extensionMethod);
}
else
{
AssertCatches(expression, extensionMethod);
}
}

[Test]
[TestCaseSource("IOExceptionTypeExpressions")]
public void TestIsIOException(Type exceptionType, bool expectedToThrow, Action expression) // LUCENENET NOTE: exceptionType is only here to make NUnit display them all
Expand Down Expand Up @@ -722,13 +939,20 @@ private void AssertDoesNotCatch(Action action, Func<Exception, bool> extensionMe
{
action();
}
catch (NUnit.Framework.AssertionException e)
{
// Special case - need to suppress this from being thrown to the outer catch
// or we will get a false failure
Assert.IsFalse(extensionMethodExpression(e));
Assert.Pass($"Expected: Did not catch exception {e.GetType().FullName}");
}
catch (Exception e) when (extensionMethodExpression(e))
{
// not expected
Assert.Fail($"Exception caught when expected to be thrown: {e.GetType().FullName}");
}
}
catch (Exception e)
catch (Exception e) when (!(e is NUnit.Framework.AssertionException))
{
// expected
Assert.Pass($"Expected: Did not catch exception {e.GetType().FullName}");
Expand Down
Loading

0 comments on commit 0a3ceed

Please sign in to comment.