aboutsummaryrefslogtreecommitdiff
path: root/afdo_tools/bisection/afdo_prof_analysis_e2e_test.py
diff options
context:
space:
mode:
Diffstat (limited to 'afdo_tools/bisection/afdo_prof_analysis_e2e_test.py')
-rwxr-xr-xafdo_tools/bisection/afdo_prof_analysis_e2e_test.py281
1 files changed, 0 insertions, 281 deletions
diff --git a/afdo_tools/bisection/afdo_prof_analysis_e2e_test.py b/afdo_tools/bisection/afdo_prof_analysis_e2e_test.py
deleted file mode 100755
index 85c1c175..00000000
--- a/afdo_tools/bisection/afdo_prof_analysis_e2e_test.py
+++ /dev/null
@@ -1,281 +0,0 @@
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
-# Copyright 2019 The Chromium OS Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""End-to-end test for afdo_prof_analysis."""
-
-from __future__ import absolute_import, division, print_function
-
-import json
-import os
-import shutil
-import tempfile
-import unittest
-from datetime import date
-
-import afdo_prof_analysis as analysis
-
-
-class ObjectWithFields(object):
- """Turns kwargs given to the constructor into fields on an object.
-
- Example usage:
- x = ObjectWithFields(a=1, b=2)
- assert x.a == 1
- assert x.b == 2
- """
-
- def __init__(self, **kwargs):
- for key, val in kwargs.items():
- setattr(self, key, val)
-
-
-class AfdoProfAnalysisE2ETest(unittest.TestCase):
- """Class for end-to-end testing of AFDO Profile Analysis"""
-
- # nothing significant about the values, just easier to remember even vs odd
- good_prof = {
- 'func_a': ':1\n 1: 3\n 3: 5\n 5: 7\n',
- 'func_b': ':3\n 3: 5\n 5: 7\n 7: 9\n',
- 'func_c': ':5\n 5: 7\n 7: 9\n 9: 11\n',
- 'func_d': ':7\n 7: 9\n 9: 11\n 11: 13\n',
- 'good_func_a': ':11\n',
- 'good_func_b': ':13\n'
- }
-
- bad_prof = {
- 'func_a': ':2\n 2: 4\n 4: 6\n 6: 8\n',
- 'func_b': ':4\n 4: 6\n 6: 8\n 8: 10\n',
- 'func_c': ':6\n 6: 8\n 8: 10\n 10: 12\n',
- 'func_d': ':8\n 8: 10\n 10: 12\n 12: 14\n',
- 'bad_func_a': ':12\n',
- 'bad_func_b': ':14\n'
- }
-
- expected = {
- 'good_only_functions': False,
- 'bad_only_functions': True,
- 'bisect_results': {
- 'ranges': [],
- 'individuals': ['func_a']
- }
- }
-
- def test_afdo_prof_analysis(self):
- # Individual issues take precedence by nature of our algos
- # so first, that should be caught
- good = self.good_prof.copy()
- bad = self.bad_prof.copy()
- self.run_check(good, bad, self.expected)
-
- # Now remove individuals and exclusively BAD, and check that range is caught
- bad['func_a'] = good['func_a']
- bad.pop('bad_func_a')
- bad.pop('bad_func_b')
-
- expected_cp = self.expected.copy()
- expected_cp['bad_only_functions'] = False
- expected_cp['bisect_results'] = {
- 'individuals': [],
- 'ranges': [['func_b', 'func_c', 'func_d']]
- }
-
- self.run_check(good, bad, expected_cp)
-
- def test_afdo_prof_state(self):
- """Verifies that saved state is correct replication."""
- temp_dir = tempfile.mkdtemp()
- self.addCleanup(shutil.rmtree, temp_dir, ignore_errors=True)
-
- good = self.good_prof.copy()
- bad = self.bad_prof.copy()
- # add more functions to data
- for x in range(400):
- good['func_%d' % x] = ''
- bad['func_%d' % x] = ''
-
- fd_first, first_result = tempfile.mkstemp(dir=temp_dir)
- os.close(fd_first)
- fd_state, state_file = tempfile.mkstemp(dir=temp_dir)
- os.close(fd_state)
- self.run_check(
- self.good_prof,
- self.bad_prof,
- self.expected,
- state_file=state_file,
- out_file=first_result)
-
- fd_second, second_result = tempfile.mkstemp(dir=temp_dir)
- os.close(fd_second)
- completed_state_file = '%s.completed.%s' % (state_file, str(date.today()))
- self.run_check(
- self.good_prof,
- self.bad_prof,
- self.expected,
- state_file=completed_state_file,
- no_resume=False,
- out_file=second_result)
-
- with open(first_result) as f:
- initial_run = json.load(f)
- with open(second_result) as f:
- loaded_run = json.load(f)
- self.assertEqual(initial_run, loaded_run)
-
- def test_exit_on_problem_status(self):
- temp_dir = tempfile.mkdtemp()
- self.addCleanup(shutil.rmtree, temp_dir, ignore_errors=True)
-
- fd_state, state_file = tempfile.mkstemp(dir=temp_dir)
- os.close(fd_state)
- with self.assertRaises(RuntimeError):
- self.run_check(
- self.good_prof,
- self.bad_prof,
- self.expected,
- state_file=state_file,
- extern_decider='problemstatus_external.sh')
-
- def test_state_assumption(self):
-
- def compare_runs(tmp_dir, first_ctr, second_ctr):
- """Compares given prof versions between first and second run in test."""
- first_prof = '%s/.first_run_%d' % (tmp_dir, first_ctr)
- second_prof = '%s/.second_run_%d' % (tmp_dir, second_ctr)
- with open(first_prof) as f:
- first_prof_text = f.read()
- with open(second_prof) as f:
- second_prof_text = f.read()
- self.assertEqual(first_prof_text, second_prof_text)
-
- good_prof = {'func_a': ':1\n3: 3\n5: 7\n'}
- bad_prof = {'func_a': ':2\n4: 4\n6: 8\n'}
- # add some noise to the profiles; 15 is an arbitrary choice
- for x in range(15):
- func = 'func_%d' % x
- good_prof[func] = ':%d\n' % (x)
- bad_prof[func] = ':%d\n' % (x + 1)
- expected = {
- 'bisect_results': {
- 'ranges': [],
- 'individuals': ['func_a']
- },
- 'good_only_functions': False,
- 'bad_only_functions': False
- }
-
- # using a static temp dir rather than a dynamic one because these files are
- # shared between the bash scripts and this Python test, and the arguments
- # to the bash scripts are fixed by afdo_prof_analysis.py so it would be
- # difficult to communicate dynamically generated directory to bash scripts
- scripts_tmp_dir = '%s/afdo_test_tmp' % os.getcwd()
- os.mkdir(scripts_tmp_dir)
- self.addCleanup(shutil.rmtree, scripts_tmp_dir, ignore_errors=True)
-
- # files used in the bash scripts used as external deciders below
- # - count_file tracks the current number of calls to the script in total
- # - local_count_file tracks the number of calls to the script without
- # interruption
- count_file = '%s/.count' % scripts_tmp_dir
- local_count_file = '%s/.local_count' % scripts_tmp_dir
-
- # runs through whole thing at once
- initial_seed = self.run_check(
- good_prof,
- bad_prof,
- expected,
- extern_decider='state_assumption_external.sh')
- with open(count_file) as f:
- num_calls = int(f.read())
- os.remove(count_file) # reset counts for second run
- finished_state_file = 'afdo_analysis_state.json.completed.%s' % str(
- date.today())
- self.addCleanup(os.remove, finished_state_file)
-
- # runs the same analysis but interrupted each iteration
- for i in range(2 * num_calls + 1):
- no_resume_run = (i == 0)
- seed = initial_seed if no_resume_run else None
- try:
- self.run_check(
- good_prof,
- bad_prof,
- expected,
- no_resume=no_resume_run,
- extern_decider='state_assumption_interrupt.sh',
- seed=seed)
- break
- except RuntimeError:
- # script was interrupted, so we restart local count
- os.remove(local_count_file)
- else:
- raise RuntimeError('Test failed -- took too many iterations')
-
- for initial_ctr in range(3): # initial runs unaffected by interruption
- compare_runs(scripts_tmp_dir, initial_ctr, initial_ctr)
-
- start = 3
- for ctr in range(start, num_calls):
- # second run counter incremented by 4 for each one first run is because
- # +2 for performing initial checks on good and bad profs each time
- # +1 for PROBLEM_STATUS run which causes error and restart
- compare_runs(scripts_tmp_dir, ctr, 6 + (ctr - start) * 4)
-
- def run_check(self,
- good_prof,
- bad_prof,
- expected,
- state_file=None,
- no_resume=True,
- out_file=None,
- extern_decider=None,
- seed=None):
-
- temp_dir = tempfile.mkdtemp()
- self.addCleanup(shutil.rmtree, temp_dir, ignore_errors=True)
-
- good_prof_file = '%s/%s' % (temp_dir, 'good_prof.txt')
- bad_prof_file = '%s/%s' % (temp_dir, 'bad_prof.txt')
- good_prof_text = analysis.json_to_text(good_prof)
- bad_prof_text = analysis.json_to_text(bad_prof)
- with open(good_prof_file, 'w') as f:
- f.write(good_prof_text)
- with open(bad_prof_file, 'w') as f:
- f.write(bad_prof_text)
-
- dir_path = os.path.dirname(os.path.realpath(__file__)) # dir of this file
- external_script = '%s/%s' % (dir_path, extern_decider or 'e2e_external.sh')
-
- # FIXME: This test ideally shouldn't be writing to $PWD
- if state_file is None:
- state_file = '%s/afdo_analysis_state.json' % os.getcwd()
-
- def rm_state():
- try:
- os.unlink(state_file)
- except OSError:
- # Probably because the file DNE. That's fine.
- pass
-
- self.addCleanup(rm_state)
-
- actual = analysis.main(
- ObjectWithFields(
- good_prof=good_prof_file,
- bad_prof=bad_prof_file,
- external_decider=external_script,
- analysis_output_file=out_file or '/dev/null',
- state_file=state_file,
- no_resume=no_resume,
- remove_state_on_completion=False,
- seed=seed,
- ))
- actual_seed = actual.pop('seed') # nothing to check
- self.assertEqual(actual, expected)
- return actual_seed
-
-
-if __name__ == '__main__':
- unittest.main()