@@ -855,17 +855,109 class Issue < ActiveRecord::Base | |||||
855 |
|
855 | |||
856 | # Returns all the other issues that depend on the issue |
|
856 | # Returns all the other issues that depend on the issue | |
857 | def all_dependent_issues(except=[]) |
|
857 | def all_dependent_issues(except=[]) | |
858 | except << self |
|
858 | ||
|
859 | # The algorithm as a modified bread first search (bfs) | |||
|
860 | ||||
|
861 | # The found dependencies | |||
859 | dependencies = [] |
|
862 | dependencies = [] | |
860 | dependencies += relations_from.map(&:issue_to) |
|
863 | ||
861 | dependencies += children unless leaf? |
|
864 | # The visited flag for every node (issue) used by the breadth first search | |
862 | dependencies.compact! |
|
865 | eNOT_DISCOVERED = 0 # The issue is "new" to the algorithm, it has not seen it before. | |
|
866 | ||||
|
867 | ePROCESS_ALL = 1 # The issue is added to the queue. Process both children and relations of | |||
|
868 | # the issue when it is processed. | |||
|
869 | ||||
|
870 | ePROCESS_RELATIONS_ONLY = 2 # The issue was added to the queue and will be output as dependent issue, | |||
|
871 | # but its children will not be added to the queue when it is processed. | |||
|
872 | ||||
|
873 | eRELATIONS_PROCESSED = 3 # The related issues, the parent issue and the issue itself have been added to | |||
|
874 | # the queue, but its children have not been added. | |||
|
875 | ||||
|
876 | ePROCESS_CHILDREN_ONLY = 4 # The relations and the parent of the issue have been added to the queue, but | |||
|
877 | # the children still need to be processed. | |||
|
878 | ||||
|
879 | eALL_PROCESSED = 5 # The issue and all its children, its parent and its related issues have been | |||
|
880 | # added as dependent issues. It needs no further processing. | |||
|
881 | ||||
|
882 | issueStatus = Hash.new(eNOT_DISCOVERED) | |||
|
883 | ||||
|
884 | # The queue | |||
|
885 | queue = [] | |||
|
886 | ||||
|
887 | # Initialize the bfs, add start node (self) to the queue | |||
|
888 | queue << self | |||
|
889 | issueStatus[self] = ePROCESS_ALL | |||
|
890 | ||||
|
891 | while (!queue.empty?) do | |||
|
892 | ||||
|
893 | currentIssue = queue.shift | |||
|
894 | currentIssueStatus = issueStatus[currentIssue] | |||
|
895 | ||||
|
896 | dependencies << currentIssue | |||
|
897 | ||||
|
898 | # Add parent to queue, if not already in it. | |||
|
899 | parent = currentIssue.parent | |||
|
900 | parentStatus = issueStatus[parent] | |||
|
901 | ||||
|
902 | if parent && (parentStatus == eNOT_DISCOVERED) && !except.include?(parent) then | |||
|
903 | ||||
|
904 | queue << parent | |||
|
905 | issueStatus[parent] = ePROCESS_RELATIONS_ONLY | |||
|
906 | ||||
|
907 | end | |||
|
908 | ||||
|
909 | # Add children to queue, but only if they are not already in it and | |||
|
910 | # the children of the current node need to be processed. | |||
|
911 | if currentIssue.children && (currentIssueStatus == ePROCESS_CHILDREN_ONLY || currentIssueStatus == ePROCESS_ALL) then | |||
|
912 | ||||
|
913 | currentIssue.children.each do |child| | |||
|
914 | ||||
|
915 | if (issueStatus[child] == eNOT_DISCOVERED) && !except.include?(child) | |||
|
916 | queue << child | |||
|
917 | issueStatus[child] = ePROCESS_ALL | |||
|
918 | ||||
|
919 | elsif (issueStatus[child] == eRELATIONS_PROCESSED) && !except.include?(child) | |||
|
920 | queue << child | |||
|
921 | issueStatus[child] = ePROCESS_CHILDREN_ONLY | |||
|
922 | ||||
|
923 | elsif (issueStatus[child] == ePROCESS_RELATIONS_ONLY) && !except.include?(child) | |||
|
924 | queue << child | |||
|
925 | issueStatus[child] = ePROCESS_ALL | |||
|
926 | end | |||
|
927 | end | |||
|
928 | end | |||
|
929 | ||||
|
930 | # Add related issues to the queue, if they are not already in it. | |||
|
931 | currentIssue.relations_from.map(&:issue_to).each do |relatedIssue| | |||
|
932 | ||||
|
933 | if (issueStatus[relatedIssue] == eNOT_DISCOVERED) && !except.include?(relatedIssue) then | |||
|
934 | queue << relatedIssue | |||
|
935 | issueStatus[relatedIssue] = ePROCESS_ALL | |||
|
936 | ||||
|
937 | elsif (issueStatus[relatedIssue] == eRELATIONS_PROCESSED) && !except.include?(relatedIssue) then | |||
|
938 | queue << relatedIssue | |||
|
939 | issueStatus[relatedIssue] = ePROCESS_CHILDREN_ONLY | |||
|
940 | ||||
|
941 | elsif (issueStatus[relatedIssue] == ePROCESS_RELATIONS_ONLY) && !except.include?(relatedIssue) then | |||
|
942 | queue << relatedIssue | |||
|
943 | issueStatus[relatedIssue] = ePROCESS_ALL | |||
|
944 | end | |||
|
945 | end | |||
|
946 | ||||
|
947 | # Set new status for current issue | |||
|
948 | if (currentIssueStatus == ePROCESS_ALL) || (currentIssueStatus == ePROCESS_CHILDREN_ONLY) then | |||
|
949 | issueStatus[currentIssue] = eALL_PROCESSED | |||
|
950 | ||||
|
951 | elsif (currentIssueStatus == ePROCESS_RELATIONS_ONLY) then | |||
|
952 | issueStatus[currentIssue] = eRELATIONS_PROCESSED | |||
|
953 | end | |||
|
954 | ||||
|
955 | end # while | |||
|
956 | ||||
|
957 | # Remove the issues from the "except" parameter from the result array | |||
863 | dependencies -= except |
|
958 | dependencies -= except | |
864 | dependencies += dependencies.map {|issue| issue.all_dependent_issues(except)}.flatten |
|
959 | dependencies.delete(self) | |
865 | if parent |
|
960 | ||
866 | dependencies << parent |
|
|||
867 | dependencies += parent.all_dependent_issues(except + parent.descendants) |
|
|||
868 | end |
|
|||
869 | dependencies |
|
961 | dependencies | |
870 | end |
|
962 | end | |
871 |
|
963 |
@@ -1795,6 +1795,136 class IssueTest < ActiveSupport::TestCase | |||||
1795 | assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort |
|
1795 | assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort | |
1796 | end |
|
1796 | end | |
1797 |
|
1797 | |||
|
1798 | def test_all_dependent_issues_with_subtask | |||
|
1799 | IssueRelation.delete_all | |||
|
1800 | ||||
|
1801 | project = Project.generate!(:name => "testproject") | |||
|
1802 | ||||
|
1803 | parentIssue = Issue.generate!(:project => project) | |||
|
1804 | childIssue1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue.id) | |||
|
1805 | childIssue2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue.id) | |||
|
1806 | ||||
|
1807 | assert_equal [childIssue1.id, childIssue2.id].sort, parentIssue.all_dependent_issues.collect(&:id).uniq.sort | |||
|
1808 | end | |||
|
1809 | ||||
|
1810 | def test_all_dependent_issues_does_not_include_self | |||
|
1811 | IssueRelation.delete_all | |||
|
1812 | ||||
|
1813 | project = Project.generate!(:name => "testproject") | |||
|
1814 | ||||
|
1815 | parentIssue = Issue.generate!(:project => project) | |||
|
1816 | childIssue = Issue.generate!(:project => project, :parent_issue_id => parentIssue.id) | |||
|
1817 | ||||
|
1818 | assert_equal [childIssue.id], parentIssue.all_dependent_issues.collect(&:id) | |||
|
1819 | end | |||
|
1820 | ||||
|
1821 | def test_all_dependent_issues_with_parenttask_and_sibling | |||
|
1822 | IssueRelation.delete_all | |||
|
1823 | ||||
|
1824 | project = Project.generate!(:name => "testproject") | |||
|
1825 | ||||
|
1826 | parentIssue = Issue.generate!(:project => project) | |||
|
1827 | childIssue1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue.id) | |||
|
1828 | childIssue2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue.id) | |||
|
1829 | ||||
|
1830 | assert_equal [parentIssue.id].sort, childIssue1.all_dependent_issues.collect(&:id) | |||
|
1831 | end | |||
|
1832 | ||||
|
1833 | def test_all_dependent_issues_with_relation_to_leaf_in_other_tree | |||
|
1834 | IssueRelation.delete_all | |||
|
1835 | ||||
|
1836 | project = Project.generate!(:name => "testproject") | |||
|
1837 | ||||
|
1838 | parentIssue1 = Issue.generate!(:project => project) | |||
|
1839 | childIssue1_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id) | |||
|
1840 | childIssue1_2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id) | |||
|
1841 | ||||
|
1842 | parentIssue2 = Issue.generate!(:project => project) | |||
|
1843 | childIssue2_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id) | |||
|
1844 | childIssue2_2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id) | |||
|
1845 | ||||
|
1846 | ||||
|
1847 | assert IssueRelation.create(:issue_from => parentIssue1, | |||
|
1848 | :issue_to => childIssue2_2, | |||
|
1849 | :relation_type => IssueRelation::TYPE_BLOCKS) | |||
|
1850 | ||||
|
1851 | assert_equal [childIssue1_1.id, childIssue1_2.id, parentIssue2.id, childIssue2_2.id].sort, | |||
|
1852 | parentIssue1.all_dependent_issues.collect(&:id).uniq.sort | |||
|
1853 | end | |||
|
1854 | ||||
|
1855 | def test_all_dependent_issues_with_relation_to_parent_in_other_tree | |||
|
1856 | IssueRelation.delete_all | |||
|
1857 | ||||
|
1858 | project = Project.generate!(:name => "testproject") | |||
|
1859 | ||||
|
1860 | parentIssue1 = Issue.generate!(:project => project) | |||
|
1861 | childIssue1_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id) | |||
|
1862 | childIssue1_2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id) | |||
|
1863 | ||||
|
1864 | parentIssue2 = Issue.generate!(:project => project) | |||
|
1865 | childIssue2_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id) | |||
|
1866 | childIssue2_2 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id) | |||
|
1867 | ||||
|
1868 | ||||
|
1869 | assert IssueRelation.create(:issue_from => parentIssue1, | |||
|
1870 | :issue_to => parentIssue2, | |||
|
1871 | :relation_type => IssueRelation::TYPE_BLOCKS) | |||
|
1872 | ||||
|
1873 | assert_equal [childIssue1_1.id, childIssue1_2.id, parentIssue2.id, childIssue2_1.id, childIssue2_2.id].sort, | |||
|
1874 | parentIssue1.all_dependent_issues.collect(&:id).uniq.sort | |||
|
1875 | end | |||
|
1876 | ||||
|
1877 | def test_all_dependent_issues_with_transitive_relation | |||
|
1878 | IssueRelation.delete_all | |||
|
1879 | ||||
|
1880 | project = Project.generate!(:name => "testproject") | |||
|
1881 | ||||
|
1882 | parentIssue1 = Issue.generate!(:project => project) | |||
|
1883 | childIssue1_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id) | |||
|
1884 | ||||
|
1885 | parentIssue2 = Issue.generate!(:project => project) | |||
|
1886 | childIssue2_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id) | |||
|
1887 | ||||
|
1888 | independentIssue = Issue.generate!(:project => project) | |||
|
1889 | ||||
|
1890 | assert IssueRelation.create(:issue_from => parentIssue1, | |||
|
1891 | :issue_to => childIssue2_1, | |||
|
1892 | :relation_type => IssueRelation::TYPE_RELATES) | |||
|
1893 | ||||
|
1894 | assert IssueRelation.create(:issue_from => childIssue2_1, | |||
|
1895 | :issue_to => independentIssue, | |||
|
1896 | :relation_type => IssueRelation::TYPE_RELATES) | |||
|
1897 | ||||
|
1898 | assert_equal [childIssue1_1.id, parentIssue2.id, childIssue2_1.id, independentIssue.id].sort, | |||
|
1899 | parentIssue1.all_dependent_issues.collect(&:id).uniq.sort | |||
|
1900 | end | |||
|
1901 | ||||
|
1902 | def test_all_dependent_issues_with_transitive_relation2 | |||
|
1903 | IssueRelation.delete_all | |||
|
1904 | ||||
|
1905 | project = Project.generate!(:name => "testproject") | |||
|
1906 | ||||
|
1907 | parentIssue1 = Issue.generate!(:project => project) | |||
|
1908 | childIssue1_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue1.id) | |||
|
1909 | ||||
|
1910 | parentIssue2 = Issue.generate!(:project => project) | |||
|
1911 | childIssue2_1 = Issue.generate!(:project => project, :parent_issue_id => parentIssue2.id) | |||
|
1912 | ||||
|
1913 | independentIssue = Issue.generate!(:project => project) | |||
|
1914 | ||||
|
1915 | assert IssueRelation.create(:issue_from => parentIssue1, | |||
|
1916 | :issue_to => independentIssue, | |||
|
1917 | :relation_type => IssueRelation::TYPE_RELATES) | |||
|
1918 | ||||
|
1919 | assert IssueRelation.create(:issue_from => independentIssue, | |||
|
1920 | :issue_to => childIssue2_1, | |||
|
1921 | :relation_type => IssueRelation::TYPE_RELATES) | |||
|
1922 | ||||
|
1923 | assert_equal [childIssue1_1.id, parentIssue2.id, childIssue2_1.id, independentIssue.id].sort, | |||
|
1924 | parentIssue1.all_dependent_issues.collect(&:id).uniq.sort | |||
|
1925 | ||||
|
1926 | end | |||
|
1927 | ||||
1798 | def test_all_dependent_issues_with_persistent_circular_dependency |
|
1928 | def test_all_dependent_issues_with_persistent_circular_dependency | |
1799 | IssueRelation.delete_all |
|
1929 | IssueRelation.delete_all | |
1800 | assert IssueRelation.create!(:issue_from => Issue.find(1), |
|
1930 | assert IssueRelation.create!(:issue_from => Issue.find(1), |
General Comments 0
You need to be logged in to leave comments.
Login now