MDL-42151 behat: Adding the version to the testplan
[moodle.git] / admin / tool / generator / classes / testplan_backend.php
CommitLineData
1325d493
DM
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Test plan generator.
19 *
20 * @package tool_generator
21 * @copyright 2013 David MonllaĆ³
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25defined('MOODLE_INTERNAL') || die();
26
27/**
28 * Generates the files required by JMeter.
29 *
30 * @package tool_generator
31 * @copyright 2013 David MonllaĆ³
32 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
33 */
34class tool_generator_testplan_backend extends tool_generator_backend {
35
35aa0182
DM
36 /**
37 * @var The URL to the repository of the external project.
38 */
39 protected static $repourl = 'https://github.com/moodlehq/moodle-performance-comparison';
40
1325d493
DM
41 /**
42 * @var Number of users depending on the selected size.
43 */
44 protected static $users = array(1, 30, 200, 1000, 5000, 10000);
45
46 /**
47 * @var Number of loops depending on the selected size.
48 */
49 protected static $loops = array(1, 1, 2, 3, 3, 5);
50
51 /**
52 * @var Rampup period depending on the selected size.
53 */
54 protected static $rampups = array(1, 6, 40, 100, 500, 800);
55
56 /**
57 * Gets a list of size choices supported by this backend.
58 *
59 * @return array List of size (int) => text description for display
60 */
61 public static function get_size_choices() {
62
63 $options = array();
64 for ($size = self::MIN_SIZE; $size <= self::MAX_SIZE; $size++) {
65 $a = new stdClass();
66 $a->users = self::$users[$size];
67 $a->loops = self::$loops[$size];
68 $a->rampup = self::$rampups[$size];
69 $options[$size] = get_string('testplansize_' . $size, 'tool_generator', $a);
70 }
71 return $options;
72 }
73
74 /**
75 * Gets the list of courses that can be used used to generate a test.
76 *
77 * @return array The list of options as courseid => name
78 */
79 public static function get_course_options() {
80 $courses = get_courses('all', 'c.sortorder ASC', 'c.id, c.shortname, c.fullname');
81 if (!$courses) {
82 print_error('error_nocourses', 'tool_generator');
83 }
84
85 $options = array();
86 unset($courses[1]);
87 foreach ($courses as $course) {
88 $options[$course->id] = $course->fullname . '(' . $course->shortname . ')';
89 }
90 return $options;
91 }
92
35aa0182
DM
93 /**
94 * Getter for moodle-performance-comparison project URL.
95 *
96 * @return string
97 */
98 public static function get_repourl() {
99 return self::$repourl;
100 }
101
1325d493
DM
102 /**
103 * Creates the test plan file.
104 *
105 * @param int $courseid The target course id
106 * @param int $size The test plan size
107 * @return stored_file
108 */
109 public static function create_testplan_file($courseid, $size) {
110 $jmxcontents = self::generate_test_plan($courseid, $size);
111
112 $fs = get_file_storage();
113 $filerecord = self::get_file_record('testplan', 'jmx');
114 return $fs->create_file_from_string($filerecord, $jmxcontents);
115 }
116
117 /**
118 * Creates the users data file.
119 *
120 * @param int $courseid The target course id
121 * @param bool $updateuserspassword Updates the course users password to $CFG->tool_generator_users_password
122 * @return stored_file
123 */
124 public static function create_users_file($courseid, $updateuserspassword) {
125 $csvcontents = self::generate_users_file($courseid, $updateuserspassword);
126
127 $fs = get_file_storage();
128 $filerecord = self::get_file_record('users', 'csv');
129 return $fs->create_file_from_string($filerecord, $csvcontents);
130 }
131
132 /**
133 * Generates the test plan according to the target course contents.
134 *
135 * @param int $targetcourseid The target course id
136 * @param int $size The test plan size
137 * @return string The test plan as a string
138 */
139 protected static function generate_test_plan($targetcourseid, $size) {
140 global $CFG;
141
142 // Getting the template.
143 $template = file_get_contents(__DIR__ . '/../testplan.template.jmx');
144
145 // Getting the course modules data.
146 $coursedata = self::get_course_test_data($targetcourseid);
147
148 // Host and path to the site.
149 $urlcomponents = parse_url($CFG->wwwroot);
150 if (empty($urlcomponents['path'])) {
151 $urlcomponents['path'] = '';
152 }
153
154 $replacements = array(
9a79e456 155 $CFG->version,
1325d493
DM
156 self::$users[$size],
157 self::$loops[$size],
158 self::$rampups[$size],
159 $urlcomponents['host'],
160 $urlcomponents['path'],
161 get_string('shortsize_' . $size, 'tool_generator'),
162 $targetcourseid,
163 $coursedata->pageid,
164 $coursedata->forumid,
165 $coursedata->forumdiscussionid,
166 $coursedata->forumreplyid
167 );
168
169 $placeholders = array(
9a79e456 170 '{{MOODLEVERSION_PLACEHOLDER}}',
1325d493
DM
171 '{{USERS_PLACEHOLDER}}',
172 '{{LOOPS_PLACEHOLDER}}',
173 '{{RAMPUP_PLACEHOLDER}}',
174 '{{HOST_PLACEHOLDER}}',
175 '{{SITEPATH_PLACEHOLDER}}',
176 '{{SIZE_PLACEHOLDER}}',
177 '{{COURSEID_PLACEHOLDER}}',
178 '{{PAGEACTIVITYID_PLACEHOLDER}}',
179 '{{FORUMACTIVITYID_PLACEHOLDER}}',
180 '{{FORUMDISCUSSIONID_PLACEHOLDER}}',
181 '{{FORUMREPLYID_PLACEHOLDER}}'
182 );
183
184 // Fill the template with the target course values.
185 return str_replace($placeholders, $replacements, $template);
186 }
187
188 /**
189 * Generates the user's credentials file with all the course's users
190 *
191 * @param int $targetcourseid
192 * @param bool $updateuserspassword Updates the course users password to $CFG->tool_generator_users_password
193 * @return string The users csv file contents.
194 */
195 protected static function generate_users_file($targetcourseid, $updateuserspassword) {
196 global $CFG;
197
198 $coursecontext = context_course::instance($targetcourseid);
199
200 $users = get_enrolled_users($coursecontext, '', 0, 'u.id, u.username, u.auth', 'u.username ASC');
201 if (!$users) {
202 print_error('coursewithoutusers', 'tool_generator');
203 }
204
205 $lines = array();
206 foreach ($users as $user) {
207
208 // Updating password to the one set in config.php.
209 if ($updateuserspassword) {
210 $userauth = get_auth_plugin($user->auth);
211 if (!$userauth->user_update_password($user, $CFG->tool_generator_users_password)) {
212 print_error('errorpasswordupdate', 'auth');
213 }
214 }
215
216 // Here we already checked that $CFG->tool_generator_users_password is not null.
217 $lines[] = $user->username . ',' . $CFG->tool_generator_users_password;
218 }
219
220 return implode(PHP_EOL, $lines);
221 }
222
223 /**
224 * Returns a tool_generator file record
225 *
226 * @param string $filearea testplan or users
227 * @param string $filetype The file extension jmx or csv
228 * @return stdClass The file record to use when creating tool_generator files
229 */
230 protected static function get_file_record($filearea, $filetype) {
231
232 $systemcontext = context_system::instance();
233
234 $filerecord = new stdClass();
235 $filerecord->contextid = $systemcontext->id;
236 $filerecord->component = 'tool_generator';
237 $filerecord->filearea = $filearea;
238 $filerecord->itemid = 0;
239 $filerecord->filepath = '/';
240
241 // Random generated number to avoid concurrent execution problems.
242 $filerecord->filename = $filearea . '_' . date('YmdHi', time()) . '_' . rand(1000, 9999) . '.' . $filetype;
243
244 return $filerecord;
245 }
246
247 /**
248 * Gets the data required to fill the test plan template with the database contents.
249 *
250 * @param int $targetcourseid The target course id
251 * @return stdClass The ids required by the test plan
252 */
253 protected static function get_course_test_data($targetcourseid) {
254 global $DB, $USER;
255
256 $data = new stdClass();
257
258 // Getting course contents info as the current user (will be an admin).
259 $course = new stdClass();
260 $course->id = $targetcourseid;
261 $courseinfo = new course_modinfo($course, $USER->id);
262
263 // Getting the first page module instance.
264 if (!$pages = $courseinfo->get_instances_of('page')) {
265 print_error('error_nopageinstances', 'tool_generator');
266 }
267 $data->pageid = reset($pages)->id;
268
269 // Getting the first forum module instance and it's first discussion and reply as well.
270 if (!$forums = $courseinfo->get_instances_of('forum')) {
271 print_error('error_noforuminstances', 'tool_generator');
272 }
273 $forum = reset($forums);
274
275 // Getting the first discussion (and reply).
276 if (!$discussions = forum_get_discussions($forum, 'd.timemodified ASC', false, -1, 1)) {
277 print_error('error_noforumdiscussions', 'tool_generator');
278 }
279 $discussion = reset($discussions);
280
281 $data->forumid = $forum->id;
282 $data->forumdiscussionid = $discussion->discussion;
283 $data->forumreplyid = $discussion->id;
284
285 // According to the current test plan.
286 return $data;
287 }
288
35aa0182
DM
289 /**
290 * Checks if the selected target course is ok.
291 *
292 * @param int|string $course
293 * @param int $size
294 * @return array Errors array or false if everything is ok
295 */
296 public static function has_selected_course_any_problem($course, $size) {
297 global $DB;
298
299 $errors = array();
300
301 if (!is_numeric($course)) {
302 if (!$course = $DB->get_field('course', 'id', array('shortname' => $course))) {
303 $errors['courseid'] = get_string('error_nonexistingcourse', 'tool_generator');
304 return $errors;
305 }
306 }
307
308 $coursecontext = context_course::instance($course, IGNORE_MISSING);
309 if (!$coursecontext) {
310 $errors['courseid'] = get_string('error_nonexistingcourse', 'tool_generator');
311 return $errors;
312 }
313
314 if (!$users = get_enrolled_users($coursecontext, '', 0, 'u.id')) {
315 $errors['courseid'] = get_string('coursewithoutusers', 'tool_generator');
316 }
317
318 // Checks that the selected course has enough users.
319 $coursesizes = tool_generator_course_backend::get_users_per_size();
320 if (count($users) < $coursesizes[$size]) {
321 $errors['size'] = get_string('notenoughusers', 'tool_generator');
322 }
323
324 if (empty($errors)) {
325 return false;
326 }
327
328 return $errors;
329 }
1325d493 330}