The listener will forward the progress notifications to the output window. The listener will ignore empty and duplicate messages (duplicate with the previous one notification progress message)
Inheritance: IDisposable
        public void ProgressNotificationListener_RespondToStepExecutionChangedEvent()
        {
            // Setup
            var serviceProvider = new ConfigurableServiceProvider();

            var outputWindow = new ConfigurableVsOutputWindow();
            var outputWindowPane = outputWindow.GetOrCreateSonarLintPane();
            serviceProvider.RegisterService(typeof(SVsOutputWindow), outputWindow);

            var progressEvents = new ConfigurableProgressEvents();
            var testSubject = new ProgressNotificationListener(serviceProvider, progressEvents);
            string message1 = "Hello world";
            string formattedMessage2 = "Bye bye";

            // Step 1: no formatting
            // Act
            progressEvents.SimulateStepExecutionChanged(message1, 0);

            // Verify
            outputWindowPane.AssertOutputStrings(message1);

            // Step 2: same message as before (ignore)
            // Act
            progressEvents.SimulateStepExecutionChanged(message1, 0);

            // Verify
            outputWindowPane.AssertOutputStrings(message1);

            // Step 3: whitespace message
            // Act
            progressEvents.SimulateStepExecutionChanged(" \t", 0);

            // Verify
            outputWindowPane.AssertOutputStrings(message1);

            // Step 4: formatting
            testSubject.MessageFormat = "XXX{0}YYY";
            // Act
            progressEvents.SimulateStepExecutionChanged(formattedMessage2, 0);

            // Verify
            outputWindowPane.AssertOutputStrings(message1, "XXX" + formattedMessage2 + "YYY");

            // Step 5: different message than the previous one
            testSubject.MessageFormat = null;
            // Act
            progressEvents.SimulateStepExecutionChanged(message1, 0);

            // Verify
            outputWindowPane.AssertOutputStrings(message1, "XXX" + formattedMessage2 + "YYY", message1);

            // Step 6: dispose
            testSubject.Dispose();
            // Act
            progressEvents.SimulateStepExecutionChanged("123", 0);

            // Verify
            outputWindowPane.AssertOutputStrings(message1, "XXX" + formattedMessage2 + "YYY", message1);
        }
        internal /*for testing purposes*/ void SetConnectionInProgress(IProgressEvents progressEvents)
        {
            this.IsConnectionInProgress = true;

            ProgressNotificationListener progressListener = new ProgressNotificationListener(this.ServiceProvider, progressEvents);
            progressListener.MessageFormat = Strings.ConnectingToSonarQubePrefixMessageFormat;

            progressEvents.RunOnFinished(result =>
            {
                progressListener.Dispose();
                this.IsConnectionInProgress = false;
                this.ShowNuGetWarning(result);
            });
        }
        internal /*for testing purposes*/ void SetBindingInProgress(IProgressEvents progressEvents, ProjectInformation projectInformation)
        {
            this.OnBindingStarted();

            ProgressNotificationListener progressListener = new ProgressNotificationListener(this.ServiceProvider, progressEvents);
            progressListener.MessageFormat = Strings.BindingSolutionPrefixMessageFormat;

            progressEvents.RunOnFinished(result =>
            {
                progressListener.Dispose();

                this.OnBindingFinished(projectInformation, result == ProgressControllerResult.Succeeded);
            });
        }