Defect #14015 » all_dependend_issues.patch
test/unit/issue_test.rb (working copy) | ||
---|---|---|
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), |
app/models/issue.rb (working copy) | ||
---|---|---|
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 |