production.
=== Configuration changes in 1.31 ===
-* …
+* $wgEnableAPI and $wgEnableWriteAPI are now deprecated and will be removed in
+ a future version. The API is now considered to be stable, secure and
+ essential.
=== New features in 1.31 ===
* …
* @return string HTML
*/
private function pagingLinks( $first, $last, $type = '' ) {
- $prevLink = $this->msg( 'prev-page' )->text();
+ $prevLink = $this->msg( 'prev-page' )->escaped();
$linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
if ( $first != '' ) {
unset( $prevQuery["{$type}from"] );
$prevLink = $linkRenderer->makeKnownLink(
$this->addFragmentToTitle( $this->title, $type ),
- $prevLink,
+ new HtmlArmor( $prevLink ),
[],
$prevQuery
);
}
- $nextLink = $this->msg( 'next-page' )->text();
+ $nextLink = $this->msg( 'next-page' )->escaped();
if ( $last != '' ) {
$lastQuery = $this->query;
unset( $lastQuery["{$type}until"] );
$nextLink = $linkRenderer->makeKnownLink(
$this->addFragmentToTitle( $this->title, $type ),
- $nextLink,
+ new HtmlArmor( $nextLink ),
[],
$lastQuery
);
*/
class CommentStore {
- /** Maximum length of a comment. Longer comments will be truncated. */
+ /**
+ * Maximum length of a comment in UTF-8 characters. Longer comments will be truncated.
+ * @note This must be at least 255 and not greater than floor( MAX_COMMENT_LENGTH / 4 ).
+ */
+ const COMMENT_CHARACTER_LIMIT = 1000;
+
+ /**
+ * Maximum length of a comment in bytes. Longer comments will be truncated.
+ * @note This value is determined by the size of the underlying database field,
+ * currently BLOB in MySQL/MariaDB.
+ */
const MAX_COMMENT_LENGTH = 65535;
- /** Maximum length of serialized data. Longer data will result in an exception. */
+ /**
+ * Maximum length of serialized data in bytes. Longer data will result in an exception.
+ * @note This value is determined by the size of the underlying database field,
+ * currently BLOB in MySQL/MariaDB.
+ */
const MAX_DATA_LENGTH = 65535;
/**
# Truncate comment in a Unicode-sensitive manner
$comment->text = $this->lang->truncate( $comment->text, self::MAX_COMMENT_LENGTH );
+ if ( mb_strlen( $comment->text, 'UTF-8' ) > self::COMMENT_CHARACTER_LIMIT ) {
+ $ellipsis = wfMessage( 'ellipsis' )->inLanguage( $this->lang )->escaped();
+ if ( mb_strlen( $ellipsis ) >= self::COMMENT_CHARACTER_LIMIT ) {
+ // WTF?
+ $ellipsis = '...';
+ }
+ $maxLength = self::COMMENT_CHARACTER_LIMIT - mb_strlen( $ellipsis, 'UTF-8' );
+ $comment->text = mb_substr( $comment->text, 0, $maxLength, 'UTF-8' ) . $ellipsis;
+ }
if ( $this->stage > MIGRATION_OLD && !$comment->id ) {
$dbData = $comment->data;
* machine-readable data via api.php
*
* See https://www.mediawiki.org/wiki/API
+ *
+ * @deprecated since 1.31
*/
$wgEnableAPI = true;
* Allow the API to be used to perform write operations
* (page edits, rollback, etc.) when an authorised user
* accesses it
+ *
+ * @deprecated since 1.31
*/
$wgEnableWriteAPI = true;
* @deprecated since 1.30 use MediaWiki\Shell::isDisabled()
*/
function wfShellExecDisabled() {
+ wfDeprecated( __FUNCTION__, '1.30' );
return Shell::isDisabled() ? 'disabled' : false;
}
* @throws MWException
* @access private
*/
- function __construct( $row ) {
+ public function __construct( $row ) {
if ( is_object( $row ) ) {
- $this->mId = intval( $row->rev_id );
- $this->mPage = intval( $row->rev_page );
- $this->mTextId = intval( $row->rev_text_id );
- $this->mComment = CommentStore::newKey( 'rev_comment' )
- // Legacy because $row probably came from self::selectFields()
- ->getCommentLegacy( wfGetDB( DB_REPLICA ), $row, true )->text;
- $this->mUser = intval( $row->rev_user );
- $this->mMinorEdit = intval( $row->rev_minor_edit );
- $this->mTimestamp = $row->rev_timestamp;
- $this->mDeleted = intval( $row->rev_deleted );
-
- if ( !isset( $row->rev_parent_id ) ) {
- $this->mParentId = null;
- } else {
- $this->mParentId = intval( $row->rev_parent_id );
- }
-
- if ( !isset( $row->rev_len ) ) {
- $this->mSize = null;
- } else {
- $this->mSize = intval( $row->rev_len );
- }
-
- if ( !isset( $row->rev_sha1 ) ) {
- $this->mSha1 = null;
- } else {
- $this->mSha1 = $row->rev_sha1;
- }
+ $this->constructFromDbRowObject( $row );
+ } elseif ( is_array( $row ) ) {
+ $this->constructFromRowArray( $row );
+ } else {
+ throw new MWException( 'Revision constructor passed invalid row format.' );
+ }
+ $this->mUnpatrolled = null;
+ }
- if ( isset( $row->page_latest ) ) {
- $this->mCurrent = ( $row->rev_id == $row->page_latest );
- $this->mTitle = Title::newFromRow( $row );
- } else {
- $this->mCurrent = false;
- $this->mTitle = null;
- }
+ /**
+ * @param object $row
+ */
+ private function constructFromDbRowObject( $row ) {
+ $this->mId = intval( $row->rev_id );
+ $this->mPage = intval( $row->rev_page );
+ $this->mTextId = intval( $row->rev_text_id );
+ $this->mComment = CommentStore::newKey( 'rev_comment' )
+ // Legacy because $row probably came from self::selectFields()
+ ->getCommentLegacy( wfGetDB( DB_REPLICA ), $row, true )->text;
+ $this->mUser = intval( $row->rev_user );
+ $this->mMinorEdit = intval( $row->rev_minor_edit );
+ $this->mTimestamp = $row->rev_timestamp;
+ $this->mDeleted = intval( $row->rev_deleted );
+
+ if ( !isset( $row->rev_parent_id ) ) {
+ $this->mParentId = null;
+ } else {
+ $this->mParentId = intval( $row->rev_parent_id );
+ }
- if ( !isset( $row->rev_content_model ) ) {
- $this->mContentModel = null; # determine on demand if needed
- } else {
- $this->mContentModel = strval( $row->rev_content_model );
- }
+ if ( !isset( $row->rev_len ) ) {
+ $this->mSize = null;
+ } else {
+ $this->mSize = intval( $row->rev_len );
+ }
- if ( !isset( $row->rev_content_format ) ) {
- $this->mContentFormat = null; # determine on demand if needed
- } else {
- $this->mContentFormat = strval( $row->rev_content_format );
- }
+ if ( !isset( $row->rev_sha1 ) ) {
+ $this->mSha1 = null;
+ } else {
+ $this->mSha1 = $row->rev_sha1;
+ }
- // Lazy extraction...
- $this->mText = null;
- if ( isset( $row->old_text ) ) {
- $this->mTextRow = $row;
- } else {
- // 'text' table row entry will be lazy-loaded
- $this->mTextRow = null;
- }
+ if ( isset( $row->page_latest ) ) {
+ $this->mCurrent = ( $row->rev_id == $row->page_latest );
+ $this->mTitle = Title::newFromRow( $row );
+ } else {
+ $this->mCurrent = false;
+ $this->mTitle = null;
+ }
- // Use user_name for users and rev_user_text for IPs...
- $this->mUserText = null; // lazy load if left null
- if ( $this->mUser == 0 ) {
- $this->mUserText = $row->rev_user_text; // IP user
- } elseif ( isset( $row->user_name ) ) {
- $this->mUserText = $row->user_name; // logged-in user
- }
- $this->mOrigUserText = $row->rev_user_text;
- } elseif ( is_array( $row ) ) {
- // Build a new revision to be saved...
- global $wgUser; // ugh
-
- # if we have a content object, use it to set the model and type
- if ( !empty( $row['content'] ) ) {
- // @todo when is that set? test with external store setup! check out insertOn() [dk]
- if ( !empty( $row['text_id'] ) ) {
- throw new MWException( "Text already stored in external store (id {$row['text_id']}), " .
- "can't serialize content object" );
- }
+ if ( !isset( $row->rev_content_model ) ) {
+ $this->mContentModel = null; # determine on demand if needed
+ } else {
+ $this->mContentModel = strval( $row->rev_content_model );
+ }
- $row['content_model'] = $row['content']->getModel();
- # note: mContentFormat is initializes later accordingly
- # note: content is serialized later in this method!
- # also set text to null?
- }
+ if ( !isset( $row->rev_content_format ) ) {
+ $this->mContentFormat = null; # determine on demand if needed
+ } else {
+ $this->mContentFormat = strval( $row->rev_content_format );
+ }
- $this->mId = isset( $row['id'] ) ? intval( $row['id'] ) : null;
- $this->mPage = isset( $row['page'] ) ? intval( $row['page'] ) : null;
- $this->mTextId = isset( $row['text_id'] ) ? intval( $row['text_id'] ) : null;
- $this->mUserText = isset( $row['user_text'] )
- ? strval( $row['user_text'] ) : $wgUser->getName();
- $this->mUser = isset( $row['user'] ) ? intval( $row['user'] ) : $wgUser->getId();
- $this->mMinorEdit = isset( $row['minor_edit'] ) ? intval( $row['minor_edit'] ) : 0;
- $this->mTimestamp = isset( $row['timestamp'] )
- ? strval( $row['timestamp'] ) : wfTimestampNow();
- $this->mDeleted = isset( $row['deleted'] ) ? intval( $row['deleted'] ) : 0;
- $this->mSize = isset( $row['len'] ) ? intval( $row['len'] ) : null;
- $this->mParentId = isset( $row['parent_id'] ) ? intval( $row['parent_id'] ) : null;
- $this->mSha1 = isset( $row['sha1'] ) ? strval( $row['sha1'] ) : null;
-
- $this->mContentModel = isset( $row['content_model'] )
- ? strval( $row['content_model'] ) : null;
- $this->mContentFormat = isset( $row['content_format'] )
- ? strval( $row['content_format'] ) : null;
-
- // Enforce spacing trimming on supplied text
- $this->mComment = isset( $row['comment'] ) ? trim( strval( $row['comment'] ) ) : null;
- $this->mText = isset( $row['text'] ) ? rtrim( strval( $row['text'] ) ) : null;
+ // Lazy extraction...
+ $this->mText = null;
+ if ( isset( $row->old_text ) ) {
+ $this->mTextRow = $row;
+ } else {
+ // 'text' table row entry will be lazy-loaded
$this->mTextRow = null;
+ }
- $this->mTitle = isset( $row['title'] ) ? $row['title'] : null;
-
- // if we have a Content object, override mText and mContentModel
- if ( !empty( $row['content'] ) ) {
- if ( !( $row['content'] instanceof Content ) ) {
- throw new MWException( '`content` field must contain a Content object.' );
- }
-
- $handler = $this->getContentHandler();
- $this->mContent = $row['content'];
+ // Use user_name for users and rev_user_text for IPs...
+ $this->mUserText = null; // lazy load if left null
+ if ( $this->mUser == 0 ) {
+ $this->mUserText = $row->rev_user_text; // IP user
+ } elseif ( isset( $row->user_name ) ) {
+ $this->mUserText = $row->user_name; // logged-in user
+ }
+ $this->mOrigUserText = $row->rev_user_text;
+ }
- $this->mContentModel = $this->mContent->getModel();
- $this->mContentHandler = null;
+ /**
+ * @param array $row
+ *
+ * @throws MWException
+ */
+ private function constructFromRowArray( array $row ) {
+ // Build a new revision to be saved...
+ global $wgUser; // ugh
- $this->mText = $handler->serializeContent( $row['content'], $this->getContentFormat() );
- } elseif ( $this->mText !== null ) {
- $handler = $this->getContentHandler();
- $this->mContent = $handler->unserializeContent( $this->mText );
+ # if we have a content object, use it to set the model and type
+ if ( !empty( $row['content'] ) ) {
+ if ( !( $row['content'] instanceof Content ) ) {
+ throw new MWException( '`content` field must contain a Content object.' );
}
- // If we have a Title object, make sure it is consistent with mPage.
- if ( $this->mTitle && $this->mTitle->exists() ) {
- if ( $this->mPage === null ) {
- // if the page ID wasn't known, set it now
- $this->mPage = $this->mTitle->getArticleID();
- } elseif ( $this->mTitle->getArticleID() !== $this->mPage ) {
- // Got different page IDs. This may be legit (e.g. during undeletion),
- // but it seems worth mentioning it in the log.
- wfDebug( "Page ID " . $this->mPage . " mismatches the ID " .
- $this->mTitle->getArticleID() . " provided by the Title object." );
- }
+ // @todo when is that set? test with external store setup! check out insertOn() [dk]
+ if ( !empty( $row['text_id'] ) ) {
+ throw new MWException( "Text already stored in external store (id {$row['text_id']}), " .
+ "can't serialize content object" );
}
- $this->mCurrent = false;
+ $row['content_model'] = $row['content']->getModel();
+ # note: mContentFormat is initializes later accordingly
+ # note: content is serialized later in this method!
+ # also set text to null?
+ }
+
+ $this->mId = isset( $row['id'] ) ? intval( $row['id'] ) : null;
+ $this->mPage = isset( $row['page'] ) ? intval( $row['page'] ) : null;
+ $this->mTextId = isset( $row['text_id'] ) ? intval( $row['text_id'] ) : null;
+ $this->mUserText = isset( $row['user_text'] )
+ ? strval( $row['user_text'] ) : $wgUser->getName();
+ $this->mUser = isset( $row['user'] ) ? intval( $row['user'] ) : $wgUser->getId();
+ $this->mMinorEdit = isset( $row['minor_edit'] ) ? intval( $row['minor_edit'] ) : 0;
+ $this->mTimestamp = isset( $row['timestamp'] )
+ ? strval( $row['timestamp'] ) : wfTimestampNow();
+ $this->mDeleted = isset( $row['deleted'] ) ? intval( $row['deleted'] ) : 0;
+ $this->mSize = isset( $row['len'] ) ? intval( $row['len'] ) : null;
+ $this->mParentId = isset( $row['parent_id'] ) ? intval( $row['parent_id'] ) : null;
+ $this->mSha1 = isset( $row['sha1'] ) ? strval( $row['sha1'] ) : null;
+
+ $this->mContentModel = isset( $row['content_model'] )
+ ? strval( $row['content_model'] ) : null;
+ $this->mContentFormat = isset( $row['content_format'] )
+ ? strval( $row['content_format'] ) : null;
+
+ // Enforce spacing trimming on supplied text
+ $this->mComment = isset( $row['comment'] ) ? trim( strval( $row['comment'] ) ) : null;
+ $this->mText = isset( $row['text'] ) ? rtrim( strval( $row['text'] ) ) : null;
+ $this->mTextRow = null;
+
+ $this->mTitle = isset( $row['title'] ) ? $row['title'] : null;
+
+ // if we have a Content object, override mText and mContentModel
+ if ( !empty( $row['content'] ) ) {
+ $handler = $this->getContentHandler();
+ $this->mContent = $row['content'];
- // If we still have no length, see it we have the text to figure it out
- if ( !$this->mSize && $this->mContent !== null ) {
- $this->mSize = $this->mContent->getSize();
- }
+ $this->mContentModel = $this->mContent->getModel();
+ $this->mContentHandler = null;
- // Same for sha1
- if ( $this->mSha1 === null ) {
- $this->mSha1 = $this->mText === null ? null : self::base36Sha1( $this->mText );
+ $this->mText = $handler->serializeContent( $row['content'], $this->getContentFormat() );
+ } elseif ( $this->mText !== null ) {
+ $handler = $this->getContentHandler();
+ $this->mContent = $handler->unserializeContent( $this->mText );
+ }
+
+ // If we have a Title object, make sure it is consistent with mPage.
+ if ( $this->mTitle && $this->mTitle->exists() ) {
+ if ( $this->mPage === null ) {
+ // if the page ID wasn't known, set it now
+ $this->mPage = $this->mTitle->getArticleID();
+ } elseif ( $this->mTitle->getArticleID() !== $this->mPage ) {
+ // Got different page IDs. This may be legit (e.g. during undeletion),
+ // but it seems worth mentioning it in the log.
+ wfDebug( "Page ID " . $this->mPage . " mismatches the ID " .
+ $this->mTitle->getArticleID() . " provided by the Title object." );
}
+ }
- // force lazy init
- $this->getContentModel();
- $this->getContentFormat();
- } else {
- throw new MWException( 'Revision constructor passed invalid row format.' );
+ $this->mCurrent = false;
+
+ // If we still have no length, see it we have the text to figure it out
+ if ( !$this->mSize && $this->mContent !== null ) {
+ $this->mSize = $this->mContent->getSize();
}
- $this->mUnpatrolled = null;
+
+ // Same for sha1
+ if ( $this->mSha1 === null ) {
+ $this->mSha1 = $this->mText === null ? null : self::base36Sha1( $this->mText );
+ }
+
+ // force lazy init
+ $this->getContentModel();
+ $this->getContentFormat();
}
/**
$this->addTables( 'categorylinks' );
$this->addWhereFld( 'cl_from', array_keys( $this->getPageSet()->getGoodTitles() ) );
- if ( !is_null( $params['categories'] ) ) {
+ if ( $params['categories'] ) {
$cats = [];
foreach ( $params['categories'] as $cat ) {
$title = Title::newFromText( $cat );
$cats[] = $title->getDBkey();
}
}
+ if ( !$cats ) {
+ // No titles so no results
+ return;
+ }
$this->addWhereFld( 'cl_to', $cats );
}
}
$this->addOption( 'LIMIT', $params['limit'] + 1 );
- if ( !is_null( $params['images'] ) ) {
+ if ( $params['images'] ) {
$images = [];
foreach ( $params['images'] as $img ) {
$title = Title::newFromText( $img );
$images[] = $title->getDBkey();
}
}
+ if ( !$images ) {
+ // No titles so no results
+ return;
+ }
$this->addWhereFld( 'il_to', $images );
}
$this->addWhereFld( $this->prefix . '_from', array_keys( $this->getPageSet()->getGoodTitles() ) );
$this->addWhereFld( $this->prefix . '_namespace', $params['namespace'] );
- if ( !is_null( $params[$this->titlesParam] ) ) {
+ if ( $params[$this->titlesParam] ) {
$lb = new LinkBatch;
foreach ( $params[$this->titlesParam] as $t ) {
$title = Title::newFromText( $t );
$cond = $lb->constructSet( $this->prefix, $this->getDB() );
if ( $cond ) {
$this->addWhere( $cond );
+ } else {
+ // No titles so no results
+ return;
}
}
[ 'addPgIndex', 'user_groups', 'user_groups_expiry', '( ug_expiry )' ],
// 1.30
- [ 'modifyField', 'image', 'img_media_type', 'patch-add-3d.sql' ],
+ [ 'addPgEnumValue', 'media_type', '3D' ],
[ 'setDefault', 'revision', 'rev_comment', '' ],
[ 'changeNullableField', 'revision', 'rev_comment', 'NOT NULL', true ],
[ 'setDefault', 'archive', 'ar_comment', '' ],
}
}
+ /**
+ * Add a value to an existing PostgreSQL enum type
+ * @since 1.31
+ * @param string $type Type name. Must be in the core schema.
+ * @param string $value Value to add.
+ */
+ public function addPgEnumValue( $type, $value ) {
+ $row = $this->db->selectRow(
+ [
+ 't' => 'pg_catalog.pg_type',
+ 'n' => 'pg_catalog.pg_namespace',
+ 'e' => 'pg_catalog.pg_enum',
+ ],
+ [ 't.typname', 't.typtype', 'e.enumlabel' ],
+ [
+ 't.typname' => $type,
+ 'n.nspname' => $this->db->getCoreSchema(),
+ ],
+ __METHOD__,
+ [],
+ [
+ 'n' => [ 'JOIN', 't.typnamespace = n.oid' ],
+ 'e' => [ 'LEFT JOIN', [ 'e.enumtypid = t.oid', 'e.enumlabel' => $value ] ],
+ ]
+ );
+
+ if ( !$row ) {
+ $this->output( "...Type $type does not exist, skipping modify enum.\n" );
+ } elseif ( $row->typtype !== 'e' ) {
+ $this->output( "...Type $type does not seem to be an enum, skipping modify enum.\n" );
+ } elseif ( $row->enumlabel === $value ) {
+ $this->output( "...Enum type $type already contains value '$value'.\n" );
+ } else {
+ $this->output( "...Adding value '$value' to enum type $type.\n" );
+ $etype = $this->db->addIdentifierQuotes( $type );
+ $evalue = $this->db->addQuotes( $value );
+ $this->db->query( "ALTER TYPE $etype ADD VALUE $evalue" );
+ }
+ }
+
protected function dropFkey( $table, $field ) {
$fi = $this->db->fieldInfo( $table, $field );
if ( is_null( $fi ) ) {
$response['reached'] = 'none-possible';
return $response;
}
+
// Bail out if DB is in read-only mode
if ( wfReadOnly() ) {
$response['reached'] = 'read-only';
}
$lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
+ if ( $lbFactory->hasTransactionRound() ) {
+ throw new LogicException( __METHOD__ . ' called with an active transaction round.' );
+ }
// Bail out if there is too much DB lag.
// This check should not block as we want to try other wiki queues.
list( , $maxLag ) = $lbFactory->getMainLB( wfWikiID() )->getMaxLag();
return $response;
}
- // Flush any pending DB writes for sanity
- $lbFactory->commitAll( __METHOD__ );
-
// Catch huge single updates that lead to replica DB lag
$trxProfiler = Profiler::instance()->getTransactionProfiler();
$trxProfiler->setLogger( LoggerFactory::getInstance( 'DBPerformance' ) );
} else {
$job = $group->pop( $type ); // job from a single queue
}
- $lbFactory->commitMasterChanges( __METHOD__ ); // flush any JobQueueDB writes
if ( $job ) { // found a job
++$jobsPopped;
$info = $this->executeJob( $job, $lbFactory, $stats, $popTime );
if ( $info['status'] !== false || !$job->allowRetries() ) {
$group->ack( $job ); // succeeded or job cannot be retried
- $lbFactory->commitMasterChanges( __METHOD__ ); // flush any JobQueueDB writes
}
// Back off of certain jobs for a while (for throttling and for errors)
"MW_USE_LOG_PIPE=yes"
);
$useLogPipe = true;
- } elseif ( $this->useStderr ) {
- $cmd .= ' 2>&1';
}
- } elseif ( $this->useStderr ) {
+ }
+ if ( !$useLogPipe && $this->useStderr ) {
$cmd .= ' 2>&1';
}
wfDebug( __METHOD__ . ": $cmd\n" );
// input. See T129506.
if ( strlen( $cmd ) > SHELL_MAX_ARG_STRLEN ) {
throw new Exception( __METHOD__ .
- '(): total length of $cmd must not exceed SHELL_MAX_ARG_STRLEN' );
+ '(): total length of $cmd must not exceed SHELL_MAX_ARG_STRLEN' );
}
$desc = [
0 => [ 'file', 'php://stdin', 'r' ],
1 => [ 'pipe', 'w' ],
- 2 => [ 'file', 'php://stderr', 'w' ],
+ 2 => [ 'pipe', 'w' ],
];
if ( $useLogPipe ) {
$desc[3] = [ 'pipe', 'w' ];
throw new ProcOpenError();
}
$outBuffer = $logBuffer = '';
+ $errBuffer = null;
$emptyArray = [];
$status = false;
$logMsg = false;
} elseif ( $fd == 1 ) {
// From stdout
$outBuffer .= $block;
+ } elseif ( $fd == 2 ) {
+ // From stderr
+ $errBuffer .= $block;
} elseif ( $fd == 3 ) {
// From log FD
$logBuffer .= $block;
$this->logger->warning( "$logMsg: {command}", [ 'command' => $cmd ] );
}
- return new Result( $retval, $outBuffer );
+ return new Result( $retval, $outBuffer, $errBuffer );
}
}
/** @var string */
private $stdout;
+ /** @var string|null */
+ private $stderr;
+
/**
* @param int $exitCode
* @param string $stdout
*/
- public function __construct( $exitCode, $stdout ) {
+ public function __construct( $exitCode, $stdout, $stderr = null ) {
$this->exitCode = $exitCode;
$this->stdout = $stdout;
+ $this->stderr = $stderr;
}
/**
public function getStdout() {
return $this->stdout;
}
+
+ /**
+ * Returns stderr of the process or null if the Command was configured to add stderr to stdout
+ * with includeStderr( true )
+ *
+ * @return string|null
+ */
+ public function getStderr() {
+ return $this->stderr;
+ }
}
*
* ... = $result->getExitCode();
* ... = $result->getStdout();
+ * ... = $result->getStderr();
*/
class Shell {
if ( $savedQueries && isset( $savedQueries[ 'default' ] ) ) {
// Only load queries that are 'version' 2, since those
// have parameter representation
- if ( $savedQueries[ 'version' ] === '2' ) {
+ if ( isset( $savedQueries[ 'version' ] ) && $savedQueries[ 'version' ] === '2' ) {
$savedQueryDefaultID = $savedQueries[ 'default' ];
$defaultQuery = $savedQueries[ 'queries' ][ $savedQueryDefaultID ][ 'data' ];
+++ /dev/null
-ALTER TYPE media_type ADD VALUE '3D';
</rule>
<rule ref="MediaWiki.NamingConventions.PrefixedGlobalFunctions">
<properties>
- <property name="ignoreList" type="array" value="bfNormalizeTitleStrReplace,bfNormalizeTitleStrTr,cdbShowHelp,codepointToUtf8,compare_point,cssfilter,escapeSingleString,findAuxFile,findFiles,getEscapedProfileUrl,getFileCommentFromSourceWiki,getFileUserFromSourceWiki,hexSequenceToUtf8,mccGetHelp,mccShowUsage,mimeTypeMatch,moveToExternal,NothingFunction,NothingFunctionData,resolveStub,resolveStubs,showUsage,splitFilename,utf8ToCodepoint,utf8ToHexSequence" />
+ <!--
+ includes/compat/normal/UtfNormalUtil.php
+ * codepointToUtf8
+ * escapeSingleString
+ * hexSequenceToUtf8
+ * utf8ToCodepoint
+ * utf8ToHexSequence
+ includes/GlobalFunctions.php
+ * mimeTypeMatch
+ maintenance/benchmarks/bench_strtr_str_replace.php
+ * bfNormalizeTitleStrReplace
+ * bfNormalizeTitleStrTr
+ maintenance/cdb.php
+ * cdbShowHelp
+ maintenance/language/transstat.php
+ * showUsage
+ maintenance/mcc.php
+ * mccGetHelp
+ * mccShowUsage
+ maintenance/storage/moveToExternal.php
+ * moveToExternal
+ maintenance/storage/resolveStubs.php
+ * resolveStub
+ * resolveStubs
+ profileinfo.php
+ * compare_point
+ * getEscapedProfileUrl
+ tests/phpunit/includes/HooksTest.php
+ * NothingFunction
+ * NothingFunctionData
+ tests/qunit/data/styleTest.css.php
+ * cssfilter
+ -->
+ <property name="ignoreList" type="array" value="bfNormalizeTitleStrReplace,bfNormalizeTitleStrTr,cdbShowHelp,codepointToUtf8,compare_point,cssfilter,escapeSingleString,getEscapedProfileUrl,hexSequenceToUtf8,mccGetHelp,mccShowUsage,mimeTypeMatch,moveToExternal,NothingFunction,NothingFunctionData,resolveStub,resolveStubs,showUsage,utf8ToCodepoint,utf8ToHexSequence" />
</properties>
</rule>
<rule ref="MediaWiki.NamingConventions.ValidGlobalName">
// See bug T37201.
activateElementOnIE( this );
- if ( context ) {
- context.fn.restoreCursorAndScrollTop();
- }
if ( options.selectionStart !== undefined ) {
$( this ).textSelection( 'setSelection', { start: options.selectionStart, end: options.selectionEnd } );
}
public function testInsertTruncation() {
$comment = str_repeat( '💣', 16400 );
$truncated1 = str_repeat( '💣', 63 ) . '...';
- $truncated2 = str_repeat( '💣', 16383 ) . '...';
+ $truncated2 = str_repeat( '💣', CommentStore::COMMENT_CHARACTER_LIMIT - 3 ) . '...';
$store = $this->makeStore( MIGRATION_WRITE_BOTH, 'ipb_reason' );
$fields = $store->insert( $this->db, $comment );
*/
private $the_page;
- function __construct( $name = null, array $data = [], $dataName = '' ) {
+ public function __construct( $name = null, array $data = [], $dataName = '' ) {
parent::__construct( $name, $data, $dataName );
$this->tablesUsed = array_merge( $this->tablesUsed,
}
protected function setUp() {
- global $wgExtraNamespaces, $wgNamespaceContentModels, $wgContentHandlers, $wgContLang;
+ global $wgContLang;
parent::setUp();
- $wgExtraNamespaces[12312] = 'Dummy';
- $wgExtraNamespaces[12313] = 'Dummy_talk';
+ $this->mergeMwGlobalArrayValue(
+ 'wgExtraNamespaces',
+ [
+ 12312 => 'Dummy',
+ 12313 => 'Dummy_talk',
+ ]
+ );
- $wgNamespaceContentModels[12312] = 'DUMMY';
- $wgContentHandlers['DUMMY'] = 'DummyContentHandlerForTesting';
+ $this->mergeMwGlobalArrayValue(
+ 'wgNamespaceContentModels',
+ [
+ 12312 => 'DUMMY',
+ ]
+ );
+
+ $this->mergeMwGlobalArrayValue(
+ 'wgContentHandlers',
+ [
+ 'DUMMY' => 'DummyContentHandlerForTesting',
+ ]
+ );
MWNamespace::clearCaches();
- $wgContLang->resetNamespaces(); # reset namespace cache
+ // Reset namespace cache
+ $wgContLang->resetNamespaces();
if ( !$this->the_page ) {
$this->the_page = $this->createPage(
'RevisionStorageTest_the_page',
}
protected function tearDown() {
- global $wgExtraNamespaces, $wgNamespaceContentModels, $wgContentHandlers, $wgContLang;
+ global $wgContLang;
parent::tearDown();
- unset( $wgExtraNamespaces[12312] );
- unset( $wgExtraNamespaces[12313] );
-
- unset( $wgNamespaceContentModels[12312] );
- unset( $wgContentHandlers['DUMMY'] );
-
MWNamespace::clearCaches();
- $wgContLang->resetNamespaces(); # reset namespace cache
+ // Reset namespace cache
+ $wgContLang->resetNamespaces();
}
protected function makeRevision( $props = null ) {
return $rev;
}
- protected function createPage( $page, $text, $model = null ) {
- if ( is_string( $page ) ) {
- if ( !preg_match( '/:/', $page ) &&
- ( $model === null || $model === CONTENT_MODEL_WIKITEXT )
- ) {
- $ns = $this->getDefaultWikitextNS();
- $page = MWNamespace::getCanonicalName( $ns ) . ':' . $page;
- }
-
- $page = Title::newFromText( $page );
+ /**
+ * @param string $titleString
+ * @param string $text
+ * @param string|null $model
+ *
+ * @return WikiPage
+ */
+ protected function createPage( $titleString, $text, $model = null ) {
+ if ( !preg_match( '/:/', $titleString ) &&
+ ( $model === null || $model === CONTENT_MODEL_WIKITEXT )
+ ) {
+ $ns = $this->getDefaultWikitextNS();
+ $titleString = MWNamespace::getCanonicalName( $ns ) . ':' . $titleString;
}
- if ( $page instanceof Title ) {
- $page = new WikiPage( $page );
- }
+ $title = Title::newFromText( $titleString );
+ $wikipage = new WikiPage( $title );
- if ( $page->exists() ) {
- $page->doDeleteArticle( "done" );
+ // Delete the article if it already exists
+ if ( $wikipage->exists() ) {
+ $wikipage->doDeleteArticle( "done" );
}
- $content = ContentHandler::makeContent( $text, $page->getTitle(), $model );
- $page->doEditContent( $content, "testing", EDIT_NEW );
+ $content = ContentHandler::makeContent( $text, $title, $model );
+ $wikipage->doEditContent( $content, __METHOD__, EDIT_NEW );
- return $page;
+ return $wikipage;
}
protected function assertRevEquals( Revision $orig, Revision $rev = null ) {
$this->assertRevEquals( $orig, $rev );
}
+ /**
+ * @covers Revision::newFromTitle
+ */
+ public function testNewFromTitle_withoutId() {
+ $page = $this->createPage(
+ __METHOD__,
+ 'GOAT',
+ CONTENT_MODEL_WIKITEXT
+ );
+ $latestRevId = $page->getLatest();
+
+ $rev = Revision::newFromTitle( $page->getTitle() );
+
+ $this->assertTrue( $page->getTitle()->equals( $rev->getTitle() ) );
+ $this->assertEquals( $latestRevId, $rev->getId() );
+ }
+
+ /**
+ * @covers Revision::newFromTitle
+ */
+ public function testNewFromTitle_withId() {
+ $page = $this->createPage(
+ __METHOD__,
+ 'GOAT',
+ CONTENT_MODEL_WIKITEXT
+ );
+ $latestRevId = $page->getLatest();
+
+ $rev = Revision::newFromTitle( $page->getTitle(), $latestRevId );
+
+ $this->assertTrue( $page->getTitle()->equals( $rev->getTitle() ) );
+ $this->assertEquals( $latestRevId, $rev->getId() );
+ }
+
+ /**
+ * @covers Revision::newFromTitle
+ */
+ public function testNewFromTitle_withBadId() {
+ $page = $this->createPage(
+ __METHOD__,
+ 'GOAT',
+ CONTENT_MODEL_WIKITEXT
+ );
+ $latestRevId = $page->getLatest();
+
+ $rev = Revision::newFromTitle( $page->getTitle(), $latestRevId + 1 );
+
+ $this->assertNull( $rev );
+ }
+
/**
* @covers Revision::newFromRow
*/
}
public static function provideUserWasLastToEdit() {
- return [
- [ # 0
- 3, true, # actually the last edit
- ],
- [ # 1
- 2, true, # not the current edit, but still by this user
- ],
- [ # 2
- 1, false, # edit by another user
- ],
- [ # 3
- 0, false, # first edit, by this user, but another user edited in the mean time
- ],
- ];
+ yield 'actually the last edit' => [ 3, true ];
+ yield 'not the current edit, but still by this user' => [ 2, true ];
+ yield 'edit by another user' => [ 1, false ];
+ yield 'first edit, by this user, but another user edited in the mean time' => [ 0, false ];
}
/**
'RevisionStorageTest_testUserWasLastToEdit', $ns ) );
$page->insertOn( $dbw );
- # zero
$revisions[0] = new Revision( [
'page' => $page->getId(),
// we need the title to determine the page's default content model
] );
$revisions[0]->insertOn( $dbw );
- # one
$revisions[1] = new Revision( [
'page' => $page->getId(),
// still need the title, because $page->getId() is 0 (there's no entry in the page table)
] );
$revisions[1]->insertOn( $dbw );
- # two
$revisions[2] = new Revision( [
'page' => $page->getId(),
'title' => $page->getTitle(),
] );
$revisions[2]->insertOn( $dbw );
- # three
$revisions[3] = new Revision( [
'page' => $page->getId(),
'title' => $page->getTitle(),
] );
$revisions[3]->insertOn( $dbw );
- # four
$revisions[4] = new Revision( [
'page' => $page->getId(),
'title' => $page->getTitle(),
+++ /dev/null
-<?php
-
-/**
- * @group ContentHandler
- * @group Database
- * ^--- important, causes temporary tables to be used instead of the real database
- */
-class RevisionTestContentHandlerUseDB extends RevisionStorageTest {
-
- protected function setUp() {
- $this->setMwGlobals( 'wgContentHandlerUseDB', false );
-
- $dbw = wfGetDB( DB_MASTER );
-
- $page_table = $dbw->tableName( 'page' );
- $revision_table = $dbw->tableName( 'revision' );
- $archive_table = $dbw->tableName( 'archive' );
-
- if ( $dbw->fieldExists( $page_table, 'page_content_model' ) ) {
- $dbw->query( "alter table $page_table drop column page_content_model" );
- $dbw->query( "alter table $revision_table drop column rev_content_model" );
- $dbw->query( "alter table $revision_table drop column rev_content_format" );
- $dbw->query( "alter table $archive_table drop column ar_content_model" );
- $dbw->query( "alter table $archive_table drop column ar_content_format" );
- }
-
- parent::setUp();
- }
-
- /**
- * @covers Revision::selectFields
- */
- public function testSelectFields() {
- $fields = Revision::selectFields();
-
- $this->assertTrue( in_array( 'rev_id', $fields ), 'missing rev_id in list of fields' );
- $this->assertTrue( in_array( 'rev_page', $fields ), 'missing rev_page in list of fields' );
- $this->assertTrue(
- in_array( 'rev_timestamp', $fields ),
- 'missing rev_timestamp in list of fields'
- );
- $this->assertTrue( in_array( 'rev_user', $fields ), 'missing rev_user in list of fields' );
-
- $this->assertFalse(
- in_array( 'rev_content_model', $fields ),
- 'missing rev_content_model in list of fields'
- );
- $this->assertFalse(
- in_array( 'rev_content_format', $fields ),
- 'missing rev_content_format in list of fields'
- );
- }
-
- /**
- * @covers Revision::getContentModel
- */
- public function testGetContentModel() {
- try {
- $this->makeRevision( [ 'text' => 'hello hello.',
- 'content_model' => CONTENT_MODEL_JAVASCRIPT ] );
-
- $this->fail( "Creating JavaScript content on a wikitext page should fail with "
- . "\$wgContentHandlerUseDB disabled" );
- } catch ( MWException $ex ) {
- $this->assertTrue( true ); // ok
- }
- }
-
- /**
- * @covers Revision::getContentFormat
- */
- public function testGetContentFormat() {
- try {
- // @todo change this to test failure on using a non-standard (but supported) format
- // for a content model supported in the given location. As of 1.21, there are
- // no alternative formats for any of the standard content models that could be
- // used for this though.
-
- $this->makeRevision( [ 'text' => 'hello hello.',
- 'content_model' => CONTENT_MODEL_JAVASCRIPT,
- 'content_format' => 'text/javascript' ] );
-
- $this->fail( "Creating JavaScript content on a wikitext page should fail with "
- . "\$wgContentHandlerUseDB disabled" );
- } catch ( MWException $ex ) {
- $this->assertTrue( true ); // ok
- }
- }
-}
parent::tearDown();
}
+ public function provideConstructFromArray() {
+ yield 'with text' => [
+ [
+ 'text' => 'hello world.',
+ 'content_model' => CONTENT_MODEL_JAVASCRIPT
+ ],
+ ];
+ yield 'with content' => [
+ [
+ 'content' => new JavaScriptContent( 'hellow world.' )
+ ],
+ ];
+ }
+
/**
- * @covers Revision::getRevisionText
+ * @dataProvider provideConstructFromArray
*/
- public function testGetRevisionText() {
- $row = new stdClass;
- $row->old_flags = '';
- $row->old_text = 'This is a bunch of revision text.';
- $this->assertEquals(
- 'This is a bunch of revision text.',
- Revision::getRevisionText( $row ) );
+ public function testConstructFromArray( $rowArray ) {
+ $rev = new Revision( $rowArray );
+ $this->assertNotNull( $rev->getContent(), 'no content object available' );
+ $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContent()->getModel() );
+ $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContentModel() );
+ }
+
+ public function provideConstructFromArrayThrowsExceptions() {
+ yield 'content and text_id both not empty' => [
+ [
+ 'content' => new WikitextContent( 'GOAT' ),
+ 'text_id' => 'someid',
+ ],
+ new MWException( "Text already stored in external store (id someid), " .
+ "can't serialize content object" )
+ ];
+ yield 'with bad content object (class)' => [
+ [ 'content' => new stdClass() ],
+ new MWException( '`content` field must contain a Content object.' )
+ ];
+ yield 'with bad content object (string)' => [
+ [ 'content' => 'ImAGoat' ],
+ new MWException( '`content` field must contain a Content object.' )
+ ];
+ yield 'bad row format' => [
+ 'imastring, not a row',
+ new MWException( 'Revision constructor passed invalid row format.' )
+ ];
}
/**
- * @covers Revision::getRevisionText
+ * @dataProvider provideConstructFromArrayThrowsExceptions
*/
- public function testGetRevisionTextGzip() {
- $this->checkPHPExtension( 'zlib' );
+ public function testConstructFromArrayThrowsExceptions( $rowArray, Exception $expectedException ) {
+ $this->setExpectedException(
+ get_class( $expectedException ),
+ $expectedException->getMessage(),
+ $expectedException->getCode()
+ );
+ new Revision( $rowArray );
+ }
- $row = new stdClass;
- $row->old_flags = 'gzip';
- $row->old_text = gzdeflate( 'This is a bunch of revision text.' );
- $this->assertEquals(
- 'This is a bunch of revision text.',
- Revision::getRevisionText( $row ) );
+ public function provideGetRevisionText() {
+ yield 'Generic test' => [
+ 'This is a goat of revision text.',
+ [
+ 'old_flags' => '',
+ 'old_text' => 'This is a goat of revision text.',
+ ],
+ ];
}
/**
* @covers Revision::getRevisionText
+ * @dataProvider provideGetRevisionText
*/
- public function testGetRevisionTextUtf8Native() {
- $row = new stdClass;
- $row->old_flags = 'utf-8';
- $row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
- $GLOBALS['wgLegacyEncoding'] = 'iso-8859-1';
+ public function testGetRevisionText( $expected, $rowData, $prefix = 'old_', $wiki = false ) {
$this->assertEquals(
- "Wiki est l'\xc3\xa9cole superieur !",
- Revision::getRevisionText( $row ) );
+ $expected,
+ Revision::getRevisionText( (object)$rowData, $prefix, $wiki ) );
+ }
+
+ public function provideGetRevisionTextWithZlibExtension() {
+ yield 'Generic gzip test' => [
+ 'This is a small goat of revision text.',
+ [
+ 'old_flags' => 'gzip',
+ 'old_text' => gzdeflate( 'This is a small goat of revision text.' ),
+ ],
+ ];
}
/**
* @covers Revision::getRevisionText
+ * @dataProvider provideGetRevisionTextWithZlibExtension
*/
- public function testGetRevisionTextUtf8Legacy() {
- $row = new stdClass;
- $row->old_flags = '';
- $row->old_text = "Wiki est l'\xe9cole superieur !";
- $GLOBALS['wgLegacyEncoding'] = 'iso-8859-1';
- $this->assertEquals(
+ public function testGetRevisionWithZlibExtension( $expected, $rowData ) {
+ $this->checkPHPExtension( 'zlib' );
+ $this->testGetRevisionText( $expected, $rowData );
+ }
+
+ public function provideGetRevisionTextWithLegacyEncoding() {
+ yield 'Utf8Native' => [
"Wiki est l'\xc3\xa9cole superieur !",
- Revision::getRevisionText( $row ) );
+ 'iso-8859-1',
+ [
+ 'old_flags' => 'utf-8',
+ 'old_text' => "Wiki est l'\xc3\xa9cole superieur !",
+ ]
+ ];
+ yield 'Utf8Legacy' => [
+ "Wiki est l'\xc3\xa9cole superieur !",
+ 'iso-8859-1',
+ [
+ 'old_flags' => '',
+ 'old_text' => "Wiki est l'\xe9cole superieur !",
+ ]
+ ];
}
/**
* @covers Revision::getRevisionText
+ * @dataProvider provideGetRevisionTextWithLegacyEncoding
*/
- public function testGetRevisionTextUtf8NativeGzip() {
- $this->checkPHPExtension( 'zlib' );
+ public function testGetRevisionWithLegacyEncoding( $expected, $encoding, $rowData ) {
+ $GLOBALS['wgLegacyEncoding'] = $encoding;
+ $this->testGetRevisionText( $expected, $rowData );
+ }
- $row = new stdClass;
- $row->old_flags = 'gzip,utf-8';
- $row->old_text = gzdeflate( "Wiki est l'\xc3\xa9cole superieur !" );
- $GLOBALS['wgLegacyEncoding'] = 'iso-8859-1';
- $this->assertEquals(
+ public function provideGetRevisionTextWithGzipAndLegacyEncoding() {
+ yield 'Utf8NativeGzip' => [
"Wiki est l'\xc3\xa9cole superieur !",
- Revision::getRevisionText( $row ) );
+ 'iso-8859-1',
+ [
+ 'old_flags' => 'gzip,utf-8',
+ 'old_text' => gzdeflate( "Wiki est l'\xc3\xa9cole superieur !" ),
+ ]
+ ];
+ yield 'Utf8LegacyGzip' => [
+ "Wiki est l'\xc3\xa9cole superieur !",
+ 'iso-8859-1',
+ [
+ 'old_flags' => 'gzip',
+ 'old_text' => gzdeflate( "Wiki est l'\xe9cole superieur !" ),
+ ]
+ ];
}
/**
* @covers Revision::getRevisionText
+ * @dataProvider provideGetRevisionTextWithGzipAndLegacyEncoding
*/
- public function testGetRevisionTextUtf8LegacyGzip() {
+ public function testGetRevisionWithGzipAndLegacyEncoding( $expected, $encoding, $rowData ) {
$this->checkPHPExtension( 'zlib' );
-
- $row = new stdClass;
- $row->old_flags = 'gzip';
- $row->old_text = gzdeflate( "Wiki est l'\xe9cole superieur !" );
- $GLOBALS['wgLegacyEncoding'] = 'iso-8859-1';
- $this->assertEquals(
- "Wiki est l'\xc3\xa9cole superieur !",
- Revision::getRevisionText( $row ) );
+ $GLOBALS['wgLegacyEncoding'] = $encoding;
+ $this->testGetRevisionText( $expected, $rowData );
}
/**
Revision::getRevisionText( $row ), "getRevisionText" );
}
- # =========================================================================
-
/**
* @param string $text
* @param string $title
return $rev;
}
- public function dataGetContentModel() {
+ public function provideGetContentModel() {
// NOTE: we expect the help namespace to always contain wikitext
return [
[ 'hello world', 'Help:Hello', null, null, CONTENT_MODEL_WIKITEXT ],
/**
* @group Database
- * @dataProvider dataGetContentModel
+ * @dataProvider provideGetContentModel
* @covers Revision::getContentModel
*/
public function testGetContentModel( $text, $title, $model, $format, $expectedModel ) {
$this->assertEquals( $expectedModel, $rev->getContentModel() );
}
- public function dataGetContentFormat() {
+ public function provideGetContentFormat() {
// NOTE: we expect the help namespace to always contain wikitext
return [
[ 'hello world', 'Help:Hello', null, null, CONTENT_FORMAT_WIKITEXT ],
/**
* @group Database
- * @dataProvider dataGetContentFormat
+ * @dataProvider provideGetContentFormat
* @covers Revision::getContentFormat
*/
public function testGetContentFormat( $text, $title, $model, $format, $expectedFormat ) {
$this->assertEquals( $expectedFormat, $rev->getContentFormat() );
}
- public function dataGetContentHandler() {
+ public function provideGetContentHandler() {
// NOTE: we expect the help namespace to always contain wikitext
return [
[ 'hello world', 'Help:Hello', null, null, 'WikitextContentHandler' ],
/**
* @group Database
- * @dataProvider dataGetContentHandler
+ * @dataProvider provideGetContentHandler
* @covers Revision::getContentHandler
*/
public function testGetContentHandler( $text, $title, $model, $format, $expectedClass ) {
$this->assertEquals( $expectedClass, get_class( $rev->getContentHandler() ) );
}
- public function dataGetContent() {
+ public function provideGetContent() {
// NOTE: we expect the help namespace to always contain wikitext
return [
[ 'hello world', 'Help:Hello', null, null, Revision::FOR_PUBLIC, 'hello world' ],
/**
* @group Database
- * @dataProvider dataGetContent
+ * @dataProvider provideGetContent
* @covers Revision::getContent
*/
public function testGetContent( $text, $title, $model, $format,
);
}
- public function dataGetSize() {
+ public function provideGetSize() {
return [
[ "hello world.", CONTENT_MODEL_WIKITEXT, 12 ],
[ serialize( "hello world." ), "testing", 12 ],
/**
* @covers Revision::getSize
* @group Database
- * @dataProvider dataGetSize
+ * @dataProvider provideGetSize
*/
public function testGetSize( $text, $model, $expected_size ) {
$rev = $this->newTestRevision( $text, 'RevisionTest_testGetSize', $model );
$this->assertEquals( $expected_size, $rev->getSize() );
}
- public function dataGetSha1() {
+ public function provideGetSha1() {
return [
[ "hello world.", CONTENT_MODEL_WIKITEXT, Revision::base36Sha1( "hello world." ) ],
[
/**
* @covers Revision::getSha1
* @group Database
- * @dataProvider dataGetSha1
+ * @dataProvider provideGetSha1
*/
public function testGetSha1( $text, $model, $expected_hash ) {
$rev = $this->newTestRevision( $text, 'RevisionTest_testGetSha1', $model );
$this->assertEquals( $expected_hash, $rev->getSha1() );
}
- /**
- * @covers Revision::__construct
- */
- public function testConstructWithText() {
- $rev = new Revision( [
- 'text' => 'hello world.',
- 'content_model' => CONTENT_MODEL_JAVASCRIPT
- ] );
-
- $this->assertNotNull( $rev->getContent(), 'no content object available' );
- $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContent()->getModel() );
- $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContentModel() );
- }
-
- /**
- * @covers Revision::__construct
- */
- public function testConstructWithContent() {
- $title = Title::newFromText( 'RevisionTest_testConstructWithContent' );
-
- $rev = new Revision( [
- 'content' => ContentHandler::makeContent( 'hello world.', $title, CONTENT_MODEL_JAVASCRIPT ),
- ] );
-
- $this->assertNotNull( $rev->getContent(), 'no content object available' );
- $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContent()->getModel() );
- $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContentModel() );
- }
-
/**
* Tests whether $rev->getContent() returns a clone when needed.
*
$this->assertSame( "bar\n", $result->getStdout() );
}
+ public function testStdout() {
+ $this->requirePosix();
+
+ $command = new Command();
+
+ $result = $command
+ ->params( 'bash', '-c', 'echo ThisIsStderr 1>&2' )
+ ->execute();
+
+ $this->assertNotContains( 'ThisIsStderr', $result->getStdout() );
+ $this->assertEquals( "ThisIsStderr\n", $result->getStderr() );
+ }
+
+ public function testStdoutRedirection() {
+ $this->requirePosix();
+
+ $command = new Command();
+
+ $result = $command
+ ->params( 'bash', '-c', 'echo ThisIsStderr 1>&2' )
+ ->includeStderr( true )
+ ->execute();
+
+ $this->assertEquals( "ThisIsStderr\n", $result->getStdout() );
+ $this->assertNull( $result->getStderr() );
+ }
+
public function testOutput() {
global $IP;
$this->requirePosix();
+ chdir( $IP );
$command = new Command();
$result = $command
- ->params( [ 'ls', "$IP/index.php" ] )
+ ->params( [ 'ls', 'index.php' ] )
->execute();
- $this->assertSame( "$IP/index.php", trim( $result->getStdout() ) );
+ $this->assertRegExp( '/^index.php$/m', $result->getStdout() );
+ $this->assertSame( null, $result->getStderr() );
$command = new Command();
$result = $command
->params( [ 'ls', 'index.php', 'no-such-file' ] )
->includeStderr()
->execute();
+ $this->assertRegExp( '/^index.php$/m', $result->getStdout() );
$this->assertRegExp( '/^.+no-such-file.*$/m', $result->getStdout() );
+ $this->assertSame( null, $result->getStderr() );
+
+ $command = new Command();
+ $result = $command
+ ->params( [ 'ls', 'index.php', 'no-such-file' ] )
+ ->execute();
+ $this->assertRegExp( '/^index.php$/m', $result->getStdout() );
+ $this->assertRegExp( '/^.+no-such-file.*$/m', $result->getStderr() );
}
public function testT69870() {