Use proper netconf testtool artifact
[integration/test.git] / csit / libraries / ScalarClosures.robot
1 *** Settings ***
2 Documentation     Robot keyword library (Resource) for supporting functional programming via "scalar closures".
3 ...
4 ...               Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
5 ...
6 ...               This program and the accompanying materials are made available under the
7 ...               terms of the Eclipse Public License v1.0 which accompanies this distribution,
8 ...               and is available at http://www.eclipse.org/legal/epl-v10.html
9 ...
10 ...
11 ...               Python has fist-class functions. It is possible to perform partial application
12 ...               and have the resulting anonymous function passed around as an object.
13 ...               Robot Framework has second class Keywords. Keyword has to be specified by its name,
14 ...               and ordering between positional and named arguments limit their usage.
15 ...
16 ...               There are several different ways how to overcame these limitations
17 ...               to offer something resembling functional programming in Robot.
18 ...               This library does everything via "scalar closures".
19 ...
20 ...               Closure is a function together with values for some seemingly free variables.
21 ...               This library encodes closure as a scalar value (that is in fact a list).
22 ...               Scalars cannot be run in Robot directly, so a method to run them as closure is provided.
23 ...               Instead of alowing arguments, methods to run with substituted values are used.
24 ...               For substitution to work, the original closure has to be defined with (placeholder) arguments.
25 ...               TODO: Look once again for a way to remove this limitation.
26 ...
27 ...               For Keywords of this library to be easily wrappable (and runable with substitution),
28 ...               their arguments are usually positional.
29 ...               TODO: Look once again if adding/substituting named arguments is doable.
30 ...
31 ...               Current limitation: Keywords inside closures may detect there were given @{args} list, even if it is empty.
32 ...
33 ...               There are convenience closures defined, but SC_Setup has to be called to make them available.
34 Library           Collections
35
36 *** Keywords ***
37 Closure_From_Keyword_And_Arguments
38     [Arguments]    ${keyword}    @{args}    &{kwargs}
39     [Documentation]    Turn keyword with given arguments into a scalar closure.
40     ...
41     ...    Implemented as triple of keyword name, args as scalar and kwargs as scalar.
42     [Return]    ${keyword}    ${args}    ${kwargs}
43
44 Run_Closure_As_Is
45     [Arguments]    ${closure}
46     [Documentation]    Run the keyword from closure without affecting arguments.
47     ${keyword}    ${args}    ${kwargs} =    BuiltIn.Set_Variable    ${closure}
48     ${result} =    BuiltIn.Run_Keyword    ${keyword}    @{args}    &{kwargs}
49     [Return]    ${result}
50
51 Run_Closure_After_Replacing_First_Argument
52     [Arguments]    ${closure}    ${argument}
53     [Documentation]    Run the keyword from closure with replaced first positional argument.
54     ...
55     ...    Note, this will currently fail if the closure was created with less than 1 positional argument.
56     ${keyword}    ${args}    ${kwargs} =    BuiltIn.Set_Variable    ${closure}
57     Collections.Set_List_Value    ${args}    0    ${argument}
58     # TODO: Catch failure and use Collections.Append_To_List to overcome current limitation.
59     ${result} =    BuiltIn.Run_Keyword    ${keyword}    @{args}    &{kwargs}
60     [Return]    ${result}
61
62 Run_Closure_After_Replacing_First_Two_Arguments
63     [Arguments]    ${closure}    ${arg1}    ${arg2}
64     [Documentation]    Run the keyword from closure with replaced first two positional arguments.
65     ...
66     ...    Note, this will currently fail if the closure was created with less than 2 positional arguments.
67     ${keyword}    ${args}    ${kwargs} =    BuiltIn.Set_Variable    ${closure}
68     Collections.Set_List_Value    ${args}    0    ${arg1}
69     Collections.Set_List_Value    ${args}    1    ${arg2}
70     ${result} =    BuiltIn.Run_Keyword    ${keyword}    @{args}    &{kwargs}
71     [Return]    ${result}
72
73 Run_Keyword_And_Collect_Garbage
74     [Arguments]    ${keyword_to_gc}=BuiltIn.Fail    @{args}    &{kwargs}
75     [Documentation]    Runs Keyword, but performs garbage collection before pass/fail.
76     ...
77     ...    TODO: Move to more appropriate Resource.
78     ...
79     ...    Keyword to run is given as named argument, hopefully to not mess with replaced arg execution.
80     ...    TODO: Test that, as the "hopefully" part may not really work.
81     ...
82     ...    Return value / failure message is copied from Keyword response.
83     ...
84     ...    This is a convenience wrapper to be used by callers.
85     ...    Usually, Run_Closure_* end up being wrapped this way.
86     ...
87     ...    Some Keywords may generate a LOT of garbage, especially when
88     ...    they download massive datasets and then passage them all the way down
89     ...    to just one integer or something similarly small (an example can be
90     ...    getting count of routes in topology which can generate several tens of
91     ...    MB of garbage if the topology contains several million routes). This
92     ...    garbage is not immediately reclaimed by Python once it is no longer in
93     ...    use because Robot creates cycled structures that hold references to
94     ...    this multi-megabyte garbage. Allowing this garbage to build could cause
95     ...    "sudden death syndrome" (OOM killer invocation) of the Robot process
96     ...    before Python decides to collect the multi-megabyte pieces of the
97     ...    garbage on its own.
98     # Execute Keyword but postpone failing.
99     ${status}    ${message}=    BuiltIn.Run Keyword And Ignore Error    ${keyword_to_gc}    @{args}    &{kwargs}
100     # Collect garbage.
101     BuiltIn.Evaluate    gc.collect()    modules=gc
102     # Resume possible failure state
103     Propagate_Fail    status=${status}    message=${message}
104     # So we have passed, return value.
105     [Return]    ${message}
106
107 Propagate_Fail
108     [Arguments]    ${status}=PASS    ${message}=Message unknown.
109     [Documentation]    If ${status} is PASS do nothing. Otherwise Fail with ${message}.
110     ...
111     ...    TODO: Move to more appropriate Resource.
112     BuiltIn.Return_From_Keyword_If    '''${status}''' == '''PASS'''
113     BuiltIn.Fail    ${message}
114
115 SC_Setup
116     [Documentation]    Resource setup. Create closures and assign them to suite variables.
117     ...
118     ...    As scalar closures are values (as opposed to Keywords), lowercase is used.
119     ...    Prefix of Resource name and two underscores is added to avoid possible name clashes with other libraries.
120     # No need to detect multiple Setup calls.
121     ${sc_fail} =    Closure_From_Keyword_And_Arguments    BuiltIn.Fail
122     BuiltIn.Set_Suite_Variable    ${ScalarClosures__fail}    ${sc_fail}
123     ${sc_identity} =    Closure_From_Keyword_And_Arguments    BuiltIn.Set_Variable    placeholder
124     BuiltIn.Set_Suite_Variable    ${ScalarClosures__identity}    ${sc_identity}