Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
90.00% covered (success)
90.00%
45 / 50
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
FunctionsByTestsPager
90.00% covered (success)
90.00%
45 / 50
66.67% covered (warning)
66.67%
2 / 3
9.08
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getQueryInfo
85.71% covered (warning)
85.71%
30 / 35
0.00% covered (danger)
0.00%
0 / 1
6.10
 formatRow
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * WikiLambda FunctionsByTestsPager extends AbstractZObjectPager by
4 * adding filter conditions to the base table of all zobjects and
5 * their preferred labels given by AbstractZObjectPager::getQueryInfo
6 *
7 * @file
8 * @ingroup Extensions
9 * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
10 * @license MIT
11 */
12
13namespace MediaWiki\Extension\WikiLambda\Pagers;
14
15use MediaWiki\Context\IContextSource;
16use MediaWiki\Extension\WikiLambda\Registry\ZTypeRegistry;
17use MediaWiki\Extension\WikiLambda\ZObjectStore;
18use Wikimedia\Rdbms\Subquery;
19
20/**
21 * Pages Functions filtered by their testing status and quality.
22 */
23class FunctionsByTestsPager extends AbstractZObjectPager {
24
25    private array $filters;
26
27    /**
28     * @param IContextSource|null $context Context.
29     * @param ZObjectStore $zObjectStore
30     * @param array $languageZids
31     * @param bool|null $excludePreDefined
32     * @param array|null $filters [ min, max, connected, pending, pass, fail ]
33     */
34    public function __construct(
35        $context, $zObjectStore, $languageZids, $excludePreDefined = null, $filters = []
36    ) {
37        parent::__construct( $context, $zObjectStore, $languageZids, null, $excludePreDefined );
38
39        $this->filters = $filters;
40    }
41
42    /**
43     * Gets the base conditions from the parent class and adds the
44     * additional conditions for this pager, depending on the filters.
45     * This pager inner joins the preferredLabels table returned by the
46     * AbstractZObjectPager with a table with all function ids and their
47     * relevant test counts:
48     * - all_tests: All the tests created for each function.
49     * - connected_tests: Number of connected tests for each function.
50     * - failing_tests: Number of tests failing for each function
51     *   (against at least one connected implementation)
52     * - matching_tests: Number of tests that match the conditions passed
53     *   as input in the Request: connected status and failure/success status.
54     *
55     * @return array
56     */
57    public function getQueryInfo() {
58        // Get base queryInfo from parent
59        $queryInfo = parent::getQueryInfo();
60
61        $min = $this->filters[ 'min' ];
62        $max = $this->filters[ 'max' ];
63        $connected = $this->filters[ 'connected' ];
64        $pending = $this->filters[ 'pending' ];
65        $pass = $this->filters[ 'pass' ];
66        $fail = $this->filters[ 'fail' ];
67
68        $testStatus = $this->getZObjectStore()->getTestStatusQuery();
69        $testFilters = [];
70        // Connection status filter:
71        // * Add where clause only if one value is selected
72        // * If both true or both false, leave unfiltered
73        if ( $connected !== $pending ) {
74            $testFilters[] = 'is_connected = ' . (int)$connected;
75        }
76        // Test results filter:
77        // * Add where clause only if one value is selected
78        // * If both true or both false, leave unfiltered
79        if ( $pass !== $fail ) {
80            $testFilters[] = 'is_passing = ' . (int)$pass;
81        }
82
83        // Conditional to count matching tests, if no filters count 1 per entry
84        $matchingConditional = 1;
85        if ( count( $testFilters ) > 0 ) {
86            $matchingConditional = $this->getDatabase()->conditional( $testFilters, '1', 'NULL' );
87        }
88        // Table with all function ids and their test counts:
89        $filteredFunctions = $this->getDatabase()->newSelectQueryBuilder()
90            ->select( [
91                'function_zid',
92                'all_tests',
93                'connected_tests' => 'COUNT( CASE WHEN is_connected = 1 THEN 1 END )',
94                'failing_tests' => 'COUNT( CASE WHEN is_passing = 0 THEN 1 END )',
95                'matching_tests' => 'COUNT( ' . $matchingConditional . ' )'
96            ] )
97            ->from( new Subquery( $testStatus ), 'filtered_functions' )
98            ->groupBy( [ 'function_zid', 'all_tests' ] );
99
100        // Add additional data to parent queryInfo
101        // 1. Return all test counts in the main select
102        array_push( $queryInfo[ 'fields' ], 'all_tests', 'matching_tests', 'connected_tests', 'failing_tests' );
103        // 2. Join filteredFunctions with preferredLabels
104        $queryInfo[ 'tables' ][ 'tests' ] = new Subquery( $filteredFunctions->getSQL() );
105        $queryInfo[ 'join_conds' ][ 'tests' ] = [ 'LEFT JOIN', 'wlzl_zobject_zid = tests.function_zid' ];
106        // 3. Return functions for which matching_tests count is less than max.
107        $queryInfo[ 'conds' ][ 'wlzl_type' ] = ZTypeRegistry::Z_FUNCTION;
108        if ( $max > -1 ) {
109            $queryInfo[ 'conds' ][] = "matching_tests <= $max";
110        }
111        if ( $min > 0 ) {
112            $queryInfo[ 'conds' ][] = "matching_tests >= $min";
113        }
114
115        return $queryInfo;
116    }
117
118    /**
119     * @param \stdClass $row
120     * @return string
121     */
122    public function formatRow( $row ) {
123        $zid = $row->wlzl_zobject_zid;
124        $label = $row->wlzl_label;
125
126        $functionInfo = "# [[$zid|$label]] ($zid)";
127
128        $tests = $row->all_tests;
129        $fail = $row->failing_tests;
130        $conn = $row->connected_tests;
131
132        $testsMsg = $this->msg( 'wikilambda-special-functionsbytests-row-tests' )->params( $tests )->text();
133        $failMsg = $this->msg( 'wikilambda-special-functionsbytests-row-failing' )->params( $fail )->text();
134        $connMsg = $this->msg( 'wikilambda-special-functionsbytests-row-connected' )->params( $conn )->text();
135
136        $testInfo = "$testsMsg"
137            . "<span class='ext-wikilambda-special-tests-connected'>$connMsg</span>"
138            . ( (int)$fail == 0 ? '' : ", <span class='ext-wikilambda-special-tests-failing'>$failMsg</span>" );
139
140        return $functionInfo . ' - ' . $this->msg( 'parentheses' )->params( $testInfo )->text() . "\n";
141    }
142}