blob: 41a24d1a68dc464b07a2938cd23e3ae2e7b78d33 [file] [log] [blame]
Gilles Peskinebdffaea2021-01-12 00:37:38 +01001#!/usr/bin/env python3
2
3"""Edit test cases to use PSA dependencies instead of classic dependencies.
4"""
5
6# Copyright The Mbed TLS Contributors
7# SPDX-License-Identifier: Apache-2.0
8#
9# Licensed under the Apache License, Version 2.0 (the "License"); you may
10# not use this file except in compliance with the License.
11# You may obtain a copy of the License at
12#
13# http://www.apache.org/licenses/LICENSE-2.0
14#
15# Unless required by applicable law or agreed to in writing, software
16# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18# See the License for the specific language governing permissions and
19# limitations under the License.
20
21import os
Gilles Peskine82ebaa42021-01-12 00:45:14 +010022import re
Gilles Peskinebdffaea2021-01-12 00:37:38 +010023import sys
24
Gilles Peskine82ebaa42021-01-12 00:45:14 +010025def updated_dependencies(file_name, function_name, arguments, dependencies):
26 """Rework the list of dependencies into PSA_WANT_xxx.
27
28 Remove classic crypto dependencies such as MBEDTLS_RSA_C,
29 MBEDTLS_PKCS1_V15, etc.
30
31 Add systematic PSA_WANT_xxx dependencies based on the called function and
32 its arguments, replacing existing PSA_WANT_xxx dependencies.
33 """
34 return dependencies #TODO
35
Gilles Peskine45e9e732021-01-12 00:47:03 +010036def keep_manual_dependencies(file_name, function_name, arguments):
37 #pylint: disable=unused-argument
38 """Declare test functions with unusual dependencies here."""
39 return False
40
Gilles Peskinebdffaea2021-01-12 00:37:38 +010041def process_data_stanza(stanza, file_name, test_case_number):
42 """Update PSA crypto dependencies in one Mbed TLS test case.
43
44 stanza is the test case text (including the description, the dependencies,
45 the line with the function and arguments, and optionally comments). Return
46 a new stanza with an updated dependency line, preserving everything else
47 (description, comments, arguments, etc.).
48 """
Gilles Peskine82ebaa42021-01-12 00:45:14 +010049 if not stanza.lstrip('\n'):
50 # Just blank lines
51 return stanza
52 # Expect 2 or 3 non-comment lines: description, optional dependencies,
53 # function-and-arguments.
54 content_matches = list(re.finditer(r'^[\t ]*([^\t #].*)$', stanza, re.M))
55 if len(content_matches) < 2:
56 raise Exception('Not enough content lines in paragraph {} in {}'
57 .format(test_case_number, file_name))
58 if len(content_matches) > 3:
59 raise Exception('Too many content lines in paragraph {} in {}'
60 .format(test_case_number, file_name))
61 arguments = content_matches[-1].group(0).split(':')
62 function_name = arguments.pop(0)
Gilles Peskine45e9e732021-01-12 00:47:03 +010063 if keep_manual_dependencies(file_name, function_name, arguments):
64 return stanza
Gilles Peskine82ebaa42021-01-12 00:45:14 +010065 if len(content_matches) == 2:
66 # Insert a line for the dependencies. If it turns out that there are
67 # no dependencies, we'll remove that empty line below.
68 dependencies_location = content_matches[-1].start()
69 text_before = stanza[:dependencies_location]
70 text_after = '\n' + stanza[dependencies_location:]
71 old_dependencies = []
72 dependencies_leader = 'depends_on:'
73 else:
74 dependencies_match = content_matches[-2]
75 text_before = stanza[:dependencies_match.start()]
76 text_after = stanza[dependencies_match.end():]
77 old_dependencies = dependencies_match.group(0).split(':')
78 dependencies_leader = old_dependencies.pop(0) + ':'
79 if dependencies_leader != 'depends_on:':
80 raise Exception('Next-to-last line does not start with "depends_on:"'
81 ' in paragraph {} in {}'
82 .format(test_case_number, file_name))
83 new_dependencies = updated_dependencies(file_name, function_name, arguments,
84 old_dependencies)
85 if new_dependencies:
86 stanza = (text_before +
87 dependencies_leader + ':'.join(new_dependencies) +
88 text_after)
89 else:
90 # The dependencies have become empty. Remove the depends_on: line.
91 assert text_after[0] == '\n'
92 stanza = text_before + text_after[1:]
Gilles Peskinebdffaea2021-01-12 00:37:38 +010093 return stanza
94
95def process_data_file(file_name, old_content):
96 """Update PSA crypto dependencies in an Mbed TLS test suite data file.
97
98 Process old_content (the old content of the file) and return the new content.
99 """
100 old_stanzas = old_content.split('\n\n')
101 new_stanzas = [process_data_stanza(stanza, file_name, n)
102 for n, stanza in enumerate(old_stanzas, start=1)]
103 return '\n\n'.join(new_stanzas)
104
105def update_file(file_name, old_content, new_content):
106 """Update the given file with the given new content.
107
108 Replace the existing file. The previous version is renamed to *.bak.
109 Don't modify the file if the content was unchanged.
110 """
111 if new_content == old_content:
112 return
113 backup = file_name + '.bak'
114 tmp = file_name + '.tmp'
115 with open(tmp, 'w', encoding='utf-8') as new_file:
116 new_file.write(new_content)
117 os.replace(file_name, backup)
118 os.replace(tmp, file_name)
119
120def process_file(file_name):
121 """Update PSA crypto dependencies in an Mbed TLS test suite data file.
122
123 Replace the existing file. The previous version is renamed to *.bak.
124 Don't modify the file if the content was unchanged.
125 """
126 old_content = open(file_name, encoding='utf-8').read()
127 if file_name.endswith('.data'):
128 new_content = process_data_file(file_name, old_content)
129 else:
130 raise Exception('File type not recognized: {}'
131 .format(file_name))
132 update_file(file_name, old_content, new_content)
133
134def main(args):
135 for file_name in args:
136 process_file(file_name)
137
138if __name__ == '__main__':
139 main(sys.argv[1:])