示例#1
0
        public async Task TotalCountWithTooManyElements()
        {
            // Insert a few records
            IMobileServiceTable <ToDo> table = GetClient().GetTable <ToDo>();

            ToDo first = new ToDo {
                Title = "TotalCount1", Complete = false
            };
            await table.InsertAsync(first);

            long totalCount = 65L;

            for (int i = 2; i <= totalCount; i++)
            {
                await table.InsertAsync(new ToDo { Title = "TotalCount" + i, Complete = true });
            }

            // Get the total count and make sure we've got more than just 50
            // items in the total count
            List <ToDo> items = await table.Where(t => t.Id >= first.Id).IncludeTotalCount().ToListAsync();

            ITotalCountProvider countProvider = items as ITotalCountProvider;

            Assert.AreEqual(50, items.Count);
            Assert.IsNotNull(countProvider);
            Assert.AreEqual(totalCount, countProvider.TotalCount);
        }
示例#2
0
        public async Task TotalCountBasics()
        {
            // Insert a few records
            IMobileServiceTable <ToDo> table = GetClient().GetTable <ToDo>();
            ToDo first = new ToDo {
                Title = "ABC", Complete = false
            };
            await table.InsertAsync(first);

            await table.InsertAsync(new ToDo { Title = "DEF", Complete = true });

            await table.InsertAsync(new ToDo { Title = "GHI", Complete = false });

            ITotalCountProvider             countProvider = null;
            IMobileServiceTableQuery <ToDo> query         = table.Where(t => t.Id >= first.Id);

            // Run a simple query and verify we get all 3 items, but the
            // TotalCount is not provided.
            List <ToDo> items = await query.ToListAsync();

            countProvider = items as ITotalCountProvider;
            Assert.AreEqual(3, items.Count);
            Assert.IsNotNull(countProvider);
            Assert.AreEqual(-1L, countProvider.TotalCount);

            IEnumerable <ToDo> sequence = await query.ToEnumerableAsync();

            countProvider = sequence as ITotalCountProvider;
            Assert.IsNotNull(countProvider);
            Assert.AreEqual(-1L, countProvider.TotalCount);

            // Now use IncludeTotalCount and make sure we get the expected
            // number of results
            query = query.IncludeTotalCount();
            items = await query.ToListAsync();

            countProvider = items as ITotalCountProvider;
            Assert.AreEqual(3, items.Count);
            Assert.IsNotNull(countProvider);
            Assert.AreEqual(3L, countProvider.TotalCount);

            sequence = await query.ToEnumerableAsync();

            countProvider = sequence as ITotalCountProvider;
            Assert.IsNotNull(countProvider);
            Assert.AreEqual(3L, countProvider.TotalCount);

            // Verify that IncludeTotalCount is correctly propagated with
            // projections
            List <string> titles = await query.Select(t => t.Title).ToListAsync();

            countProvider = titles as ITotalCountProvider;
            Assert.AreEqual(3, titles.Count);
            Assert.IsNotNull(countProvider);
            Assert.AreEqual(3L, countProvider.TotalCount);
        }
        /// <summary>
        /// Initializes a new instance of the TotalCountList class.
        /// </summary>
        /// <param name="sequence">
        /// The sequence whose elements comprise the list.
        /// </param>
        public TotalCountList(IEnumerable <T> sequence)
            : base(sequence)
        {
            this.TotalCount = -1;

            // Forward along the total count from our sequence if it was
            // provided
            ITotalCountProvider provider = sequence as ITotalCountProvider;

            if (provider != null)
            {
                this.TotalCount = provider.TotalCount;
            }
        }
        private async Task CreateQueryTest <MovieType, TExpectedException>(
            string name, Expression <Func <MovieType, bool> > whereClause,
            int?top = null, int?skip = null, OrderByClause[] orderBy = null,
            Expression <Func <MovieType, string> > selectExpression = null, bool?includeTotalCount = null,
            string odataExpression             = null, bool useStringIdTable = false,
            Func <MovieType, bool> whereLambda = null)
            where MovieType : class, IMovie
            where TExpectedException : Exception
        {
            Log("### Executing {0}.", name);
            if (whereClause == null && whereLambda != null)
            {
                Assert.Fail("The argument 'whereLambda' is optional and can only be specified if 'whereClause' is also specified.");
            }

            try
            {
                var table = this.GetClient().GetTable <MovieType>();
                IEnumerable <MovieType> readMovies          = null;
                IEnumerable <string>    readProjectedMovies = null;

                if (odataExpression == null)
                {
                    IMobileServiceTableQuery <MovieType> query         = null;
                    IMobileServiceTableQuery <string>    selectedQuery = null;

                    if (whereClause != null)
                    {
                        query = table.Where(whereClause);
                    }

                    if (orderBy != null)
                    {
                        if (query == null)
                        {
                            query = table.Where(m => m.Duration == m.Duration);
                        }

                        query = ApplyOrdering(query, orderBy);
                    }

                    if (top.HasValue)
                    {
                        query = query == null?table.Take(top.Value) : query.Take(top.Value);
                    }

                    if (skip.HasValue)
                    {
                        query = query == null?table.Skip(skip.Value) : query.Skip(skip.Value);
                    }

                    if (selectExpression != null)
                    {
                        selectedQuery = query == null?table.Select(selectExpression) : query.Select(selectExpression);
                    }

                    if (includeTotalCount.HasValue)
                    {
                        query = query.IncludeTotalCount();
                    }

                    if (selectedQuery == null)
                    {
                        // Both ways of querying should be equivalent, so using both with equal probability here.
                        // TODO: Make it deterministic
                        var tickCount = Environment.TickCount;
                        if ((tickCount % 2) == 0)
                        {
                            Log("Querying using MobileServiceTableQuery<T>.ToEnumerableAsync");
                            readMovies = await query.ToEnumerableAsync();
                        }
                        else
                        {
                            Log("Querying using IMobileServiceTable<T>.ReadAsync(MobileServiceTableQuery<U>)");
                            readMovies = await table.ReadAsync(query);
                        }
                    }
                    else
                    {
                        readProjectedMovies = await selectedQuery.ToEnumerableAsync();
                    }
                }
                else
                {
                    Log("Using the OData query directly");
                    JToken result = await table.ReadAsync(odataExpression);

                    readMovies = result.ToObject <IEnumerable <MovieType> >();
                }

                long actualTotalCount = -1;
                ITotalCountProvider totalCountProvider = (readMovies as ITotalCountProvider) ?? (readProjectedMovies as ITotalCountProvider);
                if (totalCountProvider != null)
                {
                    actualTotalCount = totalCountProvider.TotalCount;
                }

                IEnumerable <MovieType> expectedData;
                if (useStringIdTable)
                {
                    var movies = QueryTestData.TestMovies();
                    expectedData = new MovieType[movies.Length];
                    for (var i = 0; i < movies.Length; i++)
                    {
                        ((MovieType[])expectedData)[i] = (MovieType)(IMovie)movies[i];
                    }
                }
                else
                {
                    expectedData = QueryTestData.TestIntIdMovies.Select(s => (MovieType)(IMovie)s);
                }

                // Due to a Xamarin.iOS bug, Expression.Compile() does not work for some expression trees,
                // in which case we allow the caller to provide a lambda directly and we use it instead of
                // compiling the expression tree.
                if (whereLambda != null)
                {
                    expectedData = expectedData.Where(whereLambda);
                }
                else if (whereClause != null)
                {
                    expectedData = expectedData.Where(whereClause.Compile());
                }

                long expectedTotalCount = -1;
                if (includeTotalCount.HasValue && includeTotalCount.Value)
                {
                    expectedTotalCount = expectedData.Count();
                }

                if (orderBy != null)
                {
                    expectedData = ApplyOrdering(expectedData, orderBy);
                }

                if (skip.HasValue)
                {
                    expectedData = expectedData.Skip(skip.Value);
                }

                if (top.HasValue)
                {
                    expectedData = expectedData.Take(top.Value);
                }

                if (includeTotalCount.HasValue)
                {
                    if (expectedTotalCount != actualTotalCount)
                    {
                        Log("Total count was requested, but the returned value is incorrect: expected={0}, actual={1}", expectedTotalCount, actualTotalCount);
                        Assert.Fail("");
                        return;
                    }
                }

                List <string> errors = new List <string>();
                bool          expectedDataIsSameAsReadData;

                if (selectExpression != null)
                {
                    string[] expectedProjectedData = expectedData.Select(selectExpression.Compile()).ToArray();
                    expectedDataIsSameAsReadData = Utilities.CompareArrays(expectedProjectedData, readProjectedMovies.ToArray(), errors);
                }
                else
                {
                    expectedDataIsSameAsReadData = Utilities.CompareArrays(expectedData.ToArray(), readMovies.ToArray(), errors);
                }

                if (!expectedDataIsSameAsReadData)
                {
                    foreach (var error in errors)
                    {
                        Log(error);
                    }

                    Log("Expected data is different");
                    Assert.Fail("");
                    return;
                }
                else
                {
                    if (typeof(TExpectedException) == typeof(ExceptionTypeWhichWillNeverBeThrown))
                    {
                        return;
                    }
                    else
                    {
                        Log("Error, test should have failed with {0}, but succeeded.", typeof(TExpectedException).FullName);
                        Assert.Fail("");
                        return;
                    }
                }
            }
            catch (TExpectedException ex)
            {
                Log("Caught expected exception - {0}: {1}", ex.GetType().FullName, ex.Message);
                return;
            }
        }
示例#5
0
        private static ZumoTest CreateQueryTest <TExpectedException>(
            string name, Expression <Func <Movie, bool> > whereClause,
            int?top = null, int?skip = null, OrderByClause[] orderBy = null,
            Expression <Func <Movie, string> > selectExpression = null, bool?includeTotalCount = null)
            where TExpectedException : Exception
        {
            return(new ZumoTest(name, async delegate(ZumoTest test)
            {
                try
                {
                    var table = ZumoTestGlobals.Instance.Client.GetTable <Movie>();
                    MobileServiceTableQuery <Movie> query = null;
                    MobileServiceTableQuery <string> selectedQuery = null;

                    if (whereClause != null)
                    {
                        query = table.Where(whereClause);
                    }

                    if (orderBy != null)
                    {
                        if (query == null)
                        {
                            query = table.Where(m => m.Duration == m.Duration);
                        }

                        query = ApplyOrdering(query, orderBy);
                    }

                    if (top.HasValue)
                    {
                        query = query == null ? table.Take(top.Value) : query.Take(top.Value);
                    }

                    if (skip.HasValue)
                    {
                        query = query == null ? table.Skip(skip.Value) : query.Skip(skip.Value);
                    }

                    if (selectExpression != null)
                    {
                        selectedQuery = query == null ? table.Select(selectExpression) : query.Select(selectExpression);
                    }

                    if (includeTotalCount.HasValue)
                    {
                        query = query.IncludeTotalCount();
                    }

                    IEnumerable <Movie> readMovies = null;
                    IEnumerable <string> readProjectedMovies = null;
                    if (selectedQuery == null)
                    {
                        // Both ways of querying should be equivalent, so using both with equal probability here.
                        var tickCount = Environment.TickCount;
                        if ((tickCount % 2) == 0)
                        {
                            test.AddLog("Querying using MobileServiceTableQuery<T>.ToEnumerableAsync");
                            readMovies = await query.ToEnumerableAsync();
                        }
                        else
                        {
                            test.AddLog("Querying using IMobileServiceTable<T>.ReadAsync(MobileServiceTableQuery<U>)");
                            readMovies = await table.ReadAsync(query);
                        }
                    }
                    else
                    {
                        readProjectedMovies = await selectedQuery.ToEnumerableAsync();
                    }

                    long actualTotalCount = -1;
                    ITotalCountProvider totalCountProvider = (readMovies as ITotalCountProvider) ?? (readProjectedMovies as ITotalCountProvider);
                    if (totalCountProvider != null)
                    {
                        actualTotalCount = totalCountProvider.TotalCount;
                    }

                    IEnumerable <Movie> expectedData = ZumoQueryTestData.AllMovies;
                    if (whereClause != null)
                    {
                        expectedData = expectedData.Where(whereClause.Compile());
                    }

                    long expectedTotalCount = -1;
                    if (includeTotalCount.HasValue && includeTotalCount.Value)
                    {
                        expectedTotalCount = expectedData.Count();
                    }

                    if (orderBy != null)
                    {
                        expectedData = ApplyOrdering(expectedData, orderBy);
                    }

                    if (skip.HasValue)
                    {
                        expectedData = expectedData.Skip(skip.Value);
                    }

                    if (top.HasValue)
                    {
                        expectedData = expectedData.Take(top.Value);
                    }


                    if (includeTotalCount.HasValue)
                    {
                        if (expectedTotalCount != actualTotalCount)
                        {
                            test.AddLog("Total count was requested, but the returned value is incorrect: expected={0}, actual={1}", expectedTotalCount, actualTotalCount);
                            return false;
                        }
                    }

                    List <string> errors = new List <string>();
                    bool expectedDataIsSameAsReadData;

                    if (selectExpression != null)
                    {
                        string[] expectedProjectedData = expectedData.Select(selectExpression.Compile()).ToArray();
                        expectedDataIsSameAsReadData = Util.CompareArrays(expectedProjectedData, readProjectedMovies.ToArray(), errors);
                    }
                    else
                    {
                        expectedDataIsSameAsReadData = Util.CompareArrays(expectedData.ToArray(), readMovies.ToArray(), errors);
                    }

                    if (!expectedDataIsSameAsReadData)
                    {
                        foreach (var error in errors)
                        {
                            test.AddLog(error);
                        }

                        test.AddLog("Expected data is different");
                        return false;
                    }
                    else
                    {
                        if (typeof(TExpectedException) == typeof(ExceptionTypeWhichWillNeverBeThrown))
                        {
                            return true;
                        }
                        else
                        {
                            test.AddLog("Error, test should have failed with {0}, but succeeded.", typeof(TExpectedException).FullName);
                            return false;
                        }
                    }
                }
                catch (TExpectedException ex)
                {
                    test.AddLog("Caught expected exception - {0}: {1}", ex.GetType().FullName, ex.Message);
                    return true;
                }
            }));
        }
        private async Task CreateQueryTest <MovieType, TExpectedException>(
            string name, Expression <Func <MovieType, bool> > whereClause,
            int?top = null, int?skip = null, OrderByClause[] orderBy = null,
            Expression <Func <MovieType, string> > selectExpression = null, bool?includeTotalCount = null,
            string odataExpression             = null, bool useStringIdTable = false,
            Func <MovieType, bool> whereLambda = null)
            where MovieType : class, IMovie
            where TExpectedException : Exception
        {
            Assert.False(whereClause == null && whereLambda != null);

            try
            {
                var table = this.GetClient().GetTable <MovieType>();
                IEnumerable <MovieType> readMovies          = null;
                IEnumerable <string>    readProjectedMovies = null;

                if (odataExpression == null)
                {
                    IMobileServiceTableQuery <MovieType> query         = null;
                    IMobileServiceTableQuery <string>    selectedQuery = null;

                    if (whereClause != null)
                    {
                        query = table.Where(whereClause);
                    }

                    if (orderBy != null)
                    {
                        if (query == null)
                        {
                            query = table.Where(m => m.Duration == m.Duration);
                        }

                        query = ApplyOrdering(query, orderBy);
                    }

                    if (top.HasValue)
                    {
                        query = query == null?table.Take(top.Value) : query.Take(top.Value);
                    }

                    if (skip.HasValue)
                    {
                        query = query == null?table.Skip(skip.Value) : query.Skip(skip.Value);
                    }

                    if (selectExpression != null)
                    {
                        selectedQuery = query == null?table.Select(selectExpression) : query.Select(selectExpression);
                    }

                    if (includeTotalCount.HasValue)
                    {
                        query = query.IncludeTotalCount();
                    }

                    if (selectedQuery == null)
                    {
                        // Both ways of querying should be equivalent, so using both with equal probability here.
                        // TODO: Make it deterministic
                        var tickCount = Environment.TickCount;
                        if ((tickCount % 2) == 0)
                        {
                            readMovies = await query.ToEnumerableAsync();
                        }
                        else
                        {
                            readMovies = await table.ReadAsync(query);
                        }
                    }
                    else
                    {
                        readProjectedMovies = await selectedQuery.ToEnumerableAsync();
                    }
                }
                else
                {
                    JToken result = await table.ReadAsync(odataExpression);

                    readMovies = result.ToObject <IEnumerable <MovieType> >();
                }

                long actualTotalCount = -1;
#pragma warning disable CS0618 // Type or member is obsolete
                ITotalCountProvider totalCountProvider = (readMovies as ITotalCountProvider) ?? (readProjectedMovies as ITotalCountProvider);
#pragma warning restore CS0618 // Type or member is obsolete
                if (totalCountProvider != null)
                {
                    actualTotalCount = totalCountProvider.TotalCount;
                }

                IEnumerable <MovieType> expectedData;
                if (useStringIdTable)
                {
                    var movies = QueryTestData.TestMovies();
                    expectedData = new MovieType[movies.Length];
                    for (var i = 0; i < movies.Length; i++)
                    {
                        ((MovieType[])expectedData)[i] = (MovieType)(IMovie)movies[i];
                    }
                }
                else
                {
                    expectedData = QueryTestData.TestIntIdMovies.Select(s => (MovieType)(IMovie)s);
                }

                // Due to a Xamarin.iOS bug, Expression.Compile() does not work for some expression trees,
                // in which case we allow the caller to provide a lambda directly and we use it instead of
                // compiling the expression tree.
                if (whereLambda != null)
                {
                    expectedData = expectedData.Where(whereLambda);
                }
                else if (whereClause != null)
                {
                    expectedData = expectedData.Where(whereClause.Compile());
                }

                long expectedTotalCount = -1;
                if (includeTotalCount.HasValue && includeTotalCount.Value)
                {
                    expectedTotalCount = expectedData.Count();
                }

                if (orderBy != null)
                {
                    expectedData = ApplyOrdering(expectedData, orderBy);
                }

                if (skip.HasValue)
                {
                    expectedData = expectedData.Skip(skip.Value);
                }

                if (top.HasValue)
                {
                    expectedData = expectedData.Take(top.Value);
                }

                if (includeTotalCount.HasValue)
                {
                    Assert.Equal(expectedTotalCount, actualTotalCount);
                }

                List <string> errors = new List <string>();
                bool          expectedDataIsSameAsReadData;

                if (selectExpression != null)
                {
                    string[] expectedProjectedData = expectedData.Select(selectExpression.Compile()).ToArray();
                    expectedDataIsSameAsReadData = Utilities.CompareArrays(expectedProjectedData, readProjectedMovies.ToArray(), errors);
                }
                else
                {
                    expectedDataIsSameAsReadData = Utilities.CompareArrays(expectedData.ToArray(), readMovies.ToArray(), errors);
                }

                Assert.True(expectedDataIsSameAsReadData);

                if (typeof(TExpectedException) != typeof(ExceptionTypeWhichWillNeverBeThrown))
                {
                    Assert.True(false, "Test should have failed");
                    return;
                }
            }
            catch (TExpectedException)
            {
                return;
            }
        }
示例#7
0
        private static ZumoTest CreateQueryTest <MovieType, TExpectedException>(
            string name, Expression <Func <MovieType, bool> > whereClause,
            int?top = null, int?skip = null, OrderByClause[] orderBy = null,
            Expression <Func <MovieType, string> > selectExpression = null, bool?includeTotalCount = null,
            string odataExpression = null, bool useStringIdTable = false)
            where MovieType : class, IMovie
            where TExpectedException : Exception
        {
            return(new ZumoTest(name, async delegate(ZumoTest test)
            {
                try
                {
                    var table = ZumoTestGlobals.Instance.Client.GetTable <MovieType>();
                    IEnumerable <MovieType> readMovies = null;
                    IEnumerable <string> readProjectedMovies = null;

                    if (odataExpression == null)
                    {
                        IMobileServiceTableQuery <MovieType> query = null;
                        IMobileServiceTableQuery <string> selectedQuery = null;

                        if (whereClause != null)
                        {
                            query = table.Where(whereClause);
                        }

                        if (orderBy != null)
                        {
                            if (query == null)
                            {
                                query = table.Where(m => m.Duration == m.Duration);
                            }

                            query = ApplyOrdering(query, orderBy);
                        }

                        if (top.HasValue)
                        {
                            query = query == null ? table.Take(top.Value) : query.Take(top.Value);
                        }

                        if (skip.HasValue)
                        {
                            query = query == null ? table.Skip(skip.Value) : query.Skip(skip.Value);
                        }

                        if (selectExpression != null)
                        {
                            selectedQuery = query == null ? table.Select(selectExpression) : query.Select(selectExpression);
                        }

                        if (includeTotalCount.HasValue)
                        {
                            query = query.IncludeTotalCount();
                        }

                        if (selectedQuery == null)
                        {
                            // Both ways of querying should be equivalent, so using both with equal probability here.
                            var tickCount = Environment.TickCount;
                            if ((tickCount % 2) == 0)
                            {
                                test.AddLog("Querying using MobileServiceTableQuery<T>.ToEnumerableAsync");
                                readMovies = await query.ToEnumerableAsync();
                            }
                            else
                            {
                                test.AddLog("Querying using IMobileServiceTable<T>.ReadAsync(MobileServiceTableQuery<U>)");
                                readMovies = await table.ReadAsync(query);
                            }
                        }
                        else
                        {
                            readProjectedMovies = await selectedQuery.ToEnumerableAsync();
                        }
                    }
                    else
                    {
                        test.AddLog("Using the OData query directly");
                        JToken result = await table.ReadAsync(odataExpression);
                        if (ZumoTestGlobals.Instance.IsNetRuntime)
                        {
                            var serializer = new JsonSerializer();
                            serializer.Converters.Add(new MobileServiceIsoDateTimeConverter());
                            readMovies = result.ToObject <IEnumerable <MovieType> >(serializer);
                        }
                        else
                        {
                            readMovies = result.ToObject <IEnumerable <MovieType> >();
                        }
                    }

                    long actualTotalCount = -1;
                    ITotalCountProvider totalCountProvider = (readMovies as ITotalCountProvider) ?? (readProjectedMovies as ITotalCountProvider);
                    if (totalCountProvider != null)
                    {
                        actualTotalCount = totalCountProvider.TotalCount;
                    }

                    if (ZumoTestGlobals.Instance.IsNetRuntime && top.HasValue && top.Value == VeryLargeTopValue)
                    {
                        test.AddLog("NetRuntime throttles to 100 and does not throw");
                        return readMovies.Count() == 100;
                    }

                    IEnumerable <MovieType> expectedData;
                    if (useStringIdTable)
                    {
                        var movies = ZumoQueryTestData.AllStringIdMovies();
                        expectedData = new MovieType[movies.Length];
                        for (var i = 0; i < movies.Length; i++)
                        {
                            ((MovieType[])expectedData)[i] = (MovieType)(IMovie)movies[i];
                        }
                    }
                    else
                    {
                        expectedData = ZumoQueryTestData.AllMovies.Select(s => (MovieType)(IMovie)s);
                    }

                    if (whereClause != null)
                    {
                        expectedData = expectedData.Where(whereClause.Compile());
                    }

                    long expectedTotalCount = -1;
                    if (includeTotalCount.HasValue && includeTotalCount.Value)
                    {
                        expectedTotalCount = expectedData.Count();
                    }

                    if (orderBy != null)
                    {
                        expectedData = ApplyOrdering(expectedData, orderBy);
                    }

                    if (skip.HasValue)
                    {
                        expectedData = expectedData.Skip(skip.Value);
                    }

                    if (top.HasValue)
                    {
                        expectedData = expectedData.Take(top.Value);
                    }


                    if (includeTotalCount.HasValue)
                    {
                        if (expectedTotalCount != actualTotalCount)
                        {
                            test.AddLog("Total count was requested, but the returned value is incorrect: expected={0}, actual={1}", expectedTotalCount, actualTotalCount);
                            return false;
                        }
                    }

                    List <string> errors = new List <string>();
                    bool expectedDataIsSameAsReadData;

                    if (selectExpression != null)
                    {
                        string[] expectedProjectedData = expectedData.Select(selectExpression.Compile()).ToArray();
                        expectedDataIsSameAsReadData = Util.CompareArrays(expectedProjectedData, readProjectedMovies.ToArray(), errors);
                    }
                    else
                    {
                        expectedDataIsSameAsReadData = Util.CompareArrays(expectedData.ToArray(), readMovies.ToArray(), errors);
                    }

                    if (!expectedDataIsSameAsReadData)
                    {
                        foreach (var error in errors)
                        {
                            test.AddLog(error);
                        }

                        test.AddLog("Expected data is different");
                        return false;
                    }
                    else
                    {
                        if (typeof(TExpectedException) == typeof(ExceptionTypeWhichWillNeverBeThrown))
                        {
                            return true;
                        }
                        else
                        {
                            test.AddLog("Error, test should have failed with {0}, but succeeded.", typeof(TExpectedException).FullName);
                            return false;
                        }
                    }
                }
                catch (TExpectedException ex)
                {
                    test.AddLog("Caught expected exception - {0}: {1}", ex.GetType().FullName, ex.Message);
                    return true;
                }
            }, typeof(MovieType) == typeof(Movie) ? ZumoTestGlobals.RuntimeFeatureNames.INT_ID_TABLES : ZumoTestGlobals.RuntimeFeatureNames.STRING_ID_TABLES));
        }