/// <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 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;
        }