3 namespace MediaWiki\Tests\Maintenance
;
9 * Makes parts of Maintenance class API visible for testing, and makes up for a
10 * stream closing hack in Maintenance.php.
12 * This class is solely used for being able to test Maintenance right now
13 * without having to apply major refactorings to fix some design issues in
14 * Maintenance.php. Before adding more functions here, please consider whether
15 * this approach is correct, or a refactoring Maintenance to separate concerns
16 * is more appropriate.
18 * Upon refactoring, keep in mind that besides the maintenance scripts themselves
19 * and tests right here, some extensions including Extension:Maintenance make
20 * use of the Maintenance class.
22 * Due to a hack in Maintenance.php using register_shutdown_function, be sure to
23 * call simulateShutdown on MaintenanceFixup instance before a test ends.
26 * It would be great if we were able to use PHPUnit's getMockForAbstractClass
27 * instead of the MaintenanceFixup hack below. However, we cannot do so
28 * without changing method visibility and without working around hacks in
31 * For the same reason, we cannot just use FakeMaintenance.
33 class MaintenanceFixup
extends Maintenance
{
35 // --- Making up for the register_shutdown_function hack in Maintenance.php
38 * The test case that generated this instance.
40 * This member is motivated by allowing the destructor to check whether or not
41 * the test failed, in order to avoid unnecessary nags about omitted shutdown
43 * But as it is already available, we also usi it to flagging tests as failed
45 * @var MediaWikiTestCase
50 * shutdownSimulated === true if simulateShutdown has done its work
54 private $shutdownSimulated = false;
57 * Simulates what Maintenance wants to happen at script's end.
59 public function simulateShutdown() {
60 if ( $this->shutdownSimulated
) {
61 $this->testCase
->fail( __METHOD__
. " called more than once" );
64 // The cleanup action.
65 $this->outputChanneled( false );
67 // Bookkeeping that we simulated the clean up.
68 $this->shutdownSimulated
= true;
71 // Note that the "public" here does not change visibility
72 public function outputChanneled( $msg, $channel = null ) {
73 if ( $this->shutdownSimulated
) {
74 if ( $msg !== false ) {
75 $this->testCase
->fail( "Already past simulated shutdown, but msg is "
76 . "not false. Did the hack in Maintenance.php change? Please "
77 . "adapt the test case or Maintenance.php" );
80 // The current call is the one registered via register_shutdown_function.
81 // We can safely ignore it, as we simulated this one via simulateShutdown
82 // before (if we did not, the destructor of this instance will warn about
87 call_user_func_array( [ "parent", __FUNCTION__
], func_get_args() );
91 * Safety net around register_shutdown_function of Maintenance.php
93 public function __destruct() {
94 if ( !$this->shutdownSimulated
) {
95 // Someone generated a MaintenanceFixup instance without calling
96 // simulateShutdown. We'd have to raise a PHPUnit exception to correctly
97 // flag this illegal usage. However, we are already in a destruktor, which
98 // would trigger undefined behavior. Hence, we can only report to the
99 // error output :( Hopefully people read the PHPUnit output.
100 $name = $this->testCase
->getName();
101 fwrite( STDERR
, "ERROR! Instance of " . __CLASS__
. " for test $name "
102 . "destructed without calling simulateShutdown method. Call "
103 . "simulateShutdown on the instance before it gets destructed." );
106 // The following guard is required, as PHP does not offer default destructors :(
107 if ( is_callable( "parent::__destruct" ) ) {
108 parent
::__destruct();
112 public function __construct( MediaWikiTestCase
$testCase ) {
113 parent
::__construct();
114 $this->testCase
= $testCase;
117 // --- Making protected functions visible for test
119 public function output( $out, $channel = null ) {
120 // Just to make PHP not nag about signature mismatches, we copied
121 // Maintenance::output signature. However, we do not use (or rely on)
122 // those variables. Instead we pass to Maintenance::output whatever we
123 // receive at runtime.
124 return call_user_func_array( [ "parent", __FUNCTION__
], func_get_args() );
127 public function addOption( $name, $description, $required = false,
128 $withArg = false, $shortName = false, $multiOccurance = false
130 return call_user_func_array( [ "parent", __FUNCTION__
], func_get_args() );
133 public function getOption( $name, $default = null ) {
134 return call_user_func_array( [ "parent", __FUNCTION__
], func_get_args() );
137 // --- Requirements for getting instance of abstract class
139 public function execute() {
140 $this->testCase
->fail( __METHOD__
. " called unexpectedly" );