smarbs 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +23 -0
- data/History.txt +3 -0
- data/Manifest.txt +28 -0
- data/README.txt +88 -0
- data/Rakefile +10 -0
- data/bin/smarbs +11 -0
- data/lib/backup.rb +275 -0
- data/lib/data/configuration.txt +209 -0
- data/lib/data/iconblue.png +0 -0
- data/lib/data/icongreen.png +0 -0
- data/lib/data/iconred.png +0 -0
- data/lib/data/intro.txt +16 -0
- data/lib/data/outro.txt +15 -0
- data/lib/helpers.rb +438 -0
- data/lib/log.rb +214 -0
- data/lib/script.rb +217 -0
- data/lib/smarbs.rb +5 -0
- data/lib/syslog.rb +98 -0
- data/lib/types.rb +172 -0
- data/test/smarbs_test.rb +114 -0
- data/test/test_backup.rb +171 -0
- data/test/test_helpers.rb +500 -0
- data/test/test_log.rb +128 -0
- data/test/test_script.rb +118 -0
- data/test/test_smarbs.rb +131 -0
- data/test/test_smarbs_test.rb +76 -0
- data/test/test_suite.rb +11 -0
- data/test/test_types.rb +141 -0
- metadata +107 -0
data/lib/script.rb
ADDED
@@ -0,0 +1,217 @@
|
|
1
|
+
require 'types'
|
2
|
+
require 'backup'
|
3
|
+
require 'smarbs'
|
4
|
+
|
5
|
+
class Script
|
6
|
+
|
7
|
+
def initialize (configdir, argv = [])
|
8
|
+
@halt = false
|
9
|
+
@argv=argv
|
10
|
+
@configfile = nil
|
11
|
+
begin
|
12
|
+
@config_dir = Directory.new(configdir)
|
13
|
+
getconfigfiles
|
14
|
+
rescue RuntimeError
|
15
|
+
puts "Error: " + $!.to_s
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def getconfigfiles #return an array of config files
|
22
|
+
@configfile_names = @config_dir.files(false)
|
23
|
+
|
24
|
+
if @configfile_names.size == 0 #create sample configfile
|
25
|
+
@configfile=ConfigFile.new(@config_dir.to_s+"backup1")
|
26
|
+
end
|
27
|
+
|
28
|
+
parse(@argv)
|
29
|
+
end
|
30
|
+
|
31
|
+
def backup
|
32
|
+
if @status
|
33
|
+
begin
|
34
|
+
require 'gtk2'
|
35
|
+
rescue LoadError
|
36
|
+
raise "Error: No ruby - gtk2 installed, which is needed when using the '--status' option."
|
37
|
+
end
|
38
|
+
|
39
|
+
datadir=SMARBS::DATADIR
|
40
|
+
|
41
|
+
Gtk.init
|
42
|
+
menu = Gtk::Menu.new
|
43
|
+
|
44
|
+
item3 = Gtk::ImageMenuItem.new(Gtk::Stock::EXECUTE)
|
45
|
+
item3.show
|
46
|
+
item1 = Gtk::CheckMenuItem.new("Shutdown after Backup")
|
47
|
+
if @config_dir.to_s == "/etc/smarbs/" then
|
48
|
+
item1.show
|
49
|
+
else
|
50
|
+
puts @config_dir.to_s
|
51
|
+
end
|
52
|
+
item2 = Gtk::ImageMenuItem.new(Gtk::Stock::QUIT)
|
53
|
+
item2.show
|
54
|
+
|
55
|
+
menu.append(item3)
|
56
|
+
menu.append(item1)
|
57
|
+
menu.append(item2)
|
58
|
+
|
59
|
+
icon = Gtk::StatusIcon.new
|
60
|
+
icon.pixbuf=Gdk::Pixbuf.new(datadir + "/icongreen.png")
|
61
|
+
icon.tooltip="Smarbs is ready to back up!";
|
62
|
+
icon.blinking=true
|
63
|
+
|
64
|
+
handler = icon.signal_connect("activate") {item3.signal_emit("activate") }
|
65
|
+
|
66
|
+
handler2 = icon.signal_connect('popup-menu') { |w, button, time|
|
67
|
+
menu.popup(nil, nil, button, time)
|
68
|
+
}
|
69
|
+
|
70
|
+
item3.signal_connect("activate") do
|
71
|
+
icon.blinking=false
|
72
|
+
item2.hide
|
73
|
+
item3.hide
|
74
|
+
icon.tooltip="Smarbs is working...";
|
75
|
+
if item1.active?
|
76
|
+
icon.pixbuf=Gdk::Pixbuf.new(datadir + "/iconred.png")
|
77
|
+
else
|
78
|
+
icon.pixbuf=Gdk::Pixbuf.new(datadir + "/iconblue.png")
|
79
|
+
end
|
80
|
+
if not item1.visible? then
|
81
|
+
icon.signal_handler_disconnect handler2
|
82
|
+
end
|
83
|
+
icon.signal_handler_disconnect handler
|
84
|
+
Thread.new{start_backup}
|
85
|
+
end
|
86
|
+
|
87
|
+
if item1.visible? then
|
88
|
+
item1.signal_connect("toggled") do
|
89
|
+
if item1.active?
|
90
|
+
if not item3.visible? then icon.pixbuf=Gdk::Pixbuf.new(datadir + "/iconred.png") end
|
91
|
+
@halt = true
|
92
|
+
else
|
93
|
+
if not item3.visible? then icon.pixbuf=Gdk::Pixbuf.new(datadir + "/iconblue.png") end
|
94
|
+
@halt = false
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
item2.signal_connect("activate") { Gtk.main_quit }
|
100
|
+
|
101
|
+
Gtk.main
|
102
|
+
else
|
103
|
+
start_backup
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
def start_backup
|
109
|
+
if @configfile_arguments.size == 0
|
110
|
+
todo = @configfile_names
|
111
|
+
else
|
112
|
+
todo = @configfile_arguments
|
113
|
+
end
|
114
|
+
todo.each do |name|
|
115
|
+
Backup.new(@config_dir.to_s + name, @rsync_arguments.join(" "), @verbose)
|
116
|
+
end unless not @configfile.nil? and @configfile.new
|
117
|
+
if @status then
|
118
|
+
Gtk.main_quit
|
119
|
+
if @halt then `halt` end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def print_help
|
124
|
+
print_version
|
125
|
+
|
126
|
+
puts "\nsmarbs is a backup script written in ruby capable of doing intelligent and\nautomated backups using rsync."
|
127
|
+
|
128
|
+
puts "\nUsage: smarbs [OPTION]... [CONFIGFILE]... "
|
129
|
+
puts "Makes backup according to the specified configfile(s),"
|
130
|
+
puts "when no configfile is specified and no information options are invoked,\nall existing configfiles are executed."
|
131
|
+
|
132
|
+
puts "\nBackup options:"
|
133
|
+
puts " -v, --verbose increase verbosity, ignoring the configfile option"
|
134
|
+
puts " --pass-rsync=OPTIONS pass OPTIONS to rsync before executing it"
|
135
|
+
puts " =>use with caution! usually this is not needed"
|
136
|
+
puts " -s, --status only usable when \"gtk2-ruby\" is installed"
|
137
|
+
puts " =>shows a gtk tray/status icon with the options"
|
138
|
+
puts " \"backup\", \"quit\" and as root a checkbox"
|
139
|
+
puts " \"shutdown after backup\""
|
140
|
+
|
141
|
+
puts "\nInformation options:"
|
142
|
+
puts "(won't work together with backup options)"
|
143
|
+
puts " -l, --list list available configfiles"
|
144
|
+
puts " -h, --help show this help"
|
145
|
+
puts " -V, --Version print version number"
|
146
|
+
puts "\nConfigfiles are stored in the directory /etc/smarbs \nor ~/.smarbs, if not executed as root."
|
147
|
+
end
|
148
|
+
|
149
|
+
def print_version
|
150
|
+
puts "smarbs " + SMARBS::VERSION.to_s + " (" + SMARBS::DATE.to_s + ")"
|
151
|
+
puts "Copyright (C) 2006-2009 by Jan Rueegg (rggjan)"
|
152
|
+
puts "<http://smarbs.sourceforge.net/>\n\n"
|
153
|
+
|
154
|
+
puts 'smarbs comes with ABSOLUTELY NO WARRANTY. This is free software, and you
|
155
|
+
are welcome to redistribute it under certain conditions. See the GNU
|
156
|
+
General Public Licence for details.'
|
157
|
+
end
|
158
|
+
|
159
|
+
public
|
160
|
+
|
161
|
+
def parse (arguments)
|
162
|
+
if not arguments.nil? then
|
163
|
+
@configfile_arguments = Array.new
|
164
|
+
@rsync_arguments = Array.new
|
165
|
+
infarg = Array.new #Information Arguments (--help or --version)
|
166
|
+
@verbose = false
|
167
|
+
@status = false
|
168
|
+
begin
|
169
|
+
arguments.each do
|
170
|
+
|argv|
|
171
|
+
case argv
|
172
|
+
when "--version", "-V"
|
173
|
+
infarg.push argv
|
174
|
+
when "--help", "-h", "-?"
|
175
|
+
infarg.push argv
|
176
|
+
when "--list", "-l"
|
177
|
+
infarg.push argv
|
178
|
+
when "--verbose", "-v"
|
179
|
+
@verbose=true
|
180
|
+
when /^--pass-rsync=/
|
181
|
+
@rsync_arguments.push(argv.gsub(/^--pass-rsync=/, ""))
|
182
|
+
when "--status", "-s"
|
183
|
+
@status=true
|
184
|
+
else
|
185
|
+
if not @configfile_names.include? argv then
|
186
|
+
raise "Unknown argument '#{argv}'!\nUse '--help' to see possible options."
|
187
|
+
end
|
188
|
+
@configfile_arguments.push(argv)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
if infarg.size == 0 then
|
193
|
+
backup
|
194
|
+
else
|
195
|
+
if @configfile_arguments.size != 0 or @verbose == true or @rsync_arguments.size != 0 then
|
196
|
+
raise "You cannot backup and use '#{infarg[0]}' at the same time."
|
197
|
+
end
|
198
|
+
infarg.uniq.each do
|
199
|
+
|arg|
|
200
|
+
case arg
|
201
|
+
when "--version", "-V"
|
202
|
+
print_version
|
203
|
+
when "--help", "-h", "-?"
|
204
|
+
print_help
|
205
|
+
when "--list", "-l"
|
206
|
+
puts "Available configfiles:"
|
207
|
+
puts @configfile_names
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
rescue RuntimeError
|
213
|
+
puts $!
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
data/lib/smarbs.rb
ADDED
data/lib/syslog.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Syslog wrapper class
|
4
|
+
# might be needed for reasons discussed on
|
5
|
+
# http://dev.animoto.com/articles/ruby-syslog-considered-harmful-or-at-least-undocumented
|
6
|
+
#
|
7
|
+
# Paavo Pokkinen (C) 2009
|
8
|
+
# License: GPL
|
9
|
+
#
|
10
|
+
# $Id$
|
11
|
+
|
12
|
+
require 'syslog'
|
13
|
+
|
14
|
+
class SyslogWrapper
|
15
|
+
|
16
|
+
def initialize()
|
17
|
+
# facility is "user", since there is no "backup" facility
|
18
|
+
# also, if we are running root, perhaps we should log with
|
19
|
+
# "daemon" facility?
|
20
|
+
#
|
21
|
+
# facilies and priorities are listed here:
|
22
|
+
# http://docstore.mik.ua/orelly/networking/puis/ch10_05.htm
|
23
|
+
@@log = Syslog.open('smarbs', Syslog::LOG_PID, Syslog::LOG_USER)
|
24
|
+
|
25
|
+
# normally log up to INFO level
|
26
|
+
# for debug, try DEBUG here (if we have any debug messages)
|
27
|
+
@@log.mask = Syslog::LOG_UPTO(Syslog::LOG_INFO)
|
28
|
+
end
|
29
|
+
|
30
|
+
def finalize()
|
31
|
+
# not sure if closing is really necessary,
|
32
|
+
# nothing bad really happens if we pass this...
|
33
|
+
@@log.close() if @@log
|
34
|
+
end
|
35
|
+
|
36
|
+
public
|
37
|
+
|
38
|
+
# logger function, does some input satitation, and logs to syslog
|
39
|
+
def log(msg="", level=1)
|
40
|
+
|
41
|
+
# Oh yes, we need to sanitaze here.
|
42
|
+
|
43
|
+
msg.gsub!(/%/, ' ') # '%' needs to be removed, else there will be runtimeError
|
44
|
+
msg.gsub!(/\n/, ' ')
|
45
|
+
msg.gsub!(/\0/, ' ')
|
46
|
+
|
47
|
+
# Ideally we would split the msg, and post in multiple parts
|
48
|
+
# but I guess this is enough, we should never have that long lines anyway
|
49
|
+
if msg.length >= 1024
|
50
|
+
msg = msg[0..1023]
|
51
|
+
end
|
52
|
+
|
53
|
+
# levels are:
|
54
|
+
# * 0: debug
|
55
|
+
# * 1: info
|
56
|
+
# * 2: notice
|
57
|
+
# * 3: warning
|
58
|
+
# * 4: error
|
59
|
+
|
60
|
+
case level
|
61
|
+
when 1
|
62
|
+
@@log.info(msg) unless msg.empty?
|
63
|
+
when 2
|
64
|
+
@@log.notice(msg) unless msg.empty?
|
65
|
+
when 3
|
66
|
+
@@log.warning(msg) unless msg.empty?
|
67
|
+
when 4
|
68
|
+
@@log.err(msg) unless msg.empty?
|
69
|
+
when 0
|
70
|
+
@@log.debug(msg) unless msg.empty?
|
71
|
+
else
|
72
|
+
# something tries to log with nonexisting level, or with too high level
|
73
|
+
# its probably better to halt the execution than log with wrong level, or not log at all
|
74
|
+
raise RuntimeError.new("Wrong loglevel defined somewhere! This is a bug.")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
if __FILE__ == $0
|
81
|
+
log = SyslogWrapper.new()
|
82
|
+
|
83
|
+
log.log("testing")
|
84
|
+
log.log("Just a notice.", 2)
|
85
|
+
log.log("Should not print", 0)
|
86
|
+
#log.log("We can't print with too high priority", 5)
|
87
|
+
|
88
|
+
# Torture test strings, from animoto's blog
|
89
|
+
test1 = (0..255).map{|x|"<#{x.chr}>\n" }.join
|
90
|
+
test2 = (1..3000).map{|x|"<#{(97+(x%26)).chr}>\n" }.join
|
91
|
+
log.log( test1 + test2 + "is this thing still on?")
|
92
|
+
|
93
|
+
# empty msg, should not print
|
94
|
+
log.log("")
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
# $Id$
|
data/lib/types.rb
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
# types.rb contains all the classes that might also be used in other projects,
|
2
|
+
# for example to handle files / directories or to represent sizes.
|
3
|
+
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
# Can be used to create directories, to get files within a directory or to see
|
7
|
+
# if a directory is writable.
|
8
|
+
class Directory
|
9
|
+
|
10
|
+
# Takes a (path)string as input and creates a directory there, if possible.
|
11
|
+
def initialize(directory, *args)
|
12
|
+
@dir = Directory.prepare(directory)
|
13
|
+
if not @dir[-1, 1].to_s == "/"
|
14
|
+
@dir.insert(-1, '/')
|
15
|
+
end
|
16
|
+
if args.include?(:writable)
|
17
|
+
if not Directory.writable?(File.dirname(@dir))
|
18
|
+
raise "#{@dir} is not writable!"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
if not File.directory?(@dir)
|
22
|
+
if not Directory.writable?(File.dirname(@dir))
|
23
|
+
raise "Directory '#{@dir}' is not existing!"
|
24
|
+
else
|
25
|
+
FileUtils.makedirs(@dir)
|
26
|
+
puts "#{@dir} created."
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
public
|
32
|
+
|
33
|
+
attr_reader(:dir)
|
34
|
+
|
35
|
+
# Checks if input is a valid argument and returns normalized string.
|
36
|
+
def Directory.prepare (input)
|
37
|
+
raise "Internal Error: Argument not a String" if input.class != String
|
38
|
+
input.strip!
|
39
|
+
raise "No directory specified!" if input.empty?
|
40
|
+
input.insert(0, '/') unless input[0, 1].to_s == "/"
|
41
|
+
return input
|
42
|
+
end
|
43
|
+
|
44
|
+
# Checks if the directory (relative to the root directory) is writable, or, if
|
45
|
+
# not yet existing, creatable.
|
46
|
+
def Directory.writable?(path)
|
47
|
+
Directory.prepare(path)
|
48
|
+
if File.exists?(path) and File.directory?(path)
|
49
|
+
return File.writable?(path)
|
50
|
+
end
|
51
|
+
return writable?(File.dirname(path))
|
52
|
+
end
|
53
|
+
|
54
|
+
# Compares if the two directories are equal.
|
55
|
+
def == (other_directory)
|
56
|
+
return @dir == other_directory.dir
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the normalized directory path as a string.
|
60
|
+
def to_s
|
61
|
+
@dir
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns how much free space there is (on the whole partition, using "df")
|
65
|
+
# as a Float of bytes
|
66
|
+
def free
|
67
|
+
path = to_s
|
68
|
+
`df --block-size=1 #{path}`.split[-3].to_f
|
69
|
+
end
|
70
|
+
|
71
|
+
# Gives back (in bytes) how much space is used for the whole Smarbsbackupdir.
|
72
|
+
#
|
73
|
+
# If ownpartition is true, it will use 'df' instead of 'du' which is much faster.
|
74
|
+
def space(ownpartition = false)
|
75
|
+
path = to_s
|
76
|
+
if ownpartition
|
77
|
+
`df --block-size=1 #{path}`.split[-4].to_f
|
78
|
+
else
|
79
|
+
`du -bcs #{path}`.split[-2].to_f
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns an array of files and folders that the Directory contains, excluding "." and "..".
|
84
|
+
#
|
85
|
+
# When 'all' is 'false', then it excludes files with a "~" at the end and all folders.
|
86
|
+
def files(all=true)
|
87
|
+
if all then
|
88
|
+
return Dir.entries(@dir) - ["."] - [".."]
|
89
|
+
else
|
90
|
+
configfile_names = Array.new
|
91
|
+
Dir.foreach(@dir) do |filename|
|
92
|
+
path = @dir+"/"+filename
|
93
|
+
if File.file?(path) and not path =~ /.*~/ # put every file without ~ at the end into the array
|
94
|
+
configfile_names.push(filename)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
return configfile_names
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# A class that represents sizes of directories. The main feature is to
|
103
|
+
# give back a size in a "human readable" format, like "5 MiB".
|
104
|
+
class Size
|
105
|
+
# Takes a size as a string. With no ending it is assumed as a size in bytes.
|
106
|
+
# Otherwise, (for example gvien a "32m") it is interpreted depending on the ending:
|
107
|
+
# g:: GiB
|
108
|
+
# m:: MiB
|
109
|
+
# k:: KiB
|
110
|
+
# Raises an error if an unexpected string is given (like "m32" or "32 MiB").
|
111
|
+
def initialize(string)
|
112
|
+
size = string.to_s.strip.downcase
|
113
|
+
case size
|
114
|
+
when "" then @size=0
|
115
|
+
when /g$/ then @size=$`.to_f * 1024**3
|
116
|
+
raise "Wrong Size format, '" + string + "' not valid!" unless $`.to_f.to_s == $` or $`.to_i.to_s == $`
|
117
|
+
when /m$/ then @size=$`.to_f * 1024**2
|
118
|
+
raise "Wrong Size format, '" + string + "' not valid!" unless $`.to_f.to_s == $` or $`.to_i.to_s == $`
|
119
|
+
when /k$/ then @size=$`.to_f * 1024
|
120
|
+
raise "Wrong Size format, '" + string + "' not valid!" unless $`.to_f.to_s == $` or $`.to_i.to_s == $`
|
121
|
+
else
|
122
|
+
@size=size.to_f
|
123
|
+
raise "Wrong Size format, '" + string + "' not valid!" unless size.to_f.to_s == size or size.to_i.to_s == size
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Returns a string which is "optimal" formatted, like "34.333 GiB", rounded
|
128
|
+
# to "dec" digits after the comma.
|
129
|
+
#
|
130
|
+
# Default dec: 2. A dec of "-1" means no rounding.
|
131
|
+
#
|
132
|
+
# Uses "B", "KiB", "MiB" or "GiB" as ending.
|
133
|
+
def opt(dec = 2)
|
134
|
+
case @size.abs
|
135
|
+
when 0..1023 then s, d=@size, " B"
|
136
|
+
when 1024..1024**2-1 then s, d=@size/1024, " KiB"
|
137
|
+
when 1024**2..1024**3-1 then s, d=@size/1024**2, " MiB"
|
138
|
+
else s, d=@size/1024**3, " GiB"
|
139
|
+
end
|
140
|
+
return dec==-1 ? s.to_s + d : ((s * 10**dec).round.to_f / 10**dec).to_s + d
|
141
|
+
end
|
142
|
+
|
143
|
+
# Returns a string which is "optimal" formatted, but with a short
|
144
|
+
# ending, like "34.33g", rounded to "dec" digits after the comma.
|
145
|
+
#
|
146
|
+
# Default dec: 2. A dec of "-1" means no rounding.
|
147
|
+
#
|
148
|
+
# Uses "", "k", "m" or "g" as ending.
|
149
|
+
def short(dec = 2)
|
150
|
+
case @size.abs
|
151
|
+
when 0..1023 then s, d=@size, ""
|
152
|
+
when 1024..1024**2-1 then s, d=@size.to_f/1024, "k"
|
153
|
+
when 1024**2..1024**3-1 then s, d=@size.to_f/1024**2, "m"
|
154
|
+
else s, d=@size.to_f/1024**3, "g"
|
155
|
+
end
|
156
|
+
return dec==-1 ? s.to_s + d : ((s * 10**dec).round.to_f / 10**dec).to_s + d
|
157
|
+
end
|
158
|
+
|
159
|
+
# Returns the size in bytes as an integer.
|
160
|
+
def b
|
161
|
+
@size.round
|
162
|
+
end
|
163
|
+
|
164
|
+
# Returns the size in bytes as a string.
|
165
|
+
def to_s
|
166
|
+
return @size.to_s
|
167
|
+
end
|
168
|
+
|
169
|
+
def ==(other)
|
170
|
+
return b == other.b
|
171
|
+
end
|
172
|
+
end
|
data/test/smarbs_test.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'smarbs'
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
module SMARBS
|
6
|
+
begin
|
7
|
+
remove_const "VERSION"
|
8
|
+
remove_const "DATE"
|
9
|
+
rescue NameError
|
10
|
+
end
|
11
|
+
VERSION = "0.9.1"
|
12
|
+
DATE = "4. September 2009"
|
13
|
+
end
|
14
|
+
|
15
|
+
module SmarbsTest
|
16
|
+
def clean
|
17
|
+
clean_all
|
18
|
+
`mkdir /tmp/smarbs`
|
19
|
+
`cd /tmp/smarbs && mkdir smarbs && mkdir backmeup && mkdir backedup`
|
20
|
+
end
|
21
|
+
|
22
|
+
def clean_all
|
23
|
+
`rm -r /tmp/smarbs` if File.exists?("/tmp/smarbs")
|
24
|
+
end
|
25
|
+
|
26
|
+
# give :nonequal to test for non-equality, :exact for exact results as argument
|
27
|
+
def assert_output(strings, *params)
|
28
|
+
output = with_stdout_captured { yield }
|
29
|
+
|
30
|
+
strings = [strings] if strings.class == String or strings.class == Regexp
|
31
|
+
strings.each do |string|
|
32
|
+
unless params.include? :nonequal
|
33
|
+
if params.include? :exact
|
34
|
+
assert_equal string, output
|
35
|
+
else
|
36
|
+
string = Regexp.new(string) if string.class == String
|
37
|
+
assert_match string, output
|
38
|
+
end
|
39
|
+
else
|
40
|
+
if params.include? :exact
|
41
|
+
assert_not_equal string, output
|
42
|
+
else
|
43
|
+
string = Regexp.new(string) if string.class == String
|
44
|
+
assert_no_match string, output
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def assert_success(string="") #TODO write tests
|
51
|
+
arg = ["Successfully backed up."]
|
52
|
+
if string.class == Array then
|
53
|
+
arg += string
|
54
|
+
else
|
55
|
+
arg << string
|
56
|
+
end
|
57
|
+
assert_output(arg) do
|
58
|
+
yield
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def assert_fail(string="")
|
63
|
+
arg = ["Backup NOT successful!"]
|
64
|
+
if string.class == Array then
|
65
|
+
arg += string
|
66
|
+
else
|
67
|
+
arg << string
|
68
|
+
end
|
69
|
+
assert_output(arg) do
|
70
|
+
yield
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def assert_backup_dirs(array)
|
75
|
+
actual = Dir.entries("/tmp/smarbs/backedup/smarbsbackup/")
|
76
|
+
actual.delete(".")
|
77
|
+
actual.delete("..")
|
78
|
+
actual.collect! {|name| name.slice(/-backup.*$/)}
|
79
|
+
actual.sort!
|
80
|
+
array.collect! {|name| "-backup." + name.to_s}
|
81
|
+
array.sort!
|
82
|
+
assert_equal(array, actual)
|
83
|
+
end
|
84
|
+
|
85
|
+
def default_configfile
|
86
|
+
cf = nil
|
87
|
+
assert_output "" do
|
88
|
+
cf = ConfigFile.new("/tmp/smarbs/smarbs/backup1")
|
89
|
+
end
|
90
|
+
cf.src = ["/tmp/smarbs/backmeup"]
|
91
|
+
cf.dest = "/tmp/smarbs/backedup"
|
92
|
+
cf.write
|
93
|
+
cf
|
94
|
+
end
|
95
|
+
|
96
|
+
def assert_raise_message(string, error = RuntimeError)
|
97
|
+
m = assert_raise( error ) {yield}
|
98
|
+
assert_equal(string, m.message)
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def with_stdout_captured
|
104
|
+
old_stdout = $stdout
|
105
|
+
out = StringIO.new
|
106
|
+
$stdout = out
|
107
|
+
begin
|
108
|
+
yield
|
109
|
+
ensure
|
110
|
+
$stdout = old_stdout
|
111
|
+
end
|
112
|
+
out.string
|
113
|
+
end
|
114
|
+
end
|