Skip to content

Commit

Permalink
Add unit test to ensure dispose is only called once when Analyzer is …
Browse files Browse the repository at this point in the history
…disposed, apache#271
  • Loading branch information
paulirwin committed Dec 19, 2024
1 parent 90ad94d commit 61f5c0b
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -986,10 +986,6 @@ private static void CheckAnalysisConsistency(Random random, Analyzer a, bool use
// LUCENENET: We are doing this in the finally block to ensure it happens
// when there are exceptions thrown (such as when the assert fails).
ts.Close();

// ... and we dispose of it because it's the last use of the token stream
// before being reassigned below
ts.Dispose();
}

// verify reusing is "reproducable" and also get the normal tokenstream sanity checks
Expand Down Expand Up @@ -1047,7 +1043,6 @@ private static void CheckAnalysisConsistency(Random random, Analyzer a, bool use
finally
{
ts.Close();
ts.Dispose();
}
}
else if (evilness == 7)
Expand Down Expand Up @@ -1080,7 +1075,6 @@ private static void CheckAnalysisConsistency(Random random, Analyzer a, bool use
finally
{
ts.Close();
ts.Dispose();
}
}
}
Expand Down Expand Up @@ -1185,7 +1179,6 @@ private static void CheckAnalysisConsistency(Random random, Analyzer a, bool use
finally
{
ts.Close();
ts.Dispose();
}

if (field != null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
using Lucene.Net.Analysis.Core;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Analysis.TokenAttributes;
using Lucene.Net.Analysis.Util;
using Lucene.Net.Attributes;
using Lucene.Net.Util;
using NUnit.Framework;

#nullable enable

namespace Lucene.Net.Analysis
{
/*
* 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>
/// Tests for <see cref="BaseTokenStreamTestCase"/>.
/// </summary>
[TestFixture]
public class TestBaseTokenStreamTestCase : BaseTokenStreamTestCase
{
[Test]
[LuceneNetSpecific] // lucenenet#271
public void TestTokenStreamNotUsedAfterDispose()
{
DisposeTrackingLowerCaseFilter? leakedReference = null;

using (var a = Analyzer.NewAnonymous((_, reader) =>
{
// copied from StandardAnalyzer, but purposefully leaking our dispose tracking reference
var src = new StandardTokenizer(LuceneVersion.LUCENE_48, reader);
src.MaxTokenLength = 255;
TokenStream tok = new StandardFilter(LuceneVersion.LUCENE_48, src);
tok = leakedReference = new DisposeTrackingLowerCaseFilter(LuceneVersion.LUCENE_48, tok);
return new TokenStreamComponents(src, tok);
}))
{
CheckAnalysisConsistency(Random, a, false, "This is a test to make sure dispose is called only once");
}

Assert.IsNotNull(leakedReference);
Assert.IsTrue(leakedReference!.IsDisposed, "Dispose was not called on the token stream");
}

/// <summary>
/// LUCENENET specific class for <see cref="TestBaseTokenStreamTestCase.TestTokenStreamNotUsedAfterDispose"/>
/// that tracks whether <see cref="TokenStream.Dispose()"/> was called, and throws an exception
/// if it is called more than once, or if other operations are called after it is disposed.
/// Code copied from <see cref="LowerCaseFilter"/>.
/// </summary>
private class DisposeTrackingLowerCaseFilter : TokenFilter
{
private readonly CharacterUtils charUtils;
private readonly ICharTermAttribute termAtt;

public DisposeTrackingLowerCaseFilter(LuceneVersion matchVersion, TokenStream @in)
: base(@in)
{
termAtt = AddAttribute<ICharTermAttribute>();
charUtils = CharacterUtils.GetInstance(matchVersion);
}

protected override void Dispose(bool disposing)
{
if (!IsDisposed)
{
IsDisposed = true;
base.Dispose(disposing);
}
else
{
throw new AssertionException("Dispose called more than once on TokenStream instance");
}
}

public override bool IncrementToken()
{
if (IsDisposed)
{
throw new AssertionException("IncrementToken called after Dispose");
}

if (m_input.IncrementToken())
{
charUtils.ToLower(termAtt.Buffer, 0, termAtt.Length);
return true;
}
else
{
return false;
}
}

public override void End()
{
if (IsDisposed)
{
throw new AssertionException("End called after Dispose");
}

base.End();
}

public override void Reset()
{
if (IsDisposed)
{
throw new AssertionException("Reset called after Dispose");
}

base.Reset();
}

public override void Close()
{
if (IsDisposed)
{
throw new AssertionException("Close called after Dispose");
}

base.Close();
}

public bool IsDisposed { get; private set; }
}
}
}

0 comments on commit 61f5c0b

Please sign in to comment.