Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JRuby 9k doesn't call a rack method in Lotus::Controller tests but MRI yes #3004

Closed
deepj opened this issue May 29, 2015 · 10 comments
Closed

Comments

@deepj
Copy link

deepj commented May 29, 2015

https://github.com/lotus/controller

OK. This is another weird issue for me. I'm not sure how to describe it.

The problem probably lies in calling get rack method.

@app      = Rack::MockRequest.new(MimeRoutes)
@response = @app.get('/accept', 'HTTP_ACCEPT' => accept)

Due to this some tests fail on wrong mime type detection under JRuby 9k.

# Running:

F

Finished in 0.091769s, 10.8970 runs/s, 10.8970 assertions/s.

  1) Failure:
Accept::when Accept is sent::when weighted#test_0001_accepts selected mime types [test/integration/mime_type_test.rb:30]:
Expected: "html"
  Actual: "xhtml"

1 runs, 1 assertions, 1 failures, 0 errors, 0 skips

This is a minimal case how to reproduce it:

require 'test_helper'
require 'lotus/router'

MimeRoutes = Lotus::Router.new do
  get '/accept', to: 'mimes#accept'
end

module Mimes
  class Accept
    include Lotus::Action

    def call(params)
      self.body = format
    end
  end
end

describe 'Accept' do
  before do
    @app      = Rack::MockRequest.new(MimeRoutes)
    @response = @app.get('/accept', 'HTTP_ACCEPT' => accept)
  end

  describe 'when Accept is sent' do
    describe 'when weighted' do
      let(:accept) { 'text/html,application/xhtml+xml,application/xml;q=0.9' }

      it 'accepts selected mime types' do
        @response.body.must_equal 'html'
      end
    end
  end
end
@headius
Copy link
Member

headius commented May 29, 2015

For some reason, I looked at this one briefly a couple days ago. I think it has something to do with the way the weighting is being applied.

@deepj
Copy link
Author

deepj commented May 29, 2015

At least what I tried it seems the code in Rack::MockRequest.get is never triggered.

I used the following most stupid way (I'm in cloned Lotus::Controller project)

  1. bundle open rack
  2. find lib/mock.rb
  3. edit the file
  4. find line 64.: def request(method="GET", uri="", opts={})
  5. put inside the method something like puts 'echo!'
  6. save
  7. rerun the case above

Result:

Ruby 2.2.2

Run options: --seed 3254

# Running:

echo!
.

Finished in 0.028882s, 34.6238 runs/s, 34.6238 assertions/s.

1 runs, 1 assertions, 0 failures, 0 errors, 0 skips

JRuby 9k head

Run options: --seed 36101

# Running:

F

Finished in 0.091957s, 10.8747 runs/s, 10.8747 assertions/s.

  1) Failure:
Accept::when Accept is sent::when weighted#test_0001_accepts selected mime types [test/integration/mime_type_test.rb:30]:
Expected: "html"
  Actual: "xhtml"

1 runs, 1 assertions, 1 failures, 0 errors, 0 skips

No echo! printed into console. And this is exactly problem why the weighting is wrong. Because the code from Rack is never triggered.

@headius
Copy link
Member

headius commented May 29, 2015

I believe this one is a simple difference in how JRuby and MRI sort the list of mime types and weights. After the above "accept" string is processed, we end up with the following array of mime types and weights (or qualities...both terms are used in the source):

[["text/html", 1.0], ["application/xhtml+xml", 1.0], ["application/xml", 0.9]]

MRI and JRuby sort this list differently:

[] ~/projects/lotus-controller $ jruby blah2.rb
[["application/xml", 0.9], ["text/html", 1.0], ["application/xhtml+xml", 1.0]]

[] ~/projects/lotus-controller $ rvm ruby-2.2 do ruby blah2.rb
[["application/xml", 0.9], ["application/xhtml+xml", 1.0], ["text/html", 1.0]]

Because the weights of "html" and "xhtml" are equal, these are both valid sort results. The actual sort_by is this:

values.sort_by do |match, quality|
  (match.split('/', 2).count('*') * -10) + quality
end

Because neither of these mime types has a "*" as part of its path, they both end up with the same weight for sorting.

It seems to me that that either the implementation or the test are depending on sort_by to have the same order on all implementations when elements are equal. I don't think that's a reasonable assumption.

The fact that get is not called is probably not related, but it is peculiar and I'll look into that as well.

@headius
Copy link
Member

headius commented May 29, 2015

FWIW, I did bundle open rack and added logging to MockRequest.get. It does appear to be called when I run your test case.

@headius
Copy link
Member

headius commented May 29, 2015

Also interesting, someone appears to have attempted to patch around the difference in sort algorithm. This is the full best_q_match method from mime.rb:

      def best_q_match(q_value_header, available_mimes)
        values = ::Rack::Utils.q_values(q_value_header)

        values = values.map do |req_mime, quality|
          match = available_mimes.find { |am| ::Rack::Mime.match?(am, req_mime) }
          next unless match
          [match, quality]
        end.compact

        # See https://github.com/lotus/controller/issues/59
        # See https://github.com/lotus/controller/issues/104
        values = values.reverse unless Lotus::Utils.jruby?

        value  = values.sort_by do |match, quality|
          (match.split('/', 2).count('*') * -10) + quality
        end.last

        value.first if value
      end

Note the values.reverse line which explicitly excludes JRuby. It appears this code is trying to patch around differences in sort algorithms.

@deepj
Copy link
Author

deepj commented May 29, 2015

OK. I was looking on a bad place. I'll take a look at your findings and fix later today. Thanks!

And what the another issue: not triggering code in the get rack method? It is a bug or not? :)

@headius
Copy link
Member

headius commented May 29, 2015

@deepj See #3004 (comment) where I mentioned that I did modify MockRequest in my local copy of Rack, and confirmed that it does get called.

@deepj
Copy link
Author

deepj commented May 29, 2015

Interesting, I can't confirmed it. In my case it doesn't get called.

@headius
Copy link
Member

headius commented May 29, 2015

Make sure you're opening the correct copy of Rack; I assume you're testing on JRuby as well as other Ruby impls that would have their own gems.

I commented here to close the loop, and I'm going to close this bug now. Let me know if you need anything further :-)

@headius headius closed this as completed May 29, 2015
@deepj
Copy link
Author

deepj commented May 29, 2015

OK. I'm shamed. You're completely right. My bad :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants