|
| 1 | +# Symbolic analysis of static initializers |
| 2 | + |
| 3 | +## Current behavior |
| 4 | +### Problem |
| 5 | +Before [Prohibit to set static fields from library classes](https://github.com/UnitTestBot/UTBotJava/pull/699), every static field touched outside **_clinit_** block (so-called **_meaningful_** static fields) was stored in **modelBefore** and **modelAfter** and accordingly was set (and correspondingly reset for tests isolation) in code generation part. Such behavior led to unexpected for user explicit static field initializations - for example, setting `EMPTY` static field from `Optional` class for the following method under test: |
| 6 | +```java |
| 7 | +class OptionalEmptyExample { |
| 8 | + public java.util.Optional<Integer> optionalExample(boolean isEmpty) { |
| 9 | + return isEmpty ? java.util.Optional.empty() : java.util.Optional.of(42); |
| 10 | + } |
| 11 | +} |
| 12 | +``` |
| 13 | +like: |
| 14 | +```java |
| 15 | +setStaticField(optionalClazz, "EMPTY", empty); |
| 16 | +``` |
| 17 | + |
| 18 | +Such behavior is mostly strange for a user and should be properly fixed - we should not set such static fields with initializers manually. |
| 19 | + |
| 20 | +### Current solution |
| 21 | +After merging [Prohibit to set static fields from library classes](https://github.com/UnitTestBot/UTBotJava/pull/699), we do not explicitly set static fields of classes from so-called **trusted** libraries (by default represents JDK packages), accordingly to `org.utbot.framework.UtSettings#getIgnoreStaticsFromTrustedLibraries` setting. But such a solution possibly leads to coverage regression, need to be investigated [Investigate coverage regression because of not setting static fields](https://github.com/UnitTestBot/UTBotJava/issues/716). So, there are a few other ways to fix such a problem with static fields. |
| 22 | + |
| 23 | +## Solutions |
| 24 | + |
| 25 | +### Use concrete values as soft constraints (not yet implemented) |
| 26 | + |
| 27 | +The essence of the problem is assigning values for static fields that would be already set in runtime. So, to prevent it we can try to create models for static fields according to theirs runtime values and filter out statics that are equal to runtime values, with the following algorithm: |
| 28 | + |
| 29 | +1. Extract concrete value for a static field. |
| 30 | +2. Create `UtModel` for this value and store it. |
| 31 | +3. Transform produced model to soft constraints. |
| 32 | +4. Add them to the current symbolic state. |
| 33 | +5. After resolving `stateBefore` compare resulted `UtModel` for the static field with the stored model and drop resulted model out from `stateBefore` in case theirs equality. |
| 34 | + |
| 35 | +### Propagate reading of static fields (not yet implemented) |
| 36 | + |
| 37 | +We can change a bit sense of the **meaningful** statics - mark static fields as meaningful only if they affect a result of the method under test. To decide it, we can propagate such knowledge with the following algorithm: |
| 38 | + |
| 39 | +1. Store for each statement whether it reads a specific static value or not. |
| 40 | +2. While traversing a graph of the method, propagate these stores for the each statement. |
| 41 | +3. After reaching a **return** statement of the method under test, mark all propagated static fields as meaningful. |
| 42 | + |
| 43 | +#### Filter out statics by `UtExecution` affecting (not yet implemented) (*) |
| 44 | + |
| 45 | +After collecting all the executions, we can analyze them and for every static field check whether this affects a result. Briefly, if the static field value never changes in all executions, it means this value does not affect the result at all and can be dropped out **OR** it is required for all executions like an entering point (if statement as a first statement in the method under test, for example): |
| 46 | + |
| 47 | +```java |
| 48 | +class AlwaysThrowingException { |
| 49 | + public void throwIfMagic() { |
| 50 | + if (ClassWithStaticField.staticField == 42) { |
| 51 | + throw new RuntimeException("Magic number"); |
| 52 | + } |
| 53 | + } |
| 54 | +} |
| 55 | + |
| 56 | +class ClassWithStaticField { |
| 57 | + final static int staticField = 42; |
| 58 | +} |
| 59 | +``` |
| 60 | + |
| 61 | +(*) This solution should only be used with [propagation](#propagate-reading-of-static-fields) solution. |
0 commit comments