File: integration_helper.rb

package info (click to toggle)
ruby-sinatra 1.4.5-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 1,524 kB
  • ctags: 483
  • sloc: ruby: 9,521; makefile: 5
file content (236 lines) | stat: -rw-r--r-- 5,905 bytes parent folder | download | duplicates (2)
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
require 'sinatra/base'
require 'rbconfig'
require 'open-uri'
require 'net/http'
require 'timeout'

module IntegrationHelper
  class BaseServer
    extend Enumerable
    attr_accessor :server, :port, :pipe
    alias name server

    def self.all
      @all ||= []
    end

    def self.each(&block)
      all.each(&block)
    end

    def self.run(server, port)
      new(server, port).run
    end

    def app_file
      File.expand_path('../integration/app.rb', __FILE__)
    end

    def environment
      "development"
    end

    def initialize(server, port)
      @installed, @pipe, @server, @port = nil, nil, server, port
      Server.all << self
    end

    def run
      return unless installed?
      kill
      @log     = ""
      @pipe    = IO.popen(command)
      @started = Time.now
      warn "#{server} up and running on port #{port}" if ping
      at_exit { kill }
    end

    def ping(timeout = 30)
      loop do
        return if alive?
        if Time.now - @started > timeout
          $stderr.puts command, log
          fail "timeout"
        else
          sleep 0.1
        end
      end
    end

    def alive?
      3.times { get('/ping') }
      true
    rescue Errno::ECONNREFUSED, Errno::ECONNRESET, EOFError, SystemCallError, OpenURI::HTTPError, Timeout::Error
      false
    end

    def get_stream(url = "/stream", &block)
      Net::HTTP.start '127.0.0.1', port do |http|
        request = Net::HTTP::Get.new url
        http.request request do |response|
          response.read_body(&block)
        end
      end
    end

    def get_response(url)
      Net::HTTP.start '127.0.0.1', port do |http|
        request = Net::HTTP::Get.new url
        http.request request do |response|
          response
        end
      end
    end

    def get(url)
      Timeout.timeout(1) { open("http://127.0.0.1:#{port}#{url}").read }
    end

    def log
      @log ||= ""
      loop { @log <<  @pipe.read_nonblock(1) }
    rescue Exception
      @log
    end

    def installed?
      return @installed unless @installed.nil?
      s = server == 'HTTP' ? 'net/http/server' : server
      require s
      @installed = true
    rescue LoadError
      warn "#{server} is not installed, skipping integration tests"
      @installed = false
    end

    def command
      @command ||= begin
        cmd = ["RACK_ENV=#{environment}", "exec"]
        if RbConfig.respond_to? :ruby
          cmd << RbConfig.ruby.inspect
        else
          file, dir = RbConfig::CONFIG.values_at('ruby_install_name', 'bindir')
          cmd << File.expand_path(file, dir).inspect
        end
        cmd << "-w" unless thin? || net_http_server?
        cmd << "-I" << File.expand_path('../../lib', __FILE__).inspect
        cmd << app_file.inspect << '-s' << server << '-o' << '127.0.0.1' << '-p' << port
        cmd << "-e" << environment.to_s << '2>&1'
        cmd.join " "
      end
    end

    def kill
      return unless pipe
      Process.kill("KILL", pipe.pid)
    rescue NotImplementedError
      system "kill -9 #{pipe.pid}"
    rescue Errno::ESRCH
    end

    def webrick?
      name.to_s == "webrick"
    end

    def thin?
      name.to_s == "thin"
    end

    def puma?
      name.to_s == "puma"
    end

    def trinidad?
      name.to_s == "trinidad"
    end

    def net_http_server?
      name.to_s == 'HTTP'
    end

    def warnings
      log.scan(%r[(?:\(eval|lib/sinatra).*warning:.*$])
    end

    def run_test(target, &block)
      retries ||= 3
      target.server = self
      run unless alive?
      target.instance_eval(&block)
    rescue Exception => error
      retries -= 1
      kill
      retries < 0 ? retry : raise(error)
    end
  end

  if RUBY_ENGINE == "jruby"
    class JRubyServer < BaseServer
      def start_vm
        require 'java'
        # Create a new container, set load paths and env
        # SINGLETHREAD means create a new runtime
        vm = org.jruby.embed.ScriptingContainer.new(org.jruby.embed.LocalContextScope::SINGLETHREAD)
        vm.load_paths = [File.expand_path('../../lib', __FILE__)]
        vm.environment = ENV.merge('RACK_ENV' => environment.to_s)

        # This ensures processing of RUBYOPT which activates Bundler
        vm.provider.ruby_instance_config.process_arguments []
        vm.argv = ['-s', server.to_s, '-o', '127.0.0.1', '-p', port.to_s, '-e', environment.to_s]

        # Set stdout/stderr so we can retrieve log
        @pipe = java.io.ByteArrayOutputStream.new
        vm.output = java.io.PrintStream.new(@pipe)
        vm.error  = java.io.PrintStream.new(@pipe)

        Thread.new do
          # Hack to ensure that Kernel#caller has the same info as
          # when run from command-line, for Sinatra::Application.app_file.
          # Also, line numbers are zero-based in JRuby's parser
          vm.provider.runtime.current_context.set_file_and_line(app_file, 0)
          # Run the app
          vm.run_scriptlet org.jruby.embed.PathType::ABSOLUTE, app_file
          # terminate launches at_exit hooks which start server
          vm.terminate
        end
      end

      def run
        return unless installed?
        kill
        @thread  = start_vm
        @started = Time.now
        warn "#{server} up and running on port #{port}" if ping
        at_exit { kill }
      end

      def log
        String.from_java_bytes @pipe.to_byte_array
      end

      def kill
        @thread.kill if @thread
        @thread = nil
      end
    end
    Server = JRubyServer
  else
    Server = BaseServer
  end

  def it(message, &block)
    Server.each do |server|
      next unless server.installed?
      super("with #{server.name}: #{message}") { server.run_test(self, &block) }
    end
  end

  def self.extend_object(obj)
    super

    base_port = 5000 + Process.pid % 100
    Sinatra::Base.server.each_with_index do |server, index|
      Server.run(server, base_port+index)
    end
  end
end