bump
This commit is contained in:
parent
a2a5aacf63
commit
42922b74c0
@ -101,14 +101,13 @@ its (changed) value is ignored.
|
||||
- [X] dryrun
|
||||
- [X] recursion
|
||||
- [X] display directories with a trailing `/`
|
||||
- [ ] touching
|
||||
- [ ] mkdir
|
||||
- [ ] touching / mkdir
|
||||
- [X] renaming
|
||||
- [X] deletion
|
||||
- [X] change file permissions
|
||||
- [X] change owner
|
||||
- [ ] swapping
|
||||
- [ ] copying
|
||||
- [X] swapping
|
||||
- [X] copying
|
||||
- [X] specify the deletion method (so trash can be supported)
|
||||
- [X] use `${VIMDIREDITOR}`
|
||||
- [X] `NO_COLOR` / color
|
||||
|
@ -3,9 +3,10 @@
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include "kvec.h"
|
||||
@ -29,6 +30,8 @@ int entry_cmp(const void * a, const void * b) { // For qsort()
|
||||
static kvec_t(entry_t) entries;
|
||||
static kvec_t(const char*) directory_queue;
|
||||
|
||||
static kvec_t(move_data_t) move_data;
|
||||
|
||||
|
||||
|
||||
static
|
||||
@ -37,8 +40,8 @@ int add_directory(const char * const folder) {
|
||||
CHECK_OPEN(dir, folder, return 1);
|
||||
|
||||
char full_path[PATH_MAX];
|
||||
struct stat file_stat;
|
||||
struct dirent * mydirent;
|
||||
struct stat file_stat;
|
||||
entry_t entry;
|
||||
while ((mydirent = readdir(dir)) != NULL) {
|
||||
if (strcmp(mydirent->d_name, ".") == 0
|
||||
@ -72,9 +75,10 @@ int add_directory(const char * const folder) {
|
||||
}
|
||||
|
||||
int init_directive_c(const char * const folder) {
|
||||
init_file_utils(is_dry_run);
|
||||
init_file_utils(is_dry_run, custom_rm);
|
||||
kv_init(entries);
|
||||
kv_init(directory_queue);
|
||||
kv_init(move_data);
|
||||
|
||||
kv_push(const char*, directory_queue, folder);
|
||||
while (directory_queue.n) {
|
||||
@ -98,8 +102,16 @@ int deinit_directive_c(void) {
|
||||
free(kv_A(entries, i).name);
|
||||
}
|
||||
|
||||
for (int i = 0; i < move_data.n; i++) {
|
||||
move_data_t move = kv_A(move_data, i);
|
||||
free(move.orig_name);
|
||||
free(move.curt_name);
|
||||
free(move.dest_name);
|
||||
}
|
||||
|
||||
kv_destroy(directory_queue);
|
||||
kv_destroy(entries);
|
||||
kv_destroy(move_data);
|
||||
deinit_file_utis();
|
||||
|
||||
return 0;
|
||||
@ -159,36 +171,88 @@ int execute_directive_file(FILE * f) {
|
||||
#define NEXT_FIELD do { \
|
||||
if (*(sp = next_field(sp)) == '\0') { \
|
||||
errorn(E_FORMAT); \
|
||||
return 1; \
|
||||
goto recovery; \
|
||||
} \
|
||||
} while (0)
|
||||
const int LINE_SIZE = 1024;
|
||||
#define CHECK_FORMAT(n, x) do { \
|
||||
if (n != x) { \
|
||||
errorn(E_FORMAT); \
|
||||
return 1; \
|
||||
goto recovery; \
|
||||
} \
|
||||
} while (0)
|
||||
/* io buffering
|
||||
*/
|
||||
const int LINE_SIZE = 4096;
|
||||
char line[LINE_SIZE];
|
||||
char buffer[1024];
|
||||
char buffer[LINE_SIZE/2];
|
||||
/* String Pointer, indexing `line`
|
||||
*/
|
||||
char * sp;
|
||||
int id;
|
||||
/* alias reference to the current entry being operated on
|
||||
*/
|
||||
entry_t * entry;
|
||||
/* since new files fille be missing from `entries`,
|
||||
* but we only the the latest one,
|
||||
* we buffer it on the stack
|
||||
*/
|
||||
char touch_buffer[LINE_SIZE/2];
|
||||
entry_t touch_entry;
|
||||
|
||||
while (fgets(line, LINE_SIZE, f) != NULL) {
|
||||
sp = line;
|
||||
|
||||
// ID
|
||||
int e = sscanf(line, "%d\t", &id);
|
||||
// creation
|
||||
if (e != 1) {
|
||||
sscanf(sp, "%s\n", buffer);
|
||||
mytouch(buffer);
|
||||
continue;
|
||||
}
|
||||
NEXT_FIELD;
|
||||
do {
|
||||
int id;
|
||||
int e = sscanf(line, "%d\t", &id);
|
||||
if (e == 1) { // normal entry
|
||||
if (id < 0
|
||||
|| id > entries.n) {
|
||||
errorn(E_INDEX, id);
|
||||
goto recovery;
|
||||
}
|
||||
|
||||
entry_t * entry = &kv_A(entries, id);
|
||||
entry->is_mentioned = true;
|
||||
entry = &kv_A(entries, id);
|
||||
} else { // creation
|
||||
char * const saved_sp = sp;
|
||||
// skip to the name
|
||||
if (do_permissions) { NEXT_FIELD; }
|
||||
if (do_owner) { NEXT_FIELD; }
|
||||
|
||||
CHECK_FORMAT(1, sscanf(sp, "%s\n", touch_buffer));
|
||||
|
||||
mytouch(touch_buffer);
|
||||
|
||||
struct stat file_stat;
|
||||
int es = stat(touch_buffer, &file_stat);
|
||||
CHECK_OPEN(!(es == -1), touch_buffer, goto recovery); // XXX
|
||||
|
||||
touch_entry = (entry_t) {
|
||||
.name = touch_buffer,
|
||||
.st = file_stat,
|
||||
.is_mentioned = false,
|
||||
};
|
||||
entry = &touch_entry;
|
||||
|
||||
sp = saved_sp;
|
||||
}
|
||||
|
||||
NEXT_FIELD;
|
||||
} while (0);
|
||||
|
||||
// Copy
|
||||
if (entry->is_mentioned) {
|
||||
char * const saved_sp = sp;
|
||||
// skip to the name
|
||||
if (do_permissions) { NEXT_FIELD; }
|
||||
if (do_owner) { NEXT_FIELD; }
|
||||
|
||||
CHECK_FORMAT(1, sscanf(sp, "%s\n", buffer));
|
||||
|
||||
mycopy(entry->name, buffer);
|
||||
|
||||
sp = saved_sp;
|
||||
}
|
||||
|
||||
// Permission
|
||||
if (do_permissions) {
|
||||
@ -218,16 +282,31 @@ int execute_directive_file(FILE * f) {
|
||||
NEXT_FIELD;
|
||||
}
|
||||
|
||||
// Name
|
||||
CHECK_FORMAT(1, sscanf(sp, "%s\n", buffer));
|
||||
size_t len = strlen(buffer);
|
||||
if (buffer[len-1] == '/') {
|
||||
buffer[len-1] = '\0';
|
||||
}
|
||||
// Name (move)
|
||||
if (!entry->is_mentioned) {
|
||||
CHECK_FORMAT(1, sscanf(sp, "%s\n", buffer));
|
||||
size_t len = strlen(buffer);
|
||||
if (buffer[len-1] == '/') {
|
||||
buffer[len-1] = '\0';
|
||||
}
|
||||
|
||||
if (strcmp(entry->name, buffer)) {
|
||||
mymove(entry->name, buffer);
|
||||
if (strcmp(entry->name, buffer)) {
|
||||
if (access(buffer, F_OK)) {
|
||||
mymove(entry->name, buffer);
|
||||
} else {
|
||||
move_data_t move = mytempmove(entry->name, buffer);
|
||||
if (!move.orig_name) {
|
||||
errorn(E_FILE_SWAP, entry->name, buffer);
|
||||
goto recovery;
|
||||
}
|
||||
|
||||
kv_push(move_data_t, move_data, move);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- Poke
|
||||
entry->is_mentioned = true;
|
||||
}
|
||||
|
||||
// Deletion
|
||||
@ -238,6 +317,35 @@ int execute_directive_file(FILE * f) {
|
||||
}
|
||||
}
|
||||
|
||||
// Swap (move)
|
||||
for (int i = 0; i < move_data.n; i++) {
|
||||
move_data_t move = kv_A(move_data, i);
|
||||
// NOTE: we could be overwritting here;
|
||||
// thats the behaviour the user would expect
|
||||
int result = mymove(move.curt_name, move.dest_name);
|
||||
// on the otherhand, upon error,
|
||||
// you dont want your files replaced
|
||||
if (result
|
||||
&& !access(move.orig_name, F_OK)) {
|
||||
// the result of this is intentionally unchecked
|
||||
mymove(move.curt_name, move.orig_name);
|
||||
}
|
||||
}
|
||||
|
||||
#undef NEXT_FIELD
|
||||
#undef CHECK_FORMAT
|
||||
return 0;
|
||||
|
||||
recovery:
|
||||
/* If an error is encountered, we wish to leave the filesystem in a "valid" state.
|
||||
* Therefor, files waiting to be swapped (possessing a temporary name) are restored back
|
||||
* (if possible, if we run into another error, theres not much to do).
|
||||
*/
|
||||
for (int i = 0; i < move_data.n; i++) {
|
||||
move_data_t move = kv_A(move_data, i);
|
||||
if (!access(move.orig_name, F_OK)) {
|
||||
mymove(move.curt_name, move.orig_name);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
@ -27,15 +27,18 @@ void errorn(int n, ...) {
|
||||
va_start(argv, n);
|
||||
|
||||
switch (n) {
|
||||
case E_OPEN_EDITOR: verror("failed to open editor '%s'", argv); break;
|
||||
case E_FILE_ACCESS: verror("failed to interact with file '%s'", argv); break;
|
||||
case E_FILE_DELETE: verror("failed to delete file '%s'", argv); break;
|
||||
case E_FILE_MOVE: verror("failed to move '%s' to '%s'", argv); break;
|
||||
case E_FILE_CHOWN: verror("failed to chown '%s'", argv); break;
|
||||
case E_NO_USER: verror("failed to find the user '%s'", argv); break;
|
||||
case E_NO_GROUP: verror("failed to find the group '%s'", argv); break;
|
||||
case E_FORMAT: verror("directive-file format violation", argv); break;
|
||||
case E_FLAG: verror("unknown flag '%c'", argv); break;
|
||||
case E_OPEN_EDITOR: verror("failed to open editor '%s'", argv); break;
|
||||
case E_FILE_ACCESS: verror("failed to interact with file '%s'", argv); break;
|
||||
case E_FILE_DELETE: verror("failed to delete file '%s'", argv); break;
|
||||
case E_FILE_MOVE: verror("failed to move '%s' to '%s'", argv); break;
|
||||
case E_FILE_SWAP: verror("failed to swap '%s' with '%s'", argv); break;
|
||||
case E_FILE_COPY: verror("failed to copy '%s' to '%s'", argv); break;
|
||||
case E_FILE_CHOWN: verror("failed to chown '%s'", argv); break;
|
||||
case E_NO_USER: verror("failed to find the user '%s'", argv); break;
|
||||
case E_NO_GROUP: verror("failed to find the group '%s'", argv); break;
|
||||
case E_FORMAT: verror("directive-file format violation", argv); break;
|
||||
case E_INDEX: verror("file index violation encountered (%d)", argv); break;
|
||||
case E_FLAG: verror("unknown flag '%c'", argv); break;
|
||||
default: verror("unknown error encountered; this is an illegal inner state", 0); break;
|
||||
}
|
||||
|
||||
|
@ -6,10 +6,13 @@ enum {
|
||||
E_FILE_ACCESS,
|
||||
E_FILE_DELETE,
|
||||
E_FILE_MOVE,
|
||||
E_FILE_SWAP,
|
||||
E_FILE_COPY,
|
||||
E_FILE_CHOWN,
|
||||
E_NO_USER,
|
||||
E_NO_GROUP,
|
||||
E_FORMAT,
|
||||
E_INDEX,
|
||||
E_FLAG,
|
||||
};
|
||||
|
||||
|
@ -26,18 +26,25 @@ int (*mydelete)(const char *filename) = NULL;
|
||||
int (*mychmod)(const char *filename, mode_t mode) = NULL;
|
||||
int (*mychown)(const char *filename, const char *owner, const char *group) = NULL;
|
||||
int (*mymove)(const char *filename, const char *newname) = NULL;
|
||||
int (*mycopy)(const char *filename, const char *newname) = NULL;
|
||||
move_data_t (*mytempmove)(const char *filename, const char *newname) = NULL;
|
||||
|
||||
static int dry_touch(const char * filename);
|
||||
static int dry_delete(const char * filename);
|
||||
static int dry_chmod(const char * filename, mode_t mode);
|
||||
static int dry_chown(const char * filename, const char * owner, const char * group);
|
||||
static int dry_move(const char * filename, const char * newname);
|
||||
static int dry_copy(const char * filename, const char * newname);
|
||||
static move_data_t dry_mytempmove(const char *filename, const char *newname);
|
||||
|
||||
static int moist_touch(const char * filename);
|
||||
static int moist_delete(const char * filename);
|
||||
static int moist_chmod(const char * filename, mode_t mode);
|
||||
static int moist_chown(const char * filename, const char * owner, const char * group);
|
||||
static int moist_move(const char * filename, const char * newname);
|
||||
static int moist_copy(const char * filename, const char * newname);
|
||||
static move_data_t moist_mytempmove(const char *filename, const char *newname);
|
||||
|
||||
int init_file_utils(bool is_dry_run, const char * custom_rm_) {
|
||||
custom_rm = custom_rm_;
|
||||
|
||||
@ -47,12 +54,16 @@ int init_file_utils(bool is_dry_run, const char * custom_rm_) {
|
||||
mychmod = dry_chmod;
|
||||
mychown = dry_chown;
|
||||
mymove = dry_move;
|
||||
mycopy = dry_copy;
|
||||
mytempmove = dry_mytempmove;
|
||||
} else {
|
||||
//mytouch = moist_touch;
|
||||
//mydelete = moist_delete;
|
||||
//mychmod = moist_chmod;
|
||||
//mychown = moist_chown;
|
||||
//mymove = moist_move;
|
||||
mytouch = moist_touch;
|
||||
mydelete = moist_delete;
|
||||
mychmod = moist_chmod;
|
||||
mychown = moist_chown;
|
||||
mymove = moist_move;
|
||||
mycopy = moist_copy;
|
||||
mytempmove = moist_mytempmove;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -78,8 +89,8 @@ char mode_type_to_char(mode_t m) {
|
||||
case S_IFBLK: return 'b'; // block device
|
||||
case S_IFIFO: return 'p'; // fifo (pipe)
|
||||
case S_IFLNK: return 'l'; // symbolic link
|
||||
case S_IFSOCK: return 's'; // socket
|
||||
default: return '?'; // unknown
|
||||
case S_IFSOCK: return 's'; // socket
|
||||
default: return '?'; // unknown
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,7 +149,7 @@ mode_t str_to_mode(const char *permissions) {
|
||||
// --- Dry implementations
|
||||
static
|
||||
int dry_touch(const char * filename) {
|
||||
warning("touch '%s'", filename);
|
||||
warning("touch '%s' (subsequent stats will fail)", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -163,10 +174,26 @@ int dry_chown(const char * filename, const char * owner, const char * group) {
|
||||
|
||||
static
|
||||
int dry_move(const char * filename, const char * newname) {
|
||||
warning("rename '%s' (-> %s)", filename, newname);
|
||||
warning("rename '%s' (-> '%s')", filename, newname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int dry_copy(const char * filename, const char * newname) {
|
||||
warning("copy '%s' (as '%s')", filename, newname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
move_data_t dry_mytempmove(const char * filename, const char * newname) {
|
||||
warning("swap detected in a dry-run ('%s' <-> '%s'); the following logs will be inaccurate", filename, newname);
|
||||
return (move_data_t) {
|
||||
.orig_name = strdup(filename),
|
||||
.curt_name = strdup(filename),
|
||||
.dest_name = strdup(newname),
|
||||
};
|
||||
}
|
||||
|
||||
// --- Moist implementations
|
||||
static
|
||||
int moist_touch(const char * filename) {
|
||||
@ -244,3 +271,55 @@ int moist_move(const char * filename, const char * newname) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int moist_copy(const char * filename, const char * newname) {
|
||||
// Is using system for copying terrible? yes.
|
||||
// Do I have know a better solution thats not filled with footguns? no.
|
||||
size_t cmd_len = strlen("cp -a")
|
||||
+ sizeof(' ') + strlen(filename)
|
||||
+ sizeof(' ') + strlen(newname)
|
||||
+ 1
|
||||
;
|
||||
char cmd[cmd_len];
|
||||
|
||||
snprintf(cmd, cmd_len, "cp -a %s %s", filename, newname);
|
||||
|
||||
int result = system(cmd);
|
||||
if (result == 127
|
||||
|| result == -1
|
||||
|| (WIFEXITED(result) && WEXITSTATUS(result) != 0)) {
|
||||
errorn(E_FILE_COPY, filename, newname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
move_data_t moist_mytempmove(const char * filename, const char * newname) {
|
||||
move_data_t r = {
|
||||
.orig_name = NULL,
|
||||
.curt_name = NULL,
|
||||
.dest_name = NULL,
|
||||
};
|
||||
|
||||
const int COLISION_DIGITS = 3;
|
||||
const size_t buf_size = strlen(filename) + COLISION_DIGITS + sizeof("~");
|
||||
char buffer[buf_size];
|
||||
|
||||
unsigned n = 0;
|
||||
do {
|
||||
snprintf(buffer, buf_size, "%s~%d", filename, n++);
|
||||
if (n > 10 * COLISION_DIGITS) { goto end; }
|
||||
} while (!access(buffer, F_OK));
|
||||
|
||||
if (mymove(filename, buffer)) { goto end; }
|
||||
|
||||
r.orig_name = strdup(filename);
|
||||
r.curt_name = strdup(buffer);
|
||||
r.dest_name = strdup(newname);
|
||||
|
||||
end:
|
||||
return r;
|
||||
}
|
||||
|
@ -14,10 +14,35 @@ extern mode_t char_to_mode_type(const char c);
|
||||
extern char * mode_to_str(mode_t mode, char * buffer);
|
||||
extern mode_t str_to_mode(const char *permissions);
|
||||
|
||||
extern int (*mytouch)(const char *filename);
|
||||
extern int (*mydelete)(const char *filename);
|
||||
extern int (*mychmod)(const char *filename, mode_t mode);
|
||||
extern int (*mychown)(const char *filename, const char *owner, const char *group);
|
||||
extern int (*mymove)(const char *filename, const char *newname);
|
||||
extern int (*mytouch)(const char * filename);
|
||||
extern int (*mydelete)(const char * filename);
|
||||
extern int (*mychmod)(const char * filename, mode_t mode);
|
||||
extern int (*mychown)(const char * filename, const char * owner, const char * group);
|
||||
extern int (*mymove)(const char * filename, const char * newname);
|
||||
extern int (*mycopy)(const char * filename, const char * newname);
|
||||
|
||||
/* Swapping file names is only possible with an intermediate rename,
|
||||
* and it also means that we can't headlessly replace files.
|
||||
* For this reason we buffer failing renames until
|
||||
* the rest of the file operations are completed.
|
||||
* However, we have the following concerns:
|
||||
* 1) we don't want to move the file out of its directory,
|
||||
* so that in case of an error,
|
||||
* its not lost *somewhere* on the filesystem
|
||||
* 2) a simple and predictable name is preferable,
|
||||
* because if an error is encountered,
|
||||
* we want the user to easily recognize his files
|
||||
* 3) the temp name might already be taken
|
||||
* Points 1 and 2 cause 3 to exists.
|
||||
* To deal with 3, we use the tactic employed by the original vidir:
|
||||
* + we try incrementing suffixes until something works or we get bored
|
||||
*/
|
||||
typedef struct {
|
||||
char * orig_name;
|
||||
char * curt_name;
|
||||
char * dest_name;
|
||||
} move_data_t;
|
||||
|
||||
extern move_data_t (*mytempmove)(const char * filename, const char * newname);
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,7 @@
|
||||
#ifndef OPTS_H
|
||||
#define OPTS_H
|
||||
|
||||
// NOTE: these two should not clobber on globals
|
||||
extern void get_env(void);
|
||||
extern void parse_args(int argc, char * * argv);
|
||||
|
||||
|
@ -31,7 +31,7 @@ class CMDTEST_basic < Cmdtest::Testcase
|
||||
cmd "vimdir ./this/directory/does/not/exist/" do
|
||||
exit_nonzero
|
||||
created_files ["vimdir_test_file.vimdir"]
|
||||
stderr_equal /.+error.+/
|
||||
stderr_equal /\A.+error.+\n\z/
|
||||
end
|
||||
end
|
||||
|
||||
@ -91,6 +91,21 @@ class CMDTEST_mydir < Cmdtest::Testcase
|
||||
end
|
||||
end
|
||||
|
||||
def test_false_entry
|
||||
File.write('target.txt',
|
||||
[
|
||||
"005\t./mydir/.gitkeep",
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
cmd "EDITOR=./replacer.sh vimdir -n ./mydir/" do
|
||||
exit_nonzero
|
||||
created_files ["vimdir_test_file.vimdir"]
|
||||
removed_files ["target.txt"]
|
||||
stderr_equal /\A.+error.+\n\z/
|
||||
end
|
||||
end
|
||||
|
||||
def test_permission_contents
|
||||
expected = [
|
||||
"000\t-rw-r--r--\t./mydir/.gitkeep",
|
||||
@ -149,7 +164,7 @@ class CMDTEST_mydir < Cmdtest::Testcase
|
||||
exit_zero
|
||||
created_files ["vimdir_test_file.vimdir"]
|
||||
removed_files ["target.txt"]
|
||||
stderr_equal /^.*delete '.*file.txt'.*$/
|
||||
stderr_equal /\A.*delete '.*file.txt'.*\n\z/
|
||||
end
|
||||
end
|
||||
|
||||
@ -197,7 +212,43 @@ class CMDTEST_mydir < Cmdtest::Testcase
|
||||
exit_zero
|
||||
created_files ["vimdir_test_file.vimdir"]
|
||||
removed_files ["target.txt"]
|
||||
stderr_equal /^.*chmod '.*script.sh' (.+).*$/
|
||||
stderr_equal /\A.*chmod '.*script.sh' \(.+\).*\n\z/
|
||||
end
|
||||
end
|
||||
|
||||
def test_copy_file
|
||||
File.write('target.txt',
|
||||
[
|
||||
"000\t./mydir/.gitkeep",
|
||||
"001\t./mydir/file.txt",
|
||||
"001\t./mydir/file2.txt",
|
||||
"002\t./mydir/script.sh",
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
cmd "EDITOR=./replacer.sh vimdir -n ./mydir/" do
|
||||
exit_zero
|
||||
created_files ["vimdir_test_file.vimdir"]
|
||||
removed_files ["target.txt"]
|
||||
stderr_equal /\A.*copy.*'.*file2.txt'.*\n\z/
|
||||
end
|
||||
end
|
||||
|
||||
def test_touch_file
|
||||
File.write('target.txt',
|
||||
[
|
||||
"000\t./mydir/.gitkeep",
|
||||
"001\t./mydir/file.txt",
|
||||
"002\t./mydir/script.sh",
|
||||
"./mydir/new.txt",
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
cmd "EDITOR=./replacer.sh vimdir -n ./mydir/" do
|
||||
exit_nonzero
|
||||
created_files ["vimdir_test_file.vimdir"]
|
||||
removed_files ["target.txt"]
|
||||
stderr_equal /\A.*touch '.*new.txt'.*\n.*error.*\n\z/
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -212,7 +263,6 @@ class CMDTEST_mynesteddir < Cmdtest::Testcase
|
||||
def setup
|
||||
import_file "test/replacer.sh", "./"
|
||||
import_file "test/saver.sh", "./"
|
||||
import_file "test/memoryhole.sh", "./"
|
||||
import_directory "test/mynesteddir/", "./mynesteddir/"
|
||||
end
|
||||
|
||||
@ -251,11 +301,11 @@ class CMDTEST_myswapdir < Cmdtest::Testcase
|
||||
import_directory "test/myswapdir/", "./myswapdir/"
|
||||
end
|
||||
|
||||
def test_swap
|
||||
def test_dry_swap
|
||||
File.write('target.txt',
|
||||
[
|
||||
"000\t./myswapdir/file2.txt",
|
||||
"002\t./myswapdir/file1.txt",
|
||||
"001\t./myswapdir/file1.txt",
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
@ -263,7 +313,23 @@ class CMDTEST_myswapdir < Cmdtest::Testcase
|
||||
exit_zero
|
||||
created_files ["vimdir_test_file.vimdir"]
|
||||
removed_files ["target.txt"]
|
||||
stderr_equal /.+/
|
||||
stderr_equal /.+swap.+/
|
||||
end
|
||||
end
|
||||
|
||||
def test_swap
|
||||
File.write('target.txt',
|
||||
[
|
||||
"000\t./myswapdir/file2.txt",
|
||||
"001\t./myswapdir/file1.txt",
|
||||
].join("\n")
|
||||
)
|
||||
|
||||
cmd "EDITOR=./replacer.sh vimdir ./myswapdir/" do
|
||||
exit_zero
|
||||
created_files ["vimdir_test_file.vimdir"]
|
||||
removed_files ["target.txt"]
|
||||
changed_files ["myswapdir/file1.txt", "myswapdir/file2.txt"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user