1
\$\begingroup\$

I'm implementing things in Robot Framework that it does not support by default, including nestable For Loops and, now, While Loops. I'm looking for readability, algorithm, methodology, and/or performance suggestions.

The While Loop is not as advanced as the For loop is yet. I'm eventually looking to implement nesting the While Loop and breaking it on a command, but at present it will take any Robot Framework variable (assuming it's an integer) and loop until it reaches the specified breaking point. The custom keyword Increment plays the part of a line of internal code incrementing the value by a specified number. At present, Increment only supports + and - with any integer, but I can easily make it support * and / for more advanced increments.

The following code works, but as (still) a relative novice in Python I don't know all the tricks that could be used to optimize this loop structure and logic. The general idea, for those who have not read my other two RobotNest posts, is to allow the user to write nestable For and While loops in Robot Framework.

My basic methodology is as follows:

  • The user writes the Loop as a single keyword with a short list of required arguments followed by whatever keywords they want, using ... at the beginning of the line followed by \\, once for every inner loop.
  • The program loops through the list of keywords and re-formats the Loop into a format usable by Robot Framework's run_keywords method located under the BuiltIn library, swapping \\ instances for AND.
  • The program then runs the keywords through a custom run_keywords method (mirrors BuiltIn().run_keywords) so that I can control what keywords are executed when and break the loop if I need to. This method loops through the list of keywords and runs them based on their arguments individually.
  • Within the main loop, the program dynamically writes the index variable (differs in execution between For and While variants) to all keyword arguments that call it.

This methodology seems very inefficient to me. By the end of the program, I've looped through my list of keywords once statically and around 4 or 5 for every iteration of the loop itself. This is why I'm looking for algorithm suggestions.

In addition, I'd like to get away from using \\ to delineate lines, but unless I figure out how the compiler takes Robot Framework's code and says "this is what it equates to in Python", that probably won't be happening.

Here's my code as it currently stands:

from robot.libraries.BuiltIn import BuiltIn

# TODO: Create new types of For Loops
# TODO: Expand While Loop
# TODO: Create Do-While Loops


class Loops(object):

    def __init__(self):
        self.selenium_lib = BuiltIn().get_library_instance('ExtendedSelenium2Library')
        self.internal_variables = {}

    def for_loop(self, loop_type, start, end, index_var, *keywords):
        #   Format the keywords
        keywords = self._format_loop(*keywords)
        #   Clean out the internal variables from previous iterations
        self.internal_variables = {}
        #   If I'm given a range of numbers...
        if loop_type == 'IN RANGE':
            #   This is the actual looping part
            for loop_iteration in range(int(start), int(end)):
                keyword_set = self._index_var_swap(loop_iteration, index_var, *keywords)
                #   If it's a one-keyword list with no arguments, then I can use the fastest possible keyword to run it
                if len(keyword_set) == 1:
                    BuiltIn().run_keyword(keyword_set)
                #   If it's a one-keyword list with arguments, then I can use a faster keyword to run it
                elif 'AND' not in keyword_set:
                    #   If the keyword isn't Exit If, then I can just run it normally.
                    if keyword_set[0].lower() != 'exit if':
                        BuiltIn().run_keyword(*keyword_set)
                    #   If the keyword is Exit If, then I need to evaluate the keyword differently.
                    elif BuiltIn().run_keyword(*keyword_set):
                        break
                #   If it's a multiple-keyword list, then I have to use Run Keywords
                else:
                    temp = self._run_keywords(*keyword_set)
                    if not temp:
                        break

    def while_loop(self, comparison, *keywords):
        #   Format the input
        comparison = str(comparison).replace(" ", "")
        #   Clean out the internal variables from previous iterations
        self.internal_variables = {}
        #   Create an internal variable to deal with a changing index
        self._assign_internal_variable("while_loop_var", comparison[:Loops()._find_bool(comparison)])
        comp = comparison
        while Loops()._evaluate_boolean_string(comp):
            #   Format the keywords
            k_words = self._format_loop(*keywords)
            self._while_var_swap(*k_words)
            self._run_keywords(*k_words)
            comp = str(self.internal_variables["while_loop_var"]) + str(comparison[Loops()._find_bool(comparison):])

    def exit_if(self, condition):
        return self._evaluate_boolean_string(condition)

    def increment(self, inc):
        operator = inc[0]
        if self._is_number(inc[1:]):
            if operator == '+':
                self.internal_variables["while_loop_var"] =\
                    int(self.internal_variables["while_loop_var"]) + int(inc[1:])
            elif operator == '-':
                self.internal_variables["while_loop_var"] =\
                    int(self.internal_variables["while_loop_var"]) - int(inc[1:])
        return False

    def _format_loop(self, *keywords):
        keywords = list(keywords)   # I need to format the keywords as a list.
        changed = False             # Whether or not I changed anything in the previous iteration.
        index = 0                   # The item index I'm at in the list of keywords
        del_list = []               # The list of items I need to delete
        swap_list = []              # The list of items i need to swap to AND for the use of Run Keywords

        def _new_variable():
            #   Default to a variable declaration of 'name='
            t = 1
            #   If my variable declaration is 'name ='
            if x[-2:] == ' =':
                #   Reflect that in the value of t
                t = 2

            #   Count the number of cells until the end of the line
            length = self._deliminator_search(index, x, *keywords)

            if length == 3 and not BuiltIn().run_keyword_and_return_status("Keyword Should Exist", keywords[index + 1]):
                #   If I'm assigning a value to my variable
                self._assign_internal_variable(x[:-t], str(keywords[index + 1]))

            elif length == 3:
                #   If I'm assigning the result of a keyword without any arguments
                self._assign_internal_variable_to_keyword(keywords[index][:-t], str(keywords[index + 1]))
            else:
                #   If I'm assigning the result of a keyword with arguments
                self._assign_internal_variable_to_keyword(keywords[index][:-t], keywords[index + 1],
                                                          keywords[index + 2:index + (length - 1)])

            #   Add the variable declaration code to the delete list.
            del_list.extend(range(index - 1, index + length))

        #   For each argument
        for x in keywords:
            #   Format it to a string
            x = str(x)
            #   Assign new variables
            if x[-1:] == '=':
                _new_variable()

            #   If the previous element was not changed...
            if not changed:
                #   If the current item is not the last one on the list...
                if x != len(keywords) - 1:
                    #   If the current item is a deliminator...
                    if x == '\\':
                        #   If the next item is a deliminator, delete this item and set changed to True
                        if keywords[int(index) + 1] == '\\':
                            del_list.append(index)
                            changed = True
                        #   If the next item is not a deliminator...
                        else:
                            #   If this isn't the first deliminator on the list, swap it to an 'AND'
                            if index != 0:
                                swap_list.append(index)
                                changed = True
                            #   If this deliminator is in position index=0, just delete it
                            else:
                                del_list.append(index)
                                changed = True
                    #   If the current element is not a deliminator, then I don't need to touch anything.
                #   If the current element is the last one, then I don't need to touch anything
            #   If the previous element was changed, then I don't need to "change" this one...
            elif changed:
                changed = False
                #   ...but if it's a deliminator then I do need to set it up for the inner for loop it means.
                if keywords[index] == '\\':
                    keywords[index] = keywords[index]*2
            index = index + 1   # Advance the index

        # These actually do the swapping and deleting
        for thing in swap_list:
            keywords[thing] = 'AND'
        del_list.reverse()
        for item in del_list:
            del keywords[item]

        # I also need to activate my variables for this set of keywords to run.
        keywords = self._activate_variables(*keywords)

        return keywords

    def _assign_internal_variable(self, variable_name, assignment):
        # This keyword works like any other keyword so that it can be activated by BuiltIn.run_keywords
        self.internal_variables[variable_name] = assignment

    def _assign_internal_variable_to_keyword(self, variable_name, keyword, *arguments):
        # Uses assign_internal_variable to simplify code.
        self._assign_internal_variable(variable_name, BuiltIn().run_keyword(keyword, *arguments))

    def _activate_variables(self, *keywords):
        #   Initialize variables
        keywords = list(keywords)   # Cast keywords as a List
        index = 0                   # The index of the keyword I'm looking at

        #   For each keyword
        for keyword in keywords:
            keyword = str(keyword)  # Cast keyword as a String
            assignment = False      # Whether or not the found variable name is in a variable assignment
            for key in self.internal_variables.keys():
                key = str(key)      # Cast key as a String
                #   If I can find the key in the keyword and it's not an assignment...
                if keyword.find(key) > -1 and not assignment:
                    #   ...replace the text of the key in the keyword.
                    keywords[index] = keyword.replace(str(key), str(self.internal_variables[key]))
                #   If the keyword I'm looking at is an assignment...
                if keyword.lower() == 'assign internal variable'\
                        and keyword.lower() != 'assign internal variable to keyword':
                    #   ...then my next keyword is going to definitely be a known variable, so I don't want to touch it.
                    assignment = True
                #   If the keyword I'm looking at is not an assignment...
                else:
                    #   ...set assignment to False just in case the previous one happened to be an assignment.
                    assignment = False
            index = index + 1   # Advance the index
            #   NOTE: Replaces the EXACT text, even if it's in another keyword or variable, so be very careful
        return keywords     # Return the list of keywords to be used in the format loop

    def _while_var_swap(self, *keywords):
        #   Format the keywords as a list for iteration
        keywords = list(keywords)
        index = 0
        #   For every line in keywords
        for line in keywords:
            #   Replace all instances of the index_var in the string with the loop iteration as a string
            keywords[index] = str(line).replace('while_loop_var', str(self.internal_variables["while_loop_var"]))
            index = index + 1
        return keywords

    @staticmethod
    def _is_boolean_string(string_in):
        # For all of the possible Boolean parameters...
        for param in ['!', '<', '>', '=', '==']:
            # Return whether or not the parameter is in the string.
            if str(param) in str(string_in):
                return True
        return False

    @staticmethod
    def _evaluate_boolean_string(condition):
        def _eval(arg_1, arg_2):
            if (t[2][1] or t[2][2]) and not (t[2][1] and t[2][2]):  # If it has either > or < in it, but not both
                if t[2][3]:  # If it has = in it
                    if t[2][1]:  # If it's >=
                        return arg_1 >= arg_2
                    else:  # If it's <=
                        return arg_1 <= arg_2
                else:
                    if t[2][1]:  # If it's <
                        return arg_1 < arg_2
                    else:  # If it's >
                        return arg_1 > arg_2
            elif t[2][4]:  # If it's ==
                return arg_1 == arg_2
            elif t[2][0] and t[2][3]:  # If it's !=
                return arg_1 != arg_2
            else:  # In case of Tester
                return False

        # Cast the condition as a string with no whitespaces
        condition = str(condition).replace(" ", "")
        # Initialize the t-table with default values
        t = [[0, 1, 2, 3, 4],
             ['!', '<', '>', '=', '=='],
             [False, False, False, False, False]]
        # Find the start of the Boolean expression
        start, t = Loops()._find_bool(condition, t)
        # Evaluate the expression
        return _eval(condition[:start], condition[start + sum(t[2]):])

    @staticmethod
    def _find_bool(comparison, tin=None):
        if tin is None:
            t = [[0, 1, 2, 3, 4],
                 ['!', '<', '>', '=', '=='],
                 [False, False, False, False, False]]
        else:
            t = tin
        #   Initialize the starting index as the last index in condition
        begin = len(comparison)
        #   For all of the parameters...
        for index in t[0]:
            #   Find the location of the start of the boolean parameters
            temp = comparison.find(str(t[1][int(index)]))
            #   If the location exists and is less than start...
            if temp <= begin and temp != -1:
                #   Set start to the location
                begin = temp
                t[2][int(index)] = True
        #   If the input was bad, return -1 and don't change the t table
        if sum(t[2]) > 2 or sum(t[2]) == 0 or begin == len(comparison):
            if tin is None:
                return -1
            else:
                return -1, tin
        else:
            if tin is None:
                return begin
            else:
                return begin, t

    @staticmethod
    def _run_keywords(*keys):
        #   Find the end of the current keyword
        def _and_search(start, key='no key yet'):
            # I'm starting with a false key that is never 'AND'
            and_index = 1       # I never want to start on an 'AND'
            #   While the current key isn't 'AND' and I'm not at the end of the list of keywords...
            while key != 'AND' and and_index + start != len(key_list):
                #   Set the current key equal to the next key in the list.
                key = key_list[int(start) + and_index]
                and_index = and_index + 1   # Advance the index

            #   If the final key is an 'AND'...
            if key == 'AND':
                return and_index - 1    # Return the keyword length minus the 'AND'
            #   Otherwise...
            else:
                return and_index    # Return the keyword length

        def _split_keyword_list():
            first = True    # We always start at the first cell of a keyword/argument set.
            index = 0       # The item index I'm at in the list of keywords
            #   For each word in the list of keywords/arguments...
            for word in key_list:
                #   If it's the first word...
                if first:
                    #   Append the keyword
                    keywords.append(key_list[int(index):(index + _and_search(index))])
                    first = False   # Set first to False
                #   If it's any other word I don't need to append it, but...
                else:
                    #   If it's the last word in the keyword...
                    if _and_search(index, word) == 0:
                        #   The next keyword must be the first.
                        first = True
                index = index + 1  # Advance the index

        # I need to format the keywords as a list and instantiate the array of keywords as an empty array.
        key_list, keywords = list(keys), []

        #   Split list into keyword/arg sets
        _split_keyword_list()

        #   For each key/arg item in the list...
        for keyword in keywords:
            #   Run the keyword with its arguments
            if keyword[0].lower() != 'exit if':
                BuiltIn().run_keyword(keyword[0], *keyword[1:])
            elif Loops()._is_boolean_string(keyword[1]) and BuiltIn().run_keyword(keyword[0], *keyword[1:]):
                return False
        return True

    @staticmethod
    def _index_var_swap(loop_iteration, index_var, *keywords):
        #   Format the keywords as a list for iteration
        keywords = list(keywords)
        index = 0
        #   For every line in keywords
        for line in keywords:
            #   Replace all instances of the index_var in the string with the loop iteration as a string
            keywords[index] = str(line).replace(str(index_var), str(loop_iteration))
            index = index + 1
        return keywords

    @staticmethod
    def _var_swap(old_var, new_var, keywords):
        index = 0
        #   For every line in keywords
        for line in keywords:
            #   Replace the all instance of the oldVar in the string with newVar as a string
            keywords[index] = str(line).replace(str(old_var), str(new_var))
            index = index + 1
        return keywords

    @staticmethod
    def _deliminator_search(start, keyword, *keywords):
        index = 0
        while keyword != '\\' and keyword != '\\\\':
            keyword = keywords[int(start) + index]
            index = index + 1
        return index

    @staticmethod
    def _is_number(s):
        try:
            float(s)
            return True
        except ValueError:
            return False

And here's the working Robot Framework code that I use to test it:

*** Settings ***
Library     ExtendedSelenium2Library
Library     Loops.py

*** Variables ***
${blue_squadron} =      Blue
${gold_squadron} =      Gold
${green_squadron} =     Green
${red_squadron} =       Red
${test_index_1} =       0
${test_index_2} =       3

*** Test Cases ***
Test For Loop IN RANGE
    For Loop    IN RANGE    0   1   INDEX0
    ...     \\  For Loop    IN RANGE    1   6   INDEX1
    ...     \\  \\  {standing_by}=      standing by
    ...     \\  \\  Run Keyword If      INDEX1 == 1     Log to Console  This is ${red_squadron} Leader standing by
    ...     \\  \\  Run Keyword Unless  INDEX1 == 1     Log to Console  ${red_squadron} INDEX1 {standing_by}
    ...     \\  For Loop    IN RANGE    1   6   INDEX2
    ...     \\  \\  standing_by_2 =     standing by
    ...     \\  \\  Run Keyword If      INDEX2 == 1     Log to Console  This is ${gold_squadron} Leader standing by
    ...     \\  \\  Run Keyword Unless  INDEX2 == 1     Log to Console  ${gold_squadron} INDEX2 standing_by_2
    ...     \\  For Loop    IN RANGE    1   6   INDEX3
    ...     \\  \\  standing_by_3=      Get Blue Squadron
    ...     \\  \\  Run Keyword If      INDEX3 == 1     Log to Console  This is ${blue_squadron} Leader standing by
    ...     \\  \\  Run Keyword Unless  INDEX3 == 1     Log to Console  ${blue_squadron} INDEX3 standing_by_3
    ...     \\  For Loop    IN RANGE    1   6   INDEX4
    ...     \\  \\  standing_by_4 =     Get Green Squadron   null input
    ...     \\  \\  Run Keyword If      INDEX4 == 1     Log to Console  This is ${green_squadron} Leader standing by
    ...     \\  \\  Run Keyword Unless  INDEX4 == 1     Log to Console  ${green_squadron} INDEX4 standing_by_4

Test IN RANGE Edge Case 1 - Single Keyword with Single Argument
    For Loop    IN RANGE    0   1   INDEX0
    ...     \\  Log to Console  testlog

Test For Loop Exit
    For Loop    IN RANGE    0   3   INDEX0
    ...     \\  Log to Console  INDEX0
    ...     \\  Exit If     INDEX0 == 1

Test While Loop
    While Loop  ${test_index_1} < 3
    ...     \\  Log to Console  Index: while_loop_var
    ...     \\  Increment   +1
    While Loop  ${test_index_2} > 0
    ...     \\  Log to Console  Index: while_loop_var
    ...     \\  Increment   -1

*** Keywords ***
Get Blue Squadron
    [Return]    standing by

Get Green Squadron
    [Arguments]     ${text}
    [Return]        standing by

Language: Python 2.7.13, Robot Framework 3.0.2 (Python 2.7.13 on win32)

Dependencies: BuiltIn Library, ExtendedSelenium2Library

\$\endgroup\$
  • \$\begingroup\$ The two backslashes seems rather cumbersome. Have you considered some other character or characters? For example, : or :: in a cell by itself? From a human factors perspective, those are easier to type and read. Also, why are you using expressions like INDEX == 1 rather than ${INDEX} == 1? \$\endgroup\$ – Bryan Oakley Nov 10 '17 at 21:40
  • \$\begingroup\$ Originally, I was trying to stay as close to the original syntax of a Robot Framework For Loop as possible. Since the original uses \` and I was reading that as a cell into a language that interprets it as an escape character, I had to use two. Recently I've started looking into using the BuiltIn().keyword_should_exist method to find lines, but I'm running into issues with that so far. I'm using the Python-like expressions because Robot Framework-style variables are compiled to their numbers as they're being sent to Python to be run, while non-Robot Framework syntax can work in the loop. \$\endgroup\$ – Brandon Olson Nov 10 '17 at 21:53
  • \$\begingroup\$ @BryanOakley I attempted to use :: in a cell by itself, but for some reason Robot Framework or Python converts :: :: to ::::, which causes a lot more problems. \$\endgroup\$ – Brandon Olson Nov 14 '17 at 22:42
  • \$\begingroup\$ Neither python nor robot will just automatically convert one string to another unless you did something wrong. Did you have two or more spaces after :: before the next cell? \$\endgroup\$ – Bryan Oakley Nov 14 '17 at 22:45
  • \$\begingroup\$ Yes. The same thing happens with | and _. \$\endgroup\$ – Brandon Olson Nov 14 '17 at 22:48
0
\$\begingroup\$

After a significant amount of optimization, I got it... mostly. The main looping variable for the While Loop is still clunky (still working through BuiltIn's method of accessing Robot Framework-style variables), but it's about 1/6 as long as the original with the same functionality, and runs significantly faster with less overhead. I'm now working on nesting the While loops.

from robot.libraries.BuiltIn import BuiltIn


class Loops(object):
    def __init__(self):
        self.iv = {}

    def loop(self, loop_type, a, b, c, *keywords):
        def _find_bool(j):  # Used to find the next boolean operator
            return min([j.find(i) for i in ['!', '<', '>', '=', '=='] if (-1 < j.find(i) < len(j))])
        self.iv = {}  # Clean out internal variables
        if loop_type.upper() == 'FOR RANGE':    # A For loop over a range of numbers.
            keywords = self._format(*keywords)     # Format the keywords
            for loop_iteration in range(int(a), int(b)):  # The actual looping part
                proceed = self._run_keywords(*[k.replace(c, str(loop_iteration)) for k in keywords])
                if not proceed:     # If the loop decides to break,
                    break           # break the loop.
        elif loop_type.upper() == 'WHILE':      # A While loop with a condition.
            a = str(a).replace(" ", "")         # Format the condition with no spaces.
            self.iv["{{wlv}}"] = a[:_find_bool(a)]   # Create the wlv as an internal variable.
            keywords = self._format(*([b, c] + list(keywords)))     # B and C inputs are actually part of the keywords.
            while BuiltIn().evaluate(a):        # The actual looping part.
                self._run_keywords(*keywords)   # Run the keywords with Loops's Run Keywords to control it better.
                a = str(self.iv["{{wlv}}"]) + str(a[_find_bool(a):])     # Update the wlv

    def increment(self, inc):
        if inc[0] == '+':       # If I'm incrementing up, add one to the while loop var
            self.iv["{{wlv}}"] = str(int(self.iv["{{wlv}}"]) + int(inc[1:]))
        elif inc[0] == '-':     # If I'm incrementing down, subtract one from the while loop var
            self.iv["{{wlv}}"] = str(int(self.iv["{{wlv}}"]) - int(inc[1:]))

    @staticmethod
    def _format(*k):
        kl = list(k)                                                # Format keywords as list
        swap = [i for i, x, in enumerate(kl) if x == '\\' and (kl[i - 1] != '\\' and kl[i + 1] == '\\')]
        delete = [i for i, x, in enumerate(kl) if x == '\\' and kl[i - 1] == '\\' or i == 0]
        kl = ['\\\\' if i in swap else x for i, x in enumerate(kl)]    # Swap '\\' for '\\\\'
        return [k for i, k, in enumerate(kl) if i not in delete]    # Delete

    def _run_keywords(self, *kl):
        ks = [-1] + [i for i in range(0, len(kl) - 1) if kl[i] == '\\'] + [len(kl)]   # Find the deliminators
        for i in range(0, len(ks) - 1):             # For each deliminator, get the name and arguments of the keyword
            name, args = kl[ks[i] + 1], kl[ks[i] + 2:ks[i + 1]]
            if str(name)[-1:] == '=':               # If it's an assignment, assign it.
                self._assign_internal_variable(name.rstrip(' ='), *args)
            elif str(name).lower() == 'exit if':    # If it's an exit if statement, break the For Loop.
                if BuiltIn().evaluate(args[0], modules='os,sys'):
                    return False
            else:                                   # Otherwise, run the keyword
                for k in self.iv.keys():    # Activate internal variables
                    args = [x.replace(k, self.iv[k]) for x in args]
                BuiltIn().run_keyword(name, *args)          # Run the keyword
        return True     # Required for "exit if" keyword when loop breaking is installed

    def _assign_internal_variable(self, variable_name, assignment, *arguments):
        if BuiltIn().run_keyword_and_return_status("Keyword Should Exist", assignment):     # If it's a keyword
            self.iv[variable_name] = BuiltIn().run_keyword(assignment, *arguments)  # Assign the result of the keyword
        else:   # If it's a simple value
            self.iv[variable_name] = assignment     # Save it to the variable name

And the test Robot Framework code:

*** Settings ***
Library     Loops.py

*** Variables ***
${blue_squadron} =      Blue
${gold_squadron} =      Gold
${green_squadron} =     Green
${red_squadron} =       Red
${standing_by} =        standing by
${test_index} =         0
${test_index_0} =       0
${test_index_1} =       100
${test_index_2} =       3

*** Test Cases ***
Test For Loop IN RANGE
    Loop    FOR RANGE   0   1   INDEX0
    ...     \\  Log to Console  Start
    ...     \\  Loop    FOR RANGE   1   6   INDEX1
    ...     \\  \\  {standing_by}=      standing by
    ...     \\  \\  Run Keyword If      INDEX1 == 1     Log to Console  This is ${red_squadron} Leader standing by
    ...     \\  \\  Run Keyword Unless  INDEX1 == 1     Log to Console  ${red_squadron} INDEX1 {standing_by}
    ...     \\  Loop    FOR RANGE   1   6   INDEX2
    ...     \\  \\  standing_by_2 =     standing by
    ...     \\  \\  Run Keyword If      INDEX2 == 1     Log to Console  This is ${gold_squadron} Leader standing by
    ...     \\  \\  Run Keyword Unless  INDEX2 == 1     Log to Console  ${gold_squadron} INDEX2 standing_by_2
    ...     \\  Loop    FOR RANGE   1   6   INDEX3
    ...     \\  \\  standing_by_3=      Get Blue Squadron
    ...     \\  \\  Run Keyword If      INDEX3 == 1     Log to Console  This is ${blue_squadron} Leader standing by
    ...     \\  \\  Run Keyword Unless  INDEX3 == 1     Log to Console  ${blue_squadron} INDEX3 standing_by_3
    ...     \\  Loop    FOR RANGE   1   6   INDEX4
    ...     \\  \\  standing_by_4 =     Get Green Squadron   ${standing_by}
    ...     \\  \\  Run Keyword If      INDEX4 == 1     Log to Console  This is ${green_squadron} Leader standing by
    ...     \\  \\  Run Keyword Unless  INDEX4 == 1     Log to Console  ${green_squadron} INDEX4 standing_by_4

Test IN RANGE Edge Case 1 - Single Keyword with Single Argument
    Loop    FOR RANGE   0   1   INDEX0
    ...     \\  Log to Console  testlog

Test For Loop Exit
    Log to Console  Start
    Loop    FOR RANGE   0   3   INDEX0
    ...     \\  Log to Console  INDEX0
    ...     \\  Exit If     INDEX0 == 1

Test While Loops
    Log to Console  Start
    Log to Console  Testing 0 < 3
    Loop    WHILE   ${test_index_0} < 3
    ...     \\  Log to Console  Index: {{wlv}}
    ...     \\  Increment   +1
    Log to Console  Testing 3 > 0
    Loop    WHILE   ${test_index_2} > 0
    ...     \\  Log to Console  Index: {{wlv}}
    ...     \\  Increment   -1
    Log to Console  Testing 0 <= 3
    Loop    WHILE   ${test_index_0} <= 3
    ...     \\  Log to Console  Index: {{wlv}}
    ...     \\  Increment   +1
    Log to Console  Testing 3 >= 0
    Loop    WHILE   ${test_index_2} >= 0
    ...     \\  Log to Console  Index: {{wlv}}
    ...     \\  Increment   -1
    Log to Console  Final
    Log to Console  Testing 3 != 0
    Loop    WHILE   ${test_index_2} != 0
    ...     \\  Log to Console  Index: {{wlv}}
    ...     \\  Increment   -1
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.