The Rubinius project generally uses TDD/BDD-style executable specifications to drive development. The Rubinius spec directory is generally conceptually divided into two parts:

  1. All the files under spec/ruby that describe the behavior of MatzRuby
  2. And all the other files under the spec directory that describe the behavior of Rubinius

Since the Rubinius spec directory is a hybrid, see Howto - Develop with a separate RubySpec repo for more explicit, step-by-step instructions.

Organization

The Rubinius specs follow the same general organization (although in different directories) and style as the RubySpecs. So, a good place to start is by reading through the RubySpec documentation.

Ruby specs

The Rubinius repository contains two copies of the RubySpecs. The spec/frozen directory is a git submodule of the RubySpec repository. The submodule is literally frozen to a particular revision of the RubySpecs. This revision is synchronized with the tag files in the spec/tags/frozen directory. The spec/frozen files are used by the CI (continuous integration) runner (@mspec/bin/mspec-ci@, invoked by rake spec or bin/mspec ci). The CI specs are run before any changes are pushed to ensure that no new code causes regressions. Since Rubinius does not pass all existing RubySpecs, the tags in the spec/tags/frozen directory exclude known failures.

The Rubinius repository also contains a clone of the RubySpecs repository in the spec/ruby directory. This clone is included for the convenience of making changes to the RubySpec files directly.

Rubinius specs

All the other directories under the spec directory except for spec/ruby and spec/frozen are for specs specific to Rubinius. The directories are self-explanatory. For example, the spec/compiler directory is for specs for the Rubinius compiler.

Three directories could be confusing so those are described in more detail here.

spec/core

This directory is for Rubinius specific extensions to MatzRuby core library. For example, Rubinius has a Tuple and ByteArray class. The specs for these are under the spec/core directory. This directory is also for Rubinius specific extensions to methods of the MatzRuby core library classes. For example, Rubinius handles coercion of Bignum and other numeric differently. The spec/core directory parallels the purpose of the spec/ruby/1.8/core directory from the RubySpecs.

spec/language

Again, this directory contains Rubinius specific specs related to the Ruby language. The directory parallels the spec/ruby/1.8/language directory.

spec/library

This directory contains Rubinius specific standard library classes (e.g. Actor, VMActor) and Rubinius specific behavior of methods of MatzRuby standard library classes.

Subtend

Subtend is the Rubinius component that provides a C API compatible with MRI. Writing specs for the subtend functions involves writing a C extension. Review the following example for doing this. Note the following:

  1. The specs are wrapped in an "extension :rubinius" block to only be executed by Rubinius.
  2. The "compile_extension" helper is used to compile the C extension.
  3. The C extension is required after it is compiled.
  4. The rb_ary_xxx methods on SubtendArray are not the C rb_ary_xxx methods. They are just a convention that helps the specs document which C function is being referenced.
  5. The rb_ary_xxx methods are defined in the C extension and map to sa_ary_xxx functions.

(Note: This example is only partially accurate for how the existing specs are written. However, this documents the expected conventions. The actual specs will be updated shortly.)

spec/subtend/array_spec.rb

require File.dirname(__FILE__) + '/../spec_helper'
require File.dirname(__FILE__) + '/subtend_helper'

compile_extension('subtend_array')
require File.dirname(__FILE__) + '/ext/subtend_array'

describe "SubtendArray" do
  before :each do
    @s = SubtendArray.new
  end

  it "rb_ary_new should return an empty array" do
    @s.new_array.should == []
  end

  it "rb_ary_push should add an element to an array" do
    @s.rb_ary_push([], 4).should == [4]
  end

  it "rb_ary_push2 should add elements to an array" do
    o = mock('x')
    @s.rb_ary_push2([], 4, o).should == [4,o]
  end

  it "rb_ary_pop should remove the last element in the array and return it" do
    a = [1,2,3]
    @s.rb_ary_pop(a).should == 3
    a.should == [1,2]
  end
  
  it "rb_ary_join should join elements of an array with a string" do
    a = [1,2,3]
    b = ","
    @s.rb_ary_join(a,b).should == "1,2,3"
  end

  # ...

end

spec/subtend/ext/subtend_array.c

  static VALUE sa_array_store(VALUE self, VALUE array, VALUE offset, VALUE value) {
    rb_ary_store(array, FIX2INT(offset), value);

    return Qnil;
  }

  void Init_subtend_array() {
    VALUE cls;
    cls = rb_define_class("SubtendArray", rb_cObject);
    rb_define_method(cls, "new_array", sa_new_array, 0);
    rb_define_method(cls, "rb_ary_store", sa_array_store, 3);
  }  

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

0.1069% complete

 

Completed 9 of 13 tickets