RSpec integration

RPicSim has optional code that allows you to integrated it nicely with RSpec. RPicSim's RSpec integration is mainly designed to help you write shorter, less verbose tests, but it also provides helpful error messages when a test fails. Most of the example code in this manual assumes that you have the RSpec integration enabled.

Turning on RSpec integration

To load RPicSim and enable its RSpec integration, simply put this line in your spec_helper.rb:

require 'rpicsim/rspec'

That gives you all the features that are documented below.

Fine-grained configuration

Alternatively, if you just want a subset of the features described here, you can use any combination of the snippets below:

# Persistent expectations that can be stored and checked later
require 'rpicsim/rspec/persistent_expectations'
RSpec.configure { |c| c.include RPicSim::RSpec::PersistentExpectations }
# Helper methods, including persistent expectations
require 'rpicsim/rspec/helpers'
# Show debug info from sim if a spec fails
require 'rpicsim/rspec/sim_diagnostics'
# Better error messages for RSpec 2.x
require 'rpicsim/rspec/be_predicate'

You will also need to load RPicSim itself by writing:

require 'rpicsim'

Helper methods

Requiring “rpicsim/rspec” causes the RPicSim::RSpec::Helpers module to get included (i.e. mixed in) to all of your RSpec examples.

This module provides the start_sim method and the methods described on the persistent expectations page. You can call start_sim in an example or a before hook to start a new simulation. The simulation object can then be accessed through a method named sim in your examples.

Basic shortcuts

Unless you disable them, calling start_sim will also give you access to over a dozen basic shortcut methods like pin and run_to in your RSpec examples. The full list of basic shortcuts can be found in RPicSim::Sim::BasicShortcuts::ForwardedMethods. You can call these by simply typing a method name in an RSpec example:

run_to :foo    # equivalent to sim.run_to :foo

Firmware-specific shortcuts

Unless you disable them, you will get access to firmware-specific shortcuts defined by the simulation. These shortcuts correspond to items defined with def_var and def_pin.

For example, if your simulation class defines a pin named main_output, then you can just write code like this in your RSpec examples:

expect(main_output).to be_driving_high

You can disable the firmware-specific shortcuts in your RSpec examples, but they will still be available on the simulation object itself (e.g. sim.main_output).

Configuring shortcuts

RPicSim provides a custom RSpec configuration option called sim_shortcuts that can either be set to :all (default), :basic, or :none.

If you just want to use the basic shortcuts and not the firmware-specific shortcuts, add the following code to your spec_helper.rb:

RSpec.configure do |config|
  config.sim_shortcuts = :basic
end

To turn off all the shortcuts, use:

RSpec.configure do |config|
  config.sim_shortcuts = :none
end

Diagnostic information

If an error happens in a test (either from an expectation failing or from a general exception being raised), RPicSim augments the default output of RSpec in order to provide additional information about the state of the simulation. When an RSpec example fails, the output you get will look something like this:

................................................F.....

Failures:

  1) FooWidget when exposed to 1.5 ms pulses behaves correctly
     Failure/Error: run_cycles 1500*4
       expected INTCON to satisfy block
     # ./lib/rpicsim/rspec/persistent_expectations.rb:29:in `check_expectations'
     # ./lib/rpicsim/rspec/persistent_expectations.rb:27:in `check_expectations'
     # ./lib/rpicsim/rspec/helpers.rb:25:in `start_sim'
     # ./lib/rpicsim/sim.rb:574:in `step'
     # ./lib/rpicsim/sim.rb:716:in `run_to_cycle_count'
     # ./lib/rpicsim/sim.rb:708:in `run_cycles'
     # ./spec/foo_widget_spec.rb:10:in `(root)'

     Simulation cycle count: 78963

     Simulation stack trace:
     0x01A0 = startMotor
     0x0044 = motorService+0x14
     0x0B12 = mainLoop+0x2
     0x008C = start2

Finished in 4.55 seconds
44 examples, 1 failure

Failed examples:

rspec ./spec/example/nice_error_spec.rb:8 # FooWidget when exposed to 1.5ms pulses behaves correctly

In this example, we had a persistent expectation asserting something about the INTCON SFR and and at some point in a lengthy integration test our expectation failed. The “Simulation cycle count” shows us the value of RPicSim::Sim#cycle_count at the time that the error happened. The “Simulation stack trace” shows us what addresses were on the device's call stack. (Actually, the call stack stores the addresses the process will return to, but this stack trace shows the addresses where calls occurred, which is one or two less than the return address.) This information can help when you are debugging issues.

Better RSpec error messages

If you are using RSpec 2.x, RPicSim also overrides some of RSpec's error messages to be better.

For example, instead of just saying an error message like “expected driving_high? to return true, got false”, RSpec will actually say what object it called driving_high? on. This feature is important when you are using persistent expectations and want to know which expectation failed, because the stack trace will not help.