Home | History | Annotate | Download | only in test
      1 #!/usr/bin/ruby
      2 # encoding: utf-8
      3 
      4 require 'antlr3'
      5 require 'antlr3/test/core-extensions'
      6 require 'antlr3/test/grammar'
      7 require 'antlr3/test/call-stack'
      8 
      9 require 'test/unit'
     10 require 'spec'
     11 
     12 module ANTLR3
     13 module Test
     14 module Location
     15   attr_accessor :test_path
     16   
     17   def test_group
     18     File.basename( test_path, '.rb' )
     19   end
     20   
     21   def test_directory
     22     File.dirname( test_path )
     23   end
     24   
     25   def local_path( *parts )
     26     File.join( test_directory, *parts )
     27   end
     28   
     29   def output_directory( name = test_group )
     30     local_path( name )
     31   end
     32   
     33 end # module Location
     34 
     35 module NameSpace
     36   
     37   #
     38   # import( ruby_file )   => [ new constants, ... ]
     39   # Read the source code from the path given by +ruby_file+ and
     40   # evaluate it within the class body. Return new constants
     41   # created in the class after the evaluation.
     42   # 
     43   def import( ruby_file )
     44     constants_before = constants
     45     class_eval( File.read( ruby_file ), ruby_file, 1 )
     46     constants - constants_before
     47   end
     48   
     49   def import_grammar_targets( grammar )
     50     for file in grammar.target_files
     51       import( file )
     52     end
     53   end
     54 end
     55 
     56 module GrammarManager
     57   include Location
     58   include NameSpace
     59   
     60   DEFAULT_COMPILE_OPTIONS = {}
     61   
     62   def add_default_compile_option( name, value )
     63     DEFAULT_COMPILE_OPTIONS[ name ] = value
     64   end
     65   module_function :add_default_compile_option
     66   
     67   if ANTLR_JAR = ENV[ 'ANTLR_JAR' ] || ANTLR3.antlr_jar
     68     add_default_compile_option( :antlr_jar, ANTLR_JAR )
     69     
     70     Grammar.global_dependency( ANTLR_JAR )
     71   end
     72   
     73   #
     74   # Compile and load inline grammars on demand when their constant name
     75   # is referenced in the code. This makes it easier to catch big errors
     76   # quickly as test cases are run, instead of waiting a few minutes
     77   # for all grammars to compile, and then discovering there's a big dumb
     78   # error ruining most of the grammars.
     79   # 
     80   def const_missing( name )
     81     if g = grammars[ name.to_s ]
     82       compile( g )
     83       grammars.delete( name.to_s )
     84       const_get( name )
     85     elsif superclass.respond_to?( :grammars )
     86       superclass.const_missing( name )
     87       # ^-- for some reason, in ruby 1.9, rspec runs examples as instances of
     88       # anonymous subclasses, of the actual test class, which messes up the
     89       # assumptions made in the test code. Grammars are stored in @grammars belonging
     90       # to the test class, so in 1.9, this method is called with @grammars = {}
     91       # since it's a subclass
     92     else
     93       super
     94     end
     95   end
     96   
     97   # 
     98   # An index of grammar file objects created in the test class
     99   # (defined inline or loaded from a file)
    100   # 
    101   def grammars
    102     @grammars ||= {}
    103   end
    104   
    105   def grammar_count
    106     grammars.length
    107   end
    108   
    109   def load_grammar( name )
    110     path = local_path( name.to_s )
    111     path =~ /\.g$/ or path << '.g'
    112     grammar = Grammar.new( path, :output_directory => output_directory )
    113     register_grammar( grammar )
    114     return grammar
    115   end
    116   
    117   def inline_grammar( source, options = {} )
    118     call = call_stack.find { |call| call.file != __FILE__ }
    119     grammar = Grammar.inline source,
    120                 :output_directory => output_directory,
    121                 :file => ( call.file rescue nil ),
    122                 :line => ( call.line rescue nil )
    123     register_grammar( grammar )
    124     return grammar
    125   end
    126   
    127   def compile_options( defaults = nil )
    128     @compile_options ||= DEFAULT_COMPILE_OPTIONS.clone
    129     @compile_options.update( defaults ) if defaults
    130     return @compile_options
    131   end
    132   
    133   def compile( grammar, options = {} )
    134     grammar.compile( compile_options.merge( options ) )
    135     import_grammar_targets( grammar )
    136     return grammar
    137   end
    138   
    139 private
    140   
    141   def register_grammar( grammar )
    142     name = grammar.name
    143     @grammars ||= {}
    144     
    145     if conflict = @grammars[ name ] and conflict.source != grammar.source
    146       message = "Multiple grammars exist with the name ``#{ name }''"
    147       raise NameError, message
    148     else
    149       @grammars[ name ] = grammar
    150     end
    151   end
    152 end # module GrammarManager
    153 
    154 class Functional < ::Test::Unit::TestCase
    155   extend GrammarManager
    156   
    157   def self.inherited( klass )
    158     super
    159     klass.test_path = call_stack[ 0 ].file
    160   end
    161   
    162   def local_path( *args )
    163     self.class.local_path( *args )
    164   end
    165   
    166   def test_path
    167     self.class.test_path
    168   end
    169   
    170   def output_directory
    171     self.class.output_directory
    172   end
    173   
    174   def inline_grammar( source )
    175     call = call_stack.find { |call| call.file != __FILE__ }
    176     grammar = Grammar.inline source,
    177                 :output_directory => output_directory,
    178                 :file => call.file,
    179                 :line => call.line
    180   end
    181   
    182   def compile_and_load( grammar, options = {} )
    183     self.class.compile( grammar, options )
    184   end
    185 end # class Functional
    186 
    187 
    188 
    189 module CaptureOutput
    190   require 'stringio'
    191   def output_buffer
    192     defined?( @output_buffer ) or @output_buffer = StringIO.new( '' )
    193     @output_buffer
    194   end
    195   
    196   def output
    197     output_buffer.string
    198   end
    199   
    200   def say( *args )
    201     output_buffer.puts( *args )
    202   end
    203   
    204   def capture( *args )
    205     output_buffer.write( *args )
    206   end
    207 end
    208 
    209 module RaiseErrors
    210   def emit_error_message( msg )
    211     # do nothing
    212   end
    213   
    214   def report_error( error )
    215     raise error
    216   end
    217 end
    218 
    219 module CollectErrors
    220   def reported_errors
    221     defined?( @reported_errors ) or @reported_errors = []
    222     return @reported_errors
    223   end
    224   
    225   def emit_error_message( msg )
    226     reported_errors << msg
    227   end
    228 end
    229 
    230 end # module Test
    231 end # module ANTLR3
    232