I wrote:
> However, two tests now fail: Test_NotEqualIsTheSameAsNotequal and
> Test_NotNotCanBeEliminated :-(. Maybe I find the problem quickly

Ok - I have now repaired those tests. The modified file is attached.

Actually, the two tests show that there will always be complicated differences 
between the two semantics ... as a few of you have consistently pointed out. I 
copy the relevant two tests here verbatim, because I think they are interesting 
- all who are interested, read the comments, please. In the first test, I have 
"repaired" the conditions in two different ways. In the second test, I have 
used only one type of "repair".

Regards
Harald M.

[Test]
public void Test_NotEqualIsTheSameAsNotequal()
{
    // Already the following yields different results for I1 == null even 
    // though it does NOT throw an exception in Linq2Objects:
    //      ... RunTest(x => x.BO1.I1 != 1, ...);
    // * In C# logic, we get null != 1 <=> true
    // * In SQL logic, we get null != 1 <=> logical-null => false

    // To exclude this case, we can either make it false in C# ...
    int r1 = RunTest(x => x.BO1.I1 != null && x.BO1.I1 != 1,
                     Setters<TBO1_I>(MyBO.SetBO1_I1));
    // ... or force it to true in SQL
    int r2 = RunTest(x => x.BO1.I1 == null || x.BO1.I1 != 1,
                     Setters<TBO1_I>(MyBO.SetBO1_I1));

    // Also the following condition yields different results for I1 ==  
    // null even though it does NOT throw an exception in Linq2Objects:
    //      ... RunTest(x => !(x.BO1.I1 == 1), ...);
    // * In C# logic, we get !(null == 1) <=> !(false) <=> true
    // * In SQL logic, we get !(null == 1) <=> !(logical-null) <=> 
    //   logical-null => false

    // Again, to exclude this case, we can either make the inner part
    // true in C# ...
    int r3 = RunTest(x => !(x.BO1.I1 == null || x.BO1.I1 == 1),
                     Setters<TBO1_I>(MyBO.SetBO1_I1));
    // ... or force it to false in SQL:
    int r4 = RunTest(x => !(x.BO1.I1 != null && x.BO1.I1 == 1),
                     Setters<TBO1_I>(MyBO.SetBO1_I1));

    Assert.Greater(r1, 0);
    Assert.Greater(r2, 0);

    // We also expect the !(==) versions to return the same result 
    // as the != versions.
    Assert.AreEqual(r1, r3);
    Assert.AreEqual(r2, r4);
}

[Test]
public void Test_NotNotCanBeEliminated()
{
    // The following condition does *not* return the same values 
    // if I1 and/or J1 are null in Linq2Objects and in Nhib.Linq:
    //     x => x.BO1.I1 != 1 && x.BO2.J1 != 1,
    // First, assume I1 == null and J1 == 0:
    // * In C# (Linq2Objects), we get null != 1 && 0 != 1 <=> 
    //   true && true <=> true
    // * In SQL (NHib.Linq), we get null != 1 && <=> logical-null && true 
    //    <=> logical-null => false
    // For I1 == 0 and J1 == null we get the same problem, as the 
    // condition is symmetric.

    // To repair this, we force "SQL" to true for nulls:
    int r1 = RunTest(x => (x.BO1.I1 == null || x.BO1.I1 != 1) && (x.BO2.J1 == 
null || x.BO2.J1 != 1),
                     Setters<TBO1_I, TBO2_J>(MyBO.SetBO1_I1, MyBO.SetBO2_J1));
    int r2 = RunTest(x => !!((x.BO1.I1 == null || x.BO1.I1 != 1) && (x.BO2.J1 
== null || x.BO2.J1 != 1)),
                     Setters<TBO1_I, TBO2_J>(MyBO.SetBO1_I1, MyBO.SetBO2_J1));
    Assert.Greater(r1, 0);
    Assert.AreEqual(r1, r2);
}

-- 
NEU: FreePhone - kostenlos mobil telefonieren und surfen!                       
Jetzt informieren: http://www.gmx.net/de/go/freephone
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using NHibernate.Linq;
using NUnit.Framework;

namespace NHibernate.Test.NHSpecificTest.NH2583
{
    public class MassTestingNotAndDeMorganFixture : AbstractMassTestingFixture
    {
        protected override int TestAndAssert(Expression<Func<MyBO, bool>> 
condition, ISession session, IEnumerable<int> expectedIds)
        {
            var result = session.Query<MyBO>().Where(condition);
            AreEqual(expectedIds, result.Select(bo => bo.Id).ToArray());
            return expectedIds.Count();
        }

        [Test]
        public void Test_NotUnequalIsTheSameAsEqual()
        {
            int r1 = RunTest(x => !(x.BO1.I1 != 1),
                             Setters<TBO1_I>(MyBO.SetBO1_I1));
            int r2 = RunTest(x => x.BO1.I1 == 1,
                             Setters<TBO1_I>(MyBO.SetBO1_I1));
            Assert.AreEqual(r1, r2);
            Assert.Greater(r1, 0);

            r1 = RunTest(x => !(x.BO1.I1 != 1),
                             Setters<TBO1_I, TBO2_J>(MyBO.SetBO1_I1, 
MyBO.SetBO2_J1));
            r2 = RunTest(x => x.BO1.I1 == 1,
                             Setters<TBO1_I, TBO2_J>(MyBO.SetBO1_I1, 
MyBO.SetBO2_J1));
            Assert.AreEqual(r1, r2);
            Assert.Greater(r1, 0);
        }

        [Test]
        public void Test_NotEqualIsTheSameAsNotequal()
        {
            // Already the following yields different results for I1 == null 
even though
            // it does NOT throw an exception in Linq2Objects:
            //      ... RunTest(x => x.BO1.I1 != 1, ...);
            // * In C# logic, we get null != 1 <=> true
            // * In SQL logic, we get null != 1 <=> logical-null => false

            // To exclude this case, we can either make it false in C# ...
            int r1 = RunTest(x => x.BO1.I1 != null && x.BO1.I1 != 1,
                             Setters<TBO1_I>(MyBO.SetBO1_I1));
            // ... or force it to true in SQL
            int r2 = RunTest(x => x.BO1.I1 == null || x.BO1.I1 != 1,
                             Setters<TBO1_I>(MyBO.SetBO1_I1));

            // Also the following condition yields different results for I1 == 
null even 
            // though it does NOT throw an exception in Linq2Objects:
            //      ... RunTest(x => !(x.BO1.I1 == 1), ...);
            // * In C# logic, we get !(null == 1) <=> !(false) <=> true
            // * In SQL logic, we get !(null == 1) <=> !(logical-null) <=> 
logical-null => false

            // Again, to exclude this case, we can either make the inner part 
true in C# ...
            int r3 = RunTest(x => !(x.BO1.I1 == null || x.BO1.I1 == 1),
                             Setters<TBO1_I>(MyBO.SetBO1_I1));
            // ... or force it to false in SQL:
            int r4 = RunTest(x => !(x.BO1.I1 != null && x.BO1.I1 == 1),
                             Setters<TBO1_I>(MyBO.SetBO1_I1));

            Assert.Greater(r1, 0);
            Assert.Greater(r2, 0);

            // We also expect the !(==) versions to return the same result as 
the != versions.
            Assert.AreEqual(r1, r3);
            Assert.AreEqual(r2, r4);
        }
        
        [Test]
        public void Test_DeMorganNotAnd()
        {
            //      BO1.I1  BO2.J1      x.BO1.I1 != 1   x.BO2.J1 != 1   &&  !   
Result (3v-->2v)    Linq2Obj
            //      null    null            n               n           n   n   
f                   
            //      null    0               n               t           n   n   
f                   
            //      null    1               n               f           f   t   
t                   
            //      0       null            t               n           n   n   
f                   
            //      0       0               t               t           t   f   
f                   f
            //      0       1               t               f           f   t   
t                   t
            //      1       null            f               n           f   t   
t                   
            //      1       0               f               t           f   t   
t                   t
            //      1       1               f               f           f   t   
t                   t

            RunTest(x => !(x.BO1.I1 != 1 && x.BO2.J1 != 1),
                    Setters<TBO1_I, TBO2_J>(MyBO.SetBO1_I1, MyBO.SetBO2_J1));
        }

        [Test]
        public void Test_DeMorganNotOr()
        {
            int r1 = RunTest(x => !(x.BO1.I1 != 1 || x.BO2.J1 != 1),
                             Setters<TBO1_I, TBO2_J>(MyBO.SetBO1_I1, 
MyBO.SetBO2_J1));
            int r2 = RunTest(x => !(x.BO1.I1 != 1) && !(x.BO2.J1 != 1),
                             Setters<TBO1_I, TBO2_J>(MyBO.SetBO1_I1, 
MyBO.SetBO2_J1));
            int r3 = RunTest(x => x.BO1.I1 == 1 && x.BO2.J1 == 1,
                             Setters<TBO1_I, TBO2_J>(MyBO.SetBO1_I1, 
MyBO.SetBO2_J1));
            Assert.AreEqual(r1, r2);
            Assert.AreEqual(r2, r3);
            Assert.Greater(r1, 0);
        }

        [Test]
        public void Test_NotNotCanBeEliminated()
        {
            // The following condition does *not* return the same values if I1 
and/or J1 are
            // null in Linq2Objects and in Nhib.Linq:
            //     x => x.BO1.I1 != 1 && x.BO2.J1 != 1,
            // First, assume I1 == null and J1 == 0:
            // * In C# (Linq2Objects), we get null != 1 && 0 != 1 <=> true && 
true <=> true
            // * In SQL (NHib.Linq), we get null != 1 && <=> logical-null && 
true <=> logical-null => false
            // For I1 == 0 and J1 == null we get the same problem, as the 
condition is symmetric.

            // To repair this, we force "SQL" to true for nulls:
            int r1 = RunTest(x => (x.BO1.I1 == null || x.BO1.I1 != 1) && 
(x.BO2.J1 == null || x.BO2.J1 != 1),
                             Setters<TBO1_I, TBO2_J>(MyBO.SetBO1_I1, 
MyBO.SetBO2_J1));
            int r2 = RunTest(x => !!((x.BO1.I1 == null || x.BO1.I1 != 1) && 
(x.BO2.J1 == null || x.BO2.J1 != 1)),
                             Setters<TBO1_I, TBO2_J>(MyBO.SetBO1_I1, 
MyBO.SetBO2_J1));
            Assert.Greater(r1, 0);
            Assert.AreEqual(r1, r2);
        }
    }
}

Reply via email to