approvals / ApprovalTests.cpp

Native ApprovalTests for C++ on Linux, Mac and Windows
https://approvaltestscpp.readthedocs.io/en/latest/
Apache License 2.0
316 stars 51 forks source link

Provide a way to select the Reporter at run-time #99

Closed claremacrae closed 3 years ago

claremacrae commented 4 years ago

I’d really like to remove the need to edit and recompile to change the reporter used, probably by providing a command-line argument to do so.

We could base the option names on the reporter classes, with the Reporter suffix removed.

Examples that spring to mind:

-r BeyondCompareReporter
--reporter Quiet
-r Clipboard
-r AutoApprove
-r ‘C:\path to my program\...exe’
-r ‘mydifftool -1 {received} -2 {approved} &’
-r ‘someconsolediff {received} {approved}’

We would want to control whether the tool runs in the foreground or background, if it launches another process.

claremacrae commented 4 years ago

We could use one of these for implementation, if we could figure out how to include them in the single header without conflict for users, and with Catch2 which uses the first one too:

claremacrae commented 4 years ago

The Python implementation uses

pytest --approvaltests-use-reporter='PythonNative'

https://github.com/approvals/ApprovalTests.Python

alepez commented 4 years ago

What about an environmental variable? It would be simpler to implement and does not need new dependencies (we already have safeGetEnv in SystemUtils). The env var may be something like APPROVALTESTS_USE_REPORTER=Clipboard

And if in the future we want to add the opportunity to use cli arguments instead, the two methods can coexist (cli arguments override env var)

claremacrae commented 4 years ago

What about an environmental variable? It would be simpler to implement and does not need new dependencies (we already have safeGetEnv in SystemUtils). The env var may be something like APPROVALTESTS_USE_REPORTER=Clipboard

And if in the future we want to add the opportunity to use cli arguments instead, the two methods can coexist (cli arguments override env var)

Awesome idea - thank you!!!

alepez commented 4 years ago

And letting users select a different tool at runtime can be a workaround for known issues with clion #138

claremacrae commented 4 years ago

And letting users select a different tool at runtime can be a workaround for known issues with clion #138

I had a feeling you might be thinking of that!!!

Would you like to have a go at pairing on it?

alepez commented 4 years ago

Would you like to have a go at pairing on it?

Sure! Next week. I'll write you privately to schedule it.

claremacrae commented 4 years ago

Sketch of ideas discussed, so we don't lose it:

    APPROVAL_TESTS_REGISTER_REPORTER(ClipboardReporter);
    // auto reporter = ReporterFactory::reporter("ClipboardReporter");
    // auto reporter = ReporterRegistry::reporter("ClipboardReporter");

    // Static initialisation order fiasco
    //
}

// static initalisation

// 20 .cpp static init
    // factory

class ReporterFactory
{
    void registerReporter(name, reporter)
    {
        registry()->
    }
    std::map<std::string, std::shared_ptr<Reporter>>& registry()
    {
        static std::map<std::string, std::shared_ptr<Reporter>> registry;
    }
}
claremacrae commented 4 years ago

All that remains for this now is to write the documentation for the initial implementation and release it...

claremacrae commented 4 years ago

More thoughts...

  1. Maybe add some console output if the user sets the environment variable contains a value not recognised.

  2. We tested this in DocTest_Tests and it worked...

Then I remembered that the only reason it worked is because we had previously added this code to the main:

auto defaultReporterDisposer =
    Approvals::useAsDefaultReporter(std::make_shared<EnvironmentVariableReporter>());
  1. It's still unresolved whether to add support for this behaviour by default.
alepez commented 4 years ago

Oh! I didn't notice it was added here

If we make it enabled by default, it's not a breaking change, because the behavior when the env var is not defined is the same as before, right? So it may be added to DiffReporter (which is the default):

In file ApprovalTests/reporters/DiffReporter.cpp

namespace ApprovalTests
{
    DiffReporter::DiffReporter()
        : FirstWorkingReporter({
                                new EnvironmentVariableReporter(),
                                new Mac::MacDiffReporter(),
                                new Linux::LinuxDiffReporter(),
                                new Windows::WindowsDiffReporter()})
    {
    }
}
alepez commented 4 years ago

Note that now we can even select the right reporter for the current operating system. So, instead of trying MacDiffReporter first on Linux, we shouldgive priority to LinuxDiffReporter.

I made some experiments here some weeks ago.

But this should be another issue.

claremacrae commented 4 years ago

I experimented with exactly the same change as you suggested in DiffReporter above...

Right now, EnvironmentVariableReporter uses a DiffReporter as a fall-back option, which I think would cause an infinite loop... Changing EnvironmentVariableReporter to just return false on failure/no env var set works fine.

If we were to use EnvironmentVariableReporter on every call, I would suggest changing our new factory implementation so that it didn't create a new map of all the reporters on every call, as that's just wasted work - but that's a trivial change.

Let's keep experimenting with this, and see what it looks like in practice...

alepez commented 4 years ago

Right, now the ReporterFactory in EnvironmentVariableReporter is created each time report is called, while it should be a member of EnvironmentVariableReporter.

I think EnvironmentVariableReporter should return false on failure, so it has the same behavior as any other reporter, letting it compose well with external fallbacks systems (like FirstWorkingReporter)

alepez commented 4 years ago

Given that, if users want to say "use EnvironmentVariableReporter or fall back to my favourite reporter" they can just set the default reporter to FirstWorkingReporter({ new EnvironmentVariableReporter(), new MyFavouriteReporter() }) and that's all.

claremacrae commented 4 years ago

Thanks - We're thinking along the same lines...

This is a great idea - I hadn't thought of that:

Given that, if users want to say "use EnvironmentVariableReporter or fall back to my favourite reporter" they can just set the default reporter to FirstWorkingReporter({ new EnvironmentVariableReporter(), new MyFavouriteReporter() }) and that's all.

claremacrae commented 3 years ago

One scenario where this would be useful is where a team of several people work together on the same code, and they have different preferences as to which reporter to use, or maybe not all of them have licences to the same diff tool.

claremacrae commented 3 years ago

Remaining steps:

Code

Docs

Later

claremacrae commented 3 years ago

This code is now on master, and about to be released - thanks @alepez !