MDL-41421 tool_generator: Adding a test plan generator to feed JMeter
authorDavid Monllao <davidm@moodle.com>
Wed, 28 Aug 2013 08:50:00 +0000 (16:50 +0800)
committerDavid Monllao <davidm@moodle.com>
Wed, 25 Sep 2013 01:19:11 +0000 (09:19 +0800)
Also changing course_backend.php coding style
according to codechecker.

admin/tool/generator/classes/course_backend.php
admin/tool/generator/classes/make_course_form.php
admin/tool/generator/classes/make_testplan_form.php [new file with mode: 0644]
admin/tool/generator/classes/testplan_backend.php [new file with mode: 0644]
admin/tool/generator/lang/en/tool_generator.php
admin/tool/generator/lib.php [new file with mode: 0644]
admin/tool/generator/maketestcourse.php
admin/tool/generator/maketestplan.php [new file with mode: 0644]
admin/tool/generator/settings.php
admin/tool/generator/testplan.template.jmx
config-dist.php

index 5171b83..28eda17 100644 (file)
@@ -328,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);
@@ -385,7 +385,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;
@@ -463,13 +463,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);
index 7f7f629..5c96515 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+/**
+ * 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');
@@ -60,7 +68,7 @@ class tool_generator_make_course_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 (file)
index 0000000..b4c05e0
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * 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, $DB;
+
+        $errors = array();
+        if (empty($CFG->tool_generator_users_password) || is_bool($CFG->tool_generator_users_password)) {
+            $errors['updateuserspassword'] = get_string('error_nouserspassword', 'tool_generator');
+        }
+
+        // Check the course has users.
+        // Better to repeat here the query than to do it afterwards and end up with an exception.
+        $coursecontext = context_course::instance($data['courseid']);
+        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[$data['size']]) {
+            $errors['size'] = get_string('notenoughusers', 'tool_generator');
+        }
+
+        return $errors;
+    }
+
+}
diff --git a/admin/tool/generator/classes/testplan_backend.php b/admin/tool/generator/classes/testplan_backend.php
new file mode 100644 (file)
index 0000000..af1b743
--- /dev/null
@@ -0,0 +1,273 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * 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 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;
+    }
+
+    /**
+     * 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;
+    }
+
+}
index a204ce8..71d3741 100644 (file)
  */
 
 $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,38 @@ $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.
+
+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 as:
+
+* It changes the database contents
+* Logins lots of users at the same time so there is risk that your server can not handle the load and dies
+* It can update the course users passwords
+
+';
+$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 (file)
index 0000000..b9b5efb
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * 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.
+    if (!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);
+}
+
index c5f9c82..a7b760b 100644 (file)
@@ -15,8 +15,7 @@
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * 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
diff --git a/admin/tool/generator/maketestplan.php b/admin/tool/generator/maketestplan.php
new file mode 100644 (file)
index 0000000..5adef0c
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * 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();
+$repourl = 'https://github.com/moodlehq/moodle-performance-comparison';
+$markdownlink = '[' . $repourl . '](' . $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();
index 39a40ae..f1f2350 100644 (file)
 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'));
 }
 
index d08a9b5..2bfdaf4 100644 (file)
             <stringProp name="Argument.value">${__time()}</stringProp>
             <stringProp name="Argument.metadata">=</stringProp>
           </elementProp>
-          <elementProp name="users" elementType="Argument">
-            <stringProp name="Argument.name">users</stringProp>
-            <stringProp name="Argument.value">{{USERS_PLACEHOLDER}}</stringProp>
-            <stringProp name="Argument.metadata">=</stringProp>
-          </elementProp>
-          <elementProp name="loops" elementType="Argument">
-            <stringProp name="Argument.name">loops</stringProp>
-            <stringProp name="Argument.value">{{LOOPS_PLACEHOLDER}}</stringProp>
-            <stringProp name="Argument.metadata">=</stringProp>
-          </elementProp>
-          <elementProp name="rampup" elementType="Argument">
-            <stringProp name="Argument.name">rampup</stringProp>
-            <stringProp name="Argument.value">{{RAMPUP_PLACEHOLDER}}</stringProp>
+          <elementProp name="size" elementType="Argument">
+            <stringProp name="Argument.name">size</stringProp>
+            <stringProp name="Argument.value">{{SIZE_PLACEHOLDER}}</stringProp>
             <stringProp name="Argument.metadata">=</stringProp>
           </elementProp>
           <elementProp name="host" elementType="Argument">
@@ -62,7 +52,6 @@
             <stringProp name="Argument.value">{{FORUMREPLYID_PLACEHOLDER}}</stringProp>
             <stringProp name="Argument.metadata">=</stringProp>
           </elementProp>
-
         </collectionProp>
       </elementProp>
       <stringProp name="TestPlan.user_define_classpath"></stringProp>
     <hashTree>
       <ConstantThroughputTimer guiclass="TestBeanGUI" testclass="ConstantThroughputTimer" testname="Samples per minute" enabled="true">
         <stringProp name="calcMode">all active threads (shared)</stringProp>
-        <doubleProp>
-          <name>throughput</name>
-          <value>120.0</value>
-          <savedValue>0.0</savedValue>
-        </doubleProp>
+        <stringProp name="throughput">${__property(throughput,throughput,120.0)}</stringProp>
       </ConstantThroughputTimer>
       <hashTree/>
       <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Warm-up site" enabled="true">
@@ -84,8 +69,8 @@
           <boolProp name="LoopController.continue_forever">false</boolProp>
           <stringProp name="LoopController.loops">1</stringProp>
         </elementProp>
-        <stringProp name="ThreadGroup.num_threads">${users}</stringProp>
-        <stringProp name="ThreadGroup.ramp_time">${rampup}</stringProp>
+        <stringProp name="ThreadGroup.num_threads">${__P(users,{{USERS_PLACEHOLDER}})}</stringProp>
+        <stringProp name="ThreadGroup.ramp_time">${__P(rampup,{{RAMPUP_PLACEHOLDER}})}</stringProp>
         <longProp name="ThreadGroup.start_time">1378187955000</longProp>
         <longProp name="ThreadGroup.end_time">1378187955000</longProp>
         <boolProp name="ThreadGroup.scheduler">false</boolProp>
         <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
         <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
           <boolProp name="LoopController.continue_forever">false</boolProp>
-          <stringProp name="LoopController.loops">${loops}</stringProp>
+          <stringProp name="LoopController.loops">${__property(loops,loops,{{LOOPS_PLACEHOLDER}})}</stringProp>
         </elementProp>
-        <stringProp name="ThreadGroup.num_threads">${users}</stringProp>
-        <stringProp name="ThreadGroup.ramp_time">${rampup}</stringProp>
+        <stringProp name="ThreadGroup.num_threads">${__property(users,users,{{USERS_PLACEHOLDER}})}</stringProp>
+        <stringProp name="ThreadGroup.ramp_time">${__property(rampup,rampup,{{RAMPUP_PLACEHOLDER}})}</stringProp>
         <longProp name="ThreadGroup.start_time">1376636813000</longProp>
         <longProp name="ThreadGroup.end_time">1376636813000</longProp>
         <boolProp name="ThreadGroup.scheduler">false</boolProp>
index 0b2a9b1..87fa327 100644 (file)
@@ -620,6 +620,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