Skip to content

Instantly share code, notes, and snippets.

@edusantana
Last active January 2, 2016 10:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save edusantana/8291576 to your computer and use it in GitHub Desktop.
Save edusantana/8291576 to your computer and use it in GitHub Desktop.
C Arquivo Learning Test
#include <stdio.h>
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
/*
* O último padrão adotado para a Linguagem C foi publicado
* pela ISO em 12/8/2011 no documento: ISO/IEC 9899:2011.
* Você pode consultar gratuitamente a última versão pública
* do documento em:
* http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
*
* Fonte: http://www.open-std.org/jtc1/sc22/wg14/www/standards.html
*/
void fechaArquivoSeNecessario(FILE* arquivo) {
if (arquivo != NULL) {
fclose(arquivo);
}
}
bool verificaAquivoExiste(char* nomeDoArquivo) {
/* 7.21.5.3 The fopen function
Opening a file with read mode ('r' as the first character in the mode argument) fails if the file does not exist or cannot be read.
*/
FILE* arquivo = fopen(nomeDoArquivo, "r");
fechaArquivoSeNecessario(arquivo);
return arquivo != NULL;
}
void testVerificaAquivoExiste() {
assert(verificaAquivoExiste("arquivos_test.c"));
assert(!verificaAquivoExiste("arquivo_inexistente.txt"));
}
void verificaStringIguais(char* stringA, char* stringB) {
assert(strcmp(stringA, stringB) == 0);
}
bool finalDoArquivoOuErro(FILE* arquivo) {
// chegou ao fim do arquivo ou erro ao acessar o arquivo
return feof(arquivo) || ferror(arquivo);
}
bool arquivoAbertoComSucesso(FILE* arquivo) {
return arquivo != NULL;
}
int lerTamanhoDoArquivo(FILE* arquivo) {
fseek(arquivo, 0, SEEK_END); // vai para o final do arquivo
int tamanho = ftell(arquivo); // pega posição atual (final)
fseek(arquivo, 0, SEEK_SET); // volta para o início
return tamanho;
}
char* lerConteudoDeArquivoArberto(FILE* arquivo) {
int tamanhoDoArquivo = lerTamanhoDoArquivo(arquivo);
char* conteudo = malloc(tamanhoDoArquivo + 1); // 7.22.3.4 The malloc function
fread(conteudo, tamanhoDoArquivo, 1, arquivo); // 7.21.8.1 The fread function
return conteudo;
}
char* lerConteudoDoArquivo(char* nomeDoArquivo) {
char* conteudo;
FILE* arquivo = fopen(nomeDoArquivo, "r");
if (arquivoAbertoComSucesso(arquivo)) {
conteudo = lerConteudoDeArquivoArberto(arquivo);
fclose(arquivo); // fecha arquivo
} else {
conteudo = NULL;
}
return conteudo;
}
char* vogais = "aeiou\n";
char* ARQUIVO_VOGAIS = "vogais.txt";
void testLerConteudoDeArquivo() {
assert(verificaAquivoExiste(ARQUIVO_VOGAIS)); // pré-condição
char* conteudo = lerConteudoDoArquivo(ARQUIVO_VOGAIS);
verificaStringIguais(conteudo, vogais);
}
FILE *fluxoDeSaida;
FILE* saida() {
return fluxoDeSaida == NULL ? stdout : fluxoDeSaida;
}
void imprimeMensagem(char* mensagem) {
fprintf(saida(), "%s", mensagem);
}
FILE * criarArquivoTemporario(){
return tmpfile();
}
void substituiFluxoDeSaida() {
/* http://c-faq.com/stdio/undofreopen.html */
// cria um arquivo temporario e salva
// ponteiro do arquivo em fluxoDeSaida
fluxoDeSaida = criarArquivoTemporario();
}
void restauraFluxoDeSaida() {
fclose(fluxoDeSaida);
fluxoDeSaida = NULL;
}
char* lerMensagemEscritaNoFluxoDeSaida() {
return lerConteudoDeArquivoArberto(saida());
}
void verificaQueMensagemFoiImpressa(char* mensagem) {
char* mensagemEscrita = lerMensagemEscritaNoFluxoDeSaida();
verificaStringIguais(mensagemEscrita, mensagem);
}
void testVerificaImpressaoDeMensagem() {
substituiFluxoDeSaida();
char* mensagem = "oi!";
imprimeMensagem(mensagem);
verificaQueMensagemFoiImpressa(mensagem);
restauraFluxoDeSaida();
}
#define TAMANHO_DO_NOME 255
typedef struct jogador_registro{
char nome_tamanho_fixo[TAMANHO_DO_NOME];
int pontuacao;
} Jogador ;
typedef struct jogador_com_ponteiro{
char* nome;
int pontuacao;
} JogadorNomeComPonteiro;
Jogador* criaUmRegistroJogador(){
Jogador* jogador = calloc(1, sizeof(Jogador));
jogador->pontuacao = rand(); // 7.22.2.1 The rand function
strcpy(jogador->nome_tamanho_fixo, "Nome qualquer aqui");
return jogador;
}
char* nomeQualquer = "Nome qualquer";
JogadorNomeComPonteiro* criaUmRegistroJogadorNomeComPonteiro(){
JogadorNomeComPonteiro* jogador = calloc(1, sizeof(JogadorNomeComPonteiro));
jogador->pontuacao = rand(); // 7.22.2.1 The rand function
jogador->nome = nomeQualquer;
return jogador;
}
const int QUANTIDADE_DE_REGISTROS = 1;
FILE* salvaRegistroJogadorEmArquivo(Jogador *jogador){
FILE * arquivo = criarArquivoTemporario();
fwrite(jogador, sizeof(Jogador), QUANTIDADE_DE_REGISTROS, arquivo);
fflush(arquivo);
return arquivo;
}
void escreveTamanhoDoNome(JogadorNomeComPonteiro *jogador, FILE* arquivo){
int tamanho = strlen(jogador->nome);
fwrite(&tamanho, sizeof(int), 1, arquivo);
}
void escreveStringNome(JogadorNomeComPonteiro *jogador, FILE* arquivo){
fwrite(jogador->nome, sizeof(char), strlen(jogador->nome), arquivo);
}
void escrevePontuacao(JogadorNomeComPonteiro *jogador, FILE* arquivo){
fwrite(&jogador->pontuacao, sizeof(jogador->pontuacao), 1, arquivo);
}
FILE* salvaRegistroJogadorNomeComPonteiroEmArquivo(JogadorNomeComPonteiro *jogador){
FILE * arquivo = criarArquivoTemporario();
escreveTamanhoDoNome(jogador, arquivo);
escreveStringNome(jogador, arquivo);
escrevePontuacao(jogador, arquivo);
return arquivo;
}
Jogador* lerJogadorDeArquivo(FILE* arquivo){
Jogador* jogador = calloc(1, sizeof(Jogador));
fseek(arquivo, 0, SEEK_SET); // volta para o início
fread(jogador, sizeof(Jogador), QUANTIDADE_DE_REGISTROS, arquivo);
return jogador;
}
void atualizaNome(JogadorNomeComPonteiro *jogador, FILE* arquivo){
int tamanhoDoNome;
fread(&tamanhoDoNome, sizeof(int), 1, arquivo);
jogador->nome = calloc(sizeof(char), tamanhoDoNome+1); // +1 para o \0
fread(jogador->nome, sizeof(char), tamanhoDoNome, arquivo);
}
void atualizaPontuacao(JogadorNomeComPonteiro *jogador, FILE* arquivo){
fread(&jogador->pontuacao, sizeof(int), 1, arquivo);
}
JogadorNomeComPonteiro* lerJogadorNomeComPonteiroDeArquivo(FILE* arquivo){
fseek(arquivo, 0, SEEK_SET); // volta para o início
JogadorNomeComPonteiro* jogador = calloc(1, sizeof(Jogador));
atualizaNome(jogador, arquivo);
atualizaPontuacao(jogador, arquivo);
return jogador;
}
void verificaQueRegistrosPossuemOMesmoConteudo(Jogador* jogador, Jogador* jogadorLidoDoArquivo){
// verifica que apontam para posições diferentes
assert(jogador!=jogadorLidoDoArquivo);
assert(jogador->pontuacao == jogadorLidoDoArquivo->pontuacao);
verificaStringIguais(jogador->nome_tamanho_fixo, jogadorLidoDoArquivo->nome_tamanho_fixo);
}
void verificaQueRegistrosJogadorNomeComPonteiroPossuemOMesmoConteudo(JogadorNomeComPonteiro* jogador, JogadorNomeComPonteiro* jogadorLidoDoArquivo){
assert(jogador!=jogadorLidoDoArquivo);
assert(jogador->pontuacao == jogadorLidoDoArquivo->pontuacao);
verificaStringIguais(jogador->nome, jogadorLidoDoArquivo->nome);
}
void testSalvaERecuperaRegistroDeTamanhoFixoEmArquivo(){
Jogador* jogador = criaUmRegistroJogador();
assert(sizeof(jogador->nome_tamanho_fixo) == TAMANHO_DO_NOME);
FILE* arquivo = salvaRegistroJogadorEmArquivo(jogador);
Jogador* jogadorLidoDoArquivo = lerJogadorDeArquivo(arquivo);
verificaQueRegistrosPossuemOMesmoConteudo(jogador, jogadorLidoDoArquivo);
fechaArquivoSeNecessario(arquivo);
free(jogador);
free(jogadorLidoDoArquivo);
}
void testSalvaERecuperaRegistroTamanhoVariavel(){
JogadorNomeComPonteiro* jogador = criaUmRegistroJogadorNomeComPonteiro();
FILE* arquivo = salvaRegistroJogadorNomeComPonteiroEmArquivo(jogador);
JogadorNomeComPonteiro* jogadorLidoDoArquivo = lerJogadorNomeComPonteiroDeArquivo(arquivo);
verificaQueRegistrosJogadorNomeComPonteiroPossuemOMesmoConteudo(jogador, jogadorLidoDoArquivo);
fechaArquivoSeNecessario(arquivo);
free(jogador);
free(jogadorLidoDoArquivo);
}
void apagaArquivoSeExistir(char* nomeDoArquivo){
remove(nomeDoArquivo);
assert(!verificaAquivoExiste(nomeDoArquivo));
}
char* CONTEUDO_ORIGINAL = "CONTEUDO-ORIGINAL com quebra de linha\n";
void criaArquivoComConteudo(char* nomeDoArquivo){
FILE* arquivo = fopen(nomeDoArquivo, "w");
fputs(CONTEUDO_ORIGINAL, arquivo);
fclose(arquivo);
}
void abreArquivoModoAppendAdicionaMensagem(char* nomeDoArquivo, char* mensagem){
FILE* arquivo = fopen(nomeDoArquivo, "a"); // modo append
fputs(mensagem, arquivo);
fclose(arquivo);
}
int TAMANHO_MAXIMO_DA_LINHA = 1024;
void verificaQueMensagemFoiAdicionadaAoFinalDoArquivo(char* nomeDoArquivo, char* mensagem){
FILE* arquivo = fopen(nomeDoArquivo, "r");
char* primeiraLinha = calloc(TAMANHO_MAXIMO_DA_LINHA, sizeof(char));
char* segundaLinha = calloc(TAMANHO_MAXIMO_DA_LINHA, sizeof(char));
fgets(primeiraLinha, TAMANHO_MAXIMO_DA_LINHA, arquivo);
fgets(segundaLinha, TAMANHO_MAXIMO_DA_LINHA, arquivo);
verificaStringIguais(primeiraLinha, CONTEUDO_ORIGINAL);
verificaStringIguais(segundaLinha, mensagem);
}
char* NOME_DO_ARQUIVO = "meu-arquivo.txt";
char* MENSAGEM = "mensagem-qualquer com quebra de linha\n";
void testAdicionarConteudoEmArquivo(){
apagaArquivoSeExistir(NOME_DO_ARQUIVO);
criaArquivoComConteudo(NOME_DO_ARQUIVO);
abreArquivoModoAppendAdicionaMensagem(NOME_DO_ARQUIVO, MENSAGEM);
verificaQueMensagemFoiAdicionadaAoFinalDoArquivo(NOME_DO_ARQUIVO, MENSAGEM);
}
void criaArquivoEscreveDoisNumerosSeparadosPorEspaco (
char* nomeDoArquivo, int primeiroNumero, int segundoNumero){
FILE* arquivo = fopen(nomeDoArquivo, "w");
fprintf(arquivo, "%d %d",primeiroNumero, segundoNumero);
fclose(arquivo);
}
void lerDeArquivoDoisNumerosSeparadosPorEspaco(
char* nomeDoArquivo, int *primeiroValor, int *segundoValor){
FILE *arquivo = fopen(nomeDoArquivo, "r");
fscanf(arquivo, "%d %d", primeiroValor, segundoValor);
fclose(arquivo);
}
void verificaQueNumerosEscritosSaoIguaisAosLidos(
int primeiroNumero,int segundoNumero,int primeiroValor,int segundoValor){
assert(primeiroNumero==primeiroValor);
assert(segundoNumero==segundoValor);
}
void testEscrevendoConteudoFormatado(){
char* nomeDoArquivo = "numeros.txt";
int primeiroNumero = rand();
int segundoNumero = rand();
criaArquivoEscreveDoisNumerosSeparadosPorEspaco(nomeDoArquivo,primeiroNumero, segundoNumero);
int primeiroValor;
int segundoValor;
lerDeArquivoDoisNumerosSeparadosPorEspaco(nomeDoArquivo,&primeiroValor, &segundoValor);
verificaQueNumerosEscritosSaoIguaisAosLidos(primeiroNumero,segundoNumero,primeiroValor,segundoValor);
apagaArquivoSeExistir(nomeDoArquivo);
}
int main() {
testVerificaAquivoExiste();
testLerConteudoDeArquivo();
testVerificaImpressaoDeMensagem();
// Arquivos com Registros
testSalvaERecuperaRegistroDeTamanhoFixoEmArquivo();
testSalvaERecuperaRegistroTamanhoVariavel();
testAdicionarConteudoEmArquivo();
testEscrevendoConteudoFormatado();
return 0;
}
CC=gcc
CFLAGS=-Wall -g -std=c1x
all: arquivos_test
arquivos_test: arquivos_test.c
$(CC) $(CFLAGS) arquivos_test.c -o arquivos_test
test_all:
./arquivos_test
clean:
rm -rf arquivos_test
@edusantana
Copy link
Author

Este gist é o meu Learning Test sobre Arquivos em C.

O Learning Test tem um propósito diferente dos testes unitários ou de integração. Seu propósito é registrar o conhecimento sobre conteúdos específicos que você está estudando.

O teste escrito desta forma deixa claro qual o resultado esperado para as funções, além de mostrar como elas interagem entre si.

A leitura deste arquivo de teste se inicia na função main(), então seguimos o fluxo das funções que são executadas.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment