3 use Wikimedia\TestingAccessWrapper;
21 private static $testGlobals = [
38 private static $paramTypes = [
68 private static $paramRequirements = [
82 private static $paramAllowedTypes = [
98 private static $paramProhibitedTypes = [
104 private static $constantNames =
null;
110 private static function getMain() {
111 if ( !self::$main ) {
113 self::$main->getContext()->setLanguage(
'en' );
114 self::$main->getContext()->setTitle(
126 private function checkMessage( $msg, $what ) {
129 $this->assertTrue( $msg->exists(),
"$what message {$msg->getKey()} exists" );
137 public function testDocumentationExists(
$path,
array $globals ) {
138 $main = self::getMain();
145 foreach ( $globals
as $k => $v ) {
153 foreach ( $module->getHelpFlags()
as $flag ) {
154 $this->checkMessage(
"api-help-flag-$flag",
"Flag $flag" );
158 $this->checkMessage( $module->getSummaryMessage(),
'Module summary' );
159 $this->checkMessage( $module->getExtendedDescription(),
'Module help top text' );
165 if ( !is_array( $settings ) ) {
173 $msg =
"apihelp-{$path}-param-{$name}";
175 $this->checkMessage( $msg,
"Parameter $name description" );
180 "Parameter $name PARAM_HELP_MSG_PER_VALUE is array" );
182 "Parameter $name PARAM_TYPE is array for msg-per-value mode" );
185 if ( isset( $valueMsgs[
$value] ) ) {
186 $msg = $valueMsgs[
$value];
188 $msg =
"apihelp-{$path}-paramvalue-{$name}-{$value}";
190 $this->checkMessage( $msg,
"Parameter $name value $value" );
197 "Parameter $name PARAM_HELP_MSG_APPEND is array" );
199 $this->checkMessage( $msg,
"Parameter $name HELP_MSG_APPEND #$i" );
207 $tags[array_shift( $i )] = 1;
213 foreach ( $tags
as $tag => $dummy ) {
214 $this->checkMessage(
"apihelp-{$path}-paraminfo-{$tag}",
"HELP_MSG_INFO tag $tag" );
218 foreach ( $module->getExamplesMessages()
as $qs => $msg ) {
219 $this->assertStringStartsNotWith(
'api.php?', $qs,
220 "Query string must not begin with 'api.php?'" );
221 $this->checkMessage( $msg,
"Example $qs" );
225 public static function provideDocumentationExists() {
226 $main = self::getMain();
232 foreach ( self::$testGlobals
as $globals ) {
234 foreach ( $globals
as $k => $v ) {
235 $g[] =
"$k=" . var_export( $v, 1 );
237 $k =
"Module $path with " . implode(
', ', $g );
248 public function testParameterConsistency(
$path ) {
249 $main = self::getMain();
252 $paramsPlain = $module->getFinalParams();
256 $this->assertTrue(
true );
258 if ( self::$constantNames ===
null ) {
259 self::$constantNames = [];
261 foreach ( (
new ReflectionClass(
'ApiBase' ) )->getConstants()
as $key => $val ) {
262 if ( substr( $key, 0, 6 ) ===
'PARAM_' ) {
263 self::$constantNames[$val] = $key;
268 foreach ( [ $paramsPlain, $paramsForHelp ]
as $params ) {
269 foreach (
$params as $param => $config ) {
270 if ( !is_array( $config ) ) {
279 foreach ( self::$paramTypes
as $key => $types ) {
280 if ( !isset( $config[$key] ) ) {
283 $keyName = self::$constantNames[$key];
284 $this->validateType( $types, $config[$key], $param, $keyName );
287 foreach ( self::$paramRequirements
as $key => $required ) {
288 if ( !isset( $config[$key] ) ) {
291 foreach ( $required
as $requireKey => $requireVal ) {
292 $this->assertArrayHasKey( $requireKey, $config,
293 "$param: When " . self::$constantNames[$key] .
" is set, " .
294 self::$constantNames[$requireKey] .
" must also be set" );
295 if ( $requireVal !==
null ) {
296 $this->assertSame( $requireVal, $config[$requireKey],
297 "$param: When " . self::$constantNames[$key] .
" is set, " .
298 self::$constantNames[$requireKey] .
" must equal " .
299 var_export( $requireVal,
true ) );
304 foreach ( self::$paramAllowedTypes
as $key => $allowedTypes ) {
305 if ( !isset( $config[$key] ) ) {
312 $this->assertContains(
314 (
array)$allowedTypes,
315 "$param: " . self::$constantNames[$key] .
316 " can only be used with PARAM_TYPE " .
317 implode(
', ', (
array)$allowedTypes )
321 foreach ( self::$paramProhibitedTypes
as $key => $prohibitedTypes ) {
322 if ( !isset( $config[$key] ) ) {
329 $this->assertNotContains(
331 (
array)$prohibitedTypes,
332 "$param: " . self::$constantNames[$key] .
333 " cannot be used with PARAM_TYPE " .
334 implode(
', ', (
array)$prohibitedTypes )
342 "$param: A required parameter cannot have a default" );
344 $this->validateDefault( $param, $config );
351 "$param: PARAM_MAX and PARAM_MAX2 are required for limits"
353 $this->assertGreaterThanOrEqual(
356 "$param: PARAM_MAX cannot be greater than PARAM_MAX2"
364 $this->assertGreaterThanOrEqual(
367 "$param: PARAM_MIN cannot be greater than PARAM_MAX"
375 "$param: PARAM_RANGE_ENFORCE can only be set together with " .
376 "PARAM_MIN or PARAM_MAX"
383 "$param: Deprecated value \"$key\" is not allowed, " .
384 "how can it be deprecated?" );
393 "$param: PARAM_ISMULTI_LIMIT1 cannot be negative" );
397 "$param: PARAM_ISMULTI_LIMIT2 cannot be negative or zero" );
398 $this->assertGreaterThanOrEqual(
401 "$param: PARAM_ISMULTI limit cannot be smaller for users with " .
402 "apihighlimits rights" );
407 "$param: PARAM_MAX_BYTES cannot be negative or zero" );
412 "$param: PARAM_MAX_CHARS cannot be negative or zero" );
421 $this->assertGreaterThanOrEqual(
424 "$param: PARAM_MAX_BYTES cannot be less than PARAM_MAX_CHARS"
430 "$param: PARAM_TEMPLATE_VARS cannot be empty" );
432 $this->assertRegExp(
'/^[^{}]+$/', $key,
433 "$param: PARAM_TEMPLATE_VARS key may not contain '{' or '}'" );
435 $this->assertContains(
'{' . $key .
'}', $param,
436 "$param: Name must contain PARAM_TEMPLATE_VARS key {" . $key .
"}" );
437 $this->assertArrayHasKey( $target,
$params,
438 "$param: PARAM_TEMPLATE_VARS target parameter '$target' does not exist" );
441 "$param: PARAM_TEMPLATE_VARS target parameter '$target' must have PARAM_ISMULTI = true" );
444 $this->assertNotSame( $param, $target,
445 "$param: PARAM_TEMPLATE_VARS cannot target itself" );
447 $this->assertArraySubset(
451 "$param: PARAM_TEMPLATE_VARS target parameter '$target': "
452 .
"the target's PARAM_TEMPLATE_VARS must be a subset of the original."
457 $keys = implode(
'|',
460 return preg_quote( $key,
'/' );
465 $this->assertRegExp(
'/^(?>[^{}]+|\{(?:' .
$keys .
')\})+$/', $param,
466 "$param: Name may not contain '{' or '}' other than as defined by PARAM_TEMPLATE_VARS" );
468 $this->assertRegExp(
'/^[^{}]+$/', $param,
469 "$param: Name may not contain '{' or '}' without PARAM_TEMPLATE_VARS" );
483 private function validateType( $types,
$value, $param, $desc ) {
484 if (
count( $types ) === 1 ) {
486 if ( is_string( $types[0] ) ) {
490 $this->assertInternalType(
'array',
$value,
"$param: $desc type" );
492 $this->validateType( $types[0], $subvalue, $param,
"$desc value" );
498 if ( is_string(
$type ) ) {
499 if ( class_exists(
$type ) || interface_exists(
$type ) ) {
509 $this->validateType( [
$type ],
$value, $param,
"$desc type" );
512 }
catch ( Exception $unused ) {
517 $this->fail(
"$param: $desc has incorrect type" );
527 private function validateDefault( $param, $config ) {
532 if ( $default ===
'' ) {
536 $defaults = explode(
'|', $default );
538 foreach ( $defaults
as $defaultValue ) {
541 if (
$type ===
'integer' && $defaultValue === (
string)(
int)$defaultValue ) {
542 $defaultValue = (int)$defaultValue;
545 $this->validateDefault( $param, $config );
551 $this->assertFalse( $default,
552 "$param: Boolean params may only default to false" );
556 $this->assertInternalType(
'integer', $default,
557 "$param: Default $default is not an integer" );
561 if ( $default ===
'max' ) {
564 $this->assertInternalType(
'integer', $default,
565 "$param: Default $default is neither an integer nor \"max\"" );
574 $validValues = array_merge(
579 $this->assertContains( $default, $validValues,
580 "$param: Default $default is not a valid namespace" );
589 $this->assertInternalType(
'string', $default,
590 "$param: Default $default is not a string" );
594 if ( $default ===
'now' ) {
597 $this->assertNotFalse(
wfTimestamp( TS_MW, $default ),
598 "$param: Default $default is not a valid timestamp" );
605 $wrapper = TestingAccessWrapper::newFromObject(
new ApiMain() );
607 $wrapper->validateUser( $default,
'' );
609 $this->fail(
"$param: Default $default is not a valid username/IP address" );
614 if ( is_array(
$type ) ) {
615 $this->assertContains( $default,
$type,
616 "$param: Default $default is not any of " .
617 implode(
', ',
$type ) );
619 $this->fail(
"Unrecognized type $type" );
627 public static function provideParameterConsistency() {
628 $main = self::getMain();
648 $paths[] = $module->getModulePath();
649 $subManager = $module->getModuleManager();
651 $paths = array_merge( $paths, self::getSubModulePaths( $subManager ) );