Ruby Threads with Watir - ruby

I have several classes written that govern how I want to handle several websites, with similar methods in both (ie. login, refresh). Each class opens up its own WATIR browser instance.
class Site1
def initialize
#ie = Watir::Browser.new
end
def login
#ie.goto "www.blah.com"
end
end
a sample of code in the main with no threads is as follows
require 'watir'
require_relative 'site1'
agents = []
agents << Site1.new
agents.each{ |agent|
agent.login
}
This works fine, but doesnt move onto the next agent until the current one has finished logging in. I would like to incorporate multithreading to handle this, but cant seem to get it to work.
require 'watir'
require_relative 'site1'
agents = []; threads = []
agents << Site1.new
agents.each{ |agent|
threads << Thread.new(agent){ agent.login }
}
threads.each { |t| t.join }
this gives me the error: unknown property or method: navigate. HRESULT error code:0x8001010e. The application called an interface that was marshalled for a different thread.
does anyone know how to fix this, or how to implement a similar functionality?

Not really sure on this, but here is a swing using threads.
require 'thread'
threads = [] # Setting an array to store threaded commands
c_thread = Thread.new do # Start a new thread
login # Call our command in the thread
end
threads << c_thread

Related

Capybara instance of "browser" for Page Object Model?

Im writing a framework using Capybara and the Page Object Model for a web application. It's my first time writing my own framework and using PoM for automation.
My base "Page Object" essentially initializes the driver and is used in every other page object child class (for the individual pages)
class PageObject
include Capybara::DSL
BASE_URL = 'https://www.atesturl.com'
Capybara.default_max_wait_time = 5
def initialize
Capybara.register_driver :selenium_chrome do |app|
Capybara::Selenium::Driver.load_selenium
browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts|
# Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
opts.args << '--disable-site-isolation-trials'
end
Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options)
end
Capybara.register_driver :selenium_chrome_headless do |app|
Capybara::Selenium::Driver.load_selenium
browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts|
opts.args << '--headless'
opts.args << 'window-size=2880,1800'
opts.args << '--disable-gpu' if Gem.win_platform?
#opts.args << '--remote-debugging-port=9222'
# Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
opts.args << '--disable-site-isolation-trials'
end
Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options)
end
Capybara.current_driver = :selenium_chrome
end
def visit_url
visit BASE_URL
end
end
In most examples of PoM I see the methods returning an instance of that page object, but generally they use some #browser instance variable that is passed around. Within my test scripts I simple call an instance of the Base page object class via let(:p) {PageObject.new} and then p.visit_url then make new instances of the other page objects via new....but this seems like the wrong way to do it.
How exactly do I return a instance of the #browser or driver that I can pass around? And how should I be calling it in my spec?
You don't want to be registering your drivers in the initialize for the base PageObject since that means every object created will be registering new driver configs - which isn't desirable.
When you include Capybara::DSL into your class you're including methods that end up calling methods on Capybara.current_session. ie visit => Capybara.current_session.visit. The result of Capybara.current_session is the "#browser" instance you're asking about since it encapsulates the driver/browser instance. The issue with the way you've currently implemented that is that if any code changes the current session then all your objects will suddenly refer to the new session. If instead you store a reference to the session you want the object to use in each object and call the Capybara methods on that session rather than using Capybara::DSL (#session.visit ...) then you can be sure the session an object is using doesn't change unexpectedly.
Also note that things like Capybara.default_max_wait_time, `Capybara.current_driver', etc are global settings so setting them inside your PageObject class isn't a great idea.

Unable to run method multiple times

I'm using the WiringPi gem. This block of code works as expected:
pinNumber = 7
io = WiringPi::GPIO.new do |gpio|
gpio.pin_mode(pinNumber, WiringPi::INPUT)
end
pin_state = io.digital_read(pinNumber)
return pin_state
However, when I enclose this in a method so I can make a call using Sinatra, I get the following error when I try to refresh:
wiringPiSetup*: You must only call this once per program run. This is
a fatal error. Please fix your code.
Why must this be run only once, and what is the best solution? Ultimately pin_state needs to be retrieved every time I navigate to the root url.
Here's the full code:
require 'wiringpi'
require 'sinatra'
def getstate()
pinNumber = 7
io = WiringPi::GPIO.new do |gpio|
gpio.pin_mode(pinNumber, WiringPi::INPUT)
end
pin_state = io.digital_read(pinNumber)
return pin_state
end
get '/' do
getstate()
end
After creating your GPIO instance, you can make repeated calls to read pins from it. Your code is creating a new instance on each call, however.
require 'wiringpi'
require 'sinatra/base'
class MyApp < Sinatra::Base
InputPin = 7
IO = WiringPi::GPIO.new do |gpio|
gpio.pin_mode(InputPin, WiringPi::INPUT)
end
def get_state(pin_number = MyApp::InputPin)
MyApp::IO.digital_read(pin_number)
end
get '/' do
get_state
end
end
pinNumber = 7
def io
#io ||= begin
WiringPi::GPIO.new do |gpio|
gpio.pin_mode(pinNumber, WiringPi::INPUT)
end
end
end
def getstate()
pin_state = io.digital_read(pinNumber)
return pin_state
end
get '/' do
getstate() end
Initialize the API once when your application starts (outside getState() method). You're initializing the API multiple times, thus the error.

Ruby EventMachine, kill running processes?

How can I kill running processes in EventMachine?
Below is an example, I'm starting 10 processes and then I'm trying to erase them all (but it doesn't work). My goal is to not have the "Finished" output.
require "rubygems"
require "eventmachine"
class Event
def start
sleep(5)
puts Time.now.to_s + ": Finished!"
end
end
EventMachine.run do
events = []
10.times {
handle = Event.new
events << handle
EventMachine.defer(proc {
handle.start
})
}
# Terminate all events!
events.each do |handle|
handle = nil
ObjectSpace.garbage_collect
end
end
I'm aware that I could set a variable and check whether it's set when doing the output, but I feel like this isn't the "real" thing, or is this really the only solution there is?
Try EventMachine.stop_event_loop, it will “cause all open connections and accepting servers to be run down and closed”.

Is there an Asynchronous Logging Library for Ruby?

Synchronous logging incurs a large performance penalty as it may block. Is there a standalone Ruby library that does asynchronous logging (log4r doesn't seem to)? Can I modify the standard library logger to log asynchronously? I'm looking for something like log4j's AsyncAppender - but preferably an implementation that makes use of Ruby's code blocks to shift as much work to the background thread as possible.
I know you shouldn't really answer your own question, but it seems everything is easy in ruby:
require 'thread'
require 'singleton'
require 'delegate'
require 'monitor'
class Async
include Singleton
def initialize
#queue = Queue.new
Thread.new { loop { #queue.pop.call } }
end
def run(&blk)
#queue.push blk
end
end
class Work < Delegator
include MonitorMixin
def initialize(&work)
super work; #work, #done, #lock = work, false, new_cond
end
def calc
synchronize {
#result, #done = #work.call, true;
#lock.signal
}
end
def __getobj__
synchronize { #lock.wait_while { !#done } }
#result
end
end
Module.class.class_exec {
def async(*method_names)
method_names.each do |method_name|
original_method = instance_method(method_name)
define_method(method_name) do |*args,&blk|
work = Work.new { original_method.bind(self).call(*args,&blk) }
Async.instance.run { work.calc }
return work
end
end
end
}
And for my logging example:
require 'Logger'
class Logger
async :debug
end
log = Logger.new STDOUT
log.debug "heloo"
As return values work, you can use this for just about anything:
require "test/unit"
class ReturnValues < Test::Unit::TestCase
def do_it
5 + 7
end
async :do_it
def test_simple
assert_equal 10, do_it - 2
end
end
No personal experience with that:
https://github.com/dirs/analogger
The Swiftcore Analogger implements a fast asynchronous logging system
for Ruby programs as well as client library for sending logging messages
to the Analogger process.
Analogger will accept logs from multiple sources and can have multiple
logging destinations. Currently, logging to a file, to STDOUT, or to
STDERR is supported. A future revision may support logging to a
database destination, as well.
Analogger depends on EventMachine (http://rubyforge.org/projects/eventmachine)
to provide the framework for the network communications, though EM is
not used for the client library.
The built in Logger class is already thread safe
Checkout Dunder https://github.com/fonsan/dunder
I created this gem 6 months ago that does just this and more

Thread Locking in Ruby (use of soap4r and QT)

[EDIT NOTE: Noticed I had put the mutex creation in the constructor. Moved it and noticed no change.]
[EDIT NOTE 2: I changed the call to app.exec in a trial run to
while TRUE do
app.processEvents()
puts '."
end
I noticed that once the Soap4r service started running no process events ever got called again]
[EDIT NOTE 3: Created an associated question here: Thread lockup in ruby with Soap4r
I'm attempting to write a ruby program that receives SOAP commands to draw on a monitor (thus allowing remote monitor access). I've put together a simple test app to prototype the idea. The graphic toolkit is QT. I'm having what I assume is a problem with locking. I've added calls to test the methods in the server in the code shown. The server side that I'm testing right now is:
require 'rubygems'
require 'Qt4'
require 'thread'
require 'soap/rpc/standaloneserver'
class Box < Qt::Widget
def initialize(parent = nil)
super
setPalette(Qt::Palette.new(Qt::Color.new(250,0,0)))
setAutoFillBackground(true)
show
end
end
class SOAPServer < SOAP::RPC::StandaloneServer
##mutex = Mutex.new
def initialize(* args)
super
# Exposed methods
add_method(self, 'createWindow', 'x', 'y', 'width', 'length')
end
def createWindow(x, y, width, length)
puts 'received call'
windowID = 0
puts #boxList.length
puts #parent
##mutex.synchronize do
puts 'in lock'
box = Box.new(#parent)
box.setGeometry(x, y, width, length)
windowID = #boxList.push(box).length
print "This:", windowID, "--\n"
end
puts 'out lock'
return windowID
end
def postInitialize (parent)
#parent = parent
#boxList = Array.new
end
end
windowSizeX = 400
windowSizeY = 300
app = Qt::Application.new(ARGV)
mainwindow = Qt::MainWindow.new
mainwindow.resize(windowSizeX, windowSizeY)
mainwindow.show
puts 'Attempting server start'
myServer = SOAPServer.new('monitorservice', 'urn:ruby:MonitorService', 'localhost', 4004)
myServer.postInitialize(mainwindow)
Thread.new do
puts 'Starting?'
myServer.start
puts 'Started?'
end
Thread.new do
myServer.createWindow(10,0,10,10)
myServer.createWindow(10,30,10,10)
myServer.createWindow(10,60,10,10)
myServer.createWindow(10,90,10,10)
end
myServer.createWindow(10,10,10,10)
Thread.new do
app.exec
end
gets
Now when I run this I get the following output:
Attempting server start
Starting?
received call
0
#<Qt::MainWindow:0x60fea28>
in lock
received call
0
#<Qt::MainWindow:0x60fea28>
This:1--
in lock
This:2--
out lock
At that point I hang rather than recieving the total of five additions I expect. Qt does display the squares defined by "createWindow(10,0,10,10)" and "createWindow(10,10,10,10)". Given that "This:1--" and "This:2--" show within a nexted in/out lock pair I'm assuming I'm using mutex horribly wrong. This is my first time with threading in Ruby.

Resources