Skip to content
Merged
13 changes: 12 additions & 1 deletion src/Tasks.UnitTests/Exec_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,18 @@ public void LoggedErrorsCauseFailureDespiteExitCode0()
Assert.False(result);
// Exitcode is set to -1
Assert.Equal(-1, exec.ExitCode);
((MockEngine)exec.BuildEngine).AssertLogContains("MSB3073");

MockEngine engine = (MockEngine)exec.BuildEngine;

// Should log the special "exited zero with errors" message
engine.AssertLogContains("MSB3077");

// Should not log other failure-related error codes
engine.AssertLogDoesntContain("MSB3073");
engine.AssertLogDoesntContain("MSB6006");

// Errors: canonical error from tool output + MSB3077 from HandleTaskExecutionErrors
engine.Errors.ShouldBe(2);
}

[Fact]
Expand Down
67 changes: 67 additions & 0 deletions src/Tasks.UnitTests/XamlDataDrivenToolTask_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@
using System.IO;
using System.Reflection;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Microsoft.Build.Tasks;
using Microsoft.Build.Tasks.Xaml;
using Microsoft.Build.Utilities;
using Shouldly;
using Xunit;
using Xunit.Abstractions;

#nullable disable

Expand Down Expand Up @@ -382,4 +388,65 @@ public void SquareBracketEscaping()
logger.AssertLogContains("echo 14) [[notaproperty]] end");
}
}

/// <summary>
/// Tests for XamlDataDrivenToolTask error handling using direct task instantiation.
/// </summary>
public class ErrorHandlingTests
{
private readonly ITestOutputHelper _output;

public ErrorHandlingTests(ITestOutputHelper output)
{
_output = output;
}

/// <summary>
/// Tests that when a XamlDataDrivenTask's tool exits with code 0 but canonical errors
/// were logged during execution, the special "exited zero with errors" message (MSB3725)
/// is used instead of the normal command failure message (MSB3721).
/// </summary>
[Fact]
public void ExitCodeZeroWithLoggedErrors_LogsMSB3725()
{
var cmdLine = NativeMethodsShared.IsWindows
? "echo myfile(88,37): error AB1234: thisisacanonicalerror"
: "echo \"myfile(88,37): error AB1234: thisisacanonicalerror\"";

var task = new TestableXamlDataDrivenToolTask();
task.BuildEngine = new MockEngine(_output);
task.CommandLineTemplate = cmdLine;
task.EchoOff = true;

bool result = task.Execute();

result.ShouldBeFalse();

MockEngine engine = (MockEngine)task.BuildEngine;

// Should log the special "exited zero with errors" message, not the normal command failure
engine.AssertLogContains("MSB3725");

// Should not log other error messages related to XamlDataDrivenToolTask command failure
engine.AssertLogDoesntContain("MSB3721");
engine.AssertLogDoesntContain("MSB3722");

// Errors: canonical error from tool output + MSB3725 from HandleTaskExecutionErrors
engine.Errors.ShouldBe(2);
}
}

/// <summary>
/// A concrete subclass of XamlDataDrivenToolTask for direct unit testing
/// without requiring a project file or XamlTaskFactory.
/// </summary>
internal sealed class TestableXamlDataDrivenToolTask : XamlDataDrivenToolTask
{
public TestableXamlDataDrivenToolTask()
: base(Array.Empty<string>(), null)
{
}

protected override string ToolName => "unused";
}
}
5 changes: 5 additions & 0 deletions src/Tasks/Exec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,15 @@ protected override bool HandleTaskExecutionErrors()
{
Log.LogErrorWithCodeFromResources("Exec.CommandFailedAccessDenied", commandForLog, ExitCode);
}
else if (ExitCodeOverriddenToIndicateErrors)
{
Log.LogErrorWithCodeFromResources("Exec.CommandExitedZeroWithErrors", commandForLog);
}
else
{
Log.LogErrorWithCodeFromResources("Exec.CommandFailed", commandForLog, ExitCode);
}

return false;
}

Expand Down
8 changes: 8 additions & 0 deletions src/Tasks/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,10 @@
<data name="Exec.CommandFailedNoErrorCode">
<value>The command "{0}" exited with code {1}.</value>
</data>
<data name="Exec.CommandExitedZeroWithErrors">
<value>MSB3077: The command "{0}" exited with return value 0, but errors were detected during execution. ExitCode was set to -1.</value>
<comment>{StrBegin="MSB3077: "}</comment>
</data>
<data name="Exec.InvalidRegex">
<value>MSB3076: The regular expression "{0}" that was supplied is invalid. {1}</value>
<comment>{StrBegin="MSB3076: "}</comment>
Expand Down Expand Up @@ -2311,6 +2315,10 @@
<value>MSB3722: The command "{0}" exited with code {1}. Please verify that you have sufficient rights to run this command.</value>
<comment>{StrBegin="MSB3722: "}</comment>
</data>
<data name="Xaml.CommandExitedZeroWithErrors">
Comment thread
OvesN marked this conversation as resolved.
Outdated
<value>MSB3725: The command "{0}" exited with return value 0, but errors were detected during execution. ExitCode was set to -1.</value>
<comment>{StrBegin="MSB3725: "}</comment>
</data>
<data name="Xaml.MissingRequiredArgument">
<value>MSB3723: The parameter "{0}" requires missing parameter "{1}" to be set.</value>
<comment>{StrBegin="MSB3723: "}</comment>
Expand Down
10 changes: 10 additions & 0 deletions src/Tasks/Resources/xlf/Strings.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Tasks/Resources/xlf/Strings.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Tasks/Resources/xlf/Strings.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Tasks/Resources/xlf/Strings.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Tasks/Resources/xlf/Strings.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/Tasks/Resources/xlf/Strings.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading