win32-process 0.8.3 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,218 +1,218 @@
1
- if RUBY_PLATFORM == 'java'
2
- require 'rubygems'
3
- gem 'ffi'
4
- end
5
-
6
- require 'ffi'
7
-
8
- module Process::Structs
9
- extend FFI::Library
10
-
11
- typedef :ulong, :dword
12
- typedef :uintptr_t, :handle
13
- typedef :short, :word
14
-
15
- private
16
-
17
- # sizeof(LARGE_INTEGER) == 8
18
- class LARGE_INTEGER < FFI::Union
19
- layout(:QuadPart, :long_long)
20
- end
21
-
22
- # sizeof(IO_COUNTERS) == 48
23
- class IO_COUNTERS < FFI::Struct
24
- layout(
25
- :ReadOperationCount, :ulong_long,
26
- :WriteOperationCount, :ulong_long,
27
- :OtherOperationCount, :ulong_long,
28
- :ReadTransferCount, :ulong_long,
29
- :WriteTransferCount, :ulong_long,
30
- :OtherTransferCount, :ulong_long
31
- )
32
- end
33
-
34
- class JOBJECT_BASIC_LIMIT_INFORMATION < FFI::Struct
35
- layout(
36
- :PerProcessUserTimeLimit, LARGE_INTEGER,
37
- :PerJobUserTimeLimit, LARGE_INTEGER,
38
- :LimitFlags, :dword,
39
- :MinimumWorkingSetSize, :size_t,
40
- :MaximumWorkingSetSize, :size_t,
41
- :ActiveProcessLimit, :dword,
42
- :Affinity, :pointer,
43
- :PriorityClass, :dword,
44
- :SchedulingClass, :dword
45
- )
46
- end
47
-
48
- class JOBJECT_EXTENDED_LIMIT_INFORMATION < FFI::Struct
49
- layout(
50
- :BasicLimitInformation, JOBJECT_BASIC_LIMIT_INFORMATION,
51
- :IoInfo, IO_COUNTERS,
52
- :ProcessMemoryLimit, :size_t,
53
- :JobMemoryLimit, :size_t,
54
- :PeakProcessMemoryUsed, :size_t,
55
- :PeakJobMemoryUsed, :size_t
56
- )
57
- end
58
-
59
- class SECURITY_ATTRIBUTES < FFI::Struct
60
- layout(
61
- :nLength, :dword,
62
- :lpSecurityDescriptor, :pointer,
63
- :bInheritHandle, :int
64
- )
65
- end
66
-
67
- # sizeof(STARTUPINFO) == 68
68
- class STARTUPINFO < FFI::Struct
69
- layout(
70
- :cb, :ulong,
71
- :lpReserved, :string,
72
- :lpDesktop, :string,
73
- :lpTitle, :string,
74
- :dwX, :dword,
75
- :dwY, :dword,
76
- :dwXSize, :dword,
77
- :dwYSize, :dword,
78
- :dwXCountChars, :dword,
79
- :dwYCountChars, :dword,
80
- :dwFillAttribute, :dword,
81
- :dwFlags, :dword,
82
- :wShowWindow, :word,
83
- :cbReserved2, :word,
84
- :lpReserved2, :pointer,
85
- :hStdInput, :handle,
86
- :hStdOutput, :handle,
87
- :hStdError, :handle
88
- )
89
- end
90
-
91
- class PROCESS_INFORMATION < FFI::Struct
92
- layout(
93
- :hProcess, :handle,
94
- :hThread, :handle,
95
- :dwProcessId, :ulong,
96
- :dwThreadId, :ulong
97
- )
98
- end
99
-
100
- class OSVERSIONINFO < FFI::Struct
101
- layout(
102
- :dwOSVersionInfoSize, :dword,
103
- :dwMajorVersion, :dword,
104
- :dwMinorVersion, :dword,
105
- :dwBuildNumber, :dword,
106
- :dwPlatformId, :dword,
107
- :szCSDVersion, [:char, 128]
108
- )
109
- end
110
-
111
- class THREADENTRY32 < FFI::Struct
112
- layout(
113
- :dwSize, :dword,
114
- :cntUsage, :dword,
115
- :th32ThreadID, :dword,
116
- :th32OwnerProcessID, :dword,
117
- :tpBasePri, :long,
118
- :tpDeltaPri, :long,
119
- :dwFlags, :dword
120
- )
121
- end
122
-
123
- class HEAPLIST32 < FFI::Struct
124
- layout(
125
- :dwSize, :size_t,
126
- :th32ProcessID, :dword,
127
- :th32HeapID, :uintptr_t,
128
- :dwFlags, :dword
129
- )
130
- end
131
-
132
- class HEAPENTRY32 < FFI::Struct
133
- layout(
134
- :dwSize, :size_t,
135
- :hHandle, :handle,
136
- :dwAddress, :uintptr_t,
137
- :dwBlockSize, :size_t,
138
- :dwFlags, :dword,
139
- :dwLockCount, :dword,
140
- :dwResvd, :dword,
141
- :th32ProcessID, :dword,
142
- :th32HeapID, :uintptr_t
143
- )
144
- end
145
-
146
- class MODULEENTRY32 < FFI::Struct
147
- layout(
148
- :dwSize, :dword,
149
- :th32ModuleID, :dword,
150
- :th32ProcessID, :dword,
151
- :GlblcntUsage, :dword,
152
- :ProccntUsage, :dword,
153
- :modBaseAddr, :pointer,
154
- :modBaseSize, :dword,
155
- :hModule, :handle,
156
- :szModule, [:char, 256],
157
- :szExePath, [:char, 260]
158
- )
159
- end
160
-
161
- class PROCESSENTRY32 < FFI::Struct
162
- layout(
163
- :dwSize, :dword,
164
- :cntUsage, :dword,
165
- :th32ProcessID, :dword,
166
- :th32DefaultHeapID, :uintptr_t,
167
- :th32ModuleID, :dword,
168
- :cntThreads, :dword,
169
- :th32ParentProcessID, :dword,
170
- :pcPriClassBase, :long,
171
- :dwFlags, :dword,
172
- :szExeFile, [:char, 260]
173
- )
174
- end
175
-
176
- # Used by Process.create
177
-
178
- ProcessInfo = Struct.new("ProcessInfo",
179
- :process_handle,
180
- :thread_handle,
181
- :process_id,
182
- :thread_id
183
- )
184
-
185
- # Used by Process.snapshot
186
-
187
- ThreadSnapInfo = Struct.new("ThreadSnapInfo",
188
- :thread_id,
189
- :process_id,
190
- :base_priority
191
- )
192
-
193
- HeapSnapInfo = Struct.new("HeapSnapInfo",
194
- :address,
195
- :block_size,
196
- :flags,
197
- :process_id,
198
- :heap_id
199
- )
200
-
201
- ModuleSnapInfo = Struct.new("ModuleSnapInfo",
202
- :process_id,
203
- :address,
204
- :module_size,
205
- :handle,
206
- :name,
207
- :path
208
- )
209
-
210
- ProcessSnapInfo = Struct.new("ProcessSnapInfo",
211
- :process_id,
212
- :threads,
213
- :parent_process_id,
214
- :priority,
215
- :flags,
216
- :path
217
- )
218
- end
1
+ if RUBY_PLATFORM == 'java'
2
+ require 'rubygems'
3
+ gem 'ffi'
4
+ end
5
+
6
+ require 'ffi'
7
+
8
+ module Process::Structs
9
+ extend FFI::Library
10
+
11
+ typedef :ulong, :dword
12
+ typedef :uintptr_t, :handle
13
+ typedef :short, :word
14
+
15
+ private
16
+
17
+ # sizeof(LARGE_INTEGER) == 8
18
+ class LARGE_INTEGER < FFI::Union
19
+ layout(:QuadPart, :long_long)
20
+ end
21
+
22
+ # sizeof(IO_COUNTERS) == 48
23
+ class IO_COUNTERS < FFI::Struct
24
+ layout(
25
+ :ReadOperationCount, :ulong_long,
26
+ :WriteOperationCount, :ulong_long,
27
+ :OtherOperationCount, :ulong_long,
28
+ :ReadTransferCount, :ulong_long,
29
+ :WriteTransferCount, :ulong_long,
30
+ :OtherTransferCount, :ulong_long
31
+ )
32
+ end
33
+
34
+ class JOBJECT_BASIC_LIMIT_INFORMATION < FFI::Struct
35
+ layout(
36
+ :PerProcessUserTimeLimit, LARGE_INTEGER,
37
+ :PerJobUserTimeLimit, LARGE_INTEGER,
38
+ :LimitFlags, :dword,
39
+ :MinimumWorkingSetSize, :size_t,
40
+ :MaximumWorkingSetSize, :size_t,
41
+ :ActiveProcessLimit, :dword,
42
+ :Affinity, :pointer,
43
+ :PriorityClass, :dword,
44
+ :SchedulingClass, :dword
45
+ )
46
+ end
47
+
48
+ class JOBJECT_EXTENDED_LIMIT_INFORMATION < FFI::Struct
49
+ layout(
50
+ :BasicLimitInformation, JOBJECT_BASIC_LIMIT_INFORMATION,
51
+ :IoInfo, IO_COUNTERS,
52
+ :ProcessMemoryLimit, :size_t,
53
+ :JobMemoryLimit, :size_t,
54
+ :PeakProcessMemoryUsed, :size_t,
55
+ :PeakJobMemoryUsed, :size_t
56
+ )
57
+ end
58
+
59
+ class SECURITY_ATTRIBUTES < FFI::Struct
60
+ layout(
61
+ :nLength, :dword,
62
+ :lpSecurityDescriptor, :pointer,
63
+ :bInheritHandle, :int
64
+ )
65
+ end
66
+
67
+ # sizeof(STARTUPINFO) == 68
68
+ class STARTUPINFO < FFI::Struct
69
+ layout(
70
+ :cb, :ulong,
71
+ :lpReserved, :string,
72
+ :lpDesktop, :string,
73
+ :lpTitle, :string,
74
+ :dwX, :dword,
75
+ :dwY, :dword,
76
+ :dwXSize, :dword,
77
+ :dwYSize, :dword,
78
+ :dwXCountChars, :dword,
79
+ :dwYCountChars, :dword,
80
+ :dwFillAttribute, :dword,
81
+ :dwFlags, :dword,
82
+ :wShowWindow, :word,
83
+ :cbReserved2, :word,
84
+ :lpReserved2, :pointer,
85
+ :hStdInput, :handle,
86
+ :hStdOutput, :handle,
87
+ :hStdError, :handle
88
+ )
89
+ end
90
+
91
+ class PROCESS_INFORMATION < FFI::Struct
92
+ layout(
93
+ :hProcess, :handle,
94
+ :hThread, :handle,
95
+ :dwProcessId, :ulong,
96
+ :dwThreadId, :ulong
97
+ )
98
+ end
99
+
100
+ class OSVERSIONINFO < FFI::Struct
101
+ layout(
102
+ :dwOSVersionInfoSize, :dword,
103
+ :dwMajorVersion, :dword,
104
+ :dwMinorVersion, :dword,
105
+ :dwBuildNumber, :dword,
106
+ :dwPlatformId, :dword,
107
+ :szCSDVersion, [:char, 128]
108
+ )
109
+ end
110
+
111
+ class THREADENTRY32 < FFI::Struct
112
+ layout(
113
+ :dwSize, :dword,
114
+ :cntUsage, :dword,
115
+ :th32ThreadID, :dword,
116
+ :th32OwnerProcessID, :dword,
117
+ :tpBasePri, :long,
118
+ :tpDeltaPri, :long,
119
+ :dwFlags, :dword
120
+ )
121
+ end
122
+
123
+ class HEAPLIST32 < FFI::Struct
124
+ layout(
125
+ :dwSize, :size_t,
126
+ :th32ProcessID, :dword,
127
+ :th32HeapID, :uintptr_t,
128
+ :dwFlags, :dword
129
+ )
130
+ end
131
+
132
+ class HEAPENTRY32 < FFI::Struct
133
+ layout(
134
+ :dwSize, :size_t,
135
+ :hHandle, :handle,
136
+ :dwAddress, :uintptr_t,
137
+ :dwBlockSize, :size_t,
138
+ :dwFlags, :dword,
139
+ :dwLockCount, :dword,
140
+ :dwResvd, :dword,
141
+ :th32ProcessID, :dword,
142
+ :th32HeapID, :uintptr_t
143
+ )
144
+ end
145
+
146
+ class MODULEENTRY32 < FFI::Struct
147
+ layout(
148
+ :dwSize, :dword,
149
+ :th32ModuleID, :dword,
150
+ :th32ProcessID, :dword,
151
+ :GlblcntUsage, :dword,
152
+ :ProccntUsage, :dword,
153
+ :modBaseAddr, :pointer,
154
+ :modBaseSize, :dword,
155
+ :hModule, :handle,
156
+ :szModule, [:char, 256],
157
+ :szExePath, [:char, 260]
158
+ )
159
+ end
160
+
161
+ class PROCESSENTRY32 < FFI::Struct
162
+ layout(
163
+ :dwSize, :dword,
164
+ :cntUsage, :dword,
165
+ :th32ProcessID, :dword,
166
+ :th32DefaultHeapID, :uintptr_t,
167
+ :th32ModuleID, :dword,
168
+ :cntThreads, :dword,
169
+ :th32ParentProcessID, :dword,
170
+ :pcPriClassBase, :long,
171
+ :dwFlags, :dword,
172
+ :szExeFile, [:char, 260]
173
+ )
174
+ end
175
+
176
+ # Used by Process.create
177
+
178
+ ProcessInfo = Struct.new("ProcessInfo",
179
+ :process_handle,
180
+ :thread_handle,
181
+ :process_id,
182
+ :thread_id
183
+ )
184
+
185
+ # Used by Process.snapshot
186
+
187
+ ThreadSnapInfo = Struct.new("ThreadSnapInfo",
188
+ :thread_id,
189
+ :process_id,
190
+ :base_priority
191
+ )
192
+
193
+ HeapSnapInfo = Struct.new("HeapSnapInfo",
194
+ :address,
195
+ :block_size,
196
+ :flags,
197
+ :process_id,
198
+ :heap_id
199
+ )
200
+
201
+ ModuleSnapInfo = Struct.new("ModuleSnapInfo",
202
+ :process_id,
203
+ :address,
204
+ :module_size,
205
+ :handle,
206
+ :name,
207
+ :path
208
+ )
209
+
210
+ ProcessSnapInfo = Struct.new("ProcessSnapInfo",
211
+ :process_id,
212
+ :threads,
213
+ :parent_process_id,
214
+ :priority,
215
+ :flags,
216
+ :path
217
+ )
218
+ end
@@ -1,370 +1,370 @@
1
- ###############################################################################
2
- # test_win32_process.rb
3
- #
4
- # Test suite for the win32-process library. This test suite will start
5
- # at least two instances of Notepad on your system, which will then
6
- # be killed. Requires the sys-proctable library.
7
- #
8
- # I haven't added a lot of test cases for fork/wait because it's difficult
9
- # to run such tests without causing havoc with Test::Unit itself. Ideas
10
- # welcome.
11
- #
12
- # You should run this test case via the 'rake test' task.
13
- ###############################################################################
14
- require 'test-unit'
15
- require 'win32/process'
16
-
17
- class TC_Win32Process < Test::Unit::TestCase
18
- def self.startup
19
- @@pids = []
20
- @@jruby = RUBY_PLATFORM == 'java'
21
- end
22
-
23
- def setup
24
- @priority = Process::BELOW_NORMAL_PRIORITY_CLASS
25
- end
26
-
27
- test "win32-process version is set to the correct value" do
28
- assert_equal('0.8.3', Process::WIN32_PROCESS_VERSION)
29
- end
30
-
31
- test "create basic functionality" do
32
- assert_respond_to(Process, :create)
33
- end
34
-
35
- test "create with common flags works as expected" do
36
- assert_nothing_raised{
37
- @@pids << Process.create(
38
- :app_name => "notepad.exe",
39
- :creation_flags => Process::DETACHED_PROCESS,
40
- :process_inherit => false,
41
- :thread_inherit => true,
42
- :cwd => "C:\\"
43
- ).process_id
44
- }
45
-
46
- assert_nothing_raised{ Process.kill(9, @@pids.pop) }
47
- end
48
-
49
- test "create requires a hash argument" do
50
- assert_raise(TypeError){ Process.create("bogusapp.exe") }
51
- end
52
-
53
- test "create does not accept invalid keys" do
54
- assert_raise(ArgumentError){ Process.create(:bogus => 'test.exe') }
55
- assert_raise_message("invalid key 'bogus'"){
56
- Process.create(:bogus => 'test.exe')
57
- }
58
- end
59
-
60
- test "create does not accept invalid startup_info keys" do
61
- assert_raise(ArgumentError){
62
- Process.create(:startup_info => {:foo => 'test'})
63
- }
64
- assert_raise_message("invalid startup_info key 'foo'"){
65
- Process.create(:startup_info => {:foo => 'test'})
66
- }
67
- end
68
-
69
- test "create raises an error if the executable cannot be found" do
70
- assert_raise(Errno::ENOENT){ Process.create(:app_name => "bogusapp.exe") }
71
- end
72
-
73
- test "create passes local environment when environment is not specified" do
74
- omit_if(@@jruby)
75
- stdout_read, stdout_write = IO.pipe
76
-
77
- ENV['AARDVARK'] = 'B'
78
- assert_nothing_raised {
79
- Process.create(
80
- :app_name => 'cmd.exe /c echo %AARDVARK%',
81
- :creation_flags => Process::DETACHED_PROCESS,
82
- :startup_info => { :stdout => stdout_write }
83
- )
84
- }
85
-
86
- stdout_write.close
87
- assert_equal('B', stdout_read.read.chomp)
88
- end
89
-
90
- test "create does not pass local environment when environment is specified" do
91
- omit_if(@@jruby)
92
- stdout_read, stdout_write = IO.pipe
93
-
94
- ENV['AARDVARK'] = 'B'
95
- assert_nothing_raised {
96
- Process.create(
97
- :app_name => 'cmd.exe /c echo %AARDVARK%',
98
- :creation_flags => Process::DETACHED_PROCESS,
99
- :environment => "",
100
- :startup_info => { :stdout => stdout_write }
101
- )
102
- }
103
-
104
- stdout_write.close
105
- assert_equal('%AARDVARK%', stdout_read.read.chomp)
106
- end
107
-
108
- test "create supports :environment as a string" do
109
- omit_if(@@jruby)
110
- stdout_read, stdout_write = IO.pipe
111
-
112
- assert_nothing_raised {
113
- Process.create(
114
- :app_name => 'cmd.exe /c echo %A% %C%',
115
- :creation_flags => Process::DETACHED_PROCESS,
116
- :environment => "A=B;C=D",
117
- :startup_info => { :stdout => stdout_write }
118
- )
119
- }
120
-
121
- stdout_write.close
122
- assert_equal("B D", stdout_read.read.chomp)
123
- end
124
-
125
- test "create supports :environment as an array" do
126
- omit_if(@@jruby)
127
- stdout_read, stdout_write = IO.pipe
128
-
129
- assert_nothing_raised {
130
- Process.create(
131
- :app_name => 'cmd.exe /c echo %A% %C%',
132
- :creation_flags => Process::DETACHED_PROCESS,
133
- :environment => [ "A=B;X;", "C=;D;Y" ],
134
- :startup_info => { :stdout => stdout_write }
135
- )
136
- }
137
-
138
- stdout_write.close
139
- assert_equal("B;X; ;D;Y", stdout_read.read.chomp)
140
- end
141
-
142
- test "create supports empty :environment string" do
143
- assert_nothing_raised {
144
- Process.create(
145
- :creation_flags => Process::DETACHED_PROCESS,
146
- :app_name => 'cmd.exe',
147
- :environment => ''
148
- )
149
- }
150
- end
151
-
152
- test "create supports empty :environment array" do
153
- assert_nothing_raised {
154
- Process.create(
155
- :creation_flags => Process::DETACHED_PROCESS,
156
- :app_name => 'cmd.exe',
157
- :environment => []
158
- )
159
- }
160
- end
161
-
162
- test "uid basic functionality" do
163
- assert_respond_to(Process, :uid)
164
- assert_kind_of(Fixnum, Process.uid)
165
- end
166
-
167
- test "uid accepts a boolean argument" do
168
- assert_nothing_raised{ Process.uid(true) }
169
- assert_nothing_raised{ Process.uid(true) }
170
- end
171
-
172
- test "uid returns a string if its argument is true" do
173
- assert_kind_of(String, Process.uid(true))
174
- end
175
-
176
- test "uid accepts a maximum of one argument" do
177
- assert_raise(ArgumentError){ Process.uid(true, true) }
178
- end
179
-
180
- test "argument to uid must be a boolean" do
181
- assert_raise(TypeError){ Process.uid('test') }
182
- end
183
-
184
- test "getpriority basic functionality" do
185
- assert_respond_to(Process, :getpriority)
186
- assert_nothing_raised{ Process.getpriority(Process::PRIO_PROCESS, Process.pid) }
187
- assert_kind_of(Fixnum, Process.getpriority(Process::PRIO_PROCESS, Process.pid))
188
- end
189
-
190
- test "getpriority treats an int argument of zero as the current process" do
191
- assert_nothing_raised{ Process.getpriority(0, 0) }
192
- end
193
-
194
- test "getpriority requires both a kind and an int" do
195
- assert_raise(ArgumentError){ Process.getpriority }
196
- assert_raise(ArgumentError){ Process.getpriority(Process::PRIO_PROCESS) }
197
- end
198
-
199
- test "getpriority requires integer arguments" do
200
- assert_raise(TypeError){ Process.getpriority('test', 0) }
201
- assert_raise(TypeError){ Process.getpriority(Process::PRIO_PROCESS, 'test') }
202
- end
203
-
204
- test "setpriority basic functionality" do
205
- assert_respond_to(Process, :setpriority)
206
- assert_nothing_raised{ Process.setpriority(0, Process.pid, @priority) }
207
- assert_equal(@priority, Process.getpriority(0, Process.pid))
208
- end
209
-
210
- test "setpriority returns zero on success" do
211
- assert_equal(0, Process.setpriority(0, Process.pid, @priority))
212
- end
213
-
214
- test "setpriority treats an int argument of zero as the current process" do
215
- assert_equal(0, Process.setpriority(0, 0, @priority))
216
- end
217
-
218
- test "setpriority requires at least three arguments" do
219
- assert_raise(ArgumentError){ Process.setpriority }
220
- assert_raise(ArgumentError){ Process.setpriority(0) }
221
- assert_raise(ArgumentError){ Process.setpriority(0, 0) }
222
- end
223
-
224
- test "arguments to setpriority must be numeric" do
225
- assert_raise(TypeError){ Process.setpriority('test', 0, @priority) }
226
- assert_raise(TypeError){ Process.setpriority(0, 'test', @priority) }
227
- assert_raise(TypeError){ Process.setpriority(0, 0, 'test') }
228
- end
229
-
230
- test "custom creation constants are defined" do
231
- assert_not_nil(Process::CREATE_DEFAULT_ERROR_MODE)
232
- assert_not_nil(Process::CREATE_NEW_CONSOLE)
233
- assert_not_nil(Process::CREATE_NEW_PROCESS_GROUP)
234
- assert_not_nil(Process::CREATE_NO_WINDOW)
235
- assert_not_nil(Process::CREATE_SEPARATE_WOW_VDM)
236
- assert_not_nil(Process::CREATE_SHARED_WOW_VDM)
237
- assert_not_nil(Process::CREATE_SUSPENDED)
238
- assert_not_nil(Process::CREATE_UNICODE_ENVIRONMENT)
239
- assert_not_nil(Process::DEBUG_ONLY_THIS_PROCESS)
240
- assert_not_nil(Process::DEBUG_PROCESS)
241
- assert_not_nil(Process::DETACHED_PROCESS)
242
- end
243
-
244
- test "getrlimit basic functionality" do
245
- assert_respond_to(Process, :getrlimit)
246
- assert_nothing_raised{ Process.getrlimit(Process::RLIMIT_CPU) }
247
- assert_nothing_raised{ Process.getrlimit(Process::RLIMIT_FSIZE) }
248
- end
249
-
250
- test "getrlimit returns an array of two numeric elements" do
251
- assert_kind_of(Array, Process.getrlimit(Process::RLIMIT_CPU))
252
- assert_equal(2, Process.getrlimit(Process::RLIMIT_CPU).length)
253
- assert_kind_of(Integer, Process.getrlimit(Process::RLIMIT_CPU).first)
254
- end
255
-
256
- test "getrlimit can be called multiple times without issue" do
257
- assert_nothing_raised{ Process.getrlimit(Process::RLIMIT_CPU) }
258
- assert_nothing_raised{ Process.getrlimit(Process::RLIMIT_CPU) }
259
- assert_nothing_raised{ Process.getrlimit(Process::RLIMIT_CPU) }
260
- end
261
-
262
- test "getrlimit requires a valid resource value" do
263
- assert_raise(ArgumentError){ Process.getrlimit(9999) }
264
- end
265
-
266
- test "setrlimit basic functionality" do
267
- assert_respond_to(Process, :setrlimit)
268
- assert_nothing_raised{ Process.setrlimit(Process::RLIMIT_CPU, 1000000) }
269
- end
270
-
271
- test "setrlimit returns nil on success" do
272
- assert_nil(Process.setrlimit(Process::RLIMIT_CPU, 1000000))
273
- end
274
-
275
- test "setrlimit sets the resource limit as expected" do
276
- assert_nothing_raised{ Process.setrlimit(Process::RLIMIT_CPU, 1000000) }
277
- assert_equal([1000000, 1000000], Process.getrlimit(Process::RLIMIT_CPU))
278
- end
279
-
280
- test "setrlimit raises an error if the resource value is invalid" do
281
- assert_raise(ArgumentError){ Process.setrlimit(9999, 100) }
282
- end
283
-
284
- test "is_job basic functionality" do
285
- assert_respond_to(Process, :job?)
286
- assert_nothing_raised{ Process.job? }
287
- end
288
-
289
- test "is_job returns a boolean value" do
290
- assert_boolean(Process.job?)
291
- end
292
-
293
- test "is_job does not accept any arguments" do
294
- assert_raise(ArgumentError){ Process.job?(Process.pid) }
295
- end
296
-
297
- test "volume_type is a private method" do
298
- assert_not_respond_to(Process, :volume_type)
299
- end
300
-
301
- test "snapshot method basic functionality" do
302
- assert_respond_to(Process, :snapshot)
303
- assert_nothing_raised{ Process.snapshot }
304
- assert_kind_of(Hash, Process.snapshot)
305
- end
306
-
307
- test "snapshot accepts :thread, :module or :heap arguments" do
308
- assert_nothing_raised{ Process.snapshot(:thread) }
309
- assert_nothing_raised{ Process.snapshot(:module) }
310
- #assert_nothing_raised{ Process.snapshot(:heap) }
311
- assert_nothing_raised{ Process.snapshot(:process) }
312
- end
313
-
314
- test "snapshot raises an error if an invalid argument is passed" do
315
- assert_raise(ArgumentError){ Process.snapshot(:bogus) }
316
- end
317
-
318
- test "ffi functions are private" do
319
- assert_not_respond_to(Process, :CloseHandle)
320
- assert_not_respond_to(Process, :GetCurrentProcess)
321
- assert_not_respond_to(Process, :GetProcessAffinityMask)
322
- assert_not_respond_to(Process, :GetPriorityClass)
323
- assert_not_respond_to(Process, :IsProcessInJob)
324
- assert_not_respond_to(Process, :OpenProcess)
325
- assert_not_respond_to(Process, :SetHandleInformation)
326
- assert_not_respond_to(Process, :SetPriorityClass)
327
- end
328
-
329
- test "get_exitcode basic functionality" do
330
- assert_respond_to(Process, :get_exitcode)
331
- end
332
-
333
- =begin
334
- test "get_exitcode returns the process exit code" do
335
- pid = Process.create(
336
- :app_name => 'cmd /c exit 7',
337
- :creation_flags => Process::DETACHED_PROCESS
338
- ).process_id
339
- 10.times do
340
- sleep(0.1)
341
- break if Process.get_exitcode(pid)
342
- end
343
- assert_equal 7, Process.get_exitcode(pid)
344
- end
345
-
346
- test "get_exitcode returns nil while the process is running" do
347
- stdin_read, stdin_write = IO.pipe
348
- pid = Process.create(
349
- :app_name => 'cmd /c pause',
350
- :creation_flags => Process::DETACHED_PROCESS,
351
- :startup_info => { :stdin => stdin_read }
352
- ).process_id
353
- assert_equal nil, Process.get_exitcode(pid)
354
- stdin_write.write("\n")
355
- 10.times do
356
- sleep(0.1)
357
- break if Process.get_exitcode(pid)
358
- end
359
- assert_equal 1, Process.get_exitcode(pid)
360
- end
361
- =end
362
-
363
- def teardown
364
- @priority = nil
365
- end
366
-
367
- def self.shutdown
368
- @@pids = nil
369
- end
370
- end
1
+ ###############################################################################
2
+ # test_win32_process.rb
3
+ #
4
+ # Test suite for the win32-process library. This test suite will start
5
+ # at least two instances of Notepad on your system, which will then
6
+ # be killed. Requires the sys-proctable library.
7
+ #
8
+ # I haven't added a lot of test cases for fork/wait because it's difficult
9
+ # to run such tests without causing havoc with Test::Unit itself. Ideas
10
+ # welcome.
11
+ #
12
+ # You should run this test case via the 'rake test' task.
13
+ ###############################################################################
14
+ require 'test-unit'
15
+ require 'win32/process'
16
+
17
+ class TC_Win32Process < Test::Unit::TestCase
18
+ def self.startup
19
+ @@pids = []
20
+ @@jruby = RUBY_PLATFORM == 'java'
21
+ end
22
+
23
+ def setup
24
+ @priority = Process::BELOW_NORMAL_PRIORITY_CLASS
25
+ end
26
+
27
+ test "win32-process version is set to the correct value" do
28
+ assert_equal('0.9.0', Process::WIN32_PROCESS_VERSION)
29
+ end
30
+
31
+ test "create basic functionality" do
32
+ assert_respond_to(Process, :create)
33
+ end
34
+
35
+ test "create with common flags works as expected" do
36
+ assert_nothing_raised{
37
+ @@pids << Process.create(
38
+ :app_name => "notepad.exe",
39
+ :creation_flags => Process::DETACHED_PROCESS,
40
+ :process_inherit => false,
41
+ :thread_inherit => true,
42
+ :cwd => "C:\\"
43
+ ).process_id
44
+ }
45
+
46
+ assert_nothing_raised{ Process.kill(9, @@pids.pop) }
47
+ end
48
+
49
+ test "create requires a hash argument" do
50
+ assert_raise(TypeError){ Process.create("bogusapp.exe") }
51
+ end
52
+
53
+ test "create does not accept invalid keys" do
54
+ assert_raise(ArgumentError){ Process.create(:bogus => 'test.exe') }
55
+ assert_raise_message("invalid key 'bogus'"){
56
+ Process.create(:bogus => 'test.exe')
57
+ }
58
+ end
59
+
60
+ test "create does not accept invalid startup_info keys" do
61
+ assert_raise(ArgumentError){
62
+ Process.create(:startup_info => {:foo => 'test'})
63
+ }
64
+ assert_raise_message("invalid startup_info key 'foo'"){
65
+ Process.create(:startup_info => {:foo => 'test'})
66
+ }
67
+ end
68
+
69
+ test "create raises an error if the executable cannot be found" do
70
+ assert_raise(Errno::ENOENT){ Process.create(:app_name => "bogusapp.exe") }
71
+ end
72
+
73
+ test "create passes local environment when environment is not specified" do
74
+ omit_if(@@jruby)
75
+ stdout_read, stdout_write = IO.pipe
76
+
77
+ ENV['AARDVARK'] = 'B'
78
+ assert_nothing_raised {
79
+ Process.create(
80
+ :app_name => 'cmd.exe /c echo %AARDVARK%',
81
+ :creation_flags => Process::DETACHED_PROCESS,
82
+ :startup_info => { :stdout => stdout_write }
83
+ )
84
+ }
85
+
86
+ stdout_write.close
87
+ assert_equal('B', stdout_read.read.chomp)
88
+ end
89
+
90
+ test "create does not pass local environment when environment is specified" do
91
+ omit_if(@@jruby)
92
+ stdout_read, stdout_write = IO.pipe
93
+
94
+ ENV['AARDVARK'] = 'B'
95
+ assert_nothing_raised {
96
+ Process.create(
97
+ :app_name => 'cmd.exe /c echo %AARDVARK%',
98
+ :creation_flags => Process::DETACHED_PROCESS,
99
+ :environment => "",
100
+ :startup_info => { :stdout => stdout_write }
101
+ )
102
+ }
103
+
104
+ stdout_write.close
105
+ assert_equal('%AARDVARK%', stdout_read.read.chomp)
106
+ end
107
+
108
+ test "create supports :environment as a string" do
109
+ omit_if(@@jruby)
110
+ stdout_read, stdout_write = IO.pipe
111
+
112
+ assert_nothing_raised {
113
+ Process.create(
114
+ :app_name => 'cmd.exe /c echo %A% %C%',
115
+ :creation_flags => Process::DETACHED_PROCESS,
116
+ :environment => "A=B;C=D",
117
+ :startup_info => { :stdout => stdout_write }
118
+ )
119
+ }
120
+
121
+ stdout_write.close
122
+ assert_equal("B D", stdout_read.read.chomp)
123
+ end
124
+
125
+ test "create supports :environment as an array" do
126
+ omit_if(@@jruby)
127
+ stdout_read, stdout_write = IO.pipe
128
+
129
+ assert_nothing_raised {
130
+ Process.create(
131
+ :app_name => 'cmd.exe /c echo %A% %C%',
132
+ :creation_flags => Process::DETACHED_PROCESS,
133
+ :environment => [ "A=B;X;", "C=;D;Y" ],
134
+ :startup_info => { :stdout => stdout_write }
135
+ )
136
+ }
137
+
138
+ stdout_write.close
139
+ assert_equal("B;X; ;D;Y", stdout_read.read.chomp)
140
+ end
141
+
142
+ test "create supports empty :environment string" do
143
+ assert_nothing_raised {
144
+ Process.create(
145
+ :creation_flags => Process::DETACHED_PROCESS,
146
+ :app_name => 'cmd.exe',
147
+ :environment => ''
148
+ )
149
+ }
150
+ end
151
+
152
+ test "create supports empty :environment array" do
153
+ assert_nothing_raised {
154
+ Process.create(
155
+ :creation_flags => Process::DETACHED_PROCESS,
156
+ :app_name => 'cmd.exe',
157
+ :environment => []
158
+ )
159
+ }
160
+ end
161
+
162
+ test "uid basic functionality" do
163
+ assert_respond_to(Process, :uid)
164
+ assert_kind_of(Fixnum, Process.uid)
165
+ end
166
+
167
+ test "uid accepts a boolean argument" do
168
+ assert_nothing_raised{ Process.uid(true) }
169
+ assert_nothing_raised{ Process.uid(true) }
170
+ end
171
+
172
+ test "uid returns a string if its argument is true" do
173
+ assert_kind_of(String, Process.uid(true))
174
+ end
175
+
176
+ test "uid accepts a maximum of one argument" do
177
+ assert_raise(ArgumentError){ Process.uid(true, true) }
178
+ end
179
+
180
+ test "argument to uid must be a boolean" do
181
+ assert_raise(TypeError){ Process.uid('test') }
182
+ end
183
+
184
+ test "getpriority basic functionality" do
185
+ assert_respond_to(Process, :getpriority)
186
+ assert_nothing_raised{ Process.getpriority(Process::PRIO_PROCESS, Process.pid) }
187
+ assert_kind_of(Fixnum, Process.getpriority(Process::PRIO_PROCESS, Process.pid))
188
+ end
189
+
190
+ test "getpriority treats an int argument of zero as the current process" do
191
+ assert_nothing_raised{ Process.getpriority(0, 0) }
192
+ end
193
+
194
+ test "getpriority requires both a kind and an int" do
195
+ assert_raise(ArgumentError){ Process.getpriority }
196
+ assert_raise(ArgumentError){ Process.getpriority(Process::PRIO_PROCESS) }
197
+ end
198
+
199
+ test "getpriority requires integer arguments" do
200
+ assert_raise(TypeError){ Process.getpriority('test', 0) }
201
+ assert_raise(TypeError){ Process.getpriority(Process::PRIO_PROCESS, 'test') }
202
+ end
203
+
204
+ test "setpriority basic functionality" do
205
+ assert_respond_to(Process, :setpriority)
206
+ assert_nothing_raised{ Process.setpriority(0, Process.pid, @priority) }
207
+ assert_equal(@priority, Process.getpriority(0, Process.pid))
208
+ end
209
+
210
+ test "setpriority returns zero on success" do
211
+ assert_equal(0, Process.setpriority(0, Process.pid, @priority))
212
+ end
213
+
214
+ test "setpriority treats an int argument of zero as the current process" do
215
+ assert_equal(0, Process.setpriority(0, 0, @priority))
216
+ end
217
+
218
+ test "setpriority requires at least three arguments" do
219
+ assert_raise(ArgumentError){ Process.setpriority }
220
+ assert_raise(ArgumentError){ Process.setpriority(0) }
221
+ assert_raise(ArgumentError){ Process.setpriority(0, 0) }
222
+ end
223
+
224
+ test "arguments to setpriority must be numeric" do
225
+ assert_raise(TypeError){ Process.setpriority('test', 0, @priority) }
226
+ assert_raise(TypeError){ Process.setpriority(0, 'test', @priority) }
227
+ assert_raise(TypeError){ Process.setpriority(0, 0, 'test') }
228
+ end
229
+
230
+ test "custom creation constants are defined" do
231
+ assert_not_nil(Process::CREATE_DEFAULT_ERROR_MODE)
232
+ assert_not_nil(Process::CREATE_NEW_CONSOLE)
233
+ assert_not_nil(Process::CREATE_NEW_PROCESS_GROUP)
234
+ assert_not_nil(Process::CREATE_NO_WINDOW)
235
+ assert_not_nil(Process::CREATE_SEPARATE_WOW_VDM)
236
+ assert_not_nil(Process::CREATE_SHARED_WOW_VDM)
237
+ assert_not_nil(Process::CREATE_SUSPENDED)
238
+ assert_not_nil(Process::CREATE_UNICODE_ENVIRONMENT)
239
+ assert_not_nil(Process::DEBUG_ONLY_THIS_PROCESS)
240
+ assert_not_nil(Process::DEBUG_PROCESS)
241
+ assert_not_nil(Process::DETACHED_PROCESS)
242
+ end
243
+
244
+ test "getrlimit basic functionality" do
245
+ assert_respond_to(Process, :getrlimit)
246
+ assert_nothing_raised{ Process.getrlimit(Process::RLIMIT_CPU) }
247
+ assert_nothing_raised{ Process.getrlimit(Process::RLIMIT_FSIZE) }
248
+ end
249
+
250
+ test "getrlimit returns an array of two numeric elements" do
251
+ assert_kind_of(Array, Process.getrlimit(Process::RLIMIT_CPU))
252
+ assert_equal(2, Process.getrlimit(Process::RLIMIT_CPU).length)
253
+ assert_kind_of(Integer, Process.getrlimit(Process::RLIMIT_CPU).first)
254
+ end
255
+
256
+ test "getrlimit can be called multiple times without issue" do
257
+ assert_nothing_raised{ Process.getrlimit(Process::RLIMIT_CPU) }
258
+ assert_nothing_raised{ Process.getrlimit(Process::RLIMIT_CPU) }
259
+ assert_nothing_raised{ Process.getrlimit(Process::RLIMIT_CPU) }
260
+ end
261
+
262
+ test "getrlimit requires a valid resource value" do
263
+ assert_raise(ArgumentError){ Process.getrlimit(9999) }
264
+ end
265
+
266
+ test "setrlimit basic functionality" do
267
+ assert_respond_to(Process, :setrlimit)
268
+ assert_nothing_raised{ Process.setrlimit(Process::RLIMIT_CPU, 1000000) }
269
+ end
270
+
271
+ test "setrlimit returns nil on success" do
272
+ assert_nil(Process.setrlimit(Process::RLIMIT_CPU, 1000000))
273
+ end
274
+
275
+ test "setrlimit sets the resource limit as expected" do
276
+ assert_nothing_raised{ Process.setrlimit(Process::RLIMIT_CPU, 1000000) }
277
+ assert_equal([1000000, 1000000], Process.getrlimit(Process::RLIMIT_CPU))
278
+ end
279
+
280
+ test "setrlimit raises an error if the resource value is invalid" do
281
+ assert_raise(ArgumentError){ Process.setrlimit(9999, 100) }
282
+ end
283
+
284
+ test "is_job basic functionality" do
285
+ assert_respond_to(Process, :job?)
286
+ assert_nothing_raised{ Process.job? }
287
+ end
288
+
289
+ test "is_job returns a boolean value" do
290
+ assert_boolean(Process.job?)
291
+ end
292
+
293
+ test "is_job does not accept any arguments" do
294
+ assert_raise(ArgumentError){ Process.job?(Process.pid) }
295
+ end
296
+
297
+ test "volume_type is a private method" do
298
+ assert_not_respond_to(Process, :volume_type)
299
+ end
300
+
301
+ test "snapshot method basic functionality" do
302
+ assert_respond_to(Process, :snapshot)
303
+ assert_nothing_raised{ Process.snapshot }
304
+ assert_kind_of(Hash, Process.snapshot)
305
+ end
306
+
307
+ test "snapshot accepts :thread, :module or :heap arguments" do
308
+ assert_nothing_raised{ Process.snapshot(:thread) }
309
+ assert_nothing_raised{ Process.snapshot(:module) }
310
+ #assert_nothing_raised{ Process.snapshot(:heap) }
311
+ assert_nothing_raised{ Process.snapshot(:process) }
312
+ end
313
+
314
+ test "snapshot raises an error if an invalid argument is passed" do
315
+ assert_raise(ArgumentError){ Process.snapshot(:bogus) }
316
+ end
317
+
318
+ test "ffi functions are private" do
319
+ assert_not_respond_to(Process, :CloseHandle)
320
+ assert_not_respond_to(Process, :GetCurrentProcess)
321
+ assert_not_respond_to(Process, :GetProcessAffinityMask)
322
+ assert_not_respond_to(Process, :GetPriorityClass)
323
+ assert_not_respond_to(Process, :IsProcessInJob)
324
+ assert_not_respond_to(Process, :OpenProcess)
325
+ assert_not_respond_to(Process, :SetHandleInformation)
326
+ assert_not_respond_to(Process, :SetPriorityClass)
327
+ end
328
+
329
+ test "get_exitcode basic functionality" do
330
+ assert_respond_to(Process, :get_exitcode)
331
+ end
332
+
333
+ =begin
334
+ test "get_exitcode returns the process exit code" do
335
+ pid = Process.create(
336
+ :app_name => 'cmd /c exit 7',
337
+ :creation_flags => Process::DETACHED_PROCESS
338
+ ).process_id
339
+ 10.times do
340
+ sleep(0.1)
341
+ break if Process.get_exitcode(pid)
342
+ end
343
+ assert_equal 7, Process.get_exitcode(pid)
344
+ end
345
+
346
+ test "get_exitcode returns nil while the process is running" do
347
+ stdin_read, stdin_write = IO.pipe
348
+ pid = Process.create(
349
+ :app_name => 'cmd /c pause',
350
+ :creation_flags => Process::DETACHED_PROCESS,
351
+ :startup_info => { :stdin => stdin_read }
352
+ ).process_id
353
+ assert_equal nil, Process.get_exitcode(pid)
354
+ stdin_write.write("\n")
355
+ 10.times do
356
+ sleep(0.1)
357
+ break if Process.get_exitcode(pid)
358
+ end
359
+ assert_equal 1, Process.get_exitcode(pid)
360
+ end
361
+ =end
362
+
363
+ def teardown
364
+ @priority = nil
365
+ end
366
+
367
+ def self.shutdown
368
+ @@pids = nil
369
+ end
370
+ end