esp-idf/tools/kconfig_new/test/confgen/test_confgen.py
Angus Gratton 16e14104f7 kconfig: Fix generation of hex outputs for Make & CMake
And add tests for hex output formatting in all output formats.

Previously, Make & CMake outputs only formatted hex values with the 0x prefix
if they had the 0x prefix in the sdkconfig file. Now this prefix is always
applied.

Closes https://github.com/espressif/vscode-esp-idf-extension/issues/83
2020-05-05 13:15:54 +10:00

275 lines
8.6 KiB
Python
Executable File

#!/usr/bin/env python
from future.utils import iteritems
import os
import re
import subprocess
import sys
import tempfile
import textwrap
import unittest
class ConfgenBaseTestCase(unittest.TestCase):
@classmethod
def setUpClass(self):
self.args = dict()
self.functions = {'in': self.assertIn,
'not in': self.assertNotIn,
'equal': self.assertEqual,
'not equal': self.assertNotEqual}
try:
regex_func = self.assertRegex
except AttributeError:
# Python 2 fallback
regex_func = self.assertRegexpMatches
finally:
self.functions['regex'] = lambda instance, s, expr: regex_func(instance, expr, s) # reverse args order
def setUp(self):
with tempfile.NamedTemporaryFile(prefix='test_confgen_', delete=False) as f:
self.output_file = f.name
self.addCleanup(os.remove, self.output_file)
def invoke_confgen(self, args):
call_args = [sys.executable, '../../confgen.py']
for (k, v) in iteritems(args):
if k != 'output':
if isinstance(v, type('')): # easy Python 2/3 compatible str/unicode
call_args += ['--{}'.format(k), v]
else:
for i in v:
call_args += ['--{}'.format(k), i]
call_args += ['--output', args['output'], self.output_file] # these arguments belong together
subprocess.check_call(call_args)
def invoke_and_test(self, in_text, out_text, test='in'):
"""
Main utility function for testing confgen:
- Runs confgen via invoke_confgen(), using output method pre-set in test class setup
- in_text is the Kconfig file input content
- out_text is some expected output from confgen
- 'test' can be any function key from self.functions dict (see above). Default is 'in' to test if
out_text is a substring of the full confgen output.
"""
with tempfile.NamedTemporaryFile(mode='w+', prefix='test_confgen_', delete=False) as f:
self.addCleanup(os.remove, f.name)
f.write(textwrap.dedent(in_text))
self.args['kconfig'] = f.name
self.invoke_confgen(self.args)
with open(self.output_file) as f_result:
result = f_result.read()
try:
out_text = textwrap.dedent(out_text)
except TypeError:
pass # probably a regex
self.functions[test](self, out_text, result)
class CmakeTestCase(ConfgenBaseTestCase):
@classmethod
def setUpClass(self):
super(CmakeTestCase, self).setUpClass()
self.args.update({'output': 'cmake'})
def testStringEscape(self):
self.invoke_and_test("""
config PASSWORD
string "password"
default "\\\\~!@#$%^&*()\\\""
""", 'set(CONFIG_PASSWORD "\\\\~!@#$%^&*()\\\"")')
def testHexPrefix(self):
self.invoke_and_test(HEXPREFIX_KCONFIG, 'set(CONFIG_HEX_NOPREFIX "0x33")')
self.invoke_and_test(HEXPREFIX_KCONFIG, 'set(CONFIG_HEX_PREFIX "0x77")')
class JsonTestCase(ConfgenBaseTestCase):
@classmethod
def setUpClass(self):
super(JsonTestCase, self).setUpClass()
self.args.update({'output': 'json'})
def testStringEscape(self):
self.invoke_and_test("""
config PASSWORD
string "password"
default "\\\\~!@#$%^&*()\\\""
""", '"PASSWORD": "\\\\~!@#$%^&*()\\\""')
def testHexPrefix(self):
# hex values come out as integers in JSON, due to no hex type
self.invoke_and_test(HEXPREFIX_KCONFIG, '"HEX_NOPREFIX": %d' % 0x33)
self.invoke_and_test(HEXPREFIX_KCONFIG, '"HEX_PREFIX": %d' % 0x77)
class JsonMenuTestCase(ConfgenBaseTestCase):
@classmethod
def setUpClass(self):
super(JsonMenuTestCase, self).setUpClass()
self.args.update({'output': 'json_menus'})
def testMultipleRanges(self):
self.invoke_and_test("""
config IDF_TARGET
string "IDF target"
default "esp32"
config SOME_SETTING
int "setting for the chip"
range 0 100 if IDF_TARGET="esp32s0"
range 0 10 if IDF_TARGET="esp32"
range -10 1 if IDF_TARGET="esp32s2"
""", re.compile(r'"range":\s+\[\s+0,\s+10\s+\]'), 'regex')
def testHexRanges(self):
self.invoke_and_test("""
config SOME_SETTING
hex "setting for the chip"
range 0x0 0xaf if UNDEFINED
range 0x10 0xaf
""", r'"range":\s+\[\s+16,\s+175\s+\]', 'regex')
class ConfigTestCase(ConfgenBaseTestCase):
@classmethod
def setUpClass(self):
super(ConfigTestCase, self).setUpClass()
self.args.update({'output': 'config'})
self.input = """
config TEST
bool "test"
default "n"
"""
def setUp(self):
super(ConfigTestCase, self).setUp()
with tempfile.NamedTemporaryFile(mode='w+', prefix='test_confgen_', delete=False) as f:
self.addCleanup(os.remove, f.name)
self.args.update({'config': f.name}) # this is input in contrast with {'output': 'config'}
f.write(textwrap.dedent("""
CONFIG_TEST=y
CONFIG_UNKNOWN=y
"""))
def testKeepSavedOption(self):
self.invoke_and_test(self.input, 'CONFIG_TEST=y')
def testDiscardUnknownOption(self):
self.invoke_and_test(self.input, 'CONFIG_UNKNOWN', 'not in')
class MakefileTestCase(ConfgenBaseTestCase):
@classmethod
def setUpClass(self):
super(MakefileTestCase, self).setUpClass()
self.args.update({'output': 'makefile'})
def setUp(self):
super(MakefileTestCase, self).setUp()
with tempfile.NamedTemporaryFile(mode='w+', prefix='test_confgen_', delete=False) as f1:
self.addCleanup(os.remove, f1.name)
with tempfile.NamedTemporaryFile(mode='w+', prefix='test_confgen_', delete=False) as f2:
self.addCleanup(os.remove, f2.name)
self.args.update({'env': ['COMPONENT_KCONFIGS_PROJBUILD_SOURCE_FILE={}'.format(f1.name),
'COMPONENT_KCONFIGS_SOURCE_FILE={}'.format(f2.name),
'IDF_TARGET=esp32']})
def testTarget(self):
with open(os.path.join(os.environ['IDF_PATH'], 'Kconfig')) as f:
self.invoke_and_test(f.read(), 'CONFIG_IDF_TARGET="esp32"')
def testHexPrefix(self):
self.invoke_and_test(HEXPREFIX_KCONFIG, 'CONFIG_HEX_NOPREFIX=0x33')
self.invoke_and_test(HEXPREFIX_KCONFIG, 'CONFIG_HEX_PREFIX=0x77')
class HeaderTestCase(ConfgenBaseTestCase):
@classmethod
def setUpClass(self):
super(HeaderTestCase, self).setUpClass()
self.args.update({'output': 'header'})
def testStringEscape(self):
self.invoke_and_test("""
config PASSWORD
string "password"
default "\\\\~!@#$%^&*()\\\""
""", '#define CONFIG_PASSWORD "\\\\~!@#$%^&*()\\\""')
def testHexPrefix(self):
self.invoke_and_test(HEXPREFIX_KCONFIG, '#define CONFIG_HEX_NOPREFIX 0x33')
self.invoke_and_test(HEXPREFIX_KCONFIG, '#define CONFIG_HEX_PREFIX 0x77')
class DocsTestCase(ConfgenBaseTestCase):
@classmethod
def setUpClass(self):
super(DocsTestCase, self).setUpClass()
self.args.update({'output': 'docs',
'env': 'IDF_TARGET=esp32'})
def testChoice(self):
self.invoke_and_test("""
menu "TEST"
choice TYPES
prompt "types"
default TYPES_OP2
help
Description of TYPES
config TYPES_OP1
bool "option 1"
config TYPES_OP2
bool "option 2"
endchoice
endmenu
""", """
TEST
----
Contains:
- :ref:`CONFIG_TYPES`
.. _CONFIG_TYPES:
CONFIG_TYPES
^^^^^^^^^^^^
types
:emphasis:`Found in:` :ref:`test`
Description of TYPES
Available options:
- option 1 (TYPES_OP1)
- option 2 (TYPES_OP2)
""") # this is more readable than regex
# Used by multiple testHexPrefix() test cases to verify correct hex output for each format
HEXPREFIX_KCONFIG = """
config HEX_NOPREFIX
hex "Hex Item default no prefix"
default 33
config HEX_PREFIX
hex "Hex Item default prefix"
default 0x77
"""
if __name__ == "__main__":
unittest.main()