Scarefault
Introdução
[editar | editar código-fonte]Essa página foi escrita e criada dentro do contexto da disciplina de Gerência de Configuração de Software, ministrada pelo professor Dr. Paulo Meirelles, FGA, na Universidade de Brasília.
Finalidade
[editar | editar código-fonte]Este documento tem por propósito apresentar o plano de execução de gerência de configuração de software para o projeto Scarefault, bem como o relato dos resultados.
Visão Geral
[editar | editar código-fonte]Scarefault é um projeto que visa o desenvolvimento de uma ferramenta que possibilite a geração de teste unitários, de forma semiautomatizada, para diferentes linguagens. O alvo inicial é a linguagem de programação Grails. O código-fonte é escrito em C/C++, com o suporte das ferramentas Flexc++ e Bisonc++.
Escopo
[editar | editar código-fonte]As demandas que pretende-se atender com a execução do plano são:
- Configuração de Ambiente Virtual de Desenvolvimento;
- Preparar Ambiente de Desenvolvimento de Testes Unitários;
- Automatização da Execução do Projeto e de Testes Unitários;
- Implantação de Integração Contínua.
Organização e Responsabilidades
[editar | editar código-fonte]Responsável | Papel | Responsabilidade |
---|---|---|
Thaiane Braga | Gerente de Configuração e Desenvolvedora | Escrever plano de CM, Realizar auditoria de configuração, Relatar status de configuração, Criar unidade de implantação e Analisar e criar os itens de configuração no código fonte |
Tomáz Martins | Gerente de Configuração e Desenvolvedor | Escrever plano de CM, Realizar auditoria de configuração, Relatar status de configuração, Criar unidade de implantação e Analisar e criar os itens de configuração no código fonte |
Ferramentas
[editar | editar código-fonte]Para a execução da gerência de configuração foram selecionadas as seguintes ferramentas:
Ferramenta | Descrição |
---|---|
Git | Serviço de Controle de Versão |
GitHub | Web Hosting para projetos em Git |
Vagrant | Ferramenta para criação de ambientes completos de desenvolvimento com foco na automação |
Shell Script | Linguagem de script usado em diversos sistemas operacionais |
Make | Linguagem de script usada para automação de build, comum em projetos em C/C++ |
Catch | Framework de desenvolvimento de testes unitários em C/C++ |
Travis | Serviço de integração contínua |
Milestones do Projeto
[editar | editar código-fonte]Os Mileslones são marcos do projeto onde ocorrerão entregas. Definiu-se um cronograma para auxiliar no andamento do projeto.
Cronograma
[editar | editar código-fonte]Marco | Período | Atividade |
---|---|---|
Plano de Gerência de Configuração | 27 abr 2016 | Apresentação do Plano de Gerência de Configuração |
Ambiente Virtual | (28-12) mai 2016 | Estudo e Criação de Máquina Virtual |
Ambiente de Testes | (13-19) mai 2016 | Estudo e Configuração de Framework Catch |
Execução do Projeto e Testes | (20-29) mai 2016 | Implementação de scripts para automatização |
Ponto de Controle | 01 jun 2016 | Apresentação de Resultados Parciais |
Integração Contínua | (02-20) jun 2016 | Estudar e Implantar Integração Contínua com Travis |
Entrega Final | 22 jun 2016 | Apresentação dos Resultados Finais |
Resultados
[editar | editar código-fonte]Ambiente Virtual
[editar | editar código-fonte]Para configuração do ambiente virtual foi selecionado a ferramenta Vagrant. Essa ferramenta permite a criação de uma box (máquina virtual) com uma configuração específica facilitando, com isso, o levantamento e padronização dos ambientes de desenvolvimento.
Na wiki do projeto Scarefault há um guia com o passo a passo para utilizar uma máquina virtual com a configuração e dependências necessárias para o desenvolvimento e manutenção do projeto. Esse guia está disponível em https://github.com/Scarefault/scarefault/wiki/Como-Contribuir.
A configuração desse ambiente virtual tornou-se possível após a criação de um Vagrantfile. Esse arquivo é responsável pela configuração da box que será instanciada. Os scripts referenciados nas linhas 19, 20 e 21 foram criados para instalação das ferramentas e dependências do projeto.
Vagrant.configure(2) do |config|
config.vm.box = "ubuntu/trusty64"
config.vm.network :forwarded_port, guest: 8080, host: 2323
config.vm.network :private_network, ip: "192.168.222.222"
config.vm.provider "virtualbox" do |v|
v.memory = 1536
v.cpus = 2
end
# Fix error stdin no tty
#
config.vm.provision "fix-no-tty", type: "shell" do |shell|
shell.privileged = false
shell.inline = "sudo sed -i '/tty/!s/mesg n/tty -s \\&\\& mesg n/' /root/.profile"
end
config.vm.provision "shell", path: "scripts/scarefault.sh"
config.vm.provision "shell", path: "scripts/gpp.sh"
config.vm.provision "shell", path: "scripts/java.sh"
config.vm.provision "shell", inline: "echo 'Installing: ( 1 ) sdkman and ( 2 ) grails'"
config.vm.provision "install-sdkman", type: "shell" do |shell|
shell.privileged = false
shell.inline = "curl -s get.sdkman.io | bash"
end
end
A equipe enfrentou dificuldades no início do uso do vagrant, entretanto, é uma ferramenta que possui densa documentação e tutoriais disponíveis e isso facilitou a configuração necessária para o projeto. Não houve relato de nenhuma dificuldade específica nesse âmbito.
Automatização do Build
[editar | editar código-fonte]A automatização do build fez-se necessária desde o início do desenvolvimento pois gerava um esforço consideravelmente alto e periódico. Com isso, decidiu-se criar um script MakeFile de forma a otimizar essa atividade.
Na wiki do repositório do projeto há um guia explicando como compilar o projeto com o uso do makefile e sem o uso do makefile. É importante salientar que o uso do makefile é extremamente recomendado pois durante o desenvolvimento do projeto esse arquivo é atualizado fazendo com que os outros desenvolvedores não precisem se preocupar em editar esse script. Para visualização do guia de compilação do projeto acesse o link: https://github.com/Scarefault/scarefault/wiki/Primeira-Compila%C3%A7%C3%A3o.
Segue abaixo o makefile atual do projeto. Caso tenha interesse, o arquivo também encontra-se no repositório: https://github.com/Scarefault/scarefault/blob/master/Makefile.
CC = g++
CFLAGS = --std=c++0x -o
OBJS = src/*/*/*.cc # identifier/(parser|scanner)/*.cc
SRC_S = src/scarefault.cpp
TST_H = src/*/tester/*.hpp
TST_I = src/*/tester/*.cpp
TCS_H = src/*/testcase/*.hpp
TCS_I = src/*/testcase/*.cpp
GEN_H = src/*/value_generator/*.hpp
GEN_I = src/*/value_generator/*.cpp
CLL_H = src/*/collector/*.hpp
CLL_I = src/*/collector/*.cpp
HLP_H = src/helper/*.hpp
HLP_I = src/helper/*.cpp
LOG_H = log/Log.hpp
LOG_I = log/Log.cpp
all: parse.cc lex.cc scarefault
parse.cc:
cd src/identifier/parser && $(MAKE)
lex.cc:
cd src/identifier/scanner && $(MAKE)
scarefault:
$(CC) $(CFLAGS) scarefault $(SRC_S) $(TST_H) $(TST_I) $(TCS_H) $(TCS_I) $(GEN_H) $(GEN_I) $(CLL_H) $(CLL_I) $(HLP_H) $(HLP_I) $(LOG_H) $(LOG_I) $(OBJS)
clean: parser_clean scanner_clean scarefault_clean
parser_clean:
cd src/identifier/parser && $(MAKE) clean
scanner_clean:
cd src/identifier/scanner && $(MAKE) clean
scarefault_clean:
rm scarefault
O makefile principal, função all, faz chamadas para outros dois scripts que são responsáveis pela compilação do parser (bison) e scanner (flex). Segue abaixo o makefile criado para a compilação do parser e do scanner, respectivamente.
all: parser.cc changes_parser.h changes_parser.ih
parser.cc:
bisonc++ grammar.y -V
changes_parser.h:
$(shell sed -i "17i \\\t Collector::CollectorBase * collector;" Parser.h)
$(shell sed -i "18i \\\t LogSystem::Log log;" Parser.h)
$(shell sed -i "21i \\\t\t explicit Parser(Collector::Language, std::istream &in = std::cin, std::ostream &out = std::cout);" Parser.h)
changes_parser.ih:
$(shell sed -i "8i Parser::Parser(Collector::Language language,std::istream &in, std::ostream &out)" Parser.ih)
$(shell sed -i "9i {" Parser.ih)
$(shell sed -i "10i \\\t d_scanner.setSval(&d_val__);" Parser.ih)
$(shell sed -i "11i \\\t collector = Collector::CollectorBase::get_collector(language);" Parser.ih)
$(shell sed -i "12i }" Parser.ih)
clean:
rm parse.cc Parserbase.h Parser.h Parser.ih
PARSER_PATH = ../parser/Parserbase.h
all: lex.cc changes_scanner.ih changes_scanner.h
lex.cc:
flexc++ lexer.l
changes_scanner.ih:
$(shell echo "// \$$insert class_h" >> Scanner.ih)
$(shell echo "#include \"Scanner.h\"" >> Scanner.ih)
$(shell echo "#include \"$(PARSER_PATH)\"" >> Scanner.ih)
changes_scanner.h:
$(shell sed -i "8i #include \"../parser/Parserbase.h\"" Scanner.h)
$(shell sed -i "22i \\\t\t\t void setSval(Parser::STYPE__* d_val__);" Scanner.h)
$(shell sed -i "25i \\\t\t\t Parser::STYPE__* d_val;" Scanner.h)
$(shell sed -i "38i inline void Scanner::setSval(Parser::STYPE__* d_val__)" Scanner.h)
$(shell sed -i "39i {" Scanner.h)
$(shell sed -i "40i \\\t d_val = d_val__;" Scanner.h)
$(shell sed -i "41i }" Scanner.h)
clean:
rm lex.cc Scannerbase.h Scanner.h Scanner.ih
É possível observar quão oneroso seria compilar o projeto scarefault sem o uso do makefile.
Integração Contínua
[editar | editar código-fonte]Selecionou-se a ferramenta Travis CI para a configuração da integração contínua. O Travis CI é um serviço de integração contínua vinculado com o GitHub. Para utilizar-se o Travis CI é necessário ter uma conta do GitHub. O Travis CI possibilita ao usuário fazer o build de sua aplicação e a execução dos testes, bem como a configuração do ambiente necessária para essas duas atividades.
Cadastro e Configuração do Travis CI
[editar | editar código-fonte]Para o uso do Travis CI há a necessidade, primeiramente, de fazer o cadastro. Isso é simples, pois é suficiente logar no site do Travis CI <https://travis-ci.org/>. Com o cadastro finalizado a tela inicial é um Dashboard, onde encontra-se à esquerda a listagem das organizações que você faz parte e, à direita, constam os repositórios pertencentes a você. A Figura abaixo mostra isso:
Nessa tela habilita-se o Travis CI a fazer o monitoramento do repositório selecionado. Foi selecionado o repositório do projeto scarefault. Agora seleciona-se o ícone de configuração, para fazer os ajustes e definir como se dará o monitoramento. A Figura abaixo apresenta as opções.
As configurações selecionadas para o projeto scarefault:
- Build only if .travis.yml is present: define que o Travis CI só será executado se existir no projeto um arquivo .travis.yml.
- Build pushes: define que o Travis CI fará um build toda vez que observar um push efetuado.
- Build pull request: define que toda que vez que um pull request for efetuado.
Usando o Travis CI
[editar | editar código-fonte]Tendo as configurações sido efetuadas, agora deve-se escrever o script de execução do Travis CI. Na Figura abaixo consta o script definido para o scarefault.
sudo: required
language: cpp
compiler:
- gcc
before_install:
- sudo cp /etc/apt/sources.list /etc/apt/sources.list.d/trusty.list
- sudo sed -i 's/precise/trusty/g' /etc/apt/sources.list.d/trusty.list
- if [ "$CXX" == "g++" ]; then sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; fi
- sudo apt-get update -qq -y
install:
- if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.8; fi
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8"; fi
- sudo apt-get install -qq -y --force-yes flexc++ bisonc++
script:
- make
notifications:
email:
thaianefbraga@gmail.com
tomaz.r.martins@gmail.com
O script acima é dividido em seções, conforme o padrão especificado e esperado pelo Travis CI. As principais seções são:
- language: define a linguagem de programação do código fonte contido no repositório.
- before_install: especifica as ações que devem ser efetuadas antes das instalações principais. É onde se faz requisições para que haja a instalação de dependências para a instalação de pacotes essenciais para a execução da aplicação.
- install: requisita a instalação de pacotes essenciais para a execução do código fonte contido no repositório.
- script: apresenta ao Travis CI quais os comandos devem ser executados para que ocorra o build e a execução dos testes.
Abaixo evidencia-se as demais seções do script:
- compiler: especifica qual o compilador que será usado. Como opções tem o gcc e o clang.
- notifications: aponta a forma que deve-se usar para notificações, no caso email. Em seguida, diz quais os emails que deverão ser notificados. Por default, será notificado quando ocorrer alguma alteração no build.
Um detalhe interessante de citar é a variável de ambiente $CXX, que aparece nas linhas 11, 15 e 16. Ela representa a chamada para o compilador, quando a linguagem é C++. Para a linguagem de programação C, usa-se a variável $CC. A linha 10, da mesma forma que as linhas 15 e 16, fazem uma verificação de qual o compilador que está configurado. Sendo o g++, ele faz a adição de um repositório e a instalação da versão 4.8 do g++, na linha 15, e na linha16 altera a variável $CXX para fazer a chamada para o g++-4.8, ao invés da g++-4.6, que é a default no Travis CI.
Dificuldades e Soluções
[editar | editar código-fonte]Um ponto que vale ser considerado na criação do script, apresentado acima, foi a configuração para a instalação das ferramentas Flexc++ e Bisonc++. Utilizando a linha de comando utilizada na máquina local o Travis CI não reconheceu os pacotes solicitados, Flexc++ e Bisonc++. Procurou-se na documetação do Travis CI, mas nada foi encontrado a respeito.
A estratégia foi buscar respostas no repositório oficial do Travis CI no GitHub. Encontrou-se um issue a respeito, dizendo que já estava solucionada. A solução encontra-se abaixo:
addons:
apt:
sources:
- 'debian-sid'
packages:
- flexc++
Fez-se tentativas utilizando-a, no entanto, ao utilizar a solução proposta, não se obteve sucesso. Procurando-se no próprio GitHub se alguém teve os mesmos problemas, encontrou-se a solução dispostas no script anterior, na linha 17. Ela encontra-se destacada abaixo:
sudo apt-get install -qq -y --force-yes flexc++ bisonc++
Com essa solução a instalação obteve êxito.
Resultado Final
[editar | editar código-fonte]Com a adição do arquivo .travis.yml na raíz do projeto e as configurações efetuadas, no momento de um push o Travis CI executará o script especificado. No caso do scarefault, até o momento, o Travis CI faz a execução de um build. A Figura abaixo mostra o que o Travis CI apresenta no momento em que um push é executado:
Ele aponta em qual branch foi efetuado o push, o autor e o link para o commit no repositório do GitHub. A Figura abaixo mostra o resultado final, após execução completa do script do Travis CI:
A Figura ao lado mostra o resultado da execução das linhas de comando especificadas no
script do .travis.yml. Dessa forma, é possível ter um log do que ocorreu durante a execução e, caso ocorra um erro, ser possível ver em qual momento isso ocorreu.
[Extra] Empacotamento Debian
[editar | editar código-fonte]Como referência para essa tentativa de empacotamento foi utilizado o vídeo que consta no endereço <https://www.youtube.com/watch?v=aW3wdkvmMZ0&list=LLQ5qTRiOotfpROXc8oPknLA&index=15>. Esse tutorial foi produzido por Eriberto Mota, um desenvolvedor Debian.
Criando Jaula SID
[editar | editar código-fonte]Para a construção de um pacote .deb é necessário a criação de um ambiente seguro e limpo de interferências, na medida em que há a necessidade de manter apenas os recursos essenciais do pacote a ser produzido disponíveis. Assim sendo, prepara-se uma jaula onde poder-se-á trabalhar no processo de criação do pacote.
Para isso, cria-se um novo diretório. Em seguida, instala-se uma unstable do Debian nessa pasta, por meio do comando:
$ debootstrap sid [diretorio da jaula] http://ftp.br.debian.org/debian
Com a finalização da instalação, dentro do diretório da jaula, as seguinte pastas devem estar presentes:
Feito isso, o enjaulamento do ambiente de produção do pacote só estará completo quando o comando abaixo for executado:
$ chroot [diretorio da jaula]
Dentro da jaula é necessário a criação de um \proc para o bom funcionamento da jaula. O \proc é um sistema virtual de arquivos. Para isso, deve-se efetuar o os comandos a seguir:
$ echo proc /proc proc defaults 0 0 >> /etc/fstab
$ mount /proc
Há outra configuração a ser feita antes de declarar a jaula completa. É a adição do seguinte conteúdo ao final do arquivo bash.bashrc:
alias ls='ls color=auto'
alias tree='tree aC'
alias debuildsa='dpkgbuildpackage sa ksua_chave_gpg'
alias uscan='uscan verbose report'
alias aptcache='LANG=C aptcache'
alias man='LANG=C man'
export DEBFULLNAME="seu_nome_completo_sem_acentos/cedilha"
export DEBEMAIL="seu_email"
export EDITOR=mcedit
export QUILT_PATCHES=debian/patches
export QUILT_DIFF_ARGS="notimestamps noindex pab"
export QUILT_REFRESH_ARGS="notimestamps noindex pab"
export PS1='JAULASID\u@\h:\w\$ '
mount /proc
Isso é feito pelas linhas de comando listadas abaixo:
$ wget http://bit.ly/bash-rc-txt
$ cat bash-rc-txt >> /etc/bash.bashrc
Pronto. A jaula está configurada.
Instalação de Pacotes Essenciais
[editar | editar código-fonte]Alguns pacotes são importantes para o empacotamento. É importante ressaltar que não é aconselhável instalar na jaula qualquer pacote. Ela deve estar o mais limpa possível. Os pacotes aqui listados são importantes para auxiliar o empacotamento. Use o comando:
$ apt-get install blhc devscripts dh-make dput-ng locales mc quilt renameutils spell tree
É necessário agora a reconfiguração de alguns pacotes. Para isso, utilize a seguinte linha de comando:
$ dpkg-reconfigure locales tzdata
O primeiro é para a configuração de idioma, enquanto que o segundo para a configuração do fuso horário. Executado esse comando, a seguinte tela aparece:
Nesta tela marque as opções apresentadas acima. Lembrando que para marcar uma opção utilize a barra de espaço, pois a tecla ENTER é utilizada para dar um OK.
Na sequência, será efetuada a configuração do fuso horário. As seguintes telas serão apresentadas:
Na Figura acima faz-se a seleção da região.
Na Figura acima faz-se a seleção do fuso horário. Selecionou-se o fuso horário de São Paulo, o mesmo de Brasília.
Após a instalação destes pacotes é necessário a remoção dos .deb gerados dentro da jaula SID. para isso executa-se o comando:
$ apt-get clean
Configuração do Lintian
[editar | editar código-fonte]O litian é uma ferramenta que auxilia em encontrar possíveis erros durante o empacotamento. Para configurar essa ferramenta segue-se os passos a seguir:
- Abrir o arquivo /etc/litianrc.
- Habilitar as linhas:
- display-info = yes
- pedantic = yes
- display-experimental = yes
Com a finalização dessas configurações a basta dar o comando exit para sair da jaula e finalizar o processo de instalação e configuração do ambiente.
Inserção das Chaves GPG RSA 4096
[editar | editar código-fonte]Agora é necessário inserir as chaves GPG RSA, de tamanho 4096, na jaula SID. Caso você ainda não tenha uma chave GPG, segue os procedimentos para criar uma.
Criação de uma chave GPG
[editar | editar código-fonte]Execute o comando abaixo, fora da jaula SID:
$ gpg --gen-key
Siga as instruções passadas pelo gerador. Ao final do processo, você terá a sua chave GPG e o fingerprint dela. Lembre-se do fingerprint. Outra informação importante, é que durante as instruções do gerador, ele solicita o tamanho da chave GPG. Requisite 4096 como tamanho.
Exportando a chave GPG para a jaula SID
[editar | editar código-fonte]Tendo já a sua chave GPG gerada, é necessário exportá-la para dentro da jaula SID. Para isso, segue os comandos:
# Exporta as chaves, pública e privada, para os documentos especificados
#
$ gpg -a --export numero-da-chave > numero-da-chave.pub
$ gpg -a --export-scret-keys numero-da-chave > numero-da-chave.key
# Depois, move-se os documentos contendo as chaves para dentro da jaula SID
#
$ mv numero-da-chave.pub diretorio-da-jaula-SID
$ mv numero-da-chave.key diretorio-da-jaula-SID
Importando chave GPG
[editar | editar código-fonte]Para importar a chave GPG, siga os comandos:
# Enjaule-se na jaula SID
#
$ chroot diretorio-da-jaula-SID
# Importe as chaves GPG
#
$ gpg --import numero-da-chave.pub numero-da-chave.key
# Remova os documentos contendo as chaves GPG
#
$ rm numero-da-chave-pub numero-da-chave.key
Para finalizar, edit o arquivo /etc/devscripts.conf, descomentando a linha que contem DEBSIGN_KEYID e adicione a sua chave ao final dela, assim:
DEBSIGN_KEYID=numero-da-chave
Debianização e Empacotamento
[editar | editar código-fonte]Tendo a jaula sid criada e configurada, agora parte-se para a segunda parte, o empacotamento em si.
Debianização
[editar | editar código-fonte]Antes de empacotar o programa é necessário gerar alguns arquivos exigidos pela política do Debian para pacotes, bem como configurá-los. para isso, é necessário fazer uma cópia de um .tar.gz do programa para dentro da jaula sid. Para compactar utilizando o programa tar, use o comando:
# Para compactar um diretório
#
$ tar -zcf nome-do-programa.tar.gz diretorio-do-programa
Feito isso, inseri-se o arquivo compactado dentro da jaula sid.
Um passo importante agora é a verificação do licenciamento do software a ser empacotado. Há duas formas de fazê-lo: por meio do comando licensecheck ou fazendo uma busca pelo egrep. Embora o uso do licensecheck seja mais rápida, o uso do egrep é mais eficaz, pois a busca é mais profunda.
Observada a licença do software e compatibilidade com suas dependências, agora é necessário fazer a "debianização". Para isso, segue o comando:
$ dh_make -f caminho-para-tar.gz-do-software -c licença-do-software
Para o Scarefault, o comando foi executado da seguinte forma:
$ dh_make -f ../scarefault-1.0.0.tar.gz -c gpl3
Feito isso, é solicitado que defina-se o tipo de binário a ser criado. Para o Scarefault foi selecionado o tipo simples. Feito isso, a tela obtida como resultado consta abaixo:
É necessário que alguns arquivos sejam configurados. Estes arquivos estão presentes no diretório chamado de "debian". Dando uma olhada nesse diretório, tem-se:
É importante retirar os arquivos desnecessários. Assim, após a retirada do que não foi necessário para o caso do Scarefault, os arquivos que sobraram foram:
- changelog
- compat
- control
- copyright
- rules
- source
Finalizada essa etapa, temos o Scarefault "debianizado". Agora deve-se partir para o empacotamento.
Empacotamento
[editar | editar código-fonte]Essa etapa inicia-se com a edição dos arquivos que sobraram no diretório debian.
- changelog: listagem das alterações feitas no empacotamento.
- compat: define o nível de compatibilidade do debhelper. Não se deve mexer. Ele está configurado já com o nível atual, 9.
- copyright: define a licença do arquivo a ser empacotado e do empacotamento.
- rules: é como um makefile. A princípio, basta tirar o lixo que são alguns comentários desnecessários.
- control: arquivo que descreve algumas características do software a ser empacotado. O arquivo abaixo é o control final do Scarefault.
Source: scarefault
Section: development
Priority: optional
Maintainer: Tomaz Felipe Rodrigues Martins <tomaz.r.martins@gmail.com>
Build-Depends: debhelper (>=9)
Standard-Version: 3.9.8
Homepage: https://github.com/Scarefault/scarefault
Package: scarefault
Architecture: any
Depends: $(shlibs:Depends), $(misc:Depends), g++, flexc++, bisonc++
Description: Framework to generation of unit test
Aimed at developing a framework that aims to semi automated generation of unit
tests. In essence it is formed by a test generator, written in C ++.