smarbs 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|