diff --git a/src/applications/herald/adapter/HeraldAdapter.php b/src/applications/herald/adapter/HeraldAdapter.php --- a/src/applications/herald/adapter/HeraldAdapter.php +++ b/src/applications/herald/adapter/HeraldAdapter.php @@ -44,6 +44,12 @@ const FIELD_ARCANIST_PROJECT = 'arcanist-project'; const FIELD_PUSHER_IS_COMMITTER = 'pusher-is-committer'; const FIELD_PATH = 'path'; + const FIELD_ARC_HAS_NO_UNIT_TESTS = 'no-unit-tests'; + const FIELD_ARC_UNIT_TESTS_SKIPPED = 'unit-tests-skipped'; + const FIELD_ARC_LINT_SKIPPED = 'lint-skipped'; + const FIELD_DIFF_LINES = 'diff-lines'; + const FIELD_DIFF_ADDED_LINES = 'diff-added-lines'; + const FIELD_DIFF_REMOVED_LINES = 'diff-removed-lines'; const CONDITION_CONTAINS = 'contains'; const CONDITION_NOT_CONTAINS = '!contains'; @@ -68,6 +74,12 @@ const CONDITION_NOT_BIT = '!bit'; const CONDITION_IS_TRUE = 'true'; const CONDITION_IS_FALSE = 'false'; + const CONDITION_LESS_THAN = 'less'; + const CONDITION_GREATER_THAN = '!lesseq'; + const CONDITION_LESS_EQUAL = 'lesseq'; + const CONDITION_GREATER_EQUAL = '!less'; + const CONDITION_EQUAL = 'eq'; + const CONDITION_NOT_EQUAL = '!eq'; const ACTION_ADD_CC = 'addcc'; const ACTION_REMOVE_CC = 'remcc'; @@ -317,6 +329,12 @@ self::FIELD_ARCANIST_PROJECT => pht('Arcanist Project'), self::FIELD_PUSHER_IS_COMMITTER => pht('Pusher same as committer'), self::FIELD_PATH => pht('Path'), + self::FIELD_ARC_HAS_NO_UNIT_TESTS => pht('No unit tests ran on arc diff'), + self::FIELD_ARC_UNIT_TESTS_SKIPPED => pht('Unit tests were skipped on arc diff'), + self::FIELD_ARC_LINT_SKIPPED => pht('Lint was skipped on arc diff'), + self::FIELD_DIFF_LINES => pht('Number of lines'), + self::FIELD_DIFF_ADDED_LINES => pht('Number of added lines'), + self::FIELD_DIFF_REMOVED_LINES => pht('Number of removed lines'), ) + $this->getCustomFieldNameMap(); } @@ -349,6 +367,12 @@ self::CONDITION_REGEXP_PAIR => pht('matches regexp pair'), self::CONDITION_HAS_BIT => pht('has bit'), self::CONDITION_NOT_BIT => pht('lacks bit'), + self::CONDITION_LESS_THAN => pht('less than'), + self::CONDITION_LESS_EQUAL => pht('less than or equal to'), + self::CONDITION_GREATER_THAN => pht('greater than'), + self::CONDITION_GREATER_EQUAL => pht('greater than or equal to'), + self::CONDITION_EQUAL => pht('equal to'), + self::CONDITION_NOT_EQUAL => pht('not equal to'), ); } @@ -453,10 +477,24 @@ case self::FIELD_DIFF_ENORMOUS: case self::FIELD_IS_NEW_OBJECT: case self::FIELD_PUSHER_IS_COMMITTER: + case self::FIELD_ARC_HAS_NO_UNIT_TESTS: + case self::FIELD_ARC_UNIT_TESTS_SKIPPED: + case self::FIELD_ARC_LINT_SKIPPED: return array( self::CONDITION_IS_TRUE, self::CONDITION_IS_FALSE, ); + case self::FIELD_DIFF_LINES: + case self::FIELD_DIFF_ADDED_LINES: + case self::FIELD_DIFF_REMOVED_LINES: + return array( + self::CONDITION_LESS_THAN, + self::CONDITION_GREATER_THAN, + self::CONDITION_LESS_EQUAL, + self::CONDITION_GREATER_EQUAL, + self::CONDITION_EQUAL, + self::CONDITION_NOT_EQUAL, + ); default: if ($this->isHeraldCustomKey($field)) { return $this->getCustomFieldConditions($field); @@ -488,9 +526,19 @@ case self::CONDITION_NOT_CONTAINS: return (stripos($field_value, $condition_value) === false); case self::CONDITION_IS: + case self::CONDITION_EQUAL: return ($field_value == $condition_value); case self::CONDITION_IS_NOT: + case self::CONDITION_NOT_EQUAL: return ($field_value != $condition_value); + case self::CONDITION_LESS_THAN: + return ($field_value < $condition_value); + case self::CONDITION_LESS_EQUAL: + return ($field_value <= $condition_value); + case self::CONDITION_GREATER_THAN: + return ($field_value > $condition_value); + case self::CONDITION_GREATER_EQUAL: + return ($field_value >= $condition_value); case self::CONDITION_IS_ME: return ($field_value == $rule->getAuthorPHID()); case self::CONDITION_IS_NOT_ME: @@ -690,6 +738,12 @@ case self::CONDITION_NOT_BIT: case self::CONDITION_IS_TRUE: case self::CONDITION_IS_FALSE: + case self::CONDITION_LESS_THAN: + case self::CONDITION_LESS_EQUAL: + case self::CONDITION_GREATER_THAN: + case self::CONDITION_GREATER_EQUAL: + case self::CONDITION_EQUAL: + case self::CONDITION_NOT_EQUAL: // No explicit validation for these types, although there probably // should be in some cases. break; @@ -830,6 +884,12 @@ case self::CONDITION_NOT_CONTAINS: case self::CONDITION_REGEXP: case self::CONDITION_REGEXP_PAIR: + case self::CONDITION_LESS_THAN: + case self::CONDITION_GREATER_THAN: + case self::CONDITION_LESS_EQUAL: + case self::CONDITION_GREATER_EQUAL: + case self::CONDITION_EQUAL: + case self::CONDITION_NOT_EQUAL: return self::VALUE_TEXT; case self::CONDITION_IS: case self::CONDITION_IS_NOT: diff --git a/src/applications/herald/adapter/HeraldDifferentialAdapter.php b/src/applications/herald/adapter/HeraldDifferentialAdapter.php --- a/src/applications/herald/adapter/HeraldDifferentialAdapter.php +++ b/src/applications/herald/adapter/HeraldDifferentialAdapter.php @@ -62,6 +62,14 @@ return $changeset->getAbsoluteRepositoryPath($repository, $diff); } + protected function countDictionary(array $dict) { + $lines = 0; + foreach ($dict as $path => $content) { + $lines += count(phutil_split_lines($content)); + } + return $lines; + } + protected function loadContentDictionary() { $add_lines = DifferentialHunk::FLAG_LINES_ADDED; $rem_lines = DifferentialHunk::FLAG_LINES_REMOVED; diff --git a/src/applications/herald/adapter/HeraldDifferentialDiffAdapter.php b/src/applications/herald/adapter/HeraldDifferentialDiffAdapter.php --- a/src/applications/herald/adapter/HeraldDifferentialDiffAdapter.php +++ b/src/applications/herald/adapter/HeraldDifferentialDiffAdapter.php @@ -56,6 +56,12 @@ self::FIELD_DIFF_CONTENT, self::FIELD_DIFF_ADDED_CONTENT, self::FIELD_DIFF_REMOVED_CONTENT, + self::FIELD_ARC_HAS_NO_UNIT_TESTS, + self::FIELD_ARC_UNIT_TESTS_SKIPPED, + self::FIELD_ARC_LINT_SKIPPED, + self::FIELD_DIFF_LINES, + self::FIELD_DIFF_ADDED_LINES, + self::FIELD_DIFF_REMOVED_LINES, ), parent::getFields()); } @@ -117,6 +123,31 @@ return $this->loadAddedContentDictionary(); case self::FIELD_DIFF_REMOVED_CONTENT: return $this->loadRemovedContentDictionary(); + case self::FIELD_ARC_HAS_NO_UNIT_TESTS: + $load_props = id(new DifferentialDiffProperty())->loadAllWhere( + 'diffID = %d AND name = %s', + $this->getDiff()->getID(), + 'arc:unit'); + $load_props = mpull($load_props, 'getData', 'getName'); + $this->getDiff()->attachProperty('arc:unit', idx($load_props, 'arc:unit')); + + return + ($this->getDiff()->getUnitStatus() == DifferentialUnitStatus::UNIT_NONE) || + ($this->getDiff()->getProperty('arc:unit') === null) || + (count($this->getDiff()->getProperty('arc:unit')) == 0); + case self::FIELD_ARC_UNIT_TESTS_SKIPPED: + return $this->getDiff()->getUnitStatus() == DifferentialUnitStatus::UNIT_SKIP; + case self::FIELD_ARC_LINT_SKIPPED: + return $this->getDiff()->getLintStatus() == DifferentialLintStatus::LINT_SKIP; + case self::FIELD_DIFF_LINES: + return $this->countDictionary( + $this->loadContentDictionary()); + case self::FIELD_DIFF_ADDED_LINES: + return $this->countDictionary( + $this->loadAddedContentDictionary()); + case self::FIELD_DIFF_REMOVED_LINES: + return $this->countDictionary( + $this->loadRemovedContentDictionary()); } return parent::getHeraldField($field); diff --git a/src/applications/herald/adapter/HeraldDifferentialRevisionAdapter.php b/src/applications/herald/adapter/HeraldDifferentialRevisionAdapter.php --- a/src/applications/herald/adapter/HeraldDifferentialRevisionAdapter.php +++ b/src/applications/herald/adapter/HeraldDifferentialRevisionAdapter.php @@ -78,6 +78,12 @@ self::FIELD_AFFECTED_PACKAGE_OWNER, self::FIELD_IS_NEW_OBJECT, self::FIELD_ARCANIST_PROJECT, + self::FIELD_ARC_HAS_NO_UNIT_TESTS, + self::FIELD_ARC_UNIT_TESTS_SKIPPED, + self::FIELD_ARC_LINT_SKIPPED, + self::FIELD_DIFF_LINES, + self::FIELD_DIFF_ADDED_LINES, + self::FIELD_DIFF_REMOVED_LINES, ), parent::getFields()); } @@ -262,6 +268,31 @@ mpull($packages, 'getID')); case self::FIELD_ARCANIST_PROJECT: return $this->revision->getArcanistProjectPHID(); + case self::FIELD_ARC_HAS_NO_UNIT_TESTS: + $load_props = id(new DifferentialDiffProperty())->loadAllWhere( + 'diffID = %d AND name = %s', + $this->getDiff()->getID(), + 'arc:unit'); + $load_props = mpull($load_props, 'getData', 'getName'); + $this->getDiff()->attachProperty('arc:unit', idx($load_props, 'arc:unit')); + + return + ($this->getDiff()->getUnitStatus() == DifferentialUnitStatus::UNIT_NONE) || + ($this->getDiff()->getProperty('arc:unit') === null) || + (count($this->getDiff()->getProperty('arc:unit')) == 0); + case self::FIELD_ARC_UNIT_TESTS_SKIPPED: + return $this->getDiff()->getUnitStatus() == DifferentialUnitStatus::UNIT_SKIP; + case self::FIELD_ARC_LINT_SKIPPED: + return $this->getDiff()->getLintStatus() == DifferentialLintStatus::LINT_SKIP; + case self::FIELD_DIFF_LINES: + return $this->countDictionary( + $this->loadContentDictionary()); + case self::FIELD_DIFF_ADDED_LINES: + return $this->countDictionary( + $this->loadAddedContentDictionary()); + case self::FIELD_DIFF_REMOVED_LINES: + return $this->countDictionary( + $this->loadRemovedContentDictionary()); } return parent::getHeraldField($field);