I'm using rb-notify to monitor a directory within my Rspec tests for a Rails project. If the file isn't found then the inotify continues in an endless loop. Is there a way to tell it to give up after 10 seconds.
#notifier = INotify::Notifier.new
#notifier.watch(DownloadHelpers::PATH.to_s, :create) do |file|
puts "Check: #{file.name}"
if file.name.eql?(File.basename(#filename.to_s))
#pdf = PdfReader.new(#filename)
# Check PDF
expect(#pdf.page_count).to eq(1)
expect(#pdf.pages[1]).to have_text("CONTRACTORS")
#notifier.stop # Stops the infinite loop watch
end
end
visit "/#/contractors_report"
within "#heading" do
expect(page).to have_text("Contractors")
end
click_on("pdf-btn")
#notifier.run # Make sure we finish our thread
See the advanced usage section on inotify's readme on GitHub:
# Wait 10 seconds for an event then give up
if IO.select([notifier.to_io], [], [], 10)
notifier.process
end
Related
What is the best way to test something that requires background jobs with Cucumber? I need to run DelayedJob and Sneakers workers in background while tests are running.
You can run any application in the background:
#pid = Process.spawn "C:/Apps/whatever.exe"
Process.detach(#pid)
And even kill it after tests are done:
Process.kill('KILL', #pid) unless #pid.nil?
You can create your own step definition in features/step_definitions/whatever_steps.rb (hopefully with a better name)
When /^I wait for background jobs to complete$/ do
Delayed::Worker.new.work_off
end
That can be extended for any other scripts you'd like to run with that step. Then in the test, it goes something like:
Then I should see the text "..."
When I wait for background jobs to complete
And I refresh the page
Then I should see the text "..."
If anyone has similar problem I ended up writing this (thanks to Square blog post):
require "timeout"
class CucumberExternalWorker
attr_accessor :worker_pid, :start_command
def initialize(start_command)
raise ArgumentError, "start_command was expected" if start_command.nil?
self.start_command = start_command
end
def start
puts "Trying to start #{start_command}..."
self.worker_pid = fork do
start_child
end
at_exit do
stop_child
end
end
private
def start_child
exec({ "RAILS_ENV" => Rails.env }, start_command)
end
def stop_child
puts "Trying to stop #{start_command}, pid: #{worker_pid}"
# send TERM and wait for exit
Process.kill("TERM", worker_pid)
begin
Timeout.timeout(10) do
Process.waitpid(worker_pid)
puts "Process #{start_command} stopped successfully"
end
rescue Timeout::Error
# Kill process if could not exit in 10 seconds
puts "Sending KILL signal to #{start_command}, pid: #{worker_pid}"
Process.kill("KILL", worker_pid)
end
end
end
This can be called as following (added it to env.rb for cucumber):
# start delayed job
$delayed_job_worker = CucumberExternalWorker.new("rake jobs:work")
$delayed_job_worker.start
I am using Resque and Resque Schedule to start a job that has to be run immediately on the application start. Other scheduled jobs are loaded every 30 seconds.
This is the code for my config/initializers/redis.rb
require 'rake'
require 'resque'
require 'resque/server'
require 'resque_scheduler/tasks'
# This will make the tabs show up.
require 'resque_scheduler'
require 'resque_scheduler/server'
uri = URI.parse(ENV["REDISTOGO_URL"])
REDIS = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password)
Resque.redis = REDIS
Dir["#{Rails.root}/app/workers/*.rb"].each { |file| require file }
Resque.enqueue(AllMessageRetriever)
Resque.schedule = YAML.load_file(Rails.root.join('config', 'schedule.yml'))
When the application is started up, the AllMessageRetriever gets run 2-3 times rather than only once. Do the initializers get called more than once? This happens both on Heroku and my local environment?
Is it possible to set a delayed job in Resque-Scheduler which will only get executed once and immediately on runtime?
The code for AllMessageRetriever. Basically it loops over a table and calls an external API to get data and then updates it to the table. This entire task happens 2-3 times if I add the enqueue method in initializer file
require 'socialcast'
module AllMessageRetriever
#queue = :message_queue
def self.perform()
Watchedgroup.all.each do |group|
puts "Running group #{group.name}"
continueLoading=true
page=1
per_page=500
while(continueLoading == true)
User.first.refresh_token_if_expired
token = User.first.token
puts "ContinueLoading: #{continueLoading}"
#test = Socialcast.get_all_messages(group.name,token,page,per_page)
messagesArray = ActiveSupport::JSON.decode(#test)["messages"]
puts "Message Count: #{messagesArray.count}"
if messagesArray.count == 0
puts 'count is zero now'
continueLoading = false
else
messagesArray.each do |message|
if not Message.exists?(message["id"])
Message.create_with_socialcast(message, group.id)
else
Message.update_with_socialcast(message)
end
end
end
page += 1
end
Resqueaudit.create({:watchedgroup_id => group.id,:timecompleted => DateTime.now})
end
# Do anything here, like access models, etc
puts "Doing my job"
end
end
Rake
Firstly, why are you trying to queue on init?
You'd be much better delegating to a rake task which is called from an initializer.
This will remove dependency on the initialize process, which should clear things up a lot. I wouldn't put this in an initializer itself, as it will be better handled elsewhere (modularity)
Problem
I think this line is causing the issue:
Resque.enqueue(AllMessageRetriever)
Without seeing the contents of AllMessageRetriever, I'd surmise that you're AllMessageRetriever (module / class?) will be returning the results 2/3 times, causing Resque to add the (2 / 3 times) data-set to the queue
Could be wrong, but it would make sense, and mean your issue is not with Resque / Initializers, but your AllMessageRetriever class
Would be a big help if you showed it!
Whats the best way to test concurrency locally? i.e. i want to test 10 concurrent hits. I am aware of services like Blitz. However, I am trying to find a simpler way of doing it locally to test against race conditions.
Any ideas? Via Curl maybe?
Check out Apache Bench (ab). Basic usage is dead simple:
ab -n 100 -c 10 http://your.application
For locally testing race conditions in the tests you can use helpers like this
# call block in a forked process
def fork_with_new_connection(config, object = nil, options={})
raise ArgumentError, "Missing block" unless block_given?
options = {
:stop => true, :index => 0
}.merge(options)
fork do
# stop the process after fork
Signal.trap('STOP') if options[:stop]
begin
ActiveRecord::Base.establish_connection(config)
yield(object)
ensure
ActiveRecord::Base.remove_connection
end
end
end
# call multiply times blocks
def multi_times_call_in_fork(count=3, &block)
raise ArgumentError, "Missing block" unless block_given?
config = ActiveRecord::Base.remove_connection
pids = []
count.times do |index|
pids << fork_with_new_connection(config, nil, :index=>index, &block)
end
# continue forked processes
Process.kill("CONT", *pids)
Process.waitall
ActiveRecord::Base.establish_connection(config)
end
# example
multi_times_call_in_fork(5) do
# do something with race conditions
# add asserts
end
I'm trying to speed up a large RSpec project's tests. In addition to using RSpec's --profile option I wanted to get the longest running test files [1] printed out.
In my spec_helper.rb I dump the classes being tested and total time to a file, however as we have spec/model and spec/request directories I'd really like to be able to print the current test's filename and not just the class name (described_class), so that the user can disambiguate between model/foo_spec.rb and request/foo_spec.rb when optimizing.
In a before block in the spec/spec_helper.rb, how can I get the current test file's filename?
My (heavily trimmed) spec_helper looks like this:
config.before :all do
#start_time = Time.now
end
config.after :all do |test|
timings.push({ :name => test.described_class,
:file => 'I do not know how to get this',
:duration_in_seconds => (Time.now - #start_time) })
end
config.after :suite do
timing_logfile_name = 'log/rspec_file_times.log'
timing_logfile = "#{File.dirname(__FILE__)}/../#{timing_logfile_name}"
file = File.open(timing_logfile, 'w')
timings.sort_by{ |timing| timing[:duration_in_seconds].to_f }.reverse!.each do |timing|
file.write( sprintf("%-25.25s % 9.3f seconds\n",
timing[:name], timing[:duration_in_seconds]) )
end
file.close
tell_if_verbose("Overall test times are logged in '#{timing_logfile_name}'")
end
This doesn't seem to be available in the curretn RSpec meta-data, but I'm hoping someone more familiar with the internals can think of a way to expose it. Thanks,
Dave
[1] Often a file with, say, 100 examples in it yields more speed up than a single example from --profile - when that large file's before :each / before :all blocks are targetted, obviously even a ms saved is multiplied up by the number of tests in the file. Using this technique in addition to --profile helped me a lot.
As long as you're just using this for profiling your tests to identify which files need to be improved, you should be able to toss this into your spec_helper.rb file (and remove it afterwards). I fully understand that this is not pretty/clean/elegant/acceptible in production environments and I disavow that I ever wrote it :)
config.before(:each) do |example|
path = example.metadata[:example_group][:file_path]
curr_path = config.instance_variable_get(:#curr_file_path)
if (curr_path.nil? || path != curr_path)
config.instance_variable_set(:#curr_file_path, path)
puts path
end
end
I just acquired the paid version of Heroku's cron, in order to run some tasks every hour. What I would like to know is the syntax I should use to actually get it to run every hour. So far I have this, Could you please tell me if it's correct:
desc "Tasks called by the Heroku cron add-on"
task :cron => :environment do
if Time.now.hour % 1 == 0 # run every sixty minutes?
puts "Updating view counts...."
Video.update_view_counts
puts "Finished....."
puts "Updating video scores...."
VideoPost.update_score_caches
puts "Finished....."
puts "Erasing videos....."
Video.erase_videos!
puts "Finished....."
end
if Time.now.hour == 0 # run at midnight
end
end
I need to know if this line with the 1 in it is the way to go...
if Time.now.hour % 1 == 0
Thanks in advance for your help,
Best Regards.
If you want to run every hour, don't bother checking. Heroku will run it hourly.
Since %1 will always return 0, better to just have:
desc "Tasks called by the Heroku cron add-on"
task :cron => :environment do
puts "Updating view counts...."
Video.update_view_counts
puts "Finished....."
#...
if Time.now.hour == 1 #1am
#...
end
end
Also, if you want to be able to run Video.update_view_counts when you need to, you could instead (after creating the rake task):
Rake::Task["video:update_view_counts"].invoke
That way you can run it inside of cron, and manually if needed
Since, you already have an hourly cron, you don't have to check for the time to run the code.
task :cron => :environment do
#<--- hourly cron begins
puts "Updating view counts...."
Video.update_view_counts
puts "Finished....."
puts "Updating video scores...."
VideoPost.update_score_caches
puts "Finished....."
puts "Erasing videos....."
Video.erase_videos!
puts "Finished....."
#hourly cron ends --->
if Time.now.hour == 0 # run at midnight
end
end