.miglioriamo QGIS! Come? Testandolo!

In questo post voglio mostrarvi quanto è semplice scrivere dei test, o comunemente chiamati “unit test”, per il plugin processing di QGIS. Lo scopo dello unit testing è quello di verificare il corretto funzionamento di parti di programma permettendo così una precoce individuazione dei bug. Si tratta di testare alcune porzioni di codice di un software, le più piccole possibili, per individuare eventuali malfunzionamenti.

Per chi non ha seguito l’evoluzione sull’argomento, ora in QGIS è possibile creare dei test direttamente dall’interfaccia grafica per quasi tutti gli algoritmi presenti nel plugin Processing. Un grande passo avanti, soprattutto per la stabilità del programma anche sui futuri rilasci. Cosa bisogna fare?, è sufficiente eseguire un algoritmo, andare nella finestra dello Storico dei comandi, cliccare col tasto destro sull’ultimo algoritmo eseguito e creare il test. Apparirà una finestra di dialogo con il testo da copiare ed incollare in un file specifico formattato con la sintassi yaml.

Banale vero?, se non fosse che c’è di mezzo Github. L’obiettivo che mi sono prefisso scrivendo questo articolo è semplificare il più possibile le operazioni per interagire con Github, operazioni che per un utente che non ha mai utilizzato questo strumento possono rivelarsi troppo complicate. Consiglio di leggere questa guida tascabile.

Molti utenti sono intimoriti dall’utilizzo di git, anche se ultimamente è diventato molto più semplice, permettendo le modifiche ai file presenti sulla repository di un progetto anche direttamente dall’interfaccia web. Ma per alcune operazioni non è sempre così immediato.

Premetto che per alcuni potrebbe essere più semplice utilizzare direttamente git ma ho voluto creare un’alternativa giocando intorno all’utilizzo di un paio di librerie python trovate in giro per la rete. Per tale motivo ho scritto uno script da eseguire direttamente dalla console python di QGIS, senza necessità di utilizzo della linea di comando o di passare da altri software che potrebbero inibire l’utente medio dall’utilizzare uno strumento così potente.

Non è detto però che per qualcuno ho complicato di più le cose 🙂

Servono delle librerie di terze parti però…

Lo script utilizza principalmente la libreria GitPython per interagire con le repository su Github. GitPython necessita a sua volta di alcune librerie che nella fattispecie sono GitGitDB e smmap. Per le librerie python, (GitPython, GitDB e smmap), se non si vuole installarle con il comando pip install, è possibile scaricare i rispettivi pacchetti e copiare le relative cartelle (git, gitdb e smmap) all’interno di una directory sul vostro sistema. La procedura è scaricare il pacchetto relativo ad ogni libreria, estrarre il contenuto e copiare la cartella con il modulo principale su un’altra cartella (io l’ho chiamata ext-libs) presente sul vostro disco fisso. Alla fine non resta che aggiungere la cartella ext-libs, su cui sono state copiate le librerie, al percorso di ricerca dell’interprete python per renderle accessibili al nostro script.

import sys
import os
sys.path.append('/Users/larosa/dev/ext-libs')

Un’alternativa è copiare le tre cartelle all’interno della directory {utente}/.qgis2/python/, per essere caricate automaticamente all’avvio di QGIS. Per i più pigri ho messo tutto su Gitlab.Schermata 2016-03-28 alle 22.32.03

Creare un fork del progetto QGIS…

Prima di andare avanti è necessario avere un account su Github (per registrarsi è sufficiente la username, la email e la password), una volta creato l’account e fatto l’accesso va creato un fork al progetto QGIS andando sulla pagina qgis/QGIS e selezionando il bottone Fork (come in figura)

Schermata 2016-03-28 alle 19.07.47

Sarà creato un fork del progetto QGIS sul vostro profilo Github.

Adesso dobbiamo clonare la repository su una cartella in locale. Quindi compiliamo le variabili username, password, email e repo_dir ed eseguiamo il codice nella console python di QGIS. Questa operazione potrebbe prendere un po di tempo e va eseguita una sola volta, quindi non preoccupatevi!

Ricordiamoci anche di inserire un testo chiaro e sintetico alla variabile commit_message, inserendo una descrizione, preferibilmente in inglese, del test che stiamo andando ad aggiungere.

from git import *

# edit
username = 'your_github_username'
password = 'your_github_password'
email = 'your_github_email'

repo_dir = '/Users/larosa/dev/QGIS_fork'
branch = 'processing_test'
commit_message = 'add polygon to lines test'

# not edit
definition_file = 'python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml'
repo_qgis = 'https://github.com/qgis/QGIS.git'

# clone the remote repository
repo = Repo.clone_from('https://' + username + ':' + password + '@github.com/' + username + '/QGIS.git', repo_dir)

Dopo aver clonato il progetto in locale, se si vuole eseguire nuovamente lo script per inviare nuovi unit test, bisogna sostituire la riga 17 con la seguente. Questo evita di copiare nuovamente in locale tutti i file di progetto.

repo = Repo.init(repo_dir)

Creare un nuovo branch e aggiornare la repository in locale…

Dopo aver clonato la repository, creiamo un nuovo branch (ramo). I branch (ramificazioni) sono utilizzati per sviluppare nuove funzioni che sono isolate l’una dall’altra, in modo da mantenere il ramo principale (master) pulito. Il nuovo branch lo chiamiamo “processing_test” e subito dopo aver creato il ramo facciamo una pull per aggiornare il codice tra la repository locale e quella remota (questo passaggio può essere necessario solo in caso in cui il fork esisteva già ma non era stato aggiornato, altrimenti sarà automaticamente aggiornato all’ultimo commit del branch master). Anche il comando di pull può prendere un po di tempo, quindi non preoccupatevi se c’è da aspettare.

# create a new branch
new_branch = repo.create_head('processing_test') 

# create upstream remote urls
upstream = repo.create_remote('upstream', repo_qgis)

origin = repo.remotes.origin
upstream = repo.remotes.upstream

# get heads and checkout new branch
heads = repo.heads
heads.new_branch.checkout()

# pull to new branch from qgis
upstream.pull(refspec=heads.master)

Eseguire l’algoritmo su cui creare il test…

Ora che abbiamo clonato ed aggiornato la repository possiamo utilizzare i file presenti nella cartella testdata del plugin processing. Quindi eseguiamo un algoritmo di processing utilizzando come input i dati presente nella cartella testdata. Nella immagine è mostrato l’esempio con l’algoritmo “Da Poligoni a linee”. La cosa importante è scrivere l’output nella cartella expected. Il file di output è preferibile salvarlo nel formato GML.

Schermata 2016-03-28 alle 20.49.56.png

Dopo aver eseguito l’algoritmo, andare sullo Storico dei comandi di Processing (Menu Processing->Storico, vedi immagine) e creare un test. Per creare un test cliccare col tasto destro del mouse sulla riga corrispondente all’algoritmo appena eseguito, in alto alla lista degli algoritmi, e successivamente selezionare “Create test” dal menu contestuale.

Dalla finestra di dialogo “Unit test” selezionare e copiare tutto il testo riportato.

Infine, inviare le modifiche fatte in locale sul fork del progetto…

Ora che abbiamo copiato il testo, incolliamolo nella variabile “test” come mostrato nel pezzo di codice seguente ed eseguiamolo. Questo passaggio serve a modificare il file contenente la definizione dei test degli algoritmi di processing.

test = '''
    - algorithm: qgis:polygonstolines
      name: Test (qgis:polygonstolines)
      params:
          INPUT:
              name: polys.gml
              type: vector
      results:
          OUTPUT:
              name: expected/polys2lines.gml
              type: vector
'''
with open(os.path.join(repo_dir, definition_file), 'a') as f:
    f.write('\n\n' + test)

Ci siamo quasi, manca solo il push delle modifiche sulla repository che abbiamo forkato prima, quindi per aggiornare il nostro Fork eseguiamo il codice riportato sotto.

Purtroppo, mentre sto scrivendo questo post, il modulo GitPython mi da errore quando lancio la funzione repo.untracked_files() per elencare i nuovi file aggiunti. Per evitare di farvi scrivere a mano i file aggiunti e modificati nel progetto in locale, ho fatto un parsing direttamente nell’output del comando git status, ottenendo così lo stesso risultato della funzione untracked_files()

status = repo.git.status()
untracked_files = []

for l in status.split('\n'):
    if l.strip().startswith('python/plugins'):
        untracked_files.append(l.strip())

untracked_files.append(definition_file)

author = Actor('Author', email)
committer = Actor('Committer', email)
index = repo.index
index.add([f for f in untracked_files])
index.commit('[processing-test] ' + commit_message, author=author, committer=committer)

origin.push(refspec=heads.new_branch)

Abbiamo finito, l’ultimo sforzo è andare sulla pagina della repository su Github e cliccare sul bottone “Compare & pull request” che appare nella pagina del vostro fork ed infine su “Create pull request”Le figure in basso potrebbero aiutare ad individuare i due pulsanti.

Schermata 2016-03-28 alle 21.33.51Schermata 2016-03-29 alle 01.43.00.png

Spero che il tutto funzioni correttamente anche per gli amici di Windows!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s