@@ -855,17 +855,109 class Issue < ActiveRecord::Base | |||
|
855 | 855 | |
|
856 | 856 | # Returns all the other issues that depend on the issue |
|
857 | 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 | 862 | dependencies = [] |
|
860 | dependencies += relations_from.map(&:issue_to) | |
|
861 | dependencies += children unless leaf? | |
|
862 | dependencies.compact! | |
|
863 | ||
|
864 | # The visited flag for every node (issue) used by the breadth first search | |
|
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 | 958 | dependencies -= except |
|
864 | dependencies += dependencies.map {|issue| issue.all_dependent_issues(except)}.flatten | |
|
865 | if parent | |
|
866 | dependencies << parent | |
|
867 | dependencies += parent.all_dependent_issues(except + parent.descendants) | |
|
868 | end | |
|
959 | dependencies.delete(self) | |
|
960 | ||
|
869 | 961 | dependencies |
|
870 | 962 | end |
|
871 | 963 |
@@ -1795,6 +1795,136 class IssueTest < ActiveSupport::TestCase | |||
|
1795 | 1795 | assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort |
|
1796 | 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 | 1928 | def test_all_dependent_issues_with_persistent_circular_dependency |
|
1799 | 1929 | IssueRelation.delete_all |
|
1800 | 1930 | assert IssueRelation.create!(:issue_from => Issue.find(1), |
General Comments 0
You need to be logged in to leave comments.
Login now