mvz-live_ast 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES.rdoc +93 -0
  3. data/README.rdoc +419 -0
  4. data/Rakefile +21 -0
  5. data/devel/levitate.rb +853 -0
  6. data/devel/levitate_config.rb +4 -0
  7. data/lib/live_ast.rb +4 -0
  8. data/lib/live_ast/ast_eval.rb +11 -0
  9. data/lib/live_ast/ast_load.rb +15 -0
  10. data/lib/live_ast/base.rb +73 -0
  11. data/lib/live_ast/common.rb +48 -0
  12. data/lib/live_ast/error.rb +20 -0
  13. data/lib/live_ast/evaler.rb +32 -0
  14. data/lib/live_ast/full.rb +2 -0
  15. data/lib/live_ast/irb_spy.rb +43 -0
  16. data/lib/live_ast/linker.rb +122 -0
  17. data/lib/live_ast/loader.rb +60 -0
  18. data/lib/live_ast/reader.rb +26 -0
  19. data/lib/live_ast/replace_eval.rb +121 -0
  20. data/lib/live_ast/replace_load.rb +14 -0
  21. data/lib/live_ast/replace_raise.rb +18 -0
  22. data/lib/live_ast/ruby_parser.rb +36 -0
  23. data/lib/live_ast/ruby_parser/test.rb +197 -0
  24. data/lib/live_ast/ruby_parser/unparser.rb +13 -0
  25. data/lib/live_ast/to_ast.rb +26 -0
  26. data/lib/live_ast/to_ruby.rb +24 -0
  27. data/lib/live_ast/version.rb +3 -0
  28. data/test/ast_eval_feature_test.rb +11 -0
  29. data/test/ast_load_feature_test.rb +11 -0
  30. data/test/attr_test.rb +24 -0
  31. data/test/backtrace_test.rb +158 -0
  32. data/test/covert_define_method_test.rb +23 -0
  33. data/test/def_test.rb +35 -0
  34. data/test/define_method_test.rb +67 -0
  35. data/test/define_singleton_method_test.rb +15 -0
  36. data/test/encoding_test.rb +52 -0
  37. data/test/encoding_test/bad.rb +1 -0
  38. data/test/encoding_test/cp932.rb +6 -0
  39. data/test/encoding_test/default.rb +5 -0
  40. data/test/encoding_test/eucjp.rb +6 -0
  41. data/test/encoding_test/koi8.rb +6 -0
  42. data/test/encoding_test/koi8_shebang.rb +7 -0
  43. data/test/encoding_test/koi8_with_utf8bom.rb +6 -0
  44. data/test/encoding_test/usascii.rb +6 -0
  45. data/test/encoding_test/usascii_with_utf8bom.rb +6 -0
  46. data/test/encoding_test/utf8.rb +6 -0
  47. data/test/encoding_test/utf8bom.rb +6 -0
  48. data/test/encoding_test/utf8bom_only.rb +5 -0
  49. data/test/encoding_test/utf8dos.rb +6 -0
  50. data/test/encoding_test/utf8mac.rb +6 -0
  51. data/test/encoding_test/utf8mac_alt.rb +6 -0
  52. data/test/encoding_test/utf8unix.rb +6 -0
  53. data/test/error_test.rb +116 -0
  54. data/test/eval_test.rb +269 -0
  55. data/test/flush_cache_test.rb +98 -0
  56. data/test/irb_test.rb +25 -0
  57. data/test/lambda_test.rb +56 -0
  58. data/test/load_path_test.rb +78 -0
  59. data/test/load_test.rb +123 -0
  60. data/test/main.rb +140 -0
  61. data/test/nested_test.rb +29 -0
  62. data/test/noninvasive_test.rb +51 -0
  63. data/test/readme_test.rb +16 -0
  64. data/test/recursive_eval_test.rb +52 -0
  65. data/test/redefine_method_test.rb +83 -0
  66. data/test/reload_test.rb +105 -0
  67. data/test/replace_eval_test.rb +405 -0
  68. data/test/rubygems_test.rb +25 -0
  69. data/test/rubyspec_test.rb +39 -0
  70. data/test/singleton_test.rb +25 -0
  71. data/test/stdlib_test.rb +13 -0
  72. data/test/thread_test.rb +44 -0
  73. data/test/to_ast_feature_test.rb +15 -0
  74. data/test/to_ruby_feature_test.rb +15 -0
  75. data/test/to_ruby_test.rb +87 -0
  76. metadata +275 -0
@@ -0,0 +1,98 @@
1
+ require_relative 'main'
2
+
3
+ # test for flushing side-effects: unsort this TestCase from other
4
+ # TestCases.
5
+
6
+ define_unsorted_test_case "FlushCacheTest", RegularTest do
7
+ def test_cache
8
+ # testing global state of cache -- must be sequential
9
+ uncached_method_from_require
10
+ uncached_method_from_eval
11
+ cached_method_from_eval
12
+ lost_method_from_require
13
+ flush_lambda
14
+ end
15
+
16
+ def uncached_method_from_require
17
+ klass = Class.new do
18
+ def f ; end
19
+ def g ; end
20
+ end
21
+
22
+ LiveAST.flush_cache
23
+
24
+ #
25
+ # file never made it into the cache; unaffected by flush
26
+ #
27
+ assert_nothing_raised do
28
+ klass.instance_method(:g).to_ast
29
+ end
30
+ end
31
+
32
+ def uncached_method_from_eval
33
+ klass = Class.new do
34
+ ast_eval %{
35
+ def f ; end
36
+ def g ; end
37
+ }, binding
38
+ end
39
+
40
+ LiveAST.flush_cache
41
+
42
+ assert_raises LiveAST::ASTNotFoundError do
43
+ klass.instance_method(:g).to_ast
44
+ end
45
+ end
46
+
47
+ def cached_method_from_eval
48
+ klass = Class.new do
49
+ ast_eval %{
50
+ def f ; end
51
+ def g ; end
52
+ }, binding
53
+ end
54
+
55
+ f_ast = klass.instance_method(:f).to_ast
56
+
57
+ LiveAST.flush_cache
58
+
59
+ assert_equal f_ast.object_id,
60
+ klass.instance_method(:f).to_ast.object_id
61
+
62
+ assert_raises LiveAST::ASTNotFoundError do
63
+ klass.instance_method(:g).to_ast
64
+ end
65
+ end
66
+
67
+ def lost_method_from_require
68
+ klass = Class.new do
69
+ def f ; end
70
+ def g ; end
71
+ end
72
+
73
+ # check that previous flushing did not cause side effect
74
+ assert_nothing_raised do
75
+ klass.instance_method(:f).to_ast
76
+ end
77
+ end
78
+
79
+ def flush_lambda
80
+ a, b = ast_eval %{
81
+ [
82
+ lambda { "aaa" },
83
+ lambda { "bbb" },
84
+ ]
85
+ }, binding
86
+
87
+ a_ast = a.to_ast
88
+ assert_equal no_arg_block(:lambda, "aaa"), a_ast
89
+
90
+ LiveAST.flush_cache
91
+
92
+ assert_equal a_ast.object_id, a.to_ast.object_id
93
+
94
+ assert_raises LiveAST::ASTNotFoundError do
95
+ b.to_ast
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,25 @@
1
+ require_relative 'main'
2
+
3
+ class IRBTest < RegularTest
4
+ def with_module(parent, child)
5
+ parent.const_set child, Module.new
6
+ begin
7
+ yield
8
+ ensure
9
+ parent.send :remove_const, child
10
+ end
11
+ end
12
+
13
+ def test_irb
14
+ with_module(Object, :IRB) do
15
+ with_module(LiveAST, :IRBSpy) do
16
+ LiveAST::IRBSpy.class_eval do
17
+ def self.code_at(line)
18
+ "def f ; end"
19
+ end
20
+ end
21
+ LiveAST::Linker.fetch_from_cache("(irb)", 1)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,56 @@
1
+ require_relative 'main'
2
+
3
+ class LambdaTest < RegularTest
4
+ def test_block_braces_multiline
5
+ block = return_block { |x, y|
6
+ x + y
7
+ }
8
+
9
+ expected = binop_block(:return_block, :+)
10
+ assert_equal expected, block.to_ast
11
+ end
12
+
13
+ def test_block_do_end_multiline
14
+ block = return_block do |x, y|
15
+ x * y
16
+ end
17
+
18
+ expected = binop_block(:return_block, :*)
19
+ assert_equal expected, block.to_ast
20
+ end
21
+
22
+ def test_lambda
23
+ a = lambda { |x, y| x - y }
24
+
25
+ expected = binop_block(:lambda, :-)
26
+ assert_equal expected, a.to_ast
27
+ end
28
+
29
+ def test_proc
30
+ a = proc { |x, y| x / y }
31
+
32
+ expected = binop_block(:proc, :/)
33
+ assert_equal expected, a.to_ast
34
+ end
35
+
36
+ def test_proc_new
37
+ a = Proc.new { |x, y| x + y }
38
+
39
+ expected = binop_proc_new(:+)
40
+ assert_equal expected, a.to_ast
41
+ end
42
+
43
+ def test_block_braces_one_line
44
+ block = return_block { |x, y| x * y }
45
+
46
+ expected = binop_block(:return_block, :*)
47
+ assert_equal expected, block.to_ast
48
+ end
49
+
50
+ def test_block_do_end_one_line
51
+ block = return_block do |x, y| x - y end
52
+
53
+ expected = binop_block(:return_block, :-)
54
+ assert_equal expected, block.to_ast
55
+ end
56
+ end
@@ -0,0 +1,78 @@
1
+ require_relative 'main'
2
+
3
+ class AAA_LoadPathTest < BaseTest
4
+ include FileUtils
5
+
6
+ def test_load_path
7
+ $LOAD_PATH.unshift DATA_DIR
8
+ begin
9
+ check_load
10
+ check_errors
11
+ temp_file nil, "foo.rb" do
12
+ Dir.chdir(DATA_DIR) do
13
+ compare_load_errors("/foo.rb")
14
+ end
15
+ end
16
+ ensure
17
+ $LOAD_PATH.shift
18
+ end
19
+ end
20
+
21
+ def test_chdir
22
+ mkdir DATA_DIR, :verbose => false rescue nil
23
+ Dir.chdir(DATA_DIR) do
24
+ check_load
25
+ check_errors
26
+ end
27
+ end
28
+
29
+ def check_load
30
+ code_1 = %{
31
+ def hello
32
+ "password"
33
+ end
34
+ }
35
+
36
+ code_2 = %{
37
+ def goodbye
38
+ "bubbleboy"
39
+ end
40
+ }
41
+
42
+ temp_file code_1, "foo.rb" do |path|
43
+ Object.send(:remove_method, :hello) rescue nil
44
+ load "foo.rb"
45
+ assert_equal "password", hello
46
+
47
+ write_file path, code_2
48
+
49
+ Object.send(:remove_method, :goodbye) rescue nil
50
+ LiveAST.load "foo.rb"
51
+ assert_equal "bubbleboy", goodbye
52
+ end
53
+ ensure
54
+ Object.send(:remove_method, :hello) rescue nil
55
+ Object.send(:remove_method, :goodbye) rescue nil
56
+ end
57
+
58
+ def compare_load_errors(file)
59
+ error = assert_raises LoadError do
60
+ LiveAST.load file
61
+ end
62
+ assert_equal "cannot load such file -- #{file}", error.message
63
+ end
64
+
65
+ def check_errors
66
+ temp_file "# do nothing", "foo.rb" do |path|
67
+ [
68
+ "foo",
69
+ "",
70
+ "/usr",
71
+ ".",
72
+ "..",
73
+ ].each do |file|
74
+ compare_load_errors(file)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,123 @@
1
+ require_relative 'main'
2
+ require_relative '../devel/levitate'
3
+
4
+ class AAA_LoadFileTest < BaseTest
5
+ class << self
6
+ attr_accessor :flag
7
+ end
8
+
9
+ def test_a_no_locals_created
10
+ code = %{
11
+ AAA_LoadFileTest.flag = :code_a
12
+ FOO = 77
13
+ x = 33
14
+ y = 99
15
+ }
16
+
17
+ temp_file code do |file|
18
+ ret = LiveAST.load file
19
+ assert_equal true, ret
20
+ assert_equal :code_a, AAA_LoadFileTest.flag
21
+
22
+ assert_raises NameError do
23
+ eval("x", TOPLEVEL_BINDING)
24
+ end
25
+
26
+ assert_equal 77, ::FOO
27
+ end
28
+ end
29
+
30
+ def test_b_no_locals_modified
31
+ code = %{
32
+ AAA_LoadFileTest.flag = :code_b
33
+ r = 55
34
+ }
35
+
36
+ temp_file code do |file|
37
+ eval("r = 66", TOPLEVEL_BINDING)
38
+
39
+ ret = LiveAST.load file
40
+ assert_equal true, ret
41
+ assert_equal :code_b, AAA_LoadFileTest.flag
42
+
43
+ actual = eval("r", TOPLEVEL_BINDING)
44
+ assert_equal 66, actual
45
+ end
46
+ end
47
+
48
+ def test_c_wrap
49
+ code = %{
50
+ AAA_LoadFileTest.flag = :code_c
51
+ ZOOM = 111
52
+ }
53
+
54
+ temp_file code do |file|
55
+ ret = LiveAST.load file, true
56
+ assert_equal true, ret
57
+ assert_equal :code_c, AAA_LoadFileTest.flag
58
+
59
+ assert_raises NameError do
60
+ ZOOM
61
+ end
62
+ end
63
+ end
64
+
65
+ def self.from_d
66
+ self.flag = :code_d
67
+ end
68
+
69
+ def test_d_empty_locals_list
70
+ code = %{
71
+ AAA_LoadFileTest.from_d
72
+ }
73
+
74
+ temp_file code do |file|
75
+ LiveAST.load file
76
+ assert_equal :code_d, AAA_LoadFileTest.flag
77
+ end
78
+ end
79
+
80
+ def test_verbose_respected
81
+ lib = File.expand_path(File.dirname(__FILE__) + "/../lib")
82
+
83
+ [
84
+ # respects a loaded file setting $VERBOSE = true
85
+ [
86
+ "false",
87
+ "true",
88
+ lambda { |file|
89
+ Levitate.run file
90
+ }
91
+ ],
92
+
93
+ # unfixable: does not respect a loaded file setting $VERBOSE = nil
94
+ [
95
+ "true",
96
+ "false",
97
+ lambda { |file|
98
+ unfixable do
99
+ assert_nothing_raised do
100
+ Levitate.run file
101
+ end
102
+ end
103
+ }
104
+ ]
105
+ ].each do |main_value, loaded_value, action|
106
+ loaded_code = %{
107
+ $VERBOSE = #{loaded_value}
108
+ }
109
+
110
+ temp_file loaded_code do |loaded_file|
111
+ main_code = %{
112
+ $LOAD_PATH.unshift '#{lib}'
113
+ require 'live_ast/base'
114
+ toplevel_local = 444
115
+ $VERBOSE = #{main_value}
116
+ LiveAST.load '#{loaded_file}'
117
+ $VERBOSE == #{loaded_value} or exit(1)
118
+ }
119
+ temp_file main_code, &action
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,140 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
2
+
3
+ # require first for stdlib_test
4
+ require 'pp'
5
+ require 'find'
6
+ require 'fileutils'
7
+
8
+ require 'minitest/unit'
9
+ require 'minitest/mock'
10
+ require 'minitest/autorun'
11
+
12
+ $VERBOSE = true
13
+
14
+ require 'live_ast/base'
15
+
16
+ def define_unsorted_test_case(name, superclass, &block)
17
+ klass = Class.new superclass, &block
18
+ letter = ('A'..'Z').to_a[rand(26)]
19
+ Object.const_set "#{letter}#{name}", klass
20
+ end
21
+
22
+ class JLMiniTest < MiniTest::Unit::TestCase
23
+ def self.test_methods
24
+ default = super
25
+ onlies = default.select { |m| m =~ %r!__only\Z! }
26
+ if onlies.empty?
27
+ default
28
+ else
29
+ puts "\nNOTE: running ONLY *__only tests for #{self}"
30
+ onlies
31
+ end
32
+ end
33
+
34
+ def delim(char)
35
+ "\n" << (char*72) << "\n"
36
+ end
37
+
38
+ def mu_pp(obj)
39
+ delim("_") <<
40
+ obj.pretty_inspect.chomp <<
41
+ delim("=")
42
+ end
43
+
44
+ def unfixable
45
+ begin
46
+ yield
47
+ raise "claimed to be unfixable, but assertion succeeded"
48
+ rescue MiniTest::Assertion
49
+ end
50
+ end
51
+
52
+ def assert_nothing_raised
53
+ yield
54
+ assert_nil nil
55
+ rescue => ex
56
+ raise MiniTest::Assertion,
57
+ exception_details(ex, "Expected nothing raised, but got:")
58
+ end
59
+
60
+ %w[
61
+ empty equal in_delta in_epsilon includes instance_of
62
+ kind_of match nil operator respond_to same
63
+ ].each { |name|
64
+ alias_method "assert_not_#{name}", "refute_#{name}"
65
+ }
66
+ end
67
+
68
+ class BaseTest < JLMiniTest
69
+ include LiveAST.parser::Test
70
+
71
+ DATA_DIR = File.expand_path(File.dirname(__FILE__) + "/data")
72
+
73
+ def self.stdlib_has_source?
74
+ RUBY_ENGINE != "jruby"
75
+ end
76
+
77
+ def temp_file(code, basename = nil)
78
+ basename ||= ('a'..'z').to_a.shuffle.join + ".rb"
79
+ path = DATA_DIR + "/" + basename
80
+ FileUtils.mkdir DATA_DIR unless File.directory? DATA_DIR
81
+
82
+ write_file path, code if code
83
+ begin
84
+ yield path
85
+ ensure
86
+ FileUtils.rm_f path
87
+ FileUtils.rmdir DATA_DIR rescue nil
88
+ end
89
+ end
90
+
91
+ def write_file(file, contents)
92
+ File.open(file, "w") { |f| f.print contents }
93
+ end
94
+
95
+ def return_block(&block)
96
+ block
97
+ end
98
+
99
+ def exception_backtrace
100
+ begin
101
+ yield
102
+ rescue Exception => e
103
+ e.backtrace
104
+ end
105
+ end
106
+
107
+ def ignore(*args)
108
+ end
109
+ end
110
+
111
+ class RegularTest < BaseTest
112
+ def setup
113
+ super
114
+ require 'live_ast'
115
+ end
116
+ end
117
+
118
+ class ReplaceEvalTest < BaseTest
119
+ def initialize(*args)
120
+ super
121
+ ok = begin
122
+ require 'live_ast/full'
123
+ true
124
+ rescue LoadError
125
+ if RUBY_ENGINE == "ruby"
126
+ raise "need: gem install boc"
127
+ end
128
+ false
129
+ end
130
+
131
+ unless ok
132
+ self.class.class_eval do
133
+ instance_methods(false).each do |m|
134
+ remove_method(m)
135
+ define_method(m) { }
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end