public override void CalculateExpectedTestingTime(ICollection otherSubTests, ISubTestArgument arg)
        {
            // get the results from the prerequisite tests
            CdsResult_BrowseFilter       FILTER_RESULTS = null;
            CdsResult_BrowseRange        RANGE_RESULTS  = null;
            CdsResult_BrowseSortCriteria SORT_RESULTS   = null;

            foreach (ISubTest preTest in otherSubTests)
            {
                if (preTest.Name == this.PRE_FILTER.Name)
                {
                    FILTER_RESULTS = preTest.Details as CdsResult_BrowseFilter;
                }
                else if (preTest.Name == this.PRE_RANGE.Name)
                {
                    RANGE_RESULTS = preTest.Details as CdsResult_BrowseRange;
                }
                else if (preTest.Name == this.PRE_SORT.Name)
                {
                    SORT_RESULTS = preTest.Details as CdsResult_BrowseSortCriteria;
                }
            }

            if (FILTER_RESULTS == null)
            {
                return;
            }

            if (RANGE_RESULTS == null)
            {
                return;
            }

            if (SORT_RESULTS == null)
            {
                return;
            }

            CdsResult_BrowseAll BROWSE_ALL = FILTER_RESULTS.BrowseAllResults;

            if (BROWSE_ALL.LargestContainer == null)
            {
                return;
            }
            if (BROWSE_ALL.MostMetadata == null)
            {
                return;
            }
            if (SORT_RESULTS.SortFields == null)
            {
                return;
            }

            int max = Math.Max(FILTER_RESULTS.Filters.Count, BROWSE_ALL.LargestContainer.ChildCount);

            max = Math.Max(max, SORT_RESULTS.SortFields.Count);
            this._ExpectedTestingTime = max * 900;
        }
        /// <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);
        }
        public override UPnPTestStates Run(ICollection otherSubTests, CdsSubTestArgument arg)
        {
            CpContentDirectory CDS = this.GetCDS(arg._Device);

            _Details        = new CdsResult_BrowseFilter();
            this._TestState = UPnPTestStates.Running;
            arg._TestGroup.AddEvent(LogImportance.Remark, this.Name, "\"" + this.Name + "\" started.");

            // get the results from the BrowseAll test
            CdsResult_BrowseAll PRE = null;

            try
            {
                foreach (ISubTest preTest in otherSubTests)
                {
                    if (preTest.Name == this.PRE_BROWSEALL.Name)
                    {
                        PRE = preTest.Details as CdsResult_BrowseAll;
                        break;
                    }
                }

                if (PRE == null)
                {
                    throw new TestException(this._Name + " requires that the \"" + this.PRE_BROWSEALL.Name + "\" test be run as a prerequisite. The results from that test cannot be obtained.", otherSubTests);
                }
            }
            catch (Exception e)
            {
                throw new TestException(this._Name + " requires that the \"" + this.PRE_BROWSEALL.Name + "\" test be run before. An error occurred when attempting to obtain the results of a prerequisite.", otherSubTests, e);
            }
            _Details.BrowseAllResults = PRE;

            if (PRE.MostMetadata == null)
            {
                throw new TestException(this.PRE_BROWSEALL.Name + " failed to find a media object with the most metadata. " + this._Name + " requires this value.", PRE);
            }

            if (PRE.MostMetadata.Properties.Count != PRE.MostMetadata.Properties.PropertyNames.Count)
            {
                throw new TestException(this.Name + " has conflicting reports for the number of metadata properties. " + PRE.MostMetadata.Properties.Count + "/" + PRE.MostMetadata.Properties.PropertyNames.Count, PRE.MostMetadata.Properties);
            }

            IUPnPMedia MM = PRE.MostMetadata;

            if (MM == null)
            {
                string skippedMsg = "\"" + this.Name + "\" skipped because the tested content hierarchy does not have a media object with at least one resource and has an ID!=\"0\"";
                arg.TestGroup.AddEvent(LogImportance.Critical, this.Name, skippedMsg);
                arg.TestGroup.AddResult(skippedMsg);
                return(UPnPTestStates.Ready);
            }

            IMediaContainer MC = PRE.MostMetadata.Parent;

            if (MC == null)
            {
                throw new TestException(this.Name + " has MostMetadata.Parent == null", PRE.MostMetadata);
            }

            int numProps    = MM.Properties.Count;
            int numChildren = MC.ChildCount;

            // we browse for "res" and each possible res@attribute
            numProps += (2 * MediaResource.GetPossibleAttributes().Count) + 2;

            _Details.NumberOfProperties          = numProps;
            _Details.ExpectedTotalBrowseRequests = 0;
            int inc = numProps / 4;

            if (inc == 0)
            {
                inc = 1;
            }
            for (int nProps = 0; nProps < numProps; nProps++)
            {
                for (int iProp = 0; iProp < numProps; iProp += inc)
                {
                    _Details.ExpectedTotalBrowseRequests++;
                }
            }
            _Details.ExpectedTotalBrowseRequests *= 2;
            int maxTime = 90 * _Details.ExpectedTotalBrowseRequests;

            this._ExpectedTestingTime = maxTime;

            // browse metadata with various filter settings
            // browsedirectchildren with various filter settings

            UPnPTestStates state = this._TestState;

            CdsBrowseSearchResults test1 = TestFiltersBrowseMetadata(MM, CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEMETADATA, this, arg, CDS, _Details);

            if (test1.WorstError > state)
            {
                state = test1.WorstError;
            }

            CdsBrowseSearchResults test2 = TestFiltersBrowseMetadata(MM, CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEDIRECTCHILDREN, this, arg, CDS, _Details);

            if (test2.WorstError > state)
            {
                state = test2.WorstError;
            }

            // finish up logging
            this._TestState = state;

            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("\"{0}\" completed", this.Name);

            if (this._TestState <= UPnPTestStates.Running)
            {
                throw new TestException("\"" + this.Name + "\" must have a pass/warn/fail result.", this._TestState);
            }

            switch (this._TestState)
            {
            case UPnPTestStates.Pass:
                sb.Append(" successfully.");
                break;

            case UPnPTestStates.Warn:
                sb.Append(" with warnings.");
                break;

            case UPnPTestStates.Failed:
                sb.Append(" with a failed result.");
                break;
            }

            arg._TestGroup.AddResult(sb.ToString());

            if (this._TestState <= UPnPTestStates.Warn)
            {
                if (_Details.TotalBrowseRequests != _Details.ExpectedTotalBrowseRequests)
                {
                    throw new TestException("TotalBrowseRequests=" + _Details.TotalBrowseRequests.ToString() + " ExpectedTotal=" + _Details.ExpectedTotalBrowseRequests.ToString(), _Details);
                }
            }

            arg._TestGroup.AddEvent(LogImportance.Remark, this.Name, sb.ToString());

            return(this._TestState);
        }
        public override UPnPTestStates Run(ICollection otherSubTests, CdsSubTestArgument arg)
        {
            CpContentDirectory CDS = this.GetCDS(arg._Device);

            _Details        = new CdsResult_BrowseFilterRangeSort();
            this._TestState = UPnPTestStates.Running;
            arg._TestGroup.AddEvent(LogImportance.Remark, this.Name, "\"" + this.Name + "\" started.");


            // get the results from the prerequisite tests
            CdsResult_BrowseFilter       FILTER_RESULTS = null;
            CdsResult_BrowseRange        RANGE_RESULTS  = null;
            CdsResult_BrowseSortCriteria SORT_RESULTS   = null;

            try
            {
                foreach (ISubTest preTest in otherSubTests)
                {
                    if (preTest.Name == this.PRE_FILTER.Name)
                    {
                        FILTER_RESULTS = preTest.Details as CdsResult_BrowseFilter;
                    }
                    else if (preTest.Name == this.PRE_RANGE.Name)
                    {
                        RANGE_RESULTS = preTest.Details as CdsResult_BrowseRange;
                    }
                    else if (preTest.Name == this.PRE_SORT.Name)
                    {
                        SORT_RESULTS = preTest.Details as CdsResult_BrowseSortCriteria;
                    }
                }

                if (FILTER_RESULTS == null)
                {
                    throw new TestException(this._Name + " requires that the \"" + this.PRE_FILTER.Name + "\" test be run as a prerequisite. The results from that test cannot be obtained.", otherSubTests);
                }

                if (RANGE_RESULTS == null)
                {
                    throw new TestException(this._Name + " requires that the \"" + this.PRE_RANGE.Name + "\" test be run as a prerequisite. The results from that test cannot be obtained.", otherSubTests);
                }

                if (SORT_RESULTS == null)
                {
                    throw new TestException(this._Name + " requires that the \"" + this.PRE_SORT.Name + "\" test be run as a prerequisite. The results from that test cannot be obtained.", otherSubTests);
                }
            }
            catch (Exception e)
            {
                throw new TestException(this._Name + " requires that the \"" + this.PRE_FILTER.Name + "\" and \"" + this.PRE_RANGE + "\" and \"" + this.PRE_SORT + "\" tests be run before. An error occurred when attempting to obtain the results of those prerequisites.", otherSubTests, e);
            }

            UPnPTestStates      state      = this._TestState;
            CdsResult_BrowseAll BROWSE_ALL = FILTER_RESULTS.BrowseAllResults;

            if (BROWSE_ALL.LargestContainer == null)
            {
                throw new TestException(new Cds_BrowseAll().Name + " failed to find the container with the most child objects. " + this._Name + " requires this value.", BROWSE_ALL);
            }
            if (BROWSE_ALL.MostMetadata == null)
            {
                throw new TestException(new Cds_BrowseAll().Name + " failed to find the media object with the most metadata. " + this._Name + " requires this value.", BROWSE_ALL);
            }
            if (SORT_RESULTS.SortFields == null)
            {
                throw new TestException(new Cds_BrowseAll().Name + " failed to find the sortable fields. " + this._Name + " requires this value.", SORT_RESULTS);
            }

            int max = Math.Max(FILTER_RESULTS.Filters.Count, BROWSE_ALL.LargestContainer.ChildCount);

            max = Math.Max(max, SORT_RESULTS.SortFields.Count);

            this._Details.ExpectedTotalBrowseRequests = max + 1;
            this._Details.TotalBrowseRequests         = 0;
            this._ExpectedTestingTime = this._Details.ExpectedTotalBrowseRequests * 900;
            arg.ActiveTests.UpdateTimeAndProgress(this._Details.TotalBrowseRequests * 900);

            state = UPnPTestStates.Pass;
            for (int i = 0; i <= max; i++)
            {
                ArrayList filterList = new ArrayList();
                for (int j = 0; j < i; j++)
                {
                    if (j < FILTER_RESULTS.Filters.Count)
                    {
                        filterList.Add(FILTER_RESULTS.Filters[j]);
                    }
                    else
                    {
                        break;
                    }
                }
                string filterSettings = Cds_BrowseTest.GetCSVString(filterList);


                uint range = (uint)i;
                if (range > BROWSE_ALL.LargestContainer.ChildCount)
                {
                    range = (uint)BROWSE_ALL.LargestContainer.ChildCount;
                }


                ArrayList sortList = new ArrayList();
                for (int j = 0; j < i; j++)
                {
                    if (j < SORT_RESULTS.SortFields.Count)
                    {
                        sortList.Add(SORT_RESULTS.SortFields[j]);
                    }
                    else
                    {
                        break;
                    }
                }

                BrowseInput input = new BrowseInput();
                input.ObjectID       = BROWSE_ALL.LargestContainer.ID;
                input.BrowseFlag     = CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEDIRECTCHILDREN;
                input.Filter         = filterSettings;
                input.RequestedCount = range;
                input.SortCriteria   = Cds_BrowseSortCriteria.GetSortCriteriaString(sortList, i);
                input.StartingIndex  = 0;

                this._ExpectedTestingTime = (max) * 900;
                arg.ActiveTests.UpdateTimeAndProgress(this._Details.TotalBrowseRequests * 900);
                CdsBrowseSearchResults br = Cds_BrowseTest.Browse(input, this, arg, CDS, _Details);

                MediaContainer original = (MediaContainer)BROWSE_ALL.LargestContainer;
                uint           ignored;
                IList          expectedResults;

                expectedResults = original.BrowseSorted(0, input.RequestedCount, new MediaSorter(true, input.SortCriteria), out ignored);

                try
                {
                    if (br.WorstError <= UPnPTestStates.Warn)
                    {
                        if (br.MediaObjects.Count != expectedResults.Count)
                        {
                            throw new TerminateEarly(input.PrintBrowseParams() + " returned DIDL-Lite that declared " + br.MediaObjects.Count + " media objects but test expected " + expectedResults.Count.ToString() + " media objects as found in a prerequisite test.");
                        }

                        bool warnOrder = false;
                        for (int ri = 0; ri < br.MediaObjects.Count; ri++)
                        {
                            IUPnPMedia resultObj   = (IUPnPMedia)br.MediaObjects[ri];
                            IUPnPMedia originalObj = (IUPnPMedia)expectedResults[ri];

                            if (resultObj.ID != originalObj.ID)
                            {
                                warnOrder = true;
                            }

                            foreach (string propName in resultObj.Properties.PropertyNames)
                            {
                                if (filterList.Contains(propName) == false)
                                {
                                    if (
                                        (propName != T[_DC.title]) &&
                                        (propName != T[_UPNP.Class])
                                        )
                                    {
                                        StringBuilder msg = new StringBuilder();
                                        msg.AppendFormat("\"" + this.Name + "\" is terminating early because {0} returned DIDL-Lite with \"{1}\" metadata when it should not have done so.", input.PrintBrowseParams(), propName);
                                        throw new TerminateEarly(msg.ToString());
                                    }
                                }
                            }
                        }

                        int expectedCount = i;
                        if ((i == 0) || (i > BROWSE_ALL.LargestContainer.ChildCount))
                        {
                            expectedCount = BROWSE_ALL.LargestContainer.ChildCount;
                        }

                        if (br.MediaObjects.Count != expectedCount)
                        {
                            StringBuilder msg = new StringBuilder();
                            msg.AppendFormat("\"{0}\" did a {1} and the DIDL-Lite result only has {2} media objects when {3} media objects were expected.", this.Name, input.PrintBrowseParams(), br.MediaObjects.Count, expectedCount);
                            msg.AppendFormat(".\r\nDIDL-Lite ==> {0}", br.Result);
                            throw new TerminateEarly(msg.ToString());
                        }

                        if (warnOrder)
                        {
                            /*
                             * ArrayList missingResults = new ArrayList();
                             *
                             * foreach (IUPnPMedia em in expectedResults)
                             * {
                             *      bool found = false;
                             *      foreach (IUPnPMedia fm in br.MediaObjects)
                             *      {
                             *              if (em.ID == fm.ID)
                             *              {
                             *                      found = true;
                             *                      break;
                             *              }
                             *      }
                             *
                             *      if (found == false)
                             *      {
                             *              missingResults.Add(em);
                             *      }
                             * }
                             *
                             * if (missingResults.Count > 0)
                             * {
                             *      state = UPnPTestStates.Failed;
                             *      StringBuilder msg = new StringBuilder();
                             *      msg.AppendFormat("\"{0}\" did a {1} and the result is missing media objects.", this.Name, input.PrintBrowseParams());
                             *      msg.Append("\r\nExpected order of IDs: ");
                             *      int z = 0;
                             *      foreach (IUPnPMedia em in expectedResults)
                             *      {
                             *              if (z > 0)
                             *              {
                             *                      msg.Append(",");
                             *              }
                             *              msg.AppendFormat("\"{0}\"", em.ID);
                             *              z++;
                             *      }
                             *      msg.Append("\r\nDIDL-Lite result's order of IDs: ");
                             *      z = 0;
                             *      foreach (IUPnPMedia em in br.MediaObjects)
                             *      {
                             *              if (z > 0)
                             *              {
                             *                      msg.Append(",");
                             *              }
                             *              msg.AppendFormat("\"{0}\"", em.ID);
                             *              z++;
                             *      }
                             *      msg.AppendFormat(".\r\nDIDL-Lite ==> {0}", br.Result);
                             *      throw new TerminateEarly(msg.ToString());
                             * }
                             * else
                             */
                            {
                                StringBuilder msg = new StringBuilder();
                                msg.AppendFormat("WARNING: \"{0}\" did a {1} and got items in a different order. Target CDS either has an error in its sorting logic, or sorting logic intentionally deviates from test.", this.Name, input.PrintBrowseParams());
                                msg.Append("\r\nExpected order of IDs: ");
                                int z = 0;
                                foreach (IUPnPMedia em in expectedResults)
                                {
                                    if (z > 0)
                                    {
                                        msg.Append(",");
                                    }
                                    msg.AppendFormat("\"{0}\"", em.ID);
                                    z++;
                                }
                                msg.Append("\r\nDIDL-Lite result's order of IDs: ");
                                z = 0;
                                foreach (IUPnPMedia em in br.MediaObjects)
                                {
                                    if (z > 0)
                                    {
                                        msg.Append(",");
                                    }
                                    msg.AppendFormat("\"{0}\"", em.ID);
                                    z++;
                                }
                                msg.AppendFormat(".\r\nDIDL-Lite ==> {0}", br.Result);
                                arg._TestGroup.AddEvent(LogImportance.Medium, this.Name, msg.ToString());
                                state = UPnPTestStates.Warn;
                            }
                        }
                    }
                    else
                    {
                        throw new TerminateEarly("\"" + this.Name + "\" is terminating early because " + input.PrintBrowseParams() + " returned with an error or had problems with the DIDL-Lite.");
                    }
                }
                catch (TerminateEarly te)
                {
                    arg._TestGroup.AddEvent(LogImportance.Critical, this.Name, "\"" + this.Name + "\" terminating early. Reason ==> " + te.Message);
                    state = UPnPTestStates.Failed;
                    break;
                }
            }


            // finish up logging
            this._TestState = state;

            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("\"{0}\" completed", this.Name);

            if (this._TestState <= UPnPTestStates.Running)
            {
                throw new TestException("\"" + this.Name + "\" must have a pass/warn/fail result.", this._TestState);
            }

            switch (this._TestState)
            {
            case UPnPTestStates.Pass:
                sb.Append(" successfully.");
                break;

            case UPnPTestStates.Warn:
                sb.Append(" with warnings.");
                break;

            case UPnPTestStates.Failed:
                sb.Append(" with a failed result.");
                break;
            }

            arg._TestGroup.AddResult(sb.ToString());

            if (this._TestState <= UPnPTestStates.Warn)
            {
                if (_Details.TotalBrowseRequests != _Details.ExpectedTotalBrowseRequests)
                {
                    throw new TestException("TotalBrowseRequests=" + _Details.TotalBrowseRequests.ToString() + " ExpectedTotal=" + _Details.ExpectedTotalBrowseRequests.ToString(), _Details);
                }
            }

            arg._TestGroup.AddEvent(LogImportance.Remark, this.Name, this.Name + " finished.");

            return(this._TestState);
        }
        public override UPnPTestStates Run(ICollection otherSubTests, CdsSubTestArgument arg)
        {
            CpContentDirectory CDS = this.GetCDS(arg._Device);
            _Details = new CdsResult_BrowseFilter();
            this._TestState = UPnPTestStates.Running;
            arg._TestGroup.AddEvent(LogImportance.Remark, this.Name, "\"" + this.Name + "\" started.");

            // get the results from the BrowseAll test
            CdsResult_BrowseAll PRE = null;
            try
            {
                foreach (ISubTest preTest in otherSubTests)
                {
                    if (preTest.Name == this.PRE_BROWSEALL.Name)
                    {
                        PRE = preTest.Details as CdsResult_BrowseAll;
                        break;
                    }
                }

                if (PRE == null)
                {
                    throw new TestException(this._Name + " requires that the \"" + this.PRE_BROWSEALL.Name + "\" test be run as a prerequisite. The results from that test cannot be obtained.", otherSubTests);
                }
            }
            catch (Exception e)
            {
                throw new TestException(this._Name + " requires that the \"" + this.PRE_BROWSEALL.Name + "\" test be run before. An error occurred when attempting to obtain the results of a prerequisite.", otherSubTests, e);
            }
            _Details.BrowseAllResults = PRE;

            if (PRE.MostMetadata == null)
            {
                throw new TestException(this.PRE_BROWSEALL.Name + " failed to find a media object with the most metadata. " + this._Name + " requires this value.", PRE);
            }

            if (PRE.MostMetadata.Properties.Count != PRE.MostMetadata.Properties.PropertyNames.Count)
            {
                throw new TestException(this.Name + " has conflicting reports for the number of metadata properties. " + PRE.MostMetadata.Properties.Count + "/" + PRE.MostMetadata.Properties.PropertyNames.Count, PRE.MostMetadata.Properties);
            }

            IUPnPMedia MM = PRE.MostMetadata;

            if (MM == null)
            {
                string skippedMsg = "\"" + this.Name + "\" skipped because the tested content hierarchy does not have a media object with at least one resource and has an ID!=\"0\"";
                arg.TestGroup.AddEvent(LogImportance.Critical, this.Name, skippedMsg);
                arg.TestGroup.AddResult(skippedMsg);
                return UPnPTestStates.Ready;
            }

            IMediaContainer MC = PRE.MostMetadata.Parent;

            if (MC == null)
            {
                throw new TestException(this.Name + " has MostMetadata.Parent == null", PRE.MostMetadata);
            }

            int numProps = MM.Properties.Count;
            int numChildren = MC.ChildCount;

            // we browse for "res" and each possible res@attribute
            numProps += (2 * MediaResource.GetPossibleAttributes().Count) + 2;

            _Details.NumberOfProperties = numProps;
            _Details.ExpectedTotalBrowseRequests = 0;
            int inc = numProps / 4;
            if (inc == 0)
            {
                inc = 1;
            }
            for (int nProps = 0; nProps < numProps; nProps++)
            {
                for (int iProp = 0; iProp < numProps; iProp += inc)
                {
                    _Details.ExpectedTotalBrowseRequests++;
                }
            }
            _Details.ExpectedTotalBrowseRequests *= 2;
            int maxTime = 90 * _Details.ExpectedTotalBrowseRequests;
            this._ExpectedTestingTime = maxTime;

            // browse metadata with various filter settings
            // browsedirectchildren with various filter settings

            UPnPTestStates state = this._TestState;

            CdsBrowseSearchResults test1 = TestFiltersBrowseMetadata(MM, CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEMETADATA, this, arg, CDS, _Details);

            if (test1.WorstError > state)
            {
                state = test1.WorstError;
            }

            CdsBrowseSearchResults test2 = TestFiltersBrowseMetadata(MM, CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEDIRECTCHILDREN, this, arg, CDS, _Details);

            if (test2.WorstError > state)
            {
                state = test2.WorstError;
            }

            // finish up logging
            this._TestState = state;

            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("\"{0}\" completed", this.Name);

            if (this._TestState <= UPnPTestStates.Running)
            {
                throw new TestException("\"" + this.Name + "\" must have a pass/warn/fail result.", this._TestState);
            }

            switch (this._TestState)
            {
                case UPnPTestStates.Pass:
                    sb.Append(" successfully.");
                    break;
                case UPnPTestStates.Warn:
                    sb.Append(" with warnings.");
                    break;
                case UPnPTestStates.Failed:
                    sb.Append(" with a failed result.");
                    break;
            }

            arg._TestGroup.AddResult(sb.ToString());

            if (this._TestState <= UPnPTestStates.Warn)
            {
                if (_Details.TotalBrowseRequests != _Details.ExpectedTotalBrowseRequests)
                {
                    throw new TestException("TotalBrowseRequests=" + _Details.TotalBrowseRequests.ToString() + " ExpectedTotal=" + _Details.ExpectedTotalBrowseRequests.ToString(), _Details);
                }
            }

            arg._TestGroup.AddEvent(LogImportance.Remark, this.Name, sb.ToString());

            return this._TestState;
        }
        /// <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;
        }