rvc 1.5.0 → 1.6.0
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/README.rdoc +1 -1
- data/Rakefile +2 -1
- data/VERSION +1 -1
- data/bin/rvc +53 -9
- data/lib/rvc/completion.rb +57 -19
- data/lib/rvc/extensions/ComputeResource.rb +2 -2
- data/lib/rvc/extensions/DVPortSetting.rb +108 -0
- data/lib/rvc/extensions/Datacenter.rb +19 -4
- data/lib/rvc/extensions/Datastore.rb +6 -1
- data/lib/rvc/extensions/DistributedVirtualPort.rb +146 -0
- data/lib/rvc/extensions/DistributedVirtualPortgroup.rb +274 -10
- data/lib/rvc/extensions/DistributedVirtualSwitch.rb +124 -3
- data/lib/rvc/extensions/Folder.rb +9 -2
- data/lib/rvc/extensions/HostSystem.rb +60 -0
- data/lib/rvc/extensions/ManagedEntity.rb +19 -0
- data/lib/rvc/extensions/ParaVirtualSCSIController.rb +25 -0
- data/lib/rvc/extensions/PerfCounterInfo.rb +26 -0
- data/lib/rvc/extensions/PerformanceManager.rb +83 -0
- data/lib/rvc/extensions/ResourcePool.rb +21 -0
- data/lib/rvc/extensions/VirtualDevice.rb +59 -0
- data/lib/rvc/extensions/VirtualDisk.rb +25 -0
- data/lib/rvc/extensions/VirtualEthernetCard.rb +32 -0
- data/lib/rvc/extensions/VirtualMachine.rb +112 -1
- data/lib/rvc/field.rb +122 -0
- data/lib/rvc/filesystem_session.rb +20 -0
- data/lib/rvc/inventory.rb +35 -12
- data/lib/rvc/known_hosts.rb +20 -0
- data/lib/rvc/memory_session.rb +20 -0
- data/lib/rvc/modules.rb +67 -7
- data/lib/rvc/modules/alarm.rb +37 -0
- data/lib/rvc/modules/basic.rb +172 -41
- data/lib/rvc/modules/cluster.rb +18 -2
- data/lib/rvc/modules/core.rb +63 -0
- data/lib/rvc/modules/datastore.rb +158 -0
- data/lib/rvc/modules/device.rb +275 -0
- data/lib/rvc/modules/esxcli.rb +193 -0
- data/lib/rvc/modules/find.rb +125 -0
- data/lib/rvc/modules/issue.rb +33 -0
- data/lib/rvc/modules/perf.rb +284 -0
- data/lib/rvc/modules/permissions.rb +20 -0
- data/lib/rvc/modules/resource_pool.rb +69 -0
- data/lib/rvc/modules/role.rb +23 -3
- data/lib/rvc/modules/snapshot.rb +20 -0
- data/lib/rvc/modules/vds.rb +605 -0
- data/lib/rvc/modules/vim.rb +103 -26
- data/lib/rvc/modules/vm.rb +93 -220
- data/lib/rvc/modules/vnc.rb +50 -13
- data/lib/rvc/option_parser.rb +50 -2
- data/lib/rvc/readline-ffi.rb +2 -1
- data/lib/rvc/shell.rb +34 -33
- data/lib/rvc/util.rb +120 -2
- data/test/test_fs.rb +9 -5
- data/test/test_metric.rb +79 -0
- metadata +33 -3
data/lib/rvc/modules/vnc.rb
CHANGED
@@ -18,7 +18,7 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
-
VNC = ENV['VNC'] || search_path('
|
21
|
+
VNC = ENV['VNC'] || search_path('tightvnc') || search_path('vncviewer') || search_path('vinagre')
|
22
22
|
|
23
23
|
opts :view do
|
24
24
|
summary "Spawn a VNC client"
|
@@ -29,8 +29,8 @@ rvc_alias :view, :vnc
|
|
29
29
|
rvc_alias :view, :V
|
30
30
|
|
31
31
|
def view vm
|
32
|
-
ip = reachable_ip vm.runtime.host
|
33
|
-
extraConfig = vm.config.extraConfig
|
32
|
+
ip = reachable_ip vm.collect('runtime.host')[0]
|
33
|
+
extraConfig, = vm.collect('config.extraConfig')
|
34
34
|
already_enabled = extraConfig.find { |x| x.key == 'RemoteDisplay.vnc.enabled' && x.value.downcase == 'true' }
|
35
35
|
if already_enabled
|
36
36
|
puts "VNC already enabled"
|
@@ -68,7 +68,7 @@ end
|
|
68
68
|
|
69
69
|
|
70
70
|
def reachable_ip host
|
71
|
-
ips = host.config.network.vnic.map { |x| x.spec.ip.ipAddress }
|
71
|
+
ips = host.collect('config.network.vnic')[0].map { |x| x.spec.ip.ipAddress }
|
72
72
|
ips.find do |x|
|
73
73
|
begin
|
74
74
|
Timeout.timeout(1) { TCPSocket.new(x, 443).close; true }
|
@@ -95,17 +95,54 @@ def vnc_password
|
|
95
95
|
end
|
96
96
|
|
97
97
|
# Override this to spawn a VNC client differently
|
98
|
+
#
|
99
|
+
# We can save the vnc pasword out to a file, then call vncviewer with it
|
100
|
+
# directly so we don't need to "password" auth.
|
98
101
|
def vnc_client ip, port, password
|
99
|
-
|
100
|
-
fork do
|
101
|
-
$stderr.reopen("#{ENV['HOME']||'.'}/.rvc-vmrc.log", "w")
|
102
|
-
Process.setpgrp
|
103
|
-
exec VNC, "#{ip}:#{port}"
|
104
|
-
end
|
105
|
-
puts "spawning #{VNC}"
|
106
|
-
puts "#{ip}:#{port} password: #{password}"
|
107
|
-
else
|
102
|
+
unless VNC
|
108
103
|
puts "no VNC client configured"
|
109
104
|
puts "#{ip}:#{port} password: #{password}"
|
105
|
+
return false
|
106
|
+
end
|
107
|
+
|
108
|
+
if File.basename(VNC) == 'vncviewer' # or other vnc clients that support the same -passwd
|
109
|
+
tightvnc = %x(#{VNC} --version 2>&1).lines.first['TightVNC'] != nil
|
110
|
+
file = Tempfile.new('rvcvncpass')
|
111
|
+
filename = file.path
|
112
|
+
begin
|
113
|
+
if tightvnc
|
114
|
+
IO.popen("vncpasswd -f > #{filename}", 'w+') do |vncpass|
|
115
|
+
vncpass.puts password
|
116
|
+
vncpass.puts password
|
117
|
+
end
|
118
|
+
else
|
119
|
+
IO.popen("vncpasswd #{filename}", 'w+') do |vncpass|
|
120
|
+
vncpass.puts password
|
121
|
+
vncpass.puts password
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
vnc_client_connect ip, port, password, "-passwd #{filename}"
|
126
|
+
ensure
|
127
|
+
sleep 3 # we have to do this, as the vncviewer forks, and we've no simple way of working out if that thread has read the file yet.
|
128
|
+
file.close
|
129
|
+
file.unlink
|
130
|
+
end
|
131
|
+
else
|
132
|
+
vnc_client_connect ip, port, password
|
110
133
|
end
|
111
134
|
end
|
135
|
+
|
136
|
+
def vnc_client_connect ip, port, password, vnc_opts=nil
|
137
|
+
fork do
|
138
|
+
$stdout.reopen("#{ENV['HOME']||'.'}/.rvc-vnc.log", "w")
|
139
|
+
$stderr.reopen("#{ENV['HOME']||'.'}/.rvc-vnc.err", "w")
|
140
|
+
Process.setpgrp
|
141
|
+
exec [ VNC, vnc_opts, "#{ip}:#{port}" ].join ' '
|
142
|
+
end
|
143
|
+
puts "spawning #{VNC}"
|
144
|
+
print "#{ip}:#{port} password: #{password}"
|
145
|
+
print " options: #{vnc_opts}" unless vnc_opts.nil?
|
146
|
+
puts
|
147
|
+
end
|
148
|
+
|
data/lib/rvc/option_parser.rb
CHANGED
@@ -20,6 +20,13 @@
|
|
20
20
|
|
21
21
|
require 'trollop'
|
22
22
|
|
23
|
+
begin
|
24
|
+
require 'chronic'
|
25
|
+
RVC::HAVE_CHRONIC = true
|
26
|
+
rescue LoadError
|
27
|
+
RVC::HAVE_CHRONIC = false
|
28
|
+
end
|
29
|
+
|
23
30
|
module RVC
|
24
31
|
|
25
32
|
class OptionParser < Trollop::Parser
|
@@ -33,7 +40,10 @@ class OptionParser < Trollop::Parser
|
|
33
40
|
@seen_not_required = false
|
34
41
|
@seen_multi = false
|
35
42
|
@applicable = Set.new
|
36
|
-
super
|
43
|
+
super() do
|
44
|
+
opt :help, "Show this message", :short => 'h'
|
45
|
+
instance_eval &b
|
46
|
+
end
|
37
47
|
end
|
38
48
|
|
39
49
|
def summary str
|
@@ -47,7 +57,9 @@ class OptionParser < Trollop::Parser
|
|
47
57
|
|
48
58
|
def opt name, *a
|
49
59
|
super
|
50
|
-
|
60
|
+
spec = @specs[name]
|
61
|
+
@applicable << spec[:lookup] if spec[:lookup]
|
62
|
+
spec[:type] = :string if spec[:lookup] || spec[:lookup_parent]
|
51
63
|
@has_options = true unless name == :help
|
52
64
|
end
|
53
65
|
|
@@ -112,6 +124,16 @@ class OptionParser < Trollop::Parser
|
|
112
124
|
return args, opts
|
113
125
|
end
|
114
126
|
|
127
|
+
def parse_date_parameter param, arg
|
128
|
+
if RVC::HAVE_CHRONIC
|
129
|
+
Chronic.parse(param)
|
130
|
+
else
|
131
|
+
Time.parse param
|
132
|
+
end
|
133
|
+
rescue
|
134
|
+
raise ::Trollop::CommandlineError, "option '#{arg}' needs a time"
|
135
|
+
end
|
136
|
+
|
115
137
|
def postprocess_arg x, spec
|
116
138
|
if spec[:lookup]
|
117
139
|
RVC::Util.lookup!(x, spec[:lookup]).
|
@@ -138,4 +160,30 @@ class OptionParser < Trollop::Parser
|
|
138
160
|
end
|
139
161
|
end
|
140
162
|
|
163
|
+
class RawOptionParser
|
164
|
+
attr_reader :applicable
|
165
|
+
|
166
|
+
def initialize cmd, summary
|
167
|
+
@cmd = cmd
|
168
|
+
@summary = summary
|
169
|
+
@applicable = []
|
170
|
+
end
|
171
|
+
|
172
|
+
def summary?
|
173
|
+
@summary
|
174
|
+
end
|
175
|
+
|
176
|
+
def parse args
|
177
|
+
[args, {}]
|
178
|
+
end
|
179
|
+
|
180
|
+
def has_options?
|
181
|
+
false
|
182
|
+
end
|
183
|
+
|
184
|
+
def educate
|
185
|
+
# XXX
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
141
189
|
end
|
data/lib/rvc/readline-ffi.rb
CHANGED
@@ -22,7 +22,8 @@ require 'ffi'
|
|
22
22
|
|
23
23
|
module RVC::ReadlineFFI
|
24
24
|
extend FFI::Library
|
25
|
-
|
25
|
+
libreadline = ENV['RVC_READLINE'] == nil ? 'readline.so' : ENV['RVC_READLINE']
|
26
|
+
ffi_lib libreadline
|
26
27
|
callback :rl_linebuf_func_t, [ :string, :int ], :bool
|
27
28
|
attach_variable :rl_char_is_quoted_p, :rl_char_is_quoted_p, :rl_linebuf_func_t
|
28
29
|
attach_variable :rl_line_buffer, :rl_line_buffer, :string
|
data/lib/rvc/shell.rb
CHANGED
@@ -58,7 +58,7 @@ class Shell
|
|
58
58
|
end
|
59
59
|
rescue SystemExit, IOError
|
60
60
|
raise
|
61
|
-
rescue RVC::Util::UserError, RuntimeError, RbVmomi::Fault
|
61
|
+
rescue RVC::Util::UserError, RuntimeError, RbVmomi::Fault, Trollop::CommandlineError
|
62
62
|
if ruby or debug
|
63
63
|
puts "#{$!.class}: #{$!.message}"
|
64
64
|
puts $!.backtrace * "\n"
|
@@ -73,51 +73,52 @@ class Shell
|
|
73
73
|
rescue Exception
|
74
74
|
puts "#{$!.class}: #{$!.message}"
|
75
75
|
puts $!.backtrace * "\n"
|
76
|
+
ensure
|
77
|
+
$stdout.flush
|
76
78
|
end
|
77
79
|
end
|
78
80
|
|
79
|
-
def
|
81
|
+
def self.parse_input input
|
80
82
|
cmd, *args = Shellwords.shellwords(input)
|
81
|
-
return unless cmd
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
elsif ALIASES.member? cmd
|
90
|
-
module_name, cmd, = ALIASES[cmd].split '.'
|
91
|
-
else
|
92
|
-
RVC::Util.err "unknown alias #{cmd}"
|
93
|
-
end
|
94
|
-
|
95
|
-
m = MODULES[module_name] or RVC::Util.err("unknown module #{module_name}")
|
83
|
+
return nil unless cmd
|
84
|
+
if cmd.include? '.'
|
85
|
+
module_name, cmd, = cmd.split '.'
|
86
|
+
elsif ALIASES.member? cmd
|
87
|
+
module_name, cmd, = ALIASES[cmd].split '.'
|
88
|
+
end
|
89
|
+
[MODULES[module_name], cmd.to_sym, args]
|
90
|
+
end
|
96
91
|
|
97
|
-
|
98
|
-
|
92
|
+
def eval_command input
|
93
|
+
m, cmd, args = Shell.parse_input input
|
94
|
+
RVC::Util.err "invalid command" unless m != nil and
|
95
|
+
cmd.is_a? Symbol and
|
96
|
+
m.has_command? cmd
|
97
|
+
parser = m.opts_for(cmd)
|
99
98
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
99
|
+
begin
|
100
|
+
args, opts = parser.parse args
|
101
|
+
rescue Trollop::HelpNeeded
|
102
|
+
parser.educate
|
103
|
+
return
|
104
|
+
end
|
106
105
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
end
|
106
|
+
if parser.has_options?
|
107
|
+
m.send cmd.to_sym, *(args + [opts])
|
108
|
+
else
|
109
|
+
m.send cmd.to_sym, *args
|
112
110
|
end
|
113
|
-
nil
|
114
111
|
end
|
115
112
|
|
116
113
|
def eval_ruby input, file="<input>"
|
117
114
|
result = @ruby_evaluator.do_eval input, file
|
118
115
|
if $interactive
|
119
116
|
if input =~ /\#$/
|
120
|
-
|
117
|
+
if result.is_a? Class
|
118
|
+
introspect_class result
|
119
|
+
else
|
120
|
+
introspect_object result
|
121
|
+
end
|
121
122
|
else
|
122
123
|
pp result
|
123
124
|
end
|
@@ -205,7 +206,7 @@ class RubyEvaluator
|
|
205
206
|
rescue Exception => e
|
206
207
|
bt = e.backtrace
|
207
208
|
bt = bt.reverse.drop_while { |x| !(x =~ /toplevel/) }.reverse
|
208
|
-
bt[-1].gsub! ':in `toplevel\'', ''
|
209
|
+
bt[-1].gsub! ':in `toplevel\'', '' if bt[-1]
|
209
210
|
e.set_backtrace bt
|
210
211
|
raise
|
211
212
|
end
|
data/lib/rvc/util.rb
CHANGED
@@ -18,6 +18,8 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
+
require 'delegate'
|
22
|
+
|
21
23
|
module RVC
|
22
24
|
module Util
|
23
25
|
extend self
|
@@ -104,6 +106,7 @@ module Util
|
|
104
106
|
end
|
105
107
|
|
106
108
|
def progress tasks
|
109
|
+
results = {}
|
107
110
|
interested = %w(info.progress info.state info.entityName info.error info.name)
|
108
111
|
connection = single_connection tasks
|
109
112
|
connection.serviceInstance.wait_for_multiple_tasks interested, tasks do |h|
|
@@ -120,8 +123,10 @@ module Util
|
|
120
123
|
$stdout.write "\e[K#{text}#{progress_bar}\n"
|
121
124
|
elsif state == 'error'
|
122
125
|
error = props['info.error']
|
126
|
+
results[task] = error
|
123
127
|
$stdout.write "\e[K#{name} #{entityName}: #{error.fault.class.wsdl_name}: #{error.localizedMessage}\n"
|
124
128
|
else
|
129
|
+
results[task] = task.info.result if state == 'success'
|
125
130
|
$stdout.write "\e[K#{name} #{entityName}: #{state}\n"
|
126
131
|
end
|
127
132
|
end
|
@@ -130,7 +135,13 @@ module Util
|
|
130
135
|
end
|
131
136
|
end
|
132
137
|
$stdout.write "\e[#{tasks.size}B" if interactive?
|
133
|
-
|
138
|
+
results
|
139
|
+
end
|
140
|
+
|
141
|
+
def one_progress task
|
142
|
+
progress([task])[task].tap do |r|
|
143
|
+
raise r if r.is_a? VIM::LocalizedMethodFault
|
144
|
+
end
|
134
145
|
end
|
135
146
|
|
136
147
|
def terminal_columns
|
@@ -160,7 +171,7 @@ module Util
|
|
160
171
|
tcsetpgrp
|
161
172
|
exec cmd
|
162
173
|
end
|
163
|
-
Process.waitpid2 pid
|
174
|
+
Process.waitpid2 pid rescue nil
|
164
175
|
tcsetpgrp
|
165
176
|
nil
|
166
177
|
end
|
@@ -194,5 +205,112 @@ module Util
|
|
194
205
|
|
195
206
|
Hash[results.map { |r| [r['name'], r.obj] }]
|
196
207
|
end
|
208
|
+
|
209
|
+
def status_color str, status
|
210
|
+
$terminal.color(str, *VIM::ManagedEntity::STATUS_COLORS[status])
|
211
|
+
end
|
212
|
+
|
213
|
+
def metric num
|
214
|
+
MetricNumber.new(num.to_f, '', false).to_s
|
215
|
+
end
|
216
|
+
|
217
|
+
def retrieve_fields objs, fields
|
218
|
+
Hash[objs.map do |o|
|
219
|
+
begin
|
220
|
+
[o, Hash[fields.map { |f| [f, o.field(f)] }]]
|
221
|
+
rescue VIM::ManagedObjectNotFound
|
222
|
+
next
|
223
|
+
end
|
224
|
+
end]
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
class Numeric
|
230
|
+
def metric
|
231
|
+
RVC::Util.metric self
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
class TimeDiff < SimpleDelegator
|
236
|
+
def to_s
|
237
|
+
i = self.to_i
|
238
|
+
seconds = i % 60
|
239
|
+
i /= 60
|
240
|
+
minutes = i % 60
|
241
|
+
i /= 60
|
242
|
+
hours = i
|
243
|
+
[hours, minutes, seconds].join ':'
|
244
|
+
end
|
245
|
+
|
246
|
+
def self.parse str
|
247
|
+
a = str.split(':', 3).reverse
|
248
|
+
seconds = a[0].to_i rescue 0
|
249
|
+
minutes = a[1].to_i rescue 0
|
250
|
+
hours = a[2].to_i rescue 0
|
251
|
+
TimeDiff.new(hours * 3600 + minutes * 60 + seconds)
|
252
|
+
end
|
197
253
|
end
|
254
|
+
|
255
|
+
class MetricNumber < SimpleDelegator
|
256
|
+
attr_reader :unit, :binary
|
257
|
+
|
258
|
+
def initialize val, unit, binary=false
|
259
|
+
@unit = unit
|
260
|
+
@binary = binary
|
261
|
+
super val.to_f
|
262
|
+
end
|
263
|
+
|
264
|
+
def to_s
|
265
|
+
limit = @binary ? 1024 : 1000
|
266
|
+
if self < limit
|
267
|
+
prefix = ''
|
268
|
+
multiple = 1
|
269
|
+
else
|
270
|
+
prefixes = @binary ? BINARY_PREFIXES : DECIMAL_PREFIXES
|
271
|
+
prefixes = prefixes.sort_by { |k,v| v }
|
272
|
+
prefix, multiple = prefixes.find { |k,v| self/v < limit }
|
273
|
+
prefix, multiple = prefixes.last unless prefix
|
274
|
+
end
|
275
|
+
("%0.2f %s%s" % [self/multiple, prefix, @unit]).strip
|
276
|
+
end
|
277
|
+
|
278
|
+
# http://physics.nist.gov/cuu/Units/prefixes.html
|
279
|
+
DECIMAL_PREFIXES = {
|
280
|
+
'k' => 10 ** 3,
|
281
|
+
'M' => 10 ** 6,
|
282
|
+
'G' => 10 ** 9,
|
283
|
+
'T' => 10 ** 12,
|
284
|
+
'P' => 10 ** 15,
|
285
|
+
}
|
286
|
+
|
287
|
+
# http://physics.nist.gov/cuu/Units/binary.html
|
288
|
+
BINARY_PREFIXES = {
|
289
|
+
'Ki' => 2 ** 10,
|
290
|
+
'Mi' => 2 ** 20,
|
291
|
+
'Gi' => 2 ** 30,
|
292
|
+
'Ti' => 2 ** 40,
|
293
|
+
'Pi' => 2 ** 50,
|
294
|
+
}
|
295
|
+
|
296
|
+
CANONICAL_PREFIXES = Hash[(DECIMAL_PREFIXES.keys + BINARY_PREFIXES.keys).map { |x| [x.downcase, x] }]
|
297
|
+
|
298
|
+
def self.parse str
|
299
|
+
if str =~ /^([0-9,.]+)\s*([kmgtp]i?)?/i
|
300
|
+
x = $1.delete(',').to_f
|
301
|
+
binary = false
|
302
|
+
if $2
|
303
|
+
prefix = $2.downcase
|
304
|
+
binary = prefix[1..1] == 'i'
|
305
|
+
prefixes = binary ? BINARY_PREFIXES : DECIMAL_PREFIXES
|
306
|
+
multiple = prefixes[CANONICAL_PREFIXES[prefix]]
|
307
|
+
else
|
308
|
+
multiple = 1
|
309
|
+
end
|
310
|
+
units = $'
|
311
|
+
new x*multiple, units, binary
|
312
|
+
else
|
313
|
+
raise "Problem parsing SI number #{str.inspect}"
|
314
|
+
end
|
315
|
+
end
|
198
316
|
end
|
data/test/test_fs.rb
CHANGED
@@ -18,16 +18,20 @@ class FSTest < Test::Unit::TestCase
|
|
18
18
|
|
19
19
|
def setup
|
20
20
|
@context = RVC::FS.new Root
|
21
|
+
session = RVC::MemorySession.new
|
22
|
+
$shell = RVC::Shell.new session
|
23
|
+
$shell.instance_variable_set :@fs, @context
|
21
24
|
end
|
22
25
|
|
23
26
|
def teardown
|
24
27
|
@context = nil
|
28
|
+
$shell = nil
|
25
29
|
end
|
26
30
|
|
27
31
|
def test_new
|
28
32
|
assert_equal Root, @context.cur
|
29
33
|
assert_equal "", @context.display_path
|
30
|
-
assert_equal 0,
|
34
|
+
assert_equal 0, $shell.session.marks.size
|
31
35
|
assert_equal [['', Root]], @context.cur.rvc_path
|
32
36
|
end
|
33
37
|
|
@@ -93,20 +97,20 @@ class FSTest < Test::Unit::TestCase
|
|
93
97
|
assert_equal nil, obj
|
94
98
|
|
95
99
|
['foo', '~', '7', ''].each do |mark|
|
96
|
-
|
100
|
+
$shell.session.set_mark mark, [b_obj]
|
97
101
|
obj = @context.lookup("~#{mark}")[0]
|
98
102
|
assert_equal [['', Root], ['a', NodeA], ['b', NodeB]], obj.rvc_path
|
99
103
|
|
100
|
-
|
104
|
+
$shell.session.set_mark mark, []
|
101
105
|
obj = @context.lookup("~#{mark}")[0]
|
102
106
|
assert_equal nil, obj
|
103
107
|
end
|
104
108
|
|
105
|
-
|
109
|
+
$shell.session.set_mark '7', [b_obj]
|
106
110
|
obj = @context.lookup("7")[0]
|
107
111
|
assert_equal [['', Root], ['a', NodeA], ['b', NodeB]], obj.rvc_path
|
108
112
|
|
109
|
-
|
113
|
+
$shell.session.set_mark '7', []
|
110
114
|
obj = @context.lookup("7")[0]
|
111
115
|
assert_equal nil, obj
|
112
116
|
end
|