tengine_core 0.5.28

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 (156) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +40 -0
  4. data/Gemfile.lock +95 -0
  5. data/README.md +54 -0
  6. data/Rakefile +44 -0
  7. data/VERSION +1 -0
  8. data/bin/tengine_atd +8 -0
  9. data/bin/tengine_heartbeat_watchd +8 -0
  10. data/bin/tengined +182 -0
  11. data/examples/VERSION +1 -0
  12. data/examples/uc01_execute_processing_for_event.rb +11 -0
  13. data/examples/uc02_fire_another_event.rb +16 -0
  14. data/examples/uc03_2handlers_for_1event.rb +16 -0
  15. data/examples/uc08_if_both_a_and_b_occurs.rb +11 -0
  16. data/examples/uc10_if_the_event_occurs_at_the_server.rb +15 -0
  17. data/examples/uc50_commit_event_at_first.rb +17 -0
  18. data/examples/uc51_commit_event_at_first_submit.rb +29 -0
  19. data/examples/uc52_commit_event_after_all_handler_submit.rb +31 -0
  20. data/examples/uc52_never_commit_event_unless_all_handler_submit.rb +31 -0
  21. data/examples/uc60_event_in_handler.rb +18 -0
  22. data/examples/uc62_session_in_driver.rb +16 -0
  23. data/examples/uc64_safety_countup.rb +14 -0
  24. data/examples/uc70_driver_enabled_on_activation.rb +13 -0
  25. data/examples/uc71_driver_disabled_on_activation.rb +14 -0
  26. data/examples/uc72_setup_eventmachine.rb +17 -0
  27. data/examples/uc80_raise_io_error.rb +10 -0
  28. data/examples/uc81_raise_runtime_error.rb +10 -0
  29. data/examples2/driver01.rb +18 -0
  30. data/examples2/driver02.rb +19 -0
  31. data/examples2/uc08_if_both_a_and_b_occurs.rb +13 -0
  32. data/examples2/uc10_if_the_event_occurs_at_the_server.rb +18 -0
  33. data/examples2/uc51_commit_event_at_first_submit_1.rb +16 -0
  34. data/examples2/uc51_commit_event_at_first_submit_2.rb +17 -0
  35. data/examples2/uc51_commit_event_at_first_submit_3.rb +17 -0
  36. data/examples2/uc62_session_in_driver.rb +16 -0
  37. data/examples2/uc71_driver_disabled_on_activation.rb +16 -0
  38. data/failure_examples/VERSION +1 -0
  39. data/failure_examples/uc53_submit_outside_of_handler.rb +15 -0
  40. data/failure_examples/uc61_event_outside_of_handler.rb +12 -0
  41. data/failure_examples/uc63_session_outside_of_driver.rb +13 -0
  42. data/lib/tengine/core.rb +74 -0
  43. data/lib/tengine/core/bootstrap.rb +123 -0
  44. data/lib/tengine/core/collection_accessible.rb +34 -0
  45. data/lib/tengine/core/config.rb +10 -0
  46. data/lib/tengine/core/config/atd.rb +225 -0
  47. data/lib/tengine/core/config/core.rb +319 -0
  48. data/lib/tengine/core/config/heartbeat_watcher.rb +229 -0
  49. data/lib/tengine/core/connection_test/.gitignore +1 -0
  50. data/lib/tengine/core/connection_test/fire_bar_on_foo.rb +16 -0
  51. data/lib/tengine/core/driveable.rb +213 -0
  52. data/lib/tengine/core/driver.rb +69 -0
  53. data/lib/tengine/core/driver/finder.rb +42 -0
  54. data/lib/tengine/core/dsl_evaluator.rb +110 -0
  55. data/lib/tengine/core/dsl_filter_def.rb +11 -0
  56. data/lib/tengine/core/dsl_loader.rb +108 -0
  57. data/lib/tengine/core/event.rb +145 -0
  58. data/lib/tengine/core/event/finder.rb +82 -0
  59. data/lib/tengine/core/event_exception_reportable.rb +88 -0
  60. data/lib/tengine/core/event_wrapper.rb +21 -0
  61. data/lib/tengine/core/find_by_name.rb +31 -0
  62. data/lib/tengine/core/handler.rb +152 -0
  63. data/lib/tengine/core/handler_path.rb +33 -0
  64. data/lib/tengine/core/heartbeat_watcher.rb +161 -0
  65. data/lib/tengine/core/io_to_logger.rb +22 -0
  66. data/lib/tengine/core/kernel.rb +510 -0
  67. data/lib/tengine/core/kernel_runtime.rb +91 -0
  68. data/lib/tengine/core/method_traceable.rb +38 -0
  69. data/lib/tengine/core/mongoid_fix.rb +19 -0
  70. data/lib/tengine/core/mutex.rb +177 -0
  71. data/lib/tengine/core/optimistic_lock.rb +69 -0
  72. data/lib/tengine/core/plugins.rb +54 -0
  73. data/lib/tengine/core/schedule.rb +21 -0
  74. data/lib/tengine/core/scheduler.rb +156 -0
  75. data/lib/tengine/core/selectable_attr.rb +29 -0
  76. data/lib/tengine/core/session.rb +21 -0
  77. data/lib/tengine/core/session_wrapper.rb +68 -0
  78. data/lib/tengine/core/setting.rb +21 -0
  79. data/lib/tengine/core/validation.rb +36 -0
  80. data/lib/tengine/errors.rb +18 -0
  81. data/lib/tengine/rspec.rb +8 -0
  82. data/lib/tengine/rspec/context_wrapper.rb +51 -0
  83. data/lib/tengine/rspec/extension.rb +53 -0
  84. data/lib/tengine_core.rb +23 -0
  85. data/spec/factories/tengine_core_drivers.rb +10 -0
  86. data/spec/factories/tengine_core_events.rb +14 -0
  87. data/spec/factories/tengine_core_handler_paths.rb +9 -0
  88. data/spec/factories/tengine_core_handlers.rb +9 -0
  89. data/spec/factories/tengine_core_sessions.rb +9 -0
  90. data/spec/mongoid.yml +35 -0
  91. data/spec/spec_helper.rb +48 -0
  92. data/spec/support/mongo_index_key_log.rb +91 -0
  93. data/spec/tengine/core/bootstrap_spec.rb +278 -0
  94. data/spec/tengine/core/bugfix/bind_dsl_file_in_multi_byte_dir_spec.rb +21 -0
  95. data/spec/tengine/core/bugfix/enabled_on_activation_spec.rb +112 -0
  96. data/spec/tengine/core/bugfix/receive_event_spec.rb +133 -0
  97. data/spec/tengine/core/bugfix/use_dsl_version_method.rb +12 -0
  98. data/spec/tengine/core/bugfix/use_dsl_version_method_spec.rb +28 -0
  99. data/spec/tengine/core/bugfix/use_event_in_handler_dsl.rb +11 -0
  100. data/spec/tengine/core/bugfix//351/235/236ACSII/343/201/256/343/203/206/343/202/231/343/202/243/343/203/254/343/202/257/343/203/210/343/203/252/345/220/215/source_location_encoding.rb +35 -0
  101. data/spec/tengine/core/bugfix//351/235/236ACSII/343/201/256/343/203/206/343/202/231/343/202/243/343/203/254/343/202/257/343/203/210/343/203/252/345/220/215//351/235/236ASCII/343/201/256/343/203/225/343/202/241/343/202/244/343/203/253/345/220/215_dsl.rb +38 -0
  102. data/spec/tengine/core/bugfix//351/235/236ACSII/343/201/256/343/203/207/343/202/243/343/203/254/343/202/257/343/203/210/343/203/252/345/220/215/source_location_encoding.rb +35 -0
  103. data/spec/tengine/core/bugfix//351/235/236ACSII/343/201/256/343/203/207/343/202/243/343/203/254/343/202/257/343/203/210/343/203/252/345/220/215//351/235/236ASCII/343/201/256/343/203/225/343/202/241/343/202/244/343/203/253/345/220/215_dsl.rb +38 -0
  104. data/spec/tengine/core/config/atd_spec.rb +62 -0
  105. data/spec/tengine/core/config/core_spec.rb +479 -0
  106. data/spec/tengine/core/config/heartbeat_watcher_spec.rb +62 -0
  107. data/spec/tengine/core/config/syntax_error_in_erb.yml.erb +13 -0
  108. data/spec/tengine/core/config/wrong_category_name.yml.erb +13 -0
  109. data/spec/tengine/core/config/wrong_field_name.yml.erb +12 -0
  110. data/spec/tengine/core/config/wrong_yaml.yml.erb +13 -0
  111. data/spec/tengine/core/config_spec/another_port.yml +54 -0
  112. data/spec/tengine/core/config_spec/config_with_dir_absolute_load_path.yml +16 -0
  113. data/spec/tengine/core/config_spec/config_with_dir_relative_load_path.yml +16 -0
  114. data/spec/tengine/core/config_spec/config_with_file_absolute_load_path.yml +16 -0
  115. data/spec/tengine/core/config_spec/config_with_file_relative_load_path.yml +16 -0
  116. data/spec/tengine/core/config_spec/log_config_spec.rb +235 -0
  117. data/spec/tengine/core/driveable_spec.rb +240 -0
  118. data/spec/tengine/core/driver_spec.rb +159 -0
  119. data/spec/tengine/core/dsl_loader_spec.rb +172 -0
  120. data/spec/tengine/core/dsls/uc08_if_both_a_and_b_occurs_spec.rb +35 -0
  121. data/spec/tengine/core/dsls/uc10_if_the_event_occurs_at_the_server_spec.rb +58 -0
  122. data/spec/tengine/core/dsls/uc50_commit_event_at_first_spec.rb +29 -0
  123. data/spec/tengine/core/dsls/uc52_commit_event_after_all_handler_submit_spec.rb +33 -0
  124. data/spec/tengine/core/dsls/uc52_never_commit_event_unless_all_handler_submit_spec.rb +37 -0
  125. data/spec/tengine/core/dsls/uc53_submit_outside_of_handler_spec.rb +37 -0
  126. data/spec/tengine/core/dsls/uc60_event_in_handler_spec.rb +31 -0
  127. data/spec/tengine/core/dsls/uc61_event_outside_of_handler_spec.rb +37 -0
  128. data/spec/tengine/core/dsls/uc62_session_in_driver_spec.rb +36 -0
  129. data/spec/tengine/core/dsls/uc63_session_outside_of_driver_spec.rb +35 -0
  130. data/spec/tengine/core/dsls/uc64_safety_countup_spec.rb +134 -0
  131. data/spec/tengine/core/dsls/uc70_driver_enabled_on_activation_spec.rb +39 -0
  132. data/spec/tengine/core/dsls/uc71_driver_disabled_on_activation_spec.rb +36 -0
  133. data/spec/tengine/core/dsls/uc72_setup_eventmachine_spec.rb +39 -0
  134. data/spec/tengine/core/dsls/uc80_raise_io_error_spec.rb +53 -0
  135. data/spec/tengine/core/dsls/uc81_raise_runtime_error_spec.rb +49 -0
  136. data/spec/tengine/core/event/finder_spec.rb +136 -0
  137. data/spec/tengine/core/event_exception_reportable_spec.rb +33 -0
  138. data/spec/tengine/core/event_spec.rb +161 -0
  139. data/spec/tengine/core/event_wrapper_spec.rb +35 -0
  140. data/spec/tengine/core/handler_path_spec.rb +87 -0
  141. data/spec/tengine/core/handler_spec.rb +190 -0
  142. data/spec/tengine/core/heartbeat_watcher_spec.rb +131 -0
  143. data/spec/tengine/core/io_to_logger_spec.rb +30 -0
  144. data/spec/tengine/core/kernel_spec.rb +885 -0
  145. data/spec/tengine/core/mutex_spec.rb +184 -0
  146. data/spec/tengine/core/optimistic_lock_spec.rb +55 -0
  147. data/spec/tengine/core/scheculer_spec.rb +121 -0
  148. data/spec/tengine/core/selectable_attr_spec.rb +30 -0
  149. data/spec/tengine/core/session_spec.rb +104 -0
  150. data/spec/tengine/core/setting_spec.rb +79 -0
  151. data/spec/tengine/core_spec.rb +13 -0
  152. data/spec/tengine_spec.rb +14 -0
  153. data/tengine_core.gemspec +248 -0
  154. data/tmp/log/.gitignore +1 -0
  155. data/tmp/tengined_status/.gitignore +1 -0
  156. metadata +421 -0
@@ -0,0 +1,184 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'tengine/core'
4
+
5
+ describe Tengine::Core::Mutex do
6
+ before do
7
+ Tengine::Core::Mutex::Mutex.delete_all
8
+ end
9
+
10
+ context "#new" do
11
+ subject { Tengine::Core::Mutex.new "test mutex 01" }
12
+
13
+ it { should be_kind_of(Tengine::Core::Mutex) }
14
+ its(:mutex) { should be_kind_of(Tengine::Core::Mutex::Mutex) }
15
+ its(:recursive) { should be_zero }
16
+
17
+ context "negative ttl" do
18
+ it { expect { Tengine::Core::Mutex.new "test negative ttl", -1 }.to raise_exception(ArgumentError) }
19
+ end
20
+
21
+ context "infinite ttl" do
22
+ it { expect { Tengine::Core::Mutex.new "test negative ttl", (1.0 / 0.0) }.to raise_exception(TypeError) }
23
+ end
24
+ end
25
+
26
+ context "#synchronize" do
27
+ subject { Tengine::Core::Mutex.new "test mutex 01", Math::PI / 10 }
28
+
29
+ it "requires block" do
30
+ expect {
31
+ subject.synchronize
32
+ }.to raise_exception(ArgumentError)
33
+ end
34
+
35
+ it "synchronizes #0: no other lockers" do
36
+ block_called = false
37
+ EM.run_block do
38
+ subject.synchronize do
39
+ block_called = true
40
+ end
41
+ end
42
+ block_called.should be_true
43
+ end
44
+
45
+ it "synchronizes #1: with another locker, which is expired" do
46
+ # "stub" waiters
47
+ m = subject.mutex
48
+ m.waiters ||= []
49
+ m.waiters << { :_id => 1, :timeout => Time.at(0) }
50
+ m.save
51
+
52
+ block_called = false
53
+ t0 = Time.now
54
+ EM.run_block do
55
+ subject.synchronize do
56
+ block_called = true
57
+ end
58
+ end
59
+ t1 = Time.now
60
+ block_called.should be_true
61
+ m.reload.waiters.should be_empty
62
+ (t1 - t0).should be_within(0.5).of(0) #immediate
63
+ end
64
+
65
+ it "synchronizes #2: with another locker, which is expiring" do
66
+ # "stub" waiters
67
+ m = subject.mutex
68
+ m.waiters ||= []
69
+ m.waiters << { :_id => 1, :timeout => Time.now + m.ttl / 2 }
70
+ m.save
71
+
72
+ block_called = false
73
+ t0 = Time.now
74
+ EM.run do
75
+ subject.synchronize do
76
+ block_called = true
77
+ EM.stop
78
+ end
79
+ end
80
+ t1 = Time.now
81
+ block_called.should be_true
82
+ m.reload.waiters.should be_empty
83
+ (t1 - t0).should be_within(0.5).of(0.3 + m.ttl)
84
+ end
85
+
86
+ it "synchronizes #3: with another locker, which is not expiring" do
87
+ # "stub" waiters
88
+ m = subject.mutex
89
+ s = mock("mutex")
90
+ s.stub("_id").and_return(1)
91
+ m.waiters ||= []
92
+ m.waiters << { :_id => s._id, :timeout => Time.now + 10 }
93
+ m.save
94
+
95
+ block_called = false
96
+ t0 = Time.now
97
+ EM.run do
98
+ EM.add_timer 5 do
99
+ m.unlock s
100
+ end
101
+ subject.synchronize do
102
+ block_called = true
103
+ EM.stop
104
+ end
105
+ end
106
+ t1 = Time.now
107
+ block_called.should be_true
108
+ m.reload.waiters.should be_empty
109
+ (t1 - t0).should be_within(0.5).of(5 + m.ttl)
110
+ end
111
+
112
+ it "synchronizes #4: multi-threaded situation" do
113
+ x = y = z = nil
114
+
115
+ EM.run do
116
+ EM.defer do
117
+ m = Tengine::Core::Mutex.new "test mutex 02"
118
+ m.synchronize do
119
+ x = Time.now.to_f
120
+ end
121
+ y = Time.now.to_f
122
+ end
123
+
124
+ EM.defer do
125
+ sleep 0.5
126
+ m = Tengine::Core::Mutex.new "test mutex 02"
127
+ m.synchronize do
128
+ z = Time.now.to_f
129
+ end
130
+ end
131
+
132
+ EM.add_timer(2.5) do
133
+ EM.stop
134
+ end
135
+ end
136
+
137
+ x.should_not be_nil
138
+ y.should_not be_nil
139
+ z.should_not be_nil
140
+ x.should < z
141
+ y.should <= z
142
+ end
143
+ end
144
+
145
+ context "#heartbeat" do
146
+ subject { Tengine::Core::Mutex.new "test mutex 01", Math::PI / 10 }
147
+
148
+ it "prevents automatic unlocking" do
149
+ m = subject.mutex
150
+ t1 = nil
151
+ t0 = Time.now.to_f
152
+ EM.run do
153
+ EM.defer do
154
+ subject.synchronize do
155
+ 20.times do
156
+ subject.heartbeat
157
+ sleep(m.ttl / 2)
158
+ end
159
+ end
160
+ end
161
+ EM.defer do
162
+ sleep m.ttl
163
+ loop do
164
+ # hacky...
165
+ if h = m.reload.waiters.first
166
+ if h["timeout"] < Time.now
167
+ t1 = Time.now.to_f
168
+ EM.stop
169
+ break
170
+ end
171
+ else
172
+ t1 = Time.now.to_f
173
+ EM.stop
174
+ break
175
+ end
176
+ end
177
+ end
178
+ end
179
+
180
+ t1.should_not be_nil
181
+ (t1 - t0).should be_within(0.5).of(10 * subject.mutex.ttl)
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,55 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe Tengine::Core::OptimisticLock do
5
+
6
+ class Tengine::Core::OptimisticLockTestBox1
7
+ include Mongoid::Document
8
+ include Tengine::Core::OptimisticLock
9
+
10
+ set_locking_field :version
11
+
12
+ field :version, :type => Integer
13
+ field :value, :type => String
14
+ end
15
+
16
+ context "update_with_lock" do
17
+ before do
18
+ @test_box1 = Tengine::Core::OptimisticLockTestBox1.create!(:version => 2, :value => "foo")
19
+ end
20
+
21
+ it "競合がなければ素直に更新する" do
22
+ test_box = Tengine::Core::OptimisticLockTestBox1.find(@test_box1.id)
23
+ test_box.update_with_lock do
24
+ test_box.value += "o"
25
+ end
26
+ test_box.reload
27
+ test_box.value.should == "fooo"
28
+ test_box.version.should == 3
29
+ end
30
+
31
+ it "競合しても単純に上書きしたりせず、最新を取得し直して更新する" do
32
+ test_box1 = Tengine::Core::OptimisticLockTestBox1.find(@test_box1.id)
33
+ test_box2 = Tengine::Core::OptimisticLockTestBox1.find(@test_box1.id)
34
+ # test_box1を更新
35
+ test_box1_count = 0
36
+ test_box1.update_with_lock do
37
+ test_box1_count += 1
38
+ test_box1.value += "o"
39
+ end
40
+ test_box1_count.should == 1
41
+ test_box1.value.should == "fooo"
42
+ test_box1.version.should == 3
43
+ # test_box2を更新
44
+ test_box2_count = 0
45
+ test_box2.update_with_lock do
46
+ test_box2_count += 1
47
+ test_box2.value += "w"
48
+ end
49
+ test_box2_count.should == 2
50
+ test_box2.value.should == "fooow"
51
+ test_box2.version.should == 4
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,121 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'amqp'
4
+
5
+ require_relative '../../../lib/tengine/core/scheduler'
6
+ require 'tengine/mq/suite'
7
+
8
+ describe Tengine::Core::Scheduler do
9
+ before do
10
+ Tengine::Core::Schedule.delete_all
11
+ @uuid = UUID.new
12
+ end
13
+
14
+ subject do
15
+ Tengine::Core::Scheduler.new(%w[--heartbeat-atd-interval=30])
16
+ end
17
+
18
+ describe "#search_for_schedule" do
19
+
20
+ before do
21
+ @fixtures = Array.new
22
+ @fixtures << Tengine::Core::Schedule.new(event_type_name: "stop.execution.job.tengine", scheduled_at: 1.day.ago)
23
+ @fixtures << Tengine::Core::Schedule.new(event_type_name: "stop.execution.job.tengine", scheduled_at: Time.now + 10)
24
+ @fixtures << Tengine::Core::Schedule.new(event_type_name: "stop.execution.job.tengine", scheduled_at: 1.day.ago, status: Tengine::Core::Schedule::FIRED)
25
+ @fixtures.each {|i| i.save! }
26
+ end
27
+
28
+ it "古いものを検索してくる" do
29
+ EM.stub(:next_tick).and_yield
30
+ set = []
31
+ subject.search_for_schedule do |i|
32
+ set << i
33
+ end
34
+
35
+ set.should include(@fixtures[0])
36
+ set.should_not include(@fixtures[1])
37
+ set.should_not include(@fixtures[2])
38
+ end
39
+ end
40
+
41
+ describe "#send_last_event" do
42
+ it "finished.process.atd.tengineの発火" do
43
+ sender = mock(:sender)
44
+ subject.stub(:sender).and_return(sender)
45
+ sender.should_receive(:fire).with("finished.process.atd.tengine", an_instance_of(Hash))
46
+ sender.should_receive(:stop)
47
+ subject.send_last_event
48
+ end
49
+ end
50
+
51
+ describe "#send_periodic_event" do
52
+ it "atd.heartbeat.tengineの発火" do
53
+ sender = mock(:sender)
54
+ subject.stub(:sender).and_return(sender)
55
+ sender.should_receive(:fire).with("atd.heartbeat.tengine", an_instance_of(Hash))
56
+ sender.stub(:fire).with("finished.process.atd.tengine", an_instance_of(Hash)) # 来るかも
57
+ subject.send_periodic_event
58
+ end
59
+ end
60
+
61
+ describe "#send_scheduled_event" do
62
+ it "スケジュールされたイベントの発火" do
63
+ s0 = Tengine::Core::Schedule.new(event_type_name: "test.event.not.tengine", source_name: "test://localhost/dev/null", properties: {foo: 'bar'})
64
+ sender = mock(:sender)
65
+ subject.stub(:sender).and_return(sender)
66
+ sender.should_receive(:fire).with(s0.event_type_name, an_instance_of(Hash)) do |e1, h|
67
+ h[:source_name].should == s0.source_name
68
+ h[:properties].should == {:foo => 'bar'}
69
+ h[:key].should == s0._id
70
+ end
71
+
72
+ subject.send_scheduled_event s0
73
+ end
74
+ end
75
+
76
+ describe "#mark_schedule_done" do
77
+ it "実行したスケジュールは終了とする" do
78
+ s0 = Tengine::Core::Schedule.new(event_type_name: "test.event.not.tengine", source_name: "test://localhost/dev/null")
79
+ s0.save
80
+ subject.mark_schedule_done s0
81
+ s0.reload
82
+ s0.status.should == Tengine::Core::Schedule::FIRED
83
+ end
84
+
85
+ it "すでに終了していたら何もしない" do
86
+ s0 = Tengine::Core::Schedule.new(event_type_name: "test.event.not.tengine", source_name: "test://localhost/dev/null", status: Tengine::Core::Schedule::FIRED)
87
+ s0.save
88
+ s0.reload
89
+ t = s0.updated_at
90
+ subject.mark_schedule_done s0
91
+ s0.reload
92
+ s0.updated_at.should == t
93
+ end
94
+ end
95
+
96
+ describe "#sender" do
97
+ before do
98
+ conn = mock(:connection)
99
+ conn.stub(:on_tcp_connection_loss)
100
+ conn.stub(:after_recovery)
101
+ conn.stub(:on_closed)
102
+ AMQP.stub(:connect).with(an_instance_of(Hash)).and_return(conn)
103
+ end
104
+ subject { Tengine::Core::Scheduler.new([]).sender }
105
+ it { should be_kind_of(Tengine::Event::Sender) }
106
+ end
107
+
108
+ describe "#run" do
109
+ it "各種timerを登録する" do
110
+ EM.stub(:run).and_yield
111
+ Daemons.stub(:run_proc).with(anything, anything).and_yield
112
+ EM.should_receive(:add_periodic_timer).exactly(2).times
113
+ Tengine::Core::MethodTraceable.stub(:disabled=)
114
+ sender = mock(:sender)
115
+ sender.stub(:wait_for_connection).and_yield
116
+ subject.stub(:sender).and_return(sender)
117
+ subject.instance_eval { @config }.stub(:setup_loggers)
118
+ subject.run __FILE__
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,30 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ require 'stringio'
5
+
6
+ describe Tengine::Core::SelectableAttr do
7
+
8
+ module TestModule1
9
+ class TestClass1
10
+ include Tengine::Core::SelectableAttr
11
+ selectable_attr :foo do
12
+ entry 1, :a, "do"
13
+ entry 2, :b, "re"
14
+ entry 3, :c, "mi"
15
+ end
16
+ multi_selectable_attr :bar do
17
+ entry 1, :x, "red"
18
+ entry 2, :y, "green"
19
+ entry 3, :z, "blue"
20
+ end
21
+ end
22
+ end
23
+
24
+ it "i18n_scopeが設定されている" do
25
+ TestModule1::TestClass1.foo_enum.i18n_scope.should == ['selectable_attrs', 'test_module1/test_class1', 'foo']
26
+ TestModule1::TestClass1.bar_enum.i18n_scope.should == ['selectable_attrs', 'test_module1/test_class1', 'bar']
27
+ end
28
+
29
+
30
+ end
@@ -0,0 +1,104 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe Tengine::Core::Session do
5
+ context "should save Hash as properties" do
6
+ subject do
7
+ Tengine::Core::Session.create!(:properties => {
8
+ "key1" => 100,
9
+ "key2" => "string value",
10
+ "key3" => Time.utc(2011,9,4,20,58),
11
+ :key4 => [:array, "of", "variables", true, false, nil, 99.9999],
12
+ :key5 => {:nested => "hash"},
13
+ :key6 => :symbol_value,
14
+ })
15
+ end
16
+ it do
17
+ subject.properties["key1"].should == 100
18
+ subject.properties["key2"].should == "string value"
19
+ subject.properties["key3"].should == Time.utc(2011,9,4,20,58)
20
+ subject.properties[:key4].should == [:array, "of", "variables", true, false, nil, 99.9999]
21
+ subject.properties[:key5].should == {:nested => "hash"}
22
+ subject.properties[:key6].should == :symbol_value
23
+ end
24
+ it "should allow to read properties by using []" do
25
+ subject["key1"].should == 100
26
+ subject["key2"].should == "string value"
27
+ subject["key3"].should == Time.utc(2011,9,4,20,58)
28
+ subject[:key4].should == [:array, "of", "variables", true, false, nil, 99.9999]
29
+ subject[:key5].should == {:nested => "hash"}
30
+ subject[:key6].should == :symbol_value
31
+ end
32
+ it "should allow to write properties by using []=" do
33
+ subject["key1"] = 99
34
+ subject["key1"].should == 99
35
+ end
36
+ end
37
+
38
+ describe :update_with_lock do
39
+ before do
40
+ @session1 = Tengine::Core::Session.create!(
41
+ :lock_version => 2,
42
+ :properties => {
43
+ "key1" => 100,
44
+ "key2" => "string value",
45
+ "key3" => Time.utc(2011,9,4,20,58),
46
+ :key4 => [:array, "of", "variables", true, false, nil, 99.9999],
47
+ :key5 => {:nested => "hash"},
48
+ :key6 => :symbol_value,
49
+ })
50
+ end
51
+
52
+ it "競合がなければ素直に更新する" do
53
+ session = Tengine::Core::Session.find(@session1.id)
54
+ session.update_with_lock do
55
+ session.properties['key1'] += 1
56
+ end
57
+ session.reload
58
+ session.properties['key1'].should == 101
59
+ end
60
+
61
+ it "競合しても単純に上書きしたりせず、最新を取得し直して更新する" do
62
+ session1 = Tengine::Core::Session.find(@session1.id)
63
+ session2 = Tengine::Core::Session.find(@session1.id)
64
+ # session1を更新
65
+ session1.update_with_lock do
66
+ session1.properties['key1'] += 1
67
+ end
68
+ session1.properties['key1'].should == 101
69
+ # session2を更新
70
+ session1.update_with_lock do
71
+ session1.properties['key1'] += 1
72
+ end
73
+ session1.properties['key1'].should == 102
74
+ end
75
+
76
+ it "リトライ回数をオーバーすると例外が発生する" do
77
+ session1 = Tengine::Core::Session.find(@session1.id)
78
+ session2 = Tengine::Core::Session.find(@session1.id)
79
+
80
+ session1.update_with_lock{ session1.properties['key1'] += 1 } # [2] 100 -> [3] 101
81
+
82
+ f = Fiber.new do
83
+ session2.update_with_lock(:retry => 3) do
84
+ Fiber.yield
85
+ session1.properties['key1'] += 1
86
+ end
87
+ end
88
+ f.resume # [2] 100 -> [3] 101 を試みて失敗
89
+
90
+ session1.reload; session1.update_with_lock{ session1.properties['key1'] += 1 } # [3] 101 -> [4] 102
91
+ f.resume # [3] 101 -> [4] 102 を試みて失敗。リトライ1回
92
+
93
+ session1.reload; session1.update_with_lock{ session1.properties['key1'] += 1 } # [4] 102 -> [5] 103
94
+ f.resume # [4] 102 -> [5] 103 を試みて失敗。リトライ2回
95
+
96
+ session1.reload; session1.update_with_lock{ session1.properties['key1'] += 1 } # [5] 103 -> [6] 104
97
+ expect{
98
+ f.resume # [5] 103 -> [6] 104 を試みて失敗。リトライ3回失敗したので、例外がraiseされる
99
+ }.to raise_error(Tengine::Core::OptimisticLock::RetryOverError)
100
+ end
101
+
102
+ end
103
+
104
+ end