#
# This test program is contributed by   George Marrows
# Re: [Yarv-devel] Some tests	for test_jump.rb
#

require 'yarvtest/yarvtest'

class TestFlow < YarvTestBase
  def ae_flow(src, for_value=true)
    # Tracks flow through the code
    # A test like
    #   begin
    #   ensure
    #   end
    # gets transformed into
    #   a = []
    #   begin
    #     begin; a << 1
    #     ensure; a << 2
    #     end; a << 3
    #   rescue Exception
    #     a << 99
    #   end
    #   a
    # before being run. This tracks control flow through the code.
    
    cnt = 0
    src = src.gsub(/(\n|$)/) { "; $a << #{cnt+=1}\n" }
    src = "$a = []; begin; #{src}; rescue Exception; $a << 99; end; $a"
    
    if false#||true
      STDERR.puts
      STDERR.puts '#----'
      STDERR.puts src
      STDERR.puts '#----'
    end
    
    ae(src)
  end

  def test_while_with_ensure
    ae %q{
      a = []
      i = 0
      begin
        while i < 1
          i+=1
          begin
            begin
              next
            ensure
              a << :ok
            end
          ensure
            a << :ok2
          end
        end
      ensure
        a << :last
      end
    }
    ae %q{
      a = []
      i = 0
      begin
        while i < 1
          i+=1
          begin
            begin
              break
            ensure
              a << :ok
            end
          ensure
            a << :ok2
          end
        end
      ensure
        a << :last
      end
    }
    ae %q{
      a = []
      i = 0
      begin
        while i < 1
          if i>0
            break
          end
          i+=1
          begin
            begin
              redo
            ensure
              a << :ok
            end
          ensure
            a << :ok2
          end
        end
      ensure
        a << :last
      end
   }
  end
  
  def test_ensure_normal_flow
    ae_flow %{ 
      begin
      ensure
      end }
  end

  def test_ensure_exception
    ae_flow %{
      begin
        raise StandardError
      ensure
      end
    }
  end

  def test_break_in_block_runs_ensure
    ae_flow %{ 
      [1,2].each do
        begin
          break
        ensure
        end
      end
    }
  end

  def test_next_in_block_runs_ensure
    ae_flow %{ 
      [1,2].each do
        begin
          next
        ensure
        end
      end
    }
  end
  def test_return_from_method_runs_ensure
    ae_flow %{ 
      o = "test"
      def o.test(a)
        return a
      ensure
      end
      o.test(123)
    }
  end

  def test_break_from_ifunc
    ae %q{
      ["a"].inject("ng"){|x,y|
        break :ok
      }
    }
    ae %q{
      unless ''.respond_to? :lines
        class String
          def lines
            self
          end
        end
      end
      
      ('a').lines.map{|e|
        break :ok
      }
    }
    ae_flow %q{
      ["a"].inject("ng"){|x,y|
        break :ok
      }
    }
    ae_flow %q{
      ('a'..'b').map{|e|
        break :ok
      }
    }
  end
  
  def test_break_ensure_interaction1
    # make sure that any 'break state' set up in the VM is c
    # the time of the ensure
    ae_flow %{ 
      [1,2].each{
        break
      }
      begin
      ensure
      end
    }
  end
  
  def test_break_ensure_interaction2
    # ditto, different arrangement
    ae_flow %{ 
      begin
        [1,2].each do
          break
        end
      ensure
      end
    }
  end
  
  def test_break_through_2_ensures
    ae_flow %{ 
      [1,2].each do
        begin
          begin
            break
          ensure
          end
        ensure
        end
      end
    }
  end
  
  def test_ensure_break_ensure
    # break through an ensure; run 2nd normally
    ae_flow %{ 
      begin
        [1,2].each do
          begin
            break
          ensure
          end
        end
      ensure
      end
    }
  end
  
  def test_exception_overrides_break
    ae_flow %{ 
      [1,2].each do
        begin
          break
        ensure
          raise StandardError
        end
      end
    }
  end

  def test_break_overrides_exception
    ae_flow %{ 
      [1,2].each do
        begin
          raise StandardError
        ensure
          break
        end
      end
    }
    ae_flow %{ 
      [1,2].each do
        begin
          raise StandardError
        rescue
          break
        end
      end
    }
  end

  def test_break_in_exception
    ae_flow %q{
      i=0
      while i<3
        i+=1
        begin
        ensure
          break
        end
      end
    }
    ae_flow %q{
      i=0
      while i<3
        i+=1
        begin
          raise
        ensure
          break
        end
      end
    }
    ae_flow %q{
      i=0
      while i<3
        i+=1
        begin
          raise
        rescue
          break
        end
      end
    }
  end

  def test_next_in_exception
    return
    ae_flow %q{
      i=0
      while i<3
        i+=1
        begin
        ensure
          next
        end
      end
    }
    ae_flow %q{
      i=0
      while i<3
        i+=1
        begin
          raise
        ensure
          next
        end
      end
    }
    ae_flow %q{
      i=0
      while i<3
        i+=1
        begin
          raise
        rescue
          next
        end
      end
    }
  end

  def test_complex_break
    ae_flow %q{
      i = 0
      while i<3
        i+=1
        j = 0
        while j<3
          j+=1
          begin
            raise
          rescue
            break
          end
        end
      end
    }
    ae_flow %q{
      i = 0
      while i<3
        i+=1
        j = 0
        while j<3
          j+=1
          1.times{
            begin
              raise
            rescue
              break
            end
          }
        end
      end
    }
    ae_flow %q{
      i = 0
      while i<3
        i+=1
        j = 0
        while j<3
          j+=1
          begin
            raise
          ensure
            break
          end
        end
      end
    }
    ae_flow %q{
      i = 0
      while i<3
        i+=1
        j = 0
        while j<3
          j+=1
          1.times{
            begin
              raise
            ensure
              break
            end
          }
        end
      end
    }
    ae_flow %q{
      while true
        begin
          break
        ensure
          break
        end
      end
    }
    ae_flow %q{
      while true
        begin
          break
        ensure
          raise
        end
      end
    }
  end

  def test_jump_from_class
    ae_flow %q{
      3.times{
        class C
          break
        end
      }
    }
    ae_flow %q{
      3.times{
        class A
          class B
            break
          end
        end
      }
    }
    ae_flow %q{
      3.times{
        class C
          next
        end
      }
    }
    ae_flow %q{
      3.times{
        class C
          class D
            next
          end
        end
      }
    }
    ae_flow %q{
      while true
        class C
          break
        end
      end
    }
    ae_flow %q{
      while true
        class C
          class D
            break
          end
        end
      end
    }
    ae_flow %q{
      i=0
      while i<3
        i+=1
        class C
          next 10
        end
      end
    }
    ae %q{
      1.times{
        while true
          class C
            begin
              break
            ensure
              break
            end
          end
        end
      }
    }
  end

  def test_flow_with_cont_sp
    ae %q{
      def m a, b
        a + b
      end
      m(1,
        while true
          break 2
        end
        )
    }
    ae %q{
      def m a, b
        a + b
      end
      m(1,
        (i=0; while i<2
           i+=1
           class C
             next 2
           end
         end; 3)
        )
    }
    ae %q{
      def m a, b
        a+b
      end
      m(1, 1.times{break 3}) +
      m(10, (1.times{next 3}; 20))
    }
  end

  def test_return_in_deep_stack
    ae_flow %q{
      def m1 *args
        
      end
      def m2
        m1(:a, :b, (return 1; :c))
      end
      m2
    }
  end
  
  def test_return_in_ensure
    ae_flow %q{
      def m()
        begin
          2
        ensure
          return 3
        end
      end
      m
    }
    ae_flow %q{
      def m2
      end
      def m()
        m2(begin
             2
           ensure
             return 3
           end)
        4
      end
      m()
    }
    ae_flow %q{
      def m
        1
        1.times{
          2
          begin
            3
            return
            4
          ensure
            5
          end
          6
        }
        7
      end
      m()
    }
  end
end