summary refs log tree commit diff stats
path: root/tests/stdlib/tosproc.nim
blob: b8d3be9bbfe1f71e47c2ba82a4345492b5905acf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# test the osproc module

import stdtest/specialpaths
import "../.." / compiler/unittest_light

when defined(case_testfile): # compiled test file for child process
  from posix import exitnow
  proc c_exit2(code: c_int): void {.importc: "_exit", header: "<unistd.h>".}
  import os
  var a = 0
  proc fun(b = 0) =
    a.inc
    if a mod 10000000 == 0: # prevents optimizing it away
      echo a
    fun(b+1)

  proc main() =
    let args = commandLineParams()
    echo (msg: "child binary", pid: getCurrentProcessId())
    let arg = args[0]
    echo (arg: arg)
    case arg
    of "exit_0":
      if true: quit(0)
    of "exitnow_139":
      if true: exitnow(139)
    of "c_exit2_139":
      if true: c_exit2(139)
    of "quit_139":
      # `exitStatusLikeShell` doesn't distinguish between a process that
      # exit(139) and a process that gets killed with `SIGSEGV` because
      # 139 = 11 + 128 = SIGSEGV + 128.
      # However, as #10249 shows, this leads to bad debugging experience
      # when a child process dies with SIGSEGV, leaving no trace of why it
      # failed. The shell (and lldb debugger) solves that by inserting a
      # helpful msg: `segmentation fault` when it detects a signal killed
      # the child.
      # todo: expose an API that will show more diagnostic, returing
      # (exitCode, signal) instead of just `shellExitCode`.
      if true: quit(139)
    of "exit_recursion": # stack overflow by infinite recursion
      fun()
      echo a
    of "exit_array": # bad array access
      echo args[1]
  main()

else:

  import os, osproc, strutils, posix

  block execShellCmdTest:
    ## first, compile child program
    const nim = getCurrentCompilerExe()
    const sourcePath = currentSourcePath()
    let output = buildDir / "D20190111T024543".addFileExt(ExeExt)
    let cmd = "$# c -o:$# -d:release -d:case_testfile $#" % [nim, output,
        sourcePath]
    # we're testing `execShellCmd` so don't rely on it to compile test file
    # note: this should be exported in posix.nim
    proc c_system(cmd: cstring): cint {.importc: "system",
      header: "<stdlib.h>".}
    assertEquals c_system(cmd), 0

    ## use it
    template runTest(arg: string, expected: int) =
      echo (arg2: arg, expected2: expected)
      assertEquals execShellCmd(output & " " & arg), expected

    runTest("exit_0", 0)
    runTest("exitnow_139", 139)
    runTest("c_exit2_139", 139)
    runTest("quit_139", 139)
    runTest("exit_array", 1)
    when defined(posix): # on windows, -1073741571
      runTest("exit_recursion", SIGSEGV.int + 128) # bug #10273: was returning 0
      assertEquals exitStatusLikeShell(SIGSEGV), SIGSEGV + 128.cint

  block execProcessTest:
    let dir = parentDir(currentSourcePath())
    let (outp, err) = execCmdEx("nim c " & quoteShell(dir / "osproctest.nim"))
    doAssert err == 0
    let exePath = dir / addFileExt("osproctest", ExeExt)
    let outStr1 = execProcess(exePath, workingDir = dir, args = ["foo",
        "b A r"], options = {})
    doAssert outStr1 == dir & "\nfoo\nb A r\n"

    const testDir = "t e st"
    createDir(testDir)
    doAssert dirExists(testDir)
    let outStr2 = execProcess(exePath, workingDir = testDir, args = ["x yz"],
        options = {})
    doAssert outStr2 == absolutePath(testDir) & "\nx yz\n"

    removeDir(testDir)
    try:
      removeFile(exePath)
    except OSError:
      discard