1#!/usr/bin/env python3 2 3""" 4Unit tests for struct/union member extractor class. 5""" 6 7 8import os 9import re 10import unittest 11import sys 12 13from unittest.mock import MagicMock 14 15SRC_DIR = os.path.dirname(os.path.realpath(__file__)) 16sys.path.insert(0, os.path.join(SRC_DIR, "../lib/python")) 17 18from kdoc.c_lex import CTokenizer 19from unittest_helper import run_unittest 20 21 22# 23# List of tests. 24# 25# The code will dynamically generate one test for each key on this dictionary. 26# 27 28def make_private_test(name, data): 29 """ 30 Create a test named ``name`` using parameters given by ``data`` dict. 31 """ 32 33 def test(self): 34 """In-lined lambda-like function to run the test""" 35 tokens = CTokenizer(data["source"]) 36 result = str(tokens) 37 38 # 39 # Avoid whitespace false positives 40 # 41 result = re.sub(r"\s++", " ", result).strip() 42 expected = re.sub(r"\s++", " ", data["trimmed"]).strip() 43 44 msg = f"failed when parsing this source:\n{data['source']}" 45 self.assertEqual(result, expected, msg=msg) 46 47 return test 48 49#: Tests to check if CTokenizer is handling properly public/private comments. 50TESTS_PRIVATE = { 51 # 52 # Simplest case: no private. Ensure that trimming won't affect struct 53 # 54 "__run__": make_private_test, 55 "no private": { 56 "source": """ 57 struct foo { 58 int a; 59 int b; 60 int c; 61 }; 62 """, 63 "trimmed": """ 64 struct foo { 65 int a; 66 int b; 67 int c; 68 }; 69 """, 70 }, 71 72 # 73 # Play "by the books" by always having a public in place 74 # 75 76 "balanced_private": { 77 "source": """ 78 struct foo { 79 int a; 80 /* private: */ 81 int b; 82 /* public: */ 83 int c; 84 }; 85 """, 86 "trimmed": """ 87 struct foo { 88 int a; 89 int c; 90 }; 91 """, 92 }, 93 94 "balanced_non_greddy_private": { 95 "source": """ 96 struct foo { 97 int a; 98 /* private: */ 99 int b; 100 /* public: */ 101 int c; 102 /* private: */ 103 int d; 104 /* public: */ 105 int e; 106 107 }; 108 """, 109 "trimmed": """ 110 struct foo { 111 int a; 112 int c; 113 int e; 114 }; 115 """, 116 }, 117 118 "balanced_inner_private": { 119 "source": """ 120 struct foo { 121 struct { 122 int a; 123 /* private: ignore below */ 124 int b; 125 /* public: but this should not be ignored */ 126 }; 127 int b; 128 }; 129 """, 130 "trimmed": """ 131 struct foo { 132 struct { 133 int a; 134 }; 135 int b; 136 }; 137 """, 138 }, 139 140 # 141 # Test what happens if there's no public after private place 142 # 143 144 "unbalanced_private": { 145 "source": """ 146 struct foo { 147 int a; 148 /* private: */ 149 int b; 150 int c; 151 }; 152 """, 153 "trimmed": """ 154 struct foo { 155 int a; 156 }; 157 """, 158 }, 159 160 "unbalanced_inner_private": { 161 "source": """ 162 struct foo { 163 struct { 164 int a; 165 /* private: ignore below */ 166 int b; 167 /* but this should not be ignored */ 168 }; 169 int b; 170 }; 171 """, 172 "trimmed": """ 173 struct foo { 174 struct { 175 int a; 176 }; 177 int b; 178 }; 179 """, 180 }, 181 182 "unbalanced_struct_group_tagged_with_private": { 183 "source": """ 184 struct page_pool_params { 185 struct_group_tagged(page_pool_params_fast, fast, 186 unsigned int order; 187 unsigned int pool_size; 188 int nid; 189 struct device *dev; 190 struct napi_struct *napi; 191 enum dma_data_direction dma_dir; 192 unsigned int max_len; 193 unsigned int offset; 194 }; 195 struct_group_tagged(page_pool_params_slow, slow, 196 struct net_device *netdev; 197 unsigned int queue_idx; 198 unsigned int flags; 199 /* private: used by test code only */ 200 void (*init_callback)(netmem_ref netmem, void *arg); 201 void *init_arg; 202 }; 203 }; 204 """, 205 "trimmed": """ 206 struct page_pool_params { 207 struct_group_tagged(page_pool_params_fast, fast, 208 unsigned int order; 209 unsigned int pool_size; 210 int nid; 211 struct device *dev; 212 struct napi_struct *napi; 213 enum dma_data_direction dma_dir; 214 unsigned int max_len; 215 unsigned int offset; 216 }; 217 struct_group_tagged(page_pool_params_slow, slow, 218 struct net_device *netdev; 219 unsigned int queue_idx; 220 unsigned int flags; 221 }; 222 }; 223 """, 224 }, 225 226 "unbalanced_two_struct_group_tagged_first_with_private": { 227 "source": """ 228 struct page_pool_params { 229 struct_group_tagged(page_pool_params_slow, slow, 230 struct net_device *netdev; 231 unsigned int queue_idx; 232 unsigned int flags; 233 /* private: used by test code only */ 234 void (*init_callback)(netmem_ref netmem, void *arg); 235 void *init_arg; 236 }; 237 struct_group_tagged(page_pool_params_fast, fast, 238 unsigned int order; 239 unsigned int pool_size; 240 int nid; 241 struct device *dev; 242 struct napi_struct *napi; 243 enum dma_data_direction dma_dir; 244 unsigned int max_len; 245 unsigned int offset; 246 }; 247 }; 248 """, 249 "trimmed": """ 250 struct page_pool_params { 251 struct_group_tagged(page_pool_params_slow, slow, 252 struct net_device *netdev; 253 unsigned int queue_idx; 254 unsigned int flags; 255 }; 256 struct_group_tagged(page_pool_params_fast, fast, 257 unsigned int order; 258 unsigned int pool_size; 259 int nid; 260 struct device *dev; 261 struct napi_struct *napi; 262 enum dma_data_direction dma_dir; 263 unsigned int max_len; 264 unsigned int offset; 265 }; 266 }; 267 """, 268 }, 269 "unbalanced_without_end_of_line": { 270 "source": """ \ 271 struct page_pool_params { \ 272 struct_group_tagged(page_pool_params_slow, slow, \ 273 struct net_device *netdev; \ 274 unsigned int queue_idx; \ 275 unsigned int flags; 276 /* private: used by test code only */ 277 void (*init_callback)(netmem_ref netmem, void *arg); \ 278 void *init_arg; \ 279 }; \ 280 struct_group_tagged(page_pool_params_fast, fast, \ 281 unsigned int order; \ 282 unsigned int pool_size; \ 283 int nid; \ 284 struct device *dev; \ 285 struct napi_struct *napi; \ 286 enum dma_data_direction dma_dir; \ 287 unsigned int max_len; \ 288 unsigned int offset; \ 289 }; \ 290 }; 291 """, 292 "trimmed": """ 293 struct page_pool_params { 294 struct_group_tagged(page_pool_params_slow, slow, 295 struct net_device *netdev; 296 unsigned int queue_idx; 297 unsigned int flags; 298 }; 299 struct_group_tagged(page_pool_params_fast, fast, 300 unsigned int order; 301 unsigned int pool_size; 302 int nid; 303 struct device *dev; 304 struct napi_struct *napi; 305 enum dma_data_direction dma_dir; 306 unsigned int max_len; 307 unsigned int offset; 308 }; 309 }; 310 """, 311 }, 312} 313 314#: Dict containing all test groups fror CTokenizer 315TESTS = { 316 "TestPublicPrivate": TESTS_PRIVATE, 317} 318 319def setUp(self): 320 self.maxDiff = None 321 322def build_test_class(group_name, table): 323 """ 324 Dynamically creates a class instance using type() as a generator 325 for a new class derivated from unittest.TestCase. 326 327 We're opting to do it inside a function to avoid the risk of 328 changing the globals() dictionary. 329 """ 330 331 class_dict = { 332 "setUp": setUp 333 } 334 335 run = table["__run__"] 336 337 for test_name, data in table.items(): 338 if test_name == "__run__": 339 continue 340 341 class_dict[f"test_{test_name}"] = run(test_name, data) 342 343 cls = type(group_name, (unittest.TestCase,), class_dict) 344 345 return cls.__name__, cls 346 347# 348# Create classes and add them to the global dictionary 349# 350for group, table in TESTS.items(): 351 t = build_test_class(group, table) 352 globals()[t[0]] = t[1] 353 354# 355# main 356# 357if __name__ == "__main__": 358 run_unittest(__file__) 359