Powershell 的堆栈跟踪是否已损坏?

发布于 2024-09-15 09:26:41 字数 2021 浏览 14 评论 0原文

我正在 powershell 中编写一个简单的单元测试工具,

我设计了该工具,使其断言函数将脚本块作为参数,以允许工具从断言函数内运行代码,并将抛出的任何异常视为测试失败。

如果测试失败,我想返回测试失败的单元测试中的行。我的计划是通过在每个断言方法的开头进行堆栈跟踪 (Get-PSCallStack) 并使用第二个堆栈帧的信息来实现此目的,我认为该信息应与调用断言函数的行相对应。

在实践中,我发现powershell返回的信息似乎有误。第二个堆栈帧按预期引用正确的文件,但始终给出我在断言方法中调用 Get-PSCallStack 的行号。有时,这个数字可能甚至高于给定文件中的行数(即,位置给出为“ScriptFile.ps1 line 88”,但文件只有 20 行)。

powershell 中的堆栈跟踪是否有问题,或者有什么我不明白的地方?

编辑

根据要求,我发布了一个示例,该示例应产生相同的结果

Tester.ps1

#File 1 (Tester.ps1)
#Creates the tester object

$tester = (New-Object PSObject);

$tester | Add-Member -MemberType ScriptMethod -Name AssertTrue -Value {
    param($expression);

    $stackFrame = (GEt-PSCallStack)[1];

    try{
        $result = &$expression;
        if($result -eq $true){
            $this.LogPass();
        }else{
            $this.LogFailure("Evaluation Failed expected ""$true"" got ""$false""", $stackFrame);
        }
    }catch [Exception]{
        $this.LogFailure("Unexpected exception encountered", $stackFrame);
    }
}

$tester | Add-Member -MemberType ScriptMethod -Name LogPass -Value {
    #Do nothing
};

$tester | Add-Member -MemberType ScriptMethod -Name LogFailure -Value {
    param($message, $stackFrame);
    "Failure Encounterd";
    "Command: $($stackFrame.Command)"
    "Location: $($stackFrame.Location)";
    "Message: $message";
}

return $tester;

TestCase.ps1

#File 2 (TestCase.ps1)
#Runs the tests using the tester object

$tester = &(Resolve-Path "Tester.ps1");

function TestFailure($tester){
    $expression = {$false};
    $tester.AssertTrue($expression);
}

TestFailure($tester);

在 TestCase.ps1 的第 7 行调用断言,并在 Tester 的第 9 行捕获调用堆栈。 ps1

这打印

Failure Encounterd
Command: TestFailure
Location: Tester.ps1: Line 9
Message: Evaluation Failed expected "True" got "False"

命令是正确的,但文件和行都是错误的

堆栈跟踪的下一帧正确描述了调用 TestFailure() 的位置,其位置为“TestCase.ps1:第 11 行”

I am writing a simple unit test harness in powershell

I designed the harness such that its assert functions takes a script-block as a parameter to allow the harness to run the code from within the assert function and treat any exceptions are thrown as a test failure.

If the tests fail I want to return line in the unit test in which the test fails. My plan was to do this by taking a stacktrace (Get-PSCallStack) at the start of each assert method and use the information for the second stack-frame which I assume should correspond to the line where the assert function was called.

In practice, I found that the information that powershell gave back seemed wrong. The second stack-frame refers to the the correct file as expected but always gives the line number at which I called Get-PSCallStack in the assert method. Sometimes this number might be even higher than the number of lines in the file given (i.e. Location is given as "ScriptFile.ps1 line 88" but the file only has 20 lines).

Is there a problem with the stack trace in powershell or is there something I am not understanding here?

Edit

As requested I am posting an example which should produce the same results

Tester.ps1

#File 1 (Tester.ps1)
#Creates the tester object

$tester = (New-Object PSObject);

$tester | Add-Member -MemberType ScriptMethod -Name AssertTrue -Value {
    param($expression);

    $stackFrame = (GEt-PSCallStack)[1];

    try{
        $result = &$expression;
        if($result -eq $true){
            $this.LogPass();
        }else{
            $this.LogFailure("Evaluation Failed expected ""$true"" got ""$false""", $stackFrame);
        }
    }catch [Exception]{
        $this.LogFailure("Unexpected exception encountered", $stackFrame);
    }
}

$tester | Add-Member -MemberType ScriptMethod -Name LogPass -Value {
    #Do nothing
};

$tester | Add-Member -MemberType ScriptMethod -Name LogFailure -Value {
    param($message, $stackFrame);
    "Failure Encounterd";
    "Command: $($stackFrame.Command)"
    "Location: $($stackFrame.Location)";
    "Message: $message";
}

return $tester;

TestCase.ps1

#File 2 (TestCase.ps1)
#Runs the tests using the tester object

$tester = &(Resolve-Path "Tester.ps1");

function TestFailure($tester){
    $expression = {$false};
    $tester.AssertTrue($expression);
}

TestFailure($tester);

The assert is called on Line 7 of TestCase.ps1 and the call stack is captured on line 9 of Tester.ps1

This prints

Failure Encounterd
Command: TestFailure
Location: Tester.ps1: Line 9
Message: Evaluation Failed expected "True" got "False"

The command is correct but both the file and the line are wrong

The next frame of the stack trace correctly describes where TestFailure() is called with its location as "TestCase.ps1: Line 11"

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

苍景流年 2024-09-22 09:26:41

它不是您使用的断言函数,而是用作“成员函数”的断言脚本块。但它仍然是一个脚本块。

根据此报告的问题:
https://connect.microsoft.com/PowerShell/feedback/details/531086/depending-on-how-you-invoke-a -script-block-the-inspiration-details-may-not-be-available-from-inside-the-script-block#

调用 Get-PSCallStack 时出现问题脚本块。所以,你的问题的答案可能是:是的,这是一个 PowerShell 问题。

好吧,我建议你使用函数。我重构了你的脚本以使用函数(脏版本、坏名称等),它们按预期工作:

#File 1 (Tester.ps1)
#Creates the tester object

function AssertTrue {
    param($expression);

    $stackFrame = (Get-PSCallStack)[1]

    try{
        $result = . $expression;
        if($result -eq $true){
            LogPass
        }else{
            LogFailure ("Evaluation Failed expected ""$true"" got ""$false""") $stackFrame
        }
    }catch [Exception]{
        LogFailure "Unexpected exception encountered" $stackFrame
    }
}

function LogPass {
    #Do nothing
}

function LogFailure {
    param($message, $stackFrame);
    "Failure Encounterd";
    "Command: $($stackFrame.Command)"
    "Location: $($stackFrame.Location)";
    "Message: $message";
}

并且

#File 2 (TestCase.ps1)
#Runs the tests using the tester object

. (Resolve-Path "Tester.ps1");

function TestFailure {
    $expression = {$false};
    AssertTrue $expression
}

TestFailure

It is not an assert function that you use, it is an assert script block used as a “member function”. But it is still a script block.

According to this reported issue:
https://connect.microsoft.com/PowerShell/feedback/details/531086/depending-on-how-you-invoke-a-script-block-the-invocation-details-may-not-be-available-from-inside-the-script-block#

there is something wrong with calling Get-PSCallStack from script blocks. So, the answer to your question is probably: yes, it is a PowerShell issue.

Well, I would recommend you to use functions. I re-factored your scripts to use functions (dirty version, bad names, etc.) and they work as expected:

#File 1 (Tester.ps1)
#Creates the tester object

function AssertTrue {
    param($expression);

    $stackFrame = (Get-PSCallStack)[1]

    try{
        $result = . $expression;
        if($result -eq $true){
            LogPass
        }else{
            LogFailure ("Evaluation Failed expected ""$true"" got ""$false""") $stackFrame
        }
    }catch [Exception]{
        LogFailure "Unexpected exception encountered" $stackFrame
    }
}

function LogPass {
    #Do nothing
}

function LogFailure {
    param($message, $stackFrame);
    "Failure Encounterd";
    "Command: $($stackFrame.Command)"
    "Location: $($stackFrame.Location)";
    "Message: $message";
}

And

#File 2 (TestCase.ps1)
#Runs the tests using the tester object

. (Resolve-Path "Tester.ps1");

function TestFailure {
    $expression = {$false};
    AssertTrue $expression
}

TestFailure
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文