test_confserver.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. #!/usr/bin/env python
  2. from __future__ import print_function
  3. import argparse
  4. import json
  5. import os
  6. import re
  7. import tempfile
  8. import pexpect
  9. # Each protocol version to be tested needs a 'testcases_vX.txt' file
  10. PROTOCOL_VERSIONS = [1, 2]
  11. def parse_testcases(version):
  12. with open('testcases_v%d.txt' % version, 'r') as f:
  13. cases = [line for line in f.readlines() if len(line.strip()) > 0]
  14. # Each 3 lines in the file should be formatted as:
  15. # * Description of the test change
  16. # * JSON "changes" to send to the server
  17. # * Result JSON to expect back from the server
  18. if len(cases) % 3 != 0:
  19. print('Warning: testcases.txt has wrong number of non-empty lines (%d). Should be 3 lines per test case, always.' % len(cases))
  20. for i in range(0, len(cases), 3):
  21. desc = cases[i]
  22. send = cases[i + 1]
  23. expect = cases[i + 2]
  24. if not desc.startswith('* '):
  25. raise RuntimeError("Unexpected description at line %d: '%s'" % (i + 1, desc))
  26. if not send.startswith('> '):
  27. raise RuntimeError("Unexpected send at line %d: '%s'" % (i + 2, send))
  28. if not expect.startswith('< '):
  29. raise RuntimeError("Unexpected expect at line %d: '%s'" % (i + 3, expect))
  30. desc = desc[2:]
  31. send = json.loads(send[2:])
  32. expect = json.loads(expect[2:])
  33. yield (desc, send, expect)
  34. def main():
  35. parser = argparse.ArgumentParser()
  36. parser.add_argument('--logfile', type=argparse.FileType('w'), help='Optional session log of the interactions with confserver.py')
  37. args = parser.parse_args()
  38. try:
  39. # set up temporary file to use as sdkconfig copy
  40. with tempfile.NamedTemporaryFile(mode='w', delete=False) as temp_sdkconfig:
  41. temp_sdkconfig_path = os.path.join(tempfile.gettempdir(), temp_sdkconfig.name)
  42. with open('sdkconfig') as orig:
  43. temp_sdkconfig.write(orig.read())
  44. with tempfile.NamedTemporaryFile(delete=False) as f:
  45. temp_kconfigs_source_file = os.path.join(tempfile.gettempdir(), f.name)
  46. with tempfile.NamedTemporaryFile(delete=False) as f:
  47. temp_kconfig_projbuilds_source_file = os.path.join(tempfile.gettempdir(), f.name)
  48. cmdline = '''../../confserver.py --env "COMPONENT_KCONFIGS_SOURCE_FILE=%s" \
  49. --env "COMPONENT_KCONFIGS_PROJBUILD_SOURCE_FILE=%s" \
  50. --env "COMPONENT_KCONFIGS=" \
  51. --env "COMPONENT_KCONFIGS_PROJBUILD=" \
  52. --kconfig Kconfig \
  53. --config %s \
  54. ''' % (temp_kconfigs_source_file, temp_kconfig_projbuilds_source_file, temp_sdkconfig_path)
  55. cmdline = re.sub(r' +', ' ', cmdline)
  56. # prepare_kconfig_files.py doesn't have to be called because COMPONENT_KCONFIGS and
  57. # COMPONENT_KCONFIGS_PROJBUILD are empty
  58. print('Running: %s' % cmdline)
  59. p = pexpect.spawn(cmdline, timeout=30, logfile=args.logfile, echo=False, use_poll=True, maxread=1)
  60. p.expect('Server running.+\r\n')
  61. initial = expect_json(p)
  62. print('Initial: %s' % initial)
  63. for version in PROTOCOL_VERSIONS:
  64. test_protocol_version(p, version)
  65. test_load_save(p, temp_sdkconfig_path)
  66. test_invalid_json(p)
  67. print('Done. All passed.')
  68. finally:
  69. try:
  70. os.remove(temp_sdkconfig_path)
  71. os.remove(temp_kconfigs_source_file)
  72. os.remove(temp_kconfig_projbuilds_source_file)
  73. except OSError:
  74. pass
  75. def expect_json(p):
  76. # run p.expect() to expect a json object back, and return it as parsed JSON
  77. p.expect('{.+}\r\n')
  78. result = p.match.group(0).strip().decode()
  79. print('Read raw data from server: %s' % result)
  80. return json.loads(result)
  81. def send_request(p, req):
  82. req = json.dumps(req)
  83. print('Sending: %s' % (req))
  84. p.send('%s\n' % req)
  85. readback = expect_json(p)
  86. print('Read back: %s' % (json.dumps(readback)))
  87. return readback
  88. def test_protocol_version(p, version):
  89. print('*****')
  90. print('Testing version %d...' % version)
  91. # reload the config from the sdkconfig file
  92. req = {'version': version, 'load': None}
  93. readback = send_request(p, req)
  94. print('Reset response: %s' % (json.dumps(readback)))
  95. # run through each test case
  96. cases = parse_testcases(version)
  97. for (desc, send, expected) in cases:
  98. print(desc)
  99. req = {'version': version, 'set': send}
  100. readback = send_request(p, req)
  101. if readback.get('version', None) != version:
  102. raise RuntimeError('Expected {"version" : %d} in response' % version)
  103. for expect_key in expected.keys():
  104. read_vals = readback[expect_key]
  105. exp_vals = expected[expect_key]
  106. if read_vals != exp_vals:
  107. expect_diff = dict((k,v) for (k,v) in exp_vals.items() if k not in read_vals or v != read_vals[k])
  108. raise RuntimeError('Test failed! Was expecting %s: %s' % (expect_key, json.dumps(expect_diff)))
  109. print('OK')
  110. print('Version %d OK' % version)
  111. def test_load_save(p, temp_sdkconfig_path):
  112. print('Testing load/save...')
  113. before = os.stat(temp_sdkconfig_path).st_mtime
  114. save_result = send_request(p, {'version': 2, 'save': temp_sdkconfig_path})
  115. print('Save result: %s' % (json.dumps(save_result)))
  116. assert 'error' not in save_result
  117. assert len(save_result['values']) == 0 # nothing changes when we save
  118. assert len(save_result['ranges']) == 0
  119. after = os.stat(temp_sdkconfig_path).st_mtime
  120. assert after > before # something got written to disk
  121. # Do a V2 load
  122. load_result = send_request(p, {'version': 2, 'load': temp_sdkconfig_path})
  123. print('V2 Load result: %s' % (json.dumps(load_result)))
  124. assert 'error' not in load_result
  125. assert len(load_result['values']) == 0 # in V2, loading same file should return no config items
  126. assert len(load_result['ranges']) == 0
  127. # Do a V1 load
  128. load_result = send_request(p, {'version': 1, 'load': temp_sdkconfig_path})
  129. print('V1 Load result: %s' % (json.dumps(load_result)))
  130. assert 'error' not in load_result
  131. assert len(load_result['values']) > 0 # in V1, loading same file should return all config items
  132. assert len(load_result['ranges']) > 0
  133. def test_invalid_json(p):
  134. print('Testing invalid JSON formatting...')
  135. bad_escaping = r'{ "version" : 2, "load" : "c:\some\path\not\escaped\as\json" }'
  136. p.send('%s\n' % bad_escaping)
  137. readback = expect_json(p)
  138. print(readback)
  139. assert 'json' in readback['error'][0].lower()
  140. not_really_json = 'Hello world!!'
  141. p.send('%s\n' % not_really_json)
  142. readback = expect_json(p)
  143. print(readback)
  144. assert 'json' in readback['error'][0].lower()
  145. if __name__ == '__main__':
  146. main()