jinx 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. data/.gitignore +14 -0
  2. data/.rspec +3 -0
  3. data/.yardopts +1 -0
  4. data/Gemfile +6 -0
  5. data/Gemfile.lock +27 -0
  6. data/History.md +6 -0
  7. data/LEGAL +5 -0
  8. data/LICENSE +22 -0
  9. data/README.md +44 -0
  10. data/Rakefile +41 -0
  11. data/examples/family/README.md +10 -0
  12. data/examples/family/ext/build.xml +35 -0
  13. data/examples/family/ext/src/family/Address.java +68 -0
  14. data/examples/family/ext/src/family/Child.java +24 -0
  15. data/examples/family/ext/src/family/DomainObject.java +26 -0
  16. data/examples/family/ext/src/family/Household.java +36 -0
  17. data/examples/family/ext/src/family/Parent.java +48 -0
  18. data/examples/family/ext/src/family/Person.java +42 -0
  19. data/examples/family/lib/family.rb +15 -0
  20. data/examples/family/lib/family/address.rb +6 -0
  21. data/examples/family/lib/family/domain_object.rb +6 -0
  22. data/examples/family/lib/family/household.rb +6 -0
  23. data/examples/family/lib/family/parent.rb +16 -0
  24. data/examples/family/lib/family/person.rb +6 -0
  25. data/examples/model/README.md +25 -0
  26. data/examples/model/ext/build.xml +35 -0
  27. data/examples/model/ext/src/domain/Child.java +192 -0
  28. data/examples/model/ext/src/domain/Dependent.java +29 -0
  29. data/examples/model/ext/src/domain/DomainObject.java +26 -0
  30. data/examples/model/ext/src/domain/Independent.java +83 -0
  31. data/examples/model/ext/src/domain/Parent.java +129 -0
  32. data/examples/model/ext/src/domain/Person.java +14 -0
  33. data/examples/model/lib/model.rb +13 -0
  34. data/examples/model/lib/model/child.rb +13 -0
  35. data/examples/model/lib/model/domain_object.rb +6 -0
  36. data/examples/model/lib/model/independent.rb +11 -0
  37. data/examples/model/lib/model/parent.rb +17 -0
  38. data/jinx.gemspec +22 -0
  39. data/lib/jinx.rb +3 -0
  40. data/lib/jinx/active_support/README.txt +2 -0
  41. data/lib/jinx/active_support/core_ext/string.rb +7 -0
  42. data/lib/jinx/active_support/core_ext/string/inflections.rb +167 -0
  43. data/lib/jinx/active_support/inflections.rb +55 -0
  44. data/lib/jinx/active_support/inflector.rb +398 -0
  45. data/lib/jinx/cli/application.rb +36 -0
  46. data/lib/jinx/cli/command.rb +214 -0
  47. data/lib/jinx/helpers/array.rb +108 -0
  48. data/lib/jinx/helpers/boolean.rb +42 -0
  49. data/lib/jinx/helpers/case_insensitive_hash.rb +39 -0
  50. data/lib/jinx/helpers/class.rb +149 -0
  51. data/lib/jinx/helpers/collection.rb +33 -0
  52. data/lib/jinx/helpers/collections.rb +11 -0
  53. data/lib/jinx/helpers/collector.rb +20 -0
  54. data/lib/jinx/helpers/conditional_enumerator.rb +21 -0
  55. data/lib/jinx/helpers/enumerable.rb +242 -0
  56. data/lib/jinx/helpers/enumerate.rb +35 -0
  57. data/lib/jinx/helpers/error.rb +15 -0
  58. data/lib/jinx/helpers/file_separator.rb +65 -0
  59. data/lib/jinx/helpers/filter.rb +52 -0
  60. data/lib/jinx/helpers/flattener.rb +38 -0
  61. data/lib/jinx/helpers/hash.rb +12 -0
  62. data/lib/jinx/helpers/hashable.rb +502 -0
  63. data/lib/jinx/helpers/inflector.rb +36 -0
  64. data/lib/jinx/helpers/key_transformer_hash.rb +43 -0
  65. data/lib/jinx/helpers/lazy_hash.rb +44 -0
  66. data/lib/jinx/helpers/log.rb +106 -0
  67. data/lib/jinx/helpers/math.rb +12 -0
  68. data/lib/jinx/helpers/merge.rb +60 -0
  69. data/lib/jinx/helpers/module.rb +18 -0
  70. data/lib/jinx/helpers/multi_enumerator.rb +31 -0
  71. data/lib/jinx/helpers/options.rb +92 -0
  72. data/lib/jinx/helpers/os.rb +19 -0
  73. data/lib/jinx/helpers/partial_order.rb +37 -0
  74. data/lib/jinx/helpers/pretty_print.rb +207 -0
  75. data/lib/jinx/helpers/set.rb +8 -0
  76. data/lib/jinx/helpers/stopwatch.rb +76 -0
  77. data/lib/jinx/helpers/transformer.rb +24 -0
  78. data/lib/jinx/helpers/transitive_closure.rb +55 -0
  79. data/lib/jinx/helpers/uniquifier.rb +50 -0
  80. data/lib/jinx/helpers/validation.rb +33 -0
  81. data/lib/jinx/helpers/visitor.rb +370 -0
  82. data/lib/jinx/import/class_path_modifier.rb +77 -0
  83. data/lib/jinx/import/java.rb +337 -0
  84. data/lib/jinx/importer.rb +240 -0
  85. data/lib/jinx/metadata.rb +155 -0
  86. data/lib/jinx/metadata/attribute_enumerator.rb +73 -0
  87. data/lib/jinx/metadata/dependency.rb +244 -0
  88. data/lib/jinx/metadata/id_alias.rb +23 -0
  89. data/lib/jinx/metadata/introspector.rb +179 -0
  90. data/lib/jinx/metadata/inverse.rb +170 -0
  91. data/lib/jinx/metadata/java_property.rb +169 -0
  92. data/lib/jinx/metadata/propertied.rb +500 -0
  93. data/lib/jinx/metadata/property.rb +401 -0
  94. data/lib/jinx/metadata/property_characteristics.rb +114 -0
  95. data/lib/jinx/resource.rb +862 -0
  96. data/lib/jinx/resource/copy_visitor.rb +36 -0
  97. data/lib/jinx/resource/inversible.rb +90 -0
  98. data/lib/jinx/resource/match_visitor.rb +180 -0
  99. data/lib/jinx/resource/matcher.rb +20 -0
  100. data/lib/jinx/resource/merge_visitor.rb +73 -0
  101. data/lib/jinx/resource/mergeable.rb +185 -0
  102. data/lib/jinx/resource/reference_enumerator.rb +49 -0
  103. data/lib/jinx/resource/reference_path_visitor.rb +38 -0
  104. data/lib/jinx/resource/reference_visitor.rb +55 -0
  105. data/lib/jinx/resource/unique.rb +35 -0
  106. data/lib/jinx/version.rb +3 -0
  107. data/spec/defaults_spec.rb +30 -0
  108. data/spec/definitions/model/alias/child.rb +5 -0
  109. data/spec/definitions/model/base/child.rb +5 -0
  110. data/spec/definitions/model/base/domain_object.rb +5 -0
  111. data/spec/definitions/model/base/independent.rb +5 -0
  112. data/spec/definitions/model/defaults/child.rb +5 -0
  113. data/spec/definitions/model/dependency/child.rb +5 -0
  114. data/spec/definitions/model/dependency/parent.rb +6 -0
  115. data/spec/definitions/model/inverse/child.rb +5 -0
  116. data/spec/definitions/model/inverse/independent.rb +5 -0
  117. data/spec/definitions/model/inverse/parent.rb +5 -0
  118. data/spec/definitions/model/mandatory/child.rb +6 -0
  119. data/spec/dependency_spec.rb +47 -0
  120. data/spec/family_spec.rb +64 -0
  121. data/spec/inverse_spec.rb +53 -0
  122. data/spec/mandatory_spec.rb +43 -0
  123. data/spec/metadata_spec.rb +68 -0
  124. data/spec/resource_spec.rb +30 -0
  125. data/spec/spec_helper.rb +3 -0
  126. data/spec/support/model.rb +19 -0
  127. data/test/fixtures/line_separator/cr_line_sep.txt +1 -0
  128. data/test/fixtures/line_separator/crlf_line_sep.txt +3 -0
  129. data/test/fixtures/line_separator/lf_line_sep.txt +3 -0
  130. data/test/fixtures/mixed/ext/build.xml +35 -0
  131. data/test/fixtures/mixed/ext/src/mixed/Case/Example.java +5 -0
  132. data/test/helper.rb +7 -0
  133. data/test/lib/jinx/command_test.rb +41 -0
  134. data/test/lib/jinx/helpers/boolean_test.rb +27 -0
  135. data/test/lib/jinx/helpers/class_test.rb +60 -0
  136. data/test/lib/jinx/helpers/collections_test.rb +402 -0
  137. data/test/lib/jinx/helpers/file_separator_test.rb +29 -0
  138. data/test/lib/jinx/helpers/inflector_test.rb +11 -0
  139. data/test/lib/jinx/helpers/lazy_hash_test.rb +32 -0
  140. data/test/lib/jinx/helpers/module_test.rb +24 -0
  141. data/test/lib/jinx/helpers/options_test.rb +66 -0
  142. data/test/lib/jinx/helpers/partial_order_test.rb +41 -0
  143. data/test/lib/jinx/helpers/pretty_print_test.rb +83 -0
  144. data/test/lib/jinx/helpers/stopwatch_test.rb +16 -0
  145. data/test/lib/jinx/helpers/transitive_closure_test.rb +80 -0
  146. data/test/lib/jinx/helpers/visitor_test.rb +288 -0
  147. data/test/lib/jinx/import/java_test.rb +78 -0
  148. data/test/lib/jinx/import/mixed_case_test.rb +16 -0
  149. metadata +272 -0
@@ -0,0 +1,29 @@
1
+ require File.dirname(__FILE__) + '/../../../helper'
2
+ require 'test/unit'
3
+ require 'jinx/helpers/file_separator'
4
+
5
+ class FileSeparatorTest < Test::Unit::TestCase
6
+ FIXTURES = File.dirname(__FILE__) + '/../../../fixtures/line_separator'
7
+ LF_FILE = File.join(FIXTURES, 'lf_line_sep.txt')
8
+ CR_FILE = File.join(FIXTURES, 'cr_line_sep.txt')
9
+ CRLF_FILE = File.join(FIXTURES, 'crlf_line_sep.txt')
10
+
11
+ def test_lf_line_separator
12
+ verify_read(LF_FILE, "LF")
13
+ end
14
+
15
+ def test_cr_line_separator
16
+ verify_read(CR_FILE, "CR")
17
+ end
18
+
19
+ def test_crlf_line_separator
20
+ verify_read(CRLF_FILE, "CRLF")
21
+ end
22
+
23
+ def verify_read(file, type)
24
+ lines = File.open(file) { |io| io.readlines }
25
+ assert_equal(3, lines.size, "#{type} line separator not recognized in readlines")
26
+ lines = File.open(file) { |io| io.to_a }
27
+ assert_equal(3, lines.size, "#{type} line separator not recognized in to_a")
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ require File.dirname(__FILE__) + '/../../../helper'
2
+ require 'test/unit'
3
+ require 'jinx/helpers/inflector'
4
+
5
+ class InflectorTest < Test::Unit::TestCase
6
+ def test_quantified_s
7
+ assert_equal("1 person", "person".quantify(1))
8
+ assert_equal("2 people", "person".quantify(2))
9
+ assert_equal("0 people", "person".quantify(0))
10
+ end
11
+ end
@@ -0,0 +1,32 @@
1
+ require File.dirname(__FILE__) + '/../../../helper'
2
+ require "test/unit"
3
+ require 'jinx/helpers/lazy_hash'
4
+
5
+ class LazyHastTest < Test::Unit::TestCase
6
+
7
+ def test_value_factory
8
+ hash = Jinx::LazyHash.new { |key| key.to_s }
9
+ assert_equal('1', hash[1], "Factory return value is incorrect")
10
+ end
11
+
12
+ def test_default_value_factory
13
+ hash = Jinx::LazyHash.new
14
+ assert_nil(hash[1], "Default factory does not return")
15
+ assert(hash.has_key?(1), "Default entry not created")
16
+ end
17
+
18
+ def test_nil_key
19
+ hash = Jinx::LazyHash.new
20
+ assert_nil(hash[nil], "nil key does not return")
21
+ assert(!hash.has_key?(nil), "Entry created for nil key")
22
+ end
23
+
24
+ def test_reject_missing_value
25
+ hash = Jinx::LazyHash.new(:compact => true)
26
+ assert(!hash.has_key?(1), "Default entry created for nil value")
27
+ end
28
+
29
+ def test_fetch
30
+ assert_raises(IndexError, "Fetch non-existent doesn't raise IndexError") { Jinx::LazyHash.new.fetch(:a) }
31
+ end
32
+ end
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__) + '/../../../helper'
2
+ require "test/unit"
3
+ require 'jinx/helpers/module'
4
+
5
+ module Outer
6
+ module Middle
7
+ class C; end
8
+ end
9
+ end
10
+
11
+ class ModuleTest < Test::Unit::TestCase
12
+ def test_module_with_name
13
+ assert_equal(Outer::Middle, Outer.module_with_name('Middle'), "Unqualified module incorrect")
14
+ assert_nil(Outer.module_with_name('Zed'), "Missing module incorrectly resolves to non-nil value")
15
+ assert_equal(Outer::Middle::C, Outer.module_with_name('Middle::C'), "Qualified module incorrect")
16
+ assert_equal(Outer, Kernel.module_with_name('Outer'), "Top-level module incorrect")
17
+ end
18
+
19
+ def test_parent_module
20
+ assert_equal(Outer, Outer::Middle.parent_module, "Middle parent module incorrect")
21
+ assert_equal(Outer::Middle, Outer::Middle::C.parent_module, "Inner parent module incorrect")
22
+ assert_equal(Kernel, Outer.parent_module, "Outer parent module incorrect")
23
+ end
24
+ end
@@ -0,0 +1,66 @@
1
+ require File.dirname(__FILE__) + '/../../../helper'
2
+ require "test/unit"
3
+ require 'jinx/helpers/options'
4
+
5
+ class OptionsTest < Test::Unit::TestCase
6
+ def test_present
7
+ assert(Options.get(:key, {:key => true}), "Option true value not returned")
8
+ assert(!Options.get(:key, {:key => false}), "Option true value not returned")
9
+ end
10
+
11
+ def test_nil_value
12
+ assert_equal(:a, Options.get(:key, {:key => nil}, :a), "Options nil value not returned")
13
+ end
14
+
15
+ def test_false_with_true_default
16
+ assert_equal(false, Options.get(:key, {:key => false}, true), "Option false value with true default doesn't return false")
17
+ end
18
+
19
+ def test_missing
20
+ assert_nil(Options.get(:key, {}), "Missing option incorrectly found")
21
+ end
22
+
23
+ def test_default_value
24
+ assert_equal(:a, Options.get(:key, {}, :a), "Option default value not used")
25
+ end
26
+
27
+ def test_nil_with_false_default
28
+ assert_equal(false, Options.get(:key, nil, false), "Option default value not used")
29
+ end
30
+
31
+ def test_default_block
32
+ assert_equal(:b, Options.get(:key, {}) { :b }, "Option default block not called")
33
+ end
34
+
35
+ def test_symbol
36
+ assert(Options.get(:key, :key), "Option not found in singleton options")
37
+ end
38
+
39
+ def test_array
40
+ assert(Options.get(:b, [:a, :b]), "Option not found in array options")
41
+ end
42
+
43
+ def test_array_with_hash
44
+ assert_equal(:c, Options.get(:b, [:a, {:b => :c}]), "Option not found in array options")
45
+ end
46
+
47
+ def test_collection_value
48
+ assert_equal([:a], Options.get(:key, {:key => [:a]}, []), "Option array value not returned")
49
+ end
50
+
51
+ def test_empty_to_hash
52
+ assert_equal({}, Options.to_hash(), "Option to_hash with empty argument list not an empty hash")
53
+ end
54
+
55
+ def test_nil_to_hash
56
+ assert_equal({}, Options.to_hash(nil), "Option to_hash with nil argument list not an empty hash")
57
+ end
58
+
59
+ def test_hash_to_hash
60
+ assert_equal({:a => 1}, Options.to_hash({:a => 1}), "Option to_hash with hash argument list not an empty hash")
61
+ end
62
+
63
+ def test_list_to_hash
64
+ assert_equal({:a => 1, :b => true, :c => 2}, Options.to_hash({:a => 1}, :b, :c => 2), "Option to_hash with list argument list incorrect")
65
+ end
66
+ end
@@ -0,0 +1,41 @@
1
+ require File.dirname(__FILE__) + '/../../../helper'
2
+ require "test/unit"
3
+ require 'jinx/helpers/partial_order'
4
+
5
+ class Queued
6
+ include Jinx::PartialOrder
7
+
8
+ attr_reader :queue
9
+
10
+ def initialize(on)
11
+ @queue = on.push(self)
12
+ end
13
+
14
+ def <=>(other)
15
+ queue.index(self) <=> other.queue.index(other) if queue.equal?(other.queue)
16
+ end
17
+ end
18
+
19
+ class PartialOrderTest < Test::Unit::TestCase
20
+ def test_same_queue
21
+ q = []
22
+ a = Queued.new(q)
23
+ assert_equal(a, a, "Same value, queue not equal")
24
+ end
25
+
26
+ def test_different_eql_queue
27
+ a = Queued.new([])
28
+ @b = Queued.new([])
29
+ assert_nil(a <=> @b, "Same value, different queue <=> not nil")
30
+ assert_not_equal(a, @b, "Same value, different queue is equal")
31
+ end
32
+
33
+ def test_less_than
34
+ q = []
35
+ a = Queued.new(q)
36
+ b = Queued.new(q)
37
+ c = Queued.new([])
38
+ assert(a < b, "Comparison incorrect")
39
+ assert_nil(a < c, "Comparison incorrect")
40
+ end
41
+ end
@@ -0,0 +1,83 @@
1
+ require File.dirname(__FILE__) + '/../../../helper'
2
+ require "test/unit"
3
+ require 'set'
4
+ require 'date'
5
+ require 'jinx/helpers/pretty_print'
6
+
7
+ class PrettyPrintTest < Test::Unit::TestCase
8
+ def test_nil
9
+ assert_equal('nil', nil.pp_s, 'Nil pretty print incorrect')
10
+ end
11
+
12
+ def test_date_time
13
+ date = DateTime.civil(2009, 4, 15, 5, 55, 55)
14
+ assert_equal(date.strftime, date.pp_s, 'Date pretty print incorrect')
15
+ end
16
+
17
+ def test_array
18
+ assert_equal('[:a, :b]', [:a, :b].pp_s, 'Array pretty print incorrect')
19
+ end
20
+
21
+ def test_java_collection
22
+ a = Java::JavaUtil::ArrayList.new
23
+ a << :a << :b
24
+ assert_equal(a.to_a.pp_s, a.pp_s, 'Java collection pretty print incorrect')
25
+ end
26
+
27
+ def test_java_collection_cycle
28
+ a = Java::JavaUtil::ArrayList.new
29
+ a << :a << a
30
+ assert_equal('[:a, [...]]', a.pp_s(:single_line), 'Cyclic set pretty print incorrect')
31
+ end
32
+
33
+ def test_hash
34
+ assert_equal('{:a=>a, :b=>b}', {:a => 'a', :b => 'b'}.pp_s(:single_line), 'Hash pretty print incorrect')
35
+ end
36
+
37
+ def test_qp
38
+ assert_equal('1', 1.qp, 'Numeric quick print incorrect')
39
+ assert_equal('nil', nil.qp, 'nil quick print incorrect')
40
+ assert_equal('a', 'a'.qp, 'String quick print incorrect')
41
+ assert_equal(':a', :a.qp, 'Symbol quick print incorrect')
42
+ assert_equal('[:a]', [:a].qp, 'Array quick print incorrect')
43
+ assert_equal('TestCase', Test::Unit::TestCase.qp, 'Class quick print incorrect')
44
+
45
+ x = {:a => 'a', :b => 'b'}
46
+ x.qp
47
+
48
+
49
+ assert_equal('{:a=>a, :b=>b}', {:a => 'a', :b => 'b'}.qp, 'Hash quick print incorrect')
50
+ end
51
+
52
+ def test_set
53
+ assert_equal([:a].pp_s, [:a].to_set.pp_s, 'Set pretty print incorrect')
54
+ end
55
+
56
+ def test_set_cycle
57
+ a = [:a].to_set
58
+ a << a
59
+ assert_equal('[:a, [...]]', a.pp_s(:single_line), 'Cyclic set pretty print incorrect')
60
+ end
61
+
62
+ def test_single_line_argument
63
+ a = [].fill(:a, 0, 80)
64
+ assert(a.pp_s =~ /\n/, 'Test array not long enough')
65
+ assert(!a.pp_s(:single_line).include?("\n"), 'Single line option ignored')
66
+ end
67
+
68
+ def test_single_line_option
69
+ a = [].fill(:a, 0, 80)
70
+ assert_equal(a.pp_s(:single_line => true), a.pp_s(:single_line), 'Single line option ignored')
71
+ end
72
+
73
+ def test_print_wrapper_block
74
+ tuples = [[:a, :b], [:c]]
75
+ wrapper = PrintWrapper.new { |tuple| "<#{tuple.join(',')}>" }
76
+ wrappers = tuples.wrap{ |tuple| wrapper.wrap(tuple) }
77
+ assert_equal("[<a,b>, <c>]", wrappers.pp_s)
78
+ end
79
+
80
+ def test_wrapper_block
81
+ assert_equal("[<a,b>, <c>]", [[:a, :b], [:c]].pp_s { |tuple| "<#{tuple.join(',')}>" })
82
+ end
83
+ end
@@ -0,0 +1,16 @@
1
+ require File.dirname(__FILE__) + '/../../../helper'
2
+ require "test/unit"
3
+ require 'jinx/helpers/stopwatch'
4
+
5
+ class StopwatchTest < Test::Unit::TestCase
6
+ def setup
7
+ @timer = Jinx::Stopwatch.new
8
+ end
9
+
10
+ def test_run
11
+ t1 = @timer.run { 1000000.times { " " * 100 } }
12
+ t2 = @timer.run { 1000000.times { " " * 100 } }
13
+ assert_equal(t1.elapsed + t2.elapsed, @timer.elapsed, "Elapsed time incorrectly accumulated")
14
+ assert_equal(t1.cpu + t2.cpu, @timer.cpu, "CPU time incorrectly accumulated")
15
+ end
16
+ end
@@ -0,0 +1,80 @@
1
+ require File.dirname(__FILE__) + '/../../../helper'
2
+ require "test/unit"
3
+ require 'jinx/helpers/collections'
4
+
5
+ require 'jinx/helpers/transitive_closure'
6
+
7
+ class TransitiveClosureTest < Test::Unit::TestCase
8
+ # Verifies closure iteration for the following hierarchy:
9
+ # root -> a, e
10
+ # a -> b, c
11
+ # c -> d
12
+ # The expected iteration is +root+ preceding +a+ and +e+, +a+ preceding +b+ and +c+, +c+ preceding +d+.
13
+ def test_hierarchy
14
+ root= Node.new('root'); a = Node.new('a', root); b = Node.new('b', a); c = Node.new('c', a); d = Node.new('d', c); e = Node.new('e', root)
15
+ verify_closure([root, a, b, c, d, e], root.transitive_closure(:children))
16
+ end
17
+
18
+ def test_internal
19
+ root= Node.new('root'); a = Node.new('a', root); b = Node.new('b', a); c = Node.new('c', a); d = Node.new('d', c); e = Node.new('e', root)
20
+ verify_closure([a, b, c, d], a.transitive_closure(:children))
21
+ end
22
+
23
+ def test_leaf
24
+ leaf = Node.new(1)
25
+ verify_closure([leaf], leaf.transitive_closure(:children))
26
+ end
27
+
28
+ def test_collection
29
+ a = Node.new('a'); b = Node.new('b'); c = Node.new('c', a); d = Node.new('d', b); e = Node.new('e', c)
30
+ verify_closure([a, b, c, d, e], [a, b].transitive_closure(:children))
31
+ end
32
+
33
+ def test_cycle
34
+ root= Node.new('root'); a = Node.new('a', root); b = Node.new('b', a); c = Node.new('c', a); c.children << root
35
+ expected = [root, a, b, c].to_set
36
+ verify_closure([root, a, b, c], root.transitive_closure(:children))
37
+ end
38
+
39
+ def test_class_hierarchy
40
+ result = [C, D].transitive_closure { |k| [k.superclass] }
41
+ assert_equal([D, C].to_set, result[0..1].to_set, "Class hierarchy closure incomparable leaf class order incorrect")
42
+ assert_equal([B, A, Object], result[2..-1], "Class hierarchy closure comparable non-leaf class order incorrect")
43
+ end
44
+
45
+ def verify_closure(content, closure)
46
+ assert_equal(content.to_set, closure.to_set, "Hierarchy closure incorrect")
47
+ # Verify that no child succeeds the parent.
48
+ closure.each_with_index do |node, index|
49
+ par = node.parent
50
+ if content.include?(par) then
51
+ assert(closure.index(par) < index, "Child #{node} precedes parent #{par}")
52
+ end
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ class A; end
59
+ class B < A; end
60
+ class C < B; end
61
+ class D < A; end
62
+
63
+ class Node
64
+ attr_reader :parent, :children, :value
65
+
66
+ def initialize(value, parent=nil)
67
+ super()
68
+ @value = value
69
+ @parent = parent
70
+ @children = []
71
+ parent.children << self if parent
72
+ end
73
+
74
+ def to_s
75
+ value.to_s
76
+ end
77
+
78
+ alias :inspect :to_s
79
+ end
80
+ end
@@ -0,0 +1,288 @@
1
+ require File.dirname(__FILE__) + '/../../../helper'
2
+ require "test/unit"
3
+
4
+ # JRuby SyncEnumerator moved from generator to REXML in JRuby 1.5.
5
+ require 'rexml/document'
6
+ require 'jinx/helpers/collections'
7
+
8
+ require 'jinx/helpers/visitor'
9
+
10
+ class Node
11
+ attr_reader :parent, :children, :friends
12
+
13
+ attr_accessor :value
14
+
15
+ def initialize(value, parent=nil)
16
+ @value = value
17
+ @children = []
18
+ @friends = []
19
+ @parent = parent
20
+ @parent.children << self if @parent
21
+ end
22
+
23
+ def <=>(other)
24
+ value <=> other.value if other
25
+ end
26
+
27
+ def to_s
28
+ "#{self.class.name}@#{hash}{value => #{value}}"
29
+ end
30
+
31
+ alias :inspect :to_s
32
+ end
33
+
34
+ class VistorTest < Test::Unit::TestCase
35
+ def test_visit
36
+ parent = Node.new(1)
37
+ child = Node.new(2, parent)
38
+ multiplier = 1
39
+ visitor = Jinx::Visitor.new { |node| node.children }
40
+ result = visitor.visit(parent) { |node| node.value *= (multiplier *= 2) }
41
+ assert_equal(2, parent.value, "Visit parent value incorrect")
42
+ assert_equal(8, child.value, "Visit child value incorrect")
43
+ assert_equal(2, result, "Visit result incorrect")
44
+ end
45
+
46
+ def test_cycle
47
+ parent = Node.new(1)
48
+ child = Node.new(2, parent)
49
+ child.children << parent
50
+ multiplier = 2
51
+ visitor = Jinx::Visitor.new { |node| node.children }
52
+ visitor.visit(parent) { |node| node.value *= multiplier }
53
+ assert_equal(2, parent.value, "Cycle parent value incorrect")
54
+ assert_equal(4, child.value, "Cycle child value incorrect")
55
+ end
56
+
57
+ def test_depth_first
58
+ parent = Node.new(1)
59
+ child = Node.new(2, parent)
60
+ multiplier = 1
61
+ visitor = Jinx::Visitor.new(:depth_first) { |node| node.children }
62
+ visitor.visit(parent) { |node| node.value *= (multiplier *= 2) }
63
+ assert_equal(4, parent.value, "Depth-first parent value incorrect")
64
+ assert_equal(4, child.value, "Depth-first child value incorrect")
65
+ end
66
+
67
+ def test_return
68
+ parent = Node.new(1)
69
+ child = Node.new(2, parent)
70
+ result = increment(parent, 2)
71
+ assert_nil(result, "Pre-emptive return incorrect")
72
+ assert_equal(2, parent.value, "Pre-emptive return parent value incorrect")
73
+ assert_equal(2, child.value, "Pre-emptive return child value incorrect")
74
+ end
75
+
76
+ def test_visited_detection
77
+ parent = Node.new(1)
78
+ child = Node.new(2, parent)
79
+ c2 = Node.new(3, parent)
80
+ c2.children << child
81
+ visitor = Jinx::Visitor.new { |node| node.children }
82
+ visitor.visit(parent) { |node| node.value += 1 }
83
+ assert_equal(3, child.value, "Child visited twice")
84
+ end
85
+
86
+ def test_root_cycle
87
+ parent = Node.new(1)
88
+ c1 = Node.new(2, parent)
89
+ c2 = Node.new(3, parent)
90
+ c2.children << parent
91
+ gc11 = Node.new(4, c1)
92
+ gc12 = Node.new(5, c1)
93
+ gc12.children << c1
94
+ gc121 = Node.new(6, gc12)
95
+ gc121.children << parent
96
+ visitor = Jinx::Visitor.new { |node| node.children }
97
+ result = visitor.visit(parent)
98
+ assert_equal([[2, 5, 2], [1, 2, 5, 6, 1], [1, 3, 1]], visitor.cycles.map { |cycle| cycle.map { |node| node.value } }, "Root cycles incorrect")
99
+ end
100
+
101
+ def increment(parent, limit)
102
+ visitor = Jinx::Visitor.new { |node| node.children }
103
+ visitor.visit(parent) { |node| node.value < limit ? node.value += 1 : return }
104
+ end
105
+
106
+ def test_collection
107
+ p1 = Node.new(1)
108
+ child = Node.new(2, p1)
109
+ p2 = Node.new(3)
110
+ p2.children << child
111
+ visitor = Jinx::Visitor.new { |pair| REXML::SyncEnumerator.new(pair.first.children, pair.last.children).to_a }
112
+ result = visitor.to_enum([p1, p2]).map { |pair| [pair.first.value, pair.last.value] }
113
+ assert_equal([[1, 3], [2, 2]], result.to_a, "Collection visit result incorrect")
114
+ end
115
+
116
+ def node_value(node)
117
+ node.value if node
118
+ end
119
+
120
+ def test_enumeration
121
+ parent = Node.new(1)
122
+ c1 = Node.new(2, parent)
123
+ c2 = Node.new(3, parent)
124
+ visitor = Jinx::Visitor.new { |node| node.children }
125
+ result = visitor.to_enum(parent).map { |node| node.value }
126
+ assert_equal([1, 2, 3], result, "Enumeration result incorrect")
127
+ end
128
+
129
+ def test_exclude_cycles
130
+ parent = Node.new(1)
131
+ c1 = Node.new(2, parent)
132
+ gc11 = Node.new(3, c1)
133
+ gc11.children << c1
134
+ c2 = Node.new(4, parent)
135
+ gc21 = Node.new(5, c2)
136
+ gc21.children << parent
137
+ visitor = Jinx::Visitor.new(:prune_cycle) { |node| node.children }
138
+ result = visitor.to_enum(parent).map { |node| node.value }
139
+ assert_equal([1, 2, 3], result, "Exclude result incorrect")
140
+ end
141
+
142
+ def test_missing_block
143
+ parent = Node.new(1)
144
+ c1 = Node.new(2, parent)
145
+ c2 = Node.new(3, parent)
146
+ visitor = Jinx::Visitor.new { |node| node.children }
147
+ visitor.visit(parent)
148
+ assert_equal([parent, c1, c2], visitor.visited.values.sort, "Missing visit operator result incorrect")
149
+ end
150
+
151
+ def test_filter
152
+ parent = Node.new(1)
153
+ c1 = Node.new(2, parent)
154
+ c2 = Node.new(3, parent)
155
+ gc1 = Node.new(4, c1)
156
+ gc2 = Node.new(5, c1)
157
+ visitor = Jinx::Visitor.new { |node| node.children }.filter { |parent, children| children.first if parent.value < 4 }
158
+ result = visitor.to_enum(parent).map { |node| node.value }
159
+ assert_equal([1, 2, 4], result, "Filter result incorrect")
160
+ end
161
+
162
+ def test_sync_without_block
163
+ p1 = Node.new(1)
164
+ c11 = Node.new(2, p1)
165
+ c12 = Node.new(3, p1)
166
+ gc111 = Node.new(4, c11)
167
+ gc121 = Node.new(5, c12)
168
+ p2 = Node.new(6)
169
+ c21 = Node.new(7, p2)
170
+ c22 = Node.new(8, p2)
171
+ gc211 = Node.new(9, c21)
172
+ visitor = Jinx::Visitor.new { |node| node.children }.sync
173
+ result = visitor.to_enum(p1, p2).map { |pair| pair.map { |node| node.value unless node.nil? } }
174
+ assert_equal([[1, 6], [2, 7], [4, 9], [3, 8], [5, nil]], result, "Sync without block result incorrect")
175
+ end
176
+
177
+ def test_sync_with_matcher
178
+ p1 = Node.new(1)
179
+ c11 = Node.new(2, p1)
180
+ c12 = Node.new(3, p1)
181
+ gc111 = Node.new(4, c11)
182
+ gc121 = Node.new(5, c12)
183
+ p2 = Node.new(1)
184
+ c21 = Node.new(2, p2)
185
+ c22 = Node.new(3, p2)
186
+ gc211 = Node.new(5, c21)
187
+ visitor = Jinx::Visitor.new { |node| node.children }
188
+ synced = visitor.sync { |nodes, others| nodes.to_compact_hash { |n| others.detect { |o| n.value == o.value } } }
189
+ result = synced.to_enum(p1, p2).map { |pair| pair.map { |node| node.value if node } }
190
+ assert_equal([[1, 1], [2, 2], [4, nil], [3, 3], [5, nil]], result, "Sync with block result incorrect")
191
+ end
192
+
193
+ def test_sync_noncollection
194
+ p1 = Node.new(1)
195
+ child = Node.new(2, p1)
196
+ p2 = Node.new(3)
197
+ p2.children << child
198
+ visitor = Jinx::Visitor.new { |node| node.children.first }.sync
199
+ value_hash = {}
200
+ result = visitor.visit(p1, p2) { |first, last| value_hash[node_value(first)] = node_value(last) }
201
+ assert_equal({1 => 3, 2 => 2}, value_hash, "Sync with non-collection children result incorrect")
202
+ result = visitor.to_enum(p1, p2).map { |first, last| [node_value(first), node_value(last)] }
203
+ assert_equal([[1, 3], [2, 2]], result.to_a, "Sync with non-collection children result incorrect")
204
+ end
205
+
206
+ def test_sync_missing
207
+ p1 = Node.new(1)
208
+ p2 = Node.new(2)
209
+ c1 = Node.new(3, p1)
210
+ c2 = Node.new(4, p2)
211
+ gcren = Node.new(5, c2)
212
+ visitor = Jinx::Visitor.new { |node| node.children.first }.sync
213
+ result = visitor.to_enum(p1, p2).map { |pair| [node_value(pair.first), node_value(pair.last)] }
214
+ assert_equal([[1, 2], [3, 4]], result.to_a, "Sync with missing children result incorrect")
215
+ end
216
+
217
+ def test_missing_node
218
+ parent = Node.new(1)
219
+ child = Node.new(2, parent)
220
+ multiplier = 2
221
+ visitor = Jinx::Visitor.new { |node| node.children unless node == child }
222
+ visitor.visit(parent) { |node| node.value *= multiplier }
223
+ assert_equal(2, parent.value, "Missing node parent value incorrect")
224
+ assert_equal(4, child.value, "Missing node child value incorrect")
225
+ end
226
+
227
+ def test_noncollection_traversal
228
+ parent = Node.new(1)
229
+ child = Node.new(2, parent)
230
+ multiplier = 2
231
+ Jinx::Visitor.new { |node| node.parent }.visit(child) { |node| node.value *= multiplier }
232
+ assert_equal(2, parent.value, "Non-collection parent value incorrect")
233
+ assert_equal(4, child.value, "Non-collection child value incorrect")
234
+ end
235
+
236
+ def test_parent
237
+ parent = Node.new(1)
238
+ c1 = Node.new(2, parent)
239
+ c2 = Node.new(3, parent)
240
+ gc = Node.new(4, c1)
241
+ visitor = Jinx::Visitor.new { |node| node.children }
242
+ visitor.visit(parent) { |node| node.value = visitor.parent.nil? ? 0 : visitor.parent.value + 1 }
243
+ assert_equal(0, parent.value, "Parent value incorrect")
244
+ assert_equal(1, c1.value, "Child value incorrect")
245
+ assert_equal(1, c2.value, "Child value incorrect")
246
+ assert_equal(2, gc.value, "gc value incorrect")
247
+ end
248
+
249
+ def test_parent_depth_first
250
+ # An interesting variant: the parent node value is not reset until after the children are visited
251
+ parent = Node.new(1)
252
+ c1 = Node.new(2, parent)
253
+ c2 = Node.new(3, parent)
254
+ gc = Node.new(4, c1)
255
+ visitor = Jinx::Visitor.new(:depth_first) { |node| node.children }
256
+ visitor.visit(parent) { |node| node.value = visitor.parent.nil? ? 0 : visitor.parent.value + 1 }
257
+ assert_equal(0, parent.value, "Parent value incorrect")
258
+ assert_equal(2, c1.value, "Child value incorrect")
259
+ assert_equal(2, c2.value, "Child value incorrect")
260
+ assert_equal(3, gc.value, "gc value incorrect")
261
+ end
262
+
263
+ def test_visited
264
+ parent = Node.new(1)
265
+ c1 = Node.new(nil, parent)
266
+ c2 = Node.new(nil, parent)
267
+ gc = Node.new(nil, c1)
268
+ visitor = Jinx::Visitor.new { |node| node.children }
269
+ visitor.visit(parent) { |node| node.value ||= visitor.visited[node.parent] + 1 }
270
+ assert_equal(1, parent.value, "Parent value incorrect")
271
+ assert_equal(2, c1.value, "Child value incorrect")
272
+ assert_equal(2, c2.value, "Child value incorrect")
273
+ assert_equal(3, gc.value, "gc value incorrect")
274
+ end
275
+
276
+ def test_visited_result
277
+ parent = Node.new(1)
278
+ c1 = Node.new(2, parent)
279
+ c2 = Node.new(3, parent)
280
+ gc = Node.new(4, c1)
281
+ visitor = Jinx::Visitor.new { |node| node.children }
282
+ visitor.visit(parent) { |node| node.value + 1 }
283
+ assert_equal(2, visitor.visited[parent], "Parent visited value incorrect")
284
+ assert_equal(3, visitor.visited[c1], "Child visited value incorrect")
285
+ assert_equal(4, visitor.visited[c2], "Child visited value incorrect")
286
+ assert_equal(5, visitor.visited[gc], "gc visited value incorrect")
287
+ end
288
+ end