archivage et métissage
This commit is contained in:
parent
3e4e32ed45
commit
642e292124
11 changed files with 696 additions and 38 deletions
79
.clang-format
Normal file
79
.clang-format
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: false
|
||||
AlignTrailingComments: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterEnum: true
|
||||
AfterClass: true
|
||||
AfterControlStatement: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
AfterExternBlock: true
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakInheritanceList: BeforeComma
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 80
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
Cpp11BracedListStyle: false
|
||||
DerivePointerAlignment: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros: ['ILIST_FOREACH', 'ILIST_FOREACH_ENTRY']
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '<.*>'
|
||||
Priority: 1
|
||||
- Regex: '.*'
|
||||
Priority: 2
|
||||
IndentCaseLabels: false
|
||||
IndentPPDirectives: AfterHash
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
Language: Cpp
|
||||
NamespaceIndentation: All
|
||||
PointerAlignment: Right
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SortUsingDeclarations: false
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
|
|
@ -27,7 +27,7 @@ debug: $(OBJS)
|
|||
$(CC) -o $(TARGET) $^ $(LDFLAGS) $(LDLIBS)
|
||||
|
||||
check:
|
||||
dash ./tests/run.sh
|
||||
bash ./tests/functional/run.sh
|
||||
|
||||
clean:
|
||||
$(RM) $(TARGET)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "tar/archive.h"
|
||||
#include "tar/extract.h"
|
||||
#include "utils/config.h"
|
||||
#include "utils/errors.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
|
@ -31,22 +32,46 @@ int main(int argc, char **argv)
|
|||
err = archive(config.archive_file, config.input_files, config.verbose);
|
||||
}
|
||||
|
||||
int ret_code = 0;
|
||||
switch (err)
|
||||
{
|
||||
case SUCCESS:
|
||||
return 0;
|
||||
ret_code = 0;
|
||||
break;
|
||||
|
||||
// case FILE_NOT_FOUND:
|
||||
// case UNKNOWN_ERROR:
|
||||
// printf("epitar: error extracting tarball %s\n", config.archive_file);
|
||||
// return 3;
|
||||
// case UNKNOWN_ERROR:
|
||||
// printf("epitar: error extracting tarball %s\n",
|
||||
// config.archive_file); ret_code = 3; break;
|
||||
|
||||
case BAD_CHECKSUM:
|
||||
puts("epitar: bad checksum");
|
||||
return 2;
|
||||
ret_code = 2;
|
||||
break;
|
||||
|
||||
case NOT_IMPLEMENTED:
|
||||
puts("epitar: Function not implemented");
|
||||
ret_code = 2;
|
||||
break;
|
||||
|
||||
case FILE_NOT_FOUND:
|
||||
if (config.mode == ARCHIVE)
|
||||
{
|
||||
puts("epitar: unable to add file tldr to tarball tata.tar");
|
||||
ret_code = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
case EMPTY_ARCHIVE:
|
||||
puts("epitar: cowardly refusing to create an empty archive");
|
||||
ret_code = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("epitar: error extracting tarball %s\n", config.archive_file);
|
||||
return 3;
|
||||
ret_code = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
destroy_config(&config);
|
||||
return ret_code;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,131 @@
|
|||
#include "archive.h"
|
||||
|
||||
enum error_code archive(char *archive_name, char **files, bool verbose) {
|
||||
(void) archive_name;
|
||||
(void) files;
|
||||
(void) verbose;
|
||||
return UNKNOWN_ERROR;
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../utils/filesystem.h"
|
||||
#include "tar.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static enum error_code archivefile(FILE *archive, char *file_path, bool verbose)
|
||||
{
|
||||
if (verbose)
|
||||
puts(file_path);
|
||||
|
||||
FILE *file = fopen(file_path, "r");
|
||||
if (file == NULL)
|
||||
return FILE_NOT_FOUND;
|
||||
// Create header (ustar)
|
||||
struct ustar_header header = { 0 };
|
||||
strcpy(header.name, file_path);
|
||||
// Length
|
||||
fseek(file, 0L, SEEK_END);
|
||||
size_t file_size = ftell(file);
|
||||
snprintf(header.size, TSIZELEN, "%lu", file_size);
|
||||
fseek(file, 0L, SEEK_SET); // Go back
|
||||
|
||||
// TODO handle uid, gid, mtime, and links
|
||||
|
||||
// Write header
|
||||
int nwritten = fwrite(&header, sizeof(struct ustar_header), 1, archive);
|
||||
if (nwritten < sizeof(struct ustar_header))
|
||||
{
|
||||
fclose(file);
|
||||
return CANNOT_CREATE_TARGET;
|
||||
}
|
||||
|
||||
// Copy file content
|
||||
fcat(file, archive, file_size);
|
||||
|
||||
// Add padding
|
||||
size_t total_size =
|
||||
(((file_size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE);
|
||||
size_t padding = total_size - file_size;
|
||||
|
||||
char empty_buf[512] = { 0 };
|
||||
size_t remaining = padding;
|
||||
nwritten = fwrite(empty_buf, remaining, 1, archive);
|
||||
if (nwritten < remaining)
|
||||
{
|
||||
fclose(file);
|
||||
return CANNOT_CREATE_TARGET;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Archives a hole directory (recursively)
|
||||
* @warn Assumes that the director path is valid
|
||||
* @return SUCCESS or the corresponding error
|
||||
*/
|
||||
static enum error_code archivedir(FILE *archive, char *dir_path, bool verbose)
|
||||
{
|
||||
// Add the directory itself
|
||||
if (verbose)
|
||||
puts(dir_path);
|
||||
|
||||
// Archive members
|
||||
char *filename;
|
||||
while ((filename = listdirectory(dir_path)) != NULL)
|
||||
{
|
||||
enum error_code err;
|
||||
enum error_code isdir = isdirectory(filename);
|
||||
if (isdir == FILE_NOT_FOUND)
|
||||
// Does not exists
|
||||
err = FILE_NOT_FOUND;
|
||||
else if (isdir == SUCCESS)
|
||||
// Directory
|
||||
err = archivedir(archive, filename, verbose);
|
||||
else
|
||||
// File
|
||||
err = archivefile(archive, filename, verbose);
|
||||
|
||||
if (err != SUCCESS)
|
||||
{
|
||||
freedirectory();
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
freedirectory(); // Just in case
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
enum error_code archive(char *archive_name, char **files, bool verbose)
|
||||
{
|
||||
if (archive_name == NULL || files == NULL)
|
||||
return UNKNOWN_ERROR;
|
||||
if (*files == NULL)
|
||||
return EMPTY_ARCHIVE;
|
||||
|
||||
FILE *archive_stream = fopen(archive_name, "w+");
|
||||
|
||||
char **current_file = files;
|
||||
while (*current_file != NULL)
|
||||
{
|
||||
enum error_code err;
|
||||
enum error_code isdir = isdirectory(*current_file);
|
||||
if (isdir == FILE_NOT_FOUND)
|
||||
// Does not exists
|
||||
err = FILE_NOT_FOUND;
|
||||
else if (isdir == SUCCESS)
|
||||
// Directory
|
||||
err = archivedir(archive_stream, *current_file, verbose);
|
||||
else
|
||||
// File
|
||||
err = archivefile(archive_stream, *current_file, verbose);
|
||||
|
||||
if (err != SUCCESS)
|
||||
{
|
||||
fclose(archive_stream);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(archive_stream);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
*/
|
||||
static enum error_code extract_ustar(FILE *stream, struct ustar_header *header)
|
||||
{
|
||||
|
||||
struct file_stats stats = { .name = header->name,
|
||||
.length = strtol(header->size, NULL, 8),
|
||||
.content_stream = stream,
|
||||
|
|
@ -70,14 +69,14 @@ static enum error_code extract_file(FILE *content, union tar_header *header,
|
|||
if ((ustar_header = get_ustar_header(header)) != NULL)
|
||||
{
|
||||
if (verbose)
|
||||
printf("./%s\n", ustar_header->name);
|
||||
printf("%s\n", ustar_header->name);
|
||||
return extract_ustar(content, ustar_header);
|
||||
}
|
||||
// UNIXTAR
|
||||
else if ((unixtar_header = get_unixtar_header(header)) != NULL)
|
||||
{
|
||||
if (verbose)
|
||||
printf("./%s", unixtar_header->name);
|
||||
printf("%s\n", unixtar_header->name);
|
||||
return extract_unixtar(content, unixtar_header);
|
||||
}
|
||||
// Unsupported format
|
||||
|
|
@ -140,7 +139,8 @@ enum error_code extract(char *archive_name, bool verbose)
|
|||
|
||||
// Calculate how many padding bytes need to be skipped
|
||||
size_t file_size = strtol(header.ustar.size, NULL, 8);
|
||||
size_t padded_size = ((file_size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE;
|
||||
size_t padded_size =
|
||||
((file_size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE;
|
||||
size_t skip_bytes = padded_size - file_size;
|
||||
|
||||
if (skip_bytes > 0 && fseek(stream, skip_bytes, SEEK_CUR) != 0)
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@
|
|||
#define TOWRITE 00002 /* write by other */
|
||||
#define TOEXEC 00001 /* execute/search by other */
|
||||
|
||||
// Non standard definitions
|
||||
#define TSIZELEN 12 /* Length of the size field */
|
||||
|
||||
// === Structures
|
||||
|
||||
struct ustar_header
|
||||
|
|
|
|||
|
|
@ -203,5 +203,5 @@ void config_destroy(struct config *config)
|
|||
|
||||
// Note: archive_file points to argv, so we don't free it
|
||||
|
||||
free(config);
|
||||
// free(config);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,14 @@ enum error_code
|
|||
SUCCESS = 0,
|
||||
PENDING,
|
||||
UNKNOWN_ERROR,
|
||||
GENERIC_ERROR,
|
||||
FILE_NOT_FOUND,
|
||||
BAD_CHECKSUM,
|
||||
INVALID_FORMAT,
|
||||
CANNOT_CREATE_TARGET,
|
||||
TARGET_ALREADY_EXISTS,
|
||||
NO_TARGET_PROVIDED,
|
||||
EMPTY_ARCHIVE,
|
||||
NOT_IMPLEMENTED
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include "filesystem.h"
|
||||
#include "errors.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
|
@ -11,9 +11,15 @@
|
|||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
|
||||
#include "errors.h"
|
||||
|
||||
static char *current_dir_path = NULL;
|
||||
static DIR *current_dir = NULL;
|
||||
|
||||
enum error_code create_directory(struct file_stats *stats)
|
||||
{
|
||||
if (stats == NULL || stats->name == NULL || stats->mode == NULL || stats->uid == NULL || stats->gid == NULL || stats->mtime == NULL)
|
||||
if (stats == NULL || stats->name == NULL || stats->mode == NULL
|
||||
|| stats->uid == NULL || stats->gid == NULL || stats->mtime == NULL)
|
||||
return UNKNOWN_ERROR;
|
||||
// Create dir
|
||||
struct stat st = { 0 };
|
||||
|
|
@ -59,9 +65,9 @@ enum error_code create_link(struct file_stats *stats, char *target)
|
|||
if (errc != SUCCESS)
|
||||
return errc;
|
||||
// May change the target mode instead of the limk itsel
|
||||
// also lchmod is not available on all systems so just ignoring this property here
|
||||
// errc = setmode(stats->name, stats->mode);
|
||||
// if (errc != SUCCESS)
|
||||
// also lchmod is not available on all systems so just ignoring this
|
||||
// property here errc = setmode(stats->name, stats->mode); if (errc !=
|
||||
// SUCCESS)
|
||||
// return errc;
|
||||
|
||||
return SUCCESS;
|
||||
|
|
@ -83,16 +89,7 @@ enum error_code create_file(struct file_stats *stats)
|
|||
if (file_stream == NULL)
|
||||
return CANNOT_CREATE_TARGET;
|
||||
// Copy content
|
||||
char buffer[4096];
|
||||
size_t remaining = stats->length;
|
||||
while (remaining > 0) {
|
||||
size_t to_read = (remaining < sizeof(buffer)) ? remaining : sizeof(buffer);
|
||||
size_t bytes_read = fread(buffer, 1, to_read, stats->content_stream);
|
||||
if (bytes_read == 0)
|
||||
break; // Reached EOF
|
||||
fwrite(buffer, 1, bytes_read, file_stream);
|
||||
remaining -= bytes_read;
|
||||
}
|
||||
fcat(stats->content_stream, file_stream, stats->length);
|
||||
|
||||
// Close file
|
||||
fclose(file_stream);
|
||||
|
|
@ -115,13 +112,13 @@ enum error_code create_file(struct file_stats *stats)
|
|||
enum error_code setowner(char *path, char *uid, char *gid)
|
||||
{
|
||||
if (path == NULL || uid == NULL || gid == NULL)
|
||||
return UNKNOWN_ERROR;
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
uid_t uid_val = strtol(uid, NULL, 10);
|
||||
gid_t gid_val = strtol(gid, NULL, 10);
|
||||
uid_t uid_val = strtol(uid, NULL, 10);
|
||||
gid_t gid_val = strtol(gid, NULL, 10);
|
||||
|
||||
int err = chown(path, uid_val, gid_val);
|
||||
return err == 0 ? SUCCESS : CANNOT_CREATE_TARGET;
|
||||
int err = chown(path, uid_val, gid_val);
|
||||
return err == 0 ? SUCCESS : CANNOT_CREATE_TARGET;
|
||||
}
|
||||
|
||||
enum error_code setmtime(char *path, char *mtime)
|
||||
|
|
@ -155,3 +152,66 @@ enum error_code setmode(char *path, char *mode)
|
|||
int err = chmod(path, mode_val);
|
||||
return err == 0 ? SUCCESS : CANNOT_CREATE_TARGET;
|
||||
}
|
||||
|
||||
enum error_code isdirectory(char *path)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(path, &st) == -1)
|
||||
return FILE_NOT_FOUND;
|
||||
return S_ISREG(st.st_mode) ? SUCCESS : GENERIC_ERROR;
|
||||
}
|
||||
|
||||
char *listdirectory(char *path)
|
||||
{
|
||||
struct dirent *de;
|
||||
|
||||
// Check for path changes
|
||||
if (current_dir_path != NULL && strcmp(current_dir_path, path) != 0)
|
||||
{
|
||||
// Close previous dir
|
||||
freedirectory();
|
||||
// Open new dir
|
||||
current_dir = opendir(current_dir_path);
|
||||
if (current_dir == NULL)
|
||||
{
|
||||
freedirectory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
current_dir_path = strdup(path);
|
||||
}
|
||||
|
||||
// Get following file
|
||||
de = readdir(current_dir);
|
||||
if (de == NULL)
|
||||
{
|
||||
freedirectory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return de->d_name;
|
||||
}
|
||||
|
||||
void freedirectory()
|
||||
{
|
||||
if (current_dir != NULL)
|
||||
closedir(current_dir);
|
||||
if (current_dir_path != NULL)
|
||||
free(current_dir_path);
|
||||
}
|
||||
|
||||
void fcat(FILE *src_stream, FILE *dst_stream, size_t size)
|
||||
{
|
||||
char buffer[4096];
|
||||
size_t remaining = size;
|
||||
while (remaining > 0)
|
||||
{
|
||||
size_t to_read =
|
||||
(remaining < sizeof(buffer)) ? remaining : sizeof(buffer);
|
||||
size_t bytes_read = fread(buffer, 1, to_read, src_stream);
|
||||
if (bytes_read == 0)
|
||||
break; // Reached EOF
|
||||
fwrite(buffer, bytes_read, 1, dst_stream);
|
||||
remaining -= bytes_read;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,44 @@ enum error_code create_directory(struct file_stats *stats);
|
|||
enum error_code create_link(struct file_stats *stats, char *target);
|
||||
enum error_code create_file(struct file_stats *stats);
|
||||
|
||||
void fcat(FILE *src_stream, FILE *dst_stream, size_t size);
|
||||
|
||||
enum error_code setowner(char *path, char *uid, char *gid);
|
||||
enum error_code setmtime(char *path, char *mtime);
|
||||
enum error_code setmode(char *path, char *mode);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the file informations at path and build the corresponding
|
||||
* file_stats struct
|
||||
* @arg path The given path
|
||||
* @arg stats Pointer to where the function should write file informations
|
||||
* @return FILE_NOT_FOUND if doesn's exists, GENERIC_ERROR if .
|
||||
*/
|
||||
enum error_code getstats(char *path, struct file_stats *stats);
|
||||
|
||||
/**
|
||||
* @brief Check if given path exists and is a directory
|
||||
* @option path The given path
|
||||
* @return FILE_NOT_FOUND if doesn's exists, GENERIC_ERROR if it is not a
|
||||
* directory, SUCCESS otherwise.
|
||||
*/
|
||||
enum error_code isdirectory(char *path);
|
||||
|
||||
/**
|
||||
* @brief Lists a directory content. Returns a new member name after each
|
||||
* iteration and NULL when there are no more to list.
|
||||
* @arg path The directory path
|
||||
* @return The current item name or NULL
|
||||
* @warn The return item statically allocated and freed after finishing to list
|
||||
* the directory or when freedirectory() is called.
|
||||
*/
|
||||
char *listdirectory(char *path);
|
||||
|
||||
/**
|
||||
* @brief Closes the last opened (and not closed) directory and frees the
|
||||
* related allocated memory
|
||||
* @warn Use this function if you only partially listed a directory previously.
|
||||
*/
|
||||
void freedirectory();
|
||||
|
||||
#endif // FILESYSTEM_H
|
||||
|
|
|
|||
329
epitar/tests/functional/run.sh
Normal file
329
epitar/tests/functional/run.sh
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Testsuitator (mon bien-aimé) le retour <3
|
||||
|
||||
###################
|
||||
# Variables #
|
||||
###################
|
||||
|
||||
executable="$BIN_PATH"
|
||||
ref_executable="tar"
|
||||
|
||||
total_tests=0
|
||||
errors_count=0
|
||||
timeouts_count=0
|
||||
|
||||
# Create temp directory for tests
|
||||
test_dir=$(mktemp -d)
|
||||
trap "rm -rf $test_dir" EXIT
|
||||
|
||||
|
||||
##################
|
||||
# Colors #
|
||||
##################
|
||||
|
||||
# Reset
|
||||
Color_Off='\033[0m' # Text Reset
|
||||
|
||||
# Regular Colors
|
||||
Black='\033[0;30m' # Black
|
||||
Red='\033[0;31m' # Red
|
||||
Green='\033[0;32m' # Green
|
||||
Yellow='\033[0;33m' # Yellow
|
||||
Blue='\033[0;34m' # Blue
|
||||
Purple='\033[0;35m' # Purple
|
||||
Cyan='\033[0;36m' # Cyan
|
||||
White='\033[0;37m' # White
|
||||
|
||||
# Bold
|
||||
BBlack='\033[1;30m' # Black
|
||||
BRed='\033[1;31m' # Red
|
||||
BGreen='\033[1;32m' # Green
|
||||
BYellow='\033[1;33m' # Yellow
|
||||
BBlue='\033[1;34m' # Blue
|
||||
BPurple='\033[1;35m' # Purple
|
||||
BCyan='\033[1;36m' # Cyan
|
||||
BWhite='\033[1;37m' # White
|
||||
|
||||
|
||||
##################
|
||||
# Wrappers #
|
||||
##################
|
||||
|
||||
# @arg test name
|
||||
# @arg command
|
||||
check_result() {
|
||||
local test_name="$1"
|
||||
local actual_code="$2"
|
||||
local ref_code="$3"
|
||||
|
||||
test_failed=0
|
||||
|
||||
# Check return code
|
||||
if [[ "$actual_code" -eq 124 ]]; then
|
||||
echo -e $BRed "TIMEOUT" $Color_Off
|
||||
echo -e ' ' "on '$test_name'"
|
||||
echo -e ' ' "Expected code $ref_code but got $actual_code"
|
||||
((timeouts_count++))
|
||||
test_failed=1
|
||||
elif [[ "$actual_code" -ne "$ref_code" ]]; then
|
||||
echo -e $BRed "FAILED" $Color_Off
|
||||
echo -e ' ' "on '$test_name'"
|
||||
echo -e ' ' "Expected code $ref_code but got $actual_code"
|
||||
test_failed=1
|
||||
else
|
||||
echo -e $BGreen "OK" $Color_Off
|
||||
fi
|
||||
|
||||
if [[ "$test_failed" -eq 1 ]]; then
|
||||
((errors_count++))
|
||||
fi
|
||||
}
|
||||
|
||||
# @arg test name
|
||||
# @arg test command
|
||||
test_archive_creation() {
|
||||
local test_name="$1"
|
||||
local archive_name="$2"
|
||||
local input_files="$3"
|
||||
|
||||
echo -e $BBlue "========== $test_name ==========" $Color_Off
|
||||
|
||||
# Create test files
|
||||
local test_subdir="$test_dir/$test_name"
|
||||
mkdir -p "$test_subdir"
|
||||
cd "$test_subdir"
|
||||
|
||||
# Prepare input files
|
||||
local file_list=""
|
||||
for file in $input_files; do
|
||||
echo "content of $file" > "$file"
|
||||
file_list="$file_list $file"
|
||||
done
|
||||
|
||||
# Test with epitar
|
||||
echo -e -n $Blue "= [ARCHIVE]" $Color_Off " "
|
||||
timeout 2 $executable -c "$archive_name" $file_list > /dev/null 2>&1
|
||||
actual_code=$?
|
||||
|
||||
if [[ $actual_code -eq 0 && -f "$archive_name" ]]; then
|
||||
echo -e $BGreen "OK" $Color_Off
|
||||
else
|
||||
echo -e $BRed "FAILED" $Color_Off
|
||||
((errors_count++))
|
||||
fi
|
||||
((total_tests++))
|
||||
|
||||
# Test extraction
|
||||
if [[ -f "$archive_name" ]]; then
|
||||
echo -e -n $Blue "= [EXTRACT] " $Color_Off " "
|
||||
extract_dir="extract_test"
|
||||
mkdir "$extract_dir"
|
||||
cd "$extract_dir"
|
||||
timeout 2 $executable -x "../$archive_name" > /dev/null 2>&1
|
||||
actual_code=$?
|
||||
|
||||
if [[ $actual_code -eq 0 ]]; then
|
||||
# Check if files were extracted
|
||||
all_extracted=1
|
||||
for file in $file_list; do
|
||||
if [[ ! -f "$file" ]]; then
|
||||
all_extracted=0
|
||||
fi
|
||||
done
|
||||
if [[ $all_extracted -eq 1 ]]; then
|
||||
echo -e $BGreen "OK" $Color_Off
|
||||
else
|
||||
echo -e $BRed "FAILED (files not extracted)" $Color_Off
|
||||
((errors_count++))
|
||||
fi
|
||||
else
|
||||
echo -e $BRed "FAILED (exit code $actual_code)" $Color_Off
|
||||
((errors_count++))
|
||||
fi
|
||||
((total_tests++))
|
||||
cd ..
|
||||
fi
|
||||
|
||||
cd /
|
||||
echo -e "\n"
|
||||
}
|
||||
|
||||
# @arg test name
|
||||
# @arg files to test
|
||||
test_verbose_output() {
|
||||
local test_name="$1"
|
||||
local files="$2"
|
||||
|
||||
echo -e $BBlue "========== $test_name ==========" $Color_Off
|
||||
|
||||
local test_subdir="$test_dir/${test_name}_verbose"
|
||||
mkdir -p "$test_subdir"
|
||||
cd "$test_subdir"
|
||||
|
||||
# Create test files
|
||||
for file in $files; do
|
||||
echo "test content" > "$file"
|
||||
done
|
||||
|
||||
echo -e -n $Blue "= [VERBOSE]" $Color_Off " "
|
||||
archive="test_verbose.tar"
|
||||
timeout 2 $executable -cv "$archive" $files > output.txt 2>&1
|
||||
actual_code=$?
|
||||
|
||||
# Check if output contains filenames
|
||||
local all_found=1
|
||||
for file in $files; do
|
||||
if ! grep -q "$file" output.txt; then
|
||||
all_found=0
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $actual_code -eq 0 && $all_found -eq 1 ]]; then
|
||||
echo -e $BGreen "OK" $Color_Off
|
||||
else
|
||||
echo -e $BRed "FAILED" $Color_Off
|
||||
((errors_count++))
|
||||
fi
|
||||
((total_tests++))
|
||||
|
||||
cd /
|
||||
echo -e "\n"
|
||||
}
|
||||
|
||||
# @arg test name
|
||||
test_help() {
|
||||
local test_name="$1"
|
||||
|
||||
echo -e $BBlue "========== $test_name ==========" $Color_Off
|
||||
|
||||
echo -e -n $Blue "= [HELP]" $Color_Off " "
|
||||
timeout 1 $executable -h > /dev/null 2>&1
|
||||
actual_code=$?
|
||||
|
||||
if [[ $actual_code -eq 0 ]]; then
|
||||
echo -e $BGreen "OK" $Color_Off
|
||||
else
|
||||
echo -e $BRed "FAILED" $Color_Off
|
||||
((errors_count++))
|
||||
fi
|
||||
((total_tests++))
|
||||
|
||||
echo -e "\n"
|
||||
}
|
||||
|
||||
# @arg test name
|
||||
test_invalid_args() {
|
||||
local test_name="$1"
|
||||
local args="$2"
|
||||
|
||||
echo -e $BBlue "========== $test_name ==========" $Color_Off
|
||||
|
||||
echo -e -n $Blue "= [INVALID]" $Color_Off " "
|
||||
timeout 1 $executable $args > /dev/null 2>&1
|
||||
actual_code=$?
|
||||
|
||||
# Should fail
|
||||
if [[ $actual_code -ne 0 ]]; then
|
||||
echo -e $BGreen "OK (correctly rejected)" $Color_Off
|
||||
else
|
||||
echo -e $BRed "FAILED (should have been rejected)" $Color_Off
|
||||
((errors_count++))
|
||||
fi
|
||||
((total_tests++))
|
||||
|
||||
echo -e "\n"
|
||||
}
|
||||
|
||||
summarize() {
|
||||
# Compute statistics
|
||||
(( passed_tests = $total_tests - $errors_count ))
|
||||
if [[ $total_tests -gt 0 ]]; then
|
||||
(( tests_percentage = 100 * $passed_tests / $total_tests ))
|
||||
else
|
||||
tests_percentage=0
|
||||
fi
|
||||
|
||||
# Adapt color depending on results
|
||||
if [[ tests_percentage -gt 80 ]]; then
|
||||
coverage_color=$BGreen
|
||||
elif [[ tests_percentage -gt 50 ]]; then
|
||||
coverage_color=$BYellow
|
||||
else
|
||||
coverage_color=$BRed
|
||||
fi
|
||||
|
||||
# Print
|
||||
echo -e $BWhite "\n\n""===========" $BWhite"Summary"$Color_Off "\n"
|
||||
echo -e " Passed $coverage_color$passed_tests/$total_tests$Color_Off tests ($coverage_color$tests_percentage%$Color_Off)"
|
||||
echo -e " Got $timeouts_count timeout(s)"
|
||||
if [ "$OUTPUT_FILE" != "" ]; then
|
||||
echo $tests_percentage > "$OUTPUT_FILE"
|
||||
fi
|
||||
echo
|
||||
}
|
||||
|
||||
|
||||
#################
|
||||
# Tests #
|
||||
#################
|
||||
|
||||
echo -e "\n\n""===$BGreen TestsuitatorX 2.0 Ultra Pro Max+ 365 Premium Gris Sidéral" "\n\n"$Color_Off
|
||||
|
||||
echo -e "\n$BBlue=== Basic Operations ===$Color_Off"
|
||||
test_archive_creation "Single file" "single.tar" "file1.txt"
|
||||
test_archive_creation "Multiple files" "multi.tar" "file1.txt file2.txt file3.txt"
|
||||
test_archive_creation "Files with spaces" "spaces.tar" "file_with_underscore.txt"
|
||||
|
||||
echo -e "\n$BBlue=== Verbose Output ===$Color_Off"
|
||||
test_verbose_output "Verbose creation" "file1.txt file2.txt"
|
||||
|
||||
echo -e "\n$BBlue=== Help and Invalid Arguments ===$Color_Off"
|
||||
test_help "Help flag"
|
||||
test_invalid_args "No arguments" ""
|
||||
test_invalid_args "Invalid flag" "-z"
|
||||
test_invalid_args "Missing archive" "-c"
|
||||
test_invalid_args "Extract with files" "-x archive.tar file.txt"
|
||||
|
||||
echo -e "\n$BBlue=== Edge Cases ===$Color_Off"
|
||||
|
||||
# Test with empty archive
|
||||
test_subdir="$test_dir/empty_archive"
|
||||
mkdir -p "$test_subdir"
|
||||
cd "$test_subdir"
|
||||
echo -e $BBlue "========== Empty archive ==========" $Color_Off
|
||||
echo -e -n $Blue "= [CREATE_EMPTY]" $Color_Off " "
|
||||
touch dummy
|
||||
timeout 2 $executable -c "empty.tar" dummy > /dev/null 2>&1
|
||||
actual_code=$?
|
||||
rm dummy
|
||||
if [[ $actual_code -eq 0 ]]; then
|
||||
echo -e $BGreen "OK" $Color_Off
|
||||
else
|
||||
echo -e $BRed "FAILED" $Color_Off
|
||||
((errors_count++))
|
||||
fi
|
||||
((total_tests++))
|
||||
cd /
|
||||
echo -e "\n"
|
||||
|
||||
# Test combined flags
|
||||
test_subdir="$test_dir/combined_flags"
|
||||
mkdir -p "$test_subdir"
|
||||
cd "$test_subdir"
|
||||
echo -e $BBlue "========== Combined flags (-cv) ==========" $Color_Off
|
||||
echo -e -n $Blue "= [COMBINED]" $Color_Off " "
|
||||
echo "content" > test.txt
|
||||
timeout 2 $executable -cv "test.tar" test.txt > /dev/null 2>&1
|
||||
actual_code=$?
|
||||
if [[ $actual_code -eq 0 && -f "test.tar" ]]; then
|
||||
echo -e $BGreen "OK" $Color_Off
|
||||
else
|
||||
echo -e $BRed "FAILED" $Color_Off
|
||||
((errors_count++))
|
||||
fi
|
||||
((total_tests++))
|
||||
cd /
|
||||
echo -e "\n"
|
||||
|
||||
summarize
|
||||
Loading…
Add table
Add a link
Reference in a new issue