<?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">

    <test-code>
        <description>simple failure with method params</description>
        <expected-problems>1</expected-problems>
        <code><![CDATA[
public class Foo {
    boolean bar(String a, String b) {
        return a == b;
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>primitives are ok</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
package net.sourceforge.pmd.lang.java.rule.errorprone.compareobjectswithequals;

public class CompareObjectsWithEqualsSample {
    boolean bar(int a, int b) {
        return a == b;
    }
    void bar(Integer i, Character c) {
        int l = i;
        if (1 == i) { }
        if (i == Integer.MAX_VALUE) { } // ok
        if (c == Character.MAX_VALUE) { } // ok
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>skip nulls</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class Foo {
    boolean bar(int a, int b) {
        return a == null || null == b;
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Hits it</description>
        <expected-problems>1</expected-problems>
        <code><![CDATA[
public class Foo {
    Foo foo;

    boolean bar(Foo b) {
        return this.foo == b.foo;
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>charAt return char - that's ok</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
package net.sourceforge.pmd.lang.java.rule.errorprone.compareobjectswithequals;

public class CompareObjectsWithEqualsSample {
    boolean bar(String a, String b) {
        return a.charAt(0) == b.charAt(0);
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>locals</description>
        <expected-problems>1</expected-problems>
        <code><![CDATA[
public class Foo {
    boolean bar() {
        String a = "foo";
        String b = "bar";
        return a == b;
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>2 locals declared on one line</description>
        <expected-problems>1</expected-problems>
        <code><![CDATA[
public class Foo {
    void bar() {
        String a,b;
        a = "foo";
        b = "bar";
        if (a == b) {}
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>array element comparison</description>
        <expected-problems>1</expected-problems>
        <expected-linenumbers>11</expected-linenumbers>
        <code><![CDATA[
package net.sourceforge.pmd.lang.java.rule.errorprone.compareobjectswithequals;

public class CompareObjectsWithEqualsSample {
    void array(int[] a, String[] b) {
        if (a[1] == b[1]) {} // int == String - this comparison doesn't make sense (and doesn't compile...)
    }
    void array2(int[] c, int[] d) {
        if (c[1] == d[1]) {}
    }
    void array3(String[] a, String[] b) {
        if (a[1] == b[1]) {} // not ok
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Comparing against new object should always return false</description>
        <expected-problems>2</expected-problems>
        <expected-linenumbers>7,9</expected-linenumbers>
        <code><![CDATA[
package net.sourceforge.pmd.lang.java.rule.errorprone.compareobjectswithequals;

import java.util.Date;

public class CompareObjectsWithEqualsSample {
    void bar(Date aDate, int aInt, Integer realInt) {
        if ( aDate == new Date( 0 ) ) {} // not ok
        if ( aInt == new Integer(1) ) {} // that's actually ok, since Integer will be unboxed
        if ( realInt == new Integer(1) ) {} // not ok
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>qualified call in allocation</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
package net.sourceforge.pmd.lang.java.rule.errorprone.compareobjectswithequals;

import java.io.File;

public class CompareObjectsWithEqualsSample {
    boolean bar(String b) {
        return new File(b).exists() == false;
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#1028 False-positive: Compare objects with equals for Enums</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import java.math.RoundingMode;
public class Test {
    void doEnums() {
        RoundingMode mode1 = determineFirstMode();
        RoundingMode mode2 = determineSecondMode();
        if (mode1 == mode2) {}
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#1128 CompareObjectsWithEquals False Positive comparing boolean (primitive) values</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class SonOfSomeClass extends SomeClass {
    protected javax.swing.JCheckBox checkBox;
    public class SomeEmbeddedClass {
        public boolean someNotWorkingMethod(boolean valid){
    // This line presents a CompareObjectsWithEquals violation
            valid |= SonOfSomeClass.this.object.isConfigurationEnabled() != SonOfSomeClass.this.checkBox.isSelected();
            return valid;
        }
        public boolean someWorkingMethod(boolean valid){
    // This line does not present any violation
            valid |= (SonOfSomeClass.this.object.isConfigurationEnabled()) != SonOfSomeClass.this.checkBox.isSelected();
            return valid;
        }
    }
}
/*
// just for reference
class SomeClass {
    protected SomeObject object;
}
class SomeObject {
    private boolean configuration;
    public SomeObject() {
        super();
    }
    public boolean isConfigurationEnabled() {
        return configuration;
    }
    public void setConfiguration(boolean configuration) {
        this.configuration = configuration;
    }
}
*/
        ]]></code>
    </test-code>

    <test-code>
        <description>#1376 CompareObjectsWithEquals fails for type annotated method parameter</description>
        <expected-problems>1</expected-problems>
        <code><![CDATA[
public class CompareWithEqualsTest {
    public static boolean test(@Nullable Object a, Object b)
    {
        return a == b;
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#885 CompareObjectsWithEquals fails with Enum class</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class CompareWithEqualsTest {
    public static boolean test(Enum<?> a, Enum<?> b)
    {
        return a == b;
    }
}
        ]]></code>
    </test-code>
    <test-code>
        <description>comparing in equals method should be ok</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class Obj {
    private int i;

    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (other == null || getClass() != other.getClass()) {
            return false;
        }
        final Obj that = (Obj) other;
        return that.i == this.i;
    }
}
        ]]></code>
    </test-code>
    <test-code>
        <description>[java] CompareObjectsWithEqualsRule: False positive with Enums #2716</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class EnumTest {
    enum Type {
        A, B;
    }

    private final Type type = Type.A;

    public String isTypeA(Type param) {
        return param == type ? "Yes" : "No";
    }
}
        ]]></code>
    </test-code>
    <test-code>
        <description> [java] CompareObjectsWithEquals - false negative with type res #2880</description>
        <expected-problems>2</expected-problems>
        <code><![CDATA[
            import java.math.BigInteger;

            public class Foo {
                private BigInteger field = new BigInteger("10");
                private BigInteger field2 = new BigInteger("20");

                public boolean check(final BigInteger param) {
                    final BigInteger other = new BigInteger("20");

                    return (field == null
                            || param != field   // warning expected: Use equals() to compare object references.
                            && other != field)  // warning expected: Use equals() to compare object references.
                            && param.testBit(field2.intValue())
                            && other.testBit(field2.intValue());
                }
            }
        ]]></code>
    </test-code>

    <test-code>
        <description>#2934 False negative - class with fields</description>
        <expected-problems>4</expected-problems>
        <expected-linenumbers>8,9,10,11</expected-linenumbers>
        <code><![CDATA[
package net.sourceforge.pmd.lang.java.rule.errorprone.compareobjectswithequals;

public class ClassWithFields {
    private Object a;
    private Object b;

    boolean test1() {
        return  a == b // violation
             && this.a == b // violation
             && a != this.b // violation
             && this.a == this.b // violation
             && a.equals(b) // ok
             && this.a.equals(this.b); // ok
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#2934 comparison with null is valid and with primitive literals</description>
        <expected-problems>2</expected-problems>
        <expected-linenumbers>18,19</expected-linenumbers>
        <code><![CDATA[
package net.sourceforge.pmd.lang.java.rule.errorprone.compareobjectswithequals;

public class ClassWithFields {
    private Object a;
    private Object b;

    boolean test1() {
        return a == null // ok
             && this.a == null; // ok
    }

    void test2(StringBuilder sb) {
        if (sb != null) { } // ok
    }

    public void bar(Integer x, Integer y) {
        if (x == 1) { } // ok
        if (x == y) { } // not ok! only works -128<x<127 dependent on Integer caching
        if (x == "String") { } // not ok
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#2934 this and class should be ignored</description>
        <expected-problems>1</expected-problems>
        <expected-linenumbers>16</expected-linenumbers>
        <code><![CDATA[
package net.sourceforge.pmd.lang.java.rule.errorprone.compareobjectswithequals;

public class ClassWithFields {
    private Object a;
    private Object b;

    public boolean equals(Object o) {
        // if (this == o) { } // should be allowed, since this is a often used pattern in Object::equals.
        // comparing class instances is ok
        if (o.getClass() == a.getClass()) { }
        if (this.getClass() == this.a.getClass()) { }
        if (Object.class == a.getClass()) { }
    }

    void test(Object o) {
        if (this == o) { } // this is not allowed here, only in equals
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#2976 FP with array length</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class C0 {
    public static byte[] myArrayFunc(byte[] a1, byte[] a2) {
        if (a1.length != a2.length) {
            throw new IllegalArgumentException();
        }
    }
}
        ]]></code>
    </test-code>
    <test-code>
        <description>#2976 FP with method call (unresolved class)</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class C0 {
    class Unresolved {
        private final long val;
        public long getVal() { return val; }
    }

    {
        if (c1.getVal() != c2.getVal()) {  // <-- here
        }
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>[java] CompareObjectsWithEqualsRule: False positive with Enums #2716</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class EnumTest {
    enum Type {
        A, B;
    }
    private final Type type = Type.A;
    public String isTypeA(Type param) {
        return param == type ? "Yes" : "No";
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>static constant #3205</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
            class MyClass {
                static final MyClass MISSING = new MyClass();

                public static void isMissing(MyClass obj) {
                    return obj == MISSING; // no violation expected...
                }
            }
        ]]></code>
    </test-code>

    <test-code>
        <description>static constant in other class #3205</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
            class MyClass {
                static class Ts {
                    static final MyClass MISSING = new MyClass();
                }

                public static void isMissing(MyClass obj) {
                    return obj == Ts.MISSING; // no violation expected...
                }
            }
        ]]></code>
    </test-code>
    <test-code>
        <description>constant field on some object #3205</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
            class MyClass {
                static class Ts {
                    final MyClass MISSING = new MyClass();
                }

                public static void isMissing(MyClass obj, Ts ts) {
                    return obj == ts.MISSING; // no violation expected...
                }
            }
        ]]></code>
    </test-code>
    <test-code>
        <description>constant field on some object, more complicated expr #3205</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
            class MyClass {
                static class Ts {
                    final MyClass MISSING = new MyClass();
                    Ts id() { return this; }
                }

                public static void isMissing(MyClass obj, Ts ts) {
                    return obj == (ts.id()).id().MISSING; // no violation expected...
                }
            }
        ]]></code>
    </test-code>
    <test-code>
        <description>Property typesThatCompareByReference #3110</description>
        <rule-property name="typesThatCompareByReference">java.lang.String</rule-property>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
            class MyClass {
                public static void isMissing(String obj, Object ts) {
                    return obj == ts;
                }
            }
        ]]></code>
    </test-code>

</test-data>
