Permalink
Browse files

Use consistent test style for examples

All example test suites are run with the default Rake test task. This
ensures that examples are always working with the current version of the
code.

Also, Citrus.require should raise Citrus::LoadError if it cannot find a
suitable file to load.
  • Loading branch information...
mjackson committed Jan 5, 2011
1 parent a901d1d commit 30e9072d0a28a88b8d44ae4ed79329906f7af307
Showing with 232 additions and 303 deletions.
  1. +18 −0 README
  2. +1 −1 Rakefile
  3. +2 −3 examples/calc.citrus
  4. +79 −72 examples/calc.rb
  5. +17 −75 examples/ipaddress.rb
  6. +49 −0 examples/ipv4address.rb
  7. +55 −0 examples/ipv6address.rb
  8. +9 −5 lib/citrus.rb
  9. +2 −4 lib/citrus/file.rb
  10. +0 −16 test/calc_file_test.rb
  11. +0 −11 test/calc_test.rb
  12. +0 −116 test/helper.rb
View
18 README
@@ -567,6 +567,24 @@ To install the [Vim](http://www.vim.org/) scripts, copy the files in
[runtimepath](http://vimdoc.sourceforge.net/htmldoc/options.html#\'runtimepath\').
+# Examples
+
+
+The project source directory contains several example scripts that demonstrate
+how grammars are to be constructed and used. Each Citrus file in the examples
+directory has an accompanying Ruby file with the same name that contains a suite
+of tests for that particular file.
+
+The best way to run any of these examples is to pass the name of the Ruby file
+directly to the Ruby interpreter on the command line, e.g.:
+
+ $ ruby -Ilib examples/calc.rb
+
+This particular invocation uses the `-I` flag to ensure that you are using the
+version of Citrus that was bundled with that particular example file (i.e. the
+version that is contained in the `lib` directory).
+
+
# Links
View
@@ -6,7 +6,7 @@ task :default => :test
# TESTS #######################################################################
Rake::TestTask.new(:test) do |t|
- t.test_files = FileList['test/*_test.rb']
+ t.test_files = FileList['test/*_test.rb'] + FileList['examples/*.rb']
end
# DOCS ########################################################################
View
@@ -1,8 +1,7 @@
# A grammar for mathematical formulas that apply basic mathematical operations
# to all numbers, respecting operator precedence and grouping of expressions
-# while ignoring whitespace.
-#
-# An identical grammar that is written using pure Ruby can be found in calc.rb.
+# while ignoring whitespace. This grammar should provide the same interpretation
+# as Ruby for all mathematical expressions.
grammar Calc
## Hierarchical syntax
View
@@ -1,114 +1,121 @@
+# This file contains a suite of tests for the Calc grammar found in calc.citrus.
+
require 'citrus'
+Citrus.require File.expand_path('../calc', __FILE__)
+require 'test/unit'
+
+class CalcTest < Test::Unit::TestCase
+ # A helper method that tests the successful parsing and evaluation of the
+ # given mathematical expression.
+ def do_test(expr)
+ match = ::Calc.parse(expr)
+ assert(match)
+ assert_equal(expr, match)
+ assert_equal(expr.length, match.length)
+ assert_equal(eval(expr), match.value)
+ end
+
+ def test_int
+ do_test('3')
+ end
-# A grammar for mathematical formulas that apply basic mathematical operations
-# to all numbers, respecting operator precedence and grouping of expressions
-# while ignoring whitespace.
-#
-# An identical grammar that is written using Citrus' own grammar syntax can be
-# found in calc.citrus.
-grammar :Calc do
+ def test_float
+ do_test('1.5')
+ end
- ## Hierarchical syntax
+ def test_addition
+ do_test('1+2')
+ end
- rule :term do
- any(:additive, :factor)
+ def test_addition_multi
+ do_test('1+2+3')
end
- rule :additive do
- all(:factor, :additive_operator, :term) {
- additive_operator.value(factor.value, term.value)
- }
+ def test_addition_float
+ do_test('1.5+3')
end
- rule :factor do
- any(:multiplicative, :prefix)
+ def test_subtraction
+ do_test('3-2')
end
- rule :multiplicative do
- all(:prefix, :multiplicative_operator, :factor) {
- multiplicative_operator.value(prefix.value, factor.value)
- }
+ def test_subtraction_float
+ do_test('4.5-3')
end
- rule :prefix do
- any(:prefixed, :exponent)
+ def test_multiplication
+ do_test('2*5')
end
- rule :prefixed do
- all(:unary_operator, :prefix) {
- unary_operator.value(prefix.value)
- }
+ def test_multiplication_float
+ do_test('1.5*3')
end
- rule :exponent do
- any(:exponential, :primary)
+ def test_division
+ do_test('20/5')
end
- rule :exponential do
- all(:primary, :exponential_operator, :prefix) {
- exponential_operator.value(primary.value, prefix.value)
- }
+ def test_division_float
+ do_test('4.5/3')
end
- rule :primary do
- any(:group, :number)
+ def test_complex
+ do_test('7*4+3.5*(4.5/3)')
end
- rule :group do
- all(:lparen, :term, :rparen) {
- term.value
- }
+ def test_complex_spaced
+ do_test('7 * 4 + 3.5 * (4.5 / 3)')
end
- ## Lexical syntax
+ def test_complex_with_underscores
+ do_test('(12_000 / 3) * 2.5')
+ end
- rule :number do
- any(:float, :integer)
+ def test_modulo
+ do_test('3 % 2 + 4')
end
- rule :float do
- all(:digits, '.', :digits, zero_or_more(:space)) {
- strip.to_f
- }
+ def test_exponent
+ do_test('2**9')
end
- rule :integer do
- all(:digits, zero_or_more(:space)) {
- strip.to_i
- }
+ def test_exponent_float
+ do_test('2**2.2')
end
- rule :digits do
- # Numbers may contain underscores in Ruby.
- /[0-9]+(?:_[0-9]+)*/
+ def test_negative_exponent
+ do_test('2**-3')
end
- rule :additive_operator do
- all(any('+', '-'), zero_or_more(:space)) { |a, b|
- a.send(strip, b)
- }
+ def test_exponent_exponent
+ do_test('2**2**2')
end
- rule :multiplicative_operator do
- all(any('*', '/', '%'), zero_or_more(:space)) { |a, b|
- a.send(strip, b)
- }
+ def test_exponent_group
+ do_test('2**(3+1)')
end
- rule :exponential_operator do
- all('**', zero_or_more(:space)) { |a, b|
- a ** b
- }
+ def test_negative
+ do_test('-5')
end
- rule :unary_operator do
- all(any('~', '+', '-'), zero_or_more(:space)) { |n|
- # Unary + and - require an @.
- n.send(strip == '~' ? strip : '%s@' % strip)
- }
+ def test_double_negative
+ do_test('--5')
end
- rule :lparen, ['(', zero_or_more(:space)]
- rule :rparen, [')', zero_or_more(:space)]
- rule :space, /[ \t\n\r]/
+ def test_complement
+ do_test('~4')
+ end
+
+ def test_double_complement
+ do_test('~~4')
+ end
+
+ def test_mixed_unary
+ do_test('~-4')
+ end
+
+ def test_complex_with_negatives
+ do_test('4 * -7 / (8.0 + 1_2)**2')
+ end
end
View
@@ -1,81 +1,23 @@
-# This file contains a small suite of tests for the grammars found in
-# ipaddress.citrus. If this file is run directly (i.e. using `ruby ip.rb') the
-# tests will run. Otherwise, this file may be required by another that needs
-# access to the IP address grammars just as any other file would be.
+examples = File.expand_path('..', __FILE__)
+$LOAD_PATH.unshift(examples) unless $LOAD_PATH.include?(examples)
-# Always use the current version of Citrus with this example.
-$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
+# This file contains a suite of tests for the IPAddress grammar found in
+# ipaddress.citrus.
require 'citrus'
+Citrus.require 'ipaddress'
+require 'test/unit'
+
+class IPAddressTest < Test::Unit::TestCase
+ def test_v4
+ match = IPAddress.parse('1.2.3.4')
+ assert(match)
+ assert_equal(4, match.version)
+ end
-# Make sure that the require statements in ip*address.citrus files can find
-# one another.
-$LOAD_PATH.unshift(File.expand_path('..', __FILE__))
-
-# Load and evaluate the grammars contained in ipaddress.citrus.
-Citrus.require('ipaddress')
-
-if $0 == __FILE__
- require 'test/unit'
-
- class IPAddressTest < Test::Unit::TestCase
- def test_dec_octet
- match = IPv4Address.parse('0', :root => :'dec-octet')
- assert(match)
-
- match = IPv4Address.parse('255', :root => :'dec-octet')
- assert(match)
- end
-
- def test_hexdig
- match = IPv6Address.parse('0', :root => :HEXDIG)
- assert(match)
-
- match = IPv6Address.parse('A', :root => :HEXDIG)
- assert(match)
- end
-
- def test_v4
- match = IPv4Address.parse('0.0.0.0')
- assert(match)
-
- match = IPv4Address.parse('255.255.255.255')
- assert(match)
-
- assert_raise Citrus::ParseError do
- IPv4Address.parse('255.255.255')
- end
- end
-
- def test_v6
- match = IPv6Address.parse('1:2:3:4:5:6:7:8')
- assert(match)
-
- match = IPv6Address.parse('12AD:34FC:A453:1922::')
- assert(match)
-
- match = IPv6Address.parse('12AD::34FC')
- assert(match)
-
- match = IPv6Address.parse('12AD::')
- assert(match)
-
- match = IPv6Address.parse('::')
- assert(match)
-
- assert_raise Citrus::ParseError do
- IPv6Address.parse('1:2')
- end
- end
-
- def test_all
- match = IPAddress.parse('1.2.3.4')
- assert(match)
- assert_equal(4, match.version)
-
- match = IPAddress.parse('1:2:3:4::')
- assert(match)
- assert_equal(6, match.version)
- end
+ def test_v6
+ match = IPAddress.parse('1:2:3:4::')
+ assert(match)
+ assert_equal(6, match.version)
end
end
View
@@ -0,0 +1,49 @@
+examples = File.expand_path('..', __FILE__)
+$LOAD_PATH.unshift(examples) unless $LOAD_PATH.include?(examples)
+
+# This file contains a suite of tests for the IPv4Address grammar found in
+# ipv4address.citrus.
+
+require 'citrus'
+Citrus.require 'ipv4address'
+require 'test/unit'
+
+class IPv4AddressTest < Test::Unit::TestCase
+ def test_dec_octet
+ match = IPv4Address.parse('0', :root => :'dec-octet')
+ assert(match)
+
+ match = IPv4Address.parse('255', :root => :'dec-octet')
+ assert(match)
+ end
+
+ def test_1
+ match = IPv4Address.parse('0.0.0.0')
+ assert(match)
+ assert_equal(4, match.version)
+ end
+
+ def test_2
+ match = IPv4Address.parse('255.255.255.255')
+ assert(match)
+ assert_equal(4, match.version)
+ end
+
+ def test_invalid
+ assert_raise Citrus::ParseError do
+ IPv4Address.parse('255.255.255.256')
+ end
+ end
+
+ def test_invalid_short
+ assert_raise Citrus::ParseError do
+ IPv4Address.parse('255.255.255')
+ end
+ end
+
+ def test_invalid_long
+ assert_raise Citrus::ParseError do
+ IPv4Address.parse('255.255.255.255.255')
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 30e9072

Please sign in to comment.