summaryrefslogtreecommitdiffstats
path: root/testing/mozbase/mozpower/tests/test_intelpowergadget.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/mozbase/mozpower/tests/test_intelpowergadget.py')
-rw-r--r--testing/mozbase/mozpower/tests/test_intelpowergadget.py348
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()