<?xml version="1.0" encoding="UTF-8"?>
<test-data
    xmlns="http://pmd.sourceforge.net/rule-tests"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">

    <code-fragment id="full-example"><![CDATA[
import java.io.*;
public class Complicated {

    int x = 0, y = 1, z = 2, t = 2;
    boolean a = false, b = true, c = false, d = true;

    public void exception() {
        try {
            int k = 0;
            k++;
        } catch (IOException ioe) {
            ioe.printStackTrace();
            throw new Exception("surprise", ioe);
        } catch (Exception e) {
            // do nothing
        }
    }

    public void aSwitch(int x) {
        switch (x) {
        case 1:
            x = 2;
            break;

        case 2:
        case 3:
            x = 2;
            break;

        default:
            x = 2;
            break;
        }
    }

    public void aDo(int x, int y) {
        do {
            x++;
            y--;
        } while (x != y);
    }

    public void anAssert() {
        assert a : "fail";
        assert b || a : "fail";
    }

    public void aFor() {
        for (int n = 0; n < t; n++) {
            x = 2;
        }
    }

    public void aWhile() {
        while (z < y) {
            x = 2;
        }
    }

    public void ternaries() {
        x = a ? z : y == z && true ? y : x;
    }

    public void booleans() {
        if (a && b || b && d) {
            ternaries(x, y, z, a);
        } else if (c && d) {
            aFor(t, x);
        } else if (a && !b) {
            aWhile(x, y, z);
        } else if (b) {
            aDo(x, y);
        } else {
            aSwitch(x);
        }
    }
}
    ]]></code-fragment>

    <test-code>
        <description>Full example - No options</description>
        <expected-problems>8</expected-problems>
        <expected-messages>
            <message>'Complicated#exception()' has value 4.</message>
            <message>'Complicated#aSwitch(int)' has value 4.</message>
            <message>'Complicated#aDo(int, int)' has value 2.</message>
            <message>'Complicated#anAssert()' has value 1.</message>
            <message>'Complicated#aFor()' has value 2.</message>
            <message>'Complicated#aWhile()' has value 2.</message>
            <message>'Complicated#ternaries()' has value 4.</message>
            <message>'Complicated#booleans()' has value 10.</message>
        </expected-messages>
        <code-ref id="full-example"/>
    </test-code>

    <test-code>
        <description>Full example - Ignore boolean path option</description>
        <rule-property name="metricOptions">ignoreBooleanPaths</rule-property>
        <expected-problems>8</expected-problems>
        <expected-messages>
            <message>'Complicated#exception()' has value 4.</message>
            <message>'Complicated#aSwitch(int)' has value 3.</message>
            <message>'Complicated#aDo(int, int)' has value 2.</message>
            <message>'Complicated#anAssert()' has value 1.</message>
            <message>'Complicated#aFor()' has value 2.</message>
            <message>'Complicated#aWhile()' has value 2.</message>
            <message>'Complicated#ternaries()' has value 3.</message>
            <message>'Complicated#booleans()' has value 5.</message>
        </expected-messages>
        <code-ref id="full-example"/>
    </test-code>

    <test-code>
        <description>Full example - Consider assert option</description>
        <rule-property name="metricOptions">considerAssert</rule-property>
        <expected-problems>8</expected-problems>
        <expected-messages>
            <message>'Complicated#exception()' has value 4.</message>
            <message>'Complicated#aSwitch(int)' has value 4.</message>
            <message>'Complicated#aDo(int, int)' has value 2.</message>
            <message>'Complicated#anAssert()' has value 6.</message>
            <message>'Complicated#aFor()' has value 2.</message>
            <message>'Complicated#aWhile()' has value 2.</message>
            <message>'Complicated#ternaries()' has value 4.</message>
            <message>'Complicated#booleans()' has value 10.</message>
        </expected-messages>
        <code-ref id="full-example"/>
    </test-code>

    <test-code>
        <description>Full example - considerAssert + ignoreBooleanPaths</description>
        <rule-property name="metricOptions">ignoreBooleanPaths,considerAssert</rule-property>
        <expected-problems>8</expected-problems>
        <expected-messages>
            <message>'Complicated#exception()' has value 4.</message>
            <message>'Complicated#aSwitch(int)' has value 3.</message>
            <message>'Complicated#aDo(int, int)' has value 2.</message>
            <message>'Complicated#anAssert()' has value 5.</message>
            <message>'Complicated#aFor()' has value 2.</message>
            <message>'Complicated#aWhile()' has value 2.</message>
            <message>'Complicated#ternaries()' has value 3.</message>
            <message>'Complicated#booleans()' has value 5.</message>
        </expected-messages>
        <code-ref id="full-example"/>
    </test-code>

    <test-code>
        <description>Empty methods should count 1</description>
        <expected-problems>1</expected-problems>
        <expected-messages>
            <message>'Foo#foo()' has value 1.</message>
        </expected-messages>
        <code><![CDATA[
class Foo {
    void foo() {}
}
        ]]></code>
    </test-code>

    <code-fragment id="constructor-violation"><![CDATA[
public class Test {
    public Test() {
        if (a == 1) {
            if (b == 2) {
                System.out.println("b");
            } else if (b == 1) {
            }
        } else {
        }
    }
}
    ]]></code-fragment>

    <test-code>
        <description>#984 Cyclomatic complexity should treat constructors like methods</description>
        <expected-problems>1</expected-problems>
        <expected-messages>
            <message>'Test#Test()' has value 4.</message>
        </expected-messages>
        <code-ref id="constructor-violation"/>
    </test-code>

    <code-fragment id="fallthroughSwitch"><![CDATA[
class Foo {
    void bar(){}
    void foo() {
        int x=0;
        switch (x) {
            case 1:
            case 2: foo(); break;
            default: bar(); break;
        }
    }
}
    ]]></code-fragment>

    <test-code>
        <description>Standard Cyclo should count empty switch labels too</description>
        <rule-property name="reportLevel">2</rule-property>
        <expected-problems>1</expected-problems>
        <expected-messages>
            <message>'Foo#foo()' has value 3.</message>
        </expected-messages>
        <code-ref id="fallthroughSwitch"/>
    </test-code>

    <code-fragment id="manyBooleanOps"><![CDATA[
class Foo {
    void foo(){
        int x=0, y=1;
        boolean a, b;

        if (x > 2 || y < 4) {
            while (x++ < 10 && !(y++ < 0));
        } else if (a && b || x < 4) {
            return;
        }
    }
}
    ]]></code-fragment>

    <test-code>
        <description>Standard Cyclo should count boolean paths</description>
        <expected-problems>1</expected-problems>
        <expected-messages>
            <message>'Foo#foo()' has value 8.</message>
        </expected-messages>
        <code-ref id="manyBooleanOps"/>
    </test-code>

    <test-code>
        <description>Ternary expression counts 1 + boolean complexity</description>
        <expected-problems>1</expected-problems>
        <expected-messages>
            <message>'Foo#bar()' has value 3.</message>
        </expected-messages>
        <code><![CDATA[
class Foo {
    void bar() {
        boolean a, b;
        boolean c = (a || b) ? a : b;
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Ternary expression adds a single code path, refs #1217</description>
        <expected-problems>1</expected-problems>
        <expected-messages>
            <message>'Foo#bar()' has value 2.</message>
        </expected-messages>
        <code><![CDATA[
class Foo {
    void bar() {
        boolean a, b;
        boolean c = a ? a : b;
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Test consider assert option with ternary</description>
        <rule-property name="metricOptions">considerAssert</rule-property>
        <expected-problems>1</expected-problems>
        <expected-messages>
            <message>'Foo#bar()' has value 4.</message>
        </expected-messages>
        <code><![CDATA[
class Foo {
    void bar() {
        boolean a, b;
        // + 2 for the assert, + 1 for the ternary
        assert a : b ? "foo" : "bar";
    }
}
        ]]></code>
    </test-code>

    <code-fragment id="lambdas"><![CDATA[
import java.util.function.*;
public class LambdaTest {
    public void notSoComplex(int intInput) {                    // +1, total method = 4
        boolean binary;

        switch (intInput) {
        case 0:                                                 // +1
            binary = false;
            break;

        case 1:                                                 // +1
            binary = true;
            break;

        default:
            throw new RuntimeException();                       // +1
        }

        BiConsumer<Object, Object> lambda1 = (message, nme) -> {// lambda$notSoComplex$0, total = 2
            if (binary) {                                       // +1
                System.out.println(message);
            } else {
                System.out.println(nme);
            }
        };

        BiConsumer<Integer, Integer> lambda2 = (nme, message) -> {// lambda$notSoComplex$1, total = 8

            if ((nme != 0) && (message != 0)) {                  // +2
                try {
                    System.out.println(nme);
                } catch (IllegalArgumentException illegalArgumentException) { // +1
                    throw new RuntimeException();                // +1
                }

                if (binary) {                                    // +1
                    System.out.println(nme);
                } else {
                    System.out.println(nme);

                    if (nme != 1) {                             // +1
                        System.out.println(nme);
                    }

                    for (int i = 0; i < message; i++) {         // +1
                        System.out.println(nme);
                    }
                }
            }
        };
    }
}
    ]]></code-fragment>

    <test-code>
        <description>Complexity of lambdas doesn't affect the complexity of the method, refs #837</description>
        <expected-problems>1</expected-problems>
        <expected-messages>
            <message>'LambdaTest#notSoComplex(int)' has value 4.</message>
        </expected-messages>
        <code-ref id="lambdas"/>
    </test-code>

    <test-code>
        <description>Foreach loops count one, and add no boolean complexity</description>
        <expected-problems>1</expected-problems>
        <expected-messages>
            <message>'Elt#foreach(List&lt;List&lt;Elt&gt;&gt;)' has value 2.</message>
        </expected-messages>
        <code><![CDATA[
import java.util.List;
class Elt {
    void foreach(List<List<Elt>> elts) {
        int i = 0;
        for (Elt e : elts.get(i != 0 && i > 0)) {

        }
    }
}
        ]]></code>
    </test-code>

    <!-- Guard of for statements were previously wrongly counted. -->
    <!-- See https://github.com/pmd/pmd/blob/0c1afc426debff979b25219dbc8f4e07c76645b5/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/CycloPathAwareDecorator.java#L53 -->
    <!-- The method fetches the first descendant as the guard expression, though it should be the first child -->
    <!-- This causes the method to fetch the initializer of 'i' in the following for statement ('0') -->
    <!-- instead of the more complex boolean expression. -->

    <test-code>
        <description>The guard of a ForStatement should be counted correctly (spotted #1437)</description>
        <expected-problems>1</expected-problems>
        <expected-messages>
            <message>'Foo#compareImports(String[], String[])' has value 3.</message>
        </expected-messages>
        <code><![CDATA[
class Foo {
    private static int compareImports(String import1Tokens[], String import2Tokens[]) {
        for (int i = 0; i != import1Tokens.length && i != import2Tokens.length; i++) {
        }
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Multiple switch labels should count each</description>
        <expected-problems>2</expected-problems>
        <expected-messages>
            <message>'Foo#bar()' has value 1.</message>
            <message>'Foo#foo()' has value 3.</message>
        </expected-messages>
        <code><![CDATA[
class Foo {
    void bar(){}
    void foo() {
        int x=0;
        switch (x) {                 // +1
            case 1, 2: foo(); break; // +2
            default: bar(); break;
        }
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Switch labeled blocks should count</description>
        <expected-problems>2</expected-problems>
        <expected-messages>
            <message>'Foo#bar()' has value 1.</message>
            <message>'Foo#foo()' has value 5.</message>
        </expected-messages>
        <code><![CDATA[
class Foo {
    void bar(){}
    void foo() {                                // +1
        int x=0;
        boolean a, b;
        switch (x) {
            case 1, 2 -> { if (a && b) bar(); } // +4
            default -> { bar(); }
        }
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Switch labeled expressions should count</description>
        <expected-problems>2</expected-problems>
        <expected-messages>
            <message>'Foo#bar()' has value 1.</message>
            <message>'Foo#foo()' has value 5.</message>
        </expected-messages>
        <code><![CDATA[
class Foo {
    void bar(){}
    void foo() {                    // +1
        int x=0;
        switch (x) {
            case 1, 2, 3 -> bar();  // +3
            case 4       -> bar();  // +1
            default -> bar();
        }
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Switch expressions</description>
        <expected-problems>2</expected-problems>
        <expected-messages>
            <message>'Foo#bar()' has value 1.</message>
            <message>'Foo#foo(int)' has value 5.</message>
        </expected-messages>
        <code><![CDATA[
class Foo {
    int bar() { return 1; }
    void foo(int i) {               // +1
        int x = switch (i) {
            case 1, 2, 3 -> bar();  // +3
            case 4       -> bar();  // +1
            default -> bar();
        };
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Switch labeled expression with default only</description>
        <expected-problems>1</expected-problems>
        <expected-messages>
            <message>'Foo#foo(int)' has value 2.</message>
        </expected-messages>
        <code><![CDATA[
class Foo {
    void foo(int x) {     // +1
        foo(switch (x) {
            default -> {
                if (a) { // +1
                    yield 1;
                } else {
                    yield 2;
                }
            }
        });
    }
}
        ]]></code>
    </test-code>
</test-data>
