Module: Roby::Test::RobyAppHelpers

Defined in:
lib/roby/test/roby_app_helpers.rb

Overview

Helpers to test a full Roby app started as a subprocess

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#appObject (readonly)

Returns the value of attribute app.



5
6
7
# File 'lib/roby/test/roby_app_helpers.rb', line 5

def app
  @app
end

#app_dirObject (readonly)

Returns the value of attribute app_dir.



5
6
7
# File 'lib/roby/test/roby_app_helpers.rb', line 5

def app_dir
  @app_dir
end

Instance Method Details

#app_helpers_source_dir(source_dir) ⇒ Object



188
189
190
# File 'lib/roby/test/roby_app_helpers.rb', line 188

def app_helpers_source_dir(source_dir)
    @helpers_source_dir = source_dir
end

#assert_roby_app_can_connect_to_log_server(timeout: 2, port: app.log_server_port) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/roby/test/roby_app_helpers.rb', line 170

def assert_roby_app_can_connect_to_log_server(timeout: 2, port: app.log_server_port)
    client = roby_app_with_polling(timeout: timeout, message: "connecting to the log server on port #{port}") do
        begin DRoby::Logfile::Client.new('localhost', port)
        rescue Interface::ConnectionError
        end
    end
    while !client.init_done?
        client.read_and_process_pending
    end
rescue Exception
    # Give time to the log server to report errors before we
    # terminate it with SIGINT
    sleep 0.1
    raise
ensure
    client.close if client
end

#assert_roby_app_has_job(interface, action_name, timeout: 2, state: Interface::JOB_STARTED) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/roby/test/roby_app_helpers.rb', line 86

def assert_roby_app_has_job(interface, action_name, timeout: 2, state: Interface::JOB_STARTED)
    start_time = Time.now
    while (Time.now - start_time) < timeout
        jobs = interface.find_all_jobs_by_action_name(action_name)
        if state
            jobs = jobs.find_all { |j| j.state == state }
        end
        if j = jobs.first
            return j
        end
        sleep 0.01
    end
    flunk "timed out while waiting for action #{action_name} on #{interface}"
end

#assert_roby_app_is_running(pid, timeout: 10, host: 'localhost', port: Roby::Interface::DEFAULT_PORT) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/roby/test/roby_app_helpers.rb', line 62

def assert_roby_app_is_running(pid, timeout: 10, host: 'localhost', port: Roby::Interface::DEFAULT_PORT)
    start_time = Time.now
    while (Time.now - start_time) < timeout
        if ::Process.waitpid(pid, Process::WNOHANG)
            flunk "Roby app unexpectedly quit"
        end

        begin
            return Roby::Interface.connect_with_tcp_to(host, port)
        rescue Roby::Interface::ConnectionError
        end
        sleep 0.01
    end
    flunk "could not get a connection within #{timeout} seconds"
end

#assert_roby_app_quits(pid, interface: nil) ⇒ Object



78
79
80
81
82
83
84
# File 'lib/roby/test/roby_app_helpers.rb', line 78

def assert_roby_app_quits(pid, interface: nil)
    interface ||= assert_roby_app_is_running(pid)
    interface.quit
    _, status = Process.waitpid2(pid)
    assert status.exited?
    assert_equal 0, status.exitstatus
end

#copy_into_app(template, target = template) ⇒ Object



192
193
194
195
196
# File 'lib/roby/test/roby_app_helpers.rb', line 192

def copy_into_app(template, target = template)
    FileUtils.mkdir_p File.join(app_dir, File.dirname(target))
    FileUtils.cp File.join(@helpers_source_dir, template),
        File.join(app_dir, target)
end

#gen_app(app_dir = self.app_dir) ⇒ Object



36
37
38
39
# File 'lib/roby/test/roby_app_helpers.rb', line 36

def gen_app(app_dir = self.app_dir)
    require 'roby/cli/gen_main'
    Dir.chdir(app_dir) { CLI::GenMain.start(['app', '--quiet']) }
end

#roby_app_call_interface(host: 'localhost', port: Interface::DEFAULT_PORT) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/roby/test/roby_app_helpers.rb', line 145

def roby_app_call_interface(host: 'localhost', port: Interface::DEFAULT_PORT)
    client_thread = Thread.new do
        begin
            interface = Interface.connect_with_tcp_to(host, port)
            if block_given?
                result = yield(interface)
            end
        rescue Exception => e
            error = e
        end
        [interface, result, error]
    end
    while client_thread.alive?
        app.shell_interface.process_pending_requests
    end
    begin 
        interface, result, error = client_thread.value
    rescue Exception => e
        raise e, e.message, e.backtrace + caller
    end
    interface.close
    raise error if error
    result
end

#roby_app_create_logfileObject



137
138
139
140
141
142
143
# File 'lib/roby/test/roby_app_helpers.rb', line 137

def roby_app_create_logfile
    require 'roby/droby/logfile/writer'
    logfile_dir = make_tmpdir
    logfile_path = File.join(logfile_dir, 'logfile')
    writer = DRoby::Logfile::Writer.open(logfile_path)
    return logfile_path, writer
end

#roby_app_fixture_pathObject



108
109
110
111
112
# File 'lib/roby/test/roby_app_helpers.rb', line 108

def roby_app_fixture_path
    File.expand_path(
        File.join("..", "..", "..", 'test', "app", "fixtures"),
        __dir__)
end

#roby_app_quit(interface, timeout: 2) ⇒ Object



101
102
103
104
105
106
# File 'lib/roby/test/roby_app_helpers.rb', line 101

def roby_app_quit(interface, timeout: 2)
    _, status = Process.waitpid2(pid)
    if !status.success?
        raise "roby app with PID #{pid} exited with nonzero status"
    end
end

#roby_app_setup_single_script(*scripts) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/roby/test/roby_app_helpers.rb', line 114

def roby_app_setup_single_script(*scripts)
    dir = make_tmpdir
    FileUtils.mkdir_p File.join(dir, 'config', 'robots')
    FileUtils.mkdir_p File.join(dir, 'scripts')
    FileUtils.touch File.join(dir, 'config', 'app.yml')
    FileUtils.touch File.join(dir, 'config', 'robots', 'default.rb')
    scripts.each do |p|
        p = File.expand_path(p, roby_app_fixture_path)
        FileUtils.cp p, File.join(dir, 'scripts')
    end
    return dir
end

#roby_app_spawn(*args, silent: false, **options) ⇒ Object



127
128
129
130
131
132
133
134
135
# File 'lib/roby/test/roby_app_helpers.rb', line 127

def roby_app_spawn(*args, silent: false, **options)
    if silent
        options[:out] ||= '/dev/null'
        options[:err] ||= '/dev/null'
    end
    pid = spawn(roby_bin, *args, chdir: app_dir, **options)
    @spawned_pids << pid
    return pid
end

#roby_app_with_polling(timeout: 2, period: 0.01, message: nil) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/roby/test/roby_app_helpers.rb', line 47

def roby_app_with_polling(timeout: 2, period: 0.01, message: nil)
    start_time = Time.now
    while (Time.now - start_time) < timeout
        if result = yield
            return result
        end
        sleep 0.01
    end
    if message
        flunk "#{message} did not happen within #{timeout} seconds"
    else
        flunk "failed to reach expected result within #{timeout} seconds"
    end
end

#roby_binObject



41
42
43
44
45
# File 'lib/roby/test/roby_app_helpers.rb', line 41

def roby_bin
    File.expand_path(
        File.join("..", "..", "..", 'bin', 'roby'),
        __dir__)
end

#setupObject



7
8
9
10
11
12
13
14
15
16
# File 'lib/roby/test/roby_app_helpers.rb', line 7

def setup
    @spawned_pids = Array.new
    super
    @app = Roby::Application.new
    app.public_logs = false
    app.plugins_enabled = false
    @app_dir = make_tmpdir
    app.app_dir = app_dir
    register_plan(@app.plan)
end

#teardownObject



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/roby/test/roby_app_helpers.rb', line 18

def teardown
    super
    app.stop_log_server
    app.stop_shell_interface
    app.cleanup
    pending_children = @spawned_pids.find_all do |pid|
        begin
            Process.kill 'INT', pid
            true
        rescue Errno::ESRCH
        end
    end

    pending_children.each do |pid|
        Process.waitpid2(pid)
    end
end