1#!/usr/bin/env python3 2# 3# Copyright 2018, The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18""" 19Unit tests for the app_startup_runner.py script. 20 21Install: 22 $> sudo apt-get install python3-pytest ## OR 23 $> pip install -U pytest 24See also https://docs.pytest.org/en/latest/getting-started.html 25 26Usage: 27 $> ./app_startup_runner_test.py 28 $> pytest app_startup_runner_test.py 29 $> python -m pytest app_startup_runner_test.py 30 31See also https://docs.pytest.org/en/latest/usage.html 32""" 33 34# global imports 35from contextlib import contextmanager 36import io 37import shlex 38import sys 39import typing 40 41# pip imports 42import pytest 43 44# local imports 45import app_startup_runner as asr 46 47# 48# Argument Parsing Helpers 49# 50 51@contextmanager 52def ignore_stdout_stderr(): 53 """Ignore stdout/stderr output for duration of this context.""" 54 old_stdout = sys.stdout 55 old_stderr = sys.stderr 56 sys.stdout = io.StringIO() 57 sys.stderr = io.StringIO() 58 try: 59 yield 60 finally: 61 sys.stdout = old_stdout 62 sys.stderr = old_stderr 63 64@contextmanager 65def argparse_bad_argument(msg): 66 """ 67 Assert that a SystemExit is raised when executing this context. 68 If the assertion fails, print the message 'msg'. 69 """ 70 with pytest.raises(SystemExit, message=msg): 71 with ignore_stdout_stderr(): 72 yield 73 74def assert_bad_argument(args, msg): 75 """ 76 Assert that the command line arguments in 'args' are malformed. 77 Prints 'msg' if the assertion fails. 78 """ 79 with argparse_bad_argument(msg): 80 parse_args(args) 81 82def parse_args(args): 83 """ 84 :param args: command-line like arguments as a single string 85 :return: dictionary of parsed key/values 86 """ 87 # "-a b -c d" => ['-a', 'b', '-c', 'd'] 88 return vars(asr.parse_options(shlex.split(args))) 89 90def default_dict_for_parsed_args(**kwargs): 91 """ 92 # Combine it with all of the "optional" parameters' default values. 93 """ 94 d = {'compiler_filters': None, 'simulate': False, 'debug': False, 'output': None, 'timeout': None, 'loop_count': 1, 'inodes': None} 95 d.update(kwargs) 96 return d 97 98def default_mock_dict_for_parsed_args(include_optional=True, **kwargs): 99 """ 100 Combine default dict with all optional parameters with some mock required parameters. 101 """ 102 d = {'packages': ['com.fake.package'], 'readaheads': ['warm']} 103 if include_optional: 104 d.update(default_dict_for_parsed_args()) 105 d.update(kwargs) 106 return d 107 108def parse_optional_args(str): 109 """ 110 Parse an argument string which already includes all the required arguments 111 in default_mock_dict_for_parsed_args. 112 """ 113 req = "--package com.fake.package --readahead warm" 114 return parse_args("%s %s" %(req, str)) 115 116def test_argparse(): 117 # missing arguments 118 assert_bad_argument("", "-p and -r are required") 119 assert_bad_argument("-r warm", "-p is required") 120 assert_bad_argument("--readahead warm", "-p is required") 121 assert_bad_argument("-p com.fake.package", "-r is required") 122 assert_bad_argument("--package com.fake.package", "-r is required") 123 124 # required arguments are parsed correctly 125 ad = default_dict_for_parsed_args # assert dict 126 127 assert parse_args("--package xyz --readahead warm") == ad(packages=['xyz'], readaheads=['warm']) 128 assert parse_args("-p xyz -r warm") == ad(packages=['xyz'], readaheads=['warm']) 129 130 assert parse_args("-p xyz -r warm -s") == ad(packages=['xyz'], readaheads=['warm'], simulate=True) 131 assert parse_args("-p xyz -r warm --simulate") == ad(packages=['xyz'], readaheads=['warm'], simulate=True) 132 133 # optional arguments are parsed correctly. 134 mad = default_mock_dict_for_parsed_args # mock assert dict 135 assert parse_optional_args("--output filename.csv") == mad(output='filename.csv') 136 assert parse_optional_args("-o filename.csv") == mad(output='filename.csv') 137 138 assert parse_optional_args("--timeout 123") == mad(timeout=123) 139 assert parse_optional_args("-t 456") == mad(timeout=456) 140 141 assert parse_optional_args("--loop-count 123") == mad(loop_count=123) 142 assert parse_optional_args("-lc 456") == mad(loop_count=456) 143 144 assert parse_optional_args("--inodes bar") == mad(inodes="bar") 145 assert parse_optional_args("-in baz") == mad(inodes="baz") 146 147 148def generate_run_combinations(*args): 149 # expand out the generator values so that assert x == y works properly. 150 return [i for i in asr.generate_run_combinations(*args)] 151 152def test_generate_run_combinations(): 153 blank_nd = typing.NamedTuple('Blank') 154 assert generate_run_combinations(blank_nd, {}) == [()], "empty" 155 assert generate_run_combinations(blank_nd, {'a' : ['a1', 'a2']}) == [()], "empty filter" 156 a_nd = typing.NamedTuple('A', [('a', str)]) 157 assert generate_run_combinations(a_nd, {'a': None}) == [(None,)], "None" 158 assert generate_run_combinations(a_nd, {'a': ['a1', 'a2']}) == [('a1',), ('a2',)], "one item" 159 assert generate_run_combinations(a_nd, 160 {'a' : ['a1', 'a2'], 'b': ['b1', 'b2']}) == [('a1',), ('a2',)],\ 161 "one item filter" 162 ab_nd = typing.NamedTuple('AB', [('a', str), ('b', str)]) 163 assert generate_run_combinations(ab_nd, 164 {'a': ['a1', 'a2'], 165 'b': ['b1', 'b2']}) == [ab_nd('a1', 'b1'), 166 ab_nd('a1', 'b2'), 167 ab_nd('a2', 'b1'), 168 ab_nd('a2', 'b2')],\ 169 "two items" 170 171 assert generate_run_combinations(ab_nd, 172 {'as': ['a1', 'a2'], 173 'bs': ['b1', 'b2']}) == [ab_nd('a1', 'b1'), 174 ab_nd('a1', 'b2'), 175 ab_nd('a2', 'b1'), 176 ab_nd('a2', 'b2')],\ 177 "two items plural" 178 179def test_key_to_cmdline_flag(): 180 assert asr.key_to_cmdline_flag("abc") == "--abc" 181 assert asr.key_to_cmdline_flag("foos") == "--foo" 182 assert asr.key_to_cmdline_flag("ba_r") == "--ba-r" 183 assert asr.key_to_cmdline_flag("ba_zs") == "--ba-z" 184 185 186def test_make_script_command_with_temp_output(): 187 cmd_str, tmp_file = asr.make_script_command_with_temp_output("fake_script", args=[], count=1) 188 with tmp_file: 189 assert cmd_str == ["fake_script", "--count", "1", "--output", tmp_file.name] 190 191 cmd_str, tmp_file = asr.make_script_command_with_temp_output("fake_script", args=['a', 'b'], count=2) 192 with tmp_file: 193 assert cmd_str == ["fake_script", "a", "b", "--count", "2", "--output", tmp_file.name] 194 195def test_parse_run_script_csv_file(): 196 # empty file -> empty list 197 f = io.StringIO("") 198 assert asr.parse_run_script_csv_file(f) == [] 199 200 # common case 201 f = io.StringIO("1,2,3") 202 assert asr.parse_run_script_csv_file(f) == [1,2,3] 203 204 # ignore trailing comma 205 f = io.StringIO("1,2,3,4,5,") 206 assert asr.parse_run_script_csv_file(f) == [1,2,3,4,5] 207 208 209if __name__ == '__main__': 210 pytest.main() 211