Guards
Version 28 (Brian Ford, 02/06/2009 04:24 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 |
| 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 | 26 | Brian Ford | Different versions of Ruby have some methods that behave differently. That sounds circular, but it is the essence of software versions. To handle different version behaviors, MSpec provides the |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 | 27 | Brian Ford | The above guard will NOT yield to the block on any version of the _standard_ less than or equal to 1.8.6 patchlevel 114. |
| 123 | 27 | Brian Ford | |
| 124 | 15 | Brian Ford | The @ruby_bug@ guard serves three purposes: |
| 125 | 6 | Federico Builes | |
| 126 | 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. |
| 127 | 15 | Brian Ford | # It provides the correct behavior description for all implementations |
| 128 | 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. |
| 129 | 27 | Brian Ford | |
| 130 | 27 | Brian Ford | If it is determined that the behavior is not a bug but rather a version difference, the @ruby_bug@ guard should be replaced by an appropriate |
| 131 | 6 | Federico Builes | |
| 132 | 15 | Brian Ford | h3. 4. Implementations |
| 133 | 2 | Eero Saynatkari | |
| 134 | 19 | Brian Ford | Ideally, all Ruby implementations would have exactly the same behaviors. This is not realistically possible given differences in the underlying technology, for instance, whether the process @fork@ facility is available. |
| 135 | 19 | Brian Ford | |
| 136 | 19 | Brian Ford | It should be obvious, but it bears repeating, that in a specification for a standard, there should be an absolute minimum of incompatible behavior. These guards are not provided to make it easy to be inconsistent. Rather, they are provided to make specifying a single standard as simple as possible, recognizing that 100% conformity is an impossible ideal. |
| 137 | 19 | Brian Ford | |
| 138 | 28 | Brian Ford | Recall that the purpose of the guards are _both_ to control which specs are run AND document the specs. There are four distinct situations covered by the five guards below. Each of the guards documents this difference. |
| 139 | 19 | Brian Ford | |
| 140 | 16 | Brian Ford | # compliant_on |
| 141 | 16 | Brian Ford | # not_compliant_on |
| 142 | 16 | Brian Ford | # not_supported_on |
| 143 | 16 | Brian Ford | # deviates_on |
| 144 | 16 | Brian Ford | # extended_on |
| 145 | 2 | Eero Saynatkari | |
| 146 | 24 | Brian Ford | Keep in mind that the arguments to these guards are communal property (as are all the specs) and respect them as you would want to be respected. There is no concept of opt-in or opt-out here. Every implementation is responsible for ensuring that their implementation's behavior is accurately represented in one of these compliance scenarios. If an implementation has an excessive number of non-compliant behaviors, this will be clearly visible in the specs. |
| 147 | 24 | Brian Ford | |
| 148 | 21 | Brian Ford | h4. 4.1 compliant_on / not_compliant_on |
| 149 | 21 | Brian Ford | |
| 150 | 20 | Brian Ford | The @compliant_on@ and |
| 151 | 19 | Brian Ford | |
| 152 | 20 | Brian Ford | |
| 153 | 20 | Brian Ford | compliant_on :jruby, :rubinius do |
| 154 | 20 | Brian Ford | it "returns true" do |
| 155 | 20 | Brian Ford | end |
| 156 | 20 | Brian Ford | end |
| 157 | 20 | Brian Ford | |
| 158 | 15 | Brian Ford | |
| 159 | 22 | Brian Ford | The spec above will run ONLY on the _standard_, JRuby, or Rubinius. The @compliant_on@ guard will not run on any other implementation or platform except the ones listed. |
| 160 | 1 | ||
| 161 | 21 | Brian Ford | There is no need to list :ruby or :ruby19 for this guard. There is only one standard and specs are _always_ expected to run correctly on the _standard_. If there is version-specific behavior or a bug in the _standard_, use the @ruby_bug@ or |
| 162 | 20 | Brian Ford | |
| 163 | 20 | Brian Ford | |
| 164 | 20 | Brian Ford | not_compliant_on :rubinius do |
| 165 | 20 | Brian Ford | it "returns false" do |
| 166 | 20 | Brian Ford | end |
| 167 | 20 | Brian Ford | end |
| 168 | 20 | Brian Ford | |
| 169 | 20 | Brian Ford | |
| 170 | 20 | Brian Ford | The spec above will run on EVERY implementation except Rubinius. |
| 171 | 20 | Brian Ford | |
| 172 | 1 | It should be obvious that the @compliant_on@ guard is more convenient when the number of implementations that conform to the standard is small compared to the total number of implementations. The |
|
| 173 | 1 | ||
| 174 | 22 | Brian Ford | It is reasonable to assume that if there is a |
| 175 | 22 | Brian Ford | |
| 176 | 21 | Brian Ford | h4. 4.2 not_supported_on |
| 177 | 21 | Brian Ford | |
| 178 | 23 | Brian Ford | The |
| 179 | 21 | Brian Ford | |
| 180 | 21 | Brian Ford | |
| 181 | 21 | Brian Ford | not_supported_on :jruby do |
| 182 | 21 | Brian Ford | it "forks the process" do |
| 183 | 21 | Brian Ford | end |
| 184 | 21 | Brian Ford | end |
| 185 | 21 | Brian Ford | |
| 186 | 1 | ||
| 187 | 22 | Brian Ford | The above spec will run on EVERY implementation _except_ JRuby. |
| 188 | 22 | Brian Ford | |
| 189 | 22 | Brian Ford | The key difference between |
| 190 | 22 | Brian Ford | |
| 191 | 21 | Brian Ford | h4. 4.3 deviates_on |
| 192 | 1 | ||
| 193 | 23 | Brian Ford | If an implementation does not conform to the _standard_ behavior but instead offers an alternative behavior, the spec illustrating that is wrapped in a @deviates_on@ guard. |
| 194 | 1 | ||
| 195 | 23 | Brian Ford | |
| 196 | 23 | Brian Ford | deviates_on :rubinius do |
| 197 | 23 | Brian Ford | it "coerces to two Bignums" do |
| 198 | 23 | Brian Ford | end |
| 199 | 23 | Brian Ford | end |
| 200 | 23 | Brian Ford | |
| 201 | 23 | Brian Ford | |
| 202 | 23 | Brian Ford | The above spec will run ONLY on Rubinius. More than one implementation can be listed. |
| 203 | 23 | Brian Ford | |
| 204 | 21 | Brian Ford | h4. 4.4 extended_on |
| 205 | 1 | ||
| 206 | 23 | Brian Ford | If an implementation offers a behavior that does not exist at all in the standard, the spec illustrating that behavior is wrapped in an @extended_on@ guard. |
| 207 | 23 | Brian Ford | |
| 208 | 23 | Brian Ford | |
| 209 | 23 | Brian Ford | extended_on :rubinius do |
| 210 | 23 | Brian Ford | it "returns an immutable vector" do |
| 211 | 23 | Brian Ford | end |
| 212 | 23 | Brian Ford | end |
| 213 | 23 | Brian Ford | |
| 214 | 23 | Brian Ford | |
| 215 | 23 | Brian Ford | The above spec will run ONLY on Rubinius. More than one implementation can be listed. |
| 216 | 20 | Brian Ford | |
| 217 | 16 | Brian Ford | h3. 5. Environments |
| 218 | 18 | Brian Ford | |
| 219 | 16 | Brian Ford | The following guards are broadly grouped by their relation to how the specs are run. This is referred to as the spec _environment_. It includes guards for which runner (e.g. RSpec or MSpec) is executing the specs, which classes are loaded when the specs run, and whether the process running the specs has superuser privileges. |
| 220 | 16 | Brian Ford | |
| 221 | 16 | Brian Ford | # runner_is |
| 222 | 16 | Brian Ford | # runner_is_not |
| 223 | 1 | # conflicts_with |
|
| 224 | 20 | Brian Ford | # as_superuser |
| 225 | 1 | # quarantine! |
|
| 226 | 22 | Brian Ford | |
| 227 | 22 | Brian Ford | h4. 5.1 runner_is / runner_is_not |
| 228 | 22 | Brian Ford | |
| 229 | 25 | Brian Ford | These two guards were initially added to protect specs whose behavior would change in the presence of certain standard library classes like Rational. This situation has been taken over by the @conflicts_with@ guard below. These guards should now only be used if the particular runner framework itself is an issue. |
| 230 | 22 | Brian Ford | |
| 231 | 25 | Brian Ford | |
| 232 | 25 | Brian Ford | runner_is :rspec do |
| 233 | 25 | Brian Ford | it "does something that only runs under RSpec" do |
| 234 | 25 | Brian Ford | end |
| 235 | 25 | Brian Ford | end |
| 236 | 25 | Brian Ford | |
| 237 | 25 | Brian Ford | runner_is :mspec do |
| 238 | 25 | Brian Ford | it "does something that only runs under MSpec" do |
| 239 | 25 | Brian Ford | end |
| 240 | 25 | Brian Ford | end |
| 241 | 25 | Brian Ford | |
| 242 | 25 | Brian Ford | |
| 243 | 25 | Brian Ford | |
| 244 | 22 | Brian Ford | h4. 5.2 conflicts_with |
| 245 | 22 | Brian Ford | |
| 246 | 25 | Brian Ford | This guard wraps specs for methods whose behavior may be changed incompatibly by certain other classes. |
| 247 | 22 | Brian Ford | |
| 248 | 25 | Brian Ford | |
| 249 | 25 | Brian Ford | conflicts_with :SomeClass do |
| 250 | 25 | Brian Ford | it "returns an Integer" do |
| 251 | 25 | Brian Ford | end |
| 252 | 25 | Brian Ford | end |
| 253 | 25 | Brian Ford | |
| 254 | 25 | Brian Ford | |
| 255 | 25 | Brian Ford | The above guard will NOT yield if SomeClass is defined. |
| 256 | 25 | Brian Ford | |
| 257 | 22 | Brian Ford | h4. 5.3 as_superuser |
| 258 | 22 | Brian Ford | |
| 259 | 25 | Brian Ford | Some Ruby methods will only behave as expected if the process running the code example has superuser privileges. |
| 260 | 25 | Brian Ford | |
| 261 | 25 | Brian Ford | |
| 262 | 25 | Brian Ford | as_superuser do |
| 263 | 25 | Brian Ford | describe "File.chown" do |
| 264 | 25 | Brian Ford | end |
| 265 | 25 | Brian Ford | end |
| 266 | 25 | Brian Ford | |
| 267 | 25 | Brian Ford | |
| 268 | 25 | Brian Ford | The guard above will only yield if |
| 269 | 22 | Brian Ford | |
| 270 | 22 | Brian Ford | h4. 5.4 quarantine! |
| 271 | 1 | ||
| 272 | 19 | Brian Ford | The |
| 273 | 17 | Brian Ford | |
| 274 | 17 | Brian Ford | If a spec exposes a bug that is causing a segfault, the @ruby_bug@ guard should be used. |
| 275 | 1 | ||
| 276 | 18 | Brian Ford | |
| 277 | 17 | Brian Ford | quarantine! do |
| 278 | 17 | Brian Ford | it "does something that causes a segfault" do |
| 279 | 1 | end |
|
| 280 | 17 | Brian Ford | end |
| 281 | 18 | Brian Ford |
