jinx 2.1.1

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 (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