/// <summary>
        ///
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="cds"></param>
        /// <param name="test"></param>
        /// <param name="arg"></param>
        /// <param name="details"></param>
        /// <param name="C"></param>
        /// <returns></returns>
        /// <exception cref="TerminateEarly">
        /// </exception>
        public static ArrayList GetContainerChildrenAndValidate(IMediaContainer parent, CpContentDirectory cds, CdsSubTest test, CdsSubTestArgument arg, CdsResult_BrowseAll details, Queue C)
        {
            uint totalExpected = uint.MaxValue;
            uint currentChild  = 0;

            ArrayList children = new ArrayList();

            while (currentChild < totalExpected)
            {
                BrowseInput input = new BrowseInput();
                input.ObjectID       = parent.ID;
                input.BrowseFlag     = CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEDIRECTCHILDREN;
                input.Filter         = "*";
                input.StartingIndex  = currentChild;
                input.RequestedCount = 1;
                input.SortCriteria   = "";

                string containerID = parent.ID;

                if (currentChild == 0)
                {
                    test.SetExpectedTestingTime((++details.ExpectedTotalBrowseRequests) * 30);
                    arg.ActiveTests.UpdateTimeAndProgress(details.TotalBrowseRequests * 30);
                }

                CdsBrowseSearchResults results = Browse(input, test, arg, cds, details);

                test.SetExpectedTestingTime((details.ExpectedTotalBrowseRequests) * 30);
                arg.ActiveTests.UpdateTimeAndProgress((details.TotalBrowseRequests) * 30);

                if (results.WorstError >= UPnPTestStates.Failed)
                {
                    throw new TerminateEarly("\"" + test.Name + "\" is terminating early because " + input.PrintBrowseParams() + " returned with an error or had problems with the DIDL-Lite.");
                }
                else
                {
                    if (results.NumberReturned != 1)
                    {
                        if (currentChild != 0)
                        {
                            results.SetError(UPnPTestStates.Failed);
                            arg._TestGroup.AddEvent(LogImportance.Low, test.Name, "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned NumberReturned=" + results.NumberReturned + " when it should logically be 1.");
                        }
                    }

                    if (results.TotalMatches != totalExpected)
                    {
                        if (currentChild != 0)
                        {
                            results.SetError(UPnPTestStates.Failed);
                            arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned TotalMatches=" + results.TotalMatches + " when it should logically be " + totalExpected + " as reported in an earlier Browse request. This portion of the test requires that a MediaServer device not be in a state where its content hierarchy will change.");
                        }
                        else
                        {
                            totalExpected = results.TotalMatches;
                            if (totalExpected > 0)
                            {
                                details.ExpectedTotalBrowseRequests += ((int)results.TotalMatches * 2) - 1;
                                test.SetExpectedTestingTime((details.ExpectedTotalBrowseRequests) * 30);
                                arg.ActiveTests.UpdateTimeAndProgress(details.TotalBrowseRequests * 30);
                            }
                        }
                    }

                    if (results.MediaObjects != null)
                    {
                        if (results.MediaObjects.Count == 1)
                        {
                            IUPnPMedia child = results.MediaObjects[0] as IUPnPMedia;

                            if (child == null)
                            {
                                throw new TestException("\"" + test.Name + "\"" + " has a TEST LOGIC ERROR. Browse returned without errors but the child object's metadata is not stored in an IUPnPMedia object. The offending type is " + results.MediaObjects[0].GetType().ToString(), results.MediaObjects[0]);
                            }

                            // ensure no duplicates in object ID
                            foreach (IUPnPMedia previousChild in details.AllObjects)
                            {
                                if (previousChild.ID == child.ID)
                                {
                                    string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned an object with ID=\"" + child.ID + "\" which conflicts with a previously seen media object in ParentContainerID=\"" + previousChild.ParentID + "\".";
                                    arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg);
                                    throw new TerminateEarly(msg);
                                }
                            }

                            // ensure updateID is the same between BrowseDirectChildren and earlier BrowseMetadata.

                            try
                            {
                                uint previousUpdateID = (uint)parent.Tag;
                                if (results.UpdateID != previousUpdateID)
                                {
                                    string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned an UpdateID=" + results.UpdateID + " whilst an UpdateID=" + previousUpdateID + " was obtained in a previous call for ContainerID=\"" + parent.ID + "\" with BrowseMetadata.";
                                    arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg);
                                    throw new TerminateEarly(msg);
                                }
                            }
                            catch (TerminateEarly te)
                            {
                                throw te;
                            }
                            catch (Exception e)
                            {
                                throw new TestException(test.Name + " has a TEST LOGIC ERROR. Error comparing UpdateID values", parent, e);
                            }

                            // add the child to lists: C, parent's child list, and Allobjects
                            try
                            {
                                parent.AddObject(child, false);
                            }
                            catch (Exception e)
                            {
                                AddObjectError aoe = new AddObjectError();
                                aoe.Parent = parent;
                                aoe.Child  = child;
                                throw new TestException(test.Name + " has a TEST LOGIC ERROR. Browse returned without errors but the child object could not be added to its parent.", aoe, e);
                            }

                            details.AllObjects.Add(child);
                            children.Add(child);
                            if (child.IsContainer)
                            {
                                C.Enqueue(child);
                                details.TotalContainers++;
                            }
                            else
                            {
                                details.TotalItems++;
                            }

                            //
                            // Do a BrowseMetadata and check to see if the XML values are the same.
                            //

                            CdsBrowseSearchResults compareResults = CheckMetadata(child, cds, test, arg, details);

                            if (compareResults.InvokeError != null)
                            {
                                arg._TestGroup.AddEvent(LogImportance.High, test.Name, test.Name + ": Browse(BrowseDirectChildren,StartingIndex=" + currentChild + ",RequestedCount=0) on ContainerID=[" + containerID + "] succeeded with warnings because a BrowseMetadata request was rejected by the CDS.");
                            }
                            else if (compareResults.ResultErrors.Count > 0)
                            {
                                string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " failed because a BrowseMetadata request succeeded but the DIDL-Lite could not be represented in object form. Invalid DIDL-Lite is most likely the cause.";
                                arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg);
                                throw new TerminateEarly(msg);
                            }
                            else if (compareResults.WorstError >= UPnPTestStates.Failed)
                            {
                                string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " failed because one or more child object's failed a comparison of results between BrowseDirectChildren and BrowseMetadata or encountered some other critical error in that process.";
                                arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg);
                                throw new TerminateEarly(msg);
                            }
                            else
                            {
                                //string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " succeeded.";
                                //arg._TestGroup.AddEvent(LogImportance.Remark, test.Name, msg);
                            }

                            //
                            // Track the metadata properties found so far
                            // as we may use them in Browse SortCriteria
                            //

                            // standard top-level attributes
                            Tags T = Tags.GetInstance();
                            AddTo(details.PropertyNames, "@" + T[_ATTRIB.id]);
                            AddTo(details.PropertyNames, "@" + T[_ATTRIB.parentID]);
                            AddTo(details.PropertyNames, "@" + T[_ATTRIB.restricted]);
                            if (child.IsContainer)
                            {
                                if (child.IsSearchable)
                                {
                                    AddTo(details.PropertyNames, "@" + T[_ATTRIB.searchable]);
                                    AddTo(details.PropertyNames, T[_DIDL.Container] + "@" + T[_ATTRIB.searchable]);
                                }

                                AddTo(details.PropertyNames, T[_DIDL.Container] + "@" + T[_ATTRIB.id]);
                                AddTo(details.PropertyNames, T[_DIDL.Container] + "@" + T[_ATTRIB.parentID]);
                                AddTo(details.PropertyNames, T[_DIDL.Container] + "@" + T[_ATTRIB.restricted]);
                            }
                            else if (child.IsItem)
                            {
                                if (child.IsReference)
                                {
                                    AddTo(details.PropertyNames, "@" + T[_ATTRIB.refID]);
                                    AddTo(details.PropertyNames, T[_DIDL.Item] + "@" + T[_ATTRIB.refID]);
                                }

                                AddTo(details.PropertyNames, T[_DIDL.Item] + "@" + T[_ATTRIB.id]);
                                AddTo(details.PropertyNames, T[_DIDL.Item] + "@" + T[_ATTRIB.parentID]);
                                AddTo(details.PropertyNames, T[_DIDL.Item] + "@" + T[_ATTRIB.restricted]);
                            }

                            // standard metadata
                            IMediaProperties properties    = child.MergedProperties;
                            IList            propertyNames = properties.PropertyNames;
                            foreach (string propertyName in propertyNames)
                            {
                                if (details.PropertyNames.Contains(propertyName) == false)
                                {
                                    details.PropertyNames.Add(propertyName);

                                    // add attributes if they are not added
                                    IList propertyValues = properties[propertyName];
                                    foreach (ICdsElement val in propertyValues)
                                    {
                                        ICollection attributes = val.ValidAttributes;
                                        foreach (string attribName in attributes)
                                        {
                                            StringBuilder sbpn = new StringBuilder();
                                            sbpn.AppendFormat("{0}@{1}", propertyName, attribName);
                                            string fullAttribName = sbpn.ToString();
                                            AddTo(details.PropertyNames, fullAttribName);
                                        }
                                    }
                                }

                                // resources
                                IList resources = child.MergedResources;
                                foreach (IMediaResource res in resources)
                                {
                                    ICollection attributes = res.ValidAttributes;
                                    foreach (string attribName in attributes)
                                    {
                                        string name1 = "res@" + attribName;
                                        string name2 = "@" + attribName;

                                        AddTo(details.PropertyNames, name1);
                                        AddTo(details.PropertyNames, name2);
                                    }
                                }

                                if (resources.Count > 0)
                                {
                                    AddTo(details.PropertyNames, T[_DIDL.Res]);
                                }
                            }
                        }
                        else
                        {
                            if (results.TotalMatches > 0)
                            {
                                results.SetError(UPnPTestStates.Failed);
                                string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " did not yield exactly one CDS-compliant media object in its result. Instantiated a total of " + results.MediaObjects.Count + " media objects.";
                                arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg);
                                throw new TerminateEarly(msg);
                            }
                        }
                    }
                    else
                    {
                        throw new TestException(test.Name + " has a TEST LOGIC ERROR. Browse returned without errors but no media objects were instantiated.", null);
                    }
                }

                currentChild++;
            }

            return(children);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="cds"></param>
        /// <param name="test"></param>
        /// <param name="arg"></param>
        /// <param name="details"></param>
        /// <param name="C"></param>
        /// <returns></returns>
        /// <exception cref="TerminateEarly">
        /// </exception>
        public static ArrayList GetContainerChildrenAndValidate(IMediaContainer parent, CpContentDirectory cds, CdsSubTest test, CdsSubTestArgument arg, CdsResult_BrowseAll details, Queue C)
        {
            uint totalExpected = uint.MaxValue;
            uint currentChild = 0;

            ArrayList children = new ArrayList();

            while (currentChild < totalExpected)
            {
                BrowseInput input = new BrowseInput();
                input.ObjectID = parent.ID;
                input.BrowseFlag = CpContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEDIRECTCHILDREN;
                input.Filter = "*";
                input.StartingIndex = currentChild;
                input.RequestedCount = 1;
                input.SortCriteria = "";

                string containerID = parent.ID;

                if (currentChild == 0)
                {
                    test.SetExpectedTestingTime ((++details.ExpectedTotalBrowseRequests) * 30);
                    arg.ActiveTests.UpdateTimeAndProgress( details.TotalBrowseRequests * 30);
                }

                CdsBrowseSearchResults results = Browse(input, test, arg, cds, details);

                test.SetExpectedTestingTime ((details.ExpectedTotalBrowseRequests) * 30);
                arg.ActiveTests.UpdateTimeAndProgress( (details.TotalBrowseRequests) * 30);

                if (results.WorstError >= UPnPTestStates.Failed)
                {
                    throw new TerminateEarly("\"" + test.Name + "\" is terminating early because " +input.PrintBrowseParams()+ " returned with an error or had problems with the DIDL-Lite.");
                }
                else
                {
                    if (results.NumberReturned != 1)
                    {
                        if (currentChild != 0)
                        {
                            results.SetError(UPnPTestStates.Failed);
                            arg._TestGroup.AddEvent(LogImportance.Low, test.Name, "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned NumberReturned=" +results.NumberReturned+ " when it should logically be 1.");
                        }
                    }

                    if (results.TotalMatches != totalExpected)
                    {
                        if (currentChild != 0)
                        {
                            results.SetError(UPnPTestStates.Failed);
                            arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned TotalMatches=" +results.TotalMatches+ " when it should logically be " +totalExpected+ " as reported in an earlier Browse request. This portion of the test requires that a MediaServer device not be in a state where its content hierarchy will change.");
                        }
                        else
                        {
                            totalExpected = results.TotalMatches;
                            if (totalExpected > 0)
                            {
                                details.ExpectedTotalBrowseRequests += ((int)results.TotalMatches * 2) - 1;
                                test.SetExpectedTestingTime ((details.ExpectedTotalBrowseRequests) * 30);
                                arg.ActiveTests.UpdateTimeAndProgress( details.TotalBrowseRequests * 30);
                            }
                        }
                    }

                    if (results.MediaObjects != null)
                    {
                        if (results.MediaObjects.Count == 1)
                        {
                            IUPnPMedia child = results.MediaObjects[0] as IUPnPMedia;

                            if (child == null)
                            {
                                throw new TestException("\"" + test.Name + "\"" + " has a TEST LOGIC ERROR. Browse returned without errors but the child object's metadata is not stored in an IUPnPMedia object. The offending type is " + results.MediaObjects[0].GetType().ToString(), results.MediaObjects[0]);
                            }

                            // ensure no duplicates in object ID
                            foreach (IUPnPMedia previousChild in details.AllObjects)
                            {
                                if (previousChild.ID == child.ID)
                                {
                                    string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned an object with ID=\"" +child.ID+ "\" which conflicts with a previously seen media object in ParentContainerID=\"" +previousChild.ParentID+ "\".";
                                    arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg);
                                    throw new TerminateEarly(msg);
                                }
                            }

                            // ensure updateID is the same between BrowseDirectChildren and earlier BrowseMetadata.

                            try
                            {
                                uint previousUpdateID = (uint) parent.Tag;
                                if (results.UpdateID != previousUpdateID)
                                {
                                    string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " returned an UpdateID=" +results.UpdateID+ " whilst an UpdateID=" +previousUpdateID+ " was obtained in a previous call for ContainerID=\"" +parent.ID+ "\" with BrowseMetadata.";
                                    arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg);
                                    throw new TerminateEarly(msg);
                                }
                            }
                            catch (TerminateEarly te)
                            {
                                throw te;
                            }
                            catch (Exception e)
                            {
                                throw new TestException(test.Name + " has a TEST LOGIC ERROR. Error comparing UpdateID values", parent, e);
                            }

                            // add the child to lists: C, parent's child list, and Allobjects
                            try
                            {
                                parent.AddObject(child, false);
                            }
                            catch (Exception e)
                            {
                                AddObjectError aoe = new AddObjectError();
                                aoe.Parent = parent;
                                aoe.Child = child;
                                throw new TestException(test.Name + " has a TEST LOGIC ERROR. Browse returned without errors but the child object could not be added to its parent.", aoe, e);
                            }

                            details.AllObjects.Add(child);
                            children.Add(child);
                            if (child.IsContainer)
                            {
                                C.Enqueue(child);
                                details.TotalContainers++;
                            }
                            else
                            {
                                details.TotalItems++;
                            }

                            //
                            // Do a BrowseMetadata and check to see if the XML values are the same.
                            //

                            CdsBrowseSearchResults compareResults = CheckMetadata(child, cds, test, arg, details);

                            if (compareResults.InvokeError != null)
                            {
                                arg._TestGroup.AddEvent(LogImportance.High, test.Name, test.Name + ": Browse(BrowseDirectChildren,StartingIndex="+currentChild+",RequestedCount=0) on ContainerID=["+containerID+"] succeeded with warnings because a BrowseMetadata request was rejected by the CDS.");
                            }
                            else if (compareResults.ResultErrors.Count > 0)
                            {
                                string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " failed because a BrowseMetadata request succeeded but the DIDL-Lite could not be represented in object form. Invalid DIDL-Lite is most likely the cause.";
                                arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg);
                                throw new TerminateEarly(msg);
                            }
                            else if (compareResults.WorstError >= UPnPTestStates.Failed)
                            {
                                string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " failed because one or more child object's failed a comparison of results between BrowseDirectChildren and BrowseMetadata or encountered some other critical error in that process.";
                                arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg);
                                throw new TerminateEarly(msg);
                            }
                            else
                            {
                                //string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " succeeded.";
                                //arg._TestGroup.AddEvent(LogImportance.Remark, test.Name, msg);
                            }

                            //
                            // Track the metadata properties found so far
                            // as we may use them in Browse SortCriteria
                            //

                            // standard top-level attributes
                            Tags T = Tags.GetInstance();
                            AddTo(details.PropertyNames, "@"+T[_ATTRIB.id]);
                            AddTo(details.PropertyNames, "@"+T[_ATTRIB.parentID]);
                            AddTo(details.PropertyNames, "@"+T[_ATTRIB.restricted]);
                            if (child.IsContainer)
                            {
                                if (child.IsSearchable)
                                {
                                    AddTo(details.PropertyNames, "@"+T[_ATTRIB.searchable]);
                                    AddTo(details.PropertyNames, T[_DIDL.Container]+"@"+T[_ATTRIB.searchable]);
                                }

                                AddTo(details.PropertyNames, T[_DIDL.Container]+"@"+T[_ATTRIB.id]);
                                AddTo(details.PropertyNames, T[_DIDL.Container]+"@"+T[_ATTRIB.parentID]);
                                AddTo(details.PropertyNames, T[_DIDL.Container]+"@"+T[_ATTRIB.restricted]);
                            }
                            else if (child.IsItem)
                            {
                                if (child.IsReference)
                                {
                                    AddTo(details.PropertyNames, "@"+T[_ATTRIB.refID]);
                                    AddTo(details.PropertyNames, T[_DIDL.Item]+"@"+T[_ATTRIB.refID]);
                                }

                                AddTo(details.PropertyNames, T[_DIDL.Item]+"@"+T[_ATTRIB.id]);
                                AddTo(details.PropertyNames, T[_DIDL.Item]+"@"+T[_ATTRIB.parentID]);
                                AddTo(details.PropertyNames, T[_DIDL.Item]+"@"+T[_ATTRIB.restricted]);
                            }

                            // standard metadata
                            IMediaProperties properties = child.MergedProperties;
                            IList propertyNames = properties.PropertyNames;
                            foreach (string propertyName in propertyNames)
                            {
                                if (details.PropertyNames.Contains(propertyName) == false)
                                {
                                    details.PropertyNames.Add(propertyName);

                                    // add attributes if they are not added
                                    IList propertyValues = properties[propertyName];
                                    foreach (ICdsElement val in propertyValues)
                                    {
                                        ICollection attributes = val.ValidAttributes;
                                        foreach (string attribName in attributes)
                                        {
                                            StringBuilder sbpn = new StringBuilder();
                                            sbpn.AppendFormat("{0}@{1}", propertyName, attribName);
                                            string fullAttribName = sbpn.ToString();
                                            AddTo(details.PropertyNames, fullAttribName);
                                        }
                                    }
                                }

                                // resources
                                IList resources = child.MergedResources;
                                foreach (IMediaResource res in resources)
                                {
                                    ICollection attributes = res.ValidAttributes;
                                    foreach (string attribName in attributes)
                                    {
                                        string name1 = "res@"+attribName;
                                        string name2 = "@"+attribName;

                                        AddTo(details.PropertyNames, name1);
                                        AddTo(details.PropertyNames, name2);
                                    }
                                }

                                if (resources.Count > 0)
                                {
                                    AddTo(details.PropertyNames, T[_DIDL.Res]);
                                }
                            }
                        }
                        else
                        {
                            if (results.TotalMatches > 0)
                            {
                                results.SetError(UPnPTestStates.Failed);
                                string msg = "\"" + test.Name + "\": " + input.PrintBrowseParams() + " did not yield exactly one CDS-compliant media object in its result. Instantiated a total of " +results.MediaObjects.Count+ " media objects.";
                                arg._TestGroup.AddEvent(LogImportance.Critical, test.Name, msg);
                                throw new TerminateEarly(msg);
                            }
                        }
                    }
                    else
                    {
                        throw new TestException(test.Name + " has a TEST LOGIC ERROR. Browse returned without errors but no media objects were instantiated.", null);
                    }
                }

                currentChild++;
            }

            return children;
        }