EvoSuite / evosuite

EvoSuite - automated generation of JUnit test suites for Java classes
http://www.evosuite.org
GNU Lesser General Public License v3.0
823 stars 341 forks source link

Evosuite test calling function multiple times, failing with "Loop has been executed more times than the allowed" #446

Open xylankant opened 1 year ago

xylankant commented 1 year ago

Context

Trying to generate unit tests for a simple function with EvoSuite, the generated test case calls the function multiple times, and eventually leads to a failed test as it executes a loop more often than allowed.

There might be an option I am missing to pass to EvoSuite to change the behaviour to what is described in "Expected", but I am at a loss.

Steps to Reproduce

Use simple class that calculates the sum of divisors of an integer:

package mathtools;

public class MathTool {
    public static int divisor_sum(int n) {
        int total = 0;
        for (int i = 1; i <= n; i++) {
            if (n % i == 0) {
                total += i;
            }
        }
        return total;
    }
}

Run EvoSuite to generate unit tests.

EvoSuite Arguments

docker run --rm -u ${UID} -v ${PWD}:/evosuite evosuite/evosuite:latest-java-11 -projectCP cls -class mathtools.MathTool -Drandom_seed=1337 -Dcheck_contracts=true

Also ran with parameters -Dmax_size=1, -Dmax_initial_tests=1 or -Dmax_mutants=1 to try and influence outcome, leading to no change.

Current Result

EvoSuite generates 3 tests in MathTool_ESTest.java :

  @Test(timeout = 4000)
  public void test0()  throws Throwable  {
      int int0 = MathTool.divisor_sum((-1141));
      assertEquals(0, int0);
  }

  @Test(timeout = 4000)
  public void test1()  throws Throwable  {
      MathTool.divisor_sum(1728);
      MathTool.divisor_sum(1934);
      MathTool.divisor_sum(1186);
      MathTool.divisor_sum(1186);
      // Undeclared exception!
      MathTool.divisor_sum(4095);
  }

  @Test(timeout = 4000)
  public void test2()  throws Throwable  {
      MathTool mathTool0 = new MathTool();
  }

test0 behaves as expected, calling the function once and generating an assert for the result. test1 behaves differently from expected, calling the function many times, commenting that an Undeclared exception! occurred, and never generates asserts. test2 generates no test pass creating an instance of the MathTool class.

A separate generated file MathTool_Failed_ESTest.java contains the reason for failing:

  // Contract violation: Undeclared exception (JCrasher style)
  // Contract violation: Undeclared exception check

  @Test(timeout = 4000)
  public void test0()  throws Throwable  {
      MathTool.divisor_sum(1728);
      MathTool.divisor_sum(1934);
      MathTool.divisor_sum(1186);
      MathTool.divisor_sum(1186);
      // Throws undeclared exception (JCrasher heuristic): Loop has been executed more times than the allowed 10000
      // Undeclared exception!
      MathTool.divisor_sum(4095);
  }

  @Test(timeout = 4000)
  public void test1()  throws Throwable  {
      MathTool.divisor_sum(1728);
      MathTool.divisor_sum(1934);
      MathTool.divisor_sum(1186);
      MathTool.divisor_sum(1186);
      // Throws undeclared exception: Loop has been executed more times than the allowed 10000
      // Undeclared exception!
      MathTool.divisor_sum(4095);
  }

Notably, both test0 and test1 run into the same issue, but test0 still produces a valid test MathTool_ESTest.java .

Expected result

EvoSuite should generate asserts for test1, or split the multiple function calls in test1 into several other tests.

Additional info

Up-to-date EvoSuite docker evosuite/evosuite:latest-java-11 pulled from Dockerhub, with EvoSuite version 1.2.0