diff --git a/admin/tool/generator/classes/course_backend.php b/admin/tool/generator/classes/course_backend.php
index 1034b93bea0..7d740723cf8 100644
--- a/admin/tool/generator/classes/course_backend.php
+++ b/admin/tool/generator/classes/course_backend.php
@@ -111,6 +111,15 @@ class tool_generator_course_backend extends tool_generator_backend {
parent::__construct($size, $fixeddataset, $filesizelimit, $progress);
}
+ /**
+ * Returns the relation between users and course sizes.
+ *
+ * @return array
+ */
+ public static function get_users_per_size() {
+ return self::$paramusers;
+ }
+
/**
* Gets a list of size choices supported by this backend.
*
@@ -280,6 +289,8 @@ class tool_generator_course_backend extends tool_generator_backend {
* @param int $last Number of last user
*/
private function create_user_accounts($first, $last) {
+ global $CFG;
+
$this->log('createaccounts', (object)array('from' => $first, 'to' => $last), true);
$count = $last - $first + 1;
$done = 0;
@@ -294,6 +305,12 @@ class tool_generator_course_backend extends tool_generator_backend {
// Create user account.
$record = array('firstname' => get_string('firstname', 'tool_generator'),
'lastname' => $number, 'username' => $username);
+
+ // We add a user password if it has been specified.
+ if (!empty($CFG->tool_generator_users_password)) {
+ $record['password'] = $CFG->tool_generator_users_password;
+ }
+
$user = $this->generator->create_user($record);
$this->userids[$number] = (int)$user->id;
$this->dot($done, $count);
@@ -311,7 +328,7 @@ class tool_generator_course_backend extends tool_generator_backend {
// Create pages.
$number = self::$parampages[$this->size];
$this->log('createpages', $number, true);
- for ($i=0; $i<$number; $i++) {
+ for ($i = 0; $i < $number; $i++) {
$record = array('course' => $this->course->id);
$options = array('section' => $this->get_target_section());
$pagegenerator->create_instance($record, $options);
@@ -369,7 +386,7 @@ class tool_generator_course_backend extends tool_generator_backend {
return substr($data, -$length);
}
$length -= strlen($data);
- for ($j=0; $j < $length; $j++) {
+ for ($j = 0; $j < $length; $j++) {
$data .= chr(rand(1, 255));
}
return $data;
@@ -448,13 +465,13 @@ class tool_generator_course_backend extends tool_generator_backend {
// Add discussions and posts.
$sofar = 0;
- for ($i=0; $i < $discussions; $i++) {
+ for ($i = 0; $i < $discussions; $i++) {
$record = array('forum' => $forum->id, 'course' => $this->course->id,
'userid' => $this->get_target_user());
$discussion = $forumgenerator->create_discussion($record);
$parentid = $DB->get_field('forum_posts', 'id', array('discussion' => $discussion->id), MUST_EXIST);
$sofar++;
- for ($j=0; $j < $posts - 1; $j++, $sofar++) {
+ for ($j = 0; $j < $posts - 1; $j++, $sofar++) {
$record = array('discussion' => $discussion->id,
'userid' => $this->get_target_user(), 'parent' => $parentid);
$forumgenerator->create_post($record);
diff --git a/admin/tool/generator/classes/make_form.php b/admin/tool/generator/classes/make_course_form.php
similarity index 78%
rename from admin/tool/generator/classes/make_form.php
rename to admin/tool/generator/classes/make_course_form.php
index 879364d2174..5c965152501 100644
--- a/admin/tool/generator/classes/make_form.php
+++ b/admin/tool/generator/classes/make_course_form.php
@@ -14,6 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
+/**
+ * Course form.
+ *
+ * @package tool_generator
+ * @copyright 2013 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/formslib.php');
@@ -25,8 +33,13 @@ require_once($CFG->libdir . '/formslib.php');
* @copyright 2013 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-class tool_generator_make_form extends moodleform {
+class tool_generator_make_course_form extends moodleform {
+ /**
+ * Course generation tool form definition.
+ *
+ * @return void
+ */
public function definition() {
$mform = $this->_form;
@@ -41,6 +54,13 @@ class tool_generator_make_form extends moodleform {
$mform->addElement('submit', 'submit', get_string('createcourse', 'tool_generator'));
}
+ /**
+ * Form validation.
+ *
+ * @param array $data
+ * @param array $files
+ * @return void
+ */
public function validation($data, $files) {
global $DB;
$errors = array();
@@ -48,7 +68,7 @@ class tool_generator_make_form extends moodleform {
// Check course doesn't already exist.
if (!empty($data['shortname'])) {
// Check shortname.
- $error = tool_generator_course_backend::check_shortname_available($data['shortname']);
+ $error = tool_generator_course_backend::check_shortname_available($data['shortname']);
if ($error) {
$errors['shortname'] = $error;
}
diff --git a/admin/tool/generator/classes/make_testplan_form.php b/admin/tool/generator/classes/make_testplan_form.php
new file mode 100644
index 00000000000..15552d5775a
--- /dev/null
+++ b/admin/tool/generator/classes/make_testplan_form.php
@@ -0,0 +1,82 @@
+.
+
+/**
+ * Test plan form.
+ *
+ * @package tool_generator
+ * @copyright 2013 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->libdir . '/formslib.php');
+
+/**
+ * Form with options for creating large course.
+ *
+ * @package tool_generator
+ * @copyright 2013 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_generator_make_testplan_form extends moodleform {
+
+ /**
+ * Test plan form definition.
+ *
+ * @return void
+ */
+ public function definition() {
+ $mform = $this->_form;
+
+ $mform->addElement('select', 'size', get_string('size', 'tool_generator'),
+ tool_generator_testplan_backend::get_size_choices());
+ $mform->setDefault('size', tool_generator_testplan_backend::DEFAULT_SIZE);
+
+ $mform->addElement('select', 'courseid', get_string('targetcourse', 'tool_generator'),
+ tool_generator_testplan_backend::get_course_options());
+
+ $mform->addElement('advcheckbox', 'updateuserspassword', get_string('updateuserspassword', 'tool_generator'));
+ $mform->addHelpButton('updateuserspassword', 'updateuserspassword', 'tool_generator');
+
+ $mform->addElement('submit', 'submit', get_string('createtestplan', 'tool_generator'));
+ }
+
+ /**
+ * Checks that the submitted data allows us to create a test plan.
+ *
+ * @param array $data
+ * @param array $files
+ * @return array An array of errors
+ */
+ public function validation($data, $files) {
+ global $CFG;
+
+ $errors = array();
+ if (empty($CFG->tool_generator_users_password) || is_bool($CFG->tool_generator_users_password)) {
+ $errors['updateuserspassword'] = get_string('error_nouserspassword', 'tool_generator');
+ }
+
+ // Better to repeat here the query than to do it afterwards and end up with an exception.
+ if ($courseerrors = tool_generator_testplan_backend::has_selected_course_any_problem($data['courseid'], $data['size'])) {
+ $errors = array_merge($errors, $courseerrors);
+ }
+
+ return $errors;
+ }
+
+}
diff --git a/admin/tool/generator/classes/testplan_backend.php b/admin/tool/generator/classes/testplan_backend.php
new file mode 100644
index 00000000000..6f70dad3ed9
--- /dev/null
+++ b/admin/tool/generator/classes/testplan_backend.php
@@ -0,0 +1,328 @@
+.
+
+/**
+ * Test plan generator.
+ *
+ * @package tool_generator
+ * @copyright 2013 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Generates the files required by JMeter.
+ *
+ * @package tool_generator
+ * @copyright 2013 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_generator_testplan_backend extends tool_generator_backend {
+
+ /**
+ * @var The URL to the repository of the external project.
+ */
+ protected static $repourl = 'https://github.com/moodlehq/moodle-performance-comparison';
+
+ /**
+ * @var Number of users depending on the selected size.
+ */
+ protected static $users = array(1, 30, 200, 1000, 5000, 10000);
+
+ /**
+ * @var Number of loops depending on the selected size.
+ */
+ protected static $loops = array(1, 1, 2, 3, 3, 5);
+
+ /**
+ * @var Rampup period depending on the selected size.
+ */
+ protected static $rampups = array(1, 6, 40, 100, 500, 800);
+
+ /**
+ * Gets a list of size choices supported by this backend.
+ *
+ * @return array List of size (int) => text description for display
+ */
+ public static function get_size_choices() {
+
+ $options = array();
+ for ($size = self::MIN_SIZE; $size <= self::MAX_SIZE; $size++) {
+ $a = new stdClass();
+ $a->users = self::$users[$size];
+ $a->loops = self::$loops[$size];
+ $a->rampup = self::$rampups[$size];
+ $options[$size] = get_string('testplansize_' . $size, 'tool_generator', $a);
+ }
+ return $options;
+ }
+
+ /**
+ * Gets the list of courses that can be used used to generate a test.
+ *
+ * @return array The list of options as courseid => name
+ */
+ public static function get_course_options() {
+ $courses = get_courses('all', 'c.sortorder ASC', 'c.id, c.shortname, c.fullname');
+ if (!$courses) {
+ print_error('error_nocourses', 'tool_generator');
+ }
+
+ $options = array();
+ unset($courses[1]);
+ foreach ($courses as $course) {
+ $options[$course->id] = $course->fullname . '(' . $course->shortname . ')';
+ }
+ return $options;
+ }
+
+ /**
+ * Getter for moodle-performance-comparison project URL.
+ *
+ * @return string
+ */
+ public static function get_repourl() {
+ return self::$repourl;
+ }
+
+ /**
+ * Creates the test plan file.
+ *
+ * @param int $courseid The target course id
+ * @param int $size The test plan size
+ * @return stored_file
+ */
+ public static function create_testplan_file($courseid, $size) {
+ $jmxcontents = self::generate_test_plan($courseid, $size);
+
+ $fs = get_file_storage();
+ $filerecord = self::get_file_record('testplan', 'jmx');
+ return $fs->create_file_from_string($filerecord, $jmxcontents);
+ }
+
+ /**
+ * Creates the users data file.
+ *
+ * @param int $courseid The target course id
+ * @param bool $updateuserspassword Updates the course users password to $CFG->tool_generator_users_password
+ * @return stored_file
+ */
+ public static function create_users_file($courseid, $updateuserspassword) {
+ $csvcontents = self::generate_users_file($courseid, $updateuserspassword);
+
+ $fs = get_file_storage();
+ $filerecord = self::get_file_record('users', 'csv');
+ return $fs->create_file_from_string($filerecord, $csvcontents);
+ }
+
+ /**
+ * Generates the test plan according to the target course contents.
+ *
+ * @param int $targetcourseid The target course id
+ * @param int $size The test plan size
+ * @return string The test plan as a string
+ */
+ protected static function generate_test_plan($targetcourseid, $size) {
+ global $CFG;
+
+ // Getting the template.
+ $template = file_get_contents(__DIR__ . '/../testplan.template.jmx');
+
+ // Getting the course modules data.
+ $coursedata = self::get_course_test_data($targetcourseid);
+
+ // Host and path to the site.
+ $urlcomponents = parse_url($CFG->wwwroot);
+ if (empty($urlcomponents['path'])) {
+ $urlcomponents['path'] = '';
+ }
+
+ $replacements = array(
+ self::$users[$size],
+ self::$loops[$size],
+ self::$rampups[$size],
+ $urlcomponents['host'],
+ $urlcomponents['path'],
+ get_string('shortsize_' . $size, 'tool_generator'),
+ $targetcourseid,
+ $coursedata->pageid,
+ $coursedata->forumid,
+ $coursedata->forumdiscussionid,
+ $coursedata->forumreplyid
+ );
+
+ $placeholders = array(
+ '{{USERS_PLACEHOLDER}}',
+ '{{LOOPS_PLACEHOLDER}}',
+ '{{RAMPUP_PLACEHOLDER}}',
+ '{{HOST_PLACEHOLDER}}',
+ '{{SITEPATH_PLACEHOLDER}}',
+ '{{SIZE_PLACEHOLDER}}',
+ '{{COURSEID_PLACEHOLDER}}',
+ '{{PAGEACTIVITYID_PLACEHOLDER}}',
+ '{{FORUMACTIVITYID_PLACEHOLDER}}',
+ '{{FORUMDISCUSSIONID_PLACEHOLDER}}',
+ '{{FORUMREPLYID_PLACEHOLDER}}'
+ );
+
+ // Fill the template with the target course values.
+ return str_replace($placeholders, $replacements, $template);
+ }
+
+ /**
+ * Generates the user's credentials file with all the course's users
+ *
+ * @param int $targetcourseid
+ * @param bool $updateuserspassword Updates the course users password to $CFG->tool_generator_users_password
+ * @return string The users csv file contents.
+ */
+ protected static function generate_users_file($targetcourseid, $updateuserspassword) {
+ global $CFG;
+
+ $coursecontext = context_course::instance($targetcourseid);
+
+ $users = get_enrolled_users($coursecontext, '', 0, 'u.id, u.username, u.auth', 'u.username ASC');
+ if (!$users) {
+ print_error('coursewithoutusers', 'tool_generator');
+ }
+
+ $lines = array();
+ foreach ($users as $user) {
+
+ // Updating password to the one set in config.php.
+ if ($updateuserspassword) {
+ $userauth = get_auth_plugin($user->auth);
+ if (!$userauth->user_update_password($user, $CFG->tool_generator_users_password)) {
+ print_error('errorpasswordupdate', 'auth');
+ }
+ }
+
+ // Here we already checked that $CFG->tool_generator_users_password is not null.
+ $lines[] = $user->username . ',' . $CFG->tool_generator_users_password;
+ }
+
+ return implode(PHP_EOL, $lines);
+ }
+
+ /**
+ * Returns a tool_generator file record
+ *
+ * @param string $filearea testplan or users
+ * @param string $filetype The file extension jmx or csv
+ * @return stdClass The file record to use when creating tool_generator files
+ */
+ protected static function get_file_record($filearea, $filetype) {
+
+ $systemcontext = context_system::instance();
+
+ $filerecord = new stdClass();
+ $filerecord->contextid = $systemcontext->id;
+ $filerecord->component = 'tool_generator';
+ $filerecord->filearea = $filearea;
+ $filerecord->itemid = 0;
+ $filerecord->filepath = '/';
+
+ // Random generated number to avoid concurrent execution problems.
+ $filerecord->filename = $filearea . '_' . date('YmdHi', time()) . '_' . rand(1000, 9999) . '.' . $filetype;
+
+ return $filerecord;
+ }
+
+ /**
+ * Gets the data required to fill the test plan template with the database contents.
+ *
+ * @param int $targetcourseid The target course id
+ * @return stdClass The ids required by the test plan
+ */
+ protected static function get_course_test_data($targetcourseid) {
+ global $DB, $USER;
+
+ $data = new stdClass();
+
+ // Getting course contents info as the current user (will be an admin).
+ $course = new stdClass();
+ $course->id = $targetcourseid;
+ $courseinfo = new course_modinfo($course, $USER->id);
+
+ // Getting the first page module instance.
+ if (!$pages = $courseinfo->get_instances_of('page')) {
+ print_error('error_nopageinstances', 'tool_generator');
+ }
+ $data->pageid = reset($pages)->id;
+
+ // Getting the first forum module instance and it's first discussion and reply as well.
+ if (!$forums = $courseinfo->get_instances_of('forum')) {
+ print_error('error_noforuminstances', 'tool_generator');
+ }
+ $forum = reset($forums);
+
+ // Getting the first discussion (and reply).
+ if (!$discussions = forum_get_discussions($forum, 'd.timemodified ASC', false, -1, 1)) {
+ print_error('error_noforumdiscussions', 'tool_generator');
+ }
+ $discussion = reset($discussions);
+
+ $data->forumid = $forum->id;
+ $data->forumdiscussionid = $discussion->discussion;
+ $data->forumreplyid = $discussion->id;
+
+ // According to the current test plan.
+ return $data;
+ }
+
+ /**
+ * Checks if the selected target course is ok.
+ *
+ * @param int|string $course
+ * @param int $size
+ * @return array Errors array or false if everything is ok
+ */
+ public static function has_selected_course_any_problem($course, $size) {
+ global $DB;
+
+ $errors = array();
+
+ if (!is_numeric($course)) {
+ if (!$course = $DB->get_field('course', 'id', array('shortname' => $course))) {
+ $errors['courseid'] = get_string('error_nonexistingcourse', 'tool_generator');
+ return $errors;
+ }
+ }
+
+ $coursecontext = context_course::instance($course, IGNORE_MISSING);
+ if (!$coursecontext) {
+ $errors['courseid'] = get_string('error_nonexistingcourse', 'tool_generator');
+ return $errors;
+ }
+
+ if (!$users = get_enrolled_users($coursecontext, '', 0, 'u.id')) {
+ $errors['courseid'] = get_string('coursewithoutusers', 'tool_generator');
+ }
+
+ // Checks that the selected course has enough users.
+ $coursesizes = tool_generator_course_backend::get_users_per_size();
+ if (count($users) < $coursesizes[$size]) {
+ $errors['size'] = get_string('notenoughusers', 'tool_generator');
+ }
+
+ if (empty($errors)) {
+ return false;
+ }
+
+ return $errors;
+ }
+}
diff --git a/admin/tool/generator/cli/maketestplan.php b/admin/tool/generator/cli/maketestplan.php
new file mode 100644
index 00000000000..456393d81a9
--- /dev/null
+++ b/admin/tool/generator/cli/maketestplan.php
@@ -0,0 +1,122 @@
+.
+
+/**
+ * CLI interface for creating a test plan
+ *
+ * @package tool_generator
+ * @copyright 2013 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define('CLI_SCRIPT', true);
+define('NO_OUTPUT_BUFFERING', true);
+
+require(dirname(__FILE__) . '/../../../../config.php');
+require_once($CFG->libdir. '/clilib.php');
+
+// CLI options.
+list($options, $unrecognized) = cli_get_params(
+ array(
+ 'help' => false,
+ 'shortname' => false,
+ 'size' => false,
+ 'bypasscheck' => false,
+ 'updateuserspassword' => false
+ ),
+ array(
+ 'h' => 'help'
+ )
+);
+
+$testplansizes = '* ' . implode(PHP_EOL . '* ', tool_generator_testplan_backend::get_size_choices());
+
+// Display help.
+if (!empty($options['help']) || empty($options['shortname']) || empty($options['size'])) {
+
+ echo get_string('testplanexplanation', 'tool_generator', tool_generator_testplan_backend::get_repourl()) .
+"Options:
+-h, --help Print out this help
+--shortname Shortname of the test plan's target course (required)
+--size Size of the test plan to create XS, S, M, L, XL, or XXL (required)
+--bypasscheck Bypasses the developer-mode check (be careful!)
+--updateuserspassword Updates the target course users password according to \$CFG->tool_generator_users_password
+
+$testplansizes
+
+Consider that, the server resources you will need to run the test plan will be higher as the test plan size is higher.
+
+Example from Moodle root directory:
+\$ sudo -u www-data /usr/bin/php admin/tool/generator/cli/maketestplan.php --shortname=\"testcourse_12\" --size=S
+";
+ // Exit with error unless we're showing this because they asked for it.
+ exit(empty($options['help']) ? 1 : 0);
+}
+
+// Check debugging is set to developer level.
+if (empty($options['bypasscheck']) && !$CFG->debugdeveloper) {
+ cli_error(get_string('error_notdebugging', 'tool_generator'));
+}
+
+// Get options.
+$shortname = $options['shortname'];
+$sizename = $options['size'];
+
+// Check size.
+try {
+ $size = tool_generator_testplan_backend::size_for_name($sizename);
+} catch (coding_exception $e) {
+ cli_error("Error: Invalid size ($sizename). Use --help for help.");
+}
+
+// Check selected course.
+if ($errors = tool_generator_testplan_backend::has_selected_course_any_problem($shortname, $size)) {
+ // Showing the first reported problem.
+ cli_error("Error: " . reset($errors));
+}
+
+// Checking if test users password is set.
+if (empty($CFG->tool_generator_users_password) || is_bool($CFG->tool_generator_users_password)) {
+ cli_error("Error: " . get_string('error_nouserspassword', 'tool_generator'));
+}
+
+// Switch to admin user account.
+session_set_user(get_admin());
+
+// Create files.
+$courseid = $DB->get_field('course', 'id', array('shortname' => $shortname));
+$usersfile = tool_generator_testplan_backend::create_users_file($courseid, !empty($options['updateuserspassword']));
+$testplanfile = tool_generator_testplan_backend::create_testplan_file($courseid, $size);
+
+// One file path per line so other CLI scripts can easily parse the output.
+echo moodle_url::make_pluginfile_url(
+ $testplanfile->get_contextid(),
+ $testplanfile->get_component(),
+ $testplanfile->get_filearea(),
+ $testplanfile->get_itemid(),
+ $testplanfile->get_filepath(),
+ $testplanfile->get_filename()
+ ) .
+ PHP_EOL .
+ moodle_url::make_pluginfile_url(
+ $usersfile->get_contextid(),
+ $usersfile->get_component(),
+ $usersfile->get_filearea(),
+ $usersfile->get_itemid(),
+ $usersfile->get_filepath(),
+ $usersfile->get_filename()
+ ) .
+ PHP_EOL;
diff --git a/admin/tool/generator/lang/en/tool_generator.php b/admin/tool/generator/lang/en/tool_generator.php
index a204ce8aed7..9aab6287435 100644
--- a/admin/tool/generator/lang/en/tool_generator.php
+++ b/admin/tool/generator/lang/en/tool_generator.php
@@ -23,16 +23,7 @@
*/
$string['bigfile'] = 'Big file {$a}';
-$string['coursesize_0'] = 'XS (~10KB; create in ~1 second)';
-$string['coursesize_1'] = 'S (~10MB; create in ~30 seconds)';
-$string['coursesize_2'] = 'M (~100MB; create in ~5 minutes)';
-$string['coursesize_3'] = 'L (~1GB; create in ~1 hour)';
-$string['coursesize_4'] = 'XL (~10GB; create in ~4 hours)';
-$string['coursesize_5'] = 'XXL (~20GB; create in ~8 hours)';
-$string['createcourse'] = 'Create course';
-$string['creating'] = 'Creating course';
-$string['done'] = 'done ({$a}s)';
-$string['explanation'] = 'This tool creates standard test courses that include many
+$string['courseexplanation'] = 'This tool creates standard test courses that include many
sections, activities, and files.
This is intended to provide a standardised measure for checking the reliability
@@ -50,16 +41,38 @@ filesystem space (tens of gigabytes). You will need to delete the courses
(To avoid accidental use, this feature is disabled unless you have also selected
DEVELOPER debugging level.)';
+$string['coursesize_0'] = 'XS (~10KB; create in ~1 second)';
+$string['coursesize_1'] = 'S (~10MB; create in ~30 seconds)';
+$string['coursesize_2'] = 'M (~100MB; create in ~5 minutes)';
+$string['coursesize_3'] = 'L (~1GB; create in ~1 hour)';
+$string['coursesize_4'] = 'XL (~10GB; create in ~4 hours)';
+$string['coursesize_5'] = 'XXL (~20GB; create in ~8 hours)';
+$string['coursewithoutusers'] = 'The selected course has no users';
+$string['createcourse'] = 'Create course';
+$string['createtestplan'] = 'Create test plan';
+$string['creating'] = 'Creating course';
+$string['done'] = 'done ({$a}s)';
+$string['downloadtestplan'] = 'Download test plan';
+$string['downloadusersfile'] = 'Download users file';
+$string['error_nocourses'] = 'There are no courses to generate the test plan';
+$string['error_noforumdiscussions'] = 'The selected course does not contain forum discussions';
+$string['error_noforuminstances'] = 'The selected course does not contain forum module instances';
+$string['error_noforumreplies'] = 'The selected course does not contain forum replies';
+$string['error_nonexistingcourse'] = 'The specified course does not exist';
+$string['error_nopageinstances'] = 'The selected course does not contain page module instances';
$string['error_notdebugging'] = 'Not available on this server because debugging is not set to DEVELOPER';
+$string['error_nouserspassword'] = 'You need to set $CFG->tool_generator_users_password in config.php to generate the test plan';
$string['firstname'] = 'Test course user';
$string['fullname'] = 'Test course: {$a->size}';
$string['maketestcourse'] = 'Make test course';
+$string['maketestplan'] = 'Make JMeter test plan';
+$string['notenoughusers'] = 'The selected course does not have enough users';
$string['pluginname'] = 'Development data generator';
-$string['progress_createcourse'] = 'Creating course {$a}';
$string['progress_checkaccounts'] = 'Checking user accounts ({$a})';
$string['progress_coursecompleted'] = 'Course completed ({$a}s)';
$string['progress_createaccounts'] = 'Creating user accounts ({$a->from} - {$a->to})';
$string['progress_createbigfiles'] = 'Creating big files ({$a})';
+$string['progress_createcourse'] = 'Creating course {$a}';
$string['progress_createforum'] = 'Creating forum ({$a} posts)';
$string['progress_createpages'] = 'Creating pages ({$a})';
$string['progress_createsmallfiles'] = 'Creating small files ({$a})';
@@ -79,3 +92,33 @@ $string['sitesize_4'] = 'XL (~10GB; 1065 courses, created in ~5 hours)';
$string['sitesize_5'] = 'XXL (~20GB; 4177 courses, created in ~10 hours)';
$string['size'] = 'Size of course';
$string['smallfiles'] = 'Small files';
+$string['targetcourse'] = 'Test target course';
+$string['testplanexplanation'] = 'This tool creates a JMeter test plan file along with the user credentials file.
+
+This test plan is designed to work along with {$a}, which makes easier to run the test plan in a specific Moodle environment, gathers information about the runs and compares the results, so you will need to download it and use it\'s test_runner.sh script or follow the installation and usage instructions.
+
+You need to set a password for the course users in config.php (p.e. $CFG->tool_generator_users_password = \'moodle\';) there
+is no default value for this password to prevent unintended usages of the tool. You need to use the update passwords option in case your course users have other passwords or they were generated by tool_generator but without setting a $CFG->tool_generator_users_password value.
+
+It is part of tool_generator so it works well with the courses generated by the courses and the site generators, it can
+also be used with any course that contains, at least:
+* Enough enrolled users (depends on the test plan size you select) with the password reset to \'moodle\'
+* A page module instance
+* A forum module instance with at least one discussion and one reply
+
+You might want to consider your servers capacity when running large test plans as the amount to load generated by JMeter
+can be specially big. The ramp up period has been adjusted according to the number of threads (users) to reduce this kind
+of issues but the load is still huge.
+
+**Do not run the test plan on a live system**. This feature only creates the files to feed JMeter so is not dangerous by
+itself, but you should **NEVER** run this test plan in a production site.
+
+';
+$string['testplansize_0'] = 'XS ({$a->users} users, {$a->loops} loops and {$a->rampup} rampup period)';
+$string['testplansize_1'] = 'S ({$a->users} users, {$a->loops} loops and {$a->rampup} rampup period)';
+$string['testplansize_2'] = 'M ({$a->users} users, {$a->loops} loops and {$a->rampup} rampup period)';
+$string['testplansize_3'] = 'L ({$a->users} users, {$a->loops} loops and {$a->rampup} rampup period)';
+$string['testplansize_4'] = 'XL ({$a->users} users, {$a->loops} loops and {$a->rampup} rampup period)';
+$string['testplansize_5'] = 'XXL ({$a->users} users, {$a->loops} loops and {$a->rampup} rampup period)';
+$string['updateuserspassword'] = 'Update course users password';
+$string['updateuserspassword_help'] = 'JMeter needs to login as the course users, you can set the users password using $CFG->tool_generator_users_password in config.php; this setting updates the course user\'s password according to $CFG->tool_generator_users_password. It can be useful in case you are using a course not generated by tool_generator or $CFG->tool_generator_users_password was not set when you created the test courses.';
diff --git a/admin/tool/generator/lib.php b/admin/tool/generator/lib.php
new file mode 100644
index 00000000000..18e8a0905fe
--- /dev/null
+++ b/admin/tool/generator/lib.php
@@ -0,0 +1,59 @@
+.
+
+/**
+ * Generator tool functions.
+ *
+ * @package tool_generator
+ * @copyright David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Files support.
+ *
+ * Exits if the required permissions are not satisfied.
+ *
+ * @param stdClass $course course object
+ * @param stdClass $cm
+ * @param stdClass $context context object
+ * @param string $filearea file area
+ * @param array $args extra arguments
+ * @param bool $forcedownload whether or not force download
+ * @param array $options additional options affecting the file serving
+ * @return void The file is sent along with it's headers
+ */
+function tool_generator_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = array()) {
+
+ // Only for admins or CLI.
+ if (!defined('CLI_SCRIPT') && !is_siteadmin()) {
+ die;
+ }
+
+ if ($context->contextlevel != CONTEXT_SYSTEM) {
+ send_file_not_found();
+ }
+
+ $fs = get_file_storage();
+ $file = $fs->get_file($context->id, 'tool_generator', $filearea, $args[0], '/', $args[1]);
+
+ // Send the file, always forcing download, we don't want options.
+ session_get_instance()->write_close();
+ send_stored_file($file, 0, 0, true);
+}
+
diff --git a/admin/tool/generator/maketestcourse.php b/admin/tool/generator/maketestcourse.php
index 82af097cb8f..a7b760bd63d 100644
--- a/admin/tool/generator/maketestcourse.php
+++ b/admin/tool/generator/maketestcourse.php
@@ -15,8 +15,7 @@
// along with Moodle. If not, see .
/**
- * Script creates a standardised large course for testing reliability and
- * performance.
+ * Script creates a standardised large course for testing reliability and performance.
*
* @package tool_generator
* @copyright 2013 The Open University
@@ -32,7 +31,7 @@ require('../../../config.php');
require_once($CFG->libdir . '/adminlib.php');
// Initialise page and check permissions.
-admin_externalpage_setup('toolgenerator');
+admin_externalpage_setup('toolgeneratorcourse');
// Start page.
echo $OUTPUT->header();
@@ -40,7 +39,7 @@ echo $OUTPUT->heading(get_string('maketestcourse', 'tool_generator'));
// Information message.
$context = context_system::instance();
-echo $OUTPUT->box(format_text(get_string('explanation', 'tool_generator'),
+echo $OUTPUT->box(format_text(get_string('courseexplanation', 'tool_generator'),
FORMAT_MARKDOWN, array('context' => $context)));
// Check debugging is set to DEVELOPER.
@@ -51,7 +50,7 @@ if (!debugging('', DEBUG_DEVELOPER)) {
}
// Set up the form.
-$mform = new tool_generator_make_form('maketestcourse.php');
+$mform = new tool_generator_make_course_form('maketestcourse.php');
if ($data = $mform->get_data()) {
// Do actual work.
echo $OUTPUT->heading(get_string('creating', 'tool_generator'));
diff --git a/admin/tool/generator/maketestplan.php b/admin/tool/generator/maketestplan.php
new file mode 100644
index 00000000000..fe2965ae225
--- /dev/null
+++ b/admin/tool/generator/maketestplan.php
@@ -0,0 +1,87 @@
+.
+
+/**
+ * Generates a JMeter test plan to performance comparison.
+ *
+ * @package tool_generator
+ * @copyright 2013 David Monllaó
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require(__DIR__ . '/../../../config.php');
+require_once($CFG->libdir . '/adminlib.php');
+
+// Initialise page and check permissions.
+admin_externalpage_setup('toolgeneratortestplan');
+
+// Start page.
+echo $OUTPUT->header();
+echo $OUTPUT->heading(get_string('maketestplan', 'tool_generator'));
+
+// Information message.
+$context = context_system::instance();
+$markdownlink = '[' . tool_generator_testplan_backend::get_repourl() . '](' . tool_generator_testplan_backend::get_repourl() . ')';
+echo $OUTPUT->box(format_text(get_string('testplanexplanation', 'tool_generator', $markdownlink),
+ FORMAT_MARKDOWN, array('context' => $context)));
+
+// Check debugging is set to DEVELOPER.
+if (!$CFG->debugdeveloper) {
+ echo $OUTPUT->notification(get_string('error_notdebugging', 'tool_generator'));
+ echo $OUTPUT->footer();
+ exit;
+}
+
+// Set up the form.
+$mform = new tool_generator_make_testplan_form('maketestplan.php');
+if ($data = $mform->get_data()) {
+
+ // Creating both test plan and users files.
+ $testplanfile = tool_generator_testplan_backend::create_testplan_file($data->courseid, $data->size);
+ $usersfile = tool_generator_testplan_backend::create_users_file($data->courseid, $data->updateuserspassword);
+
+ // Test plan link.
+ $testplanurl = moodle_url::make_pluginfile_url(
+ $testplanfile->get_contextid(),
+ $testplanfile->get_component(),
+ $testplanfile->get_filearea(),
+ $testplanfile->get_itemid(),
+ $testplanfile->get_filepath(),
+ $testplanfile->get_filename()
+ );
+ echo html_writer::div(
+ html_writer::link($testplanurl, get_string('downloadtestplan', 'tool_generator'))
+ );
+
+ // Users file link.
+ $usersfileurl = moodle_url::make_pluginfile_url(
+ $usersfile->get_contextid(),
+ $usersfile->get_component(),
+ $usersfile->get_filearea(),
+ $usersfile->get_itemid(),
+ $usersfile->get_filepath(),
+ $usersfile->get_filename()
+ );
+ echo html_writer::div(
+ html_writer::link($usersfileurl, get_string('downloadusersfile', 'tool_generator'))
+ );
+
+} else {
+ // Display form.
+ $mform->display();
+}
+
+echo $OUTPUT->footer();
diff --git a/admin/tool/generator/settings.php b/admin/tool/generator/settings.php
index 39a40aeced8..f1f2350fe3a 100644
--- a/admin/tool/generator/settings.php
+++ b/admin/tool/generator/settings.php
@@ -25,8 +25,12 @@
defined('MOODLE_INTERNAL') || die;
if ($hassiteconfig) {
- $ADMIN->add('development', new admin_externalpage('toolgenerator',
+ $ADMIN->add('development', new admin_externalpage('toolgeneratorcourse',
get_string('maketestcourse', 'tool_generator'),
$CFG->wwwroot . '/' . $CFG->admin . '/tool/generator/maketestcourse.php'));
+
+ $ADMIN->add('development', new admin_externalpage('toolgeneratortestplan',
+ get_string('maketestplan', 'tool_generator'),
+ $CFG->wwwroot . '/' . $CFG->admin . '/tool/generator/maketestplan.php'));
}
diff --git a/admin/tool/generator/testplan.template.jmx b/admin/tool/generator/testplan.template.jmx
new file mode 100644
index 00000000000..2bfdaf45530
--- /dev/null
+++ b/admin/tool/generator/testplan.template.jmx
@@ -0,0 +1,885 @@
+
+
+
+
+
+ false
+ true
+
+
+
+ runtimestamp
+ ${__time()}
+ =
+
+
+ size
+ {{SIZE_PLACEHOLDER}}
+ =
+
+
+ host
+ {{HOST_PLACEHOLDER}}
+ =
+
+
+ sitepath
+ {{SITEPATH_PLACEHOLDER}}
+ =
+
+
+ courseid
+ {{COURSEID_PLACEHOLDER}}
+ =
+
+
+ pageactivityid
+ {{PAGEACTIVITYID_PLACEHOLDER}}
+ =
+
+
+ forumactivityid
+ {{FORUMACTIVITYID_PLACEHOLDER}}
+ =
+
+
+ forumdiscussionid
+ {{FORUMDISCUSSIONID_PLACEHOLDER}}
+ =
+
+
+ forumreplyid
+ {{FORUMREPLYID_PLACEHOLDER}}
+ =
+
+
+
+
+
+
+
+ all active threads (shared)
+ ${__property(throughput,throughput,120.0)}
+
+
+
+ Used to fill the caches, logs in every user
+ continue
+
+ false
+ 1
+
+ ${__P(users,{{USERS_PLACEHOLDER}})}
+ ${__P(rampup,{{RAMPUP_PLACEHOLDER}})}
+ 1378187955000
+ 1378187955000
+ false
+
+
+
+
+
+
+
+
+ ${host}
+
+
+
+
+
+ ${sitepath}
+ 4
+
+
+
+ ,
+
+ ${__P(usersfile,YOU_FORGOT_TO_SPECIFY_USERS_CSV_FILE.csv)}
+ false
+ true
+ All threads
+ false
+ username,password
+
+
+
+ true
+ 1
+
+
+
+
+ true
+ rfc2109
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GET
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+ false
+ ${username}
+ =
+ true
+ username
+
+
+ false
+ ${password}
+ =
+ true
+ password
+
+
+
+
+
+
+
+
+
+ ${sitepath}/login/index.php
+ POST
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GET
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+ false
+ ${courseid}
+ =
+ true
+ id
+
+
+
+
+
+
+
+
+
+ ${sitepath}/course/view.php
+ GET
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+ false
+ SESSION_SESSKEY
+ sesskey=([^"]+)"
+ $1$
+
+ 2
+ all
+
+
+
+
+
+
+
+ false
+ ${SESSION_SESSKEY}
+ =
+ true
+ sesskey
+
+
+
+
+
+
+
+
+
+ ${sitepath}/login/logout.php
+ GET
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+ continue
+
+ false
+ ${__property(loops,loops,{{LOOPS_PLACEHOLDER}})}
+
+ ${__property(users,users,{{USERS_PLACEHOLDER}})}
+ ${__property(rampup,rampup,{{RAMPUP_PLACEHOLDER}})}
+ 1376636813000
+ 1376636813000
+ false
+
+
+
+
+
+
+
+
+ ${host}
+
+
+
+
+
+ ${sitepath}
+ 4
+
+
+
+ ,
+
+ ${__P(usersfile,YOU_FORGOT_TO_SPECIFY_USERS_CSV_FILE.csv)}
+ false
+ true
+ All threads
+ false
+ username,password
+
+
+
+ true
+ 1
+
+
+
+
+ true
+ rfc2109
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GET
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+ false
+ ${username}
+ =
+ true
+ username
+
+
+ false
+ ${password}
+ =
+ true
+ password
+
+
+
+
+
+
+
+
+
+ ${sitepath}/login/index.php
+ POST
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+ <div class="logininfo">You are logged in as
+
+ Assertion.response_data
+ false
+ 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GET
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+ false
+ ${courseid}
+ =
+ true
+ id
+
+
+
+
+
+
+
+
+
+ ${sitepath}/course/view.php
+ GET
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+
+ false
+ ${pageactivityid}
+ =
+ true
+ id
+
+
+
+
+
+
+
+
+
+ ${sitepath}/mod/page/view.php
+ GET
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+ false
+ ${courseid}
+ =
+ true
+ id
+
+
+
+
+
+
+
+
+
+ ${sitepath}/course/view.php
+ GET
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+ false
+ ${forumactivityid}
+ =
+ true
+ id
+
+
+
+
+
+
+
+
+
+ ${sitepath}/mod/forum/view.php
+ GET
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+ false
+ ${forumdiscussionid}
+ =
+ true
+ d
+
+
+
+
+
+
+
+
+
+ ${sitepath}/mod/forum/discuss.php
+ GET
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+ false
+ ${forumreplyid}
+ =
+ true
+ reply
+
+
+
+
+
+
+
+
+
+ ${sitepath}/mod/forum/post.php
+ GET
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+ false
+ SESSION_USERID
+ name="userid"\stype="hidden"\svalue="(\d+)"
+ $1$
+ 0
+ 1
+
+
+
+ false
+ SESSION_SESSKEY
+ name="sesskey"\stype="hidden"\svalue="([^"]+)"
+ $1$
+ 0
+ 1
+
+
+
+ false
+ SESSION_FORUMFORMATTACHMENTS
+ value="(\d+)"\sname="attachments"\stype="hidden"
+ $1$
+ 0
+ 1
+
+
+
+ false
+ SESSION_FORUMFORMITEMID
+ type="hidden"\sname="message\[itemid\]"\svalue="(\d+)"
+ $1$
+ 0
+ 1
+
+
+
+
+
+
+
+ false
+ ${courseid}
+ =
+ true
+ course
+
+
+ false
+ 0
+ =
+ true
+ forum
+
+
+ false
+ ${forumdiscussionid}
+ =
+ true
+ discussion
+
+
+ false
+ ${SESSION_USERID}
+ =
+ true
+ userid
+
+
+ false
+ 0
+ =
+ true
+ groupid
+
+
+ false
+ 0
+ =
+ true
+ edit
+
+
+ false
+ ${forumreplyid}
+ =
+ true
+ reply
+
+
+ false
+ ${SESSION_SESSKEY}
+ =
+ true
+ sesskey
+
+
+ false
+ 1
+ =
+ true
+ _qf__mod_forum_post_form
+
+
+ false
+ Re: I am the test plan reply subject
+ =
+ true
+ subject
+
+
+ false
+ ${SESSION_FORUMFORMITEMID}
+ =
+ true
+ message[itemid]
+
+
+ false
+ 1
+ =
+ true
+ message[format]
+
+
+ false
+ I am the test plan reply message
+ =
+ true
+ message[text]
+
+
+ false
+ ${forumreplyid}
+ =
+ true
+ parent
+
+
+ false
+ 1
+ =
+ true
+ subscribe
+
+
+ false
+ ${SESSION_FORUMFORMATTACHMENTS}
+ =
+ true
+ attachments
+
+
+ false
+ 0
+ =
+ true
+ timestart
+
+
+ false
+ 0
+ =
+ true
+ timeend
+
+
+ false
+ Post to forum
+ =
+ true
+ submitbutton
+
+
+
+
+
+
+
+
+
+ ${sitepath}/mod/forum/post.php
+ POST
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+ false
+ ${courseid}
+ =
+ true
+ id
+
+
+
+
+
+
+
+
+
+ ${sitepath}/course/view.php
+ GET
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+ false
+ ${courseid}
+ =
+ true
+ id
+
+
+
+
+
+
+
+
+
+ ${sitepath}/user/index.php
+ GET
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+
+
+ false
+ ${SESSION_SESSKEY}
+ =
+ true
+ sesskey
+
+
+
+
+
+
+
+
+
+ ${sitepath}/login/logout.php
+ GET
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+
+ recorder.bsf
+
+ false
+
+
+
+
+ false
+
+ saveConfig
+
+
+ true
+ true
+ true
+
+ true
+ true
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ true
+ false
+ false
+ false
+ false
+ false
+ 0
+ true
+
+
+ runs_samples/data.${runtimestamp}.jtl
+
+
+
+
+
+
diff --git a/admin/tool/generator/version.php b/admin/tool/generator/version.php
index cc8744f3877..10f30beb932 100644
--- a/admin/tool/generator/version.php
+++ b/admin/tool/generator/version.php
@@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2013091200;
+$plugin->version = 2013091201;
$plugin->requires = 2013090200;
$plugin->component = 'tool_generator';
diff --git a/config-dist.php b/config-dist.php
index 59830d9862b..e57a61dff9c 100644
--- a/config-dist.php
+++ b/config-dist.php
@@ -640,6 +640,20 @@ $CFG->admin = 'admin';
// used to expand the default white list with an array of extra settings.
// Example:
// $CFG->behat_extraallowedsettings = array('logsql', 'dblogerror');
+//
+//=========================================================================
+// 12. DEVELOPER DATA GENERATOR
+//=========================================================================
+//
+// The developer data generator tool is intended to be used only in development or testing sites and
+// it's usage in production environments is not recommended; if it is used to create JMeter test plans
+// is even less recommended as JMeter needs to log in as site course users. JMeter needs to know the
+// users passwords but would be dangerous to have a default password as everybody would know it, which would
+// be specially dangerouse if somebody uses this tool in a production site, so in order to prevent unintended
+// uses of the tool and undesired accesses as well, is compulsory to set a password for the users
+// generated by this tool, but only in case you want to generate a JMeter test. The value should be a string.
+// Example:
+// $CFG->tool_generator_users_password = 'examplepassword';
//=========================================================================
// ALL DONE! To continue installation, visit your main page with a browser