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

Compass-rails test suite fails strangely under JRuby #2423

Closed
craigmcnamara opened this issue Jan 4, 2015 · 18 comments
Closed

Compass-rails test suite fails strangely under JRuby #2423

craigmcnamara opened this issue Jan 4, 2015 · 18 comments

Comments

@craigmcnamara
Copy link

It looks like under JRuby for Rails 3.2, 4.0 and 4.2 the default assets aren't being created when rails new is called. The strange thing is everything works for Rails 3.1 and the failure is only on JRuby. I originally thought that it was a FileUtils issue, but it seems that it's more likely a JRuby/Rails issue.

So the build that illustrates this is here: https://travis-ci.org/Compass/compass-rails/builds/45890747

The commit that makes it happen is here: https://github.com/Compass/compass-rails/compare/investigate-jruby-test-bug

@headius headius added this to the JRuby 9.0.0.0 milestone Jan 5, 2015
@headius
Copy link
Member

headius commented Jan 5, 2015

Some additional info from the build.

  • JRuby 9k build as of 2014-1-3.
  • Java 7
  • The compass-rails suite failed with Rails 3.2, 4, and 4.2, but not Rails 3.1

There's definitely file management involved somehow. The line that fails (first link in desc) is checking that the "project file" pf is in fact a File, using File.file?. This might indicate some path coercion issue, or an issue in File.file? logic (stat? posix stuff?).

Note also that there's no successful build against JRuby 1.7 either, so this may not be a regression at all.

@headius
Copy link
Member

headius commented Jan 5, 2015

Ok, there's a more serious problem here, and I no longer things it has anything to do with filesystem management. We've got some bad string code, it would seem:

~/projects/jruby/Compass/compass-rails/rails-temp $ rails new test-railtie-3.2 -g -G -O --skip-bundle
      create  
      create  README.rdoc
      create  Rakefile
      create  config.ru
      create  Gemfile
      create  app
      create  app/assets/images/rails.png
      create  app/assets/javascripts/application.js.tt
      create  app/assets/stylesheets/application.css
      create  app/controllers/application_controller.rb
      ...

The file "application.js.tt" should just be named "application.js". The extra .tt is coming from a JRuby bug. I saw this on some other reports form people that accidentally started running jruby-head on Travis.

@headius
Copy link
Member

headius commented Jan 5, 2015

Same issue confirmed with a simple "rails new" without compass-rails being involved.

@headius
Copy link
Member

headius commented Jan 5, 2015

Looking in the logic that copies these files into place during generation, I find:

    def javascripts
      return if options.skip_javascript?

      if mountable?
        template "#{app_templates_dir}/app/assets/javascripts/application.js.tt",
                  "app/assets/javascripts/#{name}/application.js"
      elsif full?
        empty_directory_with_gitkeep "app/assets/javascripts/#{name}"
      end
    end

So the .tt files are basically the template versions of new project files. The "template" function (which I have not found yet) takes these files and processes them...presumably into the target non-.tt filename. So somewhere along the way we're not losing the .tt filename?

@headius
Copy link
Member

headius commented Jan 5, 2015

The bug manifests while running Thor's logic for processing directories. In that code, it cycles through all files in the directory, handling them in one of three ways:

          case file_source
          when /\.empty_directory$/
$stderr.puts "empty dir"
            dirname = File.dirname(file_destination).gsub(/\/\.$/, "")
            next if dirname == given_destination
            base.empty_directory(dirname, config)
          when /#{TEMPLATE_EXTNAME}$/
$stderr.puts "template"
            base.template(file_source, file_destination[0..-4], config, &@block)
          else
$stderr.puts "else"
            base.copy_file(file_source, file_destination, config, &@block)
          end

This logic prints out "else" for every case I could find, including filenames like application.js.tt that should have matched the template extension. I have confirmed this with additional logging, and see output like this:

Code, ahead of the case/when logic:

$stderr.puts(file_source)
$stderr.puts TEMPLATE_EXTNAME
$stderr.puts /#{TEMPLATE_EXTNAME}/ === file_source

Output with stderr lines from previous snippit:

/Users/headius/projects/jruby/lib/ruby/gems/shared/gems/railties-3.2.21/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
.tt
true
else

What we have here is a regexp that does match the filename for a normal === call, but apparently is not matching the when clause.

@headius
Copy link
Member

headius commented Jan 5, 2015

Short reproduction, now that I've tracked down the problem:

FOO = '.z'
case 'x.y.z'
when /#{FOO}/
  p :here
else
  p :there
end

This code will print out ":there" even though the regexp-based when clause should have matched the file. Note that if you remove the FOO and replace with '.z', or just make the regexp be /.z/, it follows the expected path.

Digging a bit deeper...here's the IR:

BB [1:LBL_3:-1]
BB [2:LBL_4:-1]
    %self = recv_self
    line_num(0)
    put_const(module<0>, FOO) = ".z"
    %v_0 = search_const(FOO, scope<0>, no-private-consts=false)
    %v_1 = build_dregexp(["", #{%v_0}],RegexpOptions(kcode: NONE, kcodeDefault))
    beq("x.y.z", %v_1, LBL_2:-1)
BB [3:LBL_5:-1]
    %v_1 = call_1o(FUNCTIONAL, p, %self, [:'there']){1O}
    %v_0 = copy(%v_1)
    return(%v_1)
BB [4:LBL_2:-1]
    %v_1 = call_1o(FUNCTIONAL, p, %self, [:'here']){1O}
    %v_0 = copy(%v_1)
    return(%v_1)
BB [7:LBL_6:-1]

If I'm reading it right, it appears to building a "beq" instruction for the dynamic-regexp when clause, checking that the incoming value is equal to the regexp. Investigating IRBuilder next.

@headius
Copy link
Member

headius commented Jan 5, 2015

The equivalent IR for a plain literal regexp when clause, for comparison:

    %v_0 = eqq(RE:|".z"|RegexpOptions(kcode: NONE, kcodeDefault), "x.y.z")
    b_true(%v_0, LBL_2:-1)

Obviously this is completely different...and correct, since it's doing eqq (===).

@headius
Copy link
Member

headius commented Jan 5, 2015

Ok, I see the problem.

When parsed, the dynamic-regexp when clause looks like this (AST):

          WhenOneArgNode 0
            DRegexpNode 0
              StrNode 0
              EvStrNode 0
                NewlineNode 0
                  ConstNode:FOO 0

The IRBuilder logic for case/when includes this logic (comments removed):

            if (whenNode.getExpressionNodes() instanceof ListNode) {
                if (value == UndefinedValue.UNDEFINED)  {
                    v1 = build(whenNode.getExpressionNodes(), s);
                    v2 = manager.getTrue();
                } else {
                    v1 = value;
                    v2 = build(whenNode.getExpressionNodes(), s);
                }
            } else {
                s.addInstr(new EQQInstr(eqqResult, build(whenNode.getExpressionNodes(), s), value));
                v1 = eqqResult;
                v2 = manager.getTrue();
            }
            s.addInstr(BEQInstr.create(v1, v2, bodyLabel));

I believe the ListNode check here is to handle constructs like when a, b, c. This does not seem like the right logic for that case, but I'm going to leave it to @subbuss and @enebo to weigh in there.

The problem with this code for the current bug is that DRegexp's superclass, DNode, extends ListNode. That causes all dynamic string constructs to fall into the ListNode logic, which results in them being handled via a simple equality check (since all DNodes just return their constructed object).

I have a fix that special-cases DNode, but we need to review this code to be sure it's handling all when forms properly.

headius added a commit that referenced this issue Jan 5, 2015
This is a gross fix, and we need to reexamine this code.

It will be a fix for #2423 in the interim.
@headius
Copy link
Member

headius commented Jan 5, 2015

I have committed a fix based on my description above. With it, rails apps generate correctly, without the ".tt" files.

Still to do for this bug:

  • Tests for all DNode types in case/when logic.
  • Reexamine this logic in IRBuilder and make it not gross.

@headius headius added the ir label Jan 5, 2015
@headius
Copy link
Member

headius commented Jan 5, 2015

@craigmcnamara If possible, you could try out a local build of jruby-head with this fix and confirm it runs green with your patch in place. I am pretty sure it will be good.

When travis goes green jruby-head will be updated to the latest rev.

@headius
Copy link
Member

headius commented Jan 5, 2015

Travis build for the fix: https://travis-ci.org/jruby/jruby/builds/45915597

@craigmcnamara
Copy link
Author

I just started one for compass-rails: https://travis-ci.org/Compass/compass-rails/builds/45916037

@headius
Copy link
Member

headius commented Jan 5, 2015

jruby-head is probably not going to be updated by the time that build hits it...but if the build I linked goes green (I'll monitor it for a while) you should be able to restart the other build and pick up new jruby-head.

(When builds of JRuby's master branch are green in Travis, Travis updates their build for "jruby-head")

@craigmcnamara
Copy link
Author

I thought I was wishful thinking, I can do one locally if you can tell me which of these versions I should ask ruby-build for.

jruby-9.0.0.0-dev
jruby-9.0.0.0+graal-dev
jruby-9000-dev
jruby-9000+graal-dev

@headius
Copy link
Member

headius commented Jan 5, 2015

The ruby-build binaries are built nightly...and I don't think we're due for another build for at least a couple hours :-) The world just doesn't move fast enough for us, does it?

The travis build is about 2/3 done, so I'll monitor it until it completes. Then you should have an updated travis jruby-head.

@craigmcnamara
Copy link
Author

So this is pretty awesome: https://travis-ci.org/Compass/compass-rails/builds/45918791

@subbuss
Copy link
Contributor

subbuss commented Jan 5, 2015

Good catch and fix. This needs more thorough tests indeed. So, reopening so we don't forget.

@headius
Copy link
Member

headius commented Jan 5, 2015

I have added specs for the following cases:

  • when with a dynamic regexp
  • when with a dynamic string
  • when with a dynamic symbol
  • when with more than one condition element
  • when with a literal array

I believe this should cover all paths through the associated logic in IRBuilder. I'm going to be exploring coverage tools today so we are more likely to avoide these testing gaps in the future.

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

No branches or pull requests

4 participants