Skip to content

Conversation

janlazo
Copy link
Contributor

@janlazo janlazo commented Mar 9, 2018

Ref #7412 (comment), #8034
Need #8107 for jobstop to return exit code 0 if the program successfully exits and returns exit code 0 if run from a shell (ex. cmd.exe, bash).

Testing to see if switching to cmd.exe improves testing with job API and cat.exe on Windows.

I'm giving up on NULs and newlines for jobsend (list argument). cat.exe still has the invalid channel id error, write_file truncates the string with \0 and find.exe truncates the input string after \0.

start (cmd.exe builtin) reaps its child processes with /b (I can't find them with tasklist) so I'm using /min instead for a minimized window. Redirecting to nul shouldn't be necessary. If that's intended with the jobstop() test, so be it.

Not sure what's the expected behaviour for start (cmd.exe builtin) because nvim is not reaping the child processes on exit after :qa!, even on nested nvim termnal. Seems to have different semantics from fork (or what bash does with &). Perhaps powershell is better suited here but I don't know how (yes, there's Start-Process but I can't pass arguments to the external program). I can use Start-Job so if the session terminates, the jobs die too. Is that close enough? Leave it for later. I disabled the pty teardown test.

more.com (like less) is the default pager in Windows so it can be used to display files. That leaves the NUL tests for cat if more can't do it.

fnamemodify doesn't adjust slashes and it's the same on Vim too. Have to use expand() again.

@justinmk
Copy link
Member

justinmk commented Mar 9, 2018

Somehow the exit code is wrong for these two tests on the 32bit build:

[  FAILED  ] C:/projects/neovim/test/functional\core\job_spec.lua @ 140: jobs allows interactive commands
C:/projects/neovim/test/functional\core\job_spec.lua:151: Expected objects to be the same.
Passed in:
(table) {
  [1] = 'notification'
  [2] = 'exit'
 *[3] = {
    [1] = 0
   *[2] = 1 } }
Expected:
(table) {
  [1] = 'notification'
  [2] = 'exit'
 *[3] = {
    [1] = 0
   *[2] = 0 } }
stack traceback:
	C:/projects/neovim/test/functional\core\job_spec.lua:151: in function <C:/projects/neovim/test/functional\core\job_spec.lua:140>
[  FAILED  ] C:/projects/neovim/test/functional\core\job_spec.lua @ 195: jobs preserves NULs
C:/projects/neovim/test/functional\core\job_spec.lua:202: Expected objects to be the same.
Passed in:
(table) {
  [1] = 'notification'
  [2] = 'exit'
 *[3] = {
    [1] = 0
   *[2] = 1 } }
Expected:
(table) {
  [1] = 'notification'
  [2] = 'exit'
 *[3] = {
    [1] = 0
   *[2] = 0 } }
stack traceback:


should we leave those "pending" for now?

@janlazo
Copy link
Contributor Author

janlazo commented Mar 9, 2018

@justinmk Sure, but I'll do it later. I want to see if it happens in 32-bit build only.

Old tests aren't running.

[00:14:33] C:\projects\neovim\build>cmake --build "C:\projects\neovim\ci\\..\src\nvim\testdir" -- VERBOSE=1 
[00:14:33] Error: could not load cache
[00:14:33] 
[00:14:33] C:\projects\neovim\build>endlocal

@janlazo
Copy link
Contributor Author

janlazo commented Mar 9, 2018

Exit codes are still inconsistent even with cmd.exe but this is more consistent on stdout than powershell.
Powershell sometimes doesn't execute a command if the last argument and \r\n aren't separated by whitespace so, when it happens to me, I press Enter again for it to run.

I'll create cat.cmd alternative using cmd.exe built-ins to get more consistent exit codes. cat.exe is covered by other tests anyway.

@justinmk
Copy link
Member

justinmk commented Mar 9, 2018

Weird. I wonder if we should just write our own cat.exe in C, instead of this old gnu binary I found ...

@@ -138,7 +139,7 @@ describe('jobs', function()
end)

it('allows interactive commands', function()
nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)")
nvim('command', "let j = jobstart(['"..nvim_cat.."', '-'], g:job_opts)")
Copy link
Member

Choose a reason for hiding this comment

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

Doesn't foo.cmd require a shell? So I would not expect this to work, the jobstart({list}) form skips the shell.

That's one reason why including cat.exe was important.

And changing these invocations to use the shell form of jobstart({string}) changes the nature of the tests, so we don't want to do that.

Copy link
Contributor Author

@janlazo janlazo Mar 10, 2018

Choose a reason for hiding this comment

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

You're right. I'll keep the list form and run the batchfile with cmd.exe directly. It should be fine if the path of the tempfile doesn't have to be escaped.

@janlazo janlazo force-pushed the win_job_cmd branch 2 times, most recently from 548ac58 to c68f10e Compare March 10, 2018 02:46
@janlazo
Copy link
Contributor Author

janlazo commented Mar 10, 2018

find.exe /v "" (see comments in https://superuser.com/a/853718) is better for multiple newlines. It doesn't preserve NULs though.

We can use cat.exe for testing the newline -> NUL conversion without checking the error code. Nevermind. invalid channel id error still happens.

find.exe requires that on each jobsend, it's either \r\n or \n\n. On uneven # of consecutive \n, prepend the last \n with \r.

@janlazo
Copy link
Contributor Author

janlazo commented Mar 11, 2018

@justinmk How does jobstop work in Windows? IIRC, Windows doesn't support signals so killed processes can't clean up.

@justinmk
Copy link
Member

@janlazo Do you mean grandchildren? We will need to iterate through the process tree.

See terminate_all in Vim, which was basically copied from https://stackoverflow.com/q/1173342 . After a long search, I concluded that's the only way to do it, except another approach used by libuv (see uv__init_global_job_handle): create a win32 job object (but that has a quirk in Windows 7 and older: https://stackoverflow.com/a/6138203 ).

The above will be implemented in #8107 .

If you meant something else, please give a specific example.

@janlazo janlazo force-pushed the win_job_cmd branch 4 times, most recently from 79ac88b to 2cc77ff Compare March 12, 2018 14:58
nvim('command', 'call jobsend(j, "'..(iswin()
and [[a\n\nc\n\n]]
or [[a\n\nc\n\n\n\nb\n\n]]
)..'")')
Copy link
Member

Choose a reason for hiding this comment

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

Would rather keep the test pending than have a lot of special-case code that is difficult to follow, just to get the test working. Maybe something can be improved in the future so that the test can pass without requiring lots of weird workarounds.

@janlazo
Copy link
Contributor Author

janlazo commented Mar 13, 2018

That should be the last of it (unless cat.exe fails again or find.exe errors on jobstop).
I can port my changes for powershell from #8034 to test jobwait in the meantime.

@janlazo janlazo changed the title test: win: use cmd.exe by default in job_spec.lua [RFC] test: win: use cmd.exe by default in job_spec.lua Mar 13, 2018
@marvim marvim added the RFC label Mar 13, 2018
@janlazo janlazo force-pushed the win_job_cmd branch 3 times, most recently from d4ec323 to db27b09 Compare March 13, 2018 21:18
@janlazo janlazo force-pushed the win_job_cmd branch 2 times, most recently from 075e5c3 to 63d3b76 Compare March 17, 2018 15:52
return os.execute((iswin()
and 'tasklist /nh /fi "PID eq '..pid..'" | findstr '..pid..' > nul'
or 'ps -p '..pid..' > /dev/null'))
end
Copy link
Member

@justinmk justinmk Mar 17, 2018

Choose a reason for hiding this comment

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

#8107 provides nvim_get_proc({pid}) which can be used in the tests via helpers.meths.get_proc(). It returns helpers.NIL if the process doesn't exist.

Copy link
Contributor Author

@janlazo janlazo Mar 19, 2018

Choose a reason for hiding this comment

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

I need to return an exit code though.
Need something like nvim_get_proc(pid) == helpers.NIL and 1 or 0.
Or I can use it directly on the jobpid test.

Copy link
Member

@justinmk justinmk Mar 20, 2018

Choose a reason for hiding this comment

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

Why do you need an exit code? If nvim_get_proc returns NIL then the process wasn't found. Non-NIL means it was found.

return os.execute((iswin()
and 'taskkill /f /t /pid '..pid..' > nul'
or 'ps -p '..pid..' > /dev/null'))
end
Copy link
Member

Choose a reason for hiding this comment

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

Would be easy to expose nvim_kill_proc() if this turns out to be unreliable.

justinmk added a commit to justinmk/neovim that referenced this pull request Mar 18, 2018
janlazo added a commit to janlazo/neovim that referenced this pull request Mar 18, 2018
This reverts commit ae409b5.

This PR (neovim#8120) defaults to cmd.exe for job_spec.lua
@janlazo janlazo force-pushed the win_job_cmd branch 2 times, most recently from 66a5437 to bf83b3e Compare March 19, 2018 06:08
nvim('command', 'call jobsend(j, "abc\\nxyz")')
eq({'notification', 'stdout', {0, {'abc', 'xyz'}}}, next_msg())
eq({'notification', 'stdout', {0, iswin() and {'abc', ''} or {'abc', 'xyz'}}}, next_msg())
Copy link
Member

Choose a reason for hiding this comment

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

Probably should used expect_msg_seq

Copy link
Contributor Author

@janlazo janlazo Mar 19, 2018

Choose a reason for hiding this comment

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

For this line only? This is expected behaviour for find.exe because the string has to end with newline (or some character for find.exe to receive the input). As for the stdout after jobstop, I don't know why.

I can experiment with expect_msg_seq for both 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.

On 2nd thought, I'll revert it. Leave it for cat when the situation improves. There's enough tests for find.exe anyway.

-- find.exe can't handle 3+ consecutive newlines consistently
nvim('command', "let j = jobstart(['find', '/v', ''], g:job_opts)")
nvim('command', [[call jobsend(j, "a\n\nc\n\n")]])
expect_msg_seq(
Copy link
Member

Choose a reason for hiding this comment

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

If it's not consistent then just mark it pending. The entire purpose of this test is to check that newlines are preserved (consistently).

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'll revert it back to pending. Revist when testing with cat.exe gets better.

nvim('command', "call jobstop(j)")
if iswin() then
eq({'notification', 'stdout', {0, {'final nl', ''}}}, next_msg())
Copy link
Member

Choose a reason for hiding this comment

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

This output has the newline, doesn't it? That defeats the purpose of the test. Should just be pending.

Copy link
Contributor Author

@janlazo janlazo Mar 19, 2018

Choose a reason for hiding this comment

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

It's leftover from stdout when find.exe exits for some reason. Don't recall this happening on a regular cmd.exe window. Strange that this doesn't happen on jobclose(g:job_id, "stdin")

-- clean up after ourselves
os.execute('kill -9 '..pid..' > /dev/null')
os_kill(pid)
neq(0, os_ps(pid))
Copy link
Member

Choose a reason for hiding this comment

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

It should be NOT found since we killed it.

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'm checking if the os_kill did work. Is there a better way to do it or do you want to check the exit code of os_kill?

Copy link
Member

Choose a reason for hiding this comment

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

@janlazo Ah, I didn't realize os_ps was returned the exit code, I thought it returned a pid. Anyways nvim_get_proc should be used instead.

\ jobstart('Start-Sleep -Milliseconds 70; exit 4'),
\ jobstart('Start-Sleep -Milliseconds 50; exit 5'),
\ jobstart('Start-Sleep -Milliseconds 30; exit 6'),
\ jobstart('Start-Sleep -Milliseconds 10; exit 7')
Copy link
Member

Choose a reason for hiding this comment

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

This is going to be unreliable because powershell startup takes 600ms at least.

Copy link
Contributor Author

@janlazo janlazo Mar 19, 2018

Choose a reason for hiding this comment

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

Bump the sleep timeout to (1)700, (1)500, (1)300, (1)100 or use cmd.exe and ping instead?
The timeout test for jobwait lasts up to 2-3s so it's long wait either way.

Copy link
Member

@justinmk justinmk Mar 20, 2018

Choose a reason for hiding this comment

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

I think cmd.exe would be a better choice here.

The timeout test for jobwait lasts up to 2-3s so it's long wait either way.

Yes but I'm thinking of the variance in event ordering, e.g. if powershell starts 20ms faster in one case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Likely because of Powershell's lazy-loading and Appveyor-specific modules. Can't prevent them with -NoProfile.

@@ -775,22 +840,36 @@ describe("pty process teardown", function()
end)

it("does not prevent/delay exit. #4798 #4900", function()
Copy link
Member

Choose a reason for hiding this comment

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

Could use nvim_get_proc_children instead of a screen test.

Copy link
Contributor Author

@janlazo janlazo Mar 19, 2018

Choose a reason for hiding this comment

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

For the PIDs? It won't work with the child process launched with start (cmd.exe). Have to rethink this test for Windows. Anything launched with start should be killed separately.

Copy link
Member

Choose a reason for hiding this comment

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

Not sure what you mean, but if !start is detached from nvim somehow, such that it wouldn't prevent nvim from exiting (if there were a nvim bug in its jobs teardown routine), then that defeats the purpose of the test.

Copy link
Contributor Author

@janlazo janlazo Mar 20, 2018

Choose a reason for hiding this comment

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

nvim will exit completely and !start terminates after running the program (ie. ping) but the program that start does run (ie ping) will not necessarily terminate if nvim terminates.
That said, closing :terminal seems to terminate programs run with start /b.

Copy link
Member

Choose a reason for hiding this comment

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

but the program that start does run (ie ping) will not necessarily terminate if nvim terminates.

That defeats the purpose of the test :)

janlazo added a commit to janlazo/neovim that referenced this pull request Mar 20, 2018
This reverts commit ae409b5.

This PR (neovim#8120) defaults to cmd.exe for job_spec.lua
janlazo added a commit to janlazo/neovim that referenced this pull request Mar 26, 2018
This reverts commit ae409b5.

This PR (neovim#8120) defaults to cmd.exe for job_spec.lua
janlazo added 6 commits March 26, 2018 01:45
cmd.exe (shell) is faster and more reliable than powershell (.NET frontend).
It's best for short and basic tests that don't require non-trivial scripting.
cmd.exe doesn't support sleep so use powershell's Start-Sleep as substitute.
This reverts commit ae409b5.

This PR (neovim#8120) defaults to cmd.exe for job_spec.lua
Use ping to test job detach
Use find.exe as an alternative to cat.exe
Use nvim_get_proc to check pid
@janlazo janlazo force-pushed the win_job_cmd branch 2 times, most recently from fda26ed to 73d48b7 Compare March 27, 2018 02:34
@janlazo janlazo changed the title [RFC] test: win: use cmd.exe by default in job_spec.lua [RDY] test: win: use cmd.exe by default in job_spec.lua Mar 27, 2018
@marvim marvim added RDY and removed RFC labels Mar 27, 2018
@janlazo janlazo closed this Apr 14, 2018
@jszakmeister jszakmeister removed the RDY label Apr 14, 2018
@janlazo janlazo reopened this Apr 15, 2018
@marvim marvim added the RDY label Apr 15, 2018
@justinmk justinmk merged commit 7598e6c into neovim:master Apr 15, 2018
@justinmk justinmk removed the RDY label Apr 15, 2018
@janlazo janlazo deleted the win_job_cmd branch May 28, 2018 15:09
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.

5 participants