MDL-20438 updated response format support
Multiple component updates are supported now. Code cleanup and refactoring.
This commit is contained in:
+9
-9
@@ -651,7 +651,7 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
if (!$plugin->is_standard()) {
|
||||
$numextension++;
|
||||
}
|
||||
if ($plugin->available_update()) {
|
||||
if ($plugin->available_updates()) {
|
||||
$numupdatable++;
|
||||
}
|
||||
}
|
||||
@@ -779,10 +779,11 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
$requiredby = '';
|
||||
}
|
||||
|
||||
if ($updateinfo = $plugin->available_update()) {
|
||||
$updateinfo = $this->plugin_available_update_info($updateinfo);
|
||||
} else {
|
||||
$updateinfo = '';
|
||||
$updateinfo = '';
|
||||
if (is_array($plugin->available_updates())) {
|
||||
foreach ($plugin->available_updates() as $availableupdate) {
|
||||
$updateinfo .= $this->plugin_available_update_info($availableupdate);
|
||||
}
|
||||
}
|
||||
|
||||
$notes = new html_table_cell($requiredby.$updateinfo);
|
||||
@@ -801,12 +802,11 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
* Helper method to render the information about the available update
|
||||
*
|
||||
* The passed objects always provides at least the 'version' property containing
|
||||
* the (higher) version of the plugin available. Other properties may be provided, see
|
||||
* the specification of the protocol used by {@link available_update_checker}.
|
||||
* the (higher) version of the plugin available.
|
||||
*
|
||||
* @param stdClass $updateinfo information about the available update for the plugin
|
||||
* @param available_update_info $updateinfo information about the available update for the plugin
|
||||
*/
|
||||
protected function plugin_available_update_info(stdClass $updateinfo) {
|
||||
protected function plugin_available_update_info(available_update_info $updateinfo) {
|
||||
|
||||
$box = $this->output->box_start('pluginupdateinfo');
|
||||
$box .= html_writer::tag('div', get_string('updateavailable', 'core_plugin', $updateinfo->version), array('class' => 'version'));
|
||||
|
||||
+142
-88
@@ -123,7 +123,7 @@ class plugin_manager {
|
||||
$provider = available_update_checker::instance();
|
||||
foreach ($this->pluginsinfo as $plugintype => $plugins) {
|
||||
foreach ($plugins as $plugininfoholder) {
|
||||
$plugininfoholder->check_available_update($provider);
|
||||
$plugininfoholder->check_available_updates($provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -572,20 +572,15 @@ class available_update_checker {
|
||||
|
||||
/** @var available_update_checker holds the singleton instance */
|
||||
protected static $singletoninstance;
|
||||
/** @var null|string numerical version of the local Moodle site */
|
||||
protected $localversion = null;
|
||||
/** @var null|string release signature of the local Moodle site */
|
||||
protected $localrelease = null;
|
||||
/** @var array of (string)frankestyle => (string)version list of plugins installed at the local Moodle site */
|
||||
protected $localplugins = array();
|
||||
/** @var null|stdClass */
|
||||
protected $config = null;
|
||||
/** @var null|int the timestamp of when the most recent response was fetched */
|
||||
protected $recentfetch = null;
|
||||
/** @var null|array the recent response from the update notification provider */
|
||||
protected $recentresponse = null;
|
||||
|
||||
/**
|
||||
* Direct initiation not allowed, use the factory method {@link self::instance()}
|
||||
*/
|
||||
protected function __construct() {
|
||||
$this->load_config();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -658,10 +653,14 @@ class available_update_checker {
|
||||
* @return int|null null if it has never been executed or we don't known
|
||||
*/
|
||||
public function get_last_timefetched() {
|
||||
if (isset($this->config->timelastfetched)) {
|
||||
return $this->config->timelastfetched;
|
||||
|
||||
$this->restore_response();
|
||||
|
||||
if (!empty($this->recentfetch)) {
|
||||
return $this->recentfetch;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -671,126 +670,133 @@ class available_update_checker {
|
||||
* @throws available_update_checker_exception
|
||||
*/
|
||||
public function fetch() {
|
||||
$response = $this->make_request();
|
||||
$response = $this->get_response();
|
||||
$this->validate_response($response);
|
||||
$this->process_response($response);
|
||||
$this->store_response($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the available update information for the given component
|
||||
*
|
||||
* This method returns null if the most recent response does not contain any information
|
||||
* about it. Note that this does not mean that the information is not provided by the
|
||||
* remote site. The recent request might be specific
|
||||
* about it. The returned structure is an array of available updates for the given
|
||||
* component. Each update info is an object with at least one property called
|
||||
* 'version'. Other possible properties are 'release', 'maturity', 'url' and 'downloadurl'.
|
||||
*
|
||||
* @param string $component frankenstyle
|
||||
* @return null|stdClass null if the most recent response does not provide any info
|
||||
* @return null|stdClass null or array of objects
|
||||
*/
|
||||
public function get_update_info($component) {
|
||||
|
||||
$branch = moodle_major_version();
|
||||
$this->restore_response();
|
||||
|
||||
if (!empty($this->config->components->$branch->$component)) {
|
||||
return $this->config->components->$branch->$component;
|
||||
if (!empty($this->recentresponse['updates'][$component])) {
|
||||
$updates = array();
|
||||
foreach ($this->recentresponse['updates'][$component] as $info) {
|
||||
$updates[] = new available_update_info($component, $info);
|
||||
}
|
||||
return $updates;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes cURL request to get data from the remote site
|
||||
* Makes cURL request to get data from the remote site
|
||||
*
|
||||
* @return stdClass request result
|
||||
* @return string raw request result
|
||||
* @throws available_update_checker_exception
|
||||
*/
|
||||
protected function make_request() {
|
||||
protected function get_response() {
|
||||
$curl = new curl(array('proxy' => true));
|
||||
$response = $curl->post($this->prepare_request_url(), $this->prepare_request_params());
|
||||
$curlinfo = $curl->get_info();
|
||||
if ($curlinfo['http_code'] != 200) {
|
||||
throw new available_update_checker_exception('err_response_http_code', $curlinfo['http_code']);
|
||||
}
|
||||
$response = json_decode($response);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the response is valid, has correct API format etc.
|
||||
*
|
||||
* @param stdClass $response
|
||||
* @param string $response raw response as returned by the {@link self::get_response()}
|
||||
* @throws available_update_checker_exception
|
||||
*/
|
||||
protected function validate_response(stdClass $response) {
|
||||
protected function validate_response($response) {
|
||||
|
||||
$response = $this->decode_response($response);
|
||||
|
||||
if (empty($response)) {
|
||||
throw new available_update_checker_exception('err_response_empty');
|
||||
}
|
||||
|
||||
if (empty($response->status) or $response->status !== 'OK') {
|
||||
throw new available_update_checker_exception('err_response_status', $response->status);
|
||||
if (empty($response['status']) or $response['status'] !== 'OK') {
|
||||
throw new available_update_checker_exception('err_response_status', $response['status']);
|
||||
}
|
||||
|
||||
if (empty($response->apiver) or $response->apiver != '1.0') {
|
||||
throw new available_update_checker_exception('err_response_format_version', $response->apiver);
|
||||
if (empty($response['apiver']) or $response['apiver'] !== '1.0') {
|
||||
throw new available_update_checker_exception('err_response_format_version', $response['apiver']);
|
||||
}
|
||||
|
||||
if (empty($response['forbranch']) or $response['forbranch'] !== moodle_major_version(true)) {
|
||||
throw new available_update_checker_exception('err_response_target_version', $response['target']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the fetched response for later usage
|
||||
* Decodes the raw string response from the update notifications provider
|
||||
*
|
||||
* @param string $response as returned by {@link self::get_response()}
|
||||
* @return array decoded response structure
|
||||
*/
|
||||
protected function decode_response($response) {
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the valid fetched response for later usage
|
||||
*
|
||||
* This implementation uses the config_plugins table as the permanent storage.
|
||||
*
|
||||
* @param stdClass $response the data returned by the updates info provider
|
||||
* @param string $response raw valid data returned by {@link self::get_response()}
|
||||
*/
|
||||
protected function process_response(stdClass $response) {
|
||||
protected function store_response($response) {
|
||||
|
||||
$components = $this->merge_components_info($this->config->components, $response->components, $response->timegenerated);
|
||||
set_config('recentfetch', time(), 'core_plugin');
|
||||
set_config('recentresponse', $response, 'core_plugin');
|
||||
|
||||
set_config('timelastfetched', time(), 'core_plugin');
|
||||
set_config('ticket', $response->ticket, 'core_plugin');
|
||||
set_config('components', json_encode($components), 'core_plugin');
|
||||
|
||||
$this->load_config(true);
|
||||
$this->restore_response(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the current and the new info about the available updates
|
||||
* Loads the most recent raw response record we have fetched
|
||||
*
|
||||
* @param stdClass $old
|
||||
* @param stdClass $new
|
||||
* @param int $timegenerated the timestamp of when the $new was generated
|
||||
* @return stdClass merged
|
||||
* This implementation uses the config_plugins table as the permanent storage.
|
||||
*
|
||||
* @param bool $forcereload reload even if it was already loaded
|
||||
*/
|
||||
protected function merge_components_info(stdClass $old, stdClass $new, $timegenerated=null) {
|
||||
$merged = clone($old);
|
||||
if (is_null($timegenerated)) {
|
||||
$timegenerated = time();
|
||||
protected function restore_response($forcereload = false) {
|
||||
|
||||
if (!$forcereload and !is_null($this->recentresponse)) {
|
||||
// we already have it, nothing to do
|
||||
return;
|
||||
}
|
||||
foreach ($new as $branch => $components) {
|
||||
foreach ($components as $component => $info) {
|
||||
$info->timegenerated = $timegenerated;
|
||||
if (isset($info->version)) {
|
||||
$merged->$branch->$component = $info;
|
||||
}
|
||||
|
||||
$config = get_config('core_plugin');
|
||||
|
||||
if (!empty($config->recentresponse) and !empty($config->recentfetch)) {
|
||||
try {
|
||||
$this->validate_response($config->recentresponse);
|
||||
$this->recentfetch = $config->recentfetch;
|
||||
$this->recentresponse = $this->decode_response($config->recentresponse);
|
||||
}
|
||||
catch (available_update_checker_exception $e) {
|
||||
// do not set recentresponse if the validation fails
|
||||
}
|
||||
}
|
||||
return $merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the core_plugin subsystem config
|
||||
*
|
||||
* @param bool $forcereload reload the config even if it was already loaded
|
||||
*/
|
||||
protected function load_config($forcereload = false) {
|
||||
if ($forcereload or is_null($this->config)) {
|
||||
$this->config = get_config('core_plugin');
|
||||
}
|
||||
|
||||
if (empty($this->config->components)) {
|
||||
$this->config->components = new stdClass();
|
||||
} else {
|
||||
$this->config->components = json_decode($this->config->components);
|
||||
$this->recentresponse = array();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -820,11 +826,13 @@ class available_update_checker {
|
||||
protected function prepare_request_params() {
|
||||
global $CFG;
|
||||
|
||||
$this->restore_response();
|
||||
|
||||
$params = array();
|
||||
$params['format'] = 'json';
|
||||
|
||||
if (isset($this->config->ticket)) {
|
||||
$params['ticket'] = $this->config->ticket;
|
||||
if (isset($this->recentresponse['ticket'])) {
|
||||
$params['ticket'] = $this->recentresponse['ticket'];
|
||||
}
|
||||
|
||||
if (isset($this->localversion)) {
|
||||
@@ -841,6 +849,44 @@ class available_update_checker {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines the structure of objects returned by {@link available_update_checker::get_update_info()}
|
||||
*/
|
||||
class available_update_info {
|
||||
|
||||
/** @var string frankenstyle component name */
|
||||
public $component;
|
||||
/** @var int the available version of the component */
|
||||
public $version;
|
||||
/** @var string|null optional release name */
|
||||
public $release = null;
|
||||
/** @var int|null optional maturity info, eg {@link MATURITY_STABLE} */
|
||||
public $maturity = null;
|
||||
/** @var string|null optional URL of a page with more info about the update */
|
||||
public $url = null;
|
||||
/** @var string|null optional URL of a ZIP package that can be downloaded and installed */
|
||||
public $download = null;
|
||||
|
||||
/**
|
||||
* Creates new instance of the class
|
||||
*
|
||||
* The $info array must provide at least the 'version' value and optionally all other
|
||||
* values to populate the object's properties.
|
||||
*
|
||||
* @param string $name the frankenstyle component name
|
||||
* @param array $info associative array with other properties
|
||||
*/
|
||||
public function __construct($name, array $info) {
|
||||
$this->component = $name;
|
||||
foreach ($info as $k => $v) {
|
||||
if (property_exists('available_update_info', $k) and $k != 'component') {
|
||||
$this->$k = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Factory class producing required subclasses of {@link plugininfo_base}
|
||||
*/
|
||||
@@ -905,8 +951,8 @@ abstract class plugininfo_base {
|
||||
public $instances;
|
||||
/** @var int order of the plugin among other plugins of the same type - not supported yet */
|
||||
public $sortorder;
|
||||
/** @var null|stdClass holds the information about the remote available update for this plugin */
|
||||
public $availableupdate;
|
||||
/** @var array|null array of {@link available_update_info} for this plugin */
|
||||
public $availableupdates;
|
||||
|
||||
/**
|
||||
* Gathers and returns the information about all plugins of the given type
|
||||
@@ -1136,35 +1182,43 @@ abstract class plugininfo_base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the property {@link $availableupdate} with the information provided by
|
||||
* Populates the property {@link $availableupdates} with the information provided by
|
||||
* available update checker
|
||||
*
|
||||
* @param available_update_checker $provider the class providing the available update info
|
||||
*/
|
||||
public function check_available_update(available_update_checker $provider) {
|
||||
$this->availableupdate = $provider->get_update_info($this->component);
|
||||
public function check_available_updates(available_update_checker $provider) {
|
||||
$this->availableupdates = $provider->get_update_info($this->component);
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is an update of this plugin available, returns the data about it.
|
||||
* If there are updates for this plugin available, returns them.
|
||||
*
|
||||
* Returns object with various properties about the available update, if such
|
||||
* an update is available. Returns false if there is no update available for
|
||||
* this plugin. Returns null if the update availabitlity is unknown.
|
||||
* Returns array of {@link available_update_info} objects, if some update
|
||||
* is available. Returns null if there is no update available or if the update
|
||||
* availability is unknown.
|
||||
*
|
||||
* @return stdClass|false|null
|
||||
* @return array|null
|
||||
*/
|
||||
public function available_update() {
|
||||
public function available_updates() {
|
||||
|
||||
if (empty($this->availableupdate)) {
|
||||
if (empty($this->availableupdates) or !is_array($this->availableupdates)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->availableupdate->version > $this->versiondisk) {
|
||||
return $this->availableupdate;
|
||||
$updates = array();
|
||||
|
||||
foreach ($this->availableupdates as $availableupdate) {
|
||||
if ($availableupdate->version > $this->versiondisk) {
|
||||
$updates[] = $availableupdate;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
if (empty($updates)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $updates;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -93,15 +93,12 @@ class testable_plugin_manager extends plugin_manager {
|
||||
$CFG->dirroot.'/mod/foo', 'testable_plugininfo_mod'),
|
||||
'bar' => plugininfo_default_factory::make('mod', $CFG->dirroot.'/bar', 'bar',
|
||||
$CFG->dirroot.'/mod/bar', 'testable_plugininfo_mod'),
|
||||
'buz' => plugininfo_default_factory::make('mod', $CFG->dirroot.'/buz', 'buz',
|
||||
$CFG->dirroot.'/mod/buz', 'testable_plugininfo_mod'),
|
||||
)
|
||||
);
|
||||
|
||||
$checker = testable_available_update_checker::instance();
|
||||
$this->pluginsinfo['mod']['foo']->check_available_update($checker);
|
||||
$this->pluginsinfo['mod']['bar']->check_available_update($checker);
|
||||
$this->pluginsinfo['mod']['buz']->check_available_update($checker);
|
||||
$this->pluginsinfo['mod']['foo']->check_available_updates($checker);
|
||||
$this->pluginsinfo['mod']['bar']->check_available_updates($checker);
|
||||
|
||||
return $this->pluginsinfo;
|
||||
}
|
||||
@@ -113,6 +110,9 @@ class testable_plugin_manager extends plugin_manager {
|
||||
*/
|
||||
class testable_available_update_checker extends available_update_checker {
|
||||
|
||||
/** @var replaces the default DB table storage for the fetched response */
|
||||
protected $responsestorage;
|
||||
|
||||
/**
|
||||
* Factory method for this class
|
||||
*
|
||||
@@ -127,45 +127,65 @@ class testable_available_update_checker extends available_update_checker {
|
||||
return self::$singletoninstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not load config in this testable subclass
|
||||
*/
|
||||
protected function load_config() {
|
||||
protected function validate_response() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not fetch anything in this testable subclass
|
||||
*/
|
||||
public function fetch() {
|
||||
protected function store_response($response) {
|
||||
$this->responsestorage = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Here we simulate read access to the fetched remote statuses
|
||||
*/
|
||||
public function get_update_info($component) {
|
||||
if ($component === 'mod_foo') {
|
||||
// no update available
|
||||
return (object)array(
|
||||
'version' => 2012030500,
|
||||
);
|
||||
|
||||
} else if ($component === 'mod_bar') {
|
||||
// there is an update available
|
||||
return (object)array(
|
||||
'version' => 2012030501,
|
||||
);
|
||||
|
||||
} else {
|
||||
// nothing known to us
|
||||
return null;
|
||||
}
|
||||
protected function restore_response($forcereload = false) {
|
||||
$this->recentfetch = time();
|
||||
$this->recentresponse = $this->decode_response($this->get_fake_response());
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the method public so we can test it
|
||||
*/
|
||||
public function merge_components_info(stdClass $old, stdClass $new, $timegenerated=null) {
|
||||
return parent::merge_components_info($old, $new, $timegenerated);
|
||||
private function get_fake_response() {
|
||||
$fakeresponse = array(
|
||||
'status' => 'OK',
|
||||
'provider' => 'http://download.moodle.org/api/1.0/updates.php',
|
||||
'apiver' => '1.0',
|
||||
'timegenerated' => time(),
|
||||
'forversion' => '2012010100.00',
|
||||
'forbranch' => '2.3',
|
||||
'ticket' => sha1('No, I am not going to mention the word "frog" here. Oh crap. I just did.'),
|
||||
'updates' => array(
|
||||
'core' => array(
|
||||
array(
|
||||
'version' => 2012060103.00,
|
||||
'release' => '2.3.3 (Build: 20121201)',
|
||||
'maturity' => 200,
|
||||
'url' => 'http://download.moodle.org/',
|
||||
'download' => 'http://download.moodle.org/download.php/MOODLE_23_STABLE/moodle-2.3.3-latest.zip',
|
||||
),
|
||||
array(
|
||||
'version' => 2012120100.00,
|
||||
'release' => '2.4 (Build: 20121201)',
|
||||
'maturity' => 200,
|
||||
'url' => 'http://download.moodle.org/',
|
||||
'download' => 'http://download.moodle.org/download.php/MOODLE_24_STABLE/moodle-2.4.0-latest.zip',
|
||||
),
|
||||
),
|
||||
'mod_foo' => array(
|
||||
array(
|
||||
'version' => 2012030501,
|
||||
'requires' => 2012010100,
|
||||
'maturity' => 200,
|
||||
'release' => '1.1',
|
||||
'url' => 'http://moodle.org/plugins/blahblahblah/',
|
||||
'download' => 'http://moodle.org/plugins/download.php/blahblahblah',
|
||||
),
|
||||
array(
|
||||
'version' => 2012030502,
|
||||
'requires' => 2012010100,
|
||||
'maturity' => 100,
|
||||
'release' => '1.2 beta',
|
||||
'url' => 'http://moodle.org/plugins/',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return json_encode($fakeresponse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,10 +217,11 @@ class plugin_manager_test extends UnitTestCase {
|
||||
public function test_available_update() {
|
||||
$pluginman = testable_plugin_manager::instance();
|
||||
$plugins = $pluginman->get_plugins();
|
||||
$this->assertFalse($plugins['mod']['foo']->available_update());
|
||||
$this->assertNull($plugins['mod']['buz']->available_update());
|
||||
$this->assertIsA($plugins['mod']['bar']->available_update(), 'stdClass');
|
||||
$this->assertEqual($plugins['mod']['bar']->available_update()->version, 2012030501);
|
||||
$this->assertNull($plugins['mod']['bar']->available_updates());
|
||||
$this->assertIsA($plugins['mod']['foo']->available_updates(), 'array');
|
||||
foreach ($plugins['mod']['foo']->available_updates() as $availableupdate) {
|
||||
$this->assertIsA($availableupdate, 'available_update_info');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,52 +235,4 @@ class available_update_checker_test extends UnitTestCase {
|
||||
$provider = testable_available_update_checker::instance();
|
||||
$this->assertTrue($provider instanceof available_update_checker);
|
||||
}
|
||||
|
||||
public function test_merge_components_info() {
|
||||
$old = (object)array(
|
||||
'2.2' => (object)array(
|
||||
'core' => (object)array(
|
||||
'version' => 2011120501.11,
|
||||
'release' => '2.2.1+ (Build: 20120301)',
|
||||
'maturity' => MATURITY_STABLE,
|
||||
),
|
||||
'mod_foo' => (object)array(
|
||||
'version' => 2011010100,
|
||||
),
|
||||
'mod_bar' => (object)array(
|
||||
'version' => 2011020200,
|
||||
)
|
||||
)
|
||||
);
|
||||
$new = (object)array(
|
||||
'2.2' => (object)array(
|
||||
'core' => (object)array(
|
||||
'version' => 2011120501.12,
|
||||
'release' => '2.2.1+ (Build: 20120302)',
|
||||
'maturity' => MATURITY_STABLE,
|
||||
),
|
||||
'mod_bar' => (object)array(
|
||||
'version' => 2011020201,
|
||||
),
|
||||
),
|
||||
'2.3' => (object)array(
|
||||
'core' => (object)array(
|
||||
'version' => 2012030100.00,
|
||||
'release' => '2.3dev (Build: 20120301)',
|
||||
'maturity' => MATURITY_ALPHA,
|
||||
),
|
||||
'mod_foo' => (object)array(
|
||||
'version' => 2012010200,
|
||||
)
|
||||
)
|
||||
);
|
||||
$checker = testable_available_update_checker::instance();
|
||||
$now = time();
|
||||
$merged = $checker->merge_components_info($old, $new, $now);
|
||||
$this->assertEqual($merged->{2.2}->core->version, 2011120501.12); // from $new
|
||||
$this->assertEqual($merged->{2.2}->mod_bar->version, 2011020201); // from $new
|
||||
$this->assertEqual($merged->{2.2}->mod_foo->version, 2011010100); // from $old
|
||||
$this->assertEqual($merged->{2.3}->core->version, 2012030100.00); // from $new
|
||||
$this->assertFalse(isset($merged->{2.3}->mod_bar));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user