Guards

Version 16 (Brian Ford, 02/06/2009 12:56 PM)

1 1
h1. Guards
2 1
3 1
h2. Overview
4 1
5 9 Brian Ford
The RubySpec project intends to provide a complete and exhaustive specification of the Ruby language and its libraries.
6 9 Brian Ford
There is a single _standard_ implementation of Ruby. The _standard_ includes the stable, released versions available from http://ruby-lang.org. At present, the _standard_ is 1.8.6, 1.8.7, and 1.9.1. Collectively, the _standard_ is often referred to as MatzRuby or MRI.
7 9 Brian Ford
8 9 Brian Ford
The challenge for RubySpec is to correctly spec the different behaviors across versions, platforms, and implementations. To do this, the RubySpecs depend on _guards_ provided by MSpec. Guards are methods that may or may not take arguments and operate by yielding to a block if certain conditions are true. If the conditions for the guard are not true, the guard method does not yield to the block and the specs contained in the block are not run.
9 9 Brian Ford
10 11 Brian Ford
The guards serve two functions: 1) controlling which specs are run; 2) documenting the specs. The documentation function of the guards is as important for RubySpec as controlling which specs are run. Additionally, the guard structure itself was chosen to be visually and conceptually similar to the describe/it blocks in the specs.
11 1
12 12 Brian Ford
The guard blocks should be placed around @describe@ or @it@ blocks. Guards should _not_ be placed inside @it@ blocks. Guards should rarely, if ever, be placed inside @before@ or @after@ actions.
13 12 Brian Ford
14 11 Brian Ford
There are five categories of guards:
15 11 Brian Ford
16 9 Brian Ford
# versions
17 9 Brian Ford
# platforms
18 9 Brian Ford
# bugs
19 9 Brian Ford
# implementations
20 9 Brian Ford
# environments
21 9 Brian Ford
22 9 Brian Ford
The specific guards in each of these categories are explained below.
23 9 Brian Ford
24 9 Brian Ford
h3. 1. Versions
25 9 Brian Ford
26 9 Brian Ford
Different versions of Ruby have same methods that behave differently. That sounds circular, but it is the essence of software versions. To handle different version behaviors, MSpec provides the ruby_version_is guard.
27 9 Brian Ford
28 9 Brian Ford
29 9 Brian Ford
ruby_version_is "1.8.6.114" do
30 9 Brian Ford
  it "returns true" do
31 9 Brian Ford
  end
32 9 Brian Ford
end
33 9 Brian Ford
34 9 Brian Ford
ruby_version_is "1.8" .. "1.8.6" do
35 9 Brian Ford
  it "returns nil" do
36 9 Brian Ford
  end
37 9 Brian Ford
end
38 9 Brian Ford
39 9 Brian Ford
ruby_version_is "" ... "1.9" do
40 9 Brian Ford
  it "returns false" do
41 9 Brian Ford
  end
42 9 Brian Ford
end
43 9 Brian Ford
44 9 Brian Ford
45 10 Brian Ford
The ruby_version_is guard takes one argument that may be a string or a range of two strings. The format of the string is A.B.C.D, where:
46 10 Brian Ford
47 10 Brian Ford
* A is the major version
48 10 Brian Ford
* B is the minor version
49 10 Brian Ford
* C is the tiny (teeny) version
50 10 Brian Ford
* D is the patchlevel
51 10 Brian Ford
52 10 Brian Ford
The string is converted to a number on which comparisons between versions can be made so that, for instance, "1.8.6.37" is less than "1.8.6.112".
53 10 Brian Ford
54 10 Brian Ford
The range behaves as expected, respecting #exclude_end?. In the example above, "" ... "1.9" means every version *before* 1.9.
55 10 Brian Ford
56 9 Brian Ford
h3. 2. Platforms
57 1
58 11 Brian Ford
A single version of Ruby may have different behaviors depending on the platform on which it runs. MSpec provides several guards for these situations:
59 11 Brian Ford
60 11 Brian Ford
# big_endian
61 11 Brian Ford
# little_endian
62 11 Brian Ford
# platform_is
63 11 Brian Ford
# platform_is_not
64 9 Brian Ford
65 13 Brian Ford
The @big_endian@ guard yields to the block if the platform is big endian. Likewise for the @little_endian@ guard.
66 13 Brian Ford
67 13 Brian Ford
The platform_is and platform_is_not guards are more complex. As their names suggest, they are inverses.
68 13 Brian Ford
69 13 Brian Ford
70 13 Brian Ford
platform_is :linux, :bsd do
71 13 Brian Ford
  it "opens the file" do
72 13 Brian Ford
  end
73 13 Brian Ford
end
74 13 Brian Ford
75 13 Brian Ford
76 13 Brian Ford
The guard above will yield if RUBY_PLATFORM matches either "linux" OR "bsd".
77 13 Brian Ford
78 13 Brian Ford
79 13 Brian Ford
platform_is :linux, :wordsize => 32 do
80 13 Brian Ford
  it "opens the file" do
81 13 Brian Ford
  end
82 13 Brian Ford
end
83 13 Brian Ford
84 13 Brian Ford
85 13 Brian Ford
The guard above will yield if RUBY_PLATFORM matches "linux" AND the processor word size is 32-bit.
86 13 Brian Ford
87 13 Brian Ford
88 13 Brian Ford
platform_is_not :windows, :wordsize => 32 do
89 13 Brian Ford
  it "opens the file" do
90 13 Brian Ford
  end
91 13 Brian Ford
end
92 13 Brian Ford
93 13 Brian Ford
94 13 Brian Ford
The guard above will yield if RUBY_PLATFORM does not matches "windows" AND the processor word size is not 32-bit.
95 13 Brian Ford
96 13 Brian Ford
Special functionality exists for matching :windows and :java as platforms. For details, refer to the MSpec source.
97 13 Brian Ford
98 13 Brian Ford
99 14 Brian Ford
platform_is :os => [:darwin, :bsd] do
100 13 Brian Ford
  it "opens the file" do
101 13 Brian Ford
  end
102 13 Brian Ford
end
103 13 Brian Ford
104 13 Brian Ford
105 13 Brian Ford
The guard above will yield if Config::CONFIG['host_os'] matches either "darwin" OR "BSD". If Config::CONFIG['host_os'] is not set in rbconfig, the :os parameters will be matched against RUBY_PLATFORM instead.
106 13 Brian Ford
107 9 Brian Ford
h3. 3. Bugs
108 1
109 15 Brian Ford
Sometimes a bug is discovered in the _standard_. In this case, we do two things:
110 1
111 15 Brian Ford
# File a ticket on the "bug tracker":http://redmine.ruby-lang.org/ to find out if the suspected behavior is actually considered a bug.
112 15 Brian Ford
# Add a @ruby_bug@ guard that wraps the spec showing what is considered to be the _correct_ behavior.
113 2 Eero Saynatkari
114 15 Brian Ford
115 15 Brian Ford
ruby_bug "#5555", "1.8.6.114" do
116 15 Brian Ford
  it "returns the sum" do
117 15 Brian Ford
    (1 + 1).should == 2
118 7 Federico Builes
  end
119 6 Federico Builes
end
120 15 Brian Ford
121 6 Federico Builes
122 15 Brian Ford
The @ruby_bug@ guard serves three purposes:
123 6 Federico Builes
124 15 Brian Ford
# It documents that there is a bug in a particular version of the standard and refers to the ticket for that bug.
125 15 Brian Ford
# It provides the correct behavior description for all implementations
126 15 Brian Ford
# It prevents the spec for the correct behavior from running (and failing) on versions of the _standard_ implementation that have the bug and have already been released.
127 6 Federico Builes
128 15 Brian Ford
h3. 4. Implementations
129 2 Eero Saynatkari
130 16 Brian Ford
# compliant_on
131 16 Brian Ford
# not_compliant_on
132 16 Brian Ford
# not_supported_on
133 16 Brian Ford
# deviates_on
134 16 Brian Ford
# extended_on
135 2 Eero Saynatkari
136 15 Brian Ford
h3. 5. Environments
137 16 Brian Ford
138 16 Brian Ford
# runner_is
139 16 Brian Ford
# runner_is_not
140 16 Brian Ford
# as_superuser
141 16 Brian Ford
# conflicts_with
142 16 Brian Ford
# quarantine!