Skip to content

Enhance fuzzing for arrays #738 #755

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 30, 2022

Conversation

volivan239
Copy link
Collaborator

@volivan239 volivan239 commented Aug 22, 2022

Description

Now, ArrayModelProvider generates more interesting tests:

  • each element of array is generated recursively by fuzzing synthetic method.
  • arrays with "interesting" small sizes are added ("interesting" means that this number is obtained from source code)

Fixes #738

Type of Change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

Manual Scenario

Generate tests for methods from ArrayExample class (using only fuzzer):

class Point {
    int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    boolean equals(Point other) {
        return (other.x == this.x && other.y == this.y);
    }
}

public class ArrayExample {
    boolean checkAllSame(Integer[] a) {    // Fuzzer should find parameters giving true as well as parameters giving false
        Set<Integer> s = new HashSet<>(Arrays.asList(a));
        return (s.size() <= 1);
    }

    public boolean checkAllSamePoints(Point[] a) { // Also works for classes
        if (a.length == 4) {
            return false; // Test with array of size 4 should be generated by fuzzer
        }
        for (int i = 1; i < a.length; i++) {
            if (!a[i].equals(a[i - 1]))
                return false;
        }
        return true;
    }

    public boolean checkRowsWithAllSame2D(int[][] a, int y) {
        int cntSame = 0;
        for (int i = 0; i < a.length; i++) {
            boolean same = true;
            for (int j = 1; j < a[i].length; j++) {
                if (a[i][j] != a[i][j - 1]) {
                    same = false;
                    break;
                }
            }
            if (same)
                cntSame++;
        }
        return (cntSame == y && y > 0 && y < a.length);
    }
}

For each of them, meaningful tests are generated that cover both true/false outputs.

Checklist (remove irrelevant options):

This is the author self-check list

  • The change followed the style guidelines of the UTBot project
  • Self-review of the code is passed
  • The change contains enough commentaries, particularly in hard-to-understand areas
  • New documentation is provided or existed one is altered
  • No new warnings
  • New tests have been added
  • All tests pass locally with my changes

@volivan239 volivan239 marked this pull request as ready for review August 22, 2022 13:09
@volivan239 volivan239 requested a review from Markoutte August 22, 2022 13:09
@volivan239 volivan239 force-pushed the volivan239/enhance_fuzzing_for_arrays branch 2 times, most recently from 16ea1b7 to 7ab9dc6 Compare August 23, 2022 09:52
}
private val limit: Int =
when(recursion) {
1 -> Int.MAX_VALUE
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic looks different: before when recursion is 1, 2, 3 and so on then limit is 1. Now it is harder to understand what's happening. The idea behind this was that the limit is articulated as: when we find constructors that accepts another objects then we construct them, but use no more than one available constructor. If recursion is 0, than we pass null to every object parameter in constructor. But usually, we look for constructors with primitive values.

protected val recursion: Int
): ModelProvider {
// TODO: currently it is var due to tests, maybe we can make it private val?
var modelProviderForRecursiveCalls: ModelProvider =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we have lost an ability to change model providers for ObjectModelProvider, because before the changed model provider is passed into recursive call. Now, it uses only default providers.

})
}
}

private fun generateArrayLengths(description: FuzzedMethodDescription): Set<Int> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this can be simplified. It is too much work to generate some values in interval from 0 up to 10


var modelProvider: ModelProvider = objectModelProviders(idGenerator)
// TODO: can we make it private val (maybe depending on recursion)?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is manual option and a part of public API

protected val idGenerator: IdentityPreservingIdGenerator<Int>,
protected val recursion: Int
): ModelProvider {
// TODO: currently it is var due to tests, maybe we can make it private val?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was a part of public API to change a set of model providers. Currently, it looks broken

Copy link
Collaborator

@Markoutte Markoutte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, look at comments

@volivan239 volivan239 force-pushed the volivan239/enhance_fuzzing_for_arrays branch from 7ab9dc6 to f218d6b Compare August 26, 2022 13:34

data class ModelConstructor(
val neededTypes: List<ClassId>,
val createModel: (List<FuzzedValue>) -> FuzzedValue
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use notation like this:

 val createModel: (inputs: List<FuzzedValue>) -> FuzzedValue

to clarify name for parameter. Also, doc will be very useful here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added name for parameters. Also, added docs here and in other places where needed

modelProviderForRecursiveCalls.map {
if (it is RecursiveModelProvider)
it.copy(idGenerator, recursionDepthLeft - 1).apply {
modelProviderForRecursiveCalls = this@RecursiveModelProvider.modelProviderForRecursiveCalls
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we copy all these values in the copy method? It looks better as we want to get a copy of this provider. Here we can leave values that should be changed like totalLimit (but in copy it should be copied, I think)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to createNewInstance for more clarity

@@ -412,6 +413,7 @@ class ModelProviderTest {
}
}

@Disabled
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test checks that Outer class is created using only one constructor from the inner and it is the simplest one. I think we should keep this behavior for ObjectModelProvider

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returned this behavior back

val createModel: (List<FuzzedValue>) -> FuzzedValue
)

abstract class RecursiveModelProvider(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, add docs

*/
fun defaultModelProviders(idGenerator: IdentityPreservingIdGenerator<Int>, recursionDepth: Int = 1): ModelProvider =
if (recursionDepth >= 0)
nonRecursiveProviders(idGenerator).with(recursiveModelProviders(idGenerator, recursionDepth))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, that the other method called 'defaultModelProviders' without parameter with recursion has another set of model providers. Therefore. I'd recommend to change name for this method to avoid ambiguous calls.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method should have replaced the method without recursionDepth parameter with no changes in behavior when this parameter is not specified. Seems like I accidently got duplication after rebase. Deleted method without parameter now. Could you please check that it didn't change the behavior?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It changes because PrimitivesModelProvider isn't used anymore. By default I want to keep an ability to set different providers by default, because I usually want to use shallower value set for it. Right now this change is only about PrimitivesModelProvider, but in the future I'd want to add some providers that are used for generating flat parameters and aren't used for recursive.

Copy link
Collaborator

@Markoutte Markoutte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, see comments

@volivan239 volivan239 requested a review from Markoutte August 29, 2022 16:11
@volivan239 volivan239 force-pushed the volivan239/enhance_fuzzing_for_arrays branch from f4405fa to 36c749e Compare August 29, 2022 17:06
Copy link
Collaborator

@Markoutte Markoutte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good but let's discuss changes about defaultModelProviders

* Add flattening to Combined
* Change default value for RecursiveModelProvider.branchingLimit
@volivan239 volivan239 enabled auto-merge (squash) August 30, 2022 11:41
@volivan239 volivan239 merged commit 9431dd8 into main Aug 30, 2022
@volivan239 volivan239 deleted the volivan239/enhance_fuzzing_for_arrays branch August 30, 2022 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Enhance fuzzer's ArrayModelProvider
2 participants