Skip to content

Dry jobs can eat iteration failures #1045

@danmoseley

Description

@danmoseley

BDN Version

Benchmarks 0.1.1-preview2-190203-1

Repro

Consider a benchmark where the method under test sets some result which the [GlobalCleanup] examines. If the result is not set, [GlobalCleanup] will throw. Assume the test method itself has a bug, and throws an exception:

using System;
using System.IO;
using System.Text;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace Foo
{
    [DryJob] // Important!
    public class BarBaz
    {
        string _result;

        public static void Main(string[] args)
            => BenchmarkRunner.Run(typeof(BarBaz));

        [GlobalCleanup]
        public void CheckResults()
        {
            Console.WriteLine("global cleanup");
            Console.WriteLine("result " + _result.Length);
        }

        [Benchmark]
        public void BizBoz()
        {`
            Console.WriteLine("starting iteration");
            if ((new Random()).Next(2) < 100) throw new Exception("bad iteration");
            _result = "foo";
            Console.WriteLine("finishing iteration");
        }
    }
}

Run with
dotnet run -c Release

Expected

The callstack for the test method exception, possibly followed by the callstack for the cleanup exception. BDN should not hide a test method failure.

Actual

The callstack for the cleanup exception.

...
// BeforeActualRun
starting iteration
global cleanup

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at Foo.BarBaz.CheckResults() in /home/dan/git/corefxlab/tfoo.cs:line 29
   at BenchmarkDotNet.Engines.Engine.Dispose()
   at BenchmarkDotNet.Autogenerated.Runnable_0.Run(IHost host, String benchmarkName) in /home/dan/git/corefxlab/tests/Benchmarks/bin/Release/netcoreapp3.0/41182a39-ec98-4e84-a2ad-bc9ea467e163/41182a39-ec98-4e84-a2ad-bc9ea467e163.notcs:line 157
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at BenchmarkDotNet.Autogenerated.UniqueProgramName.AfterAssemblyLoadingAttached(String[] args) in /home/dan/git/corefxlab/tests/Benchmarks/bin/Release/netcoreapp3.0/41182a39-ec98-4e84-a2ad-bc9ea467e163/41182a39-ec98-4e84-a2ad-bc9ea467e163.notcs:line 60
// AfterAll
...

This caused me confusion for some time, as I thought the test method was somehow not completing before the cleanup ran (the actual method made a lot of use of asynchrony, I assumed I was not correctly waiting on it to complete, and that explained why I did not see my "finishing iteration" message) when actually it was simply throwing an exception due to a bug in the code under test.

If I remove [DryJob] I get the expected result.

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions