/// <summary>
        /// Tests filters for metadata.
        /// </summary>
        /// <param name="testThis"></param>
        /// <param name="test"></param>
        /// <param name="arg"></param>
        /// <param name="cds"></param>
        /// <param name="stats"></param>
        /// <returns></returns>
        public static CdsBrowseSearchResults TestFiltersBrowseMetadata(IUPnPMedia testThis, CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag browseFlag, Cds_BrowseFilter test, CdsSubTestArgument arg, CpContentDirectory cds, CdsResult_BrowseFilter stats)
        {
            CdsBrowseSearchResults r = new CdsBrowseSearchResults();

            try
            {
                BrowseInput input = new BrowseInput();
                input.BrowseFlag = browseFlag;
                if (input.BrowseFlag == CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEMETADATA)
                {
                    input.ObjectID = testThis.ID;
                }
                else
                {
                    input.ObjectID = testThis.ParentID;
                }
                input.SortCriteria = "";

                input.StartingIndex  = 0;
                input.RequestedCount = 0;

                //assume the test will pass, but worsen the result when we find errors
                r.SetError(UPnPTestStates.Pass);

                ArrayList propNames = new ArrayList((ICollection)testThis.Properties.PropertyNames);

                // add property names related to resources
                propNames.Add(T[_DIDL.Res]);
                foreach (string str in MediaResource.GetPossibleAttributes())
                {
                    propNames.Add(T[_DIDL.Res] + "@" + str);
                    propNames.Add("@" + str);
                }
                propNames.Add(T[_DIDL.Desc]);

                if (propNames.Count != stats.NumberOfProperties)
                {
                    throw new TestException("Number of calculated metadata properties (" + propNames.Count + ") doesn't match number of actual metadata properties (" + propNames.Count + ") for testing.", stats);
                }

                test._Details.Filters = propNames;

                int inc = propNames.Count / 4;
                if (inc == 0)
                {
                    inc = 1;
                }

                for (int numProps = 0; numProps < propNames.Count; numProps++)
                {
                    for (int iProp = 0; iProp < propNames.Count; iProp += inc)
                    {
                        IList filterSettings = GetFilterSettings(propNames, numProps, iProp, iProp);

                        input.Filter = GetCSVString(filterSettings);

                        CdsBrowseSearchResults br;

                        arg.ActiveTests.UpdateTimeAndProgress(90 * stats.TotalBrowseRequests);

                        br = Cds_BrowseAll.Browse(input, test, arg, cds, stats);

                        if (br.WorstError >= UPnPTestStates.Failed)
                        {
                            StringBuilder teMsg = new StringBuilder();

                            teMsg.AppendFormat("\"{0}\" is terminating early because {1} returned with an error or had problems with the DIDL-Lite.", test.Name, input.PrintBrowseParams());
                            if (br.Result != "")
                            {
                                teMsg.AppendFormat(" Returned DIDL=\"{0}\".", br.Result);
                            }
                            throw new TerminateEarly(teMsg.ToString());
                        }
                        //							if (br.InvokeError != null)
                        //							{
                        //								throw new TerminateEarly("\"" + test.Name + "\" is terminating early because " +input.PrintBrowseParams()+ " returned with an error" + br.InvokeError.Message, br.InvokeError);
                        //							}

                        if (input.BrowseFlag == CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEMETADATA)
                        {
                            if (br.MediaObjects.Count != 1)
                            {
                                throw new TerminateEarly("\"" + test.Name + "\" is terminating early because " + input.PrintBrowseParams() + " did not return with exactly one media object in its DIDL-Lite response. DIDL-Lite => " + br.Result);
                            }

                            IUPnPMedia mo = (IUPnPMedia)br.MediaObjects[0];

                            CheckReturnedMetadata(mo, testThis, input, filterSettings, br, test);
                            arg.TestGroup.AddEvent(LogImportance.Remark, test.Name, "\"" + test.Name + "\" did a " + input.PrintBrowseParams() + " and encountered no errors in the results.");
                        }
                        else
                        {
                            IList childList = testThis.Parent.CompleteList;

                            foreach (IUPnPMedia gotChild in br.MediaObjects)
                            {
                                IUPnPMedia testAgainstChild = null;
                                foreach (IUPnPMedia child in childList)
                                {
                                    if (child.ID == gotChild.ID)
                                    {
                                        testAgainstChild = child;
                                        break;
                                    }
                                }
                                if (testAgainstChild == null)
                                {
                                    throw new TerminateEarly("\"" + test.Name + "\" is terminating early because " + input.PrintBrowseParams() + " returned media object with ID=\"" + gotChild.ID + "\", which was not a child object of containerID=\"" + input.ObjectID + "\" during a prerequisite test.");
                                }
                                CheckReturnedMetadata(gotChild, testAgainstChild, input, filterSettings, br, test);
                                arg.TestGroup.AddEvent(LogImportance.Remark, test.Name, "\"" + test.Name + "\" did a " + input.PrintBrowseParams() + " and encountered no errors in the results.");
                            }
                        }
                    }
                }
            }
            catch (TerminateEarly te)
            {
                string reason = "\"" + test.Name + "\" terminating early. Reason => " + te.Message;
                arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, reason);

                r.SetError(UPnPTestStates.Failed);
                return(r);
            }

            if (r.WorstError > UPnPTestStates.Warn)
            {
                throw new TestException("\"" + test.Name + "\" should not reach this code if the result is worse than " + UPnPTestStates.Warn.ToString() + ".", null);
            }

            return(r);
        }
        /// <summary>
        /// Tests the container with BrowseDirectChildren and different ranges.
        /// </summary>
        /// <param name="c"></param>
        /// <param name="test"></param>
        /// <param name="arg"></param>
        /// <param name="cds"></param>
        /// <param name="stats"></param>
        /// <returns></returns>
        public static CdsBrowseSearchResults TestContainerRanges(IMediaContainer c, CdsSubTest test, CdsSubTestArgument arg, CpContentDirectory cds, CdsResult_BrowseStats stats)
        {
            CdsBrowseSearchResults r = new CdsBrowseSearchResults();

            try
            {
                BrowseInput input = new BrowseInput();
                input.BrowseFlag   = CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEDIRECTCHILDREN;
                input.Filter       = "*";
                input.ObjectID     = c.ID;
                input.SortCriteria = "";

                input.StartingIndex  = 0;
                input.RequestedCount = 0;

                if (c.CompleteList.Count != c.ChildCount)
                {
                    throw new TestException("\"" + test.Name + "\" has c.CompleteList.Count=" + c.CompleteList.Count + " but c.ChildCount=" + c.ChildCount + ".", c);
                }

                //assume the test will pass, but worsen the result when we find errors
                r.SetError(UPnPTestStates.Pass);

                // test starting index = 0 to c.ChildCount+1
                // test requested count = 0 to c.ChildCoun+1
                for (uint start = 0; start < c.ChildCount + 1; start++)
                {
                    for (uint requested = 0; requested < c.ChildCount + 1; requested++)
                    {
                        uint start_requested = start + requested;
                        bool doBrowse        = false;
                        int  tenthCount      = c.ChildCount / 10;
                        int  quarterCount    = c.ChildCount / 4;
                        if (quarterCount == 0)
                        {
                            quarterCount = 1;
                        }
                        if (tenthCount == 0)
                        {
                            tenthCount = 1;
                        }

                        if (start < LIMIT)
                        {
                            if (
                                (requested == 0) ||
                                (start_requested > c.ChildCount - LIMIT)
                                )
                            {
                                doBrowse = true;
                            }
                        }
                        else if (start >= c.ChildCount - LIMIT)
                        {
                            if (start_requested < c.ChildCount + LIMIT)
                            {
                                doBrowse = true;
                            }
                        }
                        else if ((start % quarterCount == 0) && (requested % tenthCount == 0))
                        {
                            doBrowse = true;
                        }


                        if (doBrowse == false)
                        {
                            //stats.TotalBrowseRequests++;
                            //arg.ActiveTests.UpdateTimeAndProgress(stats.TotalBrowseRequests * 300);
                        }
                        else
                        {
                            arg.ActiveTests.UpdateTimeAndProgress(stats.TotalBrowseRequests * 300);

                            input.StartingIndex  = start;
                            input.RequestedCount = requested;
                            CdsBrowseSearchResults br;

                            br = Cds_BrowseAll.Browse(input, test, arg, cds, stats);

                            if (br.WorstError >= UPnPTestStates.Failed)
                            {
                                if (
                                    (input.StartingIndex < c.ChildCount)
                                    )
                                {
                                    throw new TerminateEarly("\"" + test.Name + "\" is terminating early because " + input.PrintBrowseParams() + " returned with an error or had problems with the DIDL-Lite.");
                                }
                                else
                                {
                                    arg._TestGroup.AddEvent(LogImportance.High, test.Name, "\"" + test.Name + "\": Warning: " + input.PrintBrowseParams() + " returned an error.");
                                    r.SetError(UPnPTestStates.Warn);
                                }
                            }

                            // check return values

                            if (br.NumberReturned != br.MediaObjects.Count)
                            {
                                throw new TerminateEarly("\"" + test.Name + "\" did a " + input.PrintBrowseParams() + " and the number of media objects instantiated from the DIDL-Lite (instantiated=" + br.MediaObjects.Count + ") does not match NumberReturned=" + br.NumberReturned + ".");
                            }

                            long expectedReturned;

                            if (input.StartingIndex == 0)
                            {
                                if (input.RequestedCount == 0)
                                {
                                    expectedReturned = c.ChildCount;
                                }
                                else if (input.RequestedCount > c.ChildCount)
                                {
                                    expectedReturned = c.ChildCount;
                                }
                                else if (input.RequestedCount <= c.ChildCount)
                                {
                                    expectedReturned = input.RequestedCount;
                                }
                                else
                                {
                                    throw new TestException("\"" + test.Name + "\" should not reach here.", null);
                                }
                            }
                            else
                            {
                                if (input.RequestedCount == 0)
                                {
                                    expectedReturned = c.ChildCount - input.StartingIndex;
                                }
                                else
                                {
                                    expectedReturned = c.ChildCount - input.StartingIndex;

                                    if (expectedReturned > input.RequestedCount)
                                    {
                                        expectedReturned = input.RequestedCount;
                                    }
                                }
                            }

                            if ((expectedReturned < 0) || (expectedReturned > c.ChildCount))
                            {
                                throw new TestException("\"" + test.Name + "\" did a " + input.PrintBrowseParams() + " and the expected number of media objects is invalid=" + expectedReturned + ".", br);
                            }

                            if (br.NumberReturned != expectedReturned)
                            {
                                throw new TerminateEarly("\"" + test.Name + "\" did a " + input.PrintBrowseParams() + " and NumberReturned=" + br.NumberReturned + " but test expects " + expectedReturned + " child objects according to results from a prerequisite test.");
                            }

                            if (br.TotalMatches != c.ChildCount)
                            {
                                throw new TerminateEarly("\"" + test.Name + "\" did a " + input.PrintBrowseParams() + " and TotalMatches=" + br.TotalMatches + " but test found " + c.ChildCount + " child objects in a prerequisite test.");
                            }

                            uint cUpdateID = 0;

                            try
                            {
                                cUpdateID = (uint)c.Tag;
                            }
                            catch (Exception ce)
                            {
                                throw new TestException("\"" + test.Name + "\" could not cast c.Tag into a uint value", null, ce);
                            }

                            if (br.UpdateID != cUpdateID)
                            {
                                throw new TerminateEarly("\"" + test.Name + "\" did a " + input.PrintBrowseParams() + " and UpdateID=" + br.UpdateID + " but test expected=" + cUpdateID + " as found in a prerequisite test.");
                            }

                            arg.TestGroup.AddEvent(LogImportance.Remark, test.Name, "\"" + test.Name + "\" did a " + input.PrintBrowseParams() + " and encountered no errors in the results.");
                            arg.ActiveTests.UpdateTimeAndProgress(stats.TotalBrowseRequests * 300);
                        }
                    }
                }
            }
            catch (TerminateEarly te)
            {
                arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, test.Name + " is terminating early because of the following error: " + te.Message);
                r.SetError(UPnPTestStates.Failed);
                return(r);
            }

            if (r.WorstError > UPnPTestStates.Warn)
            {
                throw new TestException("\"" + test.Name + "\" should not reach this code if the result is worse than " + UPnPTestStates.Warn.ToString() + ".", null);
            }

            return(r);
        }