Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix conditions for transcription of Write-Information command. #6917

Merged
merged 12 commits into from
Jun 16, 2018
Merged

Fix conditions for transcription of Write-Information command. #6917

merged 12 commits into from
Jun 16, 2018

Conversation

hubuk
Copy link
Contributor

@hubuk hubuk commented May 22, 2018

PR Summary

Fix for #6916.

This change makes transcription of Write-Information command consistent with $InfomrationPreference variable.
Currently Write-Information command is being transcribed if $InfomrationPreference is set to 'SilentlyContinue' and not transcribed if set to 'Continue'.

The problem is in if/else statement which starts here:

if ((record.Tags.Contains("PSHOST") && (!record.Tags.Contains("FORWARDED")))
|| (preference == ActionPreference.Continue))

and has a continuation here:
else
{
// Only transcribe informational messages here. Transcription of PSHost-targeted messages is done in the InternalUI.Write* methods.
CBhost.InternalUI.TranscribeResult(StringUtil.Format(InternalHostUserInterfaceStrings.InformationFormatString, record.ToString()));
}

Transcription of Write-Host commands are handled in InternalUI.Write* methods as the comment suggests. These commands are distinguished by "PSHOST" tag.
Transcription of Write-Information commands is supposed to take place in the else statement but code in this statement is being executed only if

(!record.Tags.Contains("PSHOST") || record.Tags.Contains("FORWARDED"))
&&
preference != ActionPreference.Continue)

This condition is invalid. Correct condition is:

!record.Tags.Contains("PSHOST")
&&
preference == ActionPreference.Continue)

PR Checklist

@iSazonov
Copy link
Collaborator

@hubuk Thanks for your contribution. I believe we need to get a conclusion in Issue before we continue the PR.

@hubuk
Copy link
Contributor Author

hubuk commented May 22, 2018

@iSazonov Sure. I need to check why a new test is failing. It was OK on my local machine.

@hubuk
Copy link
Contributor Author

hubuk commented May 22, 2018

I found that tests are executed with redirection to a pipe.
In this case all Write-* commands are always logged in the transcript file no matter what preference is set.
According to this I removed the second test (no transcription for 'SilentlyContinue') as it cannot be satisfied by current test environment.

@iSazonov
Copy link
Collaborator

@mklement0 Could you please review the PR?

Copy link
Collaborator

@iSazonov iSazonov left a comment

Choose a reason for hiding this comment

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

Leave a comment

& $script
Test-Path $transcriptFilePath | Should -BeTrue

$transcriptFilePath | Should FileContentMatch 'Continue'
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please use new syntax Should -FileContentMatch.

Copy link
Contributor

@mklement0 mklement0 left a comment

Choose a reason for hiding this comment

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

Perhaps I'm missing something, but it it looks like you accidentally reverted the original fix.

@hubuk
Copy link
Contributor Author

hubuk commented May 23, 2018

@mklement0 You are right. My bad...
And it proved that newly written test is not that useful...
Will fix it shortly.

@hubuk
Copy link
Contributor Author

hubuk commented May 23, 2018

It should be OK now. Test was passing for the same reason why my previous test was failing.
All Write-* commands are transcribed if redirected to a pipe.

@mklement0
Copy link
Contributor

@hubuk:

All Write-* commands are transcribed if redirected to a pipe.

Can you clarify what that means, specifically, and perhaps give an example?
Is that a problem in itself or by-design behavior?

@iSazonov
Copy link
Collaborator

@hubuk Please don't fix CodeFactor issues in the PR (It's hard to review meaningful changes) - all style changes should be in new PR.

@hubuk
Copy link
Contributor Author

hubuk commented May 24, 2018

@iSazonov Reverted the change.

@mklement0

All Write-* commands are transcribed if redirected to a pipe.

Sorry, for wrong information I provided:
Tests was failing because of too simple data to check. 'Continue' was also part of the pwsh.exe command line:
Host Application: ...\bin\Release\netcoreapp2.1\win7-x64\publish\pwsh.dll -noprofile -c $env:POWERSHELL_TELEMETRY_OPTOUT = 1;$ProgressPreference = 'silentlyContinue';...
and this is part of the transcription file.
I still need to investigate my previous observations. For now lets assume that this is not an issue.

@hubuk hubuk changed the title Fix conditions for transcription of Write-Information command. WIP Fix conditions for transcription of Write-Information command. May 24, 2018
@hubuk hubuk changed the title WIP Fix conditions for transcription of Write-Information command. Fix conditions for transcription of Write-Information command. May 24, 2018
@iSazonov
Copy link
Collaborator

Write-Host is related to this code so you have to add tests for it.

@hubuk
Copy link
Contributor Author

hubuk commented May 27, 2018

@iSazonov I added tests for Write-Host with -InformationAction Continue and SilentlyContinue. Currently Ignore does not write to console but is being transcribed, so I skipped test for it. Let me know if you would like to have this test included anyway. I can also spend some time to make it produce same result for console and for transcript file. Fixing console output rather than transcription will make Write-Host behave as documented.

Additionally removing INFO: prefix mentioned by @mklement0 is easy. Should I proceed with it in this PR?

@mklement0
Copy link
Contributor

@hubuk:

Fixing console output rather than transcription will make Write-Host behave as documented.

I'm currently only aware of problems with the transcription behavior, not with console output, so the guiding principle should be to align transcription behavior with that of the current console-output behavior.

If, with respect to console-output behavior, you're referring to Write-Host being silenced by -InformationAction Ignore, that is not a behavioral problem, it is a documentation problem.

Copy link
Collaborator

@iSazonov iSazonov left a comment

Choose a reason for hiding this comment

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

Style comments for all added tests

[String]$message = New-Guid
$script = {
Start-Transcript -Path $transcriptFilePath
Write-Host -Message $message -InformationAction SilentlyContinue
Copy link
Collaborator

@iSazonov iSazonov May 28, 2018

Choose a reason for hiding this comment

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

Please add test for Ignore for current behavior.

I think we should limit the PR to fix only transcript issues.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Stop-Transcript }
& $script
Test-Path $transcriptFilePath | Should -BeTrue

Copy link
Collaborator

Choose a reason for hiding this comment

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

Please remove extra line.

Write-Information -Message $message -InformationAction Continue
Stop-Transcript }
& $script
Test-Path $transcriptFilePath | Should -BeTrue
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add new line.

$script = {
Start-Transcript -Path $transcriptFilePath
Write-Information -Message $message -InformationAction Continue
Stop-Transcript }
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please use common formatting.

$script = {  
    Start-Transcript -Path $transcriptFilePath  
    Write-Information -Message $message -InformationAction Continue  
    Stop-Transcript
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I used the same formatting as already applied for Transcription should record native command output test.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see. I believe we should use the right pattern because It block and other code use right patterns.

Copy link
Contributor Author

@hubuk hubuk May 28, 2018

Choose a reason for hiding this comment

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

I believe it is fixed, but $script block seems to be not needed and may be omitted.
What do you think?

It "Transcription should record Write-Host output when InformationAction is set to Ignore" {
    [String]$message = New-Guid
    Start-Transcript -Path $transcriptFilePath
    Write-Host -Message $message -InformationAction Ignore
    Stop-Transcript

    Test-Path $transcriptFilePath | Should -BeTrue
    $transcriptFilePath | Should -FileContentMatch $message
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

Seems it is an isolation. I'am not sure that we should remove thos.

@iSazonov
Copy link
Collaborator

I'm not sure I need to remove the INFO: prefix. I suppose it was made that distinguish the output of the cmdlet. Although if we want to get the exact snapshot of the console then we need to remove this prefix

@hubuk
Copy link
Contributor Author

hubuk commented May 28, 2018

There are two issues still present:

  1. Write-Host -InformationAction Ignore is being transcribed but is should not be.
  2. Write-Host -InformationAction Inquire has wrong order in transcript file:
> Write-Host 'Question?' -InformationAction Inquire
Question?

Confirm
Continue with this operation?
[Y] Yes  [A] Yes to All  [H] Halt Command  [S] Suspend  [?] Help (default is "Y"):

in transcript file:

Confirm
Continue with this operation?
&Yes  Yes to &All  &Halt Command  &Suspend
Y
Question?

The reason of 2. is that Write-Host is being transcribed separately in WriteConsoleCmdlet.cs file after the whole command has been handled.

Current transcript implementation for Write-Information can be used for Write-Host which will slightly simplify the code and provide fix for both 1. and 2.
May I include this fix in this PR?

@mklement0
Copy link
Contributor

@hubuk:

If there are no side effects, it seems like a good change to make (but it's not my call).

On a side note: the transcription of the prompt choices with the raw "accelerator chars." (&Yes instead of [Y] Yes) is unexpected (though not a big deal).

@iSazonov:

I'm not sure I need to remove the INFO: prefix. I suppose it was made that distinguish the output of the cmdlet. Although if we want to get the exact snapshot of the console then we need to remove this prefix

Yes, consistency with console output is important, I think.

However, you could argue that the INFO: prefix should be present in the console (as well), for consistency with the Write-Verbose, Write-Warning, and Write-Debug cmdlets, and that it is therefore console output that needs fixing in this case.

While changing that wouldn't technically be a breaking change - for-display output is not part of the contract with the user - it would be a noticeable one.

Either way, let's discuss it in a separate issue: #6951

@mklement0
Copy link
Contributor

Now that we know that the INFO: prefix is not printed to the console by design - see #6951 (comment) - we should remove it from the transcript too.

@hubuk
Copy link
Contributor Author

hubuk commented Jun 4, 2018

@mklement0 @iSazonov
Made changes for two issues I mentioned.

Unfortunately "accelerator chars." are handled by PromptForChoice method in ConsoleHostUserInterfacePromptForChoice.cs file. Fix could be done by making part of this method public.

It seems like there could be much more cases in which transcription file is different from the console output. For example there is no transcription of an invalid accelerator keys user pressed.
Currently transcription code is spreed among entire codebase and it is hard to make it consistent with console output because host code may make changes to the data just before displaying it.

Better approach would be to make a transcript part of host classes which will make it easier to ensure consistency but it would be a major refactoring.

@@ -748,7 +748,7 @@ internal void WriteInformation(InformationRecord record, bool overrideInquire =
//
if (null == Host || null == Host.UI)
{
Diagnostics.Assert(false, "No host in CommandBase.WriteVerbose()");
Diagnostics.Assert(false, "No host in CommandBase.WriteInformation()");
throw PSTraceSource.NewInvalidOperationException();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please remove the Diagnostics.Assert an move the message string in

throw PSTraceSource.NewInvalidOperationException("No host in CommandBase.WriteInformation()"); 

{
// Only transcribe informational messages here. Transcription of PSHost-targeted messages is done in the InternalUI.Write* methods.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Seems new comment about 'Inquire' and PSHost-targets will be useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Most important change is that transcription of Write-Host and Write-Information is handled in the same place now.
The part about fixing Inquire has only historical value and does not introduce any new information about current behavior.
Anyway, take a look at my comment and let me know if you want it stated differently as English is not my native language.

@@ -137,7 +137,6 @@ protected override void ProcessRecord()
}

this.WriteInformation(informationMessage, new string[] { "PSHOST" });
this.Host.UI.TranscribeResult(result);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Have we a test for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I already created three test for transcription of Write-Host command (Continue, SilentlyContinue, Ignore).
I have no idea how costly would it be to create a test for Inquire action as it makes the test interactive.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ideally we should mosk but I don't know how.

@adityapatwardhan Can we ignore the lack of this test?

Copy link
Member

Choose a reason for hiding this comment

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

Is it possible to add a test hook?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Not clear where the hook should be to emulate the user interaction. Maybe in PromptForChoice method in ConsoleHostUserInterfacePromptForChoice.cs file?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@hubuk You can add hook here

public static class InternalTestHooks
, inject in PromptForChoice and use in the test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's great. Thanks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tests added.

@mklement0
Copy link
Contributor

mklement0 commented Jun 4, 2018

@hubuk: Thanks for the update; my sense is that - at least for now and in the context of this PR - we needn't worry about transcribing 100% faithfully in all cases as long as all the information is ultimately there.

@@ -1,5 +1,8 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

using namespace System.Management.Automation.Internal
Copy link
Collaborator

Choose a reason for hiding this comment

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

Previously we don't use this in our tests so maybe remove this and use full names in tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

result = ReadLineResult.endedOnEnter;
return InternalTestHooks.ForcePromptForChoiceDefaultOption
? string.Empty
: ReadLine(false, "", out result, true, true);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Sorry, I skipped this - please use string.Empty as in previous line.
Thank you for your patience. Seems this is my last comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed. Not a problem.

@iSazonov iSazonov self-assigned this Jun 5, 2018
@iSazonov iSazonov requested a review from SteveL-MSFT June 6, 2018 05:00
result = ReadLineResult.endedOnEnter;
return InternalTestHooks.ForcePromptForChoiceDefaultOption
? string.Empty
: ReadLine(false, string.Empty, out result, true, true);
Copy link
Member

Choose a reason for hiding this comment

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

Can you use named parameters here?

$newLine = [System.Environment]::NewLine
$expectedContent = "$message$($newLine)Confirm$($newLine)Continue with this operation?"
$script = {
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('ForcePromptForChoiceDefaultOption', $True)
Copy link
Member

Choose a reason for hiding this comment

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

Seems like there should be an AfterEach{} that resets the test hook

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.


& $script

Test-Path $transcriptFilePath | Should -BeTrue
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps a search and replace to:

$transcriptFilePath | Should -Exist

@hubuk
Copy link
Contributor Author

hubuk commented Jun 13, 2018

@SteveL-MSFT Is there anything else you would like to be changed?

@iSazonov
Copy link
Collaborator

@SteveL-MSFT Please update your review.

@iSazonov
Copy link
Collaborator

I have a fear of removal FORWARDED condition - do it really not used?

/cc @SteveL-MSFT Who can confirm?

@hubuk
Copy link
Contributor Author

hubuk commented Jun 15, 2018

@iSazonov
FORWARDED condition is still there:

if ((record.Tags.Contains("PSHOST") && (!record.Tags.Contains("FORWARDED")))
|| (preference == ActionPreference.Continue))
Only logic for the else statement has changed from:
else
{
// Only transcribe informational messages here. Transcription of PSHost-targeted messages is done in the InternalUI.Write* methods.
CBhost.InternalUI.TranscribeResult(StringUtil.Format(InternalHostUserInterfaceStrings.InformationFormatString, record.ToString()));
}
to:
if (record.Tags.Contains("PSHOST") || (preference != ActionPreference.SilentlyContinue))
{
CBhost.InternalUI.TranscribeResult(record.ToString());
}

Before my changes TranscribeResult was being executed for the following conditions:

  1. In WriteConsoleCmdlet.cs which is when record.Tags.Contains("PSHOST")
  2. (preference != ActionPreference.Continue) && !record.Tags.Contains("PSHOST") as the first part of the evaluation of condition for else statement.
  3. (preference != ActionPreference.Continue) && record.Tags.Contains("PSHOST") && record.Tags.Contains("FORWARDED") as the second part.
    So transcription for FORWARDED tag was being executed only for Write-Host or for Write-Information when preference != ActionPreference.Continue. Taking into account that due to bug ActionPreference.Continue should be ActionPreference.SilentlyContinue This is what current code is doing.

After my changes TranscribeResult is being executed only for the following cases:

  1. record.Tags.Contains("PSHOST")
  2. !record.Tags.Contains("PSHOST") && preference != ActionPreference.SilentlyContinue

I hope this is somehow readable.

@iSazonov
Copy link
Collaborator

@hubuk Sorry, sometimes GitHub shows diffs in very strange way.

@iSazonov iSazonov merged commit 9ac701d into PowerShell:master Jun 16, 2018
@iSazonov
Copy link
Collaborator

@hubuk Thank you for your contribution and your patience!
I hope you will make still many useful contributions.

@hubuk
Copy link
Contributor Author

hubuk commented Jun 16, 2018

Happy to contribute. Thanks!

@hubuk hubuk deleted the issue6916 branch June 16, 2018 18:12
daxian-dbw added a commit that referenced this pull request Jun 19, 2018
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.

5 participants