Pergunta

Nós usamos Perl para automação de teste de GUI. Tem sido muito bem sucedida. Nós escrevemos um tipo DSL muito leve da linguagem para testes de GUI. O DSL é muito semelhante a um modelo de objeto.

Por exemplo, temos um objecto de aplicação na raiz. Cada folha de propriedades no aplicativo é um objeto View. Cada página sob a página é chamado de objeto Page em si. De Perl que enviar comandos para uma aplicação gráfica ea GUI interpretar o comando e responder ao comando bem. Para enviar um comando que faça o seguinte:

socket_object->send_command("App.View2.Page2.Activate()")
socket_object->send_command("App.View1.Page3.OKBtn.Click()")

Isto não é muito legível. Em vez disso, eu quero escrever uma DSL Perl para App, Vista e Página. Does Perl fornecer algum tipo de estrutura DSL onde posso fazer o seguinte?

App.View2.Page2.Activate();
App.View1.Page2.Click();

Onde App será uma instância da classe Application. Eu tenho que pegar o objeto de View2 em tempo de execução.

Como usar tais coisas um?

Foi útil?

Solução

Você pode fazer quase qualquer coisa em Perl. Mas você tem que fazer alguma estranha material para obter Perl para executar com a sintaxe que não é apenas Perl.

  • Para lidar com exatamente o que você tem aí, você teria que um monte de avançado truques, que são, por definição, não que manter. Você teria que:

    • sobrecarga o operador de concatenação '' (Requer uma referência abençoada)
    • restrições off ou criar uma subs AUTOLOAD para permitir essas palavras nuas - é claro, você poderia escrever subs para todas as palavras que você queria usar (ou use o barewords módulo).
    • possivelmente, criar vários pacotes, com múltiplas AUTOLOADs
  • Outra maneira é filtros de origem , eu provavelmente pode pegar um downvote apenas para mencionar essa capacidade. Então, eu não exatamente recomendar esta abordagem para pessoas que são pedindo para obter ajuda. Mas é lá fora. filtros de origem (e eu fiz minha parte) são apenas uma daquelas áreas onde você pode pensar que você é inteligente demais para seu próprio bem.

    Ainda assim, se você estiver interessado em Perl como um DSL linguagem "host", em seguida, não são exatamente fora dos limites. No entanto, limitando esta a apenas o que você mostrar que você quer fazer, Perl6 :: Atributos provavelmente irá fazer mais do que você necessita para a direita fora da prateleira. Levaria o . e traduzi-los para o "->" que Perl entenderia. Mas você ainda pode tomar um olhar em filtros de origem para entender o que é acontecendo nos bastidores.

    Eu também não quero deixar este assunto sem sugerindo que um monte de frustração que você poderia ter gerando seu próprio filtro de origem (que eu não aconselho a fazer) é facilitado usando Filtro :: Simple .

  • A coisa mais simples é renunciar ao '' operador e apenas em vez de esperar o código Perl para o futuro.

    App->View2->Page2->Activate(); 
    App->View1->Page2->Click();
    

    App seria um pacote ou uma sub. Quer definido no pacote corrente ou importados que devolve um objecto abençoado em um pacote com um sub View2 (possivelmente uma sub AUTOLOAD ) que devolve o nome de um pacote ou uma referência abençoado para um pacote, que compreende Page2, e então, finalmente, o retorno de que iria entender Activate ou Click. (Veja a OO tutorial , se você precisar.)

Outras dicas

Eu recomendo que você parar de tentar fazer bizarro coisas "DSL" e apenas escrever as classes Perl para lidar com os objetos que você deseja gerenciar. Eu recomendo que você olhar para usar o novo sistema de objetos Moose Perl para isso, embora tradicional Perl OO seria muito bem. Dig através da documentação Perl para os tutoriais OO; eles são ótimos.

Método chama -> uso perl5 não ., então ele vai olhar como App->View2->Page2->Activate() ou $App->View2->Page2->Active() menos que você faça algo realmente interessante (por exemplo, um filtro de origem). Assumindo que está ok, você pode usar normais coisas Perl OO.

Agora, a próxima parte do que você precisa é criar os métodos em tempo de execução. Este é realmente bastante simples:

sub _new_view {
    my ($view, $view_num);

    # ...
    # ... (code to create $view object)
    # ...

    my $sym = "App::View$view_num";
    *$sym = sub { return $view }; # can also use Symbol package
}

Como alternativa, se você quiser criar os métodos apenas quando eles são chamados, é o que AUTOLOAD faz. Você também pode abusar autoload para fazer todas as chamadas de método a ter sucesso (embora atente para aqueles com significados especiais, como destruir) a.

Isso vai te dar a sintaxe. Tendo seus objetos gerar uma cadeia para passar para send_command não deve ser tão difícil.

Além disso, eu não estou muito familiarizado com ele, mas você pode querer verificar para fora Moose . Ele pode ter maneiras mais fáceis de fazer isso.

DSL Fonte Filtro

Aqui está outra tentativa. skiphoppy tem um ponto, mas em segundo olhar, notei que (até agora) você não estava pedindo muito do que era tão complexo. Você só quer tirar cada comando e dizer ao servidor remoto para fazê-lo. Não é perl que tem de compreender os comandos, é o servidor.

Então, eu remover alguns dos meus avisos sobre filtros de origem, e decidiu mostrar-lhe como um simples pode ser escrito. Novamente, o que você está fazendo não é tão complexo, e meu "filtrando" a seguir é bastante fácil.

package RemoteAppScript;
use Filter::Simple;    # The basis of many a sane source filter
use Smart::Comments;   # treat yourself and install this if you don't have 
                       # it... or just comment it out.

# Simple test sub
sub send_command { 
    my $cmd = shift;
    print qq(Command "$cmd" sent.\n);
    return;
}

# The list of commands
my @script_list;

# The interface to Filter::Simple's method of source filters.
FILTER { 
    # Save $_, because Filter::Simple doesn't like you reading more than once.
    my $mod = $_;

    # v-- Here a Smart::Comment.
    ### $mod

    # Allow for whole-line perl style comments in the script
    $mod =~ s/^\s*#.*$//m;

    # 1. Break the package up into commands by split
    # 2. Trim the strings, if needed
    # 3. lose the entries that are just blank strings.
    @script_list 
        = grep { length } 
          map  { s/^\s+|\s+$//g; $_ } 
          split /;/, $mod
        ;
    ### @script_list

    # Replace the whole script with a command to run the steps.
    $_ = __PACKAGE__ . '::run_script();';
    # PBP.
    return;
};

# Here is the sub that performs each action.
sub run_script { 
    ### @script_list
    foreach my $command ( @script_list ) {
        #send_command( $command );
        socket_object->send_command( $command );
    }
}

1;

Você precisa salvar esta em algum lugar RemoteAppScript.pm onde seus perl pode encontrá-lo. (Experimente perl -MData::Dumper -e 'print Dumper( \@INC ), "\n"' se você precisa saber onde.)

Em seguida, você pode criar um arquivo "perl", que tem o seguinte:

use RemoteAppScript;
App.View2.Page2.Activate();
App.View1.Page2.Click();

No entanto

Não há nenhum motivo real que você não pode ler um arquivo que contém comandos do servidor. Isso seria jogar fora a chamada FILTER. Você teria

App.View2.Page2.Activate();
App.View1.Page2.Click();

em seu arquivo de script, e seu arquivo perl ficaria mais parecido com isto:

#!/bin/perl -w 

my $script = do { 
    local $/;
    <ARGV>;
};

$script =~ s/^\s*#.*$//m;

foreach my $command ( 
    grep { length() } map  { s/^\s+|\s+$//g; $_ } split /;/, $script 
) { 
    socket_object->send_command( $command );
}

E chamá-lo assim:

perl run_remote_script.pl remote_app_script.ras

http://search.cpan.org/dist/Devel-Declare/ é uma alternativa moderna para filtros fonte que trabalha na integração diretamente no analisador perl, e vale uma olhada.

Uma alternativa para substituir '.' ou usando a sintaxe -> pode estar usando a sintaxe do pacote (: :), ou seja, a criação de pacotes, como o App :: View2 e App :: View2 :: Page2 quando View2 / Page 2 se criado, adicionando um sub AUTOLOAD ao pacote que delega a um método App :: View :: Página ou App :: View, algo como isto:

Em sua App / DSL.pm:

package App::DSL;
use strict; 
use warnings;
# use to avoid *{"App::View::$view::method"} = \&sub and friends
use Package::Stash;

sub new_view(%);
our %views;

# use App::DSL (View1 => {attr1 => 'foo', attr2 => 'bar'}); 
sub import {
    my $class = shift;
    my %new_views = @_ or die 'No view specified';

    foreach my $view (keys %new_views) {
            my $stash = Package::Stash->new("App::View::$view");
        # In our AUTOLOAD we create a closure over the right
        # App::View object and call the right method on it
        # for this example I just used _api_\L$method as the
        # internal method name (Activate => _api_activate)
        $stash->add_package_symbol('&AUTOLOAD' =>  
            sub {  
                our $AUTOLOAD;
                my ($method) = 
                   $AUTOLOAD =~ m{App::View::\Q$view\E::(.*)};
                my $api_method = "_api_\L$method";
                die "Invalid method $method on App::View::$view"
                   unless my $view_sub = App::View->can($api_method);
                my $view_obj = $views{$view}
                    or die "Invalid View $view";
                my $sub = sub {
                        $view_obj->$view_sub();
                };
                     # add the function to the package, so that AUTOLOAD
                     # won't need to be called for this method again
                $stash->add_package_symbol("\&$method" => $sub);
                goto $sub;
            });
        $views{$view} = bless $new_views{$view}, 'App::View';
    }
}

package App::View;

# API Method App::View::ViewName::Activate;
sub _api_activate {
    my $self = shift;
    # do something with $self here, which is the view 
    # object created by App::DSL
    warn $self->{attr1};
}

1;

e em seu script:

use strict;
use warnings;
# Create App::View::View1 and App::View::View2
use App::DSL (View1 => {attr1 => 'hello'}, View2 => {attr1 => 'bye'});
App::View::View1::Activate();
App::View::View2::Activate();
App::View::View1::Activate();
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top