diff options
author | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2015-01-07 20:19:49 +0000 |
---|---|---|
committer | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2015-01-07 20:19:49 +0000 |
commit | 1a851318fdce55a7ffb2290de692282e294987f8 (patch) | |
tree | 6116e0043b56ed114334d5e8656b61d4a8b9666e /test | |
parent | bf5d58a76887c2d7819d9f4a1e28139de0ddc95c (diff) | |
download | redmine-1a851318fdce55a7ffb2290de692282e294987f8.tar.gz redmine-1a851318fdce55a7ffb2290de692282e294987f8.zip |
Replaces awesome_nested_set gem with a simple and more robust implementation of nested sets.
The concurrency tests added in this commit trigger dead locks and/or nested set inconsistency with awesome_nested_set.
git-svn-id: http://svn.redmine.org/redmine/trunk@13841 e93f8b46-1217-0410-a6f0-8f06a7374b81
Diffstat (limited to 'test')
-rw-r--r-- | test/test_helper.rb | 11 | ||||
-rw-r--r-- | test/unit/issue_nested_set_concurrency_test.rb | 73 | ||||
-rw-r--r-- | test/unit/project_nested_set_concurrency_test.rb | 76 |
3 files changed, 151 insertions, 9 deletions
diff --git a/test/test_helper.rb b/test/test_helper.rb index bc2d178c1..9f4274830 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -30,7 +30,6 @@ require Rails.root.join('test', 'mocks', 'open_id_authentication_mock.rb').to_s require File.expand_path(File.dirname(__FILE__) + '/object_helpers') include ObjectHelpers -require 'awesome_nested_set/version' require 'net/ldap' class ActionView::TestCase @@ -214,15 +213,9 @@ class ActiveSupport::TestCase mail.parts.first.body.encoded end - # awesome_nested_set new node lft and rgt value changed this refactor revision. - # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb938e40200cd90714dc69247ef017c61 - # The reason of behavior change is that "self.class.base_class.unscoped" was added to this line. - # https://github.com/collectiveidea/awesome_nested_set/commit/199fca9bb9#diff-f61b59a5e6319024e211b0ffdd0e4ef1R273 - # It seems correct behavior because of this line comment. - # https://github.com/collectiveidea/awesome_nested_set/blame/199fca9bb9/lib/awesome_nested_set/model.rb#L278 + # Returns the lft value for a new root issue def new_issue_lft - # ::AwesomeNestedSet::VERSION > "2.1.6" ? Issue.maximum(:rgt) + 1 : 1 - Issue.maximum(:rgt) + 1 + 1 end end diff --git a/test/unit/issue_nested_set_concurrency_test.rb b/test/unit/issue_nested_set_concurrency_test.rb new file mode 100644 index 000000000..cf45b81ae --- /dev/null +++ b/test/unit/issue_nested_set_concurrency_test.rb @@ -0,0 +1,73 @@ +# Redmine - project management software +# Copyright (C) 2006-2014 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require File.expand_path('../../test_helper', __FILE__) + +class IssueNestedSetConcurrencyTest < ActiveSupport::TestCase + fixtures :projects, :users, + :trackers, :projects_trackers, + :enabled_modules, + :issue_statuses, + :enumerations + + self.use_transactional_fixtures = false + + def setup + CustomField.delete_all + end + + def teardown + Issue.delete_all + end + + def test_concurrency + skip if sqlite? + with_settings :notified_events => [] do + # Generates an issue and destroys it in order + # to load all needed classes before starting threads + i = Issue.generate! + i.destroy + + root = Issue.generate! + assert_difference 'Issue.count', 60 do + threads = [] + 3.times do |i| + threads << Thread.new(i) do + ActiveRecord::Base.connection_pool.with_connection do + begin + 10.times do + i = Issue.generate! :parent_issue_id => root.id + c1 = Issue.generate! :parent_issue_id => i.id + c2 = Issue.generate! :parent_issue_id => i.id + c3 = Issue.generate! :parent_issue_id => i.id + c2.reload.destroy + c1.reload.destroy + end + rescue Exception => e + Thread.current[:exception] = e.message + end + end + end + end + threads.each do |thread| + thread.join + assert_nil thread[:exception] + end + end + end + end +end diff --git a/test/unit/project_nested_set_concurrency_test.rb b/test/unit/project_nested_set_concurrency_test.rb new file mode 100644 index 000000000..873d32a3a --- /dev/null +++ b/test/unit/project_nested_set_concurrency_test.rb @@ -0,0 +1,76 @@ +# Redmine - project management software +# Copyright (C) 2006-2014 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require File.expand_path('../../test_helper', __FILE__) + +class ProjectNestedSetConcurrencyTest < ActiveSupport::TestCase + self.use_transactional_fixtures = false + + def setup + CustomField.delete_all + end + + def teardown + Project.delete_all + end + + def test_concurrency + skip if sqlite? + # Generates a project and destroys it in order + # to load all needed classes before starting threads + p = generate_project! + p.destroy + + assert_difference 'Project.count', 60 do + threads = [] + 3.times do |i| + threads << Thread.new(i) do + ActiveRecord::Base.connection_pool.with_connection do + begin + 10.times do + p = generate_project! + c1 = generate_project! :parent_id => p.id + c2 = generate_project! :parent_id => p.id + c3 = generate_project! :parent_id => p.id + c2.reload.destroy + c1.reload.destroy + end + rescue Exception => e + Thread.current[:exception] = e.message + end + end + end + end + threads.each do |thread| + thread.join + assert_nil thread[:exception] + end + end + end + + # Generates a bare project with random name + # and identifier + def generate_project!(attributes={}) + identifier = "a"+Redmine::Utils.random_hex(6) + Project.generate!({ + :identifier => identifier, + :name => identifier, + :tracker_ids => [], + :enabled_module_names => [] + }.merge(attributes)) + end +end |