diff options
Diffstat (limited to 'testing/mozbase/mozpower/tests/test_intelpowergadget.py')
-rw-r--r-- | testing/mozbase/mozpower/tests/test_intelpowergadget.py | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/testing/mozbase/mozpower/tests/test_intelpowergadget.py b/testing/mozbase/mozpower/tests/test_intelpowergadget.py new file mode 100644 index 0000000000..9be01cb720 --- /dev/null +++ b/testing/mozbase/mozpower/tests/test_intelpowergadget.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python + +import datetime +import os +import time +from unittest import mock + +import mozunit +import pytest +import six +from mozpower.intel_power_gadget import ( + IPGEmptyFileError, + IPGMissingOutputFileError, + IPGTimeoutError, + IPGUnknownValueTypeError, +) + + +def thread_is_alive(thread): + if six.PY2: + return thread.isAlive() + return thread.is_alive() + + +def test_ipg_pathsplitting(ipg_obj): + """Tests that the output file path and prefix was properly split. + This test assumes that it is in the same directory as the conftest.py file. + """ + assert ( + ipg_obj.output_dir_path == os.path.abspath(os.path.dirname(__file__)) + "/files" + ) + assert ipg_obj.output_file_prefix == "raptor-tp6-amazon-firefox_powerlog" + + +def test_ipg_get_output_file_path(ipg_obj): + """Tests that the output file path is constantly changing + based on the file_counter value. + """ + test_path = "/test_path/" + test_ext = ".txt" + ipg_obj._file_counter = 1 + ipg_obj._output_dir_path = test_path + ipg_obj._output_file_ext = test_ext + + for i in range(1, 6): + fpath = ipg_obj._get_output_file_path() + + assert fpath.startswith(test_path) + assert fpath.endswith(test_ext) + assert str(i) in fpath + + +def test_ipg_start_and_stop(ipg_obj): + """Tests that the IPG thread can start and stop properly.""" + + def subprocess_side_effect(*args, **kwargs): + time.sleep(1) + + with mock.patch("subprocess.check_output") as m: + m.side_effect = subprocess_side_effect + + # Start recording IPG measurements + ipg_obj.start_ipg() + assert not ipg_obj._stop + + # Wait a bit for thread to start, then check it + timeout = 10 + start = time.time() + while time.time() - start < timeout and not ipg_obj._running: + time.sleep(1) + + assert ipg_obj._running + assert thread_is_alive(ipg_obj._thread) + + # Stop recording IPG measurements + ipg_obj.stop_ipg(wait_interval=1, timeout=30) + assert ipg_obj._stop + assert not ipg_obj._running + + +def test_ipg_stopping_timeout(ipg_obj): + """Tests that an IPGTimeoutError is raised when + the thread is still "running" and the wait in _wait_for_ipg + has exceeded the timeout value. + """ + with pytest.raises(IPGTimeoutError): + ipg_obj._running = True + ipg_obj._wait_for_ipg(wait_interval=1, timeout=2) + + +def test_ipg_rh_combine_cumulatives(ipg_rh_obj): + """Tests that cumulatives are correctly combined in + the _combine_cumulative_rows function. + """ + cumulatives_to_combine = [ + [0, 1, 2, 3, 4, 5], + [0, 1, 2, 3, 4, 5], + [0, 1, 2, 3, 4, 5], + [0, 1, 2, 3, 4, 5], + ] + + combined_cumulatives = ipg_rh_obj._combine_cumulative_rows(cumulatives_to_combine) + + # Check that accumulation worked, final value must be the maximum + assert combined_cumulatives[-1] == max(combined_cumulatives) + + # Check that the cumulative values are monotonically increasing + for count, val in enumerate(combined_cumulatives[:-1]): + assert combined_cumulatives[count + 1] - val >= 0 + + +def test_ipg_rh_clean_file(ipg_rh_obj): + """Tests that IPGResultsHandler correctly cleans the data + from one file. + """ + file = ipg_rh_obj._output_files[0] + linecount = 0 + with open(file, "r") as f: + for line in f: + linecount += 1 + + results, summary, clean_file = ipg_rh_obj._clean_ipg_file(file) + + # Check that each measure from the csv header + # is in the results dict and that the clean file output + # exists. + for measure in results: + assert measure in ipg_rh_obj._csv_header + assert os.path.exists(clean_file) + + clean_rows = [] + with open(clean_file, "r") as f: + for line in f: + if line.strip(): + clean_rows.append(line) + + # Make sure that the results and summary entries + # have the expected lengths. + for measure in results: + # Add 6 for new lines that were removed + assert len(results[measure]) + len(summary) + 6 == linecount + # Subtract 1 for the csv header + assert len(results[measure]) == len(clean_rows) - 1 + + +def test_ipg_rh_clean_ipg_data_no_files(ipg_rh_obj): + """Tests that IPGResultsHandler correctly handles the case + when no output files exist. + """ + ipg_rh_obj._output_files = [] + clean_data = ipg_rh_obj.clean_ipg_data() + assert clean_data is None + + +def test_ipg_rh_clean_ipg_data(ipg_rh_obj): + """Tests that IPGResultsHandler correctly handles cleaning + all known files and that the results and the merged output + are correct. + """ + clean_data = ipg_rh_obj.clean_ipg_data() + clean_files = ipg_rh_obj.cleaned_files + merged_output_path = ipg_rh_obj.merged_output_path + + # Check that the expected output exists + assert clean_data is not None + assert len(clean_files) == len(ipg_rh_obj._output_files) + assert os.path.exists(merged_output_path) + + # Check that the merged file length and results length + # is correct, and that no lines were lost and no extra lines + # were added. + expected_merged_line_count = 0 + for file in clean_files: + with open(file, "r") as f: + for count, line in enumerate(f): + if count == 0: + continue + if line.strip(): + expected_merged_line_count += 1 + + merged_line_count = 0 + with open(merged_output_path, "r") as f: + for count, line in enumerate(f): + if count == 0: + continue + if line.strip(): + merged_line_count += 1 + + assert merged_line_count == expected_merged_line_count + for measure in clean_data: + assert len(clean_data[measure]) == merged_line_count + + # Check that the clean data rows are ordered in increasing time + times_in_seconds = [] + for sys_time in clean_data["System Time"]: + split_sys_time = sys_time.split(":") + hour_min_sec = ":".join(split_sys_time[:-1]) + millis = float(split_sys_time[-1]) / 1000 + + timestruct = time.strptime(hour_min_sec, "%H:%M:%S") + times_in_seconds.append( + datetime.timedelta( + hours=timestruct.tm_hour, + minutes=timestruct.tm_min, + seconds=timestruct.tm_sec, + ).total_seconds() + + millis + ) + + for count, val in enumerate(times_in_seconds[:-1]): + assert times_in_seconds[count + 1] - val >= 0 + + +def test_ipg_rh_format_to_perfherder_with_no_results(ipg_rh_obj): + """Tests that formatting the data to a perfherder-like format + fails when clean_ipg_data was not called beforehand. + """ + formatted_data = ipg_rh_obj.format_ipg_data_to_partial_perfherder( + 1000, ipg_rh_obj._output_file_prefix + ) + assert formatted_data is None + + +def test_ipg_rh_format_to_perfherder_without_cutoff(ipg_rh_obj): + """Tests that formatting the data to a perfherder-like format + works as expected. + """ + ipg_rh_obj.clean_ipg_data() + formatted_data = ipg_rh_obj.format_ipg_data_to_partial_perfherder( + 1000, ipg_rh_obj._output_file_prefix + ) + + # Check that the expected entries exist + assert len(formatted_data.keys()) == 5 + assert "utilization" in formatted_data and "power-usage" in formatted_data + + assert ( + formatted_data["power-usage"]["test"] + == ipg_rh_obj._output_file_prefix + "-cumulative" + ) + assert ( + formatted_data["utilization"]["test"] + == ipg_rh_obj._output_file_prefix + "-utilization" + ) + assert ( + formatted_data["frequency-gpu"]["test"] + == ipg_rh_obj._output_file_prefix + "-frequency-gpu" + ) + assert ( + formatted_data["frequency-cpu"]["test"] + == ipg_rh_obj._output_file_prefix + "-frequency-cpu" + ) + assert ( + formatted_data["power-watts"]["test"] + == ipg_rh_obj._output_file_prefix + "-watts" + ) + + for measure in formatted_data: + # Make sure that the data exists + assert len(formatted_data[measure]["values"]) >= 1 + + for valkey in formatted_data[measure]["values"]: + # Make sure the names were simplified + assert "(" not in valkey + assert ")" not in valkey + + # Check that gpu utilization doesn't exist but cpu does + utilization_vals = formatted_data["utilization"]["values"] + assert "cpu" in utilization_vals + assert "gpu" not in utilization_vals + + expected_fields = ["processor-cores", "processor-package", "gpu", "dram"] + consumption_vals = formatted_data["power-usage"]["values"] + + consumption_vals_measures = list(consumption_vals.keys()) + + # This assertion ensures that the consumption values contain the expected + # fields and nothing more. + assert not list(set(consumption_vals_measures) - set(expected_fields)) + + +def test_ipg_rh_format_to_perfherder_with_cutoff(ipg_rh_obj): + """Tests that formatting the data to a perfherder-like format + works as expected. + """ + ipg_rh_obj.clean_ipg_data() + formatted_data = ipg_rh_obj.format_ipg_data_to_partial_perfherder( + 2.5, ipg_rh_obj._output_file_prefix + ) + + # Check that the formatted data was cutoff at the correct point, + # expecting that only the first row of merged will exist. + utilization_vals = formatted_data["utilization"]["values"] + assert utilization_vals["cpu"] == 14 + + # Expected vals are ordered in this way: [processor, cores, dram, gpu] + expected_vals = [6.517, 5.847, 0.244, 0.006] + consumption_vals = [ + formatted_data["power-usage"]["values"][measure] + for measure in formatted_data["power-usage"]["values"] + ] + assert not list(set(expected_vals) - set(consumption_vals)) + + +def test_ipg_rh_missingoutputfile(ipg_rh_obj): + """Tests that the IPGMissingOutputFileError is raised + when a bad file path is passed to _clean_ipg_file. + """ + bad_files = ["non-existent-file"] + with pytest.raises(IPGMissingOutputFileError): + ipg_rh_obj._clean_ipg_file(bad_files[0]) + + ipg_rh_obj._output_files = bad_files + with pytest.raises(IPGMissingOutputFileError): + ipg_rh_obj.clean_ipg_data() + + +def test_ipg_rh_emptyfile(ipg_rh_obj): + """Tests that the empty file error is raised when + a file exists, but does not contain any results in + it. + """ + base_path = os.path.abspath(os.path.dirname(__file__)) + "/files/" + bad_files = [base_path + "emptyfile.txt"] + with pytest.raises(IPGEmptyFileError): + ipg_rh_obj._clean_ipg_file(bad_files[0]) + + ipg_rh_obj._output_files = bad_files + with pytest.raises(IPGEmptyFileError): + ipg_rh_obj.clean_ipg_data() + + +def test_ipg_rh_valuetypeerrorfile(ipg_rh_obj): + """Tests that the IPGUnknownValueTypeError is raised + when a bad entry is encountered in a file that is cleaned. + """ + base_path = os.path.abspath(os.path.dirname(__file__)) + "/files/" + bad_files = [base_path + "valueerrorfile.txt"] + with pytest.raises(IPGUnknownValueTypeError): + ipg_rh_obj._clean_ipg_file(bad_files[0]) + + ipg_rh_obj._output_files = bad_files + with pytest.raises(IPGUnknownValueTypeError): + ipg_rh_obj.clean_ipg_data() + + +if __name__ == "__main__": + mozunit.main() |