简体   繁体   中英

How can I handle infinite for loops in a recursive function with Stacks?

So I'm in the process of creating a BaseIFrame Class to generically parse nested iFrames from any page without utilizing IDs, xpaths or anything DOM specific. I used recursion to catch all iFrames on a page and stacks to collect them. However, after debugging I discovered my recursive function loops over the same web element over and over before I hit a stack overflow.

How can I rectify this?

BaseIFrame class:

namespace [Confidental namespace] {
 public class BaseIFrame {
         protected IWebDriver _driver;

         private ReadOnlyCollection<IWebElement> _iframes;

 public BaseIFrame(IWebDriver _driver) { 
        this._driver = _driver;     
         
        _iframes = _driver.FindElements(By.TagName("frame"));             
        _iframesSize = _iframes.Count();             
     }

 public ReadOnlyCollection<IWebElement> getFrames() {
            return _iframes;
        }         

private static IEnumerable GetAllFramesRecursive<T>(IWebElement frame, IWebDriver driver)
        {
            var result = new Stack<IWebElement>();
            BaseIFrame baseClass = new BaseIFrame(driver);

            var iFrameList = baseClass._iframes;
            foreach (var i in iFrameList)
            {
                result.Push(i);

                foreach (IWebElement e in GetAllFramesRecursive<IWebElement>(i, driver))
                {
                    result.Push(e);
                }
            }

            return result;
        }

        // Public callable method for unit test
        public void GetAlliFramesRecursivePublic<T>(IWebElement test, IWebDriver driver)
        {
            GetAllFramesRecursive<IWebElement>(test, driver);
        }
    }
}

BaseIFrameTest class:

        [TestMethod]
        public void AssertiFrameLength()
        {
            Login();

            var expected = 0;

            BaseIFrame bif = new BaseIFrame(_driver);
            bif.GetAlliFramesRecursivePublic<IWebElement>(bif.getFrames()[0], _driver);

            var elementSize = _driver.FindElements(By.TagName("*")).Count;

            for (var i = 0; i < elementSize; i++)
            {
                var iframesSize = _driver.FindElements(By.TagName("frame")).Count;
                expected += iframesSize;
            }

            var actual = bif.getFrames();

            Assert.AreEqual(expected, actual.Count);
        }

A basic depth first iterator of a tree would look something like this:

        var stack = new Stack<T>();
        stack.Push(rootItem);
        while (stack.Count > 0)
        {
            var current = stack.Pop();
            // Process the current item
            foreach (var child in current.ChildItems)
            {
                stack.Push(child);
            }
        }

If there is a risk that a node occurs multiple times (ie it is a graph rather than a tree) you need some way to avoid repeated visits, for example using a hashSet:

        var stack = new Stack<T>();
        stack.Push(rootItem);
        var visited = new HashSet<T>();
        while (stack.Count > 0)
        {
            var current = stack.Pop();
            if (visited.Add(current))
            {
                // Process the current item
                foreach (var child in current.ChildItems)
                {
                    stack.Push(child);
                }
            }
        }

Using iteration instead of recursion both avoids problems with stackOverflows, and you avoid the need to create a new collection for each node in the tree. In my examples you can simply do a yield return current as the process step to produce an iterator that is lazily evaluated. Replace current.ChildItems with whatever you need to generate the edges/children for the current node.

One thing that looks odd in your example is that I cannot see where the frame parameter is used. Only the driver seem to be used to generate the list of iFrames, but the driver seem to be the same in every invocation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM