sapphire 0.6.7 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. data/lib/sapphire.rb +3 -2
  2. data/lib/sapphire/Adapters/Selenium/RubySeleniumWebDriver.rb +43 -27
  3. data/lib/sapphire/Configuration/AppConfig.rb +1 -1
  4. data/lib/sapphire/DSL.rb +6 -1
  5. data/lib/sapphire/DSL/Browser/ExecuteAgainstControl.rb +6 -20
  6. data/lib/sapphire/DSL/Browser/Fluff/Be.rb +10 -0
  7. data/lib/sapphire/DSL/Browser/Fluff/By.rb +2 -2
  8. data/lib/sapphire/DSL/Browser/Fluff/Have.rb +10 -0
  9. data/lib/sapphire/DSL/Browser/Fluff/To.rb +2 -2
  10. data/lib/sapphire/DSL/Browser/For.rb +9 -10
  11. data/lib/sapphire/DSL/Browser/Nouns/Browser.rb +2 -2
  12. data/lib/sapphire/DSL/Browser/Nouns/Day.rb +42 -24
  13. data/lib/sapphire/DSL/Browser/Nouns/Enter.rb +1 -1
  14. data/lib/sapphire/DSL/Browser/Nouns/Month.rb +42 -24
  15. data/lib/sapphire/DSL/Browser/Nouns/Popup.rb +2 -2
  16. data/lib/sapphire/DSL/Browser/Nouns/Second.rb +42 -0
  17. data/lib/sapphire/DSL/Browser/Should.rb +1 -46
  18. data/lib/sapphire/DSL/Browser/Verbs/Accept.rb +1 -2
  19. data/lib/sapphire/DSL/Browser/Verbs/Check.rb +0 -1
  20. data/lib/sapphire/DSL/Browser/Verbs/Click.rb +0 -1
  21. data/lib/sapphire/DSL/Browser/Verbs/Compare.rb +5 -5
  22. data/lib/sapphire/DSL/Browser/Verbs/Complete.rb +2 -2
  23. data/lib/sapphire/DSL/Browser/Verbs/Disable.rb +1 -1
  24. data/lib/sapphire/DSL/Browser/Verbs/Exists.rb +6 -11
  25. data/lib/sapphire/DSL/Browser/Verbs/{Close.rb → Exit.rb} +0 -0
  26. data/lib/sapphire/DSL/Browser/Verbs/Has.rb +9 -0
  27. data/lib/sapphire/DSL/Browser/Verbs/MouseOver.rb +0 -1
  28. data/lib/sapphire/DSL/Browser/Verbs/Navigate.rb +1 -2
  29. data/lib/sapphire/DSL/Browser/Verbs/On.rb +19 -0
  30. data/lib/sapphire/DSL/Browser/Verbs/Press.rb +1 -1
  31. data/lib/sapphire/DSL/Browser/Verbs/Reload.rb +1 -1
  32. data/lib/sapphire/DSL/Browser/Verbs/Remove.rb +22 -0
  33. data/lib/sapphire/DSL/Browser/Verbs/Set.rb +1 -1
  34. data/lib/sapphire/DSL/Browser/Verbs/Start.rb +14 -16
  35. data/lib/sapphire/DSL/Browser/Verbs/Uncheck.rb +0 -1
  36. data/lib/sapphire/DSL/Comparisons/CheckedComparison.rb +17 -0
  37. data/lib/sapphire/DSL/Comparisons/Comparison.rb +34 -0
  38. data/lib/sapphire/DSL/Comparisons/ContainsComparison.rb +17 -0
  39. data/lib/sapphire/DSL/Comparisons/DifferComparison.rb +17 -0
  40. data/lib/sapphire/DSL/Comparisons/EqualsComparison.rb +16 -0
  41. data/lib/sapphire/DSL/Comparisons/NotComparison.rb +21 -0
  42. data/lib/sapphire/DSL/Comparisons/SelectedComparison.rb +17 -0
  43. data/lib/sapphire/DSL/Comparisons/StartsWithComparison.rb +17 -0
  44. data/lib/sapphire/DSL/Comparisons/VisibleComparison.rb +17 -0
  45. data/lib/sapphire/DSL/Configuration/Use.rb +3 -0
  46. data/lib/sapphire/DSL/Data/GetPageField.rb +4 -8
  47. data/lib/sapphire/DSL/Evaluations/Evaluation.rb +46 -0
  48. data/lib/sapphire/DSL/Evaluations/FieldNotDefinedEvaluation.rb +21 -0
  49. data/lib/sapphire/DSL/Evaluations/FieldNotFoundEvaluation.rb +21 -0
  50. data/lib/sapphire/DSL/Evaluators/AltText.rb +17 -0
  51. data/lib/sapphire/DSL/Evaluators/Checked.rb +17 -0
  52. data/lib/sapphire/DSL/Evaluators/Contain.rb +10 -0
  53. data/lib/sapphire/DSL/Evaluators/Count.rb +9 -0
  54. data/lib/sapphire/DSL/Evaluators/Differ.rb +10 -0
  55. data/lib/sapphire/DSL/Evaluators/Evaluator.rb +22 -0
  56. data/lib/sapphire/DSL/Evaluators/Exist.rb +9 -0
  57. data/lib/sapphire/DSL/Evaluators/In.rb +10 -0
  58. data/lib/sapphire/DSL/Evaluators/Selected.rb +10 -0
  59. data/lib/sapphire/DSL/Evaluators/Show.rb +10 -0
  60. data/lib/sapphire/DSL/Evaluators/Source.rb +17 -0
  61. data/lib/sapphire/DSL/Evaluators/Transition.rb +10 -0
  62. data/lib/sapphire/DSL/Evaluators/Validate.rb +8 -0
  63. data/lib/sapphire/DSL/Events/Causes.rb +3 -0
  64. data/lib/sapphire/DSL/Events/Event.rb +19 -0
  65. data/lib/sapphire/DSL/Events/Expect.rb +19 -0
  66. data/lib/sapphire/DSL/Scenarios/then.rb +1 -1
  67. data/lib/sapphire/DSL/Scenarios/when.rb +1 -1
  68. data/lib/sapphire/DSL/TestPlans/FileHandler.rb +7 -2
  69. data/lib/sapphire/DSL/TestPlans/PathHandler.rb +2 -1
  70. data/lib/sapphire/DataAbstractions/NonQuery.rb +2 -1
  71. data/lib/sapphire/DataAbstractions/Query.rb +2 -1
  72. data/lib/sapphire/Extensions.rb +1 -0
  73. data/lib/sapphire/{DSL/Browser/Extensions → Extensions}/Class.rb +10 -0
  74. data/lib/sapphire/Extensions/Hash.rb +85 -0
  75. data/lib/sapphire/{DSL/Browser/Extensions → Extensions}/Numeric.rb +6 -6
  76. data/lib/sapphire/Extensions/Object.rb +9 -0
  77. data/lib/sapphire/{DSL/Browser/Extensions → Extensions}/String.rb +4 -0
  78. data/lib/sapphire/Extensions/Symbol.rb +105 -0
  79. data/lib/sapphire/Sessions/Parameter.rb +36 -0
  80. data/lib/sapphire/Sessions/Storage.rb +17 -0
  81. data/lib/sapphire/UI/Functions.rb +2 -1
  82. data/lib/sapphire/UI/ProcessBuilder.rb +174 -0
  83. data/lib/sapphire/Virtualization.rb +1 -1
  84. data/lib/sapphire/WebAbstractions/Browsers/Chrome.rb +22 -21
  85. data/lib/sapphire/WebAbstractions/Browsers/FireFox.rb +24 -22
  86. data/lib/sapphire/WebAbstractions/Browsers/InternetExplorer.rb +22 -21
  87. data/lib/sapphire/WebAbstractions/Controls/AlertBox.rb +21 -0
  88. data/lib/sapphire/WebAbstractions/Controls/Base/Control.rb +32 -14
  89. data/lib/sapphire/WebAbstractions/Controls/Base/Page.rb +74 -65
  90. data/lib/sapphire/WebAbstractions/Controls/CheckBox.rb +29 -28
  91. data/lib/sapphire/WebAbstractions/Controls/DropDown.rb +59 -26
  92. data/lib/sapphire/WebAbstractions/Controls/Image.rb +10 -0
  93. data/lib/sapphire/WebAbstractions/Controls/List.rb +45 -4
  94. data/lib/sapphire/WebAbstractions/Controls/RadioButton.rb +29 -18
  95. data/lib/sapphire/WebAbstractions/Controls/TextBox.rb +9 -8
  96. data/lib/sapphire/WebAbstractions/Controls/Title.rb +1 -1
  97. data/lib/sapphire/version.rb +1 -1
  98. metadata +55 -39
  99. data/lib/sapphire/Configuration/SapphireConfig.rb +0 -33
  100. data/lib/sapphire/DSL/Browser/Extensions/Hash.rb +0 -11
  101. data/lib/sapphire/DSL/Browser/Extensions/Symbol.rb +0 -37
  102. data/lib/sapphire/DSL/Browser/Not.rb +0 -39
  103. data/lib/sapphire/DSL/Browser/Nouns/AlertBox.rb +0 -15
  104. data/lib/sapphire/DSL/Browser/Verbs/Attempt.rb +0 -14
  105. data/lib/sapphire/DSL/Browser/Verbs/Contain.rb +0 -25
  106. data/lib/sapphire/DSL/Browser/Verbs/Count.rb +0 -24
  107. data/lib/sapphire/DSL/Browser/Verbs/Differ.rb +0 -19
  108. data/lib/sapphire/DSL/Browser/Verbs/Error.rb +0 -25
  109. data/lib/sapphire/DSL/Browser/Verbs/Hide.rb +0 -25
  110. data/lib/sapphire/DSL/Browser/Verbs/Show.rb +0 -25
  111. data/lib/sapphire/DSL/Browser/Verbs/Transition.rb +0 -25
  112. data/lib/sapphire/DSL/Data/Exist.rb +0 -26
  113. data/lib/sapphire/DSL/Data/Validate.rb +0 -25
  114. data/lib/sapphire/Strategies.rb +0 -2
  115. data/lib/sapphire/Strategies/ClassStrategy.rb +0 -13
  116. data/lib/sapphire/Strategies/ContainsEvaluation.rb +0 -16
  117. data/lib/sapphire/Strategies/ContainsNullModifier.rb +0 -34
  118. data/lib/sapphire/Strategies/DefaultStrategy.rb +0 -17
  119. data/lib/sapphire/Strategies/Evaluation.rb +0 -16
  120. data/lib/sapphire/Strategies/HashStrategy.rb +0 -72
  121. data/lib/sapphire/Strategies/NullModifier.rb +0 -34
  122. data/lib/sapphire/Strategies/Strategy.rb +0 -11
  123. data/lib/sapphire/Strategies/SymbolStrategy.rb +0 -59
  124. data/lib/sapphire/WebAbstractions/Controls/TableCell.rb +0 -10
@@ -19,7 +19,7 @@ class Then
19
19
 
20
20
  def add_and(pre, text, &block)
21
21
  if(self.value.instance_of? Pending)
22
- self.and << And.new(self, Pending.new(pre + text), &block)
22
+ self.and << And.new(self, Pending.new(text), &block)
23
23
  elsif(self.value.instance_of? Problematic)
24
24
  self.and << And.new(self, Problematic.new(pre + text), &block)
25
25
  else
@@ -31,7 +31,7 @@ class When
31
31
 
32
32
  def add_and(pre, text, &block)
33
33
  if(self.value.is_a? Pending)
34
- self.and << And.new(self, Pending.new(pre + text), &block)
34
+ self.and << And.new(self, Pending.new(text), &block)
35
35
  elsif(self.value.is_a? Problematic)
36
36
  self.and << And.new(self, Problematic.new(pre + text), &block)
37
37
  else
@@ -3,8 +3,13 @@ module Sapphire
3
3
  module TestPlans
4
4
  class FileHandler
5
5
  def Handle(item)
6
- x = AppConfig.Current.SpecsPath || ""
7
- require File.expand_path(x + item, __FILE__)
6
+ x = ""
7
+ x = AppConfig.Current.SpecsPath if AppConfig.Current
8
+ file = File.expand_path(item, __FILE__)
9
+ file = File.expand_path(x + item, __FILE__) if !File.exists?(file)
10
+ file = File.expand_path(File.dirname(__FILE__) + item) if !File.exists?(file)
11
+ raise "File Not Found: " + item if !File.exists?(file)
12
+ require file if File.exists?(file)
8
13
  Runner.instance.last_scenario.file_name = item
9
14
  Runner.instance.last_scenario.execute
10
15
  end
@@ -3,7 +3,8 @@ module Sapphire
3
3
  module TestPlans
4
4
  class PathHandler
5
5
  def Handle(item)
6
- x = AppConfig.Current.SpecsPath || ""
6
+ x = ""
7
+ x = AppConfig.Current.SpecsPath if AppConfig.Current
7
8
  Dir[x + item + '*.rb'].each do |file|
8
9
  require file
9
10
  Runner.instance.last_scenario.file_name = file
@@ -5,7 +5,8 @@ module Sapphire
5
5
  x = self.Database.new
6
6
 
7
7
  x.Open $config[x.Connection]
8
- y = AppConfig.Current.SqlPath || ""
8
+ y = ""
9
+ y = AppConfig.Current.SqlPath if AppConfig.Current
9
10
  path = File.expand_path(y + self.Script, __FILE__)
10
11
  file = File.open(path)
11
12
  contents = file.read
@@ -5,7 +5,8 @@ module Sapphire
5
5
  x = self.Database.new
6
6
 
7
7
  x.Open $config[x.Connection]
8
- y = AppConfig.Current.SqlPath || ""
8
+ y = ""
9
+ y = AppConfig.Current.SqlPath if AppConfig.Current
9
10
  path = File.expand_path(y + self.Script, __FILE__)
10
11
  file = File.open(path)
11
12
  contents = file.read
@@ -0,0 +1 @@
1
+ Dir[File.dirname(__FILE__) + '/Extensions/*.rb'].each {|file| require file }
@@ -36,4 +36,14 @@ class Class < Module
36
36
  x.Close
37
37
  end
38
38
 
39
+ def Show(item, comparator)
40
+ evaluation = $driver.ShouldNavigateTo(item, comparator)
41
+ Fix(evaluation, comparator)
42
+ end
43
+
44
+ def Fix(evaluation, comparator)
45
+ comparator = comparator.Create(evaluation)
46
+ comparator
47
+ end
48
+
39
49
  end
@@ -0,0 +1,85 @@
1
+ class Hash < Object
2
+
3
+ def Set(hash)
4
+ hash.keys.first.Set(hash)
5
+ end
6
+
7
+ def For(item)
8
+ {item => GetPageField(item).Text}
9
+ end
10
+
11
+ def Examine(item, comparator, &block)
12
+ key = item.keys.first
13
+ field = $page.Get(key)
14
+ value = item[key]
15
+
16
+ return FieldNotDefinedEvaluation.new(key, $page) if !$page.Contains key
17
+
18
+ begin
19
+ return FieldNotFoundEvaluation.new(key, $page) if field == nil
20
+
21
+ begin
22
+ wait = Selenium::WebDriver::Wait.new(:timeout => 5)
23
+ result = wait.until {
24
+ evaluation = block.call(field, value)
25
+ y = evaluation.Evaluate()
26
+ comparator = EqualsComparison.new(evaluation) if evaluation == nil
27
+ evaluation if comparator.Compare(y == true, true)
28
+ }
29
+
30
+ return Fix(result, comparator)
31
+ rescue
32
+ return FieldNotFoundEvaluation.new(key, $page)
33
+ end
34
+
35
+ rescue
36
+ return FieldNotFoundEvaluation.new(key, $page)
37
+ end
38
+ end
39
+
40
+ def Show(item, comparator)
41
+ Examine(item, comparator) do |field, value|
42
+ field.Equals(value, comparator)
43
+ end
44
+ end
45
+
46
+ def AltText(item, comparator)
47
+ Examine(item, comparator) do |field, value|
48
+ field.AltText(value, comparator)
49
+ end
50
+ end
51
+
52
+ def Source(item, comparator)
53
+ Examine(item, comparator) do |field, value|
54
+ field.Source(value, comparator)
55
+ end
56
+ end
57
+
58
+ def Contain(item, comparator)
59
+ Examine(item, comparator) do |field, value|
60
+ field.Contain(value)
61
+ end
62
+ end
63
+
64
+ def Count(item, comparator)
65
+ Examine(item, comparator) do |field, value|
66
+ field.Count(value)
67
+ end
68
+ end
69
+
70
+ def In(item, comparator)
71
+ Examine(item, comparator) do |field, value|
72
+ field.In(value, comparator)
73
+ end
74
+ end
75
+
76
+
77
+ def Validate(hash, comparator)
78
+ Fix(Evaluation.new(hash.keys.first.to_s, hash[hash.keys.first].to_s), comparator)
79
+ end
80
+
81
+ def Fix(evaluation, comparator)
82
+ comparator = comparator.Create(evaluation)
83
+ comparator
84
+ end
85
+ end
@@ -1,7 +1,7 @@
1
- class Numeric < Object
2
-
3
- def Wait
4
- sleep(self)
5
- end
6
-
1
+ class Numeric < Object
2
+
3
+ def Wait
4
+ sleep(self)
5
+ end
6
+
7
7
  end
@@ -0,0 +1,9 @@
1
+ class Object
2
+
3
+ def NotBeNil(item, comparator)
4
+ evaluation = Evaluation.new(item != nil, true)
5
+ comparator = EqualsComparison.new(evaluation) if comparator == nil
6
+ return comparator.Create(evaluation)
7
+ end
8
+
9
+ end
@@ -17,4 +17,8 @@ class String
17
17
  plus(item)
18
18
  end
19
19
 
20
+ def For(item)
21
+ item
22
+ end
23
+
20
24
  end
@@ -0,0 +1,105 @@
1
+ class Symbol
2
+
3
+ def Clear
4
+ ExecuteAgainstControl(self) do |control, arg|
5
+ control.Clear
6
+ end
7
+ end
8
+
9
+ def Check
10
+ ExecuteAgainstControl(self) do |control, arg|
11
+ control.Check true
12
+ end
13
+ end
14
+
15
+ def Click
16
+ ExecuteAgainstControl(self) do |control, arg|
17
+ control.Click
18
+ end
19
+ end
20
+
21
+ def Accept
22
+ ExecuteAgainstControl(self) do |control, arg|
23
+ control.Accept
24
+ end
25
+ end
26
+
27
+ def MouseOver
28
+ ExecuteAgainstControl(self) do |control, arg|
29
+ control.MouseOver
30
+ end
31
+ end
32
+
33
+ def Uncheck
34
+ ExecuteAgainstControl(self) do |control, arg|
35
+ control.Check false
36
+ end
37
+ end
38
+
39
+ def Set(hash)
40
+ ExecuteHashAgainstControl(hash) do |control, arg|
41
+ control.Set arg
42
+ end
43
+ end
44
+
45
+ def Selected(item, comparator)
46
+ Examine(item, comparator) do |field|
47
+ field.Selected
48
+ end
49
+ end
50
+
51
+ def Checked(item, comparator)
52
+ Examine(item, comparator) do |field|
53
+ field.Checked
54
+ end
55
+ end
56
+
57
+ def Show(item, comparator)
58
+ Examine(item, comparator) do |field|
59
+ field.Visible
60
+ end
61
+ end
62
+
63
+ def Examine(key, comparator, &block)
64
+ field = $page.Get(key)
65
+ element = field.Find(comparator)
66
+
67
+ return Evaluation.new(true, true) if element.nil? and comparator.Compare(true, false)
68
+ return FieldNotDefinedEvaluation.new(key, $page) if !$page.Contains key
69
+
70
+ begin
71
+ return FieldNotFoundEvaluation.new(key, $page) if field == nil
72
+
73
+ begin
74
+ wait = Selenium::WebDriver::Wait.new(:timeout => 5)
75
+ result = wait.until {
76
+ y = comparator.Compare(block.call(field), true)
77
+ y if y == true
78
+ }
79
+
80
+
81
+ return Fix(Evaluation.new(block.call(field), true), comparator)
82
+ rescue
83
+ return FieldNotFoundEvaluation.new(key, $page)
84
+ end
85
+
86
+ rescue
87
+ return FieldNotFoundEvaluation.new(key, $page)
88
+ end
89
+ end
90
+
91
+ def Fix(evaluation, comparator)
92
+ comparator = comparator.Create(evaluation)
93
+ comparator
94
+ end
95
+
96
+ def Validate(hash)
97
+ GetPageField(hash[hash.keys.first]).Equals(hash.keys.first.to_s)
98
+ end
99
+
100
+ def +(item)
101
+ return Parameter(self) + item if Sapphire::DSL::TestPlans::Parameters.instance.Contains(self)
102
+ raise "No Parameter defined for: " << self.to_s
103
+ end
104
+
105
+ end
@@ -0,0 +1,36 @@
1
+ module Sapphire
2
+ module Sessions
3
+
4
+ def Parameter(item)
5
+ Parameters.instance.Add(item) if item.is_a? Hash
6
+ return Parameters.instance.Get(item) if item.is_a? Symbol
7
+ end
8
+
9
+ class Parameters
10
+ def self.create
11
+ @@instance ||= Parameters.new
12
+ end
13
+
14
+ def self.instance
15
+ @@instance ||= self.create
16
+ end
17
+
18
+ def initialize()
19
+ $parameters ||= {}
20
+ end
21
+
22
+ def Add(item)
23
+ $parameters.merge! item
24
+ end
25
+
26
+ def Contains(item)
27
+ $parameters.has_key? item
28
+ end
29
+
30
+ def Get(item)
31
+ $parameters[item]
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,17 @@
1
+ module Sapphire
2
+ module Sessions
3
+ class Storage
4
+ def self.create
5
+ @@instance ||= Storage.new
6
+ end
7
+
8
+ def self.instance
9
+ @@instance ||= self.create
10
+ end
11
+
12
+ def initialize()
13
+ $storage ||= {}
14
+ end
15
+ end
16
+ end
17
+ end
@@ -3,7 +3,8 @@ module ChildProcess
3
3
  module Lib
4
4
 
5
5
  def self.create_proc(cmd, opts = {})
6
- cmd_ptr = FFI::MemoryPointer.from_string cmd
6
+ puts "1"
7
+ cmd_ptr = F.from_string cmd
7
8
 
8
9
  flags = 0
9
10
  inherit = !!opts[:inherit]
@@ -0,0 +1,174 @@
1
+ module ChildProcess
2
+ module Windows
3
+ class ProcessBuilder
4
+ attr_accessor :inherit, :detach, :duplex, :environment, :stdout, :stderr
5
+ attr_reader :stdin
6
+
7
+ def initialize(args)
8
+ @args = args
9
+
10
+ @inherit = false
11
+ @detach = false
12
+ @duplex = false
13
+ @environment = nil
14
+
15
+ @stdout = nil
16
+ @stderr = nil
17
+ @stdin = nil
18
+
19
+ @flags = 0
20
+ @cmd_ptr = nil
21
+ @env_ptr = nil
22
+ end
23
+
24
+ def start
25
+ create_command_pointer
26
+ create_environment_pointer
27
+
28
+ setup_detach
29
+ setup_io
30
+
31
+ pid = create_process
32
+ close_handles
33
+
34
+ pid
35
+ end
36
+
37
+ private
38
+
39
+ def create_command_pointer
40
+ string = @args.map { |arg| quote_if_necessary(arg.to_s) }.join ' '
41
+ @cmd_ptr = FFI::MemoryPointer.from_string string
42
+ end
43
+
44
+ def create_environment_pointer
45
+ return unless @environment.kind_of?(Hash) && @environment.any?
46
+
47
+ # inherited env
48
+ strings = ENV.map { |k,v| "#{k}=#{v}\0" }
49
+
50
+ # extras
51
+ @environment.each do |key, value|
52
+ if key.include?("=")
53
+ raise InvalidEnvironmentVariableName, key
54
+ end
55
+
56
+ strings << "#{key}=#{value}\0"
57
+ end
58
+
59
+ strings << "\0" # terminate the env block
60
+ env_str = strings.join
61
+
62
+ @env_ptr = FFI::MemoryPointer.new(:long, env_str.bytesize)
63
+ @env_ptr.put_bytes 0, env_str, 0, env_str.bytesize
64
+ end
65
+
66
+ def create_process
67
+ startup_info[:lpDesktop] = FFI::MemoryPointer.from_string("test") if $isVirtual
68
+
69
+ ok = Lib.create_process(
70
+ nil, # application name
71
+ @cmd_ptr, # command line
72
+ nil, # process attributes
73
+ nil, # thread attributes
74
+ @inherit, # inherit handles
75
+ @flags, # creation flags
76
+ @env_ptr, # environment
77
+ nil, # current directory
78
+ startup_info, # startup info
79
+ process_info # process info
80
+ )
81
+
82
+ ok or raise LaunchError, Lib.last_error_message
83
+
84
+ process_info[:dwProcessId]
85
+ end
86
+
87
+ def startup_info
88
+ @startup_info ||= StartupInfo.new
89
+ end
90
+
91
+ def process_info
92
+ @process_info ||= ProcessInfo.new
93
+ end
94
+
95
+ def setup_detach
96
+ @flags |= DETACHED_PROCESS if @detach
97
+ end
98
+
99
+ def setup_io
100
+ if @stdout || @stderr
101
+ startup_info[:dwFlags] ||= 0
102
+ startup_info[:dwFlags] |= STARTF_USESTDHANDLES
103
+
104
+ @inherit = true
105
+
106
+ if @stdout
107
+ startup_info[:hStdOutput] = std_stream_handle_for(@stdout)
108
+ end
109
+
110
+ if @stderr
111
+ startup_info[:hStdError] = std_stream_handle_for(@stderr)
112
+ end
113
+ end
114
+
115
+ setup_stdin if @duplex
116
+ end
117
+
118
+ def setup_stdin
119
+ read_pipe_ptr = FFI::MemoryPointer.new(:pointer)
120
+ write_pipe_ptr = FFI::MemoryPointer.new(:pointer)
121
+ sa = SecurityAttributes.new(:inherit => true)
122
+
123
+ ok = Lib.create_pipe(read_pipe_ptr, write_pipe_ptr, sa, 0)
124
+ Lib.check_error ok
125
+
126
+ @read_pipe = read_pipe_ptr.read_pointer
127
+ @write_pipe = write_pipe_ptr.read_pointer
128
+
129
+ @inherit = true
130
+ Lib.set_handle_inheritance @read_pipe, true
131
+ Lib.set_handle_inheritance @write_pipe, false
132
+
133
+ startup_info[:hStdInput] = @read_pipe
134
+ end
135
+
136
+ def std_stream_handle_for(io)
137
+ handle = Lib.handle_for(io)
138
+
139
+ begin
140
+ Lib.set_handle_inheritance handle, true
141
+ rescue ChildProcess::Error
142
+ # If the IO was set to close on exec previously, this call will fail.
143
+ # That's probably OK, since the user explicitly asked for it to be
144
+ # closed (at least I have yet to find other cases where this will
145
+ # happen...)
146
+ end
147
+
148
+ handle
149
+ end
150
+
151
+ def close_handles
152
+ Lib.close_handle process_info[:hProcess]
153
+ Lib.close_handle process_info[:hThread]
154
+
155
+ if @duplex
156
+ @stdin = Lib.io_for(Lib.duplicate_handle(@write_pipe), File::WRONLY)
157
+ Lib.close_handle @read_pipe
158
+ Lib.close_handle @write_pipe
159
+ end
160
+ end
161
+
162
+ def quote_if_necessary(str)
163
+ quote = str.start_with?('"') ? "'" : '"'
164
+
165
+ case str
166
+ when /[\s\\'"]/
167
+ [quote, str, quote].join
168
+ else
169
+ str
170
+ end
171
+ end
172
+ end # ProcessBuilder
173
+ end # Windows
174
+ end # ChildProcess