Skip to content

Conversation

@jose
Copy link
Member

@jose jose commented Jul 11, 2023

This pull request extends EvoSuite with new secondary criteria that optimize test smell metrics. The paper Automatic Generation of Smell-free Unit Tests published at SBFT'23 provides more details on that.

Copy link
Member Author

@jose jose left a comment

Choose a reason for hiding this comment

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

João Afonso (@fc51111), I've just reviewed your changes to EvoSuite regarding the smell-free project. Any chance you could answer the questions I left? And/Or update your fork that lives in here. Thanks in advance.

* @author Gordon Fraser
*/
public class Mutation implements Comparable<Mutation> {
public class Mutation implements Comparable<Mutation>, Serializable {
Copy link
Member Author

Choose a reason for hiding this comment

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

@fc51111, why does the Mutation class need to be Serializable?

Copy link
Contributor

Choose a reason for hiding this comment

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

During the test generation process, we wanted to measure the mutation score in specific time intervals, but we kept getting null pointer exceptions. I still have an old image of this error:

image

To solve this issue, it was necessary to make the "Mutation" class Serializable.

Comment on lines -50 to +57
private final BytecodeInstruction original;
private final transient BytecodeInstruction original;

private final InsnList mutation;
private final transient InsnList mutation;

private final InsnList infection;
private final transient InsnList infection;
Copy link
Member Author

Choose a reason for hiding this comment

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

Why do all these fields need to be transient?

Copy link
Contributor

Choose a reason for hiding this comment

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

We had to make the "Mutation" class Serializable. In turn, we made these 3 fields transient because the respective classes were not Serializable (and we decided that it didn't make sense to make them Serializable).

private static final long serialVersionUID = 596930765039928708L;

protected transient Mutation mutation;
protected Mutation mutation;
Copy link
Member Author

Choose a reason for hiding this comment

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

Why does not the mutation field need to be transient?

Copy link
Contributor

Choose a reason for hiding this comment

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

We had to make the "Mutation" class Serializable, so the "mutation" field does not need to remain transient.


return c1 - c2;

/*
Copy link
Member Author

Choose a reason for hiding this comment

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

Remove the following.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm afraid I can't remove this. We made this change because we decided that it would be better to normalize the secondary criteria. In our case, we wanted the smelliness of each test smell to vary between 0 and 1.
jose/smell-free-tests-evosuite@677f74d

Regarding "RhoTestSuiteSecondaryObjective.java", instead of just returning 0 or 1, we decided to calculate the smelliness of each test suite, normalize the respective values, and return the difference between the two. We calculate the difference because we considered that a test suite that is a lot less smelly than the other should have more weight than a test that is just a bit less smelly than another.

Removing this would most likely break our test smell optimization process. At the very least, there would be some unexpected results...

Is this change likely to break the rest of the code? Or are there any chances that we can keep it like this?

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.*;
Copy link
Member Author

Choose a reason for hiding this comment

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

I would rather keep the verbose imports.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done

* Coverage goals this test covers
*/
private transient Set<TestFitnessFunction> coveredGoals = new LinkedHashSet<>();
private final Set<TestFitnessFunction> coveredGoals = new LinkedHashSet<>();
Copy link
Member Author

Choose a reason for hiding this comment

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

Why does not the coveredGoals need to be transient?

Copy link
Contributor

Choose a reason for hiding this comment

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

"TestRedundancy.java" (i.e., one of the implemented test smells) uses the method "getCoveredGoals()" of "DefaultTestCase.java":
Set<TestFitnessFunction> testGoals = testChromosome.getTestCase().getCoveredGoals();

We were having problems because "testGoals" was null. In turn, this was happening because "coveredGoals" (a field in "DefaultTestCase.java") was transient, hence the change.


protected transient ExecutionResult lastExecutionResult = null;
//protected transient ExecutionResult lastExecutionResult = null;
protected ExecutionResult lastExecutionResult = null;
Copy link
Member Author

Choose a reason for hiding this comment

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

Why does not the lastExecutionResult need to be transient?

Copy link
Contributor

Choose a reason for hiding this comment

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

The same explanation as "ExecutionResult.java"

import java.util.stream.IntStream;

public class ExecutionResult implements Cloneable {
public class ExecutionResult implements Cloneable, Serializable {
Copy link
Member Author

Choose a reason for hiding this comment

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

Why does the ExecutionResult class need to be Serializable?

Copy link
Contributor

Choose a reason for hiding this comment

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

The class "RottenGreenTests" (i.e., one of the implemented test smells) has the following line of code:
ExecutionResult lastExecutionResult = chromosome.getLastExecutionResult();

where "chromosome" is of type "TestChromosome". Originally, in "ExecutableChromosome.java" (where the "getLastExecutionResult()" method is implemented), we had the following field:
protected transient ExecutionResult lastExecutionResult = null;

We were having problems because the variable "lastExecutionResult" in "RottenGreenTests.java" was null. Therefore, it was necessary to make 2 changes:

1 - In "ExecutableChromosome.java", remove "transient" from the field:
protected ExecutionResult lastExecutionResult = null;

2 - Make ExecutionResult Serializable

@gofraser
Copy link
Member

Sorry for being late to the party. This looks like a great change, but I'm not convinced by the changes related to serialisability. The Mutation in MutationTestFitness was transient because it can't be serialised properly, due to the byte code instructions. That's why MutationTestFitness has a method readObject that restores the mutation. By making Mutation serialisable you're potentially leaving objects in an inconsistent state (i.e. all its attributes you made transient without adding a readObject. I don't even understand why serialisability matters for test smell detection. I'd suggest we try to get rid of the serialisation changes (and understand why you think they are necessary), then we can merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants