From c21cc1f4f02d48db1f5f90a41cf51c4d17d74928 Mon Sep 17 00:00:00 2001 From: thrijith Date: Mon, 27 Jan 2020 18:20:46 +0530 Subject: [PATCH 01/28] Setup PHPUnit for the project --- README.md | 9 +++ bin/install-wp-tests.sh | 155 ++++++++++++++++++++++++++++++++++++++++ phpunit.xml | 25 +++++++ tests/bootstrap.php | 31 ++++++++ 4 files changed, 220 insertions(+) create mode 100755 bin/install-wp-tests.sh create mode 100644 phpunit.xml create mode 100644 tests/bootstrap.php diff --git a/README.md b/README.md index 1e6946c..f5fe938 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,15 @@ AMP_AdManager\AMP_AdManager::get_ads( $attr, true ); * Create PR against `master`. * Request review for your changes and get approvals. +## Unit testing + +- Setup local unit test environment by running the below script from terminal with appropriate values. + - `./bin/install-wp-tests.sh [db-host] [wp-version] [skip-database-creation]` + + - E.g `./bin/install-wp-tests.sh amp_admanager_test root root localhost` +- Run `phpunit` in terminal from cloned project directory to run all test cases. +- Run `phpunit ./tests/classes/test-class.php` with file path to run specific tests. + ## Change Log ### v1.0.0 (27-09-2019) diff --git a/bin/install-wp-tests.sh b/bin/install-wp-tests.sh new file mode 100755 index 0000000..5ceac4b --- /dev/null +++ b/bin/install-wp-tests.sh @@ -0,0 +1,155 @@ +#!/usr/bin/env bash + +if [ $# -lt 3 ]; then + echo "usage: $0 [db-host] [wp-version] [skip-database-creation]" + exit 1 +fi + +DB_NAME=$1 +DB_USER=$2 +DB_PASS=$3 +DB_HOST=${4-localhost} +WP_VERSION=${5-latest} +SKIP_DB_CREATE=${6-false} + +TMPDIR=${TMPDIR-/tmp} +TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") +WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} +WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/} + +download() { + if [ `which curl` ]; then + curl -s "$1" > "$2"; + elif [ `which wget` ]; then + wget -nv -O "$2" "$1" + fi +} + +if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+\-(beta|RC)[0-9]+$ ]]; then + WP_BRANCH=${WP_VERSION%\-*} + WP_TESTS_TAG="branches/$WP_BRANCH" + +elif [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then + WP_TESTS_TAG="branches/$WP_VERSION" +elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then + if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then + # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x + WP_TESTS_TAG="tags/${WP_VERSION%??}" + else + WP_TESTS_TAG="tags/$WP_VERSION" + fi +elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + WP_TESTS_TAG="trunk" +else + # http serves a single offer, whereas https serves multiple. we only want one + download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json + grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json + LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') + if [[ -z "$LATEST_VERSION" ]]; then + echo "Latest WordPress version could not be found" + exit 1 + fi + WP_TESTS_TAG="tags/$LATEST_VERSION" +fi +set -ex + +install_wp() { + + if [ -d $WP_CORE_DIR ]; then + return; + fi + + mkdir -p $WP_CORE_DIR + + if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then + mkdir -p $TMPDIR/wordpress-nightly + download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip + unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/ + mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR + else + if [ $WP_VERSION == 'latest' ]; then + local ARCHIVE_NAME='latest' + elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then + # https serves multiple offers, whereas http serves single. + download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json + if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then + # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x + LATEST_VERSION=${WP_VERSION%??} + else + # otherwise, scan the releases and get the most up to date minor version of the major release + local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'` + LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1) + fi + if [[ -z "$LATEST_VERSION" ]]; then + local ARCHIVE_NAME="wordpress-$WP_VERSION" + else + local ARCHIVE_NAME="wordpress-$LATEST_VERSION" + fi + else + local ARCHIVE_NAME="wordpress-$WP_VERSION" + fi + download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz + tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR + fi + + download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php +} + +install_test_suite() { + # portable in-place argument for both GNU sed and Mac OSX sed + if [[ $(uname -s) == 'Darwin' ]]; then + local ioption='-i.bak' + else + local ioption='-i' + fi + + # set up testing suite if it doesn't yet exist + if [ ! -d $WP_TESTS_DIR ]; then + # set up testing suite + mkdir -p $WP_TESTS_DIR + svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes + svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data + fi + + if [ ! -f wp-tests-config.php ]; then + download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php + # remove all forward slashes in the end + WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") + sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php + sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php + fi + +} + +install_db() { + + if [ ${SKIP_DB_CREATE} = "true" ]; then + return 0 + fi + + # parse DB_HOST for port or socket references + local PARTS=(${DB_HOST//\:/ }) + local DB_HOSTNAME=${PARTS[0]}; + local DB_SOCK_OR_PORT=${PARTS[1]}; + local EXTRA="" + + if ! [ -z $DB_HOSTNAME ] ; then + if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then + EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" + elif ! [ -z $DB_SOCK_OR_PORT ] ; then + EXTRA=" --socket=$DB_SOCK_OR_PORT" + elif ! [ -z $DB_HOSTNAME ] ; then + EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" + fi + fi + + # create database + mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA +} + +install_wp +install_test_suite +install_db diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..0bf7dc4 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,25 @@ + + + + ./tests/ + + + + + ./classes + + ./tests + + + + + + + diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..8b90d61 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,31 @@ + Date: Mon, 27 Jan 2020 18:24:07 +0530 Subject: [PATCH 02/28] Add GitHub Action to run PHPUnit tests Rename workflow file for PHPCS, for better clarity --- .../{pull_request.yml => phpcs_on_pull_request.yml} | 0 .github/workflows/phpunit_on_pull_request.yml | 12 ++++++++++++ 2 files changed, 12 insertions(+) rename .github/workflows/{pull_request.yml => phpcs_on_pull_request.yml} (100%) create mode 100644 .github/workflows/phpunit_on_pull_request.yml diff --git a/.github/workflows/pull_request.yml b/.github/workflows/phpcs_on_pull_request.yml similarity index 100% rename from .github/workflows/pull_request.yml rename to .github/workflows/phpcs_on_pull_request.yml diff --git a/.github/workflows/phpunit_on_pull_request.yml b/.github/workflows/phpunit_on_pull_request.yml new file mode 100644 index 0000000..1e0af67 --- /dev/null +++ b/.github/workflows/phpunit_on_pull_request.yml @@ -0,0 +1,12 @@ +on: pull_request +name: PHPUnit +jobs: + runPHPCSInspection: + name: Run PHPUnit test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Run PHPUnit test + uses: docker://rtcamp/action-run-phpunit:v1.0.0 From 175c76880f30514ca514a6959530d82e683da7bd Mon Sep 17 00:00:00 2001 From: Kiran Potphode Date: Wed, 29 Jan 2020 18:41:37 +0530 Subject: [PATCH 03/28] Add phpcs.xml for PHPCS ruleset --- phpcs.xml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 phpcs.xml diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..27b45a3 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,25 @@ + + + WordPress Coding Standards + + + + + + + + + + + + + + + + + tests/ + .github/ + */dev-lib/* + */node_modules/* + */vendor/* + From 5fc7347cc3a802eef1bd040c7af4425e19cf1a09 Mon Sep 17 00:00:00 2001 From: thrijith Date: Wed, 29 Jan 2020 20:47:54 +0530 Subject: [PATCH 04/28] Add test case for classes/class-admin.php --- tests/bootstrap.php | 1 + tests/classes/test-class-admin.php | 148 +++++++++++++++++++++++++++++ tests/helpers/class-utility.php | 130 +++++++++++++++++++++++++ 3 files changed, 279 insertions(+) create mode 100644 tests/classes/test-class-admin.php create mode 100644 tests/helpers/class-utility.php diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 8b90d61..2605748 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -24,6 +24,7 @@ */ function _manually_load_plugin() { require_once dirname( dirname( __FILE__ ) ) . '/amp-admanager.php'; + require_once dirname( __FILE__ ) . '/helpers/class-utility.php'; } tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' ); diff --git a/tests/classes/test-class-admin.php b/tests/classes/test-class-admin.php new file mode 100644 index 0000000..a669a6b --- /dev/null +++ b/tests/classes/test-class-admin.php @@ -0,0 +1,148 @@ +_instance = new Admin(); + } + + /** + * @covers \AMP_AdManager\Admin::__construct + * @throws \ReflectionException + */ + public function test_construct() { + Utility::invoke_method( $this->_instance, '__construct' ); + $this->assertEquals( 10, has_action( 'admin_menu', [ $this->_instance, 'amp_admanager_menu' ] ) ); + $this->assertEquals( 10, has_action( 'admin_init', [ $this->_instance, 'amp_admanager_menu_init' ] ) ); + } + + /** + * @covers \AMP_AdManager\Admin::amp_admanager_menu + */ + public function test_amp_admanager_menu() { + $current_user = get_current_user_id(); + wp_set_current_user( $this->factory()->user->create( array( 'role' => 'administrator' ) ) ); + $this->_instance->amp_admanager_menu(); + $admin_page_url = home_url() . '/wp-admin/admin.php?page=amp-admanager-menu'; + $this->assertEquals( + $admin_page_url, + menu_page_url( 'amp-admanager-menu', false ), 'AMP AdManager Settings Page was not created' + ); + wp_set_current_user( $current_user ); + } + + /** + * @covers \AMP_AdManager\Admin::amp_admanager_menu_init + */ + public function test_amp_admanager_menu_init() { + global $new_whitelist_options, $wp_settings_sections, $wp_settings_fields; + + $this->_instance->amp_admanager_menu_init(); + + $this->assertTrue( + array_key_exists( 'amp-admanager-menu', $new_whitelist_options ), + 'Option Group amp-admanager-menu has not been created' ); + + $settings = $new_whitelist_options['amp-admanager-menu']; + + $this->assertCount( 1, $settings, 'The Settings Group amp-admanager-menu 1 setting' ); + + $this->assertContains( 'amp-admanager-menu-settings', + $settings, + sprintf( 'Setting "%1$s" has not been created', 'amp-admanager-menu-settings' ) + ); + + $this->assertarrayHasKey( + 'amp-admanager-menu-page', + $wp_settings_sections, + 'amp-admanager-menu-page setting section has not been created' + ); + + $this->assertarrayHasKey( + 'amp-admanager-menu-page', + $wp_settings_fields, + 'amp-admanager-menu-page settings field has not been created' + ); + + $this->assertarrayHasKey( + 'amp-admanager-general-settings', + $wp_settings_fields['amp-admanager-menu-page'], + 'amp-admanager-menu-page types section settings fields has not been created' + ); + + $this->assertCount( + 2, + $wp_settings_fields['amp-admanager-menu-page']['amp-admanager-general-settings'], + 'There are less than 3 sections in the amp-admanager-menu-page settings field' + ); + + $settings_fields = array( + 'dfp-network-id', + 'load-amp-resources', + ); + + foreach ( $settings_fields as $setting_field ) { + $this->assertarrayHasKey( + $setting_field, + $wp_settings_fields['amp-admanager-menu-page']['amp-admanager-general-settings'] + ); + } + } + + /** + * @covers \AMP_AdManager\Admin::amp_admanager_menu_html + */ + public function test_amp_admanager_menu_html() { + // Test access of user with privileges. + wp_set_current_user( $this->factory()->user->create( array( 'role' => 'administrator' ) ) ); + ob_start(); + $this->_instance->amp_admanager_menu_html(); + $amp_ad_manager_settings_page = ob_get_clean(); + $this->assertContains( 'Global Settings', $amp_ad_manager_settings_page ); + + // Test access of user without privileges. + wp_set_current_user( $this->factory()->user->create( array( 'role' => 'author' ) ) ); + ob_start(); + $this->_instance->amp_admanager_menu_html(); + $amp_ad_manager_settings_page = ob_get_clean(); + $this->assertContains( 'You do not have sufficient permissions to access this page.', $amp_ad_manager_settings_page ); + } + + /** + * @covers \AMP_AdManager\Admin::get_checkbox_field + */ + public function test_get_checkbox_field() { + ob_start(); + $this->_instance->get_checkbox_field(); + $amp_ad_manager_checkbox_field = ob_get_clean(); + $this->assertContains( 'id="load-amp-resources"', $amp_ad_manager_checkbox_field ); + } + + /** + * @covers \AMP_AdManager\Admin::get_text_field + */ + public function test_get_text_field() { + ob_start(); + $this->_instance->get_text_field(); + $amp_ad_manager_checkbox_field = ob_get_clean(); + $this->assertContains( 'id="dfp-network-id"', $amp_ad_manager_checkbox_field ); + } +} diff --git a/tests/helpers/class-utility.php b/tests/helpers/class-utility.php new file mode 100644 index 0000000..96b8880 --- /dev/null +++ b/tests/helpers/class-utility.php @@ -0,0 +1,130 @@ +getMethod( $method_name ); + $method->setAccessible( true ); + + return $method->invokeArgs( $object, $parameters ); + + } + + /** + * Utility method to get private/protected property of a class/object. + * This is a generic wrapper function to align with relfection class and not to be use directly. + * + * @param mixed $object_or_class_name The object/class whose property is to be accessed. + * @param string $property_name The name of the property to access. + * + * @return mixed Value of the hidden property being accessed. + * @throws \ReflectionException + */ + public static function get_property( $object_or_class_name, $property_name ) { + + $object = null; + + if ( is_object( $object_or_class_name ) ) { + $object = $object_or_class_name; + $class_name = get_class( $object ); + } else { + $class_name = $object_or_class_name; + } + + $o_reflection = new \ReflectionClass( $class_name ); + $property = $o_reflection->getProperty( $property_name ); + $property->setAccessible( true ); + + return $property->getValue( $object ); + + } + + /** + * Utility method to set private/protected property of an object/class. + * This is a generic wrapper function to align with relfection class and not to be use directly. + * + * @param mixed $object_or_class_name The object/class whose property is to be accessed. + * @param string $property_name The name of the property to access. + * @param mixed $property_value The value to be set for the hidden property. + * + * @return mixed Value of the hidden property being accessed. + * @throws \ReflectionException + */ + public static function set_and_get_property( $object_or_class_name, string $property_name, $property_value ) { + + $object = null; + + if ( is_object( $object_or_class_name ) ) { + $object = $object_or_class_name; + $class_name = get_class( $object ); + } else { + $class_name = $object_or_class_name; + } + + $o_reflection = new \ReflectionClass( $class_name ); + $property = $o_reflection->getProperty( $property_name ); + $property->setAccessible( true ); + $property->setValue( $object, $property_value ); + + return $property->getValue( $object ); + + } + + /** + * Utility method to capture output from a function + * + * @param Callable $callback The callback from which output is to be captured + * @param array $parameters Parameters to be passed to the $callback + * + * @return mixed Output from callback + * + * @throws \ErrorException + */ + public static function buffer_and_return( $callback, array $parameters = array() ) { + + if ( ! is_callable( $callback ) ) { + throw new \ErrorException( sprintf( '%s::%s() expects first parameter to be a valid callback', __CLASS__, __FUNCTION__ ) ); + } + + ob_start(); + + call_user_func_array( $callback, $parameters ); + + return trim( ob_get_clean() ); + + } + +} From ced9320a1a2badfbc4cb3c7b470c95a98ea55873 Mon Sep 17 00:00:00 2001 From: Kiran Potphode Date: Thu, 30 Jan 2020 11:18:02 +0530 Subject: [PATCH 05/28] Update PHP compatibility --- phpcs.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpcs.xml b/phpcs.xml index 27b45a3..0b213df 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -9,7 +9,7 @@ - + From 53700abe21b6a79ef8d31c2756f5bc60326c4965 Mon Sep 17 00:00:00 2001 From: thrijith Date: Thu, 30 Jan 2020 12:47:36 +0530 Subject: [PATCH 06/28] Add test case for classes/class-shortcode.php Update admin class test to use utility methods --- tests/classes/test-class-admin.php | 20 ++----- tests/classes/test-class-shortcode.php | 80 ++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 tests/classes/test-class-shortcode.php diff --git a/tests/classes/test-class-admin.php b/tests/classes/test-class-admin.php index a669a6b..ae30acd 100644 --- a/tests/classes/test-class-admin.php +++ b/tests/classes/test-class-admin.php @@ -113,16 +113,12 @@ public function test_amp_admanager_menu_init() { public function test_amp_admanager_menu_html() { // Test access of user with privileges. wp_set_current_user( $this->factory()->user->create( array( 'role' => 'administrator' ) ) ); - ob_start(); - $this->_instance->amp_admanager_menu_html(); - $amp_ad_manager_settings_page = ob_get_clean(); + $amp_ad_manager_settings_page = Utility::buffer_and_return( [ $this->_instance, 'amp_admanager_menu_html' ] ); $this->assertContains( 'Global Settings', $amp_ad_manager_settings_page ); // Test access of user without privileges. wp_set_current_user( $this->factory()->user->create( array( 'role' => 'author' ) ) ); - ob_start(); - $this->_instance->amp_admanager_menu_html(); - $amp_ad_manager_settings_page = ob_get_clean(); + $amp_ad_manager_settings_page = Utility::buffer_and_return( [ $this->_instance, 'amp_admanager_menu_html' ] ); $this->assertContains( 'You do not have sufficient permissions to access this page.', $amp_ad_manager_settings_page ); } @@ -130,19 +126,15 @@ public function test_amp_admanager_menu_html() { * @covers \AMP_AdManager\Admin::get_checkbox_field */ public function test_get_checkbox_field() { - ob_start(); - $this->_instance->get_checkbox_field(); - $amp_ad_manager_checkbox_field = ob_get_clean(); - $this->assertContains( 'id="load-amp-resources"', $amp_ad_manager_checkbox_field ); + $amp_ad_manager_checkbox_field = Utility::buffer_and_return( [ $this->_instance, 'get_checkbox_field' ] ); + $this->assertContains( '_instance->get_text_field(); - $amp_ad_manager_checkbox_field = ob_get_clean(); - $this->assertContains( 'id="dfp-network-id"', $amp_ad_manager_checkbox_field ); + $amp_ad_manager_textbox_field = Utility::buffer_and_return( [ $this->_instance, 'get_text_field' ] ); + $this->assertContains( '_instance = new Shortcode(); + } + + /** + * @covers \AMP_AdManager\Shortcode::__construct + * @throws \ReflectionException + */ + public function test_construct() { + Utility::invoke_method( $this->_instance, '__construct' ); + // Check existence of shortcode. + $this->assertTrue( shortcode_exists( 'ampad' ) ); + } + + /** + * @covers \AMP_AdManager\Shortcode::render_amp_ad + */ + public function test_render_amp_ad() { + // Test with sizes and custom targeting for site domain. + $post_content = '

post content start [ampad ad-unit="my-ad-unit" sizes="320x50" custom-targeting="siteDomain:mysite.com" ad-refresh="30"] post content ends

'; + $response = do_shortcode( $post_content ); + $this->assertContains( 'assertContains( 'mysite.com', $response ); + + // Test with desktop sizes. + $post_content = '[ampad ad-unit="my-ad-unit" desktop-sizes="320x100,300x100"]Ad content[/ampad]'; + $response = do_shortcode( $post_content ); + $this->assertContains( 'data-multi-size="320x100,300x100"', $response ); + + // Test with tablet sizes. + $post_content = '[ampad ad-unit="my-ad-unit" tablet-sizes="468x60,300x100"]Ad content[/ampad]'; + $response = do_shortcode( $post_content ); + $this->assertContains( 'data-multi-size="468x60,300x100"', $response ); + + // Test with mobile sizes. + $post_content = '[ampad ad-unit="my-ad-unit" mobile-sizes="300x100,320x50"]Ad content[/ampad]'; + $response = do_shortcode( $post_content ); + $this->assertContains( 'data-multi-size="300x100,320x50"', $response ); + + // Test without custom targeting for domain. + $post_content = '[ampad ad-unit="my-ad-unit" sizes="300x50" ad-refresh="30"]Ad content[/ampad]'; + $response = do_shortcode( $post_content ); + $this->assertContains( 'assertNotContains( 'mysite.com', $response ); + + // Test with content. + $post_content = '[ampad ad-unit="my-ad-unit" sizes="300x50"]Ad content[/ampad]'; + $response = do_shortcode( $post_content ); + $this->assertContains( 'assertContains( 'Ad content', $response ); + + // Test with empty sizes and content. + $post_content = '[ampad ad-unit="my-ad-unit" sizes=""]Ad content[/ampad]'; + $response = do_shortcode( $post_content ); + $this->assertNotContains( 'assertEquals( 'Ad content', $response ); + } +} From d1c24a6ab9e5ebd6baeab8a31c2e90c650563bb7 Mon Sep 17 00:00:00 2001 From: thrijith Date: Thu, 30 Jan 2020 12:52:46 +0530 Subject: [PATCH 07/28] Ignore all files in tests directory --- phpcs.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpcs.xml b/phpcs.xml index 0b213df..bdb9060 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -17,7 +17,7 @@
- tests/ + tests/* .github/ */dev-lib/* */node_modules/* From 91f2f7216b7cfac7a6718efb5ae65688310723b2 Mon Sep 17 00:00:00 2001 From: thrijith Date: Thu, 30 Jan 2020 18:45:34 +0530 Subject: [PATCH 08/28] Set demo data for ad-manager settings --- tests/classes/test-class-admin.php | 5 +++++ tests/classes/test-class-shortcode.php | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/tests/classes/test-class-admin.php b/tests/classes/test-class-admin.php index ae30acd..b368310 100644 --- a/tests/classes/test-class-admin.php +++ b/tests/classes/test-class-admin.php @@ -29,6 +29,11 @@ public function setUp(): void { * @throws \ReflectionException */ public function test_construct() { + // Set demo data for settings. + update_option( 'amp-admanager-menu-settings', array( + 'dfp-network-id' => '123456789', + 'load-amp-resources' => '1', + ) ); Utility::invoke_method( $this->_instance, '__construct' ); $this->assertEquals( 10, has_action( 'admin_menu', [ $this->_instance, 'amp_admanager_menu' ] ) ); $this->assertEquals( 10, has_action( 'admin_init', [ $this->_instance, 'amp_admanager_menu_init' ] ) ); diff --git a/tests/classes/test-class-shortcode.php b/tests/classes/test-class-shortcode.php index 204f372..aad5f0f 100644 --- a/tests/classes/test-class-shortcode.php +++ b/tests/classes/test-class-shortcode.php @@ -29,6 +29,11 @@ public function setUp(): void { * @throws \ReflectionException */ public function test_construct() { + // Set demo data for settings. + update_option( 'amp-admanager-menu-settings', array( + 'dfp-network-id' => '123456789', + 'load-amp-resources' => '1', + ) ); Utility::invoke_method( $this->_instance, '__construct' ); // Check existence of shortcode. $this->assertTrue( shortcode_exists( 'ampad' ) ); From cc34293f55eb3b2d8befd1971fc393e710f9362d Mon Sep 17 00:00:00 2001 From: thrijith Date: Thu, 30 Jan 2020 19:10:54 +0530 Subject: [PATCH 09/28] Move option update to setup Initialize AMP_Admanager class --- tests/classes/test-class-shortcode.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/classes/test-class-shortcode.php b/tests/classes/test-class-shortcode.php index aad5f0f..ad2017e 100644 --- a/tests/classes/test-class-shortcode.php +++ b/tests/classes/test-class-shortcode.php @@ -1,6 +1,7 @@ '123456789', + 'load-amp-resources' => '1', + ) ); + new AMP_AdManager(); $this->_instance = new Shortcode(); } @@ -29,11 +36,6 @@ public function setUp(): void { * @throws \ReflectionException */ public function test_construct() { - // Set demo data for settings. - update_option( 'amp-admanager-menu-settings', array( - 'dfp-network-id' => '123456789', - 'load-amp-resources' => '1', - ) ); Utility::invoke_method( $this->_instance, '__construct' ); // Check existence of shortcode. $this->assertTrue( shortcode_exists( 'ampad' ) ); From 30018cea493f6270ec73bd2ee19d8ce939c881aa Mon Sep 17 00:00:00 2001 From: thrijith Date: Thu, 30 Jan 2020 19:51:56 +0530 Subject: [PATCH 10/28] Update assertion use assertarrayHasKey instead of assertTrue --- tests/classes/test-class-admin.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/classes/test-class-admin.php b/tests/classes/test-class-admin.php index b368310..e67f325 100644 --- a/tests/classes/test-class-admin.php +++ b/tests/classes/test-class-admin.php @@ -62,9 +62,11 @@ public function test_amp_admanager_menu_init() { $this->_instance->amp_admanager_menu_init(); - $this->assertTrue( - array_key_exists( 'amp-admanager-menu', $new_whitelist_options ), - 'Option Group amp-admanager-menu has not been created' ); + $this->assertarrayHasKey( + 'amp-admanager-menu', + $new_whitelist_options, + 'Option Group amp-admanager-menu has not been created' + ); $settings = $new_whitelist_options['amp-admanager-menu']; From 2cb6ac7434c7a1f0d155fdfe3052d2c64fbf851c Mon Sep 17 00:00:00 2001 From: Vaishali Agola Date: Fri, 31 Jan 2020 10:57:41 +0530 Subject: [PATCH 11/28] Add test cases for AMP Admanager class --- .gitignore | 1 - composer.json | 31 ++ composer.lock | 160 +++++++ tests/bootstrap.php | 3 + tests/classes/test-class-amp-admanager.php | 517 +++++++++++++++++++++ tests/helpers/class-utility.php | 126 +++++ 6 files changed, 837 insertions(+), 1 deletion(-) create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 tests/classes/test-class-amp-admanager.php create mode 100644 tests/helpers/class-utility.php diff --git a/.gitignore b/.gitignore index b95261b..f83903a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ *.swp vendor node_modules -composer.lock .idea .DS_Store .vscode diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..3d15b6c --- /dev/null +++ b/composer.json @@ -0,0 +1,31 @@ +{ + "name": "amp-admanager", + "description": "Amp Admanager Plugin", + "type": "wordpress-plugin", + "license": "proprietary", + "authors": [ + { + "name": "rtCamp", + "homepage": "https://rtcamp.com" + } + ], + "repositories":[ + { + "type":"composer", + "url":"https://wpackagist.org" + } + ], + "require": { + "composer/installers": "^1.6", + "wpackagist-plugin/amp":"1.4.2" + }, + "require-dev": { + }, + "extra": { + "installer-paths": { + "../amp/": [ + "wpackagist-plugin/amp" + ] + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..473f1bc --- /dev/null +++ b/composer.lock @@ -0,0 +1,160 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "fa8bad7fdb6ec16483c6eac8d7f0cc85", + "packages": [ + { + "name": "composer/installers", + "version": "v1.7.0", + "source": { + "type": "git", + "url": "https://github.com/composer/installers.git", + "reference": "141b272484481432cda342727a427dc1e206bfa0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/installers/zipball/141b272484481432cda342727a427dc1e206bfa0", + "reference": "141b272484481432cda342727a427dc1e206bfa0", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0" + }, + "replace": { + "roundcube/plugin-installer": "*", + "shama/baton": "*" + }, + "require-dev": { + "composer/composer": "1.0.*@dev", + "phpunit/phpunit": "^4.8.36" + }, + "type": "composer-plugin", + "extra": { + "class": "Composer\\Installers\\Plugin", + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Installers\\": "src/Composer/Installers" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kyle Robinson Young", + "email": "kyle@dontkry.com", + "homepage": "https://github.com/shama" + } + ], + "description": "A multi-framework Composer library installer", + "homepage": "https://composer.github.io/installers/", + "keywords": [ + "Craft", + "Dolibarr", + "Eliasis", + "Hurad", + "ImageCMS", + "Kanboard", + "Lan Management System", + "MODX Evo", + "Mautic", + "Maya", + "OXID", + "Plentymarkets", + "Porto", + "RadPHP", + "SMF", + "Thelia", + "Whmcs", + "WolfCMS", + "agl", + "aimeos", + "annotatecms", + "attogram", + "bitrix", + "cakephp", + "chef", + "cockpit", + "codeigniter", + "concrete5", + "croogo", + "dokuwiki", + "drupal", + "eZ Platform", + "elgg", + "expressionengine", + "fuelphp", + "grav", + "installer", + "itop", + "joomla", + "known", + "kohana", + "laravel", + "lavalite", + "lithium", + "magento", + "majima", + "mako", + "mediawiki", + "modulework", + "modx", + "moodle", + "osclass", + "phpbb", + "piwik", + "ppi", + "puppet", + "pxcms", + "reindex", + "roundcube", + "shopware", + "silverstripe", + "sydes", + "symfony", + "typo3", + "wordpress", + "yawik", + "zend", + "zikula" + ], + "time": "2019-08-12T15:00:31+00:00" + }, + { + "name": "wpackagist-plugin/amp", + "version": "1.4.2", + "source": { + "type": "svn", + "url": "https://plugins.svn.wordpress.org/amp/", + "reference": "tags/1.4.2" + }, + "dist": { + "type": "zip", + "url": "https://downloads.wordpress.org/plugin/amp.1.4.2.zip", + "reference": null, + "shasum": null + }, + "require": { + "composer/installers": "~1.0" + }, + "type": "wordpress-plugin", + "homepage": "https://wordpress.org/plugins/amp/" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 8b90d61..4a1f6db 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -23,7 +23,10 @@ * Manually load the plugin being tested. */ function _manually_load_plugin() { + // Load amp plugin. + require_once dirname( dirname( __FILE__ ) ) . '/../amp/amp.php'; require_once dirname( dirname( __FILE__ ) ) . '/amp-admanager.php'; + require_once dirname( dirname( __FILE__ ) ) . '/tests/helpers/class-utility.php'; } tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' ); diff --git a/tests/classes/test-class-amp-admanager.php b/tests/classes/test-class-amp-admanager.php new file mode 100644 index 0000000..6f6141f --- /dev/null +++ b/tests/classes/test-class-amp-admanager.php @@ -0,0 +1,517 @@ +_instance = new AMP_AdManager(); + } + + /** + * Test constructor. + * + * @covers ::__construct + */ + public function test_construct() { + + Utility::invoke_method( $this->_instance, '__construct' ); + + $hooks = [ + [ + 'type' => 'action', + 'name' => 'wp_head', + 'priority' => 0, + 'function' => 'load_amp_resources', + ], + ]; + + // Check if hooks loaded. + foreach ( $hooks as $hook ) { + + $this->assertEquals( + $hook['priority'], + call_user_func( + sprintf( 'has_%s', $hook['type'] ), + $hook['name'], + array( + $this->_instance, + $hook['function'], + ) + ), + sprintf( 'AMP_AdManager::__construct() failed to register %1$s "%2$s" to %3$s()', $hook['type'], $hook['name'], $hook['function'] ) + ); + } + + } + + /** + * Mock global wp query. + * + * @param array $args WP query arguments. + * @param array $conditions wp query conditions. + */ + public function mock_wp_query( $args, $conditions ) { + $wp_query = new \WP_Query( $args ); + + foreach ( $conditions as $key => $value ) { + $wp_query->{$key} = $value; + } + + $GLOBALS['wp_query'] = $wp_query; + $GLOBALS['wp_the_query'] = $GLOBALS['wp_query']; + do_action_ref_array( 'pre_get_posts', [ &$GLOBALS['wp_query'] ] ); + } + + /** + * Test for get_dfp_ad_targeting_data. + * + * @covers ::get_dfp_ad_targeting_data + */ + public function test_get_dfp_ad_targeting_data() { + + if ( ! empty( $GLOBALS['wp_query'] ) ) { + $old_wp_query = $GLOBALS['wp_query']; + } + + $post_id = $this->factory->post->create( [ 'post_type' => 'post' ] ); + + $conditions = [ 'is_home' => true ]; + $this->mock_wp_query( [ 'post_type' => 'post', 'posts_per_page' => 1 ], $conditions ); + + $attr = [ + 'ad-unit' => 'AMP_ADTest', + 'sizes' => '336x280', + 'targeting' => [ + 'contentType' => '', + 'siteDomain' => 'example.com', + 'adId' => 'AMP_ADTest', + ] + ]; + $output = AMP_AdManager::get_dfp_ad_targeting_data( $attr ); + + $expected_output = [ + 'targeting' => [ + 'contentType' => '', + 'siteDomain' => 'example.com', + 'adId' => 'AMP_ADTest', + ] + ]; + + $this->assertNotEmpty( $output ); + $this->assertArrayHasKey( 'targeting', $output ); + $this->assertEquals( $expected_output, $output ); + + // Test case for is_single() condition. + $conditions = [ 'is_home' => false, 'is_single' => true, 'is_singular' => true ]; + $this->mock_wp_query( [ 'post_type' => 'post', 'posts_per_page' => 1 ], $conditions ); + + $attr = [ + 'ad-unit' => 'AMP_ADTest', + 'sizes' => '336x280', + ]; + $output = AMP_AdManager::get_dfp_ad_targeting_data( $attr ); + + $this->assertNotEmpty( $output ); + $this->assertArrayHasKey( 'targeting', $output ); + $this->assertArrayHasKey( 'postCategories', $output['targeting'] ); + $this->assertArrayHasKey( 'postName', $output['targeting'] ); + $this->assertArrayHasKey( 'contentType', $output['targeting'] ); + $this->assertArrayHasKey( 'adId', $output['targeting'] ); + $this->assertEquals( $output['targeting']['adId'], $attr['ad-unit'] ); + $this->assertEquals( $output['targeting']['contentType'], 'post' ); + + // Test case for is_page() condition. + $this->factory->post->create( [ 'post_type' => 'page' ] ); + $conditions = [ 'is_home' => false, 'is_page' => true, 'is_singular' => true ]; + $this->mock_wp_query( [ 'post_type' => 'page', 'posts_per_page' => 1 ], $conditions ); + + $output = AMP_AdManager::get_dfp_ad_targeting_data( $attr ); + + $this->assertNotEmpty( $output ); + $this->assertArrayHasKey( 'targeting', $output ); + $this->assertArrayHasKey( 'postName', $output['targeting'] ); + $this->assertArrayHasKey( 'contentType', $output['targeting'] ); + $this->assertArrayHasKey( 'adId', $output['targeting'] ); + $this->assertEquals( $output['targeting']['adId'], $attr['ad-unit'] ); + $this->assertEquals( $output['targeting']['contentType'], 'page' ); + + // Test case for is_archive() condition. + $conditions = [ 'is_archive' => true ]; + $this->mock_wp_query( [ 'post_type' => 'page' ], $conditions ); + + $output = AMP_AdManager::get_dfp_ad_targeting_data( $attr ); + + $this->assertNotEmpty( $output ); + $this->assertArrayHasKey( 'targeting', $output ); + $this->assertArrayHasKey( 'contentType', $output['targeting'] ); + $this->assertArrayHasKey( 'adId', $output['targeting'] ); + $this->assertEquals( $output['targeting']['adId'], $attr['ad-unit'] ); + $this->assertEquals( $output['targeting']['contentType'], 'listingpage' ); + + // Test case for is_category() condition. + $term = $this->factory->category->create_and_get( + [ + 'name' => 'Parent', + 'slug' => 'parent', + 'parent' => 0, + ] + ); + $conditions = [ 'is_category' => true ]; + $this->mock_wp_query( [ 'post_type' => 'page', 'category_name' => 'Parent', 'cat' => $term->term_id ], $conditions ); + + $output = AMP_AdManager::get_dfp_ad_targeting_data( $attr ); + + $this->assertNotEmpty( $output ); + $this->assertArrayHasKey( 'targeting', $output ); + $this->assertArrayHasKey( 'contentType', $output['targeting'] ); + $this->assertArrayHasKey( 'categoryPage', $output['targeting'] ); + $this->assertArrayHasKey( 'adId', $output['targeting'] ); + $this->assertEquals( $output['targeting']['adId'], $attr['ad-unit'] ); + $this->assertEquals( $output['targeting']['contentType'], 'listingpage' ); + $this->assertEquals( $output['targeting']['categoryPage'], 'parent' ); + + // Test case for is_author() condition. + $user_id = $this->factory->user->create( [ 'user_login' => 'testuser' ] ); + $conditions = [ 'is_author' => true, 'is_home' => false, 'is_archive' => true ]; + $this->mock_wp_query( [ 'post_type' => 'page', 'author' => $user_id ], $conditions ); + + $output = AMP_AdManager::get_dfp_ad_targeting_data( $attr ); + + $this->assertNotEmpty( $output ); + $this->assertArrayHasKey( 'targeting', $output ); + $this->assertArrayHasKey( 'contentType', $output['targeting'] ); + $this->assertArrayHasKey( 'adId', $output['targeting'] ); + $this->assertEquals( $output['targeting']['adId'], $attr['ad-unit'] ); + $this->assertEquals( $output['targeting']['contentType'], 'listingpage' ); + + // Restore global wp_query. + if ( ! empty( $old_wp_query ) ) { + $GLOBALS['wp_query'] = $old_wp_query; + } + } + + /** + * Test for get_amp_ad. + * + * @covers ::get_amp_ad + */ + public function test_get_amp_ad() { + + $expected_output = ''; + $attr = [ + 'ad-unit' => 'AMP_ADTest', + 'max' => 799, + 'min' => 500, + 'width' => 336, + 'height' => 280, + 'sizes' => '336x280', + ]; + $output = Utility::invoke_method( $this->_instance, 'get_amp_ad', [ $attr ] ); + + $this->assertNotEmpty( $output ); + $this->assertEquals( $expected_output, $output ); + + // Test case for blank attributes. + $output = Utility::invoke_method( $this->_instance, 'get_amp_ad', [ [] ] ); + $this->assertEmpty( $output ); + + } + + /** + * Test for get_ads. + * + * @covers ::get_ads + */ + public function test_get_ads() { + + // Mobile Ads. + $expected_output = ''; + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'mobile-sizes' => '300x250', + 'layout' => 'fixed', + ]; + $output = AMP_AdManager::get_ads( $ad_attr ); + + $this->assertNotEmpty( $output ); + $this->assertEquals( $expected_output, $output ); + + // Tablet Ads. + $expected_output = ''; + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'tablet-sizes' => '336x280', + 'layout' => 'fixed', + ]; + $output = AMP_AdManager::get_ads( $ad_attr ); + + $this->assertNotEmpty( $output ); + $this->assertEquals( $expected_output, $output ); + + // Desktop ads. + $expected_output = ''; + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'sizes' => '970x250', + ]; + $output = AMP_AdManager::get_ads( $ad_attr ); + + $this->assertNotEmpty( $output ); + $this->assertEquals( $expected_output, $output ); + + // Test echo. + $output_echo = Utility::buffer_and_return( 'AMP_AdManager\AMP_AdManager::get_ads', [ $ad_attr, true ] ); + + $this->assertNotEmpty( $output_echo ); + $this->assertEquals( $expected_output, $output_echo ); + + } + + /** + * Test for filter_breakpoints. + * + * @covers ::filter_breakpoints + */ + public function test_filter_breakpoints() { + + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'sizes' => '320x50', + 'layout' => 'fixed', + ]; + + $output = Utility::invoke_method( $this->_instance, 'filter_breakpoints', [ $ad_attr['sizes'] ] ); + + $this->assertNotEmpty( $output ); + $this->assertNotEmpty( $output ); + $this->assertTrue( is_array( $output ) ); + + $this->assertArrayHasKey( 'desktop', $output ); + $this->assertArrayHasKey( 'tablet', $output ); + $this->assertArrayHasKey( 'mobile', $output ); + + $expected_output = [ + 'mobile' => [ + 'width' => '320', + 'height' => '50', + 'sizes' => [ '320x50' ], + ], + 'tablet' => [ + 'width' => '320', + 'height' => '50', + 'sizes' => [ '320x50' ], + ], + 'desktop' => [], + ]; + + // Check mobile sizes data. + $this->assertEquals( $expected_output, $output ); + + // Test for desktop size ad. + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'sizes' => '728x90', + 'layout' => 'fixed', + ]; + + $output = Utility::invoke_method( $this->_instance, 'filter_breakpoints', [ $ad_attr['sizes'] ] ); + + $this->assertNotEmpty( $output ); + $this->assertNotEmpty( $output ); + $this->assertTrue( is_array( $output ) ); + + $this->assertArrayHasKey( 'desktop', $output ); + $this->assertArrayHasKey( 'tablet', $output ); + $this->assertArrayHasKey( 'mobile', $output ); + + $expected_output = [ + 'mobile' => [], + 'tablet' => [], + 'desktop' => [ + 'width' => '728', + 'height' => '90', + 'sizes' => [ '728x90' ], + ], + ]; + + // Check mobile sizes data. + $this->assertEquals( $expected_output, $output ); + + } + + /** + * Test for set_max_height_and_width. + * + * @covers ::set_max_height_and_width + */ + public function test_set_max_height_and_width() { + + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'sizes' => '320x50', + 'layout' => 'fixed', + ]; + $breakpoint = Utility::invoke_method( $this->_instance, 'filter_breakpoints', [ $ad_attr['sizes'] ] ); + + $output = Utility::invoke_method( $this->_instance, 'set_max_height_and_width', [ 'mobile', $breakpoint, 300, 50 ] ); + + $this->assertNotEmpty( $output ); + $this->assertNotEmpty( $output ); + $this->assertTrue( is_array( $output ) ); + + $this->assertArrayHasKey( 'desktop', $output ); + $this->assertArrayHasKey( 'tablet', $output ); + $this->assertArrayHasKey( 'mobile', $output ); + + $mobile_output = [ + 'width' => '320', + 'height' => '50', + 'sizes' => [ '320x50', '300x50' ], + ]; + + // Check mobile sizes data. + $this->assertEquals( $mobile_output, $output['mobile'] ); + + } + + /** + * Test for set_custom_sizes. + * + * @covers ::set_custom_sizes + */ + public function test_set_custom_sizes() { + + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'desktop-sizes' => '970x250', + 'tablet-sizes' => '336x280', + 'mobile-sizes' => '300x250', + 'custom-targeting' => 'adPosition:1', + 'layout' => 'fixed', + ]; + $breakpoint = []; + + $output = Utility::invoke_method( $this->_instance, 'set_custom_sizes', [ $ad_attr, $breakpoint ] ); + + $this->assertNotEmpty( $output ); + $this->assertTrue( is_array( $output ) ); + + $this->assertArrayHasKey( 'desktop', $output ); + $this->assertArrayHasKey( 'tablet', $output ); + $this->assertArrayHasKey( 'mobile', $output ); + + $desktop_output = [ + 'width' => '970', + 'height' => '250', + 'sizes' => [ '970x250' ], + ]; + + // Check output data. + $this->assertEquals( $desktop_output, $output['desktop'] ); + + // Case with breakpoints. + $ad_attr = [ + 'ad-unit' => 'AMP_ADTest', + 'sizes' => '320x50', + 'layout' => 'fixed', + ]; + $breakpoint = Utility::invoke_method( $this->_instance, 'filter_breakpoints', [ $ad_attr['sizes'] ] ); + + $output = Utility::invoke_method( $this->_instance, 'set_custom_sizes', [ $ad_attr, $breakpoint ] ); + + $this->assertNotEmpty( $output ); + $this->assertTrue( is_array( $output ) ); + + $this->assertArrayHasKey( 'desktop', $output ); + $this->assertArrayHasKey( 'tablet', $output ); + $this->assertArrayHasKey( 'mobile', $output ); + + $mobile_output = [ + 'width' => '320', + 'height' => '50', + 'sizes' => [ '320x50' ], + ]; + + // Check output data. + $this->assertEquals( $mobile_output, $output['mobile'] ); + + } + + /** + * Test for get_slot_media_query. + * + * @covers ::get_slot_media_query + */ + public function test_get_slot_media_query() { + + $expected_output = '(min-width: 200px) and (max-width: 50px)'; + $output = AMP_AdManager::get_slot_media_query( 200, 50 ); + + $this->assertEquals( $expected_output, $output ); + + $expected_output = '(min-width: 200px)'; + $output = AMP_AdManager::get_slot_media_query( 200, 0 ); + + $this->assertEquals( $expected_output, $output ); + + } + + /** + * Test for load_amp_resources. + * + * @covers ::load_amp_resources + */ + public function test_load_amp_resources() { + + $expected = ''; + + update_option( 'amp-admanager-menu-settings', [ 'load-amp-resources' => '1' ] ); + + // Update settings after updating option. + AMP_AdManager::$amp_settings = get_option( 'amp-admanager-menu-settings' ); + $output = Utility::buffer_and_return( array( $this->_instance, 'load_amp_resources' ) ); + + $this->assertContains( $expected, $output ); + $this->assertContains( '