Best Phoronix-test-suite code snippet using phodevi.sensor_object_identifier
system_monitor.php
Source:system_monitor.php
...217 {218 foreach($sensor_list as $sensor)219 {220 $sensor_value = phodevi::read_sensor($sensor);221 if($sensor_value != -1 && pts_module::is_file('logs/' . phodevi::sensor_object_identifier($sensor)))222 {223 pts_module::save_file('logs/' . phodevi::sensor_object_identifier($sensor), $sensor_value, true);224 }225 }226 }227 private static function parse_monitor_log($log_file, $start_offset = 0, $end_offset = -1)228 {229 $log_f = pts_module::read_file($log_file);230 $line_breaks = explode(PHP_EOL, $log_f);231 $results = array();232 for($i = 0; $i < $start_offset && isset($line_breaks[$i]); $i++)233 {234 unset($line_breaks[$i]);235 }236 foreach($line_breaks as $line_number => $line)237 {238 if($end_offset != -1 && $line_number >= $end_offset)239 {240 break;241 }242 $line = trim($line);243 if(!empty($line) && $line >= 0)244 {245 $results[] = $line;246 }247 }248 if(count($results) > 0 && max($results) == 0)249 {250 $results = array();251 }252 return $results;253 }254 private static function monitor_arguments()255 {256 $args = array('all');257 foreach(phodevi::available_sensors() as $sensor)258 {259 $supported_devices = call_user_func(array($sensor[2], 'get_supported_devices'));260 if(!in_array('all.' . $sensor[0], $args))261 {262 $args[] = 'all.' . $sensor[0];263 }264 $args[] = phodevi::sensor_identifier($sensor);265 if($supported_devices !== NULL)266 {267 $args[] = 'all.' . phodevi::sensor_identifier($sensor);268 foreach($supported_devices as $device)269 {270 $args[] = phodevi::sensor_identifier($sensor) . '.' . $device;271 }272 }273 }274 return $args;275 }276 // Prevents system monitor from running when results are not saved to a file.277 private static function check_if_results_saved(&$test_run_manager)278 {279 if(!$test_run_manager->do_save_results())280 {281 throw new Exception('results not saved to a file');282 }283 }284 // Parse environmental variable containing parameters of monitored sensors.285 private static function prepare_sensor_parameters()286 {287 $sensor_list = pts_strings::comma_explode(pts_module::read_variable('MONITOR'));288 $to_monitor = array();289 foreach($sensor_list as $sensor)290 {291 $sensor_split = pts_strings::trim_explode('.', $sensor);292 // Set 'all' from the beginning (eg. all.cpu.frequency) as the last293 // element (cpu.frequency.all). As sensor parameters are also supported294 // now, it's handy to mark that we want to include all sensors of specified295 // type (cpu.all) or just all supported parameters of specified sensor296 // (cpu.frequency.all).297 if($sensor_split[0] === 'all')298 {299 $sensor_split[] = 'all';300 array_shift($sensor_split);301 }302 $type = &$sensor_split[0];303 $name = &$sensor_split[1];304 $parameter = &$sensor_split[2];305 if(empty($to_monitor[$type][$name]))306 {307 $to_monitor[$type][$name] = array();308 }309 if($parameter !== NULL)310 {311 $to_monitor[$type][$name][] = $parameter;312 }313 }314 return $to_monitor;315 }316 private static function enable_perf_per_watt(&$sensor_parameters)317 {318 if(pts_module::read_variable('PERFORMANCE_PER_WATT'))319 {320 // We need to ensure the system power consumption is being tracked to get performance-per-Watt321 if(empty($sensor_parameters['sys']['power']))322 {323 $sensor_parameters['sys']['power'] = array();324 }325 self::$perf_per_watt_collection = array();326 self::$individual_monitoring = true;327 echo PHP_EOL . 'To Provide Performance-Per-Watt Outputs.' . PHP_EOL;328 }329 }330 // Create sensor objects basing on the sensor parameter array.331 private static function process_sensor_list(&$sensor_parameters)332 {333 $monitor_all = array_key_exists('all', $sensor_parameters);334 foreach(phodevi::supported_sensors() as $sensor)335 {336 // instantiate sensor class if:337 // a) we want to monitor all the available sensors,338 // b) we want to monitor all the available sensors of the specified type,339 // c) sensor type and name was passed in an environmental variable340 // ($sensor[0] is the type, $sensor[1] is the name, $sensor[2] is the class name)341 $sensor_type_exists = array_key_exists($sensor[0], $sensor_parameters);342 $sensor_name_exists = $sensor_type_exists && array_key_exists($sensor[1], $sensor_parameters[$sensor[0]]);343 $monitor_all_of_this_type = $sensor_type_exists && array_key_exists('all', $sensor_parameters[$sensor[0]]);344 $monitor_all_of_this_sensor = $sensor_type_exists && $sensor_name_exists345 && in_array('all', $sensor_parameters[$sensor[0]][$sensor[1]]);346 $is_cgroup_sensor = $sensor[0] === 'cgroup';347 if(($monitor_all && !$is_cgroup_sensor) || $monitor_all_of_this_type || $sensor_name_exists )348 {349 // in some cases we want to create objects representing every possible device supported by the sensor350 $create_all = $monitor_all || $monitor_all_of_this_type || $monitor_all_of_this_sensor;351 self::create_sensor_instances($sensor, $sensor_parameters, $create_all);352 }353 }354 if(count(self::$to_monitor) == 0)355 {356 throw new Exception('No Supported Sensors Selected To Monitor');357 sleep(2);358 }359 }360 private static function create_sensor_instances(&$sensor, &$sensor_parameters, $create_all)361 {362 if($create_all)363 {364 self::create_all_sensor_instances($sensor);365 return;366 }367 $sensor_instances = $sensor_parameters[$sensor[0]][$sensor[1]];368 // If no instances specified, create one with default parameters.369 if(empty($sensor_instances) )370 {371 self::create_single_sensor_instance($sensor, 0, NULL);372 return;373 }374 // Create objects for all specified instances of the sensor.375 foreach($sensor_instances as $instance => $param)376 {377 self::create_single_sensor_instance($sensor, $instance, $param);378 }379 }380 // Create instances for all of the devices supported by specified sensor.381 private static function create_all_sensor_instances(&$sensor)382 {383 $supported_devices = call_user_func(array($sensor[2], 'get_supported_devices'));384 $instance_no = 0;385 if($supported_devices === NULL)386 {387 self::create_single_sensor_instance($sensor, 0, NULL);388 return;389 }390 foreach($supported_devices as $device)391 {392 self::create_single_sensor_instance($sensor, $instance_no++, $device);393 }394 }395 // Create sensor object if parameters passed to it are correct.396 private static function create_single_sensor_instance($sensor, $instance, $param)397 {398 if($sensor[0] === 'cgroup')399 {400 $cgroup_controller = call_user_func(array($sensor[2], 'get_cgroup_controller'));401 pts_arrays::unique_push(self::$cgroup_enabled_controllers, $cgroup_controller );402 self::cgroup_create(self::$cgroup_name, $cgroup_controller);403 $param = self::$cgroup_name;404 }405 if(call_user_func(array($sensor[2], 'parameter_check'), $param) === true)406 {407 $sensor_object = new $sensor[2]($instance, $param);408 self::$to_monitor[] = $sensor_object;409 pts_module::save_file('logs/' . phodevi::sensor_object_identifier($sensor_object));410 }411 }412 // Create cgroups in all of the needed controllers.413 private static function create_monitoring_cgroups()414 {415 foreach(self::$cgroup_enabled_controllers as $controller)416 {417 self::cgroup_create(self::$cgroup_name, $controller);418 }419 }420 private static function print_monitored_sensors()421 {422 echo PHP_EOL . 'Sensors To Be Logged:';423 foreach(self::$to_monitor as &$sensor)424 {425 echo PHP_EOL . ' - ' . phodevi::sensor_object_name($sensor);426 }427 echo PHP_EOL;428 }429 private static function set_monitoring_interval()430 {431 if(pts_module::read_variable('MONITOR_INTERVAL') != null)432 {433 $proposed_interval = pts_module::read_variable('MONITOR_INTERVAL');434 if(is_numeric($proposed_interval) && $proposed_interval >= 0.5)435 {436 self::$sensor_monitoring_frequency = $proposed_interval;437 }438 }439 }440 private static function cgroup_create($cgroup_name, $cgroup_controller)441 {442 //TODO if we allow custom cgroup names, we will have to add cgroup443 //name checking ("../../../etc" isn't a sane name)444 $sudo_cmd = PTS_CORE_STATIC_PATH . 'root-access.sh ';445 $cgroup_path = '/sys/fs/cgroup/' . $cgroup_controller . '/' . $cgroup_name;446 $return_val = null;447 if(!is_dir($cgroup_path)) // cgroup filesystem doesn't allow to create regular files anyway448 {449 $current_user = exec('whoami');450 $mkdir_cmd = 'mkdir ' . $cgroup_path;451 $chmod_cmd = 'chown ' . $current_user . ' ' . $cgroup_path . '/tasks';452 $command = $sudo_cmd . '"' . $mkdir_cmd . ' && ' . $chmod_cmd . '"';453 exec($command);454 }455 if(!is_writable($cgroup_path . '/tasks'))456 {457 throw new Exception('could not create cgroups');458 }459 }460 private static function cgroup_remove($cgroup_name, $cgroup_controller)461 {462 $sudo_cmd = PTS_CORE_STATIC_PATH . 'root-access.sh ';463 $cgroup_path = '/sys/fs/cgroup/' . $cgroup_controller . '/' . $cgroup_name;464 if(is_dir($cgroup_path)) // cgroup filesystem doesn't allow to create regular files anyway465 {466 $rmdir_cmd = 'rmdir ' . $cgroup_path;467 shell_exec($sudo_cmd . $rmdir_cmd);468 }469 //TODO should probably return some result470 }471 // Saves offsets of sensor log files for the current test or for the specific test try.472 // As sensor monitoring results are saved to the single file during the whole testing process,473 // we need offset information to know where to start drawing chart for the individual test run from.474 private static function save_log_offsets($type)475 {476 foreach(self::$to_monitor as &$sensor)477 {478 $log_f = pts_module::read_file('logs/' . phodevi::sensor_object_identifier($sensor));479 $offset = count(explode(PHP_EOL, $log_f)) - 1; // as log file ends with an empty line480 if($type === 'try')481 {482 self::$test_run_tries_offsets[phodevi::sensor_object_identifier($sensor)][self::$test_run_try_number] = $offset;483 }484 else if($type === 'run')485 {486 self::$individual_test_run_offsets[phodevi::sensor_object_identifier($sensor)] = $offset;487 }488 }489 }490 private static function process_perf_per_watt(&$result_file)491 {492 $sensor = array('sys', 'power');493 $sensor_results = self::parse_monitor_log('logs/' . phodevi::sensor_identifier($sensor) . '.0', self::$individual_test_run_offsets[phodevi::sensor_identifier($sensor) . '.0']);494 if(count($sensor_results) > 2 && self::$successful_test_run_request)495 {496 // Copy the value each time as if you are directly writing the original data, each succeeding time in the loop the used arguments gets borked497 $test_result = clone self::$successful_test_run_request;498 $process_perf_per_watt = true;499 $watt_average = array_sum($sensor_results) / count($sensor_results);500 switch(phodevi::read_sensor_unit($sensor))501 {502 case 'Milliwatts':503 $watt_average = $watt_average / 1000;504 case 'Watts':505 break;506 default:507 $process_perf_per_watt = false;508 }509 if($process_perf_per_watt && $watt_average > 0 && $test_result->test_profile->get_display_format() == 'BAR_GRAPH')510 {511 $test_result->test_profile->set_identifier(null);512 //$test_result->set_used_arguments_description(phodevi::sensor_name('sys.power') . ' Monitor');513 //$test_result->set_used_arguments(phodevi::sensor_name('sys.power') . ' ' . $test_result->get_arguments());514 $test_result->test_result_buffer = new pts_test_result_buffer();515 if($test_result->test_profile->get_result_proportion() == 'HIB')516 {517 $test_result->test_profile->set_result_scale($test_result->test_profile->get_result_scale() . ' Per Watt');518 $test_result->test_result_buffer->add_test_result(self::$result_identifier, pts_math::set_precision($test_result->active->get_result() / $watt_average));519 $result_file->add_result($test_result);520 }521 else if($test_result->test_profile->get_result_proportion() == 'LIB')522 {523 $test_result->test_profile->set_result_proportion('HIB');524 $test_result->test_profile->set_result_scale('Performance Per Watt');525 $test_result->test_result_buffer->add_test_result(self::$result_identifier, pts_math::set_precision((1 / $test_result->active->get_result()) / $watt_average));526 $result_file->add_result($test_result);527 }528 self::$perf_per_watt_collection[] = $test_result->active->get_result();529 }530 }531 }532 // Saves average of perf-per-watt results to the result file.533 private static function process_perf_per_watt_collection(&$test_run_manager)534 {535 if(count(self::$perf_per_watt_collection) > 2)536 {537 // Performance per watt overall538 $avg = array_sum(self::$perf_per_watt_collection) / count(self::$perf_per_watt_collection);539 $test_profile = new pts_test_profile();540 $test_result = new pts_test_result($test_profile);541 $test_result->test_profile->set_test_title('Meta Performance Per Watt');542 $test_result->test_profile->set_identifier(null);543 $test_result->test_profile->set_version(null);544 $test_result->test_profile->set_result_proportion(null);545 $test_result->test_profile->set_display_format('BAR_GRAPH');546 $test_result->test_profile->set_result_scale('Performance Per Watt');547 $test_result->test_profile->set_result_proportion('HIB');548 $test_result->set_used_arguments_description('Performance Per Watt');549 $test_result->set_used_arguments('Per-Per-Watt');550 $test_result->test_result_buffer = new pts_test_result_buffer();551 $test_result->test_result_buffer->add_test_result(self::$result_identifier, pts_math::set_precision($avg));552 $test_run_manager->result_file->add_result($test_result);553 }554 }555 private static function process_test_run_results(&$sensor, &$result_file)556 {557 $result_buffer = new pts_test_result_buffer();558 if(self::$per_test_try_monitoring)559 {560 self::prepare_per_try_results($sensor, $result_buffer);561 }562 else563 {564 $sensor_results = self::parse_monitor_log('logs/' . phodevi::sensor_object_identifier($sensor),565 self::$individual_test_run_offsets[phodevi::sensor_object_identifier($sensor)]);566 if(count($sensor_results) > 0)567 {568 $result_buffer->add_test_result(self::$result_identifier, implode(',', $sensor_results), implode(',', $sensor_results));569 }570 }571 self::write_test_run_results($result_buffer, $result_file, $sensor);572 self::$individual_test_run_offsets[phodevi::sensor_object_identifier($sensor)] = array();573 self::$test_run_tries_offsets[phodevi::sensor_object_identifier($sensor)] = array();574 }575 private static function write_test_run_results(&$result_buffer, &$result_file, &$sensor)576 {577 // TODO result count checks should probably be done before cloning the test_result578 // Copy the value each time as if you are directly writing the original data, each succeeding time in the loop the used arguments gets borked579 if(!is_object(self::$individual_test_run_request))580 return;581 $test_result = clone self::$individual_test_run_request;582 if (pts_module_manager::is_module_attached("matisk"))583 {584 // TODO find some better way than adding a number to distinguish the results between the MATISK runs585 $arguments_description = phodevi::sensor_object_name($sensor) . ' Monitor (test ' . count($result_file->get_systems()) . ')';586 $arguments_try_description = phodevi::sensor_object_name($sensor) . ' Per Test Try Monitor (test ' . count($result_file->get_systems()) . ')';587 }588 else589 {590 $arguments_description = phodevi::sensor_object_name($sensor) . ' Monitor';591 $arguments_try_description = phodevi::sensor_object_name($sensor) . ' Per Test Try Monitor';592 }593 $test_result->test_profile->set_identifier(null);594 $test_result->test_profile->set_result_proportion('LIB');595 $test_result->test_profile->set_display_format('LINE_GRAPH');596 $test_result->test_profile->set_result_scale(phodevi::read_sensor_object_unit($sensor));597 $test_result->set_used_arguments_description($arguments_description);598 $test_result->set_used_arguments(phodevi::sensor_object_name($sensor) . ' ' . $test_result->get_arguments());599 $test_result->test_result_buffer = $result_buffer;600 if(self::$per_test_try_monitoring && $result_buffer->get_count() > 1)601 {602 $test_result->set_used_arguments_description($arguments_try_description);603 }604 $result_file->add_result($test_result);605 }606 private static function prepare_per_try_results(&$sensor, &$result_buffer)607 {608 $sensor_offsets = self::$test_run_tries_offsets[phodevi::sensor_object_identifier($sensor)];609 for($try_number = 1; $try_number <= self::$test_run_try_number; $try_number++)610 {611 $start_offset = $sensor_offsets[$try_number - 1];612 $end_offset = $sensor_offsets[$try_number];613 $sensor_results = self::parse_monitor_log('logs/' . phodevi::sensor_object_identifier($sensor),614 $start_offset, $end_offset);615 if(count($sensor_results) > 2)616 {617 $result_identifier = self::$result_identifier . " (try " . ($try_number) . ")";618 $result_value = implode(',', $sensor_results);619 $result_buffer->add_test_result($result_identifier, $result_value, $result_value);620 }621 }622 }623 // Generates summary result (covering all test runs) for specified sensor and adds it to the result file.624 private static function process_summary_results(&$sensor, &$test_run_manager)625 {626 $sensor_results = self::parse_monitor_log('logs/' . phodevi::sensor_object_identifier($sensor));627 pts_module::remove_file('logs/' . phodevi::sensor_object_identifier($sensor));628 if(count($sensor_results) > 2 && self::$monitor_test_count > 1)629 {630 $test_profile = new pts_test_profile();631 $test_result = new pts_test_result($test_profile);632 $test_result->test_profile->set_test_title(phodevi::sensor_object_name($sensor) . ' Monitor');633 $test_result->test_profile->set_identifier(null);634 $test_result->test_profile->set_version(null);635 $test_result->test_profile->set_result_proportion(null);636 $test_result->test_profile->set_display_format('LINE_GRAPH');637 $test_result->test_profile->set_result_scale(phodevi::read_sensor_object_unit($sensor));638 $test_result->set_used_arguments_description('Phoronix Test Suite System Monitoring');639 $test_result->set_used_arguments(phodevi::sensor_object_identifier($sensor));640 $test_result->test_result_buffer = new pts_test_result_buffer();641 $test_result->test_result_buffer->add_test_result(self::$result_identifier, implode(',', $sensor_results), implode(',', $sensor_results), implode(',', $sensor_results), implode(',', $sensor_results));642 $test_run_manager->result_file->add_result($test_result);643 }644 }645}646?>...
sensor_object_identifier
Using AI Code Generation
1require_once('phodevi.php');2$phodevi = new phodevi();3$phodevi->sensor_object_identifier();4require_once('phodevi.php');5$phodevi = new phodevi();6$phodevi->sensor_object_identifier();7require_once('phodevi.php');8$phodevi = new phodevi();9$phodevi->sensor_object_identifier();10require_once('phodevi.php');11$phodevi = new phodevi();12$phodevi->sensor_object_identifier();13require_once('phodevi.php');14$phodevi = new phodevi();15$phodevi->sensor_object_identifier();16require_once('phodevi.php');17$phodevi = new phodevi();18$phodevi->sensor_object_identifier();19require_once('phodevi.php');20$phodevi = new phodevi();21$phodevi->sensor_object_identifier();22require_once('phodevi.php');23$phodevi = new phodevi();24$phodevi->sensor_object_identifier();25require_once('phodevi.php');26$phodevi = new phodevi();27$phodevi->sensor_object_identifier();28require_once('phodevi.php');29$phodevi = new phodevi();30$phodevi->sensor_object_identifier();31require_once('phodevi.php');32$phodevi = new phodevi();
sensor_object_identifier
Using AI Code Generation
1require_once('/usr/share/php/phodevi.php');2$phodevi = new phodevi();3$phodevi->sensor_object_identifier('cpu', 'temperature', 0);4$phodevi->sensor_object_identifier('cpu', 'temperature', 1);5$phodevi->sensor_object_identifier('cpu', 'temperature', 2);6$phodevi->sensor_object_identifier('cpu', 'temperature', 3);7$phodevi->sensor_object_identifier('cpu', 'temperature', 4);8$phodevi->sensor_object_identifier('cpu', 'temperature', 5);9$phodevi->sensor_object_identifier('cpu', 'temperature', 6);10$phodevi->sensor_object_identifier('cpu', 'temperature', 7);11$phodevi->sensor_object_identifier('cpu', 'temperature', 8);12$phodevi->sensor_object_identifier('cpu', 'temperature', 9);13$phodevi->sensor_object_identifier('cpu', 'temperature', 10);14$phodevi->sensor_object_identifier('cpu', 'temperature', 11);15$phodevi->sensor_object_identifier('cpu', 'temperature', 12);16$phodevi->sensor_object_identifier('cpu', 'temperature', 13);17$phodevi->sensor_object_identifier('cpu', 'temperature', 14);18$phodevi->sensor_object_identifier('cpu', '
sensor_object_identifier
Using AI Code Generation
1require_once('phodevi.php');2$phodevi = new phodevi();3echo $phodevi->sensor_object_identifier();4require_once('phodevi.php');5$phodevi = new phodevi();6echo $phodevi->sensor_object_identifier();7require_once('phodevi.php');8$phodevi = new phodevi();9echo $phodevi->sensor_object_identifier();10require_once('phodevi.php');11$phodevi = new phodevi();12echo $phodevi->sensor_object_identifier();13require_once('phodevi.php');14$phodevi = new phodevi();15echo $phodevi->sensor_object_identifier();16require_once('phodevi.php');17$phodevi = new phodevi();18echo $phodevi->sensor_object_identifier();19require_once('phodevi.php');20$phodevi = new phodevi();21echo $phodevi->sensor_object_identifier();22require_once('phodevi.php');
sensor_object_identifier
Using AI Code Generation
1require_once 'phodevi.php';2$sensor_object_identifier = phodevi::sensor_object_identifier();3print $sensor_object_identifier;4require_once 'phodevi.php';5$sensor_object_identifier = phodevi::sensor_object_identifier();6print $sensor_object_identifier;7require_once 'phodevi.php';8$sensor_object_identifier = phodevi::sensor_object_identifier();9print $sensor_object_identifier;10require_once 'phodevi.php';11$sensor_object_identifier = phodevi::sensor_object_identifier();12print $sensor_object_identifier;13require_once 'phodevi.php';14$sensor_object_identifier = phodevi::sensor_object_identifier();15print $sensor_object_identifier;16require_once 'phodevi.php';17$sensor_object_identifier = phodevi::sensor_object_identifier();18print $sensor_object_identifier;19require_once 'phodevi.php';20$sensor_object_identifier = phodevi::sensor_object_identifier();21print $sensor_object_identifier;22require_once 'phodevi.php';23$sensor_object_identifier = phodevi::sensor_object_identifier();24print $sensor_object_identifier;
sensor_object_identifier
Using AI Code Generation
1$phodevi = new phodevi();2$phodevi->sensor_object_identifier();3array(1) { ["sensors/thermal_zone0/temp"]=> array(3) { ["path"]=> string(29) "/sys/class/thermal/thermal_zone0/temp" ["name"]=> string(4) "core" ["type"]=> string(4) "temp" } }4$phodevi = new phodevi();5$phodevi->sensor_object_identifier('sensors/thermal_zone1/temp');6array(3) { ["path"]=> string(29) "/sys/class/thermal/thermal_zone1/temp" ["name"]=> string(4) "core" ["type"]=> string(4) "temp" }
sensor_object_identifier
Using AI Code Generation
1$sensor_object_identifier = phodevi::sensor_object_identifier('coretemp', 'temperature', 0);2$sensor_object = phodevi::get_sensor_object($sensor_object_identifier);3$temperature = $sensor_object->get_temperature();4echo $temperature;5phodevi::destroy($sensor_object_identifier);6phodevi::destroy($sensor_object);7$sensor_object_identifier = phodevi::sensor_object_identifier('coretemp', 'temperature', 0);8$sensor_object = phodevi::get_sensor_object($sensor_object_identifier);9$temperature = $sensor_object->get_temperature();10echo $temperature;11phodevi::destroy($sensor_object_identifier);12phodevi::destroy($sensor_object);
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Execute automation tests with sensor_object_identifier on a cloud-based Grid of 3000+ real browsers and operating systems for both web and mobile applications.
Test now for FreeGet 100 minutes of automation test minutes FREE!!