Project

General

Profile

action #120022

Updated by JERiveraMoya over 1 year ago

#### Motivation 
 Once Yam squad is starting progressively to rewrite some YaST modules test cases with libyui-rest-api and apply a good design to them, like in this PR: https://github.com/os-autoinst/os-autoinst-distri-opensuse/pull/15496 
 we still need to improve how each step is visualized, we can see that the screenshot we provide is not in sync with the action: 
 https://openqa.opensuse.org/tests/2857887#step/yast2_firewall_stop_service/5 
 Wrapping the method, for example like this `wait_screen_change(sub { $testapi::distri->get_firewall()->start_firewall() } );` 
 See Draft PR: https://github.com/os-autoinst/os-autoinst-distri-opensuse/pull/15844/files#diff-8fc5ca29998de81bd0c818e44236ad9c51cc838bafc7429a1f30d83e43acbc3dR23 
 we can achieve: https://openqa.opensuse.org/tests/2857895#step/yast2_firewall_stop_service/8 

 The goal is to find a solution, perhaps decorator pattern in Perl or some sort of wrapping which is flexible and decoupled from the other layers. 
 Therefore in the example above we still run `$testapi::distri->get_firewall()->start_firewall()` but behind the scene the other actions will take place. But those actions should be assigned from outside, so Controller layer can have some variable to store and execute it, but doesn't know anything about testapi.pm. 

 #### Acceptance criteria 
 **AC1**: Research about potential solution to wrap functions in Perl 
 **AC2**: Define a wrapper/decorator for some methods of the Object Controller 
 **AC3**: Being able to have other Object Controller methods not decorated 

 #### Additional information 
 We could use this snippet as starting point: 
 ``` 
 #!/usr/bin/perl 
 use strict; 
 use warnings; 

 use experimental 'signatures'; 
 use Sub::Util 'subname'; 
 use feature qw(say); 

 # This utility function accepts a func ref and a code ref and wraps them 
 sub wrap ($func, $code) { 
     no strict; 
     no warnings 'redefine'; 
     *{ subname($func) } = $code->($func); 
 } 

 # This will be the wrapper. Like a Python decorator, it's accepts the wrapped 
 # function as the first argument, and returns a new function 
 sub debug($func) { 
     my $func_name = subname($func); 
     return sub (@args) { 
         say {*STDERR} "DEBUG: Calling $func_name(@args)"; 
         $func->(@args); 
     } 
 } 

 # You can wrap the function anywhere, but doing it before the function 
 # definition it easier to notice, and is also similar to Python decorators. 
 wrap(\&double, \&debug); 
 sub double($n) { 
     return $n * 2; 
 ``` 
 - where 'debug' might be our step_decorator method where we can apply those additional functions, but in theory we should have step_around, step_before and step_after and for our case in principle we would add `step_around` in combination with `step_after`. To make them different decorator we'll give us more flexibility and we should be able to apply more than one as in our case. 
 - Once the decorator is created the second task is calls to know where to put it? The simple way would wrap can be to put in done at the level of Test layer, completely independent of Layer, without the framework, that is the most decoupled solution probably, but in fact we already have some knowledge of the methods for a Controller, meaning that some methods are action that need to change the screen, so for those `step_around` and `step_after` decorator can be assign anything in the code implementation of the Controller, and it could also be assign on the code implementation of the Page, if we will remove the Controllers in the future. Controller or Page. 
 - Instead of assigning specific implementation of decorators in the Object (Controller or Page) we will initialize this objects applying those `wrap` operations with values of those decorator, which will not be assigned directly in the Object, but in the setup of the libyui, in what we call the App. In Notice that way at the beginning of these tests where we are testing YaST module can get function reference from inner objects, so we initially setup the specific implementation for can break this decorators. 
 Then when the Object (Controller for now) is instantiated it will already contains by code the the type of decorator for not the exact implementation, that would be passed on initialization. 
 If this suggestion doesn't work, we should discuss further where to do this `wrap` operations. example in different clases.

Back