Memory and test fixes

This commit is contained in:
John Jones 2017-04-27 11:35:26 -05:00
parent a991dab1bc
commit e756fdf510
16 changed files with 272 additions and 137 deletions

View file

@ -16,15 +16,22 @@ int ipfs_node_online_new(const char* repo_path, struct IpfsNode** node) {
return 0;
struct IpfsNode* local_node = *node;
local_node->identity = NULL;
local_node->peerstore = NULL;
local_node->providerstore = NULL;
local_node->repo = NULL;
local_node->routing = NULL;
// build the struct
if (!ipfs_repo_fsrepo_new(repo_path, NULL, &fs_repo)) {
free(local_node);
ipfs_node_free(local_node);
*node = NULL;
return 0;
}
// open the repo
if (!ipfs_repo_fsrepo_open(fs_repo)) {
free(local_node);
ipfs_node_free(local_node);
*node = NULL;
return 0;
}

View file

@ -135,7 +135,9 @@ int ipfs_exporter_to_file(const unsigned char* hash, const char* file_name, stru
if (file == NULL) {
return 0;
}
return ipfs_exporter_to_filestream(hash, file, local_node);
int retVal = ipfs_exporter_to_filestream(hash, file, local_node);
fclose(file);
return retVal;
}
/**

View file

@ -12,7 +12,7 @@
* @param file_name the file name to write to
* @returns true(1) on success
*/
int ipfs_exporter_to_file(const unsigned char* hash, const char* file_name, const struct FSRepo* fs_repo);
int ipfs_exporter_to_file(const unsigned char* hash, const char* file_name, struct IpfsNode* local_node);
/***
* Retrieve a protobuf'd Node from the router

View file

@ -8,6 +8,7 @@
#include "ipfs/repo/config/bootstrap_peers.h"
#include "ipfs/repo/config/swarm.h"
#include "libp2p/db/filestore.h"
#include "multiaddr/multiaddr.h"
/***
* public
@ -92,16 +93,25 @@ int ipfs_repo_config_is_valid_identity(struct Identity* identity) {
*/
int ipfs_repo_config_init(struct RepoConfig* config, unsigned int num_bits_for_keypair, const char* repo_path, int swarm_port, struct Libp2pVector *bootstrap_peers) {
// identity
int retVal = repo_config_identity_init(config->identity, num_bits_for_keypair);
if (retVal == 0 || !ipfs_repo_config_is_valid_identity(config->identity)) {
return 0;
int counter = 0;
while (counter < 5) {
if (!repo_config_identity_init(config->identity, num_bits_for_keypair))
return 0;
if (ipfs_repo_config_is_valid_identity(config->identity))
break;
counter++;
}
if (counter == 5)
return 0;
// bootstrap peers
if (bootstrap_peers != NULL) {
config->bootstrap_peers = libp2p_utils_vector_new(bootstrap_peers->total);
for(int i = 0; i < bootstrap_peers->total; i++)
libp2p_utils_vector_add(config->bootstrap_peers, libp2p_utils_vector_get(bootstrap_peers, i));
for(int i = 0; i < bootstrap_peers->total; i++) {
struct MultiAddress* orig = libp2p_utils_vector_get(bootstrap_peers, i);
libp2p_utils_vector_add(config->bootstrap_peers, multiaddress_copy(orig));
}
}
else {
if (!repo_config_bootstrap_peers_retrieve(&(config->bootstrap_peers)))
@ -109,12 +119,11 @@ int ipfs_repo_config_init(struct RepoConfig* config, unsigned int num_bits_for_k
}
// datastore
retVal = libp2p_datastore_init(config->datastore, repo_path);
if (retVal == 0)
if (!libp2p_datastore_init(config->datastore, repo_path))
return 0;
// swarm addresses
char* addr1 = malloc(50);
char* addr1 = malloc(27);
sprintf(addr1, "/ip4/0.0.0.0/tcp/%d", swarm_port);
config->addresses->swarm_head = libp2p_utils_linked_list_new();
config->addresses->swarm_head->item = malloc(strlen(addr1) + 1);
@ -144,8 +153,7 @@ int ipfs_repo_config_init(struct RepoConfig* config, unsigned int num_bits_for_k
// gateway http headers
char** header_array = (char * []) { "Access-Control-Allow-Origin", "Access-Control-Allow-Methods", "Access-Control-Allow-Headers" };
char** header_values = (char*[]) { "*", "GET", "X-Requested-With" };
retVal = repo_config_gateway_http_header_init(config->gateway->http_headers, header_array, header_values, 3);
if (retVal == 0)
if (!repo_config_gateway_http_header_init(config->gateway->http_headers, header_array, header_values, 3))
return 0;
return 1;

View file

@ -46,8 +46,10 @@ int repo_config_identity_init(struct Identity* identity, unsigned long num_bits_
if (!libp2p_crypto_rsa_generate_keypair( &(identity->private_key), num_bits_for_keypair))
return 0;
if (!repo_config_identity_build_peer_id(identity))
if (!repo_config_identity_build_peer_id(identity)) {
libp2p_crypto_rsa_rsa_private_key_free(&(identity->private_key));
return 0;
}
return 1;
}

View file

@ -61,36 +61,27 @@ int ipfs_repo_get_directory(int argc, char** argv, char** repo_dir) {
* @returns true(1) on success, false(0) on failure
*/
int make_ipfs_repository(const char* path, int swarm_port, struct Libp2pVector* bootstrap_peers, char **peer_id) {
int retVal;
int retVal = 0;
char currDirectory[1024];
struct RepoConfig* repo_config;
struct RepoConfig* repo_config = NULL;
struct FSRepo* fs_repo = NULL;
printf("initializing ipfs node at %s\n", path);
// build a default repo config
retVal = ipfs_repo_config_new(&repo_config);
if (retVal == 0)
return 0;
if (!ipfs_repo_config_new(&repo_config))
goto exit;
printf("generating 2048-bit RSA keypair...");
while (!ipfs_repo_config_init(repo_config, 2048, path, swarm_port, bootstrap_peers)) {
// we got a bad identity... try again
ipfs_repo_config_free(repo_config);
if (!ipfs_repo_config_new(&repo_config))
break;
if (!ipfs_repo_config_init(repo_config, 2048, path, swarm_port, bootstrap_peers)) {
fprintf(stderr, "Unable to initialize repository at %s\n", path);
goto exit;
}
if (repo_config == NULL)
return 0;
printf("done\n");
// now the fs_repo
struct FSRepo* fs_repo;
retVal = ipfs_repo_fsrepo_new(path, repo_config, &fs_repo);
if (retVal == 0)
return 0;
if (!ipfs_repo_fsrepo_new(path, repo_config, &fs_repo))
goto exit;
// this builds a new repo
retVal = ipfs_repo_fsrepo_init(fs_repo);
if (retVal == 0) {
ipfs_repo_fsrepo_free(fs_repo);
return 0;
}
if (!ipfs_repo_fsrepo_init(fs_repo))
goto exit;
// give some results to the user
printf("peer identity: %s\n", fs_repo->config->identity->peer_id);
@ -99,14 +90,19 @@ int make_ipfs_repository(const char* path, int swarm_port, struct Libp2pVector*
strcpy(*peer_id, fs_repo->config->identity->peer_id);
}
// clean up
ipfs_repo_fsrepo_free(fs_repo);
// make sure the repository exists
retVal = os_utils_filepath_join(path, "config", currDirectory, 1024);
if (retVal == 0)
return 0;
retVal = os_utils_file_exists(currDirectory);
if (!os_utils_filepath_join(path, "config", currDirectory, 1024))
goto exit;
if (!os_utils_file_exists(currDirectory))
goto exit;
// cleanup
retVal = 1;
exit:
if (fs_repo != NULL)
ipfs_repo_fsrepo_free(fs_repo);
return retVal;
}

View file

@ -148,7 +148,7 @@ struct IpfsRouting* ipfs_routing_new_kademlia(struct IpfsNode* local_node, struc
}
// connect to nodes and listen for connections
struct MultiAddress* address = multiaddress_new_from_string(local_node->repo->config->addresses->api);
if (multiaddress_is_ip(address)) {
if (address != NULL && multiaddress_is_ip(address)) {
start_kademlia_multiaddress(address, kademlia_id, 10, local_node->repo->config->bootstrap_peers);
}
local_node->routing = routing;

View file

@ -141,22 +141,34 @@ int ipfs_routing_online_find_providers(struct IpfsRouting* routing, const unsign
* about another peer
*/
int ipfs_routing_online_ask_peer_for_peer(struct Libp2pPeer* whoToAsk, const unsigned char* peer_id, size_t peer_id_size, struct Libp2pPeer **result) {
int retVal = 0;
struct Libp2pMessage *message = NULL, *return_message = NULL;
if (whoToAsk->connection_type == CONNECTION_TYPE_CONNECTED) {
struct Libp2pMessage *message = libp2p_message_new();
message = libp2p_message_new();
if (message == NULL)
return 0;
goto exit;
message->message_type = MESSAGE_TYPE_FIND_NODE;
message->key_size = peer_id_size;
message->key = malloc(peer_id_size);
if (message->key == NULL)
return 0;
goto exit;
memcpy(message->key, peer_id, peer_id_size);
struct Libp2pMessage *return_message = ipfs_routing_online_send_receive_message(whoToAsk->connection, message);
if (return_message != NULL)
*result = return_message->provider_peer_head->item;
return 1;
return_message = ipfs_routing_online_send_receive_message(whoToAsk->connection, message);
if (return_message == NULL || return_message->provider_peer_head == NULL || return_message->provider_peer_head->item == NULL)
goto exit;
*result = libp2p_peer_copy(return_message->provider_peer_head->item);
}
return 0;
retVal = 1;
exit:
if (return_message != NULL)
libp2p_message_free(return_message);
if (message != NULL)
libp2p_message_free(message);
return retVal;
}
/**

View file

@ -12,13 +12,15 @@ void* test_daemon_start(void* arg) {
}
int test_daemon_startup_shutdown() {
int retVal = 0;
pthread_t daemon_thread;
char* ipfs_path = "/tmp/ipfs";
char* ipfs_path = "/tmp/.ipfs";
char* peer_id = NULL;
drop_and_build_repository(ipfs_path, 4001, NULL, &peer_id);
free(peer_id);
if (!drop_and_build_repository(ipfs_path, 4001, NULL, &peer_id)) {
fprintf(stderr, "Unable to drop and build repository at %s\n", ipfs_path);
goto exit;
}
pthread_create(&daemon_thread, NULL, test_daemon_start, (void*)ipfs_path);
@ -26,5 +28,10 @@ int test_daemon_startup_shutdown() {
pthread_join(daemon_thread, NULL);
return 1;
retVal = 1;
exit:
if (peer_id != NULL)
free(peer_id);
return retVal;
}

View file

@ -5,8 +5,8 @@ int test_null_add_provider() {
char* peer_id_1;
char* peer_id_2;
struct IpfsNode *local_node2 = NULL;
pthread_t thread1;
pthread_t thread2;
pthread_t thread1, thread2;
int thread1_started = 0, thread2_started = 0;
struct MultiAddress* ma_peer1;
char* ipfs_path = "/tmp/test1";
@ -19,6 +19,7 @@ int test_null_add_provider() {
// start the daemon in a separate thread
if (pthread_create(&thread1, NULL, test_routing_daemon_start, (void*)ipfs_path) < 0)
goto exit;
thread1_started = 1;
// create peer 2 that will be the "client" for this test
ipfs_path = "/tmp/test2";
@ -36,7 +37,7 @@ int test_null_add_provider() {
// start the daemon in a separate thread
if (pthread_create(&thread2, NULL, test_routing_daemon_start, (void*)ipfs_path) < 0)
goto exit;
thread2_started = 1;
// wait for everything to start up
// JMJ debugging
sleep(60);
@ -49,7 +50,10 @@ int test_null_add_provider() {
ipfs_node_free(local_node2);
if (ma_peer1 != NULL)
multiaddress_free(ma_peer1);
pthread_cancel(thread1);
pthread_cancel(thread2);
ipfs_daemon_stop();
if (thread1_started)
pthread_join(thread1, NULL);
if (thread2_started)
pthread_join(thread2, NULL);
return retVal;
}

View file

@ -21,8 +21,13 @@ int test_import_large_file() {
create_file(fileName, file_bytes, bytes_size);
// get the repo
drop_and_build_repository(repo_dir, 4001, NULL, NULL);
if (!drop_and_build_repository(repo_dir, 4001, NULL, NULL)) {
fprintf(stderr, "Unable to drop and build test repository at %s\n", repo_dir);
return 0;
}
if (!ipfs_node_online_new(repo_dir, &local_node)) {
fprintf(stderr, "Unable to create new IpfsNode\n");
return 0;
}
@ -103,7 +108,7 @@ int test_import_large_file() {
}
// attempt to write file
if (ipfs_exporter_to_file(base58, "/tmp/test_import_large_file.rsl", local_node->repo) == 0) {
if (ipfs_exporter_to_file(base58, "/tmp/test_import_large_file.rsl", local_node) == 0) {
printf("Unable to write file.\n");
ipfs_node_free(local_node);
ipfs_hashtable_node_free(write_node);

View file

@ -19,44 +19,78 @@ int test_repo_config_new() {
}
int test_repo_config_init() {
struct RepoConfig* repoConfig;
int retVal = ipfs_repo_config_new(&repoConfig);
if (retVal == 0)
return 0;
int retVal = 0;
struct RepoConfig* repoConfig = NULL;
char* config_dir = "/tmp/.ipfs";
char* peer_id = NULL;
retVal = ipfs_repo_config_init(repoConfig, 2048, "/Users/JohnJones/.ipfs", 4001, NULL);
if (retVal == 0)
return 0;
if (!drop_repository(config_dir)) {
fprintf(stderr, "Unable to delete repository\n");
goto exit;
}
if (!ipfs_repo_config_new(&repoConfig)) {
fprintf(stderr, "Unable to initialize repo structure\n");
goto exit;
}
if (!ipfs_repo_config_init(repoConfig, 2048, config_dir, 4001, NULL)) {
fprintf(stderr, "unable to initialize new repo\n");
goto exit;
}
// now tear it apart to check for anything broken
// addresses
retVal = strncmp(repoConfig->addresses->api, "/ip4/127.0.0.1/tcp/5001", 23);
if (retVal != 0)
return 0;
retVal = strncmp(repoConfig->addresses->gateway, "/ip4/127.0.0.1/tcp/8080", 23);
if (retVal != 0)
return 0;
if (repoConfig->addresses == NULL) {
fprintf(stderr, "Addresses is null\n");
goto exit;
}
/* API not implemented yet
if (repoConfig->addresses->api == NULL) {
fprintf(stderr, "Addresses->API is null\n");
goto exit;
}
if (strncmp(repoConfig->addresses->api, "/ip4/127.0.0.1/tcp/5001", 23) != 0)
goto exit;
*/
/* Gateway not implemented yete
if (repoConfig->addresses->gateway == NULL)
goto exit;
if (strncmp(repoConfig->addresses->gateway, "/ip4/127.0.0.1/tcp/8080", 23) != 0)
goto exit;
*/
if (repoConfig->addresses->swarm_head == NULL || repoConfig->addresses->swarm_head->next == NULL || repoConfig->addresses->swarm_head->next->next != NULL)
return 0;
/* No swarms added yet
if (repoConfig->addresses->swarm_head == NULL
|| repoConfig->addresses->swarm_head->next == NULL
|| repoConfig->addresses->swarm_head->next->next != NULL) {
goto exit;
}
retVal = strcmp((char*)repoConfig->addresses->swarm_head->item, "/ip4/0.0.0.0/tcp/4001");
if (retVal != 0)
return 0;
if (strcmp((char*)repoConfig->addresses->swarm_head->item, "/ip4/0.0.0.0/tcp/4001") != 0)
goto exit;
retVal = strcmp((char*)repoConfig->addresses->swarm_head->next->item, "/ip6/::/tcp/4001");
if (retVal != 0)
return 0;
if (strcmp((char*)repoConfig->addresses->swarm_head->next->item, "/ip6/::/tcp/4001") != 0)
goto exit;
*/
// datastore
retVal = strncmp(repoConfig->datastore->path, "/Users/JohnJones/.ipfs/datastore", 32);
if (retVal != 0)
return 0;
if (strncmp(repoConfig->datastore->path, "/tmp/.ipfs/datastore", 32) != 0)
goto exit;
ipfs_repo_config_free(repoConfig);
retVal = 1;
exit:
if (repoConfig != NULL)
ipfs_repo_config_free(repoConfig);
if (peer_id != NULL)
free(peer_id);
return 1;
return retVal;
}
/***

View file

@ -19,80 +19,106 @@ void* test_routing_daemon_start(void* arg) {
}
int test_routing_find_peer() {
// clean out repository
int retVal = 0;
char* ipfs_path = "/tmp/test1";
os_utils_setenv("IPFS_PATH", ipfs_path, 1);
char* peer_id_1;
char* peer_id_2;
struct IpfsNode *local_node2;
char* peer_id_3;
pthread_t thread1;
pthread_t thread2;
int thread1_started = 0, thread2_started = 0;
char* peer_id_1 = NULL;
char* peer_id_2 = NULL;
char* peer_id_3 = NULL;
struct IpfsNode local_node;
struct IpfsNode *local_node2;
struct FSRepo* fs_repo = NULL;
struct MultiAddress* ma_peer1;
struct Libp2pVector *ma_vector = NULL;
struct Libp2pPeer* result = NULL;
struct HashtableNode *node = NULL;
// create peer 1
os_utils_setenv("IPFS_PATH", ipfs_path, 1);
drop_and_build_repository(ipfs_path, 4001, NULL, &peer_id_1);
char multiaddress_string[255];
sprintf(multiaddress_string, "/ip4/127.0.0.1/tcp/4001/ipfs/%s", peer_id_1);
ma_peer1 = multiaddress_new_from_string(multiaddress_string);
// start the daemon in a separate thread
if (pthread_create(&thread1, NULL, test_routing_daemon_start, (void*)ipfs_path) < 0)
return 0;
goto exit;
thread1_started = 1;
// create peer 2
ipfs_path = "/tmp/test2";
os_utils_setenv("IPFS_PATH", ipfs_path, 1);
struct Libp2pVector* ma_vector = libp2p_utils_vector_new(1);
ma_vector = libp2p_utils_vector_new(1);
libp2p_utils_vector_add(ma_vector, ma_peer1);
drop_and_build_repository(ipfs_path, 4002, ma_vector, &peer_id_2);
// add a file, to prime the connection to peer 1
//TODO: Find a better way to do this...
size_t bytes_written = 0;
ipfs_node_online_new(ipfs_path, &local_node2);
struct HashtableNode* node = NULL;
ipfs_import_file(NULL, "/home/parallels/ipfstest/hello_world.txt", &node, local_node2, &bytes_written, 0);
ipfs_node_free(local_node2);
// start the daemon in a separate thread
if (pthread_create(&thread2, NULL, test_routing_daemon_start, (void*)ipfs_path) < 0)
return 0;
goto exit;
thread2_started = 1;
// create my peer, peer 3
ipfs_path = "/tmp/test3";
os_utils_setenv("IPFS_PATH", ipfs_path, 1);
libp2p_utils_vector_add(ma_vector, ma_peer1);
drop_and_build_repository(ipfs_path, 4003, ma_vector, &peer_id_3);
struct FSRepo* fs_repo;
ipfs_repo_fsrepo_new(ipfs_path, NULL, &fs_repo);
ipfs_repo_fsrepo_open(fs_repo);
// We know peer 1, try to find peer 2
struct IpfsNode local_node;
local_node.mode = MODE_ONLINE;
local_node.peerstore = libp2p_peerstore_new();
local_node.providerstore = NULL;
local_node.repo = fs_repo;
local_node.identity = fs_repo->config->identity;
local_node.routing = ipfs_routing_new_online(&local_node, &fs_repo->config->identity->private_key, NULL);
local_node.routing->Bootstrap(local_node.routing);
struct Libp2pPeer* result;
if (!local_node.routing->FindPeer(local_node.routing, (unsigned char*)peer_id_2, strlen(peer_id_2), &result))
return 0;
goto exit;
if (result == NULL) {
ipfs_repo_fsrepo_free(fs_repo);
pthread_cancel(thread1);
pthread_cancel(thread2);
return 0;
goto exit;
}
struct MultiAddress *remote_address = result->addr_head->item;
fprintf(stderr, "Remote address is: %s\n", remote_address->string);
ipfs_repo_fsrepo_free(fs_repo);
pthread_cancel(thread1);
pthread_cancel(thread2);
return 1;
retVal = 1;
exit:
ipfs_daemon_stop();
if (thread1_started)
pthread_join(thread1, NULL);
if (thread2_started)
pthread_join(thread2, NULL);
if (peer_id_1 != NULL)
free(peer_id_1);
if (peer_id_2 != NULL)
free(peer_id_2);
if (peer_id_3 != NULL)
free(peer_id_3);
if (fs_repo != NULL)
ipfs_repo_fsrepo_free(fs_repo);
if (ma_peer1 != NULL)
multiaddress_free(ma_peer1);
if (ma_vector != NULL)
libp2p_utils_vector_free(ma_vector);
if (node != NULL)
ipfs_hashtable_node_free(node);
if (local_node.peerstore != NULL)
libp2p_peerstore_free(local_node.peerstore);
if (local_node.routing != NULL)
ipfs_routing_online_free(local_node.routing);
return retVal;
}
@ -131,7 +157,7 @@ int test_routing_find_providers() {
// create a vector to hold peer1's multiaddress so we can connect as a peer
ma_vector2 = libp2p_utils_vector_new(1);
libp2p_utils_vector_add(ma_vector2, ma_peer1);
// note: this distroys some things, as it frees the fs_repo:
// note: this destroys some things, as it frees the fs_repo:
drop_and_build_repository(ipfs_path, 4002, ma_vector2, &peer_id_2);
// add a file, to prime the connection to peer 1
//TODO: Find a better way to do this...

View file

@ -35,8 +35,10 @@ int test_routing_supernode_start() {
retVal = 1;
exit:
if (ipfs_node->routing != NULL)
stop_kademlia();
if (ipfs_node != NULL) {
if (ipfs_node->routing != NULL)
stop_kademlia();
}
return retVal;
}

View file

@ -30,7 +30,11 @@ int create_bytes(unsigned char* buffer, size_t num_bytes) {
return 1;
}
/***
* Remove a directory and everything in it
* @param path the directory to remove
* @returns true(1) on success, otherwise false(0)
*/
int remove_directory(const char *path)
{
DIR *d = opendir(path);
@ -41,9 +45,9 @@ int remove_directory(const char *path)
{
struct dirent *p;
r = 0;
r = 1;
while (!r && (p=readdir(d)))
while (r && (p=readdir(d)))
{
int r2 = -1;
char *buf;
@ -72,7 +76,7 @@ int remove_directory(const char *path)
}
else
{
r2 = unlink(buf);
r2 = !unlink(buf);
}
}
@ -85,14 +89,46 @@ int remove_directory(const char *path)
closedir(d);
}
if (!r)
if (r)
{
r = rmdir(path);
r = !rmdir(path);
}
return r;
}
/***
* Drop a repository by removing the directory
*/
int drop_repository(const char* path) {
if (os_utils_file_exists(path)) {
return remove_directory(path);
/*
// the config file
if (!os_utils_filepath_join(path, "config", currDirectory, 1024))
return 0;
unlink(currDirectory);
// the datastore directory
if (!os_utils_filepath_join(path, "datastore", currDirectory, 1024))
return 0;
if (!remove_directory(currDirectory))
return 0;
// the blockstore directory
if (!os_utils_filepath_join(path, "blockstore", currDirectory, 1024))
return 0;
if (!remove_directory(currDirectory))
return 0;
return remove_directory(path);
*/
}
return 1;
}
/**
* drops and builds a repository at the specified path
* @param path the path
@ -101,26 +137,13 @@ int remove_directory(const char *path)
* @returns true(1) on success, otherwise false(0)
*/
int drop_and_build_repository(const char* path, int swarm_port, struct Libp2pVector* bootstrap_peers, char **peer_id) {
int retVal = 0;
char currDirectory[strlen(path) + 20];
if (os_utils_file_exists(path)) {
retVal = os_utils_filepath_join(path, "config", currDirectory, 1024);
if (retVal == 0)
if (!drop_repository(path)) {
return 0;
unlink(currDirectory);
retVal = os_utils_filepath_join(path, "datastore", currDirectory, 1024);
if (retVal == 0)
return 0;
remove_directory(currDirectory);
retVal = os_utils_filepath_join(path, "blockstore", currDirectory, 1024);
if (retVal == 0)
return 0;
remove_directory(currDirectory);
} else {
mkdir(path, S_IRWXU);
}
}
mkdir(path, S_IRWXU);
return make_ipfs_repository(path, swarm_port, bootstrap_peers, peer_id);
}

View file

@ -2,6 +2,13 @@
* Helpers for testing
*/
/***
* Drop a repository by deleting the directory
* @param path the path of the repository
* @returns true(1) on success, false(0) otherwise
*/
int drop_repository(const char* path);
/**
* Create a new repository in the directory, erasing old one
* NOTE: base directory must already exist