Begin splitting out maintenance test base classes
[lhc/web/wiklou.git] / tests / phpunit / maintenance / MaintenanceFixup.php
1 <?php
2
3 namespace MediaWiki\Tests\Maintenance;
4
5 use Maintenance;
6 use MediaWikiTestCase;
7
8 /**
9 * Makes parts of Maintenance class API visible for testing, and makes up for a
10 * stream closing hack in Maintenance.php.
11 *
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.
17 *
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.
21 *
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.
24 *
25 * FIXME:
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
29 * Maintenance.php
30 *
31 * For the same reason, we cannot just use FakeMaintenance.
32 */
33 class MaintenanceFixup extends Maintenance {
34
35 // --- Making up for the register_shutdown_function hack in Maintenance.php
36
37 /**
38 * The test case that generated this instance.
39 *
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
42 * simulation.
43 * But as it is already available, we also usi it to flagging tests as failed
44 *
45 * @var MediaWikiTestCase
46 */
47 private $testCase;
48
49 /**
50 * shutdownSimulated === true if simulateShutdown has done its work
51 *
52 * @var bool
53 */
54 private $shutdownSimulated = false;
55
56 /**
57 * Simulates what Maintenance wants to happen at script's end.
58 */
59 public function simulateShutdown() {
60 if ( $this->shutdownSimulated ) {
61 $this->testCase->fail( __METHOD__ . " called more than once" );
62 }
63
64 // The cleanup action.
65 $this->outputChanneled( false );
66
67 // Bookkeeping that we simulated the clean up.
68 $this->shutdownSimulated = true;
69 }
70
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" );
78 }
79
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
83 // it)
84 return;
85 }
86
87 call_user_func_array( [ "parent", __FUNCTION__ ], func_get_args() );
88 }
89
90 /**
91 * Safety net around register_shutdown_function of Maintenance.php
92 */
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." );
104 }
105
106 // The following guard is required, as PHP does not offer default destructors :(
107 if ( is_callable( "parent::__destruct" ) ) {
108 parent::__destruct();
109 }
110 }
111
112 public function __construct( MediaWikiTestCase $testCase ) {
113 parent::__construct();
114 $this->testCase = $testCase;
115 }
116
117 // --- Making protected functions visible for test
118
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() );
125 }
126
127 public function addOption( $name, $description, $required = false,
128 $withArg = false, $shortName = false, $multiOccurance = false
129 ) {
130 return call_user_func_array( [ "parent", __FUNCTION__ ], func_get_args() );
131 }
132
133 public function getOption( $name, $default = null ) {
134 return call_user_func_array( [ "parent", __FUNCTION__ ], func_get_args() );
135 }
136
137 // --- Requirements for getting instance of abstract class
138
139 public function execute() {
140 $this->testCase->fail( __METHOD__ . " called unexpectedly" );
141 }
142 }