<NME> test_comparisons.py
<BEF> import pytest
from testing.test_interpreter import BaseTestInterpreter


class TestComparisons(BaseTestInterpreter):
    # Test PHP's "recursive" comparison scheme.
    # Implemented hippy iteratively to avoid exhausting the stack

    # XXX move into Base
    @pytest.fixture
    def php_space(self):
        return self.space

    def test_compare_int_eq_int(self, php_space):
        output = self.run("""
        $a = 1;
        $b = 1;
        echo $a == $b;
        """)
        assert php_space.is_true(output[0])

    def test_compare_int_arry_eq_int_arry(self, php_space):
        output = self.run("""
        $a = array(1, 6, 8);
        $b = array(1, 6, 8);
        echo $a == $b;
        """)
        assert php_space.is_true(output[0])

    def test_compare_nested_int_arry_eq_int_arry(self, php_space):
        output = self.run("""
        $a = array(1, array(8));
        $b = array(1, array(8));
        echo $a == $b;
        """)
        assert php_space.is_true(output[0])

    def test_compare_nested_int_arry_not_eq_int_arry(self, php_space):
        output = self.run("""
        $a = array(1, array(9));
        $b = array(0, array(9));
        echo $a == $b;
        """)
        assert not php_space.is_true(output[0])

    def test_object_eq_object(self, php_space):
        output = self.run("""
        class X {
            public $f1 = 2;
            public $f2 = 3;
        }

        $a = new X();
        $b = new X();

        echo $a == $b;
        """)
        assert php_space.is_true(output[0])

    def test_object_not_eq_object_strict(self, php_space):
        output = self.run("""
        class X {
            public $f1 = 2;
            public $f2 = 3;
        }

        $a = new X();
        $b = new X();

        echo $a === $b;
        """)
        assert not php_space.is_true(output[0])

    def test_object_not_eq_object_strict2(self, php_space):
        output = self.run("""
        class X {
            public $f1 = 2;
            public $f2 = 3;
        }

        class Y {
            public $f1 = 2;
            public $f2 = 3;
        }

        $a = new X();
        $b = new Y();

        echo $a === $b;
        """)
        assert not php_space.is_true(output[0])

    def test_object_diff_class_eq_object(self, php_space):
        output = self.run("""
        class X {
            public $f1 = 2;
            public $f2 = 3;
        }

        class Y {
            public $f1 = 2;
            public $f2 = 3;
        }

        $a = new X();
        $b = new Y();

        echo $a == $b;
        """)
        assert not php_space.is_true(output[0])

    def test_object_diff_class_not_eq_object(self, php_space):
        output = self.run("""
        class X {
            public $f1 = 2;
            public $f2 = 3;
        }

        class Y {
            public $f1 = 2;
            public $f2 = 3;
        }

        $a = new X();
        $a->f1 = 666;
        $b = new Y();

        echo $a == $b;
        """)
        assert not php_space.is_true(output[0])

    def test_object_not_eq_object(self, php_space):
        output = self.run("""
        class X {
            public $f1 = 2;
            public $f2 = 3;
        }

        $a = new X();
        $a->f1 = 666;
        $b = new X();

        echo $a == $b;
        """)
        assert not php_space.is_true(output[0])

    def test_nested_object_eq_object(self, php_space):
        output = self.run("""
        class Node {
            function __construct($l, $r) {
                $this->l = $l;
                $this->r = $r;
            }
        }

        $tree1 = new Node(
            new Node(
                new Node(1, 2),
                new Node(3, 4)
            ),
            new Node(
                new Node(5, new Node(6, 7)),
                8
            )
        );

        $tree2 = new Node(
            new Node(
                new Node(1, 2),
                new Node(3, 4)
            ),
            new Node(
                new Node(5, new Node(6, 7)),
                8
            )
        );

        echo $tree1 == $tree2;

        """)
        assert php_space.is_true(output[0])

    def test_nested_object_eq_object_strict(self, php_space):
        output = self.run("""
        class Node {
            function __construct($l, $r) {
                $this->l = $l;
                $this->r = $r;
            }
        }

        $tree1 = new Node(
            new Node(
                new Node(1, 2),
                new Node(3, 4)
            ),
            new Node(
                new Node(5, new Node(6, 7)),
                8
            )
        );

        $tree2 = $tree1;

        echo $tree1 === $tree2;
        """)
        assert php_space.is_true(output[0])

    def test_nested_object_not_eq_object_strict(self, php_space):
        output = self.run("""
        class Node {
            function __construct($l, $r) {
                $this->l = $l;
                $this->r = $r;
            }
        }

        $tree1 = new Node(
            new Node(
                new Node(1, 2),
                new Node(3, 4)
            ),
            new Node(
                new Node(5, new Node(6, 7)),
                8
            )
        );

        $tree2 = new Node(
            new Node(
                new Node(1, 2),
                new Node(3, 4)
            ),
            new Node(
                new Node(5, new Node(6, 7)),
                8
            )
        );

        echo $tree1 === $tree2;
        """)
        assert not php_space.is_true(output[0])

    def test_nested_objects_deep(self, php_space):
        output = self.run("""
        class N {
            function __construct($l) {
                $this->l = $l;
            }
        }
            $b = array("0"=>"1", "1"=>"1", "2"=>"2", 4=>"php");

            echo $a < $b;
        """)
        assert php_space.is_true(output[0])

    def test_array_shortcuts1(self, php_space):
        output = self.run("""

        echo $chain1 < $chain2;
        echo $chain1 > $chain2;

        echo $chain1 <= $chain2;
        echo $chain1 >= $chain2;

        echo $chain1 === $chain2;
        echo $chain1 <> $chain2;

        // reverse args

        echo $chain1 == $chain2;
        echo $chain1 != $chain2;

        echo $chain1 < $chain2;
        echo $chain1 > $chain2;

        echo $chain1 <= $chain2;
        echo $chain1 >= $chain2;

        echo $chain1 === $chain2;
        echo $chain1 <> $chain2;
        """)
        assert php_space.is_true(output[0])
        assert not php_space.is_true(output[1])

        assert not php_space.is_true(output[2])
        assert not php_space.is_true(output[3])

        assert php_space.is_true(output[4])
        assert php_space.is_true(output[5])

        assert not php_space.is_true(output[6])
        assert not php_space.is_true(output[7])

        assert php_space.is_true(output[8])
        assert not php_space.is_true(output[9])

        assert not php_space.is_true(output[10])
        assert not php_space.is_true(output[11])

        assert php_space.is_true(output[12])
        assert php_space.is_true(output[13])

        assert not php_space.is_true(output[14])
        assert not php_space.is_true(output[15])

    def test_nested_objects_deep2(self, php_space):
        output = self.run("""
        class N {
            function __construct($l) {
                $this->l = $l;
            }
        }

        $chain1 = NULL;
        $chain2 = "not null";
        for ($i = 0; $i < 100; $i++) {
            $chain1 = new N($chain1);
            $chain2 = new N($chain2);
        }

        echo $chain1 == $chain2;
        echo $chain1 != $chain2;

        echo $chain1 < $chain2;
        echo $chain1 > $chain2;

        echo $chain1 <= $chain2;
        echo $chain1 >= $chain2;

        echo $chain1 === $chain2;
        echo $chain1 <> $chain2;

        // reverse args

        echo $chain2 == $chain1;
        echo $chain2 != $chain1;

        echo $chain2 < $chain1;
        echo $chain2 > $chain1;

        echo $chain2 <= $chain1;
        echo $chain2 >= $chain1;

        echo $chain2 === $chain1;
        echo $chain2 <> $chain1;
        """)
        assert not php_space.is_true(output[0]) # ==
        assert php_space.is_true(output[1]) # !=

        assert php_space.is_true(output[2]) # <
        assert not php_space.is_true(output[3]) # >

        assert php_space.is_true(output[4]) # <=
        assert not php_space.is_true(output[5]) # >=

        assert not php_space.is_true(output[6]) # ===
        assert php_space.is_true(output[7]) # <>

        assert not php_space.is_true(output[8]) # ==
        assert php_space.is_true(output[9]) # !=

        assert not php_space.is_true(output[10]) # <
        assert php_space.is_true(output[11]) # >

        assert not php_space.is_true(output[12]) # <=
        assert php_space.is_true(output[13]) # >=

        assert not php_space.is_true(output[14]) # ===
        assert php_space.is_true(output[15]) # <>

    def test_nested_object_and_arrays(self, php_space):
        output = self.run("""
        class Node {
            function __construct($l, $r) {
                $this->l = $l;
                $this->r = $r;
            }
        }

        $tree1 = new Node(
            new Node(
                new Node(array(8, "a"), 2),
                new Node(3, array(8, "a"))
            ),
            new Node(
                new Node(5, new Node(6, 7)),
                8
            )
        );

        $tree2 = new Node(
            new Node(
                new Node(1, 2),
                new Node(3, 4)
            ),
            new Node(
                new Node(5, new Node(6, 7)),
                8
            )
        );

        echo $tree1 != $tree2;
        echo $tree1 == $tree2;
        echo $tree1 < $tree2;
        echo $tree1 > $tree2;
        echo $tree1 <= $tree2;
        echo $tree1 >= $tree2;
        echo $tree1 === $tree2;

        // same in reverse
        echo $tree2 != $tree1;
        echo $tree2 == $tree1;
        echo $tree2 < $tree1;
        echo $tree2 > $tree1;
        echo $tree2 <= $tree1;
        echo $tree2 >= $tree1;
        echo $tree2 === $tree1;

        """)
        assert php_space.is_true(output[0])
        assert not php_space.is_true(output[1])
        assert not php_space.is_true(output[2])
        assert php_space.is_true(output[3])
        assert not php_space.is_true(output[4])
        assert php_space.is_true(output[5])
        assert not php_space.is_true(output[6])

        assert php_space.is_true(output[7])
        assert not php_space.is_true(output[8])
        assert php_space.is_true(output[9])
        assert not php_space.is_true(output[10])
        assert php_space.is_true(output[11])
        assert not php_space.is_true(output[12])
        assert not php_space.is_true(output[13])

    def test_deferred_comparison(self, php_space):
        # tests an annoying comparison ordering quirk.
        # In short, when we see array $b has no key 3, we know the arrays
        # differ, but we still need to compare the common-keyed values
        # since they may differ with a different ordering outcome.
        output = self.run("""
            $a = array("0", "1", "2", "php");
            $b = array("0"=>"1", "1"=>"1", "2"=>"2", 4=>"php");

            echo $a < $b;
            echo $b > $a;
        """)
        assert php_space.is_true(output[0])
        assert php_space.is_true(output[1])

    def test_array_shortcuts1(self, php_space):
        output = self.run("""
            $a = array(0, 1, 2);
            $b = array(0, 1, 3);

            echo $a == $b;
        """)
        assert not php_space.is_true(output[0])

    def test_array_shortcuts2(self, php_space):
        output = self.run("""
            $a = array(0, 1, array(2, 3));
            $b = array(0, 1, array(2, 3));

            echo $a < $b;
            echo $a > $b;
            echo $a == $b;
        """)
        assert not php_space.is_true(output[0])
        assert not php_space.is_true(output[1])
        assert php_space.is_true(output[2])

    def test_array_shortcuts3(self, php_space):
        output = self.run("""
            $a = array(0, 1, array(2, 3));
            $b = array(0, 1, array(2, 4));

            echo $a == $b;
        """)
        assert not php_space.is_true(output[0])

    def test_array_shortcuts4(self, php_space):
        output = self.run("""
            $a = array(0, 1, array(2, 3), 4);
            $b = array(0, 1, array(2, 3), 5);

            echo $a < $b;
            echo $a == $b;
        """)
        assert php_space.is_true(output[0])
        assert not php_space.is_true(output[1])

    def test_array_shortcuts5(self, php_space):
        output = self.run("""
            $cmn = array(2, 3);
            $a = array(0, 1, $cmn, 4);
            $b = array(0, 1, $cmn, 4);

            echo $a < $b;
            echo $a > $b;
            echo $a == $b;
        """)
        assert not php_space.is_true(output[0])
        assert not php_space.is_true(output[1])
        assert php_space.is_true(output[2])

    def test_array_shortcuts6(self, php_space):
        output = self.run("""
            class Triple {
                function __construct($a, $b, $c) {
                    $this->a = $a;
                    $this->b = $b;
                    $this->c = $c;
                }
            }
            $t1 = new Triple(new Triple(new Triple(1, 2, 3), 2, 3), NULL, 0);
            $t2 = new Triple(new Triple(new Triple(1, 2, 3), 2, 3), NULL, 1);

            echo $t1 == $t2;
            echo $t1 != $t2;
        """)
        assert not php_space.is_true(output[0])
        assert php_space.is_true(output[1])

    def test_lr_order_comp_array(self, php_space):
        output = self.run("""
            $a = array(array(1, 2, 3), 1, 2);
            $b = array(array(1, 2, 3), 2, 1);

            echo $a < $b;
        """)
        assert php_space.is_true(output[0])

    def test_order_comp_obj(self, php_space):
        output = self.run("""
            class N {
                function __construct($l, $r) { $this->l = $l; $this->r = $r; }
            }

            $a = new N(New N(1, 1), New N(2, 2));
            $b = new N(New N(0, 0), New N(4, 4));

            echo $a > $b;
        """)
        assert php_space.is_true(output[0])

    # http://phpsadness.com/sad/47
    def test_odd_string_comparison(self, php_space):
        output = self.run('''echo "1e3" == "1000";''')
        assert php_space.is_true(output[0])


    # http://phpsadness.com/sad/52
    def test_nontransitive_comparisons(self, php_space):
        output = self.run('''
                          echo TRUE == "a";
                          echo "a" == 0;
                          echo TRUE == 0;
                          ''')
        assert php_space.is_true(output[0])
        assert php_space.is_true(output[1])
        assert not php_space.is_true(output[2])

    # http://phpsadness.com/sad/52
    def test_nontransitive_comparisons2(self, php_space):
        output = self.run('''
                          echo -INF < 0;
                          echo 0 < TRUE;
                          echo -INF < TRUE;
                          ''')
        assert php_space.is_true(output[0])
        assert php_space.is_true(output[1])
        assert not php_space.is_true(output[2])

    def test_cycles_id(self, php_space):
        output = self.run('''
                          class A { function __construct($x=NULL) { $this->x = $x; }}

                          $a = new A();
                          $b = new A($a);
                          $a->x = $b; // completes the loop

                          echo $a == $a;
                          ''')
        assert php_space.is_true(output[0]) # flukey pass due to id check.

    def test_cycles(self, php_space):
        pytest.skip("does not terminate")
        output = self.run('''
                          class A { function __construct($x=NULL) { $this->x = $x; }}

                          $a = new A();
                          $b = new A($a);
                          $a->x = $b; // completes the loop

                          echo $a == $b;
                          ''')
        # should terminate

<MSG> Add minor test variant.

<DFF> @@ -251,8 +251,10 @@ class TestComparisons(BaseTestInterpreter):
             $b = array("0"=>"1", "1"=>"1", "2"=>"2", 4=>"php");
 
             echo $a < $b;
+            echo $b > $a;
         """)
         assert php_space.is_true(output[0])
+        assert php_space.is_true(output[1])
 
     def test_array_shortcuts1(self, php_space):
         output = self.run("""

{'commit': 'diff --git a/testing/test_comparisons.py b/testing/test_comparisons.py\nindex 5c76b990..0039f868 100644\n--- a/testing/test_comparisons.py\n+++ b/testing/test_comparisons.py\n@@ -251,8 +251,10 @@ class TestComparisons(BaseTestInterpreter):\n             $b = array("0"=>"1", "1"=>"1", "2"=>"2", 4=>"php");\n \n             echo $a < $b;\n+            echo $b > $a;\n         """)\n         assert php_space.is_true(output[0])\n+        assert php_space.is_true(output[1])\n \n     def test_array_shortcuts1(self, php_space):\n         output = self.run("""\n', 'commit_subject': 'Add minor test variant.', 'repo_name': 'hippyvm/hippyvm', 'committer': {'name': 'Laurence Tratt', 'email': '8468275f0d120527324004754c3d088324306866@tratt.net', 'time_sec': '1422351697', 'tz_offset': '0', 'date': {'seconds': '1422351697'}}, 'commit_tree': '77ad8647edabab34bda64d16d776dc05c3cd2bae', 'commit_message': 'Add minor test variant.\n', 'language_name': 'HTML', 'license': 'mit', 'hunks': '@@ -251,8 +251,10 @@ class TestComparisons(BaseTestInterpreter):\n             $b = array("0"=>"1", "1"=>"1", "2"=>"2", 4=>"php");\n \n             echo $a < $b;\n+            echo $b > $a;\n         """)\n         assert php_space.is_true(output[0])\n+        assert php_space.is_true(output[1])\n \n     def test_array_shortcuts1(self, php_space):\n         output = self.run("""\n', 'addition_count': 2, 'deletion_count': 0, 'src_file': 'a/testing/test_comparisons.py', 'tgt_file': 'b/testing/test_comparisons.py', 'file_extension': '.py', 'before_file': 'import pytest\nfrom testing.test_interpreter import BaseTestInterpreter\n\n\nclass TestComparisons(BaseTestInterpreter):\n    # Test PHP\'s "recursive" comparison scheme.\n    # Implemented hippy iteratively to avoid exhausting the stack\n\n    # XXX move into Base\n    @pytest.fixture\n    def php_space(self):\n        return self.space\n\n    def test_compare_int_eq_int(self, php_space):\n        output = self.run("""\n        $a = 1;\n        $b = 1;\n        echo $a == $b;\n        """)\n        assert php_space.is_true(output[0])\n\n    def test_compare_int_arry_eq_int_arry(self, php_space):\n        output = self.run("""\n        $a = array(1, 6, 8);\n        $b = array(1, 6, 8);\n        echo $a == $b;\n        """)\n        assert php_space.is_true(output[0])\n\n    def test_compare_nested_int_arry_eq_int_arry(self, php_space):\n        output = self.run("""\n        $a = array(1, array(8));\n        $b = array(1, array(8));\n        echo $a == $b;\n        """)\n        assert php_space.is_true(output[0])\n\n    def test_compare_nested_int_arry_not_eq_int_arry(self, php_space):\n        output = self.run("""\n        $a = array(1, array(9));\n        $b = array(0, array(9));\n        echo $a == $b;\n        """)\n        assert not php_space.is_true(output[0])\n\n    def test_object_eq_object(self, php_space):\n        output = self.run("""\n        class X {\n            public $f1 = 2;\n            public $f2 = 3;\n        }\n\n        $a = new X();\n        $b = new X();\n\n        echo $a == $b;\n        """)\n        assert php_space.is_true(output[0])\n\n    def test_object_not_eq_object_strict(self, php_space):\n        output = self.run("""\n        class X {\n            public $f1 = 2;\n            public $f2 = 3;\n        }\n\n        $a = new X();\n        $b = new X();\n\n        echo $a === $b;\n        """)\n        assert not php_space.is_true(output[0])\n\n    def test_object_not_eq_object_strict2(self, php_space):\n        output = self.run("""\n        class X {\n            public $f1 = 2;\n            public $f2 = 3;\n        }\n\n        class Y {\n            public $f1 = 2;\n            public $f2 = 3;\n        }\n\n        $a = new X();\n        $b = new Y();\n\n        echo $a === $b;\n        """)\n        assert not php_space.is_true(output[0])\n\n    def test_object_diff_class_eq_object(self, php_space):\n        output = self.run("""\n        class X {\n            public $f1 = 2;\n            public $f2 = 3;\n        }\n\n        class Y {\n            public $f1 = 2;\n            public $f2 = 3;\n        }\n\n        $a = new X();\n        $b = new Y();\n\n        echo $a == $b;\n        """)\n        assert not php_space.is_true(output[0])\n\n    def test_object_diff_class_not_eq_object(self, php_space):\n        output = self.run("""\n        class X {\n            public $f1 = 2;\n            public $f2 = 3;\n        }\n\n        class Y {\n            public $f1 = 2;\n            public $f2 = 3;\n        }\n\n        $a = new X();\n        $a->f1 = 666;\n        $b = new Y();\n\n        echo $a == $b;\n        """)\n        assert not php_space.is_true(output[0])\n\n    def test_object_not_eq_object(self, php_space):\n        output = self.run("""\n        class X {\n            public $f1 = 2;\n            public $f2 = 3;\n        }\n\n        $a = new X();\n        $a->f1 = 666;\n        $b = new X();\n\n        echo $a == $b;\n        """)\n        assert not php_space.is_true(output[0])\n\n    def test_nested_object_eq_object(self, php_space):\n        output = self.run("""\n        class Node {\n            function __construct($l, $r) {\n                $this->l = $l;\n                $this->r = $r;\n            }\n        }\n\n        $tree1 = new Node(\n            new Node(\n                new Node(1, 2),\n                new Node(3, 4)\n            ),\n            new Node(\n                new Node(5, new Node(6, 7)),\n                8\n            )\n        );\n\n        $tree2 = new Node(\n            new Node(\n                new Node(1, 2),\n                new Node(3, 4)\n            ),\n            new Node(\n                new Node(5, new Node(6, 7)),\n                8\n            )\n        );\n\n        echo $tree1 == $tree2;\n\n        """)\n        assert php_space.is_true(output[0])\n\n    def test_nested_object_eq_object_strict(self, php_space):\n        output = self.run("""\n        class Node {\n            function __construct($l, $r) {\n                $this->l = $l;\n                $this->r = $r;\n            }\n        }\n\n        $tree1 = new Node(\n            new Node(\n                new Node(1, 2),\n                new Node(3, 4)\n            ),\n            new Node(\n                new Node(5, new Node(6, 7)),\n                8\n            )\n        );\n\n        $tree2 = $tree1;\n\n        echo $tree1 === $tree2;\n        """)\n        assert php_space.is_true(output[0])\n\n    def test_nested_object_not_eq_object_strict(self, php_space):\n        output = self.run("""\n        class Node {\n            function __construct($l, $r) {\n                $this->l = $l;\n                $this->r = $r;\n            }\n        }\n\n        $tree1 = new Node(\n            new Node(\n                new Node(1, 2),\n                new Node(3, 4)\n            ),\n            new Node(\n                new Node(5, new Node(6, 7)),\n                8\n            )\n        );\n\n        $tree2 = new Node(\n            new Node(\n                new Node(1, 2),\n                new Node(3, 4)\n            ),\n            new Node(\n                new Node(5, new Node(6, 7)),\n                8\n            )\n        );\n\n        echo $tree1 === $tree2;\n        """)\n        assert not php_space.is_true(output[0])\n\n    def test_nested_objects_deep(self, php_space):\n        output = self.run("""\n        class N {\n            function __construct($l) {\n                $this->l = $l;\n            }\n        }\n            $b = array("0"=>"1", "1"=>"1", "2"=>"2", 4=>"php");\n\n            echo $a < $b;\n        """)\n        assert php_space.is_true(output[0])\n\n    def test_array_shortcuts1(self, php_space):\n        output = self.run("""\n\n        echo $chain1 < $chain2;\n        echo $chain1 > $chain2;\n\n        echo $chain1 <= $chain2;\n        echo $chain1 >= $chain2;\n\n        echo $chain1 === $chain2;\n        echo $chain1 <> $chain2;\n\n        // reverse args\n\n        echo $chain1 == $chain2;\n        echo $chain1 != $chain2;\n\n        echo $chain1 < $chain2;\n        echo $chain1 > $chain2;\n\n        echo $chain1 <= $chain2;\n        echo $chain1 >= $chain2;\n\n        echo $chain1 === $chain2;\n        echo $chain1 <> $chain2;\n        """)\n        assert php_space.is_true(output[0])\n        assert not php_space.is_true(output[1])\n\n        assert not php_space.is_true(output[2])\n        assert not php_space.is_true(output[3])\n\n        assert php_space.is_true(output[4])\n        assert php_space.is_true(output[5])\n\n        assert not php_space.is_true(output[6])\n        assert not php_space.is_true(output[7])\n\n        assert php_space.is_true(output[8])\n        assert not php_space.is_true(output[9])\n\n        assert not php_space.is_true(output[10])\n        assert not php_space.is_true(output[11])\n\n        assert php_space.is_true(output[12])\n        assert php_space.is_true(output[13])\n\n        assert not php_space.is_true(output[14])\n        assert not php_space.is_true(output[15])\n\n    def test_nested_objects_deep2(self, php_space):\n        output = self.run("""\n        class N {\n            function __construct($l) {\n                $this->l = $l;\n            }\n        }\n\n        $chain1 = NULL;\n        $chain2 = "not null";\n        for ($i = 0; $i < 100; $i++) {\n            $chain1 = new N($chain1);\n            $chain2 = new N($chain2);\n        }\n\n        echo $chain1 == $chain2;\n        echo $chain1 != $chain2;\n\n        echo $chain1 < $chain2;\n        echo $chain1 > $chain2;\n\n        echo $chain1 <= $chain2;\n        echo $chain1 >= $chain2;\n\n        echo $chain1 === $chain2;\n        echo $chain1 <> $chain2;\n\n        // reverse args\n\n        echo $chain2 == $chain1;\n        echo $chain2 != $chain1;\n\n        echo $chain2 < $chain1;\n        echo $chain2 > $chain1;\n\n        echo $chain2 <= $chain1;\n        echo $chain2 >= $chain1;\n\n        echo $chain2 === $chain1;\n        echo $chain2 <> $chain1;\n        """)\n        assert not php_space.is_true(output[0]) # ==\n        assert php_space.is_true(output[1]) # !=\n\n        assert php_space.is_true(output[2]) # <\n        assert not php_space.is_true(output[3]) # >\n\n        assert php_space.is_true(output[4]) # <=\n        assert not php_space.is_true(output[5]) # >=\n\n        assert not php_space.is_true(output[6]) # ===\n        assert php_space.is_true(output[7]) # <>\n\n        assert not php_space.is_true(output[8]) # ==\n        assert php_space.is_true(output[9]) # !=\n\n        assert not php_space.is_true(output[10]) # <\n        assert php_space.is_true(output[11]) # >\n\n        assert not php_space.is_true(output[12]) # <=\n        assert php_space.is_true(output[13]) # >=\n\n        assert not php_space.is_true(output[14]) # ===\n        assert php_space.is_true(output[15]) # <>\n\n    def test_nested_object_and_arrays(self, php_space):\n        output = self.run("""\n        class Node {\n            function __construct($l, $r) {\n                $this->l = $l;\n                $this->r = $r;\n            }\n        }\n\n        $tree1 = new Node(\n            new Node(\n                new Node(array(8, "a"), 2),\n                new Node(3, array(8, "a"))\n            ),\n            new Node(\n                new Node(5, new Node(6, 7)),\n                8\n            )\n        );\n\n        $tree2 = new Node(\n            new Node(\n                new Node(1, 2),\n                new Node(3, 4)\n            ),\n            new Node(\n                new Node(5, new Node(6, 7)),\n                8\n            )\n        );\n\n        echo $tree1 != $tree2;\n        echo $tree1 == $tree2;\n        echo $tree1 < $tree2;\n        echo $tree1 > $tree2;\n        echo $tree1 <= $tree2;\n        echo $tree1 >= $tree2;\n        echo $tree1 === $tree2;\n\n        // same in reverse\n        echo $tree2 != $tree1;\n        echo $tree2 == $tree1;\n        echo $tree2 < $tree1;\n        echo $tree2 > $tree1;\n        echo $tree2 <= $tree1;\n        echo $tree2 >= $tree1;\n        echo $tree2 === $tree1;\n\n        """)\n        assert php_space.is_true(output[0])\n        assert not php_space.is_true(output[1])\n        assert not php_space.is_true(output[2])\n        assert php_space.is_true(output[3])\n        assert not php_space.is_true(output[4])\n        assert php_space.is_true(output[5])\n        assert not php_space.is_true(output[6])\n\n        assert php_space.is_true(output[7])\n        assert not php_space.is_true(output[8])\n        assert php_space.is_true(output[9])\n        assert not php_space.is_true(output[10])\n        assert php_space.is_true(output[11])\n        assert not php_space.is_true(output[12])\n        assert not php_space.is_true(output[13])\n\n    def test_deferred_comparison(self, php_space):\n        # tests an annoying comparison ordering quirk.\n        # In short, when we see array $b has no key 3, we know the arrays\n        # differ, but we still need to compare the common-keyed values\n        # since they may differ with a different ordering outcome.\n        output = self.run("""\n            $a = array("0", "1", "2", "php");\n            $b = array("0"=>"1", "1"=>"1", "2"=>"2", 4=>"php");\n\n            echo $a < $b;\n            echo $b > $a;\n        """)\n        assert php_space.is_true(output[0])\n        assert php_space.is_true(output[1])\n\n    def test_array_shortcuts1(self, php_space):\n        output = self.run("""\n            $a = array(0, 1, 2);\n            $b = array(0, 1, 3);\n\n            echo $a == $b;\n        """)\n        assert not php_space.is_true(output[0])\n\n    def test_array_shortcuts2(self, php_space):\n        output = self.run("""\n            $a = array(0, 1, array(2, 3));\n            $b = array(0, 1, array(2, 3));\n\n            echo $a < $b;\n            echo $a > $b;\n            echo $a == $b;\n        """)\n        assert not php_space.is_true(output[0])\n        assert not php_space.is_true(output[1])\n        assert php_space.is_true(output[2])\n\n    def test_array_shortcuts3(self, php_space):\n        output = self.run("""\n            $a = array(0, 1, array(2, 3));\n            $b = array(0, 1, array(2, 4));\n\n            echo $a == $b;\n        """)\n        assert not php_space.is_true(output[0])\n\n    def test_array_shortcuts4(self, php_space):\n        output = self.run("""\n            $a = array(0, 1, array(2, 3), 4);\n            $b = array(0, 1, array(2, 3), 5);\n\n            echo $a < $b;\n            echo $a == $b;\n        """)\n        assert php_space.is_true(output[0])\n        assert not php_space.is_true(output[1])\n\n    def test_array_shortcuts5(self, php_space):\n        output = self.run("""\n            $cmn = array(2, 3);\n            $a = array(0, 1, $cmn, 4);\n            $b = array(0, 1, $cmn, 4);\n\n            echo $a < $b;\n            echo $a > $b;\n            echo $a == $b;\n        """)\n        assert not php_space.is_true(output[0])\n        assert not php_space.is_true(output[1])\n        assert php_space.is_true(output[2])\n\n    def test_array_shortcuts6(self, php_space):\n        output = self.run("""\n            class Triple {\n                function __construct($a, $b, $c) {\n                    $this->a = $a;\n                    $this->b = $b;\n                    $this->c = $c;\n                }\n            }\n            $t1 = new Triple(new Triple(new Triple(1, 2, 3), 2, 3), NULL, 0);\n            $t2 = new Triple(new Triple(new Triple(1, 2, 3), 2, 3), NULL, 1);\n\n            echo $t1 == $t2;\n            echo $t1 != $t2;\n        """)\n        assert not php_space.is_true(output[0])\n        assert php_space.is_true(output[1])\n\n    def test_lr_order_comp_array(self, php_space):\n        output = self.run("""\n            $a = array(array(1, 2, 3), 1, 2);\n            $b = array(array(1, 2, 3), 2, 1);\n\n            echo $a < $b;\n        """)\n        assert php_space.is_true(output[0])\n\n    def test_order_comp_obj(self, php_space):\n        output = self.run("""\n            class N {\n                function __construct($l, $r) { $this->l = $l; $this->r = $r; }\n            }\n\n            $a = new N(New N(1, 1), New N(2, 2));\n            $b = new N(New N(0, 0), New N(4, 4));\n\n            echo $a > $b;\n        """)\n        assert php_space.is_true(output[0])\n\n    # http://phpsadness.com/sad/47\n    def test_odd_string_comparison(self, php_space):\n        output = self.run(\'\'\'echo "1e3" == "1000";\'\'\')\n        assert php_space.is_true(output[0])\n\n\n    # http://phpsadness.com/sad/52\n    def test_nontransitive_comparisons(self, php_space):\n        output = self.run(\'\'\'\n                          echo TRUE == "a";\n                          echo "a" == 0;\n                          echo TRUE == 0;\n                          \'\'\')\n        assert php_space.is_true(output[0])\n        assert php_space.is_true(output[1])\n        assert not php_space.is_true(output[2])\n\n    # http://phpsadness.com/sad/52\n    def test_nontransitive_comparisons2(self, php_space):\n        output = self.run(\'\'\'\n                          echo -INF < 0;\n                          echo 0 < TRUE;\n                          echo -INF < TRUE;\n                          \'\'\')\n        assert php_space.is_true(output[0])\n        assert php_space.is_true(output[1])\n        assert not php_space.is_true(output[2])\n\n    def test_cycles_id(self, php_space):\n        output = self.run(\'\'\'\n                          class A { function __construct($x=NULL) { $this->x = $x; }}\n\n                          $a = new A();\n                          $b = new A($a);\n                          $a->x = $b; // completes the loop\n\n                          echo $a == $a;\n                          \'\'\')\n        assert php_space.is_true(output[0]) # flukey pass due to id check.\n\n    def test_cycles(self, php_space):\n        pytest.skip("does not terminate")\n        output = self.run(\'\'\'\n                          class A { function __construct($x=NULL) { $this->x = $x; }}\n\n                          $a = new A();\n                          $b = new A($a);\n                          $a->x = $b; // completes the loop\n\n                          echo $a == $b;\n                          \'\'\')\n        # should terminate\n', 'message': 'Add minor test variant.\n', 'author_name': 'Laurence Tratt', 'lang': 'py'}