Support only Fluorine+ distributions
[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
35 Library             Collections
36
37
38 *** Keywords ***
39 Closure_From_Keyword_And_Arguments
40     [Documentation]    Turn keyword with given arguments into a scalar closure.
41     ...
42     ...    Implemented as triple of keyword name, args as scalar and kwargs as scalar.
43     [Arguments]    ${keyword}    @{args}    &{kwargs}
44     RETURN    ${keyword}    ${args}    ${kwargs}
45
46 Run_Closure_As_Is
47     [Documentation]    Run the keyword from closure without affecting arguments.
48     [Arguments]    ${closure}
49     ${keyword}    ${args}    ${kwargs} =    BuiltIn.Set_Variable    ${closure}
50     ${result} =    BuiltIn.Run_Keyword    ${keyword}    @{args}    &{kwargs}
51     RETURN    ${result}
52
53 Run_Closure_After_Replacing_First_Argument
54     [Documentation]    Run the keyword from closure with replaced first positional argument.
55     ...
56     ...    Note, this will currently fail if the closure was created with less than 1 positional argument.
57     [Arguments]    ${closure}    ${argument}
58     ${keyword}    ${args}    ${kwargs} =    BuiltIn.Set_Variable    ${closure}
59     Collections.Set_List_Value    ${args}    0    ${argument}
60     # TODO: Catch failure and use Collections.Append_To_List to overcome current limitation.
61     ${result} =    BuiltIn.Run_Keyword    ${keyword}    @{args}    &{kwargs}
62     RETURN    ${result}
63
64 Run_Closure_After_Replacing_First_Two_Arguments
65     [Documentation]    Run the keyword from closure with replaced first two positional arguments.
66     ...
67     ...    Note, this will currently fail if the closure was created with less than 2 positional arguments.
68     [Arguments]    ${closure}    ${arg1}    ${arg2}
69     ${keyword}    ${args}    ${kwargs} =    BuiltIn.Set_Variable    ${closure}
70     Collections.Set_List_Value    ${args}    0    ${arg1}
71     Collections.Set_List_Value    ${args}    1    ${arg2}
72     ${result} =    BuiltIn.Run_Keyword    ${keyword}    @{args}    &{kwargs}
73     RETURN    ${result}
74
75 Run_Keyword_And_Collect_Garbage
76     [Documentation]    Runs Keyword, but performs garbage collection before pass/fail.
77     ...
78     ...    TODO: Move to more appropriate Resource.
79     ...
80     ...    Keyword to run is given as named argument, hopefully to not mess with replaced arg execution.
81     ...    TODO: Test that, as the "hopefully" part may not really work.
82     ...
83     ...    Return value / failure message is copied from Keyword response.
84     ...
85     ...    This is a convenience wrapper to be used by callers.
86     ...    Usually, Run_Closure_* end up being wrapped this way.
87     ...
88     ...    Some Keywords may generate a LOT of garbage, especially when
89     ...    they download massive datasets and then passage them all the way down
90     ...    to just one integer or something similarly small (an example can be
91     ...    getting count of routes in topology which can generate several tens of
92     ...    MB of garbage if the topology contains several million routes). This
93     ...    garbage is not immediately reclaimed by Python once it is no longer in
94     ...    use because Robot creates cycled structures that hold references to
95     ...    this multi-megabyte garbage. Allowing this garbage to build could cause
96     ...    "sudden death syndrome" (OOM killer invocation) of the Robot process
97     ...    before Python decides to collect the multi-megabyte pieces of the
98     ...    garbage on its own.
99     [Arguments]    ${keyword_to_gc}=BuiltIn.Fail    @{args}    &{kwargs}
100     # Execute Keyword but postpone failing.
101     ${status}    ${message} =    BuiltIn.Run Keyword And Ignore Error    ${keyword_to_gc}    @{args}    &{kwargs}
102     # Collect garbage.
103     BuiltIn.Evaluate    gc.collect()    modules=gc
104     # Resume possible failure state
105     Propagate_Fail    status=${status}    message=${message}
106     RETURN    ${message}
107
108     # So we have passed, return value.
109
110 Propagate_Fail
111     [Documentation]    If ${status} is PASS do nothing. Otherwise Fail with ${message}.
112     ...
113     ...    TODO: Move to more appropriate Resource.
114     [Arguments]    ${status}=PASS    ${message}=Message unknown.
115     IF    '''${status}''' == '''PASS'''    RETURN
116     BuiltIn.Fail    ${message}
117
118 SC_Setup
119     [Documentation]    Resource setup. Create closures and assign them to suite variables.
120     ...
121     ...    As scalar closures are values (as opposed to Keywords), lowercase is used.
122     ...    Prefix of Resource name and two underscores is added to avoid possible name clashes with other libraries.
123     # No need to detect multiple Setup calls.
124     ${sc_fail} =    Closure_From_Keyword_And_Arguments    BuiltIn.Fail
125     BuiltIn.Set_Suite_Variable    ${ScalarClosures__fail}    ${sc_fail}
126     ${sc_identity} =    Closure_From_Keyword_And_Arguments    BuiltIn.Set_Variable    placeholder
127     BuiltIn.Set_Suite_Variable    ${ScalarClosures__identity}    ${sc_identity}