Skip to content

Instantly share code, notes, and snippets.

@lucatume
Last active October 10, 2023 11:12
Show Gist options
  • Save lucatume/654304a4700751582a4e427c29a78497 to your computer and use it in GitHub Desktop.
Save lucatume/654304a4700751582a4e427c29a78497 to your computer and use it in GitHub Desktop.
Generate The Events Calendar test events using wp-cli

The Events Calendar load test generation plugin

A wp-cli generator of test events.

Installation and activation

Copy the "Raw" version of the tec-load-test.php file to a file called tec-load-test.php in your WordPress installation plugins folder.
Activate the plugin, via the WordPress Plugins administration page as you would do for any other plugin.

Usage

Load test

The plugin adds the tec-load-test command to wp-cli.

To generate 30 events per day between 2019-01-01 and 2019-01-30 run:

wp tec-load-test 2019-01-01 2019-01-30 30

Use wp tec-load-test --help to know about the command.

This command is heavy and event generation will take a lot. Try to reach your desired load in increasing steps.
Generating 100 events per day in one command is slower than generating 50 events per day in 2.

Checking detected MySQL max packet size

The plugin also adds the tec-mysql-packet-size command.

The command does not provide any option, just run wp tec-mysql-packet-size to see the size in bytes.

Counting events in any status in the database

The plugin adds another command as well: tec-count.

The command requires no arguments and supports no options, running wp tec-count will just return the number of events currently in the database, of any nature and in any status.

You can narrow down events to specific post stati by using the --status=<stati> flag:

wp tec-count --status=publish

wp tec-count --status=publish,draft
<?php
/**
* Plugin Name: TEC View load test CLI
* Plugin Description: Adds a wp-cli command the WordPress installation to generate test events, this plugin requires
* an active The Events Calendar plugin installation to work. Run <code>wp tec-load-test --help</code> for the command
* synopsis.
*/
namespace Tribe\Events\Tests;
use Tribe__Date_Utils as Dates;
use Tribe__Events__Organizer as Organizer;
use Tribe__Timezones as Timezones;
use Tribe__Utils__Array as Arr;
if ( ! defined( 'WP_CLI' ) ) {
return;
}
\WP_CLI::add_command( 'tec-load-test', '\Tribe\Events\Tests\generate_test_events' );
\WP_CLI::add_command( 'tec-mysql-packet-size', '\Tribe\Events\Tests\msyql_packet_size' );
\WP_CLI::add_command( 'tec-count', '\Tribe\Events\Tests\count_events' );
/**
* Generates n test events for each day in the specified period.
*
* ## OPTIONS
*
* <start>
* : The period start date, in a format parsable by `strtotime`.
*
* <end>
* : The period end date, in a format parsable by `strtotime`.
*
* [<n>]
* : The number of events to generate for each day.
* default: 200
*
* [--every_days=<every_days>]
* : An optional value to set the day frequency of the generated events. Defaults to 1 to create events each 1 day.
* default: 1
*
* [--title=<title>]
* : An optional title that will be used in the template '<date> <title> <n>'.
* default: test event
*
* [--category=<category>]
* : An optional category to assign to each crated event; either the category slug or ID.
* default: test
*
* [--featured]
* : All created events will be featured if this flag is set.
*
* [--max_duration=<max_duration>]
* : An optional upper limit to the maximum event duration, in hours.
* default: 16
*
* [--organizer=<organizer>]
* : An optional comma-separated list of Organizer post IDs that will be randomly assigned to each event.
*
* [--with_thumbnails]
* : Optionally pull and attach Lorem Picsum images to each event as featured images.
*
* [--with_ideal_ratio_thumbnails]
* : Optionally pull and attach Lorem Picsum images to each event as featured images in the 16:9 ideal ratio.
*
* ## EXAMPLES
*
* Generate 200 events on each day between two dates:
* wp tec-load-test 2019-09-25 2019-12-31
*
* Generate 50 events on each day between two dates with a title seed of "single event":
* wp tec-load-test 2019-09-25 2019-12-31 50 --title="single event"
*
* Generate 50 events on each day between two dates limiting duration to, at the most, 6 hours:
* wp tec-load-test 2019-09-25 2019-12-31 50 --max_duration=6
*
* Generate 2 events on each day between two dates assigning to each a random number of organizers from the list:
* wp tec-load-test 2019-09-25 2019-12-31 2 --organizer=23,89,45,59
*
* Generate 2 events each 3 days between two dates:
* wp tec-load-test 2019-09-25 2019-12-31 2 --every_days=3
*
* Generate 2 events each day attaching a post thumbnail to each:
* wp tec-load-test 2019-09-25 2019-12-31 2 --with_thumbnails
*
* Generate 2 events each day attaching an ideal 16:9 ratio post thumbnail to each:
* wp tec-load-test 2019-09-25 2019-12-31 2 --with_ideal_ratio_thumbnails
*/
function generate_test_events( array $args, array $assoc_args ) {
$count = 0;
list( $start, $end ) = $args;
$n = isset( $args[2] ) ? (int) $args[2] : 200;
$start_date = Dates::build_date_object( $start );
$end_date = Dates::build_date_object( $end );
$days_count = $end_date->diff( $start_date )->days;
\WP_CLI::debug( 'Start date: ' . $start_date->format( 'Y-m-d' ) );
\WP_CLI::debug( 'End date: ' . $end_date->format( 'Y-m-d' ) );
\WP_CLI::debug( 'Events per day: ' . $n );
$categories = handle_categories( $assoc_args );
$featured = ! empty( $assoc_args['featured'] );
$with_thumbnails = ! ( empty( $assoc_args['with_thumbnails'] ) && empty( $assoc_args['with_ideal_ratio_thumbnails'] ) );
$with_ideal_ratio_thumbnails = ! empty( $assoc_args['with_ideal_ratio_thumbnails'] );
$image_id = 1;
$progress = \WP_CLI\Utils\make_progress_bar( sprintf( 'Generating %d events per day...', $n ), $days_count );
$one_minute = new \DateInterval( 'PT1M' );
$site_timezone = get_option( 'timezone_string', 'UTC' );
if ( ! Timezones::is_utc_offset( $site_timezone ) ) {
// To avoid issues with event fetching, set the timezone to a "pretty" one, bail if we cannot.
$site_timezone = Timezones::generate_timezone_string_from_utc_offset( $site_timezone );
if ( Timezones::is_utc_offset( $site_timezone ) ) {
\WP_CLI::error(
sprintf(
'The site timezone is "%s"; please set a real timezone string (e.g. Europe/Paris).',
$site_timezone
)
);
}
}
$every_days = isset( $assoc_args['every_days'] ) ? $assoc_args['every_days'] : 1;
$interval_spec = "P{$every_days}D";
// Add one minute to the end date, else the `00:00:00` time would exclude it from the period.
$period = new \DatePeriod( $start_date,
new \DateInterval( $interval_spec ),
$end_date->add( $one_minute )
);
$colors = [ 'blue', 'yellow', 'red', 'green', 'purple', 'saffron', 'crimson', 'magenta', 'orange' ];
$names = [ 'whale', 'elephant', 'seal', 'giraffe', 'condor', 'fox', 'dog', 'squirrel', 'raccoon', 'lion' ];
$max_duration = isset( $assoc_args['max_duration'] ) ? (int) $assoc_args['max_duration'] : 16;
$organizer = isset( $assoc_args['organizer'] ) ?
array_filter( Arr::list_to_array( $assoc_args['organizer'] ) )
: false;
if ( ! empty( $organizer ) ) {
foreach ( $organizer as $o ) {
$organizer_post = get_post( $o );
if ( ! ( $organizer_post instanceof \WP_Post && $organizer_post->post_type === Organizer::POSTTYPE ) ) {
\WP_CLI::error( 'Organizer ' . $o . ' is not a valid Organizer.' );
}
}
}
/** @var \DateTime $day */
foreach ( $period as $day ) {
$date_string = $day->format( ( 'm/d' ) );
foreach ( range( 1, $n ) as $i ) {
$title = isset( $assoc_args['title'] )
? trim( $assoc_args['title'] )
: $colors[ random_int( 0, count( $colors ) - 1 ) ]
. ' ' . $names[ random_int( 0, count( $names ) - 1 ) ];
$update_map = [
'title' => sprintf( '%s %s %s', $date_string, $title, $i ),
'content' => sprintf( '%s %s %s', $date_string, $title, $i ),
'status' => 'publish',
'start_date' => ( clone $day )->setTime( mt_rand( 0, 23 ), mt_rand( 0, 59 ) ),
'duration' => mt_rand( HOUR_IN_SECONDS, $max_duration * HOUR_IN_SECONDS ),
'timezone' => $site_timezone,
];
if ( $featured ) {
$update_map['featured'] = true;
}
if ( $with_thumbnails || $with_ideal_ratio_thumbnails ) {
$image_width = random_int( 320, 2000 );
$image_height = $with_ideal_ratio_thumbnails ?
$image_width / 16 * 9
: random_int( 200, 2000 );
$image_url = sprintf(
'https://picsum.photos/%d/%d.jpg',
$image_width,
$image_height
);
$thumbnail_id = tribe_upload_image( $image_url );
if ( false === $thumbnail_id ) {
\WP_CLI::error( 'Failed to attach thumbnail "' . $image_url . '" to event.' );
}
$update_map['_thumbnail_id'] = $thumbnail_id;
}
if ( ! empty( $organizer ) ) {
if ( count( $organizer ) === 1 ) {
$update_map['organizer'] = $organizer;
} else {
$picks = array_rand( $organizer, mt_rand( 1, count( $organizer ) ) );
if ( count( $picks ) ) {
$update_map['organizer'] = array_map( static function ( $pick ) use ( $organizer ) {
return $organizer[ $pick ];
}, (array) $picks );
}
}
}
if ( ! empty( $categories ) ) {
$taxonomy_obj = get_taxonomy( 'tribe_events_cat' );
if ( ! current_user_can( $taxonomy_obj->cap->assign_terms ) ) {
$first_admin = get_users( [ 'role' => 'administrator', 'number' => 1 ] );
if ( empty( $first_admin ) || ! reset( $first_admin ) instanceof \WP_User ) {
\WP_CLI::error( 'Current user cannot assign taxonomy terms and no admin user was found.' );
}
$first_admin = reset( $first_admin );
wp_set_current_user( $first_admin->ID );
\WP_CLI::debug( 'Current user cannot assign taxonomy, switching to user ' . $first_admin->ID . '.' );
}
$update_map['category'] = $categories;
}
try {
$created = tribe_events()->set_args( $update_map )->create();
} catch ( \Tribe__Repository__Usage_Error $e ) {
\WP_CLI::error( 'Error while trying to create event: ' . $e->getMessage() );
}
if ( ! $created instanceof \WP_Post ) {
\WP_CLI::error( 'Error while trying to create event: created event is not a post.' );
}
\WP_CLI::debug( sprintf(
'Created event %s for date %s',
$i,
$day->format( 'Y-m-d H:i:s' )
)
);
// Space events out by 1 minute.
$day->add( $one_minute );
$count ++;
}
$progress->tick();
}
\Tribe__Events__Dates__Known_Range::instance()->rebuild_known_range();
$progress->finish();
\WP_CLI::success( sprintf( 'Generated %d events. Done!', $count ) );
}
/**
* Handles the creation of event categories.
*
* @param array<string,mixed> $assoc_args The associative arguments as passed by the user.
*
* @return array<int> The term IDs of the found, or created, categories.
*
* @throws \WP_CLI\ExitException If the category, or categories, specified by the user cannot be found or created.
*/
function handle_categories( array $assoc_args ) {
$term_ids = [];
if ( ! empty( $assoc_args['category'] ) ) {
$categories = Arr::list_to_array( $assoc_args['category'] );
foreach ( $categories as $category ) {
$term = get_term_by( 'slug', strtolower( $category ), 'tribe_events_cat' );
if ( ! $term instanceof \WP_Term ) {
$term = get_term_by( 'id', $category, 'tribe_events_cat' );
}
if ( ! $term instanceof \WP_Term ) {
$inserted = wp_insert_term( $category, 'tribe_events_cat', [ 'slug' => $category ] );
if ( $inserted instanceof \WP_Error ) {
\WP_CLI::error( 'Error while creating the event category: ' . $inserted->get_error_message() );
}
list( $term_id ) = array_values( $inserted );
$term = get_term_by( 'id', $term_id, 'tribe_events_cat' );
}
if ( ! $term instanceof \WP_Term ) {
\WP_CLI::error( 'Could not find or create the "' . $category . '" event category.' );
}
$term_data = [
'Name' => $term->name,
'Slug' => $term->slug,
'ID' => $term->term_id,
];
\WP_CLI::debug( 'Event category found or created: ' . wp_json_encode( $term_data ) );
$term_ids[] = $term->term_id;
}
return $term_ids;
}
}
/**
* Returns the detected MySQL max_packet_size value.
*
* ## EXAMPLES
*
* wp tec-mysql-packet-size
*/
function msyql_packet_size() {
/** @var \Tribe__Feature_Detection $feature_detection */
$feature_detection = tribe( 'feature-detection' );
\WP_CLI::success( 'MySQL detected max_packet_size: ' . $feature_detection->get_mysql_max_packet_size() );
}
/**
* Counts The Events Calendar events in the database.
*
* ## OPTIONS
*
* [--status=<status>]
* : The status, or a comma-separated list of post statuses, of the events to count.
*
* ## EXAMPLES
*
* Count the events currently in the database.
* wp tec-count
*
* Count the events currently in the database that are in the "publish" post status.
* wp tec-count --status=publish
*
* Count the events currently in the database that are in the "private" or "draft" post status.
* wp tec-count --status=publish,draft
*/
function count_events( array $args, array $assoc_args ) {
$status = Arr::get( $assoc_args, 'status', 'any' );
\WP_CLI::line( tribe_events()->where( 'status', $status )->found() );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment