runtimeanalyz.ing

← Runtime Analysis

What it found in Redmine

Redmine is an open-source Rails app that we did not write. We ran the free gem on a clone of it, and every command and its output are quoted as they ran. You can repeat it on your own checkout.

Record

The checkout is Redmine’s trunk at commit 72914fa, set up so that its own tests run. Nothing was added to its Gemfile. It already held an earlier recording, so this run was written to a directory of its own, set with RUNTIME_ANALYSIS_OUTPUT=tmp/ra-install-check, and the questions below name it with --recording. On a fresh checkout you need neither.

$ curl -fsSL https://www.runtimeanalyz.ing/install.sh | sh
$ ra record -- bundle exec bin/rails test test/unit/lib/redmine/field_format test/helpers/issues_helper_test.rb
ra: recording `bundle exec bin/rails test test/unit/lib/redmine/field_format test/helpers/issues_helper_test.rb` into tmp/ra-install-check

Finished in 1.504617s, 96.3700 runs/s, 309.7134 assertions/s.
145 runs, 466 assertions, 0 failures, 0 errors, 0 skips
ra: recorded. `ra tools` lists what you can ask about it.

The whole command took three seconds, Rails boot included. The 145 tests pass with the recorder loaded, as they do without it. git status in the checkout shows nothing afterwards.

Ask what a method receives

Redmine::FieldFormat::Base#formatted_value turns a custom field’s stored value into what a page shows. Its signature is in lib/redmine/field_format.rb:

def formatted_value(view, custom_field, value, customized=nil, html=false)

ra what_it_receives lists the classes each of those parameters was given in the recording:

$ ra what_it_receives 'Redmine::FieldFormat::Base#formatted_value' --recording tmp/ra-install-check
Redmine::FieldFormat::Base#formatted_value
· 245 calls · 6 call sites
view (arg 1)
    classes   Mailer::HelperMethods(anonymous) 224 · IssuesHelperTest 10 · Redmine::NumericFieldFormatTest 7 · Redmine::ListFieldFormatTest 3 · Redmine::UserFieldFormatTest 1
    receives  #format_object
  · from 17 spec examples and 0 app runs
custom_field (arg 2)
    classes   IssueCustomField
    receives  (no app messages)
    never     #type_name #delete_workflow_rules #visible_by? #visibility_by_project_condition #validate_custom_field
  · from 17 spec examples and 0 app runs
value (arg 3)
    classes   String 156 · NilClass 80 · Array 9
    receives  (no app messages)
  · from 17 spec examples and 0 app runs
customized (arg 4)
    classes   Issue 235 · FalseClass 10
    receives  (no app messages)
  · from 17 spec examples and 0 app runs
html (arg 5)
    classes   FalseClass 230 · TrueClass 4
    receives  (no app messages)
  · from 17 spec examples and 0 app runs

customized is the record the field belongs to. It was an Issue in 235 calls and false in 10.

Ask who sent it

$ ra what_calls 'Redmine::FieldFormat::Base#formatted_value' --recording tmp/ra-install-check
Redmine::FieldFormat::Base#formatted_value
· 7 callers in 1 recorded run
  Redmine::FieldFormat::DateFormat#formatted_custom_value       lib/redmine/field_format.rb:248                                   112
  Redmine::FieldFormat::FloatFormat#formatted_custom_value      lib/redmine/field_format.rb:248                                    58
  Redmine::FieldFormat::ListFormat#formatted_custom_value       lib/redmine/field_format.rb:248                                    56
  IssuesHelperTest#format_value                                 app/helpers/custom_fields_helper.rb:171                            10
  Redmine::FieldFormat::IntFormat#formatted_custom_value        lib/redmine/field_format.rb:248                                     5
  test/unit/lib/redmine/field_format/list_format_test.rb        test/unit/lib/redmine/field_format/list_format_test.rb:145          3
  test/unit/lib/redmine/field_format/user_field_format_test.rb  test/unit/lib/redmine/field_format/user_field_format_test.rb:130    1

The only caller with 10 calls is the helper at app/helpers/custom_fields_helper.rb:171:

def format_value(value, custom_field)
  format_object(custom_field.format.formatted_value(self, custom_field, value, false), html: false)
end

The helper passes four arguments. The fourth parameter is customized and the fifth is html, so the false is bound to customized. Compared with the signature, the false looks intended for html. That last sentence is our reading of the code, and the counts above it come from the recording.

The effect on Redmine today

It has no visible effect today. html defaults to false, which is what the caller appears to want, and false is falsy like the nil that customized defaults to. The branch that calls customized.id runs only when html is true. The mistake will have an effect when someone adds a line that calls a method on customized on this path.

No test fails and no type is declared, so the source and the test results give no warning of it. An agent editing formatted_value would read the signature and assume a record or nil. From one recorded run of the tests, the tools report that a third class arrives and which line sends it.

More from the same recording

This run recorded part of one test suite, which is why the output says 0 app runs. The tools used here are the free ones. The Pro tools read the same recording and add the call stack down to that line and the messages each value went on to receive.

Run it on your app

curl -fsSL https://www.runtimeanalyz.ing/install.sh | sh
ra record
ra what_it_receives 'YourClass#your_method'

Install it and connect your agent and editor →