diff --git a/defines.php b/defines.php index c1c97c899..de83b054f 100644 --- a/defines.php +++ b/defines.php @@ -134,7 +134,23 @@ * Define for vipgoci_git_diffs_fetch() */ -define( 'VIPGOCI_GIT_DIFF_CALC_CHANGES', array ('+' => 'additions', '-' => 'deletions') ); +define( 'VIPGOCI_GIT_DIFF_CALC_CHANGES', array ('+' => 'additions', '-' => 'deletions') ); define( 'VIPGOCI_GIT_DIFF_DATA_SOURCE_GIT_REPO', 'local-git-repo' ); define( 'VIPGOCI_GIT_DIFF_DATA_SOURCE_GITHUB_API', 'github-api' ); + + +/** + * Define file number of lines limit + */ +define( 'VIPGOCI_SKIPPED_FILES', 'skipped-files' ); + +define( 'VIPGOCI_VALIDATION_MAXIMUM_LINES_LIMIT', 15000 ); +define( 'VIPGOCI_VALIDATION_MAXIMUM_LINES', 'max-lines' ); +define( + 'VIPGOCI_VALIDATION', + [ + VIPGOCI_VALIDATION_MAXIMUM_LINES + => 'Maximum number of lines exceeded (%d)' + ] +); diff --git a/file-validation.php b/file-validation.php new file mode 100644 index 000000000..b34e7aec6 --- /dev/null +++ b/file-validation.php @@ -0,0 +1,128 @@ + + * [ + * 'max-lines' => [$file_name], + * ], + * 'total' => 1 + * ] + * In a oop we could simply inject a service for caching + * Vars and set values in the constructor + */ +function vipgoci_validate( string $temp_file_name, string $file_name, string $commit_id, int $max_lines ): array { + $validation_result = array( 'total' => 0 ); + + if ( false === vipgoci_is_number_of_lines_valid( $temp_file_name, $file_name, $commit_id, $max_lines ) ) { + $validation_result['issues'][ VIPGOCI_VALIDATION_MAXIMUM_LINES ] = [ $file_name ]; + $validation_result['total'] = count( $validation_result['issues'] ); + } + + return $validation_result; +} + +/** + * @param string $temp_file_name + * @param string $file_name + * @param string $commit_id + * @param int $max_lines + * + * @return bool + */ +function vipgoci_is_number_of_lines_valid( string $temp_file_name, string $file_name, string $commit_id, int $max_lines ): bool { + /** + * Verifies if number of lines validation are in the cache + * If so, returns the value + */ + + $cache_key = vipgoci_cache_get_is_number_of_lines_valid_key( $file_name, $commit_id ); + $is_number_of_lines_valid = vipgoci_cache_get_is_number_of_lines_valid( $cache_key ); + if ( ! is_null( $is_number_of_lines_valid ) ) { + return $is_number_of_lines_valid; + } + + /** + * Calculates the file number of lines + * if it is > than the default limit (defined at defined) + * the bot won't scan it + */ + $cmd = sprintf( 'wc -l %s | awk \'{print $1;}\' 2>&1', escapeshellcmd( $temp_file_name ) ); + vipgoci_log( 'Validating number of lines', array( 'file_name' => $file_name ) ); + + $output = vipgoci_sanitize_string( + vipgoci_runtime_measure_shell_exec( $cmd, 'file_validation' ) + ); + + vipgoci_log( + 'Validation number of lines ', + array( 'file_name' => $file_name, 'cmd' => $cmd, 'output' => $output ) + ); + + $is_number_of_lines_valid = vipgoci_verify_number_of_lines_output( $output, $max_lines ); + + vipgoci_cache_set_is_number_of_lines_valid( $cache_key, $is_number_of_lines_valid ); + + return $is_number_of_lines_valid; +} + +/** + * @param array $cache_key + * @param bool $is_number_of_lines_valid + * + * Sets cache for converted is_number_of_lines_valid + */ +function vipgoci_cache_set_is_number_of_lines_valid( array $cache_key, bool $is_number_of_lines_valid ): void { + vipgoci_cache( + $cache_key, + $is_number_of_lines_valid === true ? 'true' : 'false' + ); +} + +/** + * @param string $output + * @param int $max_lines + * + * @return bool + */ +function vipgoci_verify_number_of_lines_output( string $output, int $max_lines ): bool { + return is_numeric( $output ) && $output < $max_lines; +} + +/** + * @param array $cache_key + * + * Verifies if there's cached validation result available + * returns it if it does, else returns null + * + * @return bool|null + */ +function vipgoci_cache_get_is_number_of_lines_valid( array $cache_key ): ?bool { + $cached_value = vipgoci_cache( $cache_key ); + + return false !== $cached_value ? 'true' === $cached_value : null; +} + +/** + * @param string $file_name + * @param string $commit_id + * Builds the cache key for the number of lines validation + * + * @return array + */ +function vipgoci_cache_get_is_number_of_lines_valid_key( string $file_name, string $commit_id ): array { + return array( __FUNCTION__, $file_name, $commit_id ); +} diff --git a/github-api.php b/github-api.php index d1a15484b..b38655c2a 100644 --- a/github-api.php +++ b/github-api.php @@ -2593,7 +2593,6 @@ function vipgoci_github_pr_review_submit( } } - /* * If there are no issues to report to GitHub, * do not continue processing the Pull-Request. @@ -2602,7 +2601,8 @@ function vipgoci_github_pr_review_submit( if ( ( false === $github_errors ) && ( false === $github_warnings ) && - ( false === $github_info ) + ( false === $github_info ) && + empty( $results[ VIPGOCI_SKIPPED_FILES ][ $pr_number ] ) ) { continue; } @@ -2718,6 +2718,16 @@ function vipgoci_github_pr_review_submit( } } + /** + * Format skipped files message if it validation has issues + */ + if ( ! empty( $results[ VIPGOCI_SKIPPED_FILES ][ $pr_number ] ) ) { + $github_postfields[ 'body' ] .= vipgoci_get_skipped_files_message( + $results[ VIPGOCI_SKIPPED_FILES ][ $pr_number ], + $pr_number + ); + } + /* * If we have a informational-URL about * the bot, append it along with a generic diff --git a/lint-scan.php b/lint-scan.php index 24ba9cad9..094e7eee0 100644 --- a/lint-scan.php +++ b/lint-scan.php @@ -198,7 +198,8 @@ function vipgoci_lint_parse_results( function vipgoci_lint_scan_commit( $options, &$commit_issues_submit, - &$commit_issues_stats + &$commit_issues_stats, + array &$commit_skipped_files ) { $repo_owner = $options['repo-owner']; $repo_name = $options['repo-name']; @@ -258,6 +259,7 @@ function vipgoci_lint_scan_commit( /* * Lint every PHP file existing in the commit + * $commit_tree is an array of files for that commit */ foreach( $commit_tree as $filename ) { @@ -279,6 +281,31 @@ function vipgoci_lint_scan_commit( $file_contents ); + /** + * Validates the file + * and if it's not valid, the scans skips it + */ + if ( true === $options['skip-large-files'] ) { + $validation = vipgoci_validate( + $temp_file_name, + $filename, + $commit_id, + $options[ 'skip-large-files-limit' ] + ); + if ( 0 !== $validation[ 'total' ] ) { + unlink( $temp_file_name ); + + vipgoci_set_prs_implicated_skipped_files( $prs_implicated, $commit_skipped_files, $validation ); + vipgoci_runtime_measure( VIPGOCI_RUNTIME_STOP, 'lint_scan_single_file' ); + + continue; + } + } + + /** + * The lint scan will only proceed if the file is valid + * + */ /* * Keep statistics of what we do. */ diff --git a/main.php b/main.php index 75a0656c3..49280eb4b 100755 --- a/main.php +++ b/main.php @@ -24,6 +24,10 @@ function vipgoci_help_print( $argv ) { "\t" . ' that expect a URL are HTTPS and not HTTP. Default is true.' . PHP_EOL . "\t" . '--skip-draft-prs=BOOL If true, skip scanning of all Pull-Requests that are in draft mode.' . PHP_EOL . "\t" . ' Default is false.' . PHP_EOL . + "\t" . '--skip-large-files=true=BOOL If true, skip scanning files that have number of lines higher than the skip-large-files-limit value.' . PHP_EOL . + "\t" . ' Default is true.' . PHP_EOL . + "\t" . '--skip-large-files-limit=INTEGER Defines the maximum number of lines limit per file.' . PHP_EOL . + "\t" . ' Default is 15000 lines.' . PHP_EOL . "\t" . '--branches-ignore=STRING,... What branches to ignore -- useful to make sure' . PHP_EOL . "\t" . ' some branches never get scanned. Separate branches' . PHP_EOL . "\t" . ' with commas.' . PHP_EOL . @@ -224,6 +228,8 @@ function vipgoci_options_recognized() { 'skip-draft-prs:', 'branches-ignore:', 'local-git-repo:', + 'skip-large-files:', + 'skip-large-files-limit:', /* * Environmental & repo configuration @@ -380,7 +386,15 @@ function vipgoci_exit_status( $results ) { return VIPGOCI_EXIT_CODE_ISSUES; } } + } + if ( ! empty( $results['skipped-files'] ) ) { + foreach ( $results['skipped-files'] as $pr_number ) { + if( 0 < $pr_number[ 'total' ] ) { + // Results contains skipped files due issues, return non-zero + return VIPGOCI_EXIT_CODE_ISSUES; + } + } } return 0; @@ -813,10 +827,11 @@ function vipgoci_run() { range( 0, 500, 1 ) ); + vipgoci_option_integer_handle( $options, 'skip-large-files-limit', 15000 ); + /* * Handle boolean parameters */ - vipgoci_option_bool_handle( $options, 'skip-draft-prs', 'false' ); vipgoci_option_bool_handle( $options, 'phpcs', 'true' ); @@ -829,6 +844,8 @@ function vipgoci_run() { vipgoci_option_bool_handle( $options, 'lint', 'true' ); + vipgoci_option_bool_handle( $options, 'skip-large-files', 'true' ); + vipgoci_option_bool_handle( $options, 'lint-skip-folders-in-repo-options-file', 'false' ); vipgoci_option_bool_handle( $options, 'dismiss-stale-reviews', 'false' ); @@ -1654,7 +1671,7 @@ function vipgoci_run() { VIPGOCI_GITHUB_WEB_BASE_URL . '/' . rawurlencode( $options['repo-owner'] ) . '/' . rawurlencode( $options['repo-name'] ) . '/' . - 'commit/' . + 'commit/' . rawurlencode( $options['commit'] ) ); @@ -1671,7 +1688,7 @@ function vipgoci_run() { vipgoci_log( 'Starting up...', array( - 'options' => vipgoci_options_sensitive_clean( + 'options' => vipgoci_options_sensitive_clean( $options ) ) @@ -1889,7 +1906,8 @@ function vipgoci_run() { vipgoci_lint_scan_commit( $options, $results['issues'], - $results['stats'][ VIPGOCI_STATS_LINT ] + $results['stats'][ VIPGOCI_STATS_LINT ], + $results[ VIPGOCI_SKIPPED_FILES ] ); } @@ -1902,7 +1920,8 @@ function vipgoci_run() { vipgoci_phpcs_scan_commit( $options, $results['issues'], - $results['stats'][ VIPGOCI_STATS_PHPCS ] + $results['stats'][ VIPGOCI_STATS_PHPCS ], + $results[ VIPGOCI_SKIPPED_FILES ] ); } diff --git a/phpcs-scan.php b/phpcs-scan.php index 9e5823b92..ed47b32e7 100644 --- a/phpcs-scan.php +++ b/phpcs-scan.php @@ -212,6 +212,30 @@ function vipgoci_phpcs_scan_single_file( $file_contents ); + /* + * Skips the phpcs scan when the validation contains any issue + */ + if ( true === $options['skip-large-files'] ) { + $validation = vipgoci_validate( + $temp_file_name, + $file_name, + $options['commit'], + $options['skip-large-files-limit'] + ); + + if ( 0 !== $validation[ 'total' ] ) { + $skipped = array( + 'file_issues_arr_master' => array(), + 'file_issues_str' => array(), + 'temp_file_name' => $temp_file_name, + 'validation' => $validation + ); + unlink( $temp_file_name ); + + return $skipped; + } + } + vipgoci_log( 'About to PHPCS-scan file', array( @@ -297,6 +321,7 @@ function vipgoci_phpcs_scan_single_file( 'file_issues_arr_master' => $file_issues_arr_master, 'file_issues_str' => $file_issues_str, 'temp_file_name' => $temp_file_name, + 'validation' => $validation??[] ); } @@ -339,7 +364,8 @@ function vipgoci_phpcs_scan_output_dump( $output_file, $data ) { function vipgoci_phpcs_scan_commit( $options, &$commit_issues_submit, - &$commit_issues_stats + &$commit_issues_stats, + &$commit_skipped_files ) { $repo_owner = $options['repo-owner']; $repo_name = $options['repo-name']; @@ -506,6 +532,18 @@ function vipgoci_phpcs_scan_commit( vipgoci_runtime_measure( VIPGOCI_RUNTIME_START, 'phpcs_scan_single_file' ); foreach ( $pr_item_files_changed['all'] as $file_name ) { + if ( + isset( $commit_skipped_files[ $pr_item->number ][ 'issues' ][ VIPGOCI_VALIDATION_MAXIMUM_LINES ] ) + && true === in_array( + $file_name, + $commit_skipped_files[ $pr_item->number ][ 'issues' ][ VIPGOCI_VALIDATION_MAXIMUM_LINES ], + true + ) + ) { + $files_issues_arr[ $file_name ] = []; + continue; + } + /* * Loop through each file affected by * the commit. @@ -536,6 +574,31 @@ function vipgoci_phpcs_scan_commit( $file_name ); + if ( true === $options[ 'skip-large-files' ] && 0 !== $tmp_scanning_results[ 'validation' ][ 'total' ] ) { + vipgoci_log( + VIPGOCI_SKIPPED_FILES, + array( + 'repo_owner' => $repo_owner, + 'repo_name' => $repo_name, + 'commit_id' => $commit_id, + 'file' => $file_name, + ), + 0, + true + ); + + /* + * No further processing in case of invalid file + * Set the skipped file + * Set an empty array just in case to avoid warnings. + */ + + vipgoci_set_skipped_file( $commit_skipped_files, $tmp_scanning_results[ 'validation' ], $pr_item->number ); + $files_issues_arr[ $file_name ] = []; + + continue; + } + $file_issues_arr_master = $tmp_scanning_results['file_issues_arr_master']; diff --git a/requires.php b/requires.php index e41e8eae8..6f805dea3 100644 --- a/requires.php +++ b/requires.php @@ -10,6 +10,8 @@ require_once( __DIR__ . '/results.php' ); require_once( __DIR__ . '/options.php' ) ; require_once( __DIR__ . '/statistics.php' ); +require_once( __DIR__ . '/file-validation.php' ); +require_once( __DIR__ . '/skip-file.php' ); require_once( __DIR__ . '/phpcs-scan.php' ); require_once( __DIR__ . '/lint-scan.php' ); require_once( __DIR__ . '/auto-approval.php' ); diff --git a/skip-file.php b/skip-file.php new file mode 100644 index 000000000..e59c15c56 --- /dev/null +++ b/skip-file.php @@ -0,0 +1,93 @@ +number ); + } +} + +/** + * @param $skipped + * + * @return string + */ +function vipgoci_get_skipped_files_message( array $skipped ): string +{ + $body = '****' . PHP_EOL . '**' . VIPGOCI_SKIPPED_FILES . '**' . PHP_EOL; + foreach ( $skipped[ 'issues' ] as $issue => $file ) { + $body .= vipgoci_get_skipped_files_issue_message( + $skipped[ 'issues' ][ $issue ], + $issue + ); + } + + return $body; +} + +/** + * @param array $affected_files + * @param string $issue_type + * + * Get Markdown Skipped File error message + * @return string + */ +function vipgoci_get_skipped_files_issue_message( + array $affected_files, + string $issue_type, + int $max_lines = 15000 +): string { + $affected_files = implode( PHP_EOL . ' -', $affected_files ); + $validation_message = sprintf( + VIPGOCI_VALIDATION[ $issue_type ], + $max_lines + ); + + return sprintf( + '%s:%s -%s', + $validation_message, + PHP_EOL, + $affected_files + ); +} diff --git a/statistics.php b/statistics.php index eaac0fb8e..344119711 100644 --- a/statistics.php +++ b/statistics.php @@ -13,9 +13,13 @@ function vipgoci_stats_init( $options, $prs_implicated, &$results ) { * Initialize array for stats and * results of scanning, if needed. */ - if ( empty( $results['issues'][ $pr_item->number ] ) ) { - $results['issues'][ $pr_item->number ] = array( + $results['issues'][ $pr_item->number ] = array(); + } + + if ( empty( $results[ VIPGOCI_SKIPPED_FILES ][ $pr_item->number ] ) ) { + $results[ VIPGOCI_SKIPPED_FILES ][ $pr_item->number ] = array( + 'issues' => array(), 'total' => 0 ); } @@ -48,7 +52,6 @@ function vipgoci_stats_init( $options, $prs_implicated, &$results ) { } } - /* * A simple function to keep record of how * much a time a particular action takes to execute. @@ -282,4 +285,3 @@ function vipgoci_stats_per_file( $file_lines_cnt ); } - diff --git a/svg-scan.php b/svg-scan.php index 29175b4b8..0655c991a 100644 --- a/svg-scan.php +++ b/svg-scan.php @@ -251,6 +251,7 @@ function vipgoci_svg_scan_single_file( 'results' => $results, 'file_name' => $file_name, 'temp_file_name' => $temp_file_name, + 'validation' => array( 'total' => 0 ) ) ); @@ -260,6 +261,7 @@ function vipgoci_svg_scan_single_file( 'file_issues_arr_master' => $results, 'file_issues_str' => null, 'temp_file_name' => $temp_file_name, + 'validation' => array( 'total' => 0 ) ); } diff --git a/tests/A09LintLintScanCommitTest.php b/tests/A09LintLintScanCommitTest.php index bac5cbe46..2c1f2f839 100644 --- a/tests/A09LintLintScanCommitTest.php +++ b/tests/A09LintLintScanCommitTest.php @@ -6,16 +6,18 @@ final class A09LintLintScanCommitTest extends TestCase { var $options_lint_scan = array( - 'php-path' => null, - 'commit-test-lint-scan-commit-1' => null, - 'commit-test-lint-scan-commit-2' => null, + 'php-path' => null, + 'commit-test-lint-scan-commit-1' => null, + 'commit-test-lint-scan-commit-2' => null, + 'commit-test-lint-scan-commit-3' => null, + 'commit-test-lint-scan-commit-4' => null, ); var $options_git = array( - 'git-path' => null, - 'github-repo-url' => null, - 'repo-name' => null, - 'repo-owner' => null, + 'git-path' => null, + 'github-repo-url' => null, + 'repo-name' => null, + 'repo-owner' => null, ); protected function setUp(): void { @@ -34,7 +36,7 @@ protected function setUp(): void { $this->options_git ); - $this->options[ 'github-token' ] = + $this->options['github-token'] = vipgoci_unittests_get_config_value( 'git-secrets', 'github-token', @@ -52,6 +54,10 @@ protected function setUp(): void { $this->options['skip-draft-prs'] = false; + $this->options['skip-large-files'] = false; + + $this->options['skip-large-files-limit'] = 3; + global $vipgoci_debug_level; $vipgoci_debug_level = 2; } @@ -64,8 +70,8 @@ protected function tearDown(): void { } $this->options_lint_scan = null; - $this->options_git = null; - $this->options = null; + $this->options_git = null; + $this->options = null; } /** @@ -78,7 +84,7 @@ public function testLintDoScan1() { $this ); - if ( -1 === $options_test ) { + if ( - 1 === $options_test ) { return; } @@ -95,14 +101,15 @@ public function testLintDoScan1() { if ( false === $this->options['local-git-repo'] ) { $this->markTestSkipped( 'Could not set up git repository: ' . - vipgoci_unittests_output_get() + vipgoci_unittests_output_get() ); return; } - $issues_submit = array(); - $issues_stat = array(); + $issues_submit = array(); + $issues_stat = array(); + $issues_skipped = array(); /* * Get PRs implicated and warm up stats. @@ -115,12 +122,9 @@ public function testLintDoScan1() { $this->options['branches-ignore'] ); - foreach( $prs_implicated as $pr_item ) { - $issues_stat[ - $pr_item->number - ][ - 'error' - ] = 0; + foreach ( $prs_implicated as $pr_item ) { + $issues_stat[ $pr_item->number ]['error'] = 0; + $issues_skipped[ $pr_item->number ]['issues']['total'] = 0; } if ( @@ -138,7 +142,8 @@ public function testLintDoScan1() { vipgoci_lint_scan_commit( $this->options, $issues_submit, - $issues_stat + $issues_stat, + $issues_skipped ); vipgoci_unittests_output_unsuppress(); @@ -149,20 +154,20 @@ public function testLintDoScan1() { */ $issues_submit[ $pr_item->number ][0]['issue']['message'] = vipgoci_unittests_php_syntax_error_compat( - $issues_submit[ $pr_item->number][0]['issue']['message'] + $issues_submit[ $pr_item->number ][0]['issue']['message'] ); $this->assertSame( array( $pr_item->number => array( array( - 'type' => 'lint', + 'type' => 'lint', 'file_name' => 'lint-scan-commit-test-2.php', 'file_line' => 4, - 'issue' => array( - 'message' => "syntax error, unexpected end of file, expecting ',' or ';'", - 'level' => 'ERROR', - 'severity' => 5, + 'issue' => array( + 'message' => "syntax error, unexpected end of file, expecting ',' or ';'", + 'level' => 'ERROR', + 'severity' => 5, ) ) ) @@ -193,7 +198,7 @@ public function testLintDoScan2() { $this ); - if ( -1 === $options_test ) { + if ( - 1 === $options_test ) { return; } @@ -216,14 +221,15 @@ public function testLintDoScan2() { if ( false === $this->options['local-git-repo'] ) { $this->markTestSkipped( 'Could not set up git repository: ' . - vipgoci_unittests_output_get() + vipgoci_unittests_output_get() ); return; } - $issues_submit = array(); - $issues_stat = array(); + $issues_submit = array(); + $issues_stat = array(); + $issues_skipped = array(); /* * Get PRs implicated and warm up stats. @@ -236,12 +242,10 @@ public function testLintDoScan2() { $this->options['branches-ignore'] ); - foreach( $prs_implicated as $pr_item ) { - $issues_stat[ - $pr_item->number - ][ - 'error' - ] = 0; + foreach ( $prs_implicated as $pr_item ) { + $issues_stat[ $pr_item->number ]['error'] = 0; + + $issues_skipped[ $pr_item->number ]['issues']['total'] = 0; } if ( @@ -259,7 +263,8 @@ public function testLintDoScan2() { vipgoci_lint_scan_commit( $this->options, $issues_submit, - $issues_stat + $issues_stat, + $issues_skipped ); vipgoci_unittests_output_unsuppress(); @@ -270,12 +275,12 @@ public function testLintDoScan2() { */ $issues_submit[ $pr_item->number ][0]['issue']['message'] = vipgoci_unittests_php_syntax_error_compat( - $issues_submit[ $pr_item->number][0]['issue']['message'] + $issues_submit[ $pr_item->number ][0]['issue']['message'] ); $issues_submit[ $pr_item->number ][1]['issue']['message'] = vipgoci_unittests_php_syntax_error_compat( - $issues_submit[ $pr_item->number][1]['issue']['message'] + $issues_submit[ $pr_item->number ][1]['issue']['message'] ); @@ -283,27 +288,27 @@ public function testLintDoScan2() { array( $pr_item->number => array( array( - 'type' => 'lint', + 'type' => 'lint', 'file_name' => 'tests1/myfile1.php', 'file_line' => 4, - 'issue' => array( - 'message' => "syntax error, unexpected end of file, expecting ',' or ';'", - 'level' => 'ERROR', - 'severity' => 5, + 'issue' => array( + 'message' => "syntax error, unexpected end of file, expecting ',' or ';'", + 'level' => 'ERROR', + 'severity' => 5, ) ), array( - 'type' => 'lint', + 'type' => 'lint', 'file_name' => 'tests2/myfile1.php', 'file_line' => 4, - 'issue' => array( - 'message' => "syntax error, unexpected end of file, expecting ',' or ';'", - 'level' => 'ERROR', - 'severity' => 5, + 'issue' => array( + 'message' => "syntax error, unexpected end of file, expecting ',' or ';'", + 'level' => 'ERROR', + 'severity' => 5, ) ), /* - * Note: tests3/myfile1.php should be skipped, + * Note: tests3/myfile1.php should be skipped, * according to --lint-skip-folders option * set above. */ @@ -324,5 +329,287 @@ public function testLintDoScan2() { unset( $this->options['commit'] ); } + /** + * @covers ::vipgoci_lint_scan_commit + */ + public function testLintWillSkipLargeFileWhenOptionIsOn() { + + $options_test = vipgoci_unittests_options_test( + $this->options, + array( 'github-token', 'token' ), + $this + ); + + if ( - 1 === $options_test ) { + return; + } + + $this->options['commit'] = $this->options['commit-test-lint-scan-commit-3']; + + vipgoci_unittests_output_suppress(); + $this->options['local-git-repo'] = + vipgoci_unittests_setup_git_repo( + $this->options + ); + + if ( false === $this->options['local-git-repo'] ) { + $this->markTestSkipped( + 'Could not set up git repository: ' . + vipgoci_unittests_output_get() + ); + + return; + } + + $issues_submit = array(); + $issues_stat = array(); + $issues_skipped = array(); + + /* + * Get PRs implicated and warm up stats. + */ + $prs_implicated = vipgoci_github_prs_implicated( + $this->options['repo-owner'], + $this->options['repo-name'], + $this->options['commit'], + $this->options['github-token'], + $this->options['branches-ignore'] + ); + + foreach ( $prs_implicated as $pr_item ) { + $issues_stat[ $pr_item->number ]['error'] = 0; + $issues_skipped[ $pr_item->number ] = $this->getDefaultSkippedFilesDueIssuesMock(); + } + + if ( + ( ! isset( $pr_item->number ) ) || + ( ! is_numeric( $pr_item->number ) ) + ) { + $this->markTestSkipped( + 'Could not get Pull-Request information for the test: ' . + vipgoci_unittests_output_get() + ); + + return; + } + + $this->options['skip-large-files'] = true; + + vipgoci_lint_scan_commit( + $this->options, + $issues_submit, + $issues_stat, + $issues_skipped + ); + + vipgoci_unittests_output_unsuppress(); + + $expected_issues_skipped = array( + 39 => array( + 'issues' => array( + 'max-lines' => array( 'test1/myfile-1.php' ) + ), + 'total' => 1 + ) + ); + + $this->assertSame( + $expected_issues_skipped, + $issues_skipped + ); + + unset( $this->options['commit'] ); + } + + /** + * @covers ::vipgoci_lint_scan_commit + */ + public function testLintShouldScanAllFilesWhenSkipLargeFilesOptionIsOff() { + + $options_test = vipgoci_unittests_options_test( + $this->options, + array( 'github-token', 'token' ), + $this + ); + + if ( - 1 === $options_test ) { + return; + } + + $this->options['commit'] = $this->options['commit-test-lint-scan-commit-4']; + + $this->options['skip-large-files'] = false; + + vipgoci_unittests_output_suppress(); + + $this->options['local-git-repo'] = vipgoci_unittests_setup_git_repo( $this->options ); + + if ( false === $this->options['local-git-repo'] ) { + $this->markTestSkipped( + 'Could not set up git repository: ' . + vipgoci_unittests_output_get() + ); + + return; + } + + $issues_submit = array(); + $issues_stat = array(); + $issues_skipped = array(); + + /* + * Get PRs implicated and warm up stats. + */ + $prs_implicated = vipgoci_github_prs_implicated( + $this->options['repo-owner'], + $this->options['repo-name'], + $this->options['commit'], + $this->options['github-token'], + $this->options['branches-ignore'] + ); + + foreach ( $prs_implicated as $pr_item ) { + $issues_stat[ $pr_item->number ]['error'] = 0; + $issues_skipped[ $pr_item->number ] = $this->getDefaultSkippedFilesDueIssuesMock(); + } + + if ( + ! isset( $pr_item->number ) + || ! is_numeric( $pr_item->number ) + ) { + $this->markTestSkipped( + 'Could not get Pull-Request information for the test: ' . + vipgoci_unittests_output_get() + ); + + return; + } + + vipgoci_lint_scan_commit( + $this->options, + $issues_submit, + $issues_stat, + $issues_skipped + ); + + vipgoci_unittests_output_unsuppress(); + + $expected_issues_skipped = array( + 43 => array( + 'issues' => array(), + 'total' => 0 + ) + ); + + $this->assertSame( + $expected_issues_skipped, + $issues_skipped + ); + + unset( $this->options['commit'] ); + } + + /** + * @covers ::vipgoci_lint_scan_commit + */ + public function testLintShouldValidateAndSkipLargeFileWhenSkipLargeFilesAndLimitOptionsAreOn() { + + $options_test = vipgoci_unittests_options_test( + $this->options, + array( 'github-token', 'token' ), + $this + ); + + if ( - 1 === $options_test ) { + return; + } + + $this->options['commit'] = $this->options['commit-test-lint-scan-commit-4']; + + $this->options['skip-large-files'] = true; + $this->options['skip-large-files-limit'] = 15; + + vipgoci_unittests_output_suppress(); + + $this->options['local-git-repo'] = + vipgoci_unittests_setup_git_repo( + $this->options + ); + + if ( false === $this->options['local-git-repo'] ) { + $this->markTestSkipped( + 'Could not set up git repository: ' . + vipgoci_unittests_output_get() + ); + + return; + } + + $issues_submit = array(); + $issues_stat = array(); + $issues_skipped = array(); + + /* + * Get PRs implicated and warm up stats. + */ + $prs_implicated = vipgoci_github_prs_implicated( + $this->options['repo-owner'], + $this->options['repo-name'], + $this->options['commit'], + $this->options['github-token'], + $this->options['branches-ignore'] + ); + + foreach ( $prs_implicated as $pr_item ) { + $issues_stat[ $pr_item->number ]['error'] = 0; + $issues_skipped[ $pr_item->number ] = $this->getDefaultSkippedFilesDueIssuesMock(); + } + + if ( + ! isset( $pr_item->number ) + || ! is_numeric( $pr_item->number ) + ) { + $this->markTestSkipped( + 'Could not get Pull-Request information for the test: ' . + vipgoci_unittests_output_get() + ); + + return; + } + + vipgoci_lint_scan_commit( + $this->options, + $issues_submit, + $issues_stat, + $issues_skipped + ); + + vipgoci_unittests_output_unsuppress(); + + $expected_issues_skipped = array( + 43 => array( + 'issues' => array( + 'max-lines' => array ( + 0 => 'tests1/myfile1.php' + ) + ), + 'total' => 1 + ) + ); + + $this->assertSame( + $expected_issues_skipped, + $issues_skipped + ); + + unset( $this->options['commit'] ); + } + + private function getDefaultSkippedFilesDueIssuesMock() { + return array( + 'issues' => array(), + 'total' => 0 + ); + } } diff --git a/tests/PhpcsScanScanCommitTest.php b/tests/PhpcsScanScanCommitTest.php index 0d007faf8..c6a0fe1f1 100644 --- a/tests/PhpcsScanScanCommitTest.php +++ b/tests/PhpcsScanScanCommitTest.php @@ -6,20 +6,22 @@ final class PhpcsScanScanCommitTest extends TestCase { var $options_phpcs = array( - 'phpcs-path' => null, - 'phpcs-standard' => null, - 'phpcs-severity' => null, - 'phpcs-runtime-set' => null, - 'commit-test-phpcs-scan-commit-1' => null, - 'commit-test-phpcs-scan-commit-2' => null, - 'commit-test-phpcs-scan-commit-4' => null, + 'phpcs-path' => null, + 'phpcs-standard' => null, + 'phpcs-severity' => null, + 'phpcs-runtime-set' => null, + 'commit-test-phpcs-scan-commit-1' => null, + 'commit-test-phpcs-scan-commit-2' => null, + 'commit-test-phpcs-scan-commit-4' => null, + 'commit-test-phpcs-scan-commit-5' => null, + 'commit-test-phpcs-scan-commit-6' => null, ); var $options_git_repo = array( - 'repo-owner' => null, - 'repo-name' => null, - 'git-path' => null, - 'github-repo-url' => null, + 'repo-owner' => null, + 'repo-name' => null, + 'git-path' => null, + 'github-repo-url' => null, ); protected function setUp(): void { @@ -40,7 +42,7 @@ protected function setUp(): void { $this->options_phpcs ); - $this->options[ 'github-token' ] = + $this->options['github-token'] = vipgoci_unittests_get_config_value( 'git-secrets', 'github-token', @@ -48,7 +50,7 @@ protected function setUp(): void { ); if ( empty( $this->options['github-token'] ) ) { - $this->options['github-token'] = ''; + $this->options['github-token'] = ''; } $this->options['token'] = @@ -63,6 +65,10 @@ protected function setUp(): void { $this->options['phpcs-skip-folders'] = array(); $this->options['skip-draft-prs'] = false; + + $this->options['skip-large-files'] = false; + + $this->options['skip-large-files-limit'] = 15; } protected function tearDown(): void { @@ -72,9 +78,9 @@ protected function tearDown(): void { ); } - $this->options_phpcs = null; + $this->options_phpcs = null; $this->options_git_repo = null; - $this->options = null; + $this->options = null; } /** @@ -91,32 +97,26 @@ public function testDoScanTest1() { return; } - $this->options['commit'] = - $this->options['commit-test-phpcs-scan-commit-1']; - - $this->options['phpcs-skip-scanning-via-labels-allowed'] = - false; + $this->options['commit'] = $this->options['commit-test-phpcs-scan-commit-1']; + $this->options['phpcs-skip-scanning-via-labels-allowed'] = false; $issues_submit = array(); $issues_stats = array(); + $issues_skipped = array(); vipgoci_unittests_output_suppress(); - $prs_implicated = vipgoci_github_prs_implicated( - $this->options['repo-owner'], - $this->options['repo-name'], - $this->options['commit'], - $this->options['github-token'], - $this->options['branches-ignore'] - ); - + $prs_implicated = $this->getPRsImplicated(); foreach( $prs_implicated as $pr_item ) { $issues_stats[ - $pr_item->number + $pr_item->number ][ - 'error' + 'error' ] = 0; + + $issues_skipped[ $pr_item->number ][ 'issues' ][ 'max-lines' ] = array(); + $issues_skipped[ $pr_item->number ][ 'issues' ][ 'total' ] = 0; } @@ -128,16 +128,17 @@ public function testDoScanTest1() { if ( false === $this->options['local-git-repo'] ) { $this->markTestSkipped( 'Could not set up git repository: ' . - vipgoci_unittests_output_get() + vipgoci_unittests_output_get() ); - + return; } vipgoci_phpcs_scan_commit( $this->options, $issues_submit, - $issues_stats + $issues_stats, + $issues_skipped ); vipgoci_unittests_output_unsuppress(); @@ -235,24 +236,21 @@ public function testDoScanTest2() { $issues_submit = array(); $issues_stats = array(); + $issues_skipped = array(); vipgoci_unittests_output_suppress(); - $prs_implicated = vipgoci_github_prs_implicated( - $this->options['repo-owner'], - $this->options['repo-name'], - $this->options['commit'], - $this->options['github-token'], - $this->options['branches-ignore'] - ); - + $prs_implicated = $this->getPRsImplicated(); foreach( $prs_implicated as $pr_item ) { $issues_stats[ - $pr_item->number + $pr_item->number ][ - 'error' + 'error' ] = 0; + + $issues_skipped[ $pr_item->number ][ 'issues' ][ 'max-lines' ] = array(); + $issues_skipped[ $pr_item->number ][ 'issues' ][ 'total' ] = 0; } vipgoci_unittests_output_unsuppress(); @@ -285,16 +283,17 @@ public function testDoScanTest2() { if ( false === $this->options['local-git-repo'] ) { $this->markTestSkipped( 'Could not set up git repository: ' . - vipgoci_unittests_output_get() + vipgoci_unittests_output_get() ); - + return; } - + vipgoci_phpcs_scan_commit( $this->options, $issues_submit, - $issues_stats + $issues_stats, + $issues_skipped ); vipgoci_unittests_output_unsuppress(); @@ -390,30 +389,27 @@ public function testDoScanTest3() { $issues_submit = array(); $issues_stats = array(); + $issues_skipped = array(); vipgoci_unittests_output_suppress(); - $prs_implicated = vipgoci_github_prs_implicated( - $this->options['repo-owner'], - $this->options['repo-name'], - $this->options['commit'], - $this->options['github-token'], - $this->options['branches-ignore'] - ); - + $prs_implicated = $this->getPRsImplicated(); foreach( $prs_implicated as $pr_item ) { $issues_stats[ - $pr_item->number + $pr_item->number ][ - 'error' + 'error' ] = 0; $issues_stats[ - $pr_item->number + $pr_item->number ][ - 'warning' + 'warning' ] = 0; + + $issues_skipped[ $pr_item->number ][ 'issues' ][ 'max-lines' ] = array(); + $issues_skipped[ $pr_item->number ][ 'issues' ][ 'total' ] = 0; } @@ -425,16 +421,17 @@ public function testDoScanTest3() { if ( false === $this->options['local-git-repo'] ) { $this->markTestSkipped( 'Could not set up git repository: ' . - vipgoci_unittests_output_get() + vipgoci_unittests_output_get() ); - + return; } vipgoci_phpcs_scan_commit( $this->options, $issues_submit, - $issues_stats + $issues_stats, + $issues_skipped ); vipgoci_unittests_output_unsuppress(); @@ -562,32 +559,30 @@ public function testDoScanTest4() { $issues_submit = array(); $issues_stats = array(); + $issues_skipped = array(); vipgoci_unittests_output_suppress(); - $prs_implicated = vipgoci_github_prs_implicated( - $this->options['repo-owner'], - $this->options['repo-name'], - $this->options['commit'], - $this->options['github-token'], - $this->options['branches-ignore'] - ); + $prs_implicated = $this->getPRsImplicated(); vipgoci_unittests_output_unsuppress(); foreach( $prs_implicated as $pr_item ) { $issues_stats[ - $pr_item->number + $pr_item->number ][ - 'warning' + 'warning' ] = 0; $issues_stats[ - $pr_item->number + $pr_item->number ][ - 'error' + 'error' ] = 0; + + $issues_skipped[ $pr_item->number ][ 'issues' ][ 'max-lines' ] = array(); + $issues_skipped[ $pr_item->number ][ 'issues' ][ 'total' ] = 0; } vipgoci_unittests_output_suppress(); @@ -602,7 +597,7 @@ public function testDoScanTest4() { if ( false === $this->options['local-git-repo'] ) { $this->markTestSkipped( 'Could not set up git repository: ' . - vipgoci_unittests_output_get() + vipgoci_unittests_output_get() ); return; @@ -613,7 +608,8 @@ public function testDoScanTest4() { vipgoci_phpcs_scan_commit( $this->options, $issues_submit, - $issues_stats + $issues_stats, + $issues_skipped ); vipgoci_unittests_output_unsuppress(); @@ -657,7 +653,6 @@ public function testDoScanTest4() { ); } - /** * Tests when PHPCS uses basepath "." in its configuration. * @@ -670,7 +665,7 @@ public function testDoScanTest5() { $this ); - if ( -1 === $options_test ) { + if ( - 1 === $options_test ) { return; } @@ -684,8 +679,9 @@ public function testDoScanTest5() { $this->options['phpcs-skip-folders'] = array(); - $issues_submit = array(); - $issues_stats = array(); + $issues_submit = array(); + $issues_stats = array(); + $issues_skipped = array(); /* @@ -708,8 +704,8 @@ public function testDoScanTest5() { * the path returned. */ - $rand_str = rand(100000, 999999); - + $rand_str = rand( 100000, 999999 ); + $tmp_standard_new = str_replace( '/ruleset', '/ruleset-dir' . $rand_str . '/ruleset', @@ -718,7 +714,7 @@ public function testDoScanTest5() { $tmp_standard_dir = pathinfo( $tmp_standard_new, - PATHINFO_DIRNAME + PATHINFO_DIRNAME ); mkdir( @@ -731,40 +727,24 @@ public function testDoScanTest5() { ); $tmp_standard = $tmp_standard_new; - unset($tmp_standard_new); + unset( $tmp_standard_new ); /* * Actually use the new standard. */ $this->options['phpcs-standard'] = $tmp_standard; - vipgoci_unittests_output_suppress(); - $prs_implicated = vipgoci_github_prs_implicated( - $this->options['repo-owner'], - $this->options['repo-name'], - $this->options['commit'], - $this->options['github-token'], - $this->options['branches-ignore'] - ); + $prs_implicated = $this->getPRsImplicated(); - - foreach( $prs_implicated as $pr_item ) { - $issues_stats[ - $pr_item->number - ][ - 'error' - ] = 0; - - $issues_stats[ - $pr_item->number - ][ - 'warning' - ] = 0; + foreach ( $prs_implicated as $pr_item ) { + $issues_stats[ $pr_item->number ]['error'] = 0; + $issues_stats[ $pr_item->number ]['warning'] = 0; + $issues_skipped[ $pr_item->number ]['issues']['max-lines'] = array(); + $issues_skipped[ $pr_item->number ]['issues']['total'] = 0; } - $this->options['local-git-repo'] = vipgoci_unittests_setup_git_repo( $this->options @@ -773,16 +753,17 @@ public function testDoScanTest5() { if ( false === $this->options['local-git-repo'] ) { $this->markTestSkipped( 'Could not set up git repository: ' . - vipgoci_unittests_output_get() + vipgoci_unittests_output_get() ); - + return; } vipgoci_phpcs_scan_commit( $this->options, $issues_submit, - $issues_stats + $issues_stats, + $issues_skipped ); vipgoci_unittests_output_unsuppress(); @@ -802,98 +783,478 @@ public function testDoScanTest5() { array( 30 => array( array( - 'type' => 'phpcs', - 'file_name' => 'test.php', - 'file_line' => 3, - 'issue' => array( - 'message' => "All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found 'time'.", - 'source' => 'WordPress.Security.EscapeOutput.OutputNotEscaped', + 'type' => 'phpcs', + 'file_name' => 'test.php', + 'file_line' => 3, + 'issue' => array( + 'message' => "All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found 'time'.", + 'source' => 'WordPress.Security.EscapeOutput.OutputNotEscaped', 'severity' => 5, - 'fixable' => false, - 'type' => 'ERROR', - 'line' => 3, - 'column' => 20, - 'level' => 'ERROR', + 'fixable' => false, + 'type' => 'ERROR', + 'line' => 3, + 'column' => 20, + 'level' => 'ERROR', ) ), array( - 'type' => 'phpcs', - 'file_name' => 'test.php', - 'file_line' => 7, - 'issue' => array( - 'message' => "All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found 'time'.", - 'source' => 'WordPress.Security.EscapeOutput.OutputNotEscaped', + 'type' => 'phpcs', + 'file_name' => 'test.php', + 'file_line' => 7, + 'issue' => array( + 'message' => "All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found 'time'.", + 'source' => 'WordPress.Security.EscapeOutput.OutputNotEscaped', 'severity' => 5, - 'fixable' => false, - 'type' => 'ERROR', - 'line' => 7, - 'column' => 20, - 'level' => 'ERROR', + 'fixable' => false, + 'type' => 'ERROR', + 'line' => 7, + 'column' => 20, + 'level' => 'ERROR', ) ), array( - 'type' => 'phpcs', - 'file_name' => 'test.php', - 'file_line' => 10, - 'issue' => array( - 'message' => "Scripts should be registered/enqueued via `wp_enqueue_script`. This can improve the site's performance due to script concatenation.", - 'source' => 'WordPress.WP.EnqueuedResources.NonEnqueuedScript', + 'type' => 'phpcs', + 'file_name' => 'test.php', + 'file_line' => 10, + 'issue' => array( + 'message' => "Scripts should be registered/enqueued via `wp_enqueue_script`. This can improve the site's performance due to script concatenation.", + 'source' => 'WordPress.WP.EnqueuedResources.NonEnqueuedScript', 'severity' => 3, - 'fixable' => false, - 'type' => 'WARNING', - 'line' => 10, - 'column' => 6, - 'level' => 'WARNING' + 'fixable' => false, + 'type' => 'WARNING', + 'line' => 10, + 'column' => 6, + 'level' => 'WARNING' ) ), array( - 'type' => 'phpcs', - 'file_name' => 'tests1/some_phpcs_issues.php', - 'file_line' => 3, - 'issue' => array( - 'message' => "All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found 'time'.", - 'source' => 'WordPress.Security.EscapeOutput.OutputNotEscaped', + 'type' => 'phpcs', + 'file_name' => 'tests1/some_phpcs_issues.php', + 'file_line' => 3, + 'issue' => array( + 'message' => "All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found 'time'.", + 'source' => 'WordPress.Security.EscapeOutput.OutputNotEscaped', 'severity' => 5, - 'fixable' => false, - 'type' => 'ERROR', - 'line' => 3, - 'column' => 20, - 'level' => 'ERROR' + 'fixable' => false, + 'type' => 'ERROR', + 'line' => 3, + 'column' => 20, + 'level' => 'ERROR' ) ), array( - 'type' => 'phpcs', - 'file_name' => 'tests2/some_phpcs_issues.php', - 'file_line' => 3, - 'issue' => array( - 'message' => "All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found 'time'.", - 'source' => 'WordPress.Security.EscapeOutput.OutputNotEscaped', + 'type' => 'phpcs', + 'file_name' => 'tests2/some_phpcs_issues.php', + 'file_line' => 3, + 'issue' => array( + 'message' => "All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found 'time'.", + 'source' => 'WordPress.Security.EscapeOutput.OutputNotEscaped', 'severity' => 5, - 'fixable' => false, - 'type' => 'ERROR', - 'line' => 3, - 'column' => 20, - 'level' => 'ERROR', + 'fixable' => false, + 'type' => 'ERROR', + 'line' => 3, + 'column' => 20, + 'level' => 'ERROR', ) ), ) ), - $issues_submit ); $this->assertSame( array( 30 => array( - 'error' => 4, + 'error' => 4, 'warning' => 1, ), ), $issues_stats ); } + + /** + * @covers ::vipgoci_phpcs_scan_commit + * Should skip files with more than 15000 lines + * Expected to skip 1 file + * skip-large-files = true + * skip-large-files-limit = 15000 + */ + public function testLintSkipLargeFilesWhenSkipLargeFilesOptionIsOnAndFileIsLargerThanLimit() { + $options_test = vipgoci_unittests_options_test( + $this->options, + array( 'phpcs-runtime-set', 'github-token', 'token' ), + $this + ); + + if ( - 1 === $options_test ) { + return; + } + + $this->options['phpcs-skip-scanning-via-labels-allowed'] = false; + $this->options['commit'] = $this->options['commit-test-phpcs-scan-commit-5']; + vipgoci_unittests_output_suppress(); + + $prs_implicated = $this->getPRsImplicated(); + + $this->options['local-git-repo'] = vipgoci_unittests_setup_git_repo( $this->options ); + + if ( false === $this->options['local-git-repo'] ) { + $this->markTestSkipped( + 'Could not set up git repository: ' . + vipgoci_unittests_output_get() + ); + + return; + } + + /** + * Test actually starts here + * These options will define the test result + */ + $this->options['skip-large-files'] = true; + $this->options['skip-large-files-limit'] = 15000; + + /** + * Prepare mock data + */ + $issues_submit = array(); + $issues_stats = array(); + $issues_skipped = array(); + + foreach ( $prs_implicated as $pr_item ) { + $issues_stats[ $pr_item->number ]['error'] = 0; + $issues_skipped[ $pr_item->number ] = $this->getDefaultSkippedFilesDueIssuesMock(); + } + + vipgoci_phpcs_scan_commit( + $this->options, + $issues_submit, + $issues_stats, + $issues_skipped + ); + + vipgoci_unittests_output_unsuppress(); + + $expected_issues_skipped = array( + 39 => array( + 'issues' => array( + 'max-lines' => array( 'test1/myfile-1.php' ) + ), + 'total' => 1 + ) + ); + + $this->assertSame( + $expected_issues_skipped[39], + $issues_skipped[39] + ); + } + + /** + * @covers ::vipgoci_phpcs_scan_commit + * Should skip files with more than 15 and can otherwise + * Expected to scan 1 file normally and skip 1 file + * tests1/myfile1.php should be skipped + * tests1/myfile2.php should be scanned + * skip-large-files = true + * skip-large-files-limit = 15 + */ + public function testShouldScanRegularFilesAndSkipLargeFilesWhenSkipLargeFilesOptionIsOnAndFileIsLargerThanLimit() { + $options_test = vipgoci_unittests_options_test( + $this->options, + array( 'phpcs-runtime-set', 'github-token', 'token' ), + $this + ); + + if ( - 1 === $options_test ) { + return; + } + + vipgoci_unittests_output_suppress(); + $this->options['skip-large-files'] = true; + $this->options['skip-large-files-limit'] = 15; + $this->options['commit'] = $this->options['commit-test-phpcs-scan-commit-6']; + $this->options['phpcs-skip-scanning-via-labels-allowed'] = false; + $this->options['local-git-repo'] = vipgoci_unittests_setup_git_repo( $this->options ); + + if ( false === $this->options['local-git-repo'] ) { + $this->markTestSkipped( 'Could not set up git repository: ' . vipgoci_unittests_output_get() ); + + return; + } + + $prs_implicated = $this->getPRsImplicated(); + $issues_submit = array(); + $issues_stats = array(); + $issues_skipped = array(); + + foreach ( $prs_implicated as $pr_item ) { + $issues_stats[ $pr_item->number ] = $this->getStatsDefault(); + $issues_submit[ $pr_item->number ] = []; + $issues_skipped[ $pr_item->number ] = $this->getDefaultSkippedFilesDueIssuesMock(); + } + + vipgoci_phpcs_scan_commit( + $this->options, + $issues_submit, + $issues_stats, + $issues_skipped + ); + + vipgoci_unittests_output_unsuppress(); + + $expected_issues_skipped = array( + 'issues' => array( + 'max-lines' => array( 'tests1/myfile1.php' ) + ), + 'total' => 1 + ); + + $this->assertSame( + $expected_issues_skipped, + $issues_skipped[43] + ); + + $this->assertSame( + array( 'error' => 3, 'warning' => 1, 'info' => 0 ), + $issues_stats[43] + ); + + $this->assertSame( + $this->getFailedIssuesSubmitMock(), + $issues_submit[43] + ); + } + + /** + * @covers ::vipgoci_phpcs_scan_commit + * Should scan all the files + * Expected to scan 2 files normally + * skip-large-files = false + * skip-large-files-limit = any + */ + public function testShouldScanAllTheFilesWhenSkipLargeFilesOptionIsOff() { + $options_test = vipgoci_unittests_options_test( + $this->options, + array( 'phpcs-runtime-set', 'github-token', 'token' ), + $this + ); + if ( - 1 === $options_test ) { + return; + } + + vipgoci_unittests_output_suppress(); + + $this->options['commit'] = $this->options['commit-test-phpcs-scan-commit-6']; + $this->options['phpcs-skip-scanning-via-labels-allowed'] = false; + $this->options['local-git-repo'] = vipgoci_unittests_setup_git_repo( $this->options ); + + if ( false === $this->options['local-git-repo'] ) { + $this->markTestSkipped( 'Could not set up git repository: ' . vipgoci_unittests_output_get() ); + + return; + } + + $prs_implicated = $this->getPRsImplicated(); + + $this->options['skip-large-files'] = false; // if we set to false, it will fail + $this->options['skip-large-files-limit'] = 15; // if we decrease to 4, it will include 2 files in the result + + $issues_submit = array(); + $issues_stats = array(); + $issues_skipped = array(); + + foreach ( $prs_implicated as $pr_item ) { + $issues_stats[ $pr_item->number ] = $this->getStatsDefault(); + $issues_submit[ $pr_item->number ] = []; + $issues_skipped[ $pr_item->number ] = $this->getDefaultSkippedFilesDueIssuesMock(); + } + + vipgoci_phpcs_scan_commit( + $this->options, + $issues_submit, + $issues_stats, + $issues_skipped + ); + + vipgoci_unittests_output_unsuppress(); + + $this->assertSame( + array( 'error' => 7, 'warning' => 2, 'info' => 0 ), + $issues_stats[43] + ); + + $this->assertSame( + $issues_skipped[43], + $this->getDefaultSkippedFilesDueIssuesMock() + ); + + $failedMultipleFilesSubmitMock = $this->getFailedMultipleFilesSubmitMock(); + $this->assertSame( + $issues_submit[43][0], + $failedMultipleFilesSubmitMock[0] + ); + + $this->assertSame( + $issues_submit[43][5], + $failedMultipleFilesSubmitMock[1] + ); + } + + /** + * @return array + */ + private function getDefaultSkippedFilesDueIssuesMock() { + return array( + 'issues' => array(), + 'total' => 0 + ); + } + + /** + * @return array[] + */ + private function getFailedIssuesSubmitMock(): array { + return array ( + 0 => + array ( + 'type' => 'phpcs', + 'file_name' => 'tests1/myfile2.php', + 'file_line' => 3, + 'issue' => + array ( + 'message' => 'Detected usage of a non-sanitized input variable: $_POST[\'phpcs should catch this problem\']', + 'source' => 'WordPress.Security.ValidatedSanitizedInput.InputNotSanitized', + 'severity' => 10, + 'fixable' => false, + 'type' => 'WARNING', + 'line' => 3, + 'column' => 6, + 'level' => 'WARNING', + ), + ), + 1 => + array ( + 'type' => 'phpcs', + 'file_name' => 'tests1/myfile2.php', + 'file_line' => 3, + 'issue' => + array ( + 'message' => 'All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found \'$_POST[\'phpcs should catch this problem\']\'.', + 'source' => 'WordPress.Security.EscapeOutput.OutputNotEscaped', + 'severity' => 5, + 'fixable' => false, + 'type' => 'ERROR', + 'line' => 3, + 'column' => 6, + 'level' => 'ERROR', + ), + ), + 2 => + array ( + 'type' => 'phpcs', + 'file_name' => 'tests1/myfile2.php', + 'file_line' => 3, + 'issue' => + array ( + 'message' => 'Processing form data without nonce verification.', + 'source' => 'WordPress.Security.NonceVerification.Missing', + 'severity' => 5, + 'fixable' => false, + 'type' => 'ERROR', + 'line' => 3, + 'column' => 6, + 'level' => 'ERROR', + ), + ), + 3 => + array ( + 'type' => 'phpcs', + 'file_name' => 'tests1/myfile2.php', + 'file_line' => 3, + 'issue' => + array ( + 'message' => 'Detected usage of a possibly undefined superglobal array index: $_POST[\'phpcs should catch this problem\']. Use isset() or empty() to check the index exists before using it', + 'source' => 'WordPress.Security.ValidatedSanitizedInput.InputNotValidated', + 'severity' => 5, + 'fixable' => false, + 'type' => 'ERROR', + 'line' => 3, + 'column' => 6, + 'level' => 'ERROR', + ), + ), + ); + } + + /** + * @return string + */ + private function getFailedMultipleFilesSubmitMock(): array { + return array( + array( + 'type' => 'phpcs', + 'file_name' => 'tests1/myfile1.php', + 'file_line' => 3, + 'issue' => + array( + 'message' => 'Detected usage of a non-sanitized input variable: $_POST[\'phpcs should catch this problem\']', + 'source' => 'WordPress.Security.ValidatedSanitizedInput.InputNotSanitized', + 'severity' => 10, + 'fixable' => false, + 'type' => 'WARNING', + 'line' => 3, + 'column' => 6, + 'level' => 'WARNING', + ), + ), + array( + 'type' => 'phpcs', + 'file_name' => 'tests1/myfile2.php', + 'file_line' => 3, + 'issue' => + array( + 'message' => 'Detected usage of a non-sanitized input variable: $_POST[\'phpcs should catch this problem\']', + 'source' => 'WordPress.Security.ValidatedSanitizedInput.InputNotSanitized', + 'severity' => 10, + 'fixable' => false, + 'type' => 'WARNING', + 'line' => 3, + 'column' => 6, + 'level' => 'WARNING', + ), + ), + ); + } + + /** + * @return array|bool|mixed|null + */ + public function getPRsImplicated() { + $prs_implicated = vipgoci_github_prs_implicated( + $this->options['repo-owner'], + $this->options['repo-name'], + $this->options['commit'], + $this->options['github-token'], + $this->options['branches-ignore'] + ); + + return $prs_implicated; + } + + /** + * @return int[] + */ + public function getStatsDefault(): array { + return array( + 'error' => 0, + 'warning' => 0, + 'info' => 0 + ); + } } diff --git a/tests/PhpcsScanSingleFileTest.php b/tests/PhpcsScanSingleFileTest.php index 9a39b5ab5..f02c65b31 100644 --- a/tests/PhpcsScanSingleFileTest.php +++ b/tests/PhpcsScanSingleFileTest.php @@ -6,18 +6,18 @@ final class PhpcsScanSingleFileTest extends TestCase { var $options_phpcs = array( - 'phpcs-path' => null, - 'phpcs-standard' => null, - 'phpcs-severity' => null, - 'phpcs-runtime-set' => null, - 'commit-test-phpcs-scan-commit-1' => null, + 'phpcs-path' => null, + 'phpcs-standard' => null, + 'phpcs-severity' => null, + 'phpcs-runtime-set' => null, + 'commit-test-phpcs-scan-commit-1' => null, ); var $options_git_repo = array( - 'repo-owner' => null, - 'repo-name' => null, - 'git-path' => null, - 'github-repo-url' => null, + 'repo-owner' => null, + 'repo-name' => null, + 'git-path' => null, + 'github-repo-url' => null, ); protected function setUp(): void { @@ -38,7 +38,7 @@ protected function setUp(): void { $this->options_phpcs ); - $this->options[ 'github-token' ] = + $this->options['github-token'] = vipgoci_unittests_get_config_value( 'git-secrets', 'github-token', @@ -55,8 +55,12 @@ protected function setUp(): void { $this->options['lint-skip-folders'] = array(); $this->options['phpcs-skip-folders'] = array(); + + $this->options['skip-large-files'] = true; + + $this->options['skip-large-files-limit'] = 15000; } - + protected function tearDown(): void { if ( false !== $this->options['local-git-repo'] ) { vipgoci_unittests_remove_git_repo( @@ -64,9 +68,9 @@ protected function tearDown(): void { ); } - $this->options_phpcs = null; + $this->options_phpcs = null; $this->options_git_repo = null; - $this->options = null; + $this->options = null; } /** @@ -79,7 +83,7 @@ public function testDoScanTest1() { $this ); - if ( -1 === $options_test ) { + if ( - 1 === $options_test ) { return; } @@ -96,12 +100,12 @@ public function testDoScanTest1() { if ( false === $this->options['local-git-repo'] ) { $this->markTestSkipped( 'Could not set up git repository: ' . - vipgoci_unittests_output_get() + vipgoci_unittests_output_get() ); - + return; } - + $scan_results = vipgoci_phpcs_scan_single_file( $this->options, 'my-test-file-1.php' @@ -112,54 +116,55 @@ public function testDoScanTest1() { $expected_results = array( 'file_issues_arr_master' => array( 'totals' => array( - 'errors' => 3, - 'warnings' => 0, - 'fixable' => 0, + 'errors' => 3, + 'warnings' => 0, + 'fixable' => 0, ), 'files' => array( $scan_results['temp_file_name'] => array( - 'errors' => 3, - 'warnings' => 0, - 'messages' => array( - array( - 'message' => "All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found 'time'.", - 'source' => 'WordPress.Security.EscapeOutput.OutputNotEscaped', - 'severity' => 5, - 'fixable' => false, - 'type' => 'ERROR', - 'line' => 3, - 'column' => 20, - ), - - array( - 'message' => "All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found 'time'.", - 'source' => 'WordPress.Security.EscapeOutput.OutputNotEscaped', - 'severity' => 5, - 'fixable' => false, - 'type' => 'ERROR', - 'line' => 7, - 'column' => 20, - ), - - array( - 'message' => "All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found 'time'.", - 'source' => 'WordPress.Security.EscapeOutput.OutputNotEscaped', - 'severity' => 5, - 'fixable' => false, - 'type' => 'ERROR', - 'line' => 11, - 'column' => 20, - ), - - ) + 'errors' => 3, + 'warnings' => 0, + 'messages' => array( + array( + 'message' => "All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found 'time'.", + 'source' => 'WordPress.Security.EscapeOutput.OutputNotEscaped', + 'severity' => 5, + 'fixable' => false, + 'type' => 'ERROR', + 'line' => 3, + 'column' => 20, + ), + + array( + 'message' => "All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found 'time'.", + 'source' => 'WordPress.Security.EscapeOutput.OutputNotEscaped', + 'severity' => 5, + 'fixable' => false, + 'type' => 'ERROR', + 'line' => 7, + 'column' => 20, + ), + + array( + 'message' => "All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found 'time'.", + 'source' => 'WordPress.Security.EscapeOutput.OutputNotEscaped', + 'severity' => 5, + 'fixable' => false, + 'type' => 'ERROR', + 'line' => 11, + 'column' => 20, + ), ) + + ) ) ), - 'file_issues_str' => '', - 'temp_file_name' => $scan_results['temp_file_name'], + 'file_issues_str' => '', + 'temp_file_name' => $scan_results['temp_file_name'], + 'validation' => [ 'total' => 0 ] ); $expected_results['file_issues_str'] = json_encode( diff --git a/tests/StatsStatsInitTest.php b/tests/StatsStatsInitTest.php index 2600216ac..91d681076 100644 --- a/tests/StatsStatsInitTest.php +++ b/tests/StatsStatsInitTest.php @@ -9,18 +9,18 @@ final class StatsStatsInitTest extends TestCase { * @covers ::vipgoci_stats_init */ public function testStatsInit() { - $pr_item1 = new stdClass(); + $pr_item1 = new stdClass(); $pr_item1->number = 100; - $pr_item2 = new stdClass(); + $pr_item2 = new stdClass(); $pr_item2->number = 110; $stats_arr = array(); vipgoci_stats_init( array( - 'phpcs' => true, - 'lint' => true, + 'phpcs' => true, + 'lint' => true, 'hashes-api' => false ), array( @@ -30,48 +30,48 @@ public function testStatsInit() { $stats_arr ); - return $this->assertSame( - array( - 'issues' => array( - 100 => - array(), - - 110 => - array(), + $expected = array( + 'issues' => array( + 100 => array(), + 110 => array(), + ), + 'skipped-files' => array( + 100 => array( 'issues' => array(), 'total' => 0 ), + 110 => array( 'issues' => array(), 'total' => 0 ), + ), + 'stats' => array( + VIPGOCI_STATS_PHPCS => array( + 100 => array( + 'error' => 0, + 'warning' => 0, + 'info' => 0, + ), + 110 => array( + 'error' => 0, + 'warning' => 0, + 'info' => 0, + ), + // no hashes-api; not supposed to initialize that ), - 'stats' => array( - VIPGOCI_STATS_PHPCS => array( - 100 => array( - 'error' => 0, - 'warning' => 0, - 'info' => 0, - ), - - 110 => array( - 'error' => 0, - 'warning' => 0, - 'info' => 0, - ), - // no hashes-api; not supposed to initialize that + VIPGOCI_STATS_LINT => array( + 100 => array( + 'error' => 0, + 'warning' => 0, + 'info' => 0, ), - - VIPGOCI_STATS_LINT => array( - 100 => array( - 'error' => 0, - 'warning' => 0, - 'info' => 0, - ), - - 110 => array( - 'error' => 0, - 'warning' => 0, - 'info' => 0, - ), - // no hashes-api; not supposed to initialize that + 110 => array( + 'error' => 0, + 'warning' => 0, + 'info' => 0, ), - ) + // no hashes-api; not supposed to initialize that + ), ), + ); + + return $this->assertSame( + $expected, $stats_arr ); } diff --git a/tests/VipgociExitStatusTest.php b/tests/VipgociExitStatusTest.php index 86472751d..725cae273 100644 --- a/tests/VipgociExitStatusTest.php +++ b/tests/VipgociExitStatusTest.php @@ -11,10 +11,10 @@ final class VipgociExitStatusTest extends TestCase { public function testExitStatus1() { $exit_status = vipgoci_exit_status( array( - 'stats' => array( - 'lint' => array( - 25 => array( - 'error' => 0, + 'stats' => array( + 'lint' => array( + 25 => array( + 'error' => 0, ) ) ) @@ -33,10 +33,10 @@ public function testExitStatus1() { public function testExitStatus2() { $exit_status = vipgoci_exit_status( array( - 'stats' => array( - 'lint' => array( - 25 => array( - 'error' => 30 + 'stats' => array( + 'lint' => array( + 25 => array( + 'error' => 30 ) ) ) @@ -48,4 +48,23 @@ public function testExitStatus2() { $exit_status ); } + + /** + * @covers ::vipgoci_exit_status + */ + public function testExitStatusWillReturn250WhenSkippedFilesIsFound() { + $exit_status = vipgoci_exit_status( + array( + 'stats' => array(), + 'skipped-files' => array( + 25 => array( 'total' => 1 ) + ) + ) + ); + + $this->assertSame( + 250, + $exit_status + ); + } } diff --git a/tests/VipgociSkipFileTest.php b/tests/VipgociSkipFileTest.php new file mode 100644 index 000000000..7c5fcbf2e --- /dev/null +++ b/tests/VipgociSkipFileTest.php @@ -0,0 +1,211 @@ + array( 'max-lines' => array( 'MyFailedClass1.php' ) ), + 'total' => 1 + ); + + $current_skipped_files_mock = array( + 'total' => 2, + 'issues' => array( + 'max-lines' => array( 'MyFailedClass2.php', 'MyFailedClass3.php' ) + ), + ); + + $skipped_files = vipgoci_get_skipped_files( + $current_skipped_files_mock, + $validation_mock + ); + + $expected_skipped_files = array( + 'total' => 3, + 'issues' => array( + 'max-lines' => array( + 'MyFailedClass1.php', + 'MyFailedClass2.php', + 'MyFailedClass3.php', + ), + ), + ); + + sort( $expected_skipped_files['issues']['max-lines'] ); + sort( $skipped_files['issues']['max-lines'] ); + + $this->assertSame( + $expected_skipped_files, + $skipped_files + ); + } + + /** + * @covers ::vipgoci_get_skipped_files + */ + public function testGetSkippedFilesWillReturnCorrectValueForTotal0() { + $validation_mock = array( + 'issues' => array(), + 'total' => 0 + ); + + $current_skipped_files_mock = array( + 'total' => 2, + 'issues' => array( + 'max-lines' => array( 'MyFailedClass2.php', 'MyFailedClass3.php' ) + ), + ); + + $skipped_files = vipgoci_get_skipped_files( + $current_skipped_files_mock, + $validation_mock + ); + + $expected_skipped_files = array( + 'total' => 2, + 'issues' => array( + 'max-lines' => array( 'MyFailedClass2.php', 'MyFailedClass3.php' ) + ), + ); + + $this->assertSame( + $expected_skipped_files, + $skipped_files + ); + } + + /** + * @covers ::vipgoci_set_skipped_file + */ + public function testSetSkippedFilesWillSetCorrectValues() { + $commid_id_mock = 8; + $commit_skipped_files = array( + $commid_id_mock => array( + 'issues' => array(), + 'total' => 0 + ) + ); + $validation_mock = array( + 'total' => 1, + 'issues' => array( + 'max-lines' => array( 'MyFailedClass.php' ) + ) + ); + + vipgoci_set_skipped_file( + $commit_skipped_files, + $validation_mock, + $commid_id_mock + ); + + $expected_skipped_files = array( + '8' => array( + 'issues' => array( + 'max-lines' => array( 'MyFailedClass.php' ) + + ), + 'total' => 1 + ) + ); + + $this->assertSame( + $expected_skipped_files, + $commit_skipped_files + ); + } + + /** + * @covers ::vipgoci_set_prs_implicated_skipped_files + */ + public function testSetPRsImplicatedSkippedFilesWillSetCorrectValues() { + $pr = new \stdClass(); + $pr->number = 8; + $prs_implicated = array( 8 => $pr ); + $commit_skipped_files = array( + 8 => array( + 'issues' => array(), + 'total' => 0 + ) + ); + $validation_mock = array( + 'total' => 1, + 'issues' => array( + 'max-lines' => array( 'MyFailedClass.php' ) + ) + ); + + vipgoci_set_prs_implicated_skipped_files( + $prs_implicated, + $commit_skipped_files, + $validation_mock + ); + + $expected_skipped_files = array( + '8' => array( + 'issues' => array( + 'max-lines' => array( 'MyFailedClass.php' ) + + ), + 'total' => 1 + ) + ); + + $this->assertSame( + $expected_skipped_files, + $commit_skipped_files + ); + } + + /** + * @covers ::vipgoci_get_skipped_files_message + */ + public function testGetSkippedFilesMessage() { + + $skipped = array( + 'issues' => array( + 'max-lines' => array( 'MyFailedClass.php', 'MyFailedClass2.php' ) + ), + 'total' => 2 + ); + $skipped_files_message = vipgoci_get_skipped_files_message( $skipped ); + + $expected_skipped_files_message = '**** +**skipped-files** +Maximum number of lines exceeded (15000): + -MyFailedClass.php + -MyFailedClass2.php'; + + $this->assertSame( $expected_skipped_files_message, $skipped_files_message ); + } + + /** + * @covers ::vipgoci_get_skipped_files_issue_message + */ + public function testGetSkippedFilesIssueMessage() { + $affected_files_mock = array( 'MyFailedClass.php', 'MyFailedClass2.php' ); + + $skipped_files_issue_message = vipgoci_get_skipped_files_issue_message( + $affected_files_mock, + 'max-lines' + ); + + $expected_skipped_files_issue_message = 'Maximum number of lines exceeded (15000): + -MyFailedClass.php + -MyFailedClass2.php'; + + $this->assertSame( + $expected_skipped_files_issue_message, + $skipped_files_issue_message + ); + } +} diff --git a/unittests.ini b/unittests.ini index 1f877e95c..112b9a26c 100644 --- a/unittests.ini +++ b/unittests.ini @@ -22,6 +22,8 @@ commit-test-phpcs-scan-commit-1=9882d7da670f8f45f3431e3c7da80a00d6af4b5e commit-test-phpcs-scan-commit-2=1c63fd89388e3f5563869ca5dc54ae7d4da9f1a4 commit-test-phpcs-scan-commit-3=ba678917a65855a3129ff4f95de3b2ede3d76f4b commit-test-phpcs-scan-commit-4=4702a1416ee36510a3cc43c68e631be572acddfe +commit-test-phpcs-scan-commit-5=dd60c66a7c1e27fc78c400f0c5160c9a80c4bd49 +commit-test-phpcs-scan-commit-6=c45ea112564ce395da252fc692ebc19b605d5412 phpcs-validate-sniffs-and-report-include-commit=fa1b1a695a0c25895ed8ccb3c0d475e077f396f8 phpcs-validate-sniffs-and-report-include-invalid=MyInvalidSniff.SniffName @@ -38,6 +40,8 @@ commit-test-svg-scan-single-file-test-1=935bcf78887d54228c7d705a196de4b72947fd42 php-path=/usr/bin/php commit-test-lint-scan-commit-1=6968d452099d2b43fd85953076b34a5b2e2d707f commit-test-lint-scan-commit-2=20fee28020873cb7fdfadf1cfa101f94026bc75e +commit-test-lint-scan-commit-3=dd60c66a7c1e27fc78c400f0c5160c9a80c4bd49 +commit-test-lint-scan-commit-4=c45ea112564ce395da252fc692ebc19b605d5412 [auto-approvals] commit-test-file-types-1=2d864e5dd9d89ea3362d965477432873bf970345