366 lines
10 KiB
Ruby
366 lines
10 KiB
Ruby
|
require File.dirname(__FILE__) + '/../abstract_unit'
|
||
|
require 'logger'
|
||
|
require 'test/unit'
|
||
|
require 'cgi'
|
||
|
require 'stringio'
|
||
|
|
||
|
DEBUG=false
|
||
|
|
||
|
def test_logger
|
||
|
if DEBUG then ActionController::Base.logger = Logger.new(STDERR)
|
||
|
else ActionController::Base.logger = Logger.new(StringIO.new)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# Provide a static version of the Controllers module instead of the auto-loading version.
|
||
|
# We don't want these tests to fail when dependencies are to blame.
|
||
|
module Controllers
|
||
|
class EmptyController < ActionController::Base
|
||
|
end
|
||
|
class ApplicationController < ActionController::Base
|
||
|
end
|
||
|
|
||
|
class MockController < ActionController::Base
|
||
|
def initialize
|
||
|
super
|
||
|
@session = {:uploads => {}}
|
||
|
@params = {}
|
||
|
end
|
||
|
end
|
||
|
|
||
|
class SingleUploadController < ActionController::Base
|
||
|
upload_status_for :one
|
||
|
|
||
|
def one; end
|
||
|
end
|
||
|
|
||
|
class DoubleUploadController < ActionController::Base
|
||
|
upload_status_for :one, :two
|
||
|
|
||
|
def one; end
|
||
|
def two; end
|
||
|
end
|
||
|
|
||
|
class DoubleStatusUploadController < ActionController::Base
|
||
|
upload_status_for :one, :two, :status => :custom_status
|
||
|
|
||
|
def one; end
|
||
|
def two; end
|
||
|
end
|
||
|
|
||
|
class DoubleSeperateController < ActionController::Base
|
||
|
upload_status_for :one
|
||
|
upload_status_for :two
|
||
|
|
||
|
def one; end
|
||
|
def two; end
|
||
|
end
|
||
|
|
||
|
class UploadController < ActionController::Base
|
||
|
upload_status_for :norendered, :rendered, :redirected, :finish_param_dict, :finish_param_string, :finish_param_number
|
||
|
|
||
|
def norendered
|
||
|
end
|
||
|
|
||
|
def rendered
|
||
|
render_text("rendered")
|
||
|
end
|
||
|
|
||
|
def redirected
|
||
|
redirect_to "/redirected/"
|
||
|
end
|
||
|
|
||
|
def finish_param_dict
|
||
|
finish_upload_status "{a: 'b'}"
|
||
|
end
|
||
|
|
||
|
def finish_param_string
|
||
|
finish_upload_status "'a string'"
|
||
|
end
|
||
|
|
||
|
def finish_param_number
|
||
|
finish_upload_status 123
|
||
|
end
|
||
|
|
||
|
def finish_param_number_redirect
|
||
|
redirect_to "/redirected/"
|
||
|
finish_upload_status 123
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
class MockIO < StringIO
|
||
|
def initialize(data='', &block)
|
||
|
test_logger.debug("MockIO inializing data: #{data[0..20]}")
|
||
|
|
||
|
@block = block
|
||
|
super(data)
|
||
|
end
|
||
|
|
||
|
def write(data)
|
||
|
test_logger.debug("MockIO write #{data.size} data: #{data[0..20]}")
|
||
|
super
|
||
|
end
|
||
|
def read(size)
|
||
|
test_logger.debug("MockIO getting data from super")
|
||
|
data = super
|
||
|
|
||
|
test_logger.debug("Calling read callback")
|
||
|
@block.call
|
||
|
|
||
|
test_logger.debug("Returning data: #{data.size}")
|
||
|
data
|
||
|
end
|
||
|
end
|
||
|
|
||
|
class MockCGI < CGI
|
||
|
BOUNDARY = '----------0xKhTmLbOuNdArY'
|
||
|
FILENAME = 'dummy.nul'
|
||
|
|
||
|
attr_reader :upload_id, :session_options, :session_id
|
||
|
|
||
|
def initialize(size=1000, url='/test', &block)
|
||
|
@url = url
|
||
|
@env = {}
|
||
|
@sio = MockIO.new('') { block.call(self) if block_given? }
|
||
|
|
||
|
@upload_id = '1'
|
||
|
|
||
|
add_param('param1', 'value1')
|
||
|
add_data(size)
|
||
|
add_param('param1', 'value2')
|
||
|
add_end_boundary
|
||
|
init_env
|
||
|
@sio.rewind
|
||
|
super()
|
||
|
end
|
||
|
|
||
|
#def stdinput_without_progress
|
||
|
# @sio
|
||
|
#end
|
||
|
|
||
|
def stdinput
|
||
|
@sio
|
||
|
end
|
||
|
|
||
|
def env_table
|
||
|
@env
|
||
|
end
|
||
|
|
||
|
private
|
||
|
def init_env
|
||
|
@env['HTTP_HOST'] = 'localhost'
|
||
|
@env['SERVER_PORT'] = '80'
|
||
|
@env['REQUEST_METHOD'] = "POST"
|
||
|
@env['QUERY_STRING'] = @url.split('?')[1] || "upload_id=#{upload_id}&query_param=query_value"
|
||
|
@env['REQUEST_URI'] = @url
|
||
|
@env['SCRIPT_NAME'] = @url.split('?').first.split('/').last
|
||
|
@env['PATH_INFO'] = @url.split('?').first
|
||
|
@env['CONTENT_TYPE'] = "multipart/form-data; boundary=#{BOUNDARY}"
|
||
|
@env['CONTENT_LENGTH'] = @sio.tell - EOL.size
|
||
|
|
||
|
@session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.inject({}) { |options, pair|
|
||
|
options[pair.first.to_s] = pair.last; options
|
||
|
}
|
||
|
session = CGI::Session.new({}, @session_options.merge({'new_session' => true}))
|
||
|
@session_id = session.session_id
|
||
|
@env['COOKIE'] = "_session_id=#{session.session_id}"
|
||
|
session.close
|
||
|
end
|
||
|
|
||
|
def add_param(name, value)
|
||
|
add_boundary
|
||
|
@sio << "Content-Disposition: form-data; name=\"#{name}\"" << EOL << EOL
|
||
|
@sio << value.to_s << EOL
|
||
|
end
|
||
|
|
||
|
def add_data(size)
|
||
|
add_boundary
|
||
|
@sio << "Content-Disposition: form-data; name=\"file\"; filename=\"#{FILENAME}\"" << EOL
|
||
|
@sio << "Content-Type: application/octet-stream" << EOL << EOL
|
||
|
@sio << "." * size
|
||
|
@sio << EOL
|
||
|
end
|
||
|
|
||
|
def add_boundary
|
||
|
@sio << "--" << BOUNDARY << EOL
|
||
|
end
|
||
|
|
||
|
def add_end_boundary
|
||
|
@sio << "--" << BOUNDARY << "--" << EOL
|
||
|
end
|
||
|
end
|
||
|
|
||
|
class MultipartProgressTest < Test::Unit::TestCase
|
||
|
|
||
|
def test_domain_language_single
|
||
|
c = Controllers::SingleUploadController.new
|
||
|
assert_respond_to(c, :one)
|
||
|
assert_respond_to(c, :upload_status)
|
||
|
assert_respond_to(c, :finish_upload_status)
|
||
|
end
|
||
|
|
||
|
def test_domain_language_double
|
||
|
c = Controllers::DoubleUploadController.new
|
||
|
assert_respond_to(c, :one)
|
||
|
assert_respond_to(c, :two)
|
||
|
assert_respond_to(c, :upload_status)
|
||
|
assert_respond_to(c, :finish_upload_status)
|
||
|
end
|
||
|
|
||
|
def test_domain_language_double_status
|
||
|
c = Controllers::DoubleStatusUploadController.new
|
||
|
assert_respond_to(c, :one)
|
||
|
assert_respond_to(c, :two)
|
||
|
assert_respond_to(c, :custom_status)
|
||
|
assert_respond_to(c, :finish_upload_status)
|
||
|
end
|
||
|
|
||
|
def test_domain_language_double_seperate
|
||
|
c = Controllers::DoubleSeperateController.new
|
||
|
assert_respond_to(c, :one)
|
||
|
assert_respond_to(c, :two)
|
||
|
assert_respond_to(c, :upload_status)
|
||
|
assert_respond_to(c, :finish_upload_status)
|
||
|
end
|
||
|
|
||
|
def test_finish_status_norendered
|
||
|
# Fails to render the upload finish script because there is no view associated with this action
|
||
|
test_logger.debug('test_finish_status_norendered')
|
||
|
|
||
|
res = process(:action => 'norendered', :upload_id => 1)
|
||
|
assert_match(/ActionView::ActionViewError/s, res.body)
|
||
|
|
||
|
res = process(:action => :upload_status, :upload_id => 1)
|
||
|
assert_match(/Upload finished/s, res.body)
|
||
|
|
||
|
res = process(:action => :norendered)
|
||
|
assert_match(/ActionView::ActionViewError/s, res.body)
|
||
|
end
|
||
|
|
||
|
def test_finish_status_rendered
|
||
|
test_logger.debug('test_finish_status_rendered')
|
||
|
|
||
|
res = process(:action => :rendered, :upload_id => 1)
|
||
|
assert_match(/stop\(\)/s, res.body)
|
||
|
assert_no_match(/rendered/s, res.body)
|
||
|
|
||
|
res = process(:action => :upload_status, :upload_id => 1)
|
||
|
assert_match(/Upload finished/s, res.body)
|
||
|
|
||
|
res = process(:action => :rendered)
|
||
|
assert_no_match(/stop\(\)/s, res.body)
|
||
|
assert_match(/rendered/, res.body)
|
||
|
end
|
||
|
|
||
|
def test_finish_status_redirected
|
||
|
test_logger.debug('test_finish_status_redirected')
|
||
|
|
||
|
res = process(:action => :redirected, :upload_id => 1)
|
||
|
assert_match(/location\.replace/s, res.body)
|
||
|
|
||
|
res = process(:action => :redirected)
|
||
|
assert_no_match(/location\.replace/s, res.body)
|
||
|
assert_match(/\/redirected\//s, res.headers['location'])
|
||
|
assert_match(/302 .*$/, res.headers['Status'])
|
||
|
|
||
|
res = process(:action => :upload_status, :upload_id => 1)
|
||
|
assert_match(/Upload finished/s, res.body)
|
||
|
end
|
||
|
|
||
|
def test_finish_status_finish_param
|
||
|
test_logger.debug('test_finish_status_param')
|
||
|
|
||
|
res = process(:action => :finish_param_string, :upload_id => 1)
|
||
|
assert_match(/stop\('a string'\)/s, res.body)
|
||
|
assert_no_redirect res
|
||
|
|
||
|
res = process(:action => :finish_param_dict, :upload_id => 1)
|
||
|
assert_match(/stop\(\{a: 'b'\}\)/s, res.body)
|
||
|
assert_no_redirect res
|
||
|
|
||
|
res = process(:action => :finish_param_number, :upload_id => 1)
|
||
|
assert_match(/stop\(123\)/s, res.body)
|
||
|
assert_no_redirect res
|
||
|
|
||
|
res = process(:action => :finish_param_number_redirect, :upload_id => 1)
|
||
|
test_logger.debug('test_finish_status_param: ' + res.body)
|
||
|
assert_match(/stop\(123\)/s, res.body)
|
||
|
assert_match(/replace\('\http:\/\/localhost\/redirected\/'\).*?/s, res.body)
|
||
|
assert_no_redirect res
|
||
|
end
|
||
|
|
||
|
def test_basic_setup
|
||
|
test_logger.debug('test_basic_setup')
|
||
|
|
||
|
cgi, request, response = new_request(100000)
|
||
|
assert_not_nil(request.session)
|
||
|
assert_not_nil(request.session[:uploads], "uploads collection not set")
|
||
|
assert_not_nil(request.session[:uploads][cgi.upload_id], "upload id not set")
|
||
|
progress = request.session[:uploads][cgi.upload_id]
|
||
|
assert_equal(true, progress.finished?)
|
||
|
end
|
||
|
|
||
|
def test_params
|
||
|
test_logger.debug('test_params')
|
||
|
|
||
|
cgi, request, response = new_request(1000)
|
||
|
assert(!request.params.empty?)
|
||
|
assert(!request.params['param1'].empty?)
|
||
|
end
|
||
|
|
||
|
def test_share_session
|
||
|
cgi, request, response = new_request(100000) do |cgi, req|
|
||
|
if cgi.stdinput.tell > 50000
|
||
|
# force a save
|
||
|
cgi.stdinput.save_progress rescue flunk('Something else is wrong, our wrapper isnt setup, is ActionController::Base.logger set?')
|
||
|
|
||
|
other_session = CGI::Session.new(cgi, cgi.session_options.merge({'session_id' => cgi.session_id}))
|
||
|
assert_not_nil(other_session[:uploads])
|
||
|
assert_not_nil(other_session[:uploads][cgi.upload_id])
|
||
|
assert_in_delta(cgi.stdinput.session[:uploads][cgi.upload_id].bitrate, other_session[:uploads][cgi.upload_id].bitrate, 1000.0, "Seperate session does not share data from original session")
|
||
|
|
||
|
other_session.close
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def test_upload_ids
|
||
|
c = Controllers::MockController.new
|
||
|
(1..222).each do |id|
|
||
|
c.params = {}
|
||
|
|
||
|
assert_equal((id-1).to_s, c.last_upload_id, "last_upload_id is out of sync")
|
||
|
assert_equal(id.to_s, c.next_upload_id, "next_upload_id is out of sync")
|
||
|
assert_equal(id.to_s, c.current_upload_id, "current_upload_id is out of sync")
|
||
|
|
||
|
c.params = {:upload_id => (id-1).to_s}
|
||
|
assert_equal((id-1).to_s, c.current_upload_id, "current_upload_id is out of sync")
|
||
|
|
||
|
c.session[:uploads][id] = {}
|
||
|
end
|
||
|
end
|
||
|
|
||
|
private
|
||
|
def new_request(size=1000, url='/test', &block)
|
||
|
test_logger.debug('Creating MockCGI')
|
||
|
cgi = MockCGI.new(size, url) do |cgi|
|
||
|
block.call(cgi) if block_given?
|
||
|
end
|
||
|
|
||
|
assert(cgi.private_methods.include?("read_multipart_with_progress"))
|
||
|
return [cgi, ActionController::CgiRequest.new(cgi), ActionController::CgiResponse.new(cgi)]
|
||
|
end
|
||
|
|
||
|
def process(options = {})
|
||
|
Controllers::UploadController.process(*(new_request(1000, '/upload?' + options.map {|k,v| "#{k}=#{v}"}.join('&'))[1..2]))
|
||
|
end
|
||
|
|
||
|
def assert_no_redirect(res)
|
||
|
assert_nil(res.redirected_to)
|
||
|
assert_nil(res.headers['location'])
|
||
|
assert_match(/200 .*$/, res.headers['Status'])
|
||
|
end
|
||
|
|
||
|
end
|