Method and system for constructing unsigned external variables in unit test
By parsing the abstract syntax tree of the code under test to build an element directed graph, dynamically allocating memory to solve the validity problem of VOID* pointers, fully automated white-box unit testing of complex code is achieved, avoiding the wild pointer problem and improving test coverage.
Patent Information
- Application Number
- CN202510861535.6
- Authority / Receiving Office
- CN · China
- Patent Type
- Applications(China)
- Current Assignee / Owner
- Filing Date
- 2025-06-25
- Publication Date
- 2025-09-19
AI Technical Summary
Existing automated white-box unit testing tools cannot effectively construct VOID* pointers in the C language, resulting in the inability to guarantee test coverage of complex codes and easily triggering illegal address access.
By parsing the abstract syntax tree of the code under test, building an element directed graph, extracting the operation path of the VOID* pointer, and establishing a dynamic mapping relationship with the data structure it ultimately points to, dynamically allocates memory to ensure the validity of the VOID* pointer.
It achieves fully automated white-box unit testing of complex code, avoids wild pointer problems, and improves test coverage and test case effectiveness.
Smart Images

Figure CN120670313A_ABST
Abstract
Description
Technical Field
[0001] The present application relates to the field of automated white-box unit testing, and in particular to a method and system for constructing unsigned external variables in unit testing. Background Art
[0002] Automated white-box unit testing is a software testing methodology that allows developers to design test cases based on their understanding of a program's internal structure and logic. This approach aims to verify that each individual component or module functions as expected, and can examine elements such as paths, branches, and conditional expressions within the code. As software systems continue to grow in complexity, manually writing comprehensive test cases becomes increasingly difficult. Consequently, automated white-box unit testing tools have emerged to help improve testing efficiency and quality.
[0003] When constructing test cases for automated white-box unit testing, one can either use a constraint solver like Klee (symbolic execution) to precisely assign values to each variable, or use fuzz testing tools like AFL (American Fuzzy Lop) to construct test cases. Ultimately, neither approach solves the problem of constructing pointer variables, particularly variable-length pointers like VOID*. Consequently, they cannot guarantee applicability to complex code and are highly likely to trigger illegal address accesses during testing, making true automation difficult. Summary of the Invention
[0004] The present application provides a method for constructing unsigned external variables in unit testing, which can solve the technical problem in the prior art of being unable to construct a suitable VOID* pointer in the automated white box unit testing scenario of C language.
[0005] In a first aspect, an embodiment of the present application provides a method for constructing an unsigned type external variable in a unit test, the method for constructing an unsigned type external variable in a unit test comprising: For the code under test, parse its abstract syntax tree AST to obtain an element directed graph, and extract the dependency relationship between the operands and operators of the VOID* pointer; Determine an operation path of a VOID* pointer by recursively analyzing the element directed graph, wherein the operation path includes forced conversion, offset and / or variable length structure; Establish a dynamic mapping relationship between the VOID* pointer and the data structure it ultimately points to, and record the memory size and offset value of the data structure; Declare the external variables required for the mapping relationship in the testbed code and allocate actual memory that matches the recorded memory size and offset values.
[0006] In combination with the first aspect, in one embodiment, an element directed graph is constructed by post-order traversal of the AST of the code under test; based on the element directed graph, layer-by-layer analysis is performed starting from the final operation node of VOID* to extract the dependency relationship between operands and operators.
[0007] In combination with the first aspect, in one embodiment, the extraction of the dependency relationship is implemented through an operand stack and an operator stack, wherein when traversing the AST, the operands and operators in the AST nodes are pushed into the stack in the order of traversal, and the dependency relationship is determined based on the pop order of the stack to construct an element directed graph.
[0008] In conjunction with the first aspect, in one embodiment, the method for constructing an unsigned external variable in the unit test further includes: Register external variables through the test tool interface and inject the allocated memory address and offset value into the test environment to ensure the validity of the VOID* pointer when the test case is running.
[0009] In conjunction with the first aspect, in one embodiment, for a ternary operator node in an element directed graph, its conditional expression, true branch expression, and false branch expression are parsed to extract the operation path of the VOID* pointer in different branches; Establish a dynamic mapping relationship between the VOID* pointer and the data structure it ultimately points to, and record the memory size and offset value of the data structure; The external variables required for the mapping relationship are declared in the test bed code, and actual memory matching the true branch and the false branch is dynamically allocated, and the memory size and offset value match the recorded ones.
[0010] In combination with the first aspect, in one embodiment, for a comma operator node in an element directed graph, all its sub-expressions are parsed to extract an operation path of a VOID* pointer in a serialization operation; Before a comma operator node is pushed onto the stack, determine whether the top of the stack is a reference to a variable or structure member. If so, mark the comma operator node as a suspected cast, and record the mapping between the top of the stack reference and the cast type. According to the marked comma operator node, a dynamic mapping relationship is established only for the final reference, and the memory size and offset value of the target data structure are recorded; Declare the external variables required for the mapping relationship in the testbed code, and dynamically allocate actual memory that matches the sub-expression sequence, with the memory size and offset values matching the records.
[0011] In conjunction with the first aspect, in one embodiment, for multiple dereference expressions in the code under test, the operation path of each level of dereference is parsed; Combined with the type information of the forced conversion operator of each level of dereference, a multi-level dynamic mapping relationship is established between the VOID* pointer and the data structure it ultimately points to, and the memory size and offset value of each level of mapping are recorded; Declare the external variables required for the multi-level mapping relationship in the test bed code, and allocate actual memory that matches the memory size and offset value of each level of mapping.
[0012] In combination with the first aspect, in one implementation, when the multi-level mapping relationship includes a variable-length structure, the size of the dynamically allocated memory is expanded according to the length of the variable-length field.
[0013] In conjunction with the first aspect, in one embodiment, for a variable-length structure node in the code under test, the flexible array member expression in the AST is parsed to extract the dynamic length field of the variable-length structure; By combining the type information of the forced conversion operator, a dynamic mapping relationship is established between the VOID* pointer and the variable-length structure, and the memory offset value of the dynamic length field and the access logic of the flexible array member are recorded; The external variables required for the mapping relationship are declared in the testbed code, and memory matching the dynamic length field is dynamically allocated, where the memory size is extended according to the length of the flexible array member and the offset value matches the record.
[0014] In a second aspect, an embodiment of the present application provides a system for constructing unsigned external variables in a unit test, the system comprising: The preprocessing module is used to parse the abstract syntax tree (AST) of the code under test, obtain the element directed graph, and extract the operands and operator dependencies of the VOID* pointer from it; An analysis module, configured to recursively analyze the element directed graph to determine an operation path of a VOID* pointer, wherein the operation path includes forced conversion, offset, and / or variable-length structure; The post-processing module is used to establish a dynamic mapping relationship between the VOID* pointer and the data structure it ultimately points to, and record the memory size and offset value of the data structure; A configuration module is used to declare external variables required for the mapping relationship in the test bed code and allocate actual memory that matches the recorded memory size and offset value.
[0015] The beneficial effects of the technical solutions provided in the embodiments of the present application include: Based on AST analysis, an element directed graph is constructed to express the relationship between each operator and operand in the tested code. Then, reference chain analysis and memory analysis are performed through the element directed graph. Finally, the memory and memory layout required to test the tested function are constructed to ensure the validity of the external data of the tested function. BRIEF DESCRIPTION OF THE DRAWINGS
[0016] Figure 1 This is a flow chart of an embodiment of a method for constructing an unsigned external variable in a unit test of the present application; Figure 2 This is a schematic diagram of an abstract syntax tree of a ternary operator in an embodiment of the present application; Figure 3 This is a schematic diagram of the abstract syntax tree of the comma operator in one embodiment of the present application; Figure 4 This is a schematic diagram of an abstract syntax tree for multiple dereferences in one embodiment of the present application; Figure 5 This is a schematic diagram of a typical scenario of pointer offset in an embodiment of the present application; Figure 6 This is a schematic diagram of an abstract syntax tree of pointer offset in an embodiment of the present application; Figure 7 This is a schematic diagram of structural transformation in one embodiment of the present application; Figure 8 This is a functional module diagram of an embodiment of a system for constructing unsigned external variables in the unit test of this application. DETAILED DESCRIPTION
[0017] In order to enable those skilled in the art to better understand the present invention, the following will clearly and completely describe the technical solutions in the embodiments of the present invention in conjunction with the accompanying drawings. Obviously, the described embodiments are only part of the embodiments of the present invention, not all of the embodiments. Based on the embodiments of the present invention, all other embodiments obtained by ordinary technicians in this field without creative work are within the scope of protection of this application.
[0018] First, some technical terms in this application are explained to facilitate those skilled in the art to understand this application.
[0019] An AST (Abstract Syntax Tree) is a tree-like data structure used in computer science to represent the structure of program code. It strips away redundant details (such as brackets and semicolons) from the code, retaining only the logical structure and key syntactic elements, making it easier for compilers, interpreters, or code analysis tools to process.
[0020] In order to make the objectives, technical solutions and advantages of this application clearer, the implementation methods of this application will be further described in detail below with reference to the accompanying drawings.
[0021] In a first aspect, an embodiment of the present application provides a method for constructing an unsigned external variable in a unit test.
[0022] In one embodiment, referring to Figure 1 , Figure 1 This is a flow chart of an embodiment of a method for constructing an unsigned type external variable in a unit test of this application. Figure 1 As shown, the methods for constructing unsigned external variables in unit tests include: Step S1: parse the AST of the code under test to obtain an element directed graph, and extract the dependency relationship between the operands and operators of the VOID* pointer.
[0023] Step S2: Determine the operation path of the VOID* pointer by recursively analyzing the element directed graph, where the operation path includes forced conversion, offset and / or variable-length structure.
[0024] Step S3: Establish a dynamic mapping relationship between the VOID* pointer and the data structure it ultimately points to, and record the memory size and offset value of the data structure.
[0025] Step S4: Declare the external variables required for the mapping relationship in the test bed code, and allocate actual memory that matches the recorded memory size and offset value.
[0026] In this embodiment, during automated white-box unit testing, it is necessary to construct external variables used by the code under test, such as function input parameters and global variables. For functions that require stubbing, it is also necessary to construct the parameters and return values. At this time, if the variable is a memory pointer, the corresponding memory needs to be allocated to ensure the validity of the memory pointer value.
[0027] This patent uses Clang and abstract syntax analysis to automatically analyze the external variables of the code under test, extract the pointer elements in the external variables, analyze the required memory size, and construct the corresponding memory, ensuring the validity of the external data of the function under test. This solves the variable-length pointer construction problem that has remained unresolved in the construction of automated test cases in white-box unit testing. Only by solving the VOID* pointer construction problem in the automated white-box unit testing scenario of the C language can fully automated white-box unit test case construction for complex code be achieved.
[0028] In a specific embodiment, this patent can be adapted to a variety of automated testing tools to assist in generating test cases. For ease of explanation, taking Klee, an automated testing tool based on symbolic execution, as an example, when Klee automatically constructs test cases, it can only assign random values to pointer variables, but will not construct memory for pointers. Therefore, when testing code with complex memory operations, Klee often causes wild pointer access and makes testing impossible. This problem can be solved well using this patent. The specific workflow is as follows: Precompile the code under test to prepare the environment for subsequent AST generation.
[0029] The code under test is parsed by Clang to build an abstract syntax tree.
[0030] Analyze the abstract syntax tree, generate an element directed graph, extract the external pointer variables of the function under test and the operations on the external pointer variables.
[0031] Recursively parse the element directed graph and external pointer variables, establish the mapping relationship between the external pointer variables and the corresponding structures, specifically, determine the mapping relationship between the void* pointer and the target data structure.
[0032] According to the mapping relationship, local variables of each structure type (or its array) in the mapping are declared in the test bed code, thereby constructing the external input environment required to test the function under test.
[0033] Use the interface provided by Klee to register external variables with Klee, telling Klee to generate data for these variables during testing.
[0034] Use the interface provided by Klee to point the pointers in the external variables to the local variable addresses constructed previously. When Klee generates data for the external variables, it will assign the local variable addresses we specify to these pointers.
[0035] According to the offset relationship in the mapping, use the interface provided by Klee to assign the corresponding offset in the external variable to the difference between the actual address values of the previously constructed memory.
[0036] So far, the construction of external variables has been completed, and the tested function is constructed using external variables.
[0037] This patented adaptation encapsulates the code under test, with the outer function layer including the external variables of the function under test. The same approach is applied to global variables and stub functions. When Klee is called for testing, it constructs external input data using valid memory addresses, ensuring the test proceeds normally.
[0038] In summary, this patent eliminates the need for manual construction of complex pointer scenarios, automatically generating memory mappings through AST analysis. It supports multiple traversal methods (pre-order, post-order, and in-order), adapting to different testing tools (such as Klee) for improved compatibility. This mapping ensures that void* pointers point to valid memory, avoiding runtime errors and resolving orphan pointer issues. It also covers variable-length structure scenarios, handling complex situations such as pointer offsets, zero-length arrays, and end-of-structure transitions, improving test coverage.
[0039] Furthermore, in one embodiment, the method for constructing an unsigned external variable in the above unit test further includes: Register external variables through the test tool interface and inject the allocated memory address and offset value into the test environment to ensure the validity of the VOID* pointer when the test case is running.
[0040] In this embodiment, the address and offset value of the external variable are bound through the test tool interface to ensure the validity of the VOID* pointer when the test case is running.
[0041] Furthermore, in one embodiment, an element directed graph is constructed by traversing the AST of the code under test; based on the element directed graph, layer-by-layer analysis is performed starting from the final operation node of VOID* to extract the dependency relationship between operands and operators.
[0042] In this embodiment, when traversing the AST, the final operation node of the void* pointer (such as variable reference, dereference operator, etc.) is used as the starting point, and its dependency relationship is analyzed step by step upward.
[0043] When traversing the AST, the stack structure records the operands (such as variables a and b) and operators (such as the ternary operator, comma operator, cast, etc.) on the path, and determines their dependencies based on the context in the stack. For example, the dependency between the conditional branch (var == NULL) and the return value (a or b) of the ternary operator, or the association between the last operand (such as d) and the cast in the comma operator.
[0044] By extracting these dependencies, we ultimately form a directed element graph, representing the hierarchical relationship between operands and operators in the function under test. The purpose of constructing this directed graph is to clarify the use cases of void* pointers (such as casts and offset operations), thereby guiding subsequent memory allocation and test environment construction.
[0045] Furthermore, in one embodiment, the extraction of the above-mentioned dependency relationships is implemented through an operand stack and an operator stack, wherein when traversing the AST, the operands and operators in the AST nodes are pushed into the stack in the order of traversal, and the dependency relationships are determined based on the pop order of the stack to construct an element directed graph.
[0046] In this embodiment, the stack structure is used to store operands (such as variables a and d) and operators (such as the ternary operator, comma operator, forced conversion, etc.). For example, in the comma operator scenario, the binary operator at the top of the stack is marked as "suspected forced conversion", and operand d is recorded as the final reference.
[0047] When traversing, the order in which the stack is popped (i.e., the path from leaves to roots) reflects the execution order and dependency hierarchy of the code. For example: For the code void *c = (TYPE *)(var == NULL ? a : b);, the traversal path is a → forced conversion → ternary operator → root node. The stack popping order is that a depends on forced conversion, and forced conversion depends on ternary operator.
[0048] Through this order, the hierarchical dependency between operands and operators can be made clear, thereby constructing a directed graph.
[0049] The order in which the stack is popped is directly used to generate the edges of the directed graph, for example: Operand a → Operator "Coercion" → Operator "Ternary Operator" → Operator "Assignment" → Root Node (Function Call).
[0050] The nodes of a directed graph include operands (such as a, b) and operators (such as ?:, +, *), and the edges represent dependencies in the code logic (such as "forced conversion" depends on the value of a).
[0051] In one specific embodiment, the implementation of this patent requires a bottom-up traversal of the AST. This involves selecting a leaf node (such as a variable reference or constant) as the starting point, traversing the tree step-by-step toward the root node via the parent node pointer, while maintaining a list-based, one-dimensional data structure (e.g., a stack) to record node information along the path. This stack structure is used to store the complete path from leaf to root, facilitating analysis of operators, type conversions, and data structure references within the path.
[0052] The conditions for node stacking are divided into nodes that must be stacked and nodes that do not need to be stacked: Nodes that must be pushed onto the stack include variable references (such as a), type conversions (such as (TYPE*)), ternary operators, comma operators, and dereference operators (such as *e). These nodes directly participate in pointer type conversions or memory operations and must be included in path analysis.
[0053] Nodes that do not need to be pushed onto the stack, such as bracket operators (such as (), only represent priorities or function calls in the AST and have no direct impact on pointer types and memory allocation, so they do not need to be saved on the stack.
[0054] Node processing strategies include path recording and scenario differentiation processing: Path recording means starting from a leaf node and traversing the AST upwards, pushing the nodes that meet the conditions onto the stack layer by layer. The top of the stack always represents the type of node currently traversed (such as a variable reference, operator, or type conversion).
[0055] Scenario differentiation processing includes: For ternary operators, if the path contains a ternary operator (such as var == NULL ? a : b), you need to analyze the memory requirements corresponding to its conditional branches and return values (for example, type conversions of a and b).
[0056] For the comma operator, it is represented as a nested binary operator chain in the AST. It is necessary to determine whether it is a reference to the final value (such as the last operand d) and mark the binary operator at the top of the stack as a suspected forced conversion.
[0057] For casts and mappings, when traversing a cast node (e.g., (TYPE*)), the target type of the cast is determined based on the context in the stack, and a mapping relationship is established between the source node (e.g., a) and the target type (e.g., TYPE*). For example, if a is an external variable, memory space of at least sizeof(TYPE) is allocated to ensure that invalid addresses are not accessed during dereference.
[0058] For recursive parsing of variable-length structures, for scenarios involving pointer offsets, zero-length arrays at the end, or nested structure conversions, it is necessary to recursively analyze the AST path and dynamically expand the memory allocation strategy (such as constructing multiple memory segments or new structures).
[0059] The technical objectives achieved through the above operations are as follows: Accurately analyze pointer usage, save the path from leaves to roots through the stack structure, and track the actual usage of void* pointers in the code (such as type conversion and offset operations), thereby providing accurate memory allocation basis for test case generation.
[0060] Avoid tool limitations. For example, symbolic execution tools like Klee can only assign random values to pointer variables when constructing test data, but cannot allocate actual memory. This method explicitly defines the memory structure pointed to by pointer variables through AST traversal and stack path analysis, thus solving the problem of orphan pointers.
[0061] It's important to note that the stack push rules only reflect the node types that must be considered for this solution, and do not impose hard restrictions on implementation. In actual development, the processing logic can be adjusted based on the AST parsing capabilities of specific tools, for example, allowing certain nodes to only record metadata without being pushed onto the stack.
[0062] Furthermore, in one embodiment, for a ternary operator node in an element directed graph, its conditional expression, true branch expression, and false branch expression are parsed to extract the operation path of the VOID* pointer in different branches.
[0063] Establish a dynamic mapping relationship between the VOID* pointer and the data structure it ultimately points to, and record the memory size and offset value of the data structure.
[0064] Declare the external variables required for the above mapping relationship in the testbed code, and dynamically allocate actual memory matching the true and false branches, with the memory size and offset values matching the recorded ones.
[0065] In this embodiment, the typical encoding method of the ternary operator is: void *c = (TYPE *)(var == NULL? a : b). The parsed abstract syntax tree is as follows Figure 2 As shown, the ternary operator has three subtrees. The first subtree is the conditional judgment, and the second and third subtrees are the return values.
[0066] This patent traverses from the leaf to the root. Taking reference a as an example, traverse from the reference a node to the parent node until the root: The reference a is pushed onto the stack, implicit conversion, ternary operation, and brackets are not pushed onto the stack.
[0067] When a forced conversion to TYPE* is encountered, it can be marked that a in this function is forced to be converted to TYPE*.
[0068] A mapping relationship is formed. When building the test bed, if a is an external variable, the memory occupied by a is not less than the memory of TYPE.
[0069] Furthermore, in one embodiment, for a comma operator node in an element directed graph, all its sub-expressions are parsed to extract the operation path of the VOID* pointer in the serialization operation.
[0070] Before the comma operator node is pushed onto the stack, determine whether the top of the stack is a reference to a variable or structure member. If so, mark the comma operator node as a suspected cast, and record the mapping between the top of the stack reference and the cast type.
[0071] According to the marked comma operator node, a dynamic mapping relationship is established only for the final reference, and the memory size and offset value of the target data structure are recorded. Declare the external variables required for the above mapping in the testbed code, and dynamically allocate actual memory that matches the sub-expression sequence, with the memory size and offset values matching the recorded ones.
[0072] In this embodiment, the typical encoding of the comma operator is: void *f = (TYPE *)(a, b, c, d). The parsed abstract syntax tree is as follows: Figure 3 As shown by Figure 3 As you can see, the comma operator is actually parsed as a series of nested binary operators in the abstract syntax tree, each of which is a comma operator. Due to the characteristics of the comma operator, the last element is returned, which is represented in the abstract syntax tree as the outermost comma operator and its second subtree.
[0073] Unlike ternary operators, binary operators also need to be pushed onto the stack. Therefore, before pushing a binary operator onto the stack, a check is performed to see if the top of the stack is a reference to a variable or data structure member. If so, the binary operator is marked as a suspected cast and the statement at the top of the stack is pointed to.
[0074] For example, when traversing upward from reference d, the top of the stack is the outermost binary operator because the parentheses are not pushed onto the stack, and this operator is marked as a forced conversion. Traversing further to forced conversion to TYPE*, the top of the stack is a binary operator and is marked as a suspected forced conversion, so a mapping relationship is formed, and reference d needs to be forced to TYPE*.
[0075] When traversing from other references, such as reference c, before the outermost binary operator is pushed onto the stack, the top of the stack is checked and another binary operator is found. At this time, it will not be marked as a suspected forced conversion, so no mapping will be performed.
[0076] Furthermore, in one embodiment, for multiple dereference expressions in the tested code, the operation path of each level of dereference is parsed.
[0077] Combined with the type information of the forced conversion operator of each level of dereference, a multi-level dynamic mapping relationship is established between the VOID* pointer and the data structure it ultimately points to, and the memory size and offset value of each level of mapping are recorded.
[0078] Declare the external variables required for the above multi-level mapping relationship in the testbed code, and allocate actual memory that matches the memory size and offset value of each level of mapping.
[0079] In this embodiment, the typical encoding of multiple dereferences is: TYPE *tgd = ***e. The parsed abstract syntax tree is as follows: Figure 4 As shown, the dereference needs to be marked level by level. At the same time, since tgd is of type TYPE and e is of type void, a mapping needs to be established between e and TYPE.
[0080] Furthermore, in one embodiment, when the multi-level mapping relationship includes a variable-length structure, the size of the dynamically allocated memory is expanded according to the length of the variable-length field.
[0081] In this embodiment, when parsing the syntax tree, it is necessary to determine the actual length of the memory pointed to by the VOID* pointer. In many cases, although VOID* is converted into a certain structure, the actual length occupied will be longer than the length of the structure. This is mainly reflected in the following three situations: Offsets a pointer. The offset can be a constant or a variable. The offset can also be recursive.
[0082] The last field of the structure is a zero-length array.
[0083] The last field of the structure is a structure. When using it, this structure must be forced to convert.
[0084] Furthermore, in one embodiment, for a variable-length structure node in the code under test, the flexible array member expression in the AST is parsed to extract the dynamic length field of the variable-length structure.
[0085] By combining the type information of the forced conversion operator, a dynamic mapping relationship is established between the VOID* pointer and the variable-length structure, and the memory offset value of the dynamic length field and the access logic of the flexible array member are recorded.
[0086] In the testbed code, declare the external variables required for the above mapping relationship and dynamically allocate memory matching the dynamic length field, where the memory size is extended according to the length of the flexible array member and the offset value matches the record.
[0087] In this embodiment, when multiple dereferences and variable-length structures are involved, an element directed graph is still required, but the processing method needs to be specifically adapted according to the complexity of the operation path.
[0088] All VOID* pointer operation paths (including multiple dereferences and variable-length structures) are parsed through the AST and mapped into nodes of the element-directed graph. For multiple dereferences, multi-level dereference operators (such as *ptr2 and *ptr1) are parsed. For variable-length structures, flexible array members (data[]) and dynamic length fields (len) are parsed.
[0089] Multiple dereferences record the memory size of each level of dereference (such as MyStruct**, MyStruct*, MyStruct). Variable-length structures record the offsets of flexible array members and the offsets of dynamic-length fields (such as offsetof(MyStruct, data) and offsetof(MyStruct, len)).
[0090] Multiple dereferences declare multi-level pointer variables (such as MyStruct***) and allocate matching memory. Variable-length structures declare variable-length structure variables (such as MyStruct s) and dynamically expand the memory size based on len.
[0091] In a specific embodiment, a typical scenario of pointer offset is as follows: Figure 5 As shown, Figure 5There is a data structure called TYPE1, which contains a variable called offset1. By offsetting the pointer to TYPE1 by offset1, we can get the memory of the TYPE2 data structure. The TYPE2 data structure also contains a variable called offset2. By offsetting the pointer to TYPE2 by offset2, we can get TYPE3.
[0092] For the convenience of explanation, this patent only needs to explain one layer of offset, and multiple layers can be directly used recursively. The typical code of pointer offset is as follows: TYPE2 *ptr = (char *)var + var->offset, which will form Figure 6 The abstract syntax tree for a pointer offset is shown. When traversing the abstract syntax tree, if a binary operator is encountered and the expression in one subtree returns a pointer, while the value in the other subtree is an integer variable or constant, this can be confirmed to be a pointer offset. As shown in the figure above, the first subtree of the binary operator is actually a reference to a var pointer, which is cast to TYPE1. The second subtree accesses the offset1 member variable after var is cast to TYPE1.
[0093] To avoid iterative calculations, var is mapped into two memory segments, storing data structures TYPE1 and TYPE2 respectively. At the same time, the offset variable of TYPE1 is assigned the difference between the pointer values of the two memory segments.
[0094] For recursive scenarios, the memory is mapped one by one according to the above method. If there are N offsets, N+1 memory segments are constructed. Each offset stores the difference between the memory pointer values.
[0095] In another specific embodiment, when the last field of the structure is a zero-length array, data is generally written through memory operations such as Memcpy and Strncpy, or read or written through functions such as address / string conversion. Generally, such an array will not be too long.
[0096] In this case, the length needs to be estimated based on the length field of the corresponding function. If the length cannot be estimated, the original data structure can be appropriately expanded.
[0097] At this time when mapping memory, such as Figure 7 As shown, a new structure needs to be constructed based on the original structure.
[0098] The end of the TYPE4 structure is a zero-length array. Analysis indicates that TYPE4 needs to be extended by 10 bytes. Therefore, a new TYPE5 data structure is created, with the first element being TYPE4 and the second being a 10-byte array. When creating the mapping, the TYPE4 pointer variable is mapped to a TYPE5 variable.
[0099] In another specific embodiment, when the last field of a structure is a structure, the structure is forcibly converted when used.
[0100] The processing method in this case is similar to the case where the last field of a structure is a zero-length array. Recursive analysis is required to determine whether the last field of the structure has any variable lengths, thereby estimating the actual length required for the last structure. A new structure is constructed using the same method as the last field of a structure is a zero-length array, and the pointer variable is mapped to the new structure.
[0101] In a second aspect, an embodiment of the present application also provides a system for constructing unsigned external variables in unit testing.
[0102] In one embodiment, referring to Figure 8 , Figure 8 This is a functional module diagram of an embodiment of a system for constructing unsigned type external variables in unit testing of this application. Figure 8 As shown, the system for constructing unsigned type external variables in unit testing includes: The preprocessing module 1 is used to parse the abstract syntax tree AST of the code under test, obtain an element directed graph, and extract the dependency relationship between the operands and operators of the VOID* pointer.
[0103] The analysis module 2 is used to determine the operation path of the VOID* pointer by recursively analyzing the above-mentioned element directed graph, and the above-mentioned operation path includes forced conversion, offset and / or variable-length structure.
[0104] The post-processing module 3 is used to establish a dynamic mapping relationship between the VOID* pointer and the data structure it ultimately points to, and record the memory size and offset value of the data structure.
[0105] The configuration module 4 is used to declare the external variables required for the above mapping relationship in the test bed code and allocate actual memory that matches the recorded memory size and offset value.
[0106] In this embodiment, there is no need to modify the source code, and all code modification work is completed in the preprocessing file without destroying the code logic.
[0107] Reduce the workload of programmers. Using the clang AST tool, you can automatically identify and batch modify the code in the preprocessing file.
[0108] Accurately configure the constructor input parameter type to improve the accuracy of automated unit testing and facilitate the promotion and application of tools.
[0109] Among them, the functional implementation of each module in the system for constructing unsigned type external variables in the above unit test corresponds to the steps in the method embodiment for constructing unsigned type external variables in the above unit test, and their functions and implementation processes will not be repeated here one by one.
[0110] It should be noted that the serial numbers of the above-mentioned embodiments of the present application are for description only and do not represent the advantages or disadvantages of the embodiments.
[0111] The terms "including" and "having" and any variations thereof in the specification and claims of this application and the above-mentioned drawings are intended to cover non-exclusive inclusions. For example, a process, method, system, product or device that includes a series of steps or units is not limited to the listed steps or units, but optionally includes steps or units that are not listed, or optionally includes other steps or units inherent to these processes, methods, products or devices. The terms "first", "second" and "third" are used to distinguish different objects, etc., and do not represent a sequence, nor do they limit the "first", "second" and "third" to different types.
[0112] In the description of the embodiments of this application, the words "exemplary," "for example," or "for example" are used to indicate examples, illustrations, or descriptions. Any embodiment or design described as "exemplary," "for example," or "for example" in the embodiments of this application should not be construed as being preferred or advantageous over other embodiments or designs. Rather, the use of words such as "exemplary," "for example," or "for example" is intended to present the relevant concepts in a concrete manner.
[0113] In the description of the embodiments of the present application, unless otherwise specified, “ / ” means or, for example, A / B can mean A or B; “and / or” in the text is merely a description of the association relationship of associated objects, indicating that three relationships may exist, for example, A and / or B can mean: A exists alone, A and B exist at the same time, and B exists alone. In addition, in the description of the embodiments of the present application, “multiple” refers to two or more than two.
[0114] In some processes described in the embodiments of the present application, multiple operations or steps are included that appear in a specific order. However, it should be understood that these operations or steps may not be performed in the order in which they appear in the embodiments of the present application or may be performed in parallel. The sequence numbers of the operations are only used to distinguish between different operations, and the sequence numbers themselves do not represent any order of execution. In addition, these processes may include more or fewer operations, and these operations or steps may be performed in sequence or in parallel, and these operations or steps may be combined.
[0115] Through the description of the above embodiments, those skilled in the art can clearly understand that the above-mentioned embodiment methods can be implemented by means of software plus the necessary general hardware platform. Of course, they can also be implemented by hardware, but in many cases the former is a better implementation method. Based on this understanding, the technical solution of this application, or the part that contributes to the existing technology, can be embodied in the form of a software product. This computer software product is stored in a storage medium (such as ROM / RAM, magnetic disk, optical disk) as described above and includes a number of instructions for enabling a terminal device to execute the methods described in each embodiment of this application.
[0116] The above are only preferred embodiments of the present application and do not limit the patent scope of the present application. Any equivalent structure or equivalent process transformation made using the contents of the present application specification and drawings, or directly or indirectly applied in other related technical fields, are also included in the patent protection scope of the present application.
Claims
1. A method for constructing an unsigned external variable in a unit test, characterized in that: The method for constructing an unsigned type external variable in the unit test includes: For the code under test, parse its abstract syntax tree AST to obtain an element directed graph, and extract the dependency relationship between the operands and operators of the VOID* pointer; Determine an operation path of a VOID* pointer by recursively analyzing the element directed graph, wherein the operation path includes forced conversion, offset and / or variable length structure; Establish a dynamic mapping relationship between the VOID* pointer and the data structure it ultimately points to, and record the memory size and offset value of the data structure; Declare the external variables required for the mapping relationship in the testbed code and allocate actual memory that matches the recorded memory size and offset values.
2. The method for constructing an unsigned type external variable in a unit test according to claim 1, wherein: By traversing the AST of the code under test in post-order, an element-directed graph is constructed. Based on the element-directed graph, layer-by-layer analysis is performed starting from the final operation node of VOID* to extract the dependency relationship between operands and operators.
3. The method for constructing an unsigned type external variable in a unit test according to claim 2, wherein: The extraction of the dependency relationship is achieved through an operand stack and an operator stack, wherein when traversing the AST, the operands and operators in the AST nodes are pushed into the stack in the traversal order, and the dependency relationship is determined based on the pop order of the stack to construct an element directed graph.
4. The method for constructing an unsigned type external variable in a unit test according to claim 1, wherein: The method for constructing an unsigned type external variable in the unit test also includes: Register external variables through the test tool interface and inject the allocated memory address and offset value into the test environment to ensure the validity of the VOID* pointer when the test case is running.
5. The method for constructing an unsigned type external variable in a unit test according to claim 1, wherein: For the ternary operator nodes in the element directed graph, parse their conditional expressions, true branch expressions, and false branch expressions, and extract the operation paths of the VOID* pointer in different branches; Establish a dynamic mapping relationship between the VOID* pointer and the data structure it ultimately points to, and record the memory size and offset value of the data structure; The external variables required for the mapping relationship are declared in the test bed code, and actual memory matching the true branch and the false branch is dynamically allocated, and the memory size and offset value match the recorded ones.
6. The method for constructing an unsigned type external variable in a unit test according to claim 1, wherein: For the comma operator node in the element directed graph, parse all its sub-expressions and extract the operation path of the VOID* pointer in the serialization operation; Before a comma operator node is pushed onto the stack, determine whether the top of the stack is a reference to a variable or structure member. If so, mark the comma operator node as a suspected cast, and record the mapping between the top of the stack reference and the cast type. According to the marked comma operator node, a dynamic mapping relationship is established only for the final reference, and the memory size and offset value of the target data structure are recorded; Declare the external variables required for the mapping relationship in the testbed code, and dynamically allocate actual memory that matches the sub-expression sequence, with the memory size and offset values matching the records.
7. The method for constructing an unsigned type external variable in a unit test according to claim 1, wherein: For multiple dereference expressions in the tested code, parse the operation path of each level of dereference; Combined with the type information of the forced conversion operator of each level of dereference, a multi-level dynamic mapping relationship is established between the VOID* pointer and the data structure it ultimately points to, and the memory size and offset value of each level of mapping are recorded; Declare the external variables required for the multi-level mapping relationship in the test bed code, and allocate actual memory that matches the memory size and offset value of each level of mapping.
8. The method for constructing an unsigned type external variable in a unit test according to claim 7, wherein: When the multi-level mapping relationship includes a variable-length structure, the size of the dynamically allocated memory is expanded according to the length of the variable-length field.
9. The method for constructing an unsigned type external variable in a unit test according to claim 1, wherein: For the variable-length structure nodes in the code under test, parse the flexible array member expressions in its AST and extract the dynamic length field of the variable-length structure; By combining the type information of the forced conversion operator, a dynamic mapping relationship is established between the VOID* pointer and the variable-length structure, and the memory offset value of the dynamic length field and the access logic of the flexible array member are recorded; The external variables required for the mapping relationship are declared in the testbed code, and memory matching the dynamic length field is dynamically allocated, where the memory size is extended according to the length of the flexible array member and the offset value matches the record.
10. A system for constructing unsigned external variables in unit testing, characterized in that: The system includes: The preprocessing module is used to parse the abstract syntax tree (AST) of the code under test, obtain the element directed graph, and extract the operands and operator dependencies of the VOID* pointer from it; An analysis module, configured to recursively analyze the element directed graph to determine an operation path of a VOID* pointer, wherein the operation path includes forced conversion, offset, and / or variable-length structure; The post-processing module is used to establish a dynamic mapping relationship between the VOID* pointer and the data structure it ultimately points to, and record the memory size and offset value of the data structure; A configuration module is used to declare external variables required for the mapping relationship in the test bed code and allocate actual memory that matches the recorded memory size and offset value.