Mwjames has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/131009

Change subject: Nicified some tests
......................................................................

Nicified some tests

- SetupAfterCacheTest all combinations of invalid configurations
- Remove V3ModuleDefinition from BootstrapManagerTest and use a mock object 
instead
- V3ModuleDefinitionTest adds an integration test with BootstrapManager
- Add compliance for coverage reports on PHPunit 4.0.*
- Split ModuleDefinition and V3ModuleDefinition into separate files

Change-Id: I75d280a4c0df3b9842cda3242259a76d9a248b8d
---
M Bootstrap.php
M README.md
M src/BootstrapManager.php
A src/Definition/ModuleDefinition.php
R src/Definition/V3ModuleDefinition.php
M src/Hooks/SetupAfterCache.php
M tests/phpunit/BootstrapManagerTest.php
A tests/phpunit/Definition/V3ModuleDefinitionTest.php
M tests/phpunit/Hooks/SetupAfterCacheTest.php
M tests/phpunit/ResourceLoaderBootstrapModuleTest.php
D tests/phpunit/V3ModuleDefinitionTest.php
11 files changed, 253 insertions(+), 118 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Bootstrap 
refs/changes/09/131009/1

diff --git a/Bootstrap.php b/Bootstrap.php
index 64c4ff0..adac389 100644
--- a/Bootstrap.php
+++ b/Bootstrap.php
@@ -33,11 +33,10 @@
  *
  * @file
  * @ingroup       Bootstrap
+ *
+ * @codeCoverageIgnore
  */
-
-
 call_user_func( function () {
-
 
        if ( !defined( 'MEDIAWIKI' ) ) {
                die( 'This file is part of the MediaWiki extension Bootstrap, 
it is not a valid entry point.' );
@@ -68,10 +67,10 @@
 
        // register classes
        $GLOBALS[ 'wgAutoloadClasses' ][ 
'Bootstrap\ResourceLoaderBootstrapModule' ] = __DIR__ . 
'/src/ResourceLoaderBootstrapModule.php';
-       $GLOBALS[ 'wgAutoloadClasses' ][ 'Bootstrap\BootstrapManager' ] = 
__DIR__ . '/src/BootstrapManager.php';
+       $GLOBALS[ 'wgAutoloadClasses' ][ 'Bootstrap\BootstrapManager' ]      = 
__DIR__ . '/src/BootstrapManager.php';
        $GLOBALS[ 'wgAutoloadClasses' ][ 'Bootstrap\Hooks\SetupAfterCache' ] = 
__DIR__ . '/src/Hooks/SetupAfterCache.php';
-       $GLOBALS[ 'wgAutoloadClasses' ][ 'Bootstrap\ModuleDefinition' ] = 
__DIR__ . '/src/ModuleDefinition.php';
-       $GLOBALS[ 'wgAutoloadClasses' ][ 'Bootstrap\V3ModuleDefinition' ] = 
__DIR__ . '/src/ModuleDefinition.php';
+       $GLOBALS[ 'wgAutoloadClasses' ][ 
'Bootstrap\Definition\ModuleDefinition' ]   = __DIR__ . 
'/src/Definition/ModuleDefinition.php';
+       $GLOBALS[ 'wgAutoloadClasses' ][ 
'Bootstrap\Definition\V3ModuleDefinition' ] = __DIR__ . 
'/src/Definition/V3ModuleDefinition.php';
 
        $GLOBALS[ 'wgHooks' ][ 'SetupAfterCache' ][ ] = function() {
 
@@ -89,8 +88,7 @@
        $GLOBALS[ 'wgResourceModules' ][ 'ext.bootstrap.styles' ] = array(
                'class'          => 'Bootstrap\ResourceLoaderBootstrapModule',
                'styles'         => array(),
-               'variables'      => array(
-               ),
+               'variables'      => array(),
                'dependencies'   => array(),
        );
 
diff --git a/README.md b/README.md
index b7ecb2b..21d720b 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,14 @@
 # Bootstrap extension
 [![Latest Stable 
Version](https://poser.pugx.org/mediawiki/bootstrap/version.png)](https://packagist.org/packages/mediawiki/bootstrap)
 [![Packagist download 
count](https://poser.pugx.org/mediawiki/bootstrap/d/total.png)](https://packagist.org/packages/mediawiki/bootstrap)
+[![Dependency 
Status](https://www.versioneye.com/php/mediawiki:bootstrap/badge.png)](https://www.versioneye.com/php/mediawiki:bootstrap)
 
 The [Bootstrap extension][mw-bootstrap] provides Twitter's Bootstrap web 
front-end framework to skins and extensions.
+
+## Requirements
+
+- PHP 5.3.2 or later
+- MediaWiki 1.22 or later
 
 ## Installation
 
@@ -16,5 +22,10 @@
 }
 ```
 
+## Tests
+
+The extension provides unit tests that covers core-functionality normally run 
by a continues integration platform. Tests can also be executed manually using 
the [PHPUnit][mw-testing] configuration file found in the root directory.
+
 [mw-bootstrap]: https://www.mediawiki.org/wiki/Extension:Bootstrap
+[mw-testing]: https://www.mediawiki.org/wiki/Manual:PHP_unit_testing
 [composer]: https://getcomposer.org/
diff --git a/src/BootstrapManager.php b/src/BootstrapManager.php
index bb129f0..14e7eaa 100644
--- a/src/BootstrapManager.php
+++ b/src/BootstrapManager.php
@@ -2,6 +2,9 @@
 
 namespace Bootstrap;
 
+use Bootstrap\Definition\V3ModuleDefinition;
+use Bootstrap\Definition\ModuleDefinition;
+
 /**
  * File holding the Bootstrap class
  *
@@ -122,9 +125,11 @@
                        $GLOBALS[ 'wgResourceModules' ][ 'ext.bootstrap.' . 
$filetype ][ $filetype ] =
                                array_merge(
                                        $GLOBALS[ 'wgResourceModules' ][ 
'ext.bootstrap.' . $filetype ][ $filetype ],
-                                       array_map( function ( $filename ) use ( 
$fileExt ) { return $filename . $fileExt; }, (array) $description[ $filetype ])
+                                       array_map(
+                                               function ( $filename ) use ( 
$fileExt ) { return $filename . $fileExt; },
+                                               (array) $description[ $filetype 
]
+                                       )
                                );
-
                }
        }
 
@@ -134,7 +139,6 @@
         * @since  1.0
         */
        public function addAllBootstrapModules() {
-
                $this->addBootstrapModule( $this->moduleDefinition->get( 
'optional' ) );
        }
 
@@ -147,9 +151,7 @@
         * @internal param string $path
         */
        public function addExternalModule( $file, $remotePath = '' ) {
-
-               global $wgResourceModules;
-               $wgResourceModules[ 'ext.bootstrap.styles' ][ 'external styles' 
][ $file ] = $remotePath;
+               $GLOBALS[ 'wgResourceModules' ][ 'ext.bootstrap.styles' ][ 
'external styles' ][ $file ] = $remotePath;
        }
 
        /**
@@ -159,7 +161,6 @@
         * @param string $value the value to assign to the variable
         */
        public function setLessVariable( $key, $value ) {
-
                $this->setLessVariables( array( $key => $value ) );
        }
 
@@ -169,11 +170,11 @@
         * @param $variables
         */
        public function setLessVariables( $variables ) {
-
-               global $wgResourceModules;
-
-               $wgResourceModules[ 'ext.bootstrap.styles' ][ 'variables' ] =
-                       array_merge( $wgResourceModules[ 'ext.bootstrap.styles' 
][ 'variables' ], $variables );
+               $GLOBALS[ 'wgResourceModules' ][ 'ext.bootstrap.styles' ][ 
'variables' ] =
+                       array_merge(
+                               $GLOBALS[ 'wgResourceModules' ][ 
'ext.bootstrap.styles' ][ 'variables' ],
+                               $variables
+                       );
        }
 
        protected function initCoreModules() {
diff --git a/src/Definition/ModuleDefinition.php 
b/src/Definition/ModuleDefinition.php
new file mode 100644
index 0000000..a40dd7f
--- /dev/null
+++ b/src/Definition/ModuleDefinition.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Bootstrap\Definition;
+
+/**
+ * @copyright (C) 2013, Stephan Gambke
+ * @license   http://www.gnu.org/licenses/gpl-3.0.html GNU General Public 
License, version 3 (or later)
+ *
+ * This file is part of the MediaWiki extension Bootstrap.
+ * The Bootstrap extension is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Bootstrap extension is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup   Bootstrap
+ */
+
+/**
+ * Interface describing module definitions
+ */
+interface ModuleDefinition {
+
+       /**
+        * Returns a definition array
+        *
+        * @since  1.0
+        *
+        * @param string $key
+        *
+        * @return array
+        */
+       public function get( $key );
+
+}
diff --git a/src/ModuleDefinition.php b/src/Definition/V3ModuleDefinition.php
similarity index 95%
rename from src/ModuleDefinition.php
rename to src/Definition/V3ModuleDefinition.php
index b9e3ea0..8cf9c0f 100644
--- a/src/ModuleDefinition.php
+++ b/src/Definition/V3ModuleDefinition.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Bootstrap;
+namespace Bootstrap\Definition;
 
 use InvalidArgumentException;
 
@@ -25,24 +25,6 @@
  * @file
  * @ingroup   Bootstrap
  */
-
-/**
- * Interface describing module definitions
- */
-interface ModuleDefinition {
-
-       /**
-        * Returns a definition array
-        *
-        * @since  1.0
-        *
-        * @param string $key
-        *
-        * @return array
-        */
-       public function get( $key );
-
-}
 
 /**
  * Class describing the V3 Bootstrap module definitions
diff --git a/src/Hooks/SetupAfterCache.php b/src/Hooks/SetupAfterCache.php
index 5cdaf3b..7b3e9e0 100644
--- a/src/Hooks/SetupAfterCache.php
+++ b/src/Hooks/SetupAfterCache.php
@@ -60,7 +60,7 @@
         */
        public function process() {
 
-               if ( !$this->hasConfiguration( 'localBasePath' ) || 
!$this->hasConfiguration( 'localBasePath' ) ) {
+               if ( !$this->hasConfiguration( 'localBasePath' ) || 
!$this->hasConfiguration( 'remoteBasePath' ) ) {
                        throw new InvalidArgumentException( 'Expected a valid 
configuration' );
                }
 
diff --git a/tests/phpunit/BootstrapManagerTest.php 
b/tests/phpunit/BootstrapManagerTest.php
index f43dd8d..810b522 100644
--- a/tests/phpunit/BootstrapManagerTest.php
+++ b/tests/phpunit/BootstrapManagerTest.php
@@ -3,10 +3,9 @@
 namespace Bootstrap\Tests;
 
 use Bootstrap\BootstrapManager;
-use Bootstrap\V3ModuleDefinition;
 
 /**
- * @covers \Bootstrap\BootstrapManager
+ * @uses \Bootstrap\BootstrapManager
  *
  * @ingroup Test
  *
@@ -40,19 +39,20 @@
 
                $GLOBALS['wgResourceModules'][ 'ext.bootstrap.scripts' ] = 
array(
                        'dependencies'    => array(),
-                       'scripts'          => array()
+                       'scripts'         => array()
                );
        }
 
        protected function tearDown() {
-               parent::tearDown();
                $GLOBALS['wgResourceModules'] = $this->wgResourceModules;
                BootstrapManager::clear();
+
+               parent::tearDown();
        }
 
        public function testCanConstruct() {
 
-               $moduleDefinition = $this->getMockBuilder( 
'\Bootstrap\ModuleDefinition' )
+               $moduleDefinition = $this->getMockBuilder( 
'\Bootstrap\Definition\ModuleDefinition' )
                        ->disableOriginalConstructor()
                        ->setMethods( array( 'get' ) )
                        ->getMock();
@@ -66,6 +66,8 @@
                        new BootstrapManager( $moduleDefinition )
                );
 
+               BootstrapManager::clear();
+
                $this->assertInstanceOf(
                        '\Bootstrap\BootstrapManager',
                        BootstrapManager::getInstance()
@@ -76,13 +78,35 @@
 
                $this->assertEmpty( 
$this->getGlobalResourceModuleBootstrapStyles() );
 
-               $instance = new BootstrapManager( new V3ModuleDefinition );
+               $moduleDefinition = $this->getMockBuilder( 
'\Bootstrap\Definition\ModuleDefinition' )
+                       ->disableOriginalConstructor()
+                       ->setMethods( array( 'get' ) )
+                       ->getMock();
+
+               $moduleDefinition->expects( $this->at( 0 ) )
+                       ->method( 'get' )
+                       ->with( $this->stringContains( 'descriptions' ) )
+                       ->will( $this->returnValue( array( 'variables' => 
array( 'styles' => 'variables' ) ) ) );
+
+               $moduleDefinition->expects( $this->at( 1 ) )
+                       ->method( 'get' )
+                       ->with( $this->stringContains( 'core' ) )
+                       ->will( $this->returnValue( array( 'variables' ) ) );
+
+               $moduleDefinition->expects( $this->at( 2 ) )
+                       ->method( 'get' )
+                       ->with( $this->stringContains( 'optional' ) )
+                       ->will( $this->returnValue( array( 'foo' ) ) );
+
+               $instance = new BootstrapManager( $moduleDefinition );
+               $instance->addAllBootstrapModules();
+
                $this->assertNotEmpty( 
$this->getGlobalResourceModuleBootstrapStyles() );
        }
 
        public function testSetLessVariables() {
 
-               $moduleDefinition = $this->getMockBuilder( 
'\Bootstrap\ModuleDefinition' )
+               $moduleDefinition = $this->getMockBuilder( 
'\Bootstrap\Definition\ModuleDefinition' )
                        ->disableOriginalConstructor()
                        ->setMethods( array( 'get' ) )
                        ->getMock();
@@ -95,8 +119,35 @@
                $instance->setLessVariables( array( 'foo' => 'bar') );
                $instance->setLessVariable( 'ichi', 'ni' );
 
-               $this->assertArrayHasKey( 'foo', 
$this->getGlobalResourceModuleBootstrapVariables() );
-               $this->assertArrayHasKey( 'ichi', 
$this->getGlobalResourceModuleBootstrapVariables() );
+               $this->assertArrayHasKey(
+                       'foo',
+                       $this->getGlobalResourceModuleBootstrapVariables()
+               );
+
+               $this->assertArrayHasKey(
+                       'ichi',
+                       $this->getGlobalResourceModuleBootstrapVariables()
+               );
+       }
+
+       public function testAddExternalModule() {
+
+               $moduleDefinition = $this->getMockBuilder( 
'\Bootstrap\Definition\ModuleDefinition' )
+                       ->disableOriginalConstructor()
+                       ->setMethods( array( 'get' ) )
+                       ->getMock();
+
+               $moduleDefinition->expects( $this->atLeastOnce() )
+                       ->method( 'get' )
+                       ->will( $this->returnValue( array() ) );
+
+               $instance = new BootstrapManager( $moduleDefinition );
+               $instance->addExternalModule( 'ExternalFooModule', 
'ExternalRemoteBarPath' );
+
+               $this->assertArrayHasKey(
+                       'ExternalFooModule',
+                       $this->getGlobalResourceModuleBootstrapExternalStyles()
+               );
        }
 
        private function getGlobalResourceModuleBootstrapStyles() {
@@ -107,4 +158,8 @@
                return $GLOBALS['wgResourceModules'][ 'ext.bootstrap.styles' 
]['variables'];
        }
 
+       private function getGlobalResourceModuleBootstrapExternalStyles() {
+               return $GLOBALS['wgResourceModules'][ 'ext.bootstrap.styles' 
]['external styles'];
+       }
+
 }
diff --git a/tests/phpunit/Definition/V3ModuleDefinitionTest.php 
b/tests/phpunit/Definition/V3ModuleDefinitionTest.php
new file mode 100644
index 0000000..cb52a6a
--- /dev/null
+++ b/tests/phpunit/Definition/V3ModuleDefinitionTest.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace Bootstrap\Tests\Definition;
+
+use Bootstrap\Definition\V3ModuleDefinition;
+use Bootstrap\BootstrapManager;
+
+/**
+ * @uses \Bootstrap\Definition\V3ModuleDefinition
+ *
+ * @ingroup Test
+ *
+ * @group extension-bootstrap
+ * @group mediawiki-databaseless
+ *
+ * @license GNU GPL v3+
+ * @since 1.0
+ *
+ * @author mwjames
+ */
+class V3ModuleDefinitionTest extends \PHPUnit_Framework_TestCase {
+
+       public function testCanConstruct() {
+
+               $this->assertInstanceOf(
+                       '\Bootstrap\Definition\ModuleDefinition',
+                       new V3ModuleDefinition()
+               );
+       }
+
+       /**
+        * @dataProvider keyProvider
+        */
+       public function testGet( $key ) {
+
+               $instance = new V3ModuleDefinition();
+
+               $this->assertInternalType(
+                       'array',
+                       $instance->get( $key )
+               );
+       }
+
+       public function testBootstrapManagerIntegration() {
+
+               $instance = new BootstrapManager( new V3ModuleDefinition() );
+               $instance->addAllBootstrapModules();
+
+               $this->assertTrue( true );
+       }
+
+       public function testGetOnInvalidKeyThrowsException() {
+
+               $instance = new V3ModuleDefinition();
+
+               $this->setExpectedException( 'InvalidArgumentException' );
+               $instance->get( 'Foo' );
+       }
+
+       public function keyProvider() {
+
+               $provider = array(
+                       array( 'core' ),
+                       array( 'optional' ),
+                       array( 'descriptions' )
+               );
+
+               return $provider;
+       }
+
+}
diff --git a/tests/phpunit/Hooks/SetupAfterCacheTest.php 
b/tests/phpunit/Hooks/SetupAfterCacheTest.php
index a42f54f..bb1d4d8 100644
--- a/tests/phpunit/Hooks/SetupAfterCacheTest.php
+++ b/tests/phpunit/Hooks/SetupAfterCacheTest.php
@@ -5,7 +5,7 @@
 use Bootstrap\Hooks\SetupAfterCache;
 
 /**
- * @covers \Bootstrap\Hooks\SetupAfterCache
+ * @uses \Bootstrap\Hooks\SetupAfterCache
  *
  * @ingroup Test
  *
@@ -23,7 +23,7 @@
 
        protected function setUp() {
                parent::setUp();
-               $this->localBasePath = $GLOBALS[ 'IP' ] . 
'/vendor/twitter/bootstrap';
+               $this->localBootstrapVendorPath = $GLOBALS[ 'IP' ] . 
'/vendor/twitter/bootstrap';
        }
 
        public function testCanConstruct() {
@@ -39,7 +39,7 @@
        public function testProcessWithAccessibilityOnBootstrapVendorPath() {
 
                $configuration = array(
-                       'localBasePath'  => $this->localBasePath,
+                       'localBasePath'  => $this->localBootstrapVendorPath,
                        'remoteBasePath' => ''
                );
 
@@ -51,20 +51,26 @@
        public function testProcessWithAccessibilityOnAddedLocalResourcePaths() 
{
 
                $configuration = array(
-                       'localBasePath'  => $this->localBasePath,
+                       'localBasePath'  => $this->localBootstrapVendorPath,
                        'remoteBasePath' => ''
                );
 
                $instance = new SetupAfterCache( $configuration );
                $instance->process();
 
-               $this->assertTrue( is_readable( $GLOBALS[ 'wgResourceModules' 
][ 'ext.bootstrap.styles' ]['localBasePath'] ) );
-               $this->assertTrue( is_readable( $GLOBALS[ 'wgResourceModules' 
][ 'ext.bootstrap.scripts' ]['localBasePath'] ) );
+               $this->assertIsReadeablePath(
+                       $GLOBALS[ 'wgResourceModules' ][ 'ext.bootstrap.styles' 
]['localBasePath']
+               );
+
+               $this->assertIsReadeablePath(
+                       $GLOBALS[ 'wgResourceModules' ][ 
'ext.bootstrap.scripts' ]['localBasePath']
+               );
        }
 
-       public function testProcessOnInvalidConfigurationThrowsException() {
-
-               $configuration = array();
+       /**
+        * @dataProvider invalidConfigurationProvider
+        */
+       public function testProcessOnInvalidConfigurationThrowsException( 
$configuration ) {
 
                $instance = new SetupAfterCache( $configuration );
 
@@ -85,4 +91,31 @@
                $instance->process();
        }
 
+       public function invalidConfigurationProvider() {
+
+               $provider = array();
+
+               $provider[] = array(
+                       array()
+               );
+
+               $provider[] = array(
+                       array(
+                               'localBasePath' => 'Foo'
+                       )
+               );
+
+               $provider[] = array(
+                       array(
+                               'remoteBasePath' => 'Foo'
+                       )
+               );
+
+               return $provider;
+       }
+
+       protected function assertIsReadeablePath( $path ) {
+               $this->assertTrue( is_readable( $path ) );
+       }
+
 }
diff --git a/tests/phpunit/ResourceLoaderBootstrapModuleTest.php 
b/tests/phpunit/ResourceLoaderBootstrapModuleTest.php
index 28a4045..42d7d35 100644
--- a/tests/phpunit/ResourceLoaderBootstrapModuleTest.php
+++ b/tests/phpunit/ResourceLoaderBootstrapModuleTest.php
@@ -7,7 +7,7 @@
 use HashBagOStuff;
 
 /**
- * @covers \Bootstrap\ResourceLoaderBootstrapModule
+ * @uses \Bootstrap\ResourceLoaderBootstrapModule
  *
  * @ingroup Test
  *
diff --git a/tests/phpunit/V3ModuleDefinitionTest.php 
b/tests/phpunit/V3ModuleDefinitionTest.php
deleted file mode 100644
index 3f27ef8..0000000
--- a/tests/phpunit/V3ModuleDefinitionTest.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-
-namespace Bootstrap\Tests;
-
-use Bootstrap\V3ModuleDefinition;
-
-/**
- * @covers \Bootstrap\V3ModuleDefinition
- *
- * @ingroup Test
- *
- * @group extension-bootstrap
- * @group mediawiki-databaseless
- *
- * @license GNU GPL v3+
- * @since 1.0
- *
- * @author mwjames
- */
-class V3ModuleDefinitionTest extends \PHPUnit_Framework_TestCase {
-
-       public function testCanConstruct() {
-
-               $this->assertInstanceOf(
-                       '\Bootstrap\ModuleDefinition',
-                       new V3ModuleDefinition()
-               );
-       }
-
-       /**
-        * @dataProvider keyProvider
-        */
-       public function testGet( $key ) {
-
-               $instance = new V3ModuleDefinition;
-
-               $this->assertInternalType( 'array', $instance->get( $key ) );
-       }
-
-       public function testGetOnInvalidKeyThrowsException() {
-
-               $instance = new V3ModuleDefinition;
-
-               $this->setExpectedException( 'InvalidArgumentException' );
-               $instance->get( 'Foo' );
-       }
-
-       public function keyProvider() {
-
-               $provider = array(
-                       array( 'core' ),
-                       array( 'optional' ),
-                       array( 'descriptions' )
-               );
-
-               return $provider;
-       }
-
-}

-- 
To view, visit https://gerrit.wikimedia.org/r/131009
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I75d280a4c0df3b9842cda3242259a76d9a248b8d
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Bootstrap
Gerrit-Branch: master
Gerrit-Owner: Mwjames <jamesin.hongkon...@gmail.com>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to