Skip to content

test: aumenta cobertura para 99,8%#104

Merged
afonsoft merged 2 commits into
mainfrom
feature/devin-20260712-coverage-tests
Jul 12, 2026
Merged

test: aumenta cobertura para 99,8%#104
afonsoft merged 2 commits into
mainfrom
feature/devin-20260712-coverage-tests

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

All Submissions:

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same update/change?

New Feature Submissions:

  1. Does your submission pass tests?
  2. Have you lint your code locally prior to submission?

Changes to Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

Summary

Aumenta a cobertura de testes para 99,8% line e 99,5% branch.

Novos testes:

  • Value.ToInt(null) / Value.ToInt("") retorna null (METAR e TAF)
  • RunwayVisualRangeChunkDecoder.ToValue com string vazia e string inválida
  • EvolutionChunkDecoder:
    • AddEvolution com entidade desconhecida
    • HandleDecodeFailure em modo estrito e não-estrito
    • ApplyDecodedChunk com resultado inválido e entidade nula
  • MetarDecoder.ApplyDecodedData com propriedade desconhecida
  • TafDecoder.ApplyDecodedData com propriedade desconhecida

Validação

  • dotnet test tests/Metar.Decoder.Tests/ net8.0/net10.0 — 244/244 passando
  • dotnet test tests/Taf.Decoder.Tests/ net8.0/net10.0 — 200/200 passando
  • Cobertura local: 99,8% line, 99,5% branch

Observações

As 2 linhas restantes sem cobertura em EvolutionChunkDecoder são caminhos defensivos/inacessíveis (default do switch de AddEvolution não é alcançado porque InstantiateEntity já lança para entidades desconhecidas).

Link to Devin session: https://app.devin.ai/sessions/94a258020ea4488c8eecc672035d0da1
Requested by: @afonsoft


Open in Devin Review

afonsoft and others added 2 commits July 12, 2026 18:29
…llow-up

fix: torna DictionaryExtensions public e remove using redundante
…yDecodedData)

Co-Authored-By: Afonso Dutra Nogueira Filho <[email protected]>
@afonsoft afonsoft self-assigned this Jul 12, 2026
@afonsoft
afonsoft self-requested a review July 12, 2026 21:36
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@afonsoft

Copy link
Copy Markdown
Owner

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

Qodana Community for .NET

27 new problems were found

Inspection name Severity Problems
Possible 'System.NullReferenceException' 🔶 Warning 15
Access to a static member of a type via a derived type 🔶 Warning 8
Local variable hides member 🔶 Warning 4

💡 Qodana analysis was run in the pull request mode: only the changed files were checked

View the detailed Qodana report

To be able to view the detailed Qodana report, you can either:

To get *.log files or any other Qodana artifacts, run the action with upload-result option set to true,
so that the action will upload the files as the job artifacts:

      - name: 'Qodana Scan'
        uses: JetBrains/[email protected]
        with:
          upload-result: true
Contact Qodana team

Contact us at [email protected]

@afonsoft
afonsoft merged commit 7df1872 into main Jul 12, 2026
18 of 22 checks passed

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment on lines +306 to +390
public void TestAddEvolutionThrowsForUnknownEntity()
{
var method = typeof(EvolutionChunkDecoder).GetMethod("AddEvolution", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var decoder = new EvolutionChunkDecoder(true, false);
var decodedTaf = new DecodedTaf("TAF KJFK 080500Z 0806/0910 23010KT");
var evolution = new Evolution();
var result = new Dictionary<string, object> { { "Type", new object() } };

var ex = ClassicAssert.Throws<System.Reflection.TargetInvocationException>(() =>
{
method.Invoke(decoder, new object[] { decodedTaf, evolution, result, "Type" });
});

ClassicAssert.That(ex.InnerException, Is.InstanceOf<TafChunkDecoderException>());
}

/// <summary>
/// Test HandleDecodeFailure in strict mode throws.
/// </summary>
[Test]
public void TestHandleDecodeFailureStrictThrows()
{
var method = typeof(EvolutionChunkDecoder).GetMethod("HandleDecodeFailure", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var decoder = new EvolutionChunkDecoder(true, true);
var remaining = "AAA";
var ex = ClassicAssert.Throws<System.Reflection.TargetInvocationException>(() =>
{
method.Invoke(decoder, new object[] { "BAD", remaining });
});
ClassicAssert.That(ex.InnerException, Is.InstanceOf<TafChunkDecoderException>());
}

/// <summary>
/// Test HandleDecodeFailure in non-strict mode consumes one chunk.
/// </summary>
[Test]
public void TestHandleDecodeFailureNonStrictConsumesChunk()
{
var method = typeof(EvolutionChunkDecoder).GetMethod("HandleDecodeFailure", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var decoder = new EvolutionChunkDecoder(false, false);
var parameters = new object[] { "BAD", "BAD REMAINING" };
method.Invoke(decoder, parameters);
ClassicAssert.That(parameters[1], Is.EqualTo("REMAINING"));
}

/// <summary>
/// Test ApplyDecodedChunk throws when result is not a dictionary.
/// </summary>
[Test]
public void TestApplyDecodedChunkThrowsForInvalidResult()
{
var method = typeof(EvolutionChunkDecoder).GetMethod("ApplyDecodedChunk", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var decoder = new EvolutionChunkDecoder(true, false);
var decodedTaf = new DecodedTaf("TAF KJFK 080500Z 0806/0910 23010KT");
var evolution = new Evolution();
var decoded = new Dictionary<string, object> { { TafDecoder.ResultKey, "not a dictionary" }, { TafDecoder.RemainingTafKey, "AAA" } };

var ex = ClassicAssert.Throws<System.Reflection.TargetInvocationException>(() =>
{
method.Invoke(decoder, new object[] { evolution, "AAA", decodedTaf, decoded });
});

ClassicAssert.That(ex.InnerException, Is.InstanceOf<TafChunkDecoderException>());
}

/// <summary>
/// Test ApplyDecodedChunk throws when entity is null and not visibility.
/// </summary>
[Test]
public void TestApplyDecodedChunkThrowsForNullEntity()
{
var method = typeof(EvolutionChunkDecoder).GetMethod("ApplyDecodedChunk", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var decoder = new EvolutionChunkDecoder(true, false);
var decodedTaf = new DecodedTaf("TAF KJFK 080500Z 0806/0910 23010KT");
var evolution = new Evolution();
var result = new Dictionary<string, object> { { SurfaceWindChunkDecoder.SurfaceWindParameterName, null } };
var decoded = new Dictionary<string, object> { { TafDecoder.ResultKey, result }, { TafDecoder.RemainingTafKey, "AAA" } };

var ex = ClassicAssert.Throws<System.Reflection.TargetInvocationException>(() =>
{
method.Invoke(decoder, new object[] { evolution, "AAA", decodedTaf, decoded });
});

ClassicAssert.That(ex.InnerException, Is.InstanceOf<TafChunkDecoderException>());
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Tests use reflection to access private methods, creating coupling to implementation details

All new tests in this PR use System.Reflection.BindingFlags.NonPublic to invoke private/static methods directly (e.g., RunwayVisualRangeChunkDecoder.ToValue, MetarDecoder.ApplyDecodedData, EvolutionChunkDecoder.HandleDecodeFailure). While this achieves the coverage goal, these tests will break silently (at runtime, not compile time) if any of these methods are renamed, have their signatures changed, or are refactored. Consider whether some of these error paths could be tested through the public API instead, which would be more resilient to refactoring.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +342 to +349
public void TestHandleDecodeFailureNonStrictConsumesChunk()
{
var method = typeof(EvolutionChunkDecoder).GetMethod("HandleDecodeFailure", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var decoder = new EvolutionChunkDecoder(false, false);
var parameters = new object[] { "BAD", "BAD REMAINING" };
method.Invoke(decoder, parameters);
ClassicAssert.That(parameters[1], Is.EqualTo("REMAINING"));
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Reflection-based ref parameter test relies on subtle .NET behavior

The test at line 346-348 relies on the fact that when invoking a method with a ref parameter via MethodInfo.Invoke, the parameters array is modified in-place to reflect the updated value. This is correct .NET behavior but is non-obvious. The HandleDecodeFailure method signature is (string chunk, ref string remainingEvolutions) at src/Taf.Decoder/ChunkDecoder/EvolutionChunkDecoder.cs:173, and ConsumeOneChunk("BAD REMAINING") correctly returns "REMAINING" by splitting on the first space.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

public void TestAddEvolutionThrowsForUnknownEntity()
{
var method = typeof(EvolutionChunkDecoder).GetMethod("AddEvolution", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var decoder = new EvolutionChunkDecoder(true, false);
public void TestHandleDecodeFailureStrictThrows()
{
var method = typeof(EvolutionChunkDecoder).GetMethod("HandleDecodeFailure", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var decoder = new EvolutionChunkDecoder(true, true);
public void TestHandleDecodeFailureNonStrictConsumesChunk()
{
var method = typeof(EvolutionChunkDecoder).GetMethod("HandleDecodeFailure", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var decoder = new EvolutionChunkDecoder(false, false);
public void TestApplyDecodedChunkThrowsForInvalidResult()
{
var method = typeof(EvolutionChunkDecoder).GetMethod("ApplyDecodedChunk", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var decoder = new EvolutionChunkDecoder(true, false);
public void TestApplyDecodedChunkThrowsForNullEntity()
{
var method = typeof(EvolutionChunkDecoder).GetMethod("ApplyDecodedChunk", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var decoder = new EvolutionChunkDecoder(true, false);
@afonsoft
afonsoft deleted the feature/devin-20260712-coverage-tests branch July 13, 2026 01:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants