Skip to content

[llvm-lit] Add redirection handling for env command without args and write a lit test to check behavior with lit internal shell #106629

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Harini0924
Copy link
Contributor

The current implementation of the env command in lit's internal shell lacks support for redirection to a temporary file when the command is executed without arguments. This limitation means that when users attempt to redirect the output of the env command to a temporary file, it fails to find the file, leading to unexpected behavior.

This patch enhances the TestRunner.py to correctly handle cases where the env command is executed without arguments and with redirection. Previously, the env command did not support redirection to a temporary file when no arguments were provided, leading to unexpected behavior where environment variables were not written to the specified file.

The patch introduces a check for when the env command is run without arguments. It now calls the processRedirects function, which returns the file descriptors (stdin (0), stdout (1) , stderr (2) ). If a redirection is specified (i.e., stdout is not the default stdin), the environment variables are directly written to the redirected file using the stdout.write(env_str) command. Otherwise, the output is captured for scenarios without redirection.

Additionally, this patch adds a corresponding test in the lit's internal shell to verify the correct behavior of the env command with output redirection. The test ensures that when the env command is redirected to a temp file and checks if the environment variables are correctly outputted.

This change is relevant for [RFC] Enabling the Lit Internal Shell by Default
fixes: #106627

…n lit

Added Redirection Handling: Enhanced TestRunner.py to correctly handle cases
where the env command is run without arguments. It now checks if there are
no arguments and, when redirection is specified, writes the environment
variables directly to the redirected file or a temporary file. Test for
Redirection Behavior: Created a new test in lit's internal shell to verify that
when the env command is executedwithout arguments and with redirection, it
properly outputs the environment variables to the specified file.
@Harini0924 Harini0924 force-pushed the env_add_redirection branch from aea9055 to 5f5af8c Compare August 29, 2024 21:23
@Harini0924 Harini0924 marked this pull request as ready for review August 29, 2024 21:24
@Harini0924 Harini0924 requested a review from ilovepi August 29, 2024 21:24
@Harini0924 Harini0924 requested a review from petrhosek August 29, 2024 21:24
@llvmbot
Copy link
Member

llvmbot commented Aug 29, 2024

@llvm/pr-subscribers-testing-tools

Author: None (Harini0924)

Changes

The current implementation of the env command in lit's internal shell lacks support for redirection to a temporary file when the command is executed without arguments. This limitation means that when users attempt to redirect the output of the env command to a temporary file, it fails to find the file, leading to unexpected behavior.

This patch enhances the TestRunner.py to correctly handle cases where the env command is executed without arguments and with redirection. Previously, the env command did not support redirection to a temporary file when no arguments were provided, leading to unexpected behavior where environment variables were not written to the specified file.

The patch introduces a check for when the env command is run without arguments. It now calls the processRedirects function, which returns the file descriptors (stdin (0), stdout (1) , stderr (2) ). If a redirection is specified (i.e., stdout is not the default stdin), the environment variables are directly written to the redirected file using the stdout.write(env_str) command. Otherwise, the output is captured for scenarios without redirection.

Additionally, this patch adds a corresponding test in the lit's internal shell to verify the correct behavior of the env command with output redirection. The test ensures that when the env command is redirected to a temp file and checks if the environment variables are correctly outputted.

This change is relevant for [RFC] Enabling the Lit Internal Shell by Default
fixes: #106627


Full diff: https://github.com/llvm/llvm-project/pull/106629.diff

3 Files Affected:

  • (modified) llvm/utils/lit/lit/TestRunner.py (+18-4)
  • (added) llvm/utils/lit/tests/Inputs/shtest-env-positive/env-temp-redirect.txt (+7)
  • (modified) llvm/utils/lit/tests/shtest-env-positive.py (+9-3)
diff --git a/llvm/utils/lit/lit/TestRunner.py b/llvm/utils/lit/lit/TestRunner.py
index 19f35fc7e212f3..2b3c6b8b9b750f 100644
--- a/llvm/utils/lit/lit/TestRunner.py
+++ b/llvm/utils/lit/lit/TestRunner.py
@@ -746,11 +746,25 @@ def _executeShCmd(cmd, shenv, results, timeoutHelper):
                     env_str = "\n".join(
                         f"{key}={value}" for key, value in sorted(cmd_shenv.env.items())
                     )
-                    results.append(
-                        ShellCommandResult(
-                            j, env_str, "", 0, timeoutHelper.timeoutReached(), []
-                        )
+                    # Process redirections.
+                    stdin, stdout, stderr = processRedirects(
+                        j, default_stdin, cmd_shenv, opened_files
                     )
+                    if stdout != default_stdin:
+                        # Write directly to the redirected file (stdout).
+                        stdout.write(env_str)
+                        results.append(
+                            ShellCommandResult(
+                                j, "", "", 0, timeoutHelper.timeoutReached(), []
+                            )
+                        )
+                    else:
+                        # Capture the output for cases without redirection.
+                        results.append(
+                            ShellCommandResult(
+                                j, env_str, "", 0, timeoutHelper.timeoutReached(), []
+                            )
+                        )
                     return 0
             elif args[0] == "not":
                 not_args.append(args.pop(0))
diff --git a/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-temp-redirect.txt b/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-temp-redirect.txt
new file mode 100755
index 00000000000000..b2ad755e81b0ef
--- /dev/null
+++ b/llvm/utils/lit/tests/Inputs/shtest-env-positive/env-temp-redirect.txt
@@ -0,0 +1,7 @@
+## Test the env command with output redirection to a file.
+# RUN: rm -f %t
+# RUN: env > %t
+# RUN: FileCheck %s < %t
+
+# CHECK: BAR=2
+# CHECK: FOO=1
diff --git a/llvm/utils/lit/tests/shtest-env-positive.py b/llvm/utils/lit/tests/shtest-env-positive.py
index 863fbda8c5b6dc..8f5f8385872a65 100644
--- a/llvm/utils/lit/tests/shtest-env-positive.py
+++ b/llvm/utils/lit/tests/shtest-env-positive.py
@@ -7,7 +7,7 @@
 
 ## Test the env command's successful executions.
 
-# CHECK: -- Testing: 9 tests{{.*}}
+# CHECK: -- Testing: 10 tests{{.*}}
 
 # CHECK: PASS: shtest-env :: env-args-last-is-assign.txt ({{[^)]*}})
 # CHECK: env FOO=1
@@ -47,6 +47,12 @@
 # CHECK-NOT: # error:
 # CHECK: --
 
+# CHECK: PASS: shtest-env :: env-temp-redirect.txt ({{[^)]*}})
+# CHECK: env {{.*}}/env-temp-redirect.txt.tmp
+# CHECK: # executed command: env
+# CHECK-NOT: # error:
+# CHECK: --
+
 # CHECK: PASS: shtest-env :: env-u.txt ({{[^)]*}})
 # CHECK: env -u FOO | {{.*}}
 # CHECK: # executed command: env -u FOO
@@ -65,6 +71,6 @@
 # CHECK-NOT: # error:
 # CHECK: --
 
-# CHECK: Total Discovered Tests: 9
-# CHECK: Passed: 9 {{\([0-9]*\.[0-9]*%\)}}
+# CHECK: Total Discovered Tests: 10
+# CHECK: Passed: 10 {{\([0-9]*\.[0-9]*%\)}}
 # CHECK-NOT: {{.}}

@@ -0,0 +1,7 @@
## Test the env command with output redirection to a file.
# RUN: rm -f %t
# RUN: env > %t
Copy link
Member

Choose a reason for hiding this comment

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

I can see from the code that the redirect is only consumed if there are no arguments, but could we also check that env VAR=value > %t and env A=B echo "no env output here please" behave as expected?

)
if stdout != default_stdin:
Copy link
Member

Choose a reason for hiding this comment

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

Is this always true? I admit I didn't try to understand all of processRedirects, but it doesn't look like the input argument is ever returned for stdout?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The input argument (default_stdin, which is set to subprocess.PIPE) is not always returned directly as stdout by the processRedirects function. The function's purpose is to handle any redirections specified in the command. If there's a redirection for stdout (like > or >>), processRedirects will open a file and return a file descriptor for that file instead of returning the original subprocess.PIPE.

So, when you pass subprocess.PIPE as the default for stdout, processRedirects might override it with a different file descriptor if redirection is specified. If no redirection is specified, then it could return subprocess.PIPE as it was originally passed. This means the condition if stdout != default_stdin is designed to check whether stdout was redirected to something other than the pipe, and it's not always true—it depends on whether any redirection was applied.

Did that answer your question?

Copy link
Member

Choose a reason for hiding this comment

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

I looked at processRedirects() and could not see the stdin_source parameter (what you are passing as default_stdin) ever being returned as the second tuple element.

As you say the check is actually if stdout != subprocess.PIPE and the way it is spelled now is very confusing since it seems to imply some relationship between stdout and stdin.

As I understand your comment the != default_stdin check isn't actually checking anything related to what was stdin value for the process, but just relies on the fact that default_stdin holds subprocess.PIPE. With the following check the code makes a lot more sense to me:

Suggested change
if stdout != default_stdin:
if stdout != subprocess.PIPE:

Comment on lines +754 to +767
# Write directly to the redirected file (stdout).
stdout.write(env_str)
results.append(
ShellCommandResult(
j, "", "", 0, timeoutHelper.timeoutReached(), []
)
)
else:
# Capture the output for cases without redirection.
results.append(
ShellCommandResult(
j, env_str, "", 0, timeoutHelper.timeoutReached(), []
)
)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
# Write directly to the redirected file (stdout).
stdout.write(env_str)
results.append(
ShellCommandResult(
j, "", "", 0, timeoutHelper.timeoutReached(), []
)
)
else:
# Capture the output for cases without redirection.
results.append(
ShellCommandResult(
j, env_str, "", 0, timeoutHelper.timeoutReached(), []
)
)
# Write directly to the redirected file (stdout).
stdout.write(env_str)
captured_stdout = ""
else:
# Capture the output for cases without redirection.
captured_stdout = env_str
results.append(
ShellCommandResult(
j, captured_stdout, "", 0, timeoutHelper.timeoutReached(), []
)
)

This is a bit shorter and avoids all end empty lines with closing parens.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[llvm-lit] The env command without args doesn't support redirection with lit internal shell
3 participants