Skip to content

Implement protocol handshake for Ruby bindings#3809

Merged
p0deje merged 1 commit intomasterfrom
rb-handshake
May 3, 2017
Merged

Implement protocol handshake for Ruby bindings#3809
p0deje merged 1 commit intomasterfrom
rb-handshake

Conversation

@p0deje
Copy link
Copy Markdown
Member

@p0deje p0deje commented Apr 11, 2017

Description

This is currently work in progress. This is done.

We need Ruby bindings to detect what protocol dialect it should talk (e.g. current IEDriver talks OSS dialect, but newer beta versions talk W3C dialect). In order to do that, we need to implement protocol handshake from Java bindings which:

See commit messages for details.

TODO

  • Address all new TODOs (some of them are kept for later)
  • Verify driver extensions are correct for all browsers
  • Get approval from @lmtierney and @titusfortner
  • Fix Travis

Tests

  • go //rb:unit-test
  • go //rb:chrome-test (ran against Chrome 57 and ChromeDriver 2.29)
  • go //rb:edge-test (ran against Microsoft Edge 38.14393.67.0 and 40.15063.0.0) (there are 19 failures in master and this branch)
  • go //rb:ff-esr-test (ran against Firefox 45 ESR to verify legacy driver)
  • go //rb:ff-nightly-test (there are 17 failures in master and 8 failures this branch)
  • go //rb:firefox-test (ran against Firefox 52/53 and GeckoDriver 0.15/0.16)
  • go //rb:ie-test (ran against IE11 and master IEDriver 32-bit)
  • go //rb:ie-test (ran against IE11 and beta IEDriver 32-bit) (there are 14 failures mostly about keyboard/mouse which I'm going to address in a followup commits)
  • go //rb:phantomjs-test (ran against PhantomJS 2.1.1)
  • go //rb:remote-chrome-test
  • go //rb:remote-edge-test (there are 2 failures in master and this branch)
  • go //rb:remote-ff-esr-test
  • go //rb:remote-ff-nightly-test (there are 11 failures in master and this branch)
  • go //rb:remote-firefox-test (there are 5 failures in master and this branch)
  • go //rb:remote-ie-test (master IEDriver) (there are 5 failures in master and this branch)
  • go //rb:remote-ie-test (beta IEDriver) (there are 25 failures which I'm going to work on in the followup commits)
  • go //rb:remote-phantomjs-test
  • go //rb:remote-safari-preview-test (threre are 32 failures in master and this branch)
  • go //rb:remote-safari-test (there are 10 failures in master and this branch)
  • go //rb:remote-test
  • go //rb:safari-preview-test (there are 45 failures in master and this branch)
  • go //rb:safari-test (ran against Safari 10.1)

@p0deje p0deje added the C-rb Ruby Bindings label Apr 11, 2017
@p0deje p0deje changed the title Implement protocol handshake in Ruby Implement protocol handshake for Ruby bindings Apr 11, 2017
@p0deje p0deje force-pushed the rb-handshake branch 4 times, most recently from 0630dfe to e92be5e Compare April 12, 2017 06:46
@lmtierney
Copy link
Copy Markdown
Member

Hoping to take a look at this soon

@p0deje
Copy link
Copy Markdown
Member Author

p0deje commented Apr 16, 2017

@lmtierney @titusfortner This is essentially done, so you can review it. I still need to fix unit tests on Travis (there should be 1 spec failing), but that will be a tiny change.

Since the changes are huge and it's only one commit, it might be better to have a call with screen sharing so I could guide you through the changes - it is hard to figure this out from the diff. If not, I'll try to write down the changes in architecture and post in this PR. Just let me know what you guys prefer.

@lmtierney lmtierney self-requested a review April 22, 2017 02:18

def commands(command)
case command
when :status, :is_element_displayed
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@titusfortner Any idea why this was here? There are plenty of commands duplicated between the two, it seems odd that these were singled out

e3fbb4a#diff-5efb39c0076ea2abd8b2446e895603b5R93

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two commands are implemented, but aren't part of w3c spec, so they reference the oss implementation. Probably not necessary to do it this way.

:implicit_timeout,
:page_load_timeout,
:script_timeout,
].freeze
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spec has required capabilities of:

  • browser_name
  • browser_version
  • platform_name
  • accept_insecure_certs
  • page_load_strategy
  • proxy
  • set_window_rect
  • timeouts
  • unhandled_prompt_behavior

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to rework the capabilities in the followup PRs, I've left a TODO for it above the constant.

Copy link
Copy Markdown
Member

@lmtierney lmtierney left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, great work

opts[:platform_name] = opts.delete(:platform) if opts.key?(:platform)
opts[:timeouts] = {}
opts[:timeouts]['implicit'] = opts.delete(:implicit_timeout) if opts.key?(:implicit_timeout)
opts[:timeouts]['page load'] = opts.delete(:page_load_timeout) if opts.key?(:page_load_timeout)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should actually be pageLoad per the spec, which means we currently have it wrong

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's fix it in the next PRs

Comment thread rb/build.desc Outdated
"lib/selenium/webdriver/ie.rb"
],
resources = [
{ "//javascript/webdriver/atoms:getAttribute": "rb/lib/selenium/webdriver/atoms/getAttribute.js"}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we now have the protocol handshake, we should probably add this to common instead of including in each one. The only one that shouldn't need it in the long run would be legacy FF and it's not that much overhead

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds reasonable.

@p0deje
Copy link
Copy Markdown
Member Author

p0deje commented Apr 28, 2017

It's been almost 2 weeks, so I'm going to merge it later today unless there will be any objections.

@titusfortner
Copy link
Copy Markdown
Member

I'm halfway through a review, but there's a lot more code here than I have time to work through right now. I can finish up tomorrow. Thanks for your patience.

Copy link
Copy Markdown
Member

@titusfortner titusfortner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I fully get the move from Bridge -> Driver. It feels like this is adding a lot of complexity because there is more module extension and requiring of modules with class methods than just a straight class hierarchy as before. For what we need to do with capabilities classes, though, maybe this is necessary.

I guess it decouples capabilities from the Bridge classes. So the Bridge is now just the command interface to the server/driver, which is a positive move.

Not that it should be our primary concern, but I'm curious how much this is going to affect the Ruby Appium library since it does so much monkey patching of the internals.

Also, we've been telling people that if they want to customize their driver/server interaction to subclass a Bridge and make their own custom commands. So, I'm concerned that this change will completely break their implementation.

From a high level view, I don't think this is how I would have approached it, but sketching out a few alternate ideas this week I've come to the conclusion that a more elegant solution isn't obvious to me. :) As for the actual implementation of this design, I think I see how everything is working and I don't have any major issues.

Thanks for all the work on this.

opts[:url] = @launcher.url
end

@bridge = Remote::Bridge.handshake(opts)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice you forced the Edge implementation to use W3C protocol. Since this is the "Legacy Firefox" when would this get called and need to use W3C?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some weird things there. You've implemented Edge bridge as W3C bridge, however it responds like OSS one, so there is a bit of confusion here. By forcing W3C in Edge, I'm keeping the current behavior and implementation, though I'd like to rework this later.

In regards to legacy Firefox, there is actually no need to do this, so I'll change.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edge was implemented as W3C because that was the format of the desired capabilities it accepted, which was what I was most concerned about. Edge Driver has from the beginning supported an interesting mix of OSS & W3C commands. Yes, this distinction won't matter once we send both formats, though it could raise some other issues if this approach expects drivers to be completely compatible with either OSS or W3C.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed it so now neither Marionette::Driver nor Legacy::Driver do handshake.


module Bridge

# Support for geckodriver < 0.15
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless you really want to, I'm ok with telling Ruby users to upgrade to the latest geckodriver. :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put this in the current bridge, @p0deje you can remove these commands

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lmtierney Sorry, I don't follow what you mean

Copy link
Copy Markdown
Member

@lmtierney lmtierney May 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@p0deje you can get rid of this entire bridge file. These window commands were added for backward compatibility with older geckodriver versions. Since we are fine with requiring the latest geckodriver they aren't needed anymore

execute :touch_scroll, {}, {element: element,
xoffset: x,
yoffset: y}
if oss_status
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is the correct toggle for this. Are we sure that Se4 Server isn't going to return a status value with a w3c response once it supports that? I think we might need to parse the capabilities themselves for this conditional. Of course we can make that a TODO for later if necessary.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's how Java and .NET bindings do this, so ¯_(ツ)_/¯

@p0deje
Copy link
Copy Markdown
Member Author

p0deje commented Apr 30, 2017

I guess it decouples capabilities from the Bridge classes. So the Bridge is now just the command interface to the server/driver, which is a positive move.

Yeah, that was the idea.

Not that it should be our primary concern, but I'm curious how much this is going to affect the Ruby Appium library since it does so much monkey patching of the internals.

I know they have already created appium/ruby_lib#558 and appium/ruby_lib#559. It would be great to hear from them here, but I don't really have time to reach them out.

Also, we've been telling people that if they want to customize their driver/server interaction to subclass a Bridge and make their own custom commands. So, I'm concerned that this change will completely break their implementation.

It's not changed, isn't it? You can still pass custom bridge so I don't see a problem here:

bridge = MyCustomBridge.new
driver = Selenium::WebDriver::Driver.new(bridge)

From a high level view, I don't think this is how I would have approached it, but sketching out a few alternate ideas this week I've come to the conclusion that a more elegant solution isn't obvious to me. :) As for the actual implementation of this design, I think I see how everything is working and I don't have any major issues.

I was attempting to also make it more consistent to other bindings. None of them have a bridge, but all of them have custom driver classes, so I actually think it is now clearer. I would have to decouple capabilities and bridge anyways and driver class seems like a best option.

@p0deje
Copy link
Copy Markdown
Member Author

p0deje commented May 2, 2017 via email

Ruby bindings need to detect what protocol dialect it should talk
using protocol handshake which:

1. Prepares capabilities for both OSS and W3C dialects.
2. Sends them in a single payload like this:

{
  "desiredCapabilities": {...},
  "capabilities": {
    "firstMatch": [{...}]
  }
}

3. Based on a response, understand which dialect it needs to use.

This commit adds Remote::Bridge.handshake, refactors driver-specific
bridges into Driver classes which are responsible for preparing
driver-specific capabilities, calling handshake and using the bridge
it returns (OSS or W3C) to abstract dialect-specific communication.

For Firefox, it introduces Legacy and Marionette namespaces with its own
Driver classes and bridge extensions.

For Edge, it uses a hacky way to force W3C dialect even though
MicrosoftWebDriver responds with OSS-like body.

It also namespaces remote bridges/commands/etc. into OSS and W3C
modules respectively.
@p0deje
Copy link
Copy Markdown
Member Author

p0deje commented May 2, 2017

All the comments are addressed, now just waiting for CI.

@p0deje p0deje merged commit a8a0034 into master May 3, 2017
@p0deje p0deje deleted the rb-handshake branch May 3, 2017 02:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C-rb Ruby Bindings D-chrome D-edge D-firefox D-IE Z-marionette Archived: Marionette browser / driver

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants