Home | History | Annotate | Download | only in app_startup
      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 """
     19 Unit tests for the app_startup_runner.py script.
     20 
     21 Install:
     22   $> sudo apt-get install python3-pytest   ##  OR
     23   $> pip install -U pytest
     24 See also https://docs.pytest.org/en/latest/getting-started.html
     25 
     26 Usage:
     27   $> ./app_startup_runner_test.py
     28   $> pytest app_startup_runner_test.py
     29   $> python -m pytest app_startup_runner_test.py
     30 
     31 See also https://docs.pytest.org/en/latest/usage.html
     32 """
     33 
     34 # global imports
     35 from contextlib import contextmanager
     36 import io
     37 import shlex
     38 import sys
     39 import typing
     40 
     41 # pip imports
     42 import pytest
     43 
     44 # local imports
     45 import app_startup_runner as asr
     46 
     47 #
     48 # Argument Parsing Helpers
     49 #
     50 
     51 @contextmanager
     52 def 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
     65 def 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 
     74 def 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 
     82 def 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 
     90 def 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 
     98 def 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 
    108 def 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 
    116 def 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 
    148 def 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 
    152 def 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 
    179 def 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 
    186 def 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 
    195 def 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 
    209 if __name__ == '__main__':
    210   pytest.main()
    211