ruby-vnc 1.1.0 → 1.2.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.
- checksums.yaml +7 -0
- data/{ChangeLog → Changelog.rdoc} +9 -0
- data/{README → README.rdoc} +12 -0
- data/Rakefile +23 -38
- data/lib/cipher/vncdes.rb +54 -0
- data/lib/net/rfb/frame_buffer.rb +175 -0
- data/lib/net/vnc/version.rb +3 -4
- data/lib/net/vnc.rb +367 -300
- data/spec/cipher_vncdes_spec.rb +22 -0
- data/spec/net_vnc_spec.rb +130 -133
- data/spec/real_net_vnc_spec.rb +100 -0
- metadata +125 -61
- data/lib/cipher/des.rb +0 -439
- data/spec/cipher_des_spec.rb +0 -142
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'cipher/vncdes'
|
3
|
+
|
4
|
+
RSpec.describe Cipher::VNCDES do
|
5
|
+
it 'should pad key with zeroes if key is shorter than 8 characters' do
|
6
|
+
key = Cipher::VNCDES.new('test').key
|
7
|
+
|
8
|
+
expect(key.size).to eq 8
|
9
|
+
expect(key[4..7]).to eq(0.chr * 4)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should cut the key if the key is longer than 8 characters' do
|
13
|
+
expect(Cipher::VNCDES.new('iamdefinitelylongerthan8characters').key.size).to eq 8
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should correctly encrypt keys' do
|
17
|
+
encrypted_string = Cipher::VNCDES.new('matzisnicesowearenice').encrypt("\x9D\xBBU\n\x05b\x96L \b'&\x18\xCE(\xD8")
|
18
|
+
expect(encrypted_string.encoding.to_s).to eq 'UTF-8'
|
19
|
+
expect(encrypted_string.size).to eq 16
|
20
|
+
expect(encrypted_string).to eq "2\x95\xA7\xAE\xD4A\xF3\xDCt\x82d\e\xAE\x8A\xB9c"
|
21
|
+
end
|
22
|
+
end
|
data/spec/net_vnc_spec.rb
CHANGED
@@ -1,136 +1,133 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
require 'net/vnc'
|
3
3
|
|
4
|
-
|
5
|
-
class
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
vnc = VNC.open('192.168.0.1:0')
|
4
|
+
# class SocketMock
|
5
|
+
# class MockIOError < IOError
|
6
|
+
# end
|
7
|
+
#
|
8
|
+
# def initialize
|
9
|
+
# # this can be used to make detailed assertions
|
10
|
+
# @trace = []
|
11
|
+
# @read_buf = ''
|
12
|
+
# @write_buf = ''
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# def read len
|
16
|
+
# @trace << [:read, len]
|
17
|
+
# if @read_buf.length < len
|
18
|
+
# msg = 'bad socket read sequence - read(%d) but only %d byte(s) available' % [len, @read_buf.length]
|
19
|
+
# raise MockIOError, msg
|
20
|
+
# end
|
21
|
+
# @read_buf.slice! 0, len
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# def write data
|
25
|
+
# @trace << [:write, data]
|
26
|
+
# @write_buf << data
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# class VNCServerSocketMock < SocketMock
|
31
|
+
# TICK_TIME = 0.1
|
32
|
+
#
|
33
|
+
# def initialize(&block)
|
34
|
+
# super
|
35
|
+
#
|
36
|
+
# @pending_read = nil
|
37
|
+
# obj = self
|
38
|
+
# @t = Thread.new { block.call obj; @pending_read = nil }
|
39
|
+
# 100.times do |i|
|
40
|
+
# break if @pending_read
|
41
|
+
# if i == 99
|
42
|
+
# msg = 'blah'
|
43
|
+
# raise MockIOError, msg
|
44
|
+
# end
|
45
|
+
# sleep TICK_TIME
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# def run
|
50
|
+
# yield
|
51
|
+
# 100.times do |i|
|
52
|
+
# break unless @pending_read
|
53
|
+
# if i == 99
|
54
|
+
# msg = 'missing socket write sequence'
|
55
|
+
# raise MockIOError, msg
|
56
|
+
# end
|
57
|
+
# sleep TICK_TIME
|
58
|
+
# end
|
59
|
+
# raise 'wrote to much' if @write_buf.length != 0
|
60
|
+
# raise 'did not read enough' if @read_buf.length != 0
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# def read len
|
64
|
+
# @trace << [:read, len]
|
65
|
+
# 100.times do |i|
|
66
|
+
# break if @read_buf.length >= len
|
67
|
+
# if i == 99
|
68
|
+
# msg = 'timeout during socket read sequence - read(%d) but only %d byte(s) available' % [len, @read_buf.length]
|
69
|
+
# raise MockIOError, msg
|
70
|
+
# end
|
71
|
+
# sleep TICK_TIME
|
72
|
+
# end
|
73
|
+
# @read_buf.slice! 0, len
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# def write data
|
77
|
+
# unless @read_buf.empty?
|
78
|
+
# raise MockIOError, 'tried to write with non empty read buffer - (%p, %p)' % [@read_buf, data]
|
79
|
+
# end
|
80
|
+
# super
|
81
|
+
# if !@pending_read
|
82
|
+
# raise MockIOError, "wrote to socket but server is not expecting it"
|
83
|
+
# end
|
84
|
+
# if @write_buf.length >= @pending_read
|
85
|
+
# @pending_read = @write_buf.slice!(0, @pending_read)
|
86
|
+
# sleep TICK_TIME while @pending_read.is_a? String
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# def provide_data data
|
91
|
+
# @read_buf << data
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# def expect_data len
|
95
|
+
# @pending_read = len
|
96
|
+
# sleep TICK_TIME while @pending_read.is_a? Fixnum
|
97
|
+
# @pending_read
|
98
|
+
# end
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# describe 'Net::VNC' do
|
102
|
+
# VNC = Net::VNC
|
103
|
+
#
|
104
|
+
# it 'should do something' do
|
105
|
+
# =begin
|
106
|
+
# socket_mock.should_receive(:read).once.ordered.with(12).and_return("RFB 003.003\n")
|
107
|
+
# socket_mock.should_receive(:write).once.ordered.with(/^RFB (\d{3}.\d{3})\n$/)
|
108
|
+
# socket_mock.should_receive(:read).once.ordered.with(4).and_return([1].pack('N'))
|
109
|
+
# socket_mock.should_receive(:write).once.ordered.with("\000")
|
110
|
+
# socket_mock.should_receive(:read).once.ordered.with(20).and_return('')
|
111
|
+
# socket_mock.should_receive(:read).once.ordered.with(4).and_return([0].pack('N'))
|
112
|
+
# #m = mock('my mock')
|
113
|
+
# #m.should_receive(:test1).ordered.once.with('argument').and_return(1)
|
114
|
+
# #m.should_receive(:test2).ordered.once.with('argument').and_return(2)
|
115
|
+
# #p m.test1('argument')
|
116
|
+
# #p m.test2('argument')
|
117
|
+
# vnc = VNC.open('192.168.0.1:0')
|
119
118
|
#=end
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
end
|
135
|
-
=end
|
136
|
-
|
119
|
+
#
|
120
|
+
# server = VNCServerSocketMock.new do |s|
|
121
|
+
# s.provide_data "RFB 003.003\n"
|
122
|
+
# p :read => s.expect_data(12)
|
123
|
+
# s.provide_data [1].pack('N')
|
124
|
+
# p :read => s.expect_data(1)
|
125
|
+
# s.provide_data ' ' * 20
|
126
|
+
# s.provide_data [0].pack('N')
|
127
|
+
# end
|
128
|
+
# server.run do
|
129
|
+
# TCPSocket.should_receive(:open).with('192.168.0.1', 5900).and_return(server)
|
130
|
+
# vnc = VNC.open('192.168.0.1:0')
|
131
|
+
# end
|
132
|
+
# end
|
133
|
+
# end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'net/vnc'
|
3
|
+
|
4
|
+
NO_AUTH_SERVER_DISPLAY = ':1'.freeze
|
5
|
+
WITH_AUTH_SERVER_DISPLAY = ':2'.freeze
|
6
|
+
|
7
|
+
RSpec.describe Net::VNC do
|
8
|
+
context 'no auth' do
|
9
|
+
it 'should connect with no password' do
|
10
|
+
Net::VNC.open(NO_AUTH_SERVER_DISPLAY) do |vnc|
|
11
|
+
vnc.pointer_move(10, 15)
|
12
|
+
expect(vnc.pointer.x).to eq 10
|
13
|
+
expect(vnc.pointer.y).to eq 15
|
14
|
+
|
15
|
+
vnc.pointer_move(20, 25)
|
16
|
+
expect(vnc.pointer.x).to eq 20
|
17
|
+
expect(vnc.pointer.y).to eq 25
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should connect with password even though it is not needed' do
|
22
|
+
Net::VNC.open(NO_AUTH_SERVER_DISPLAY, password: 'password') do |vnc|
|
23
|
+
vnc.pointer_move(10, 15)
|
24
|
+
expect(vnc.pointer.x).to eq 10
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with auth' do
|
30
|
+
it 'should connect with a password' do
|
31
|
+
Net::VNC.open(WITH_AUTH_SERVER_DISPLAY, password: 'matzisnicesowearenice') do |vnc|
|
32
|
+
vnc.pointer_move(10, 15)
|
33
|
+
expect(vnc.pointer.x).to eq 10
|
34
|
+
expect(vnc.pointer.y).to eq 15
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should give error with a wrong password' do
|
39
|
+
expect do
|
40
|
+
Net::VNC.open(WITH_AUTH_SERVER_DISPLAY,
|
41
|
+
password: 'wrongPasssword')
|
42
|
+
end.to raise_error(RuntimeError, 'Unable to authenticate - 1')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should give error with no password' do
|
46
|
+
expect do
|
47
|
+
Net::VNC.open(WITH_AUTH_SERVER_DISPLAY)
|
48
|
+
end.to raise_error(RuntimeError, 'Need to authenticate but no password given')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'screenshotting' do
|
53
|
+
def verify_screenshot(input)
|
54
|
+
image_size = ImageSize.path(input)
|
55
|
+
expect(image_size.format).to eq :png
|
56
|
+
expect(image_size.width).to eq 1366
|
57
|
+
expect(image_size.height).to eq 768
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should allow you to take a screenshot with a path' do
|
61
|
+
Tempfile.open('ruby-vnc-spec') do |screenshotfile|
|
62
|
+
Net::VNC.open(NO_AUTH_SERVER_DISPLAY) do |vnc|
|
63
|
+
vnc.pointer_move(10, 15)
|
64
|
+
vnc.take_screenshot(screenshotfile.path)
|
65
|
+
end
|
66
|
+
verify_screenshot(screenshotfile.path)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should allow you to take a screenshot with a blob' do
|
71
|
+
Tempfile.open('ruby-vnc-spec-blob') do |screenshotfile|
|
72
|
+
vnc = Net::VNC.open(NO_AUTH_SERVER_DISPLAY)
|
73
|
+
begin
|
74
|
+
vnc.pointer_move(10, 15)
|
75
|
+
blob = vnc.take_screenshot(nil)
|
76
|
+
screenshotfile.write(blob)
|
77
|
+
ensure
|
78
|
+
vnc.close
|
79
|
+
end
|
80
|
+
|
81
|
+
verify_screenshot(screenshotfile.path)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should allow you to take a screenshot with a IO-object' do
|
86
|
+
screenshotfile = File.new('out.png', 'w')
|
87
|
+
|
88
|
+
begin
|
89
|
+
Net::VNC.open(NO_AUTH_SERVER_DISPLAY) do |vnc|
|
90
|
+
vnc.pointer_move(10, 15)
|
91
|
+
vnc.take_screenshot(screenshotfile)
|
92
|
+
end
|
93
|
+
verify_screenshot(screenshotfile)
|
94
|
+
ensure
|
95
|
+
screenshotfile.close
|
96
|
+
File.delete(screenshotfile)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
metadata
CHANGED
@@ -1,82 +1,146 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-vnc
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease: false
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 1
|
9
|
-
- 0
|
10
|
-
version: 1.1.0
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.2.0
|
11
5
|
platform: ruby
|
12
|
-
authors:
|
6
|
+
authors:
|
13
7
|
- Charles Lowe
|
14
|
-
autorequire:
|
8
|
+
autorequire:
|
15
9
|
bindir: bin
|
16
10
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
11
|
+
date: 2021-09-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: chunky_png
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.3.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.3.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: vncrec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.0.6
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.0.6
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: image_size
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '12.3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '12.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.7'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.7'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.16'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.16'
|
22
97
|
description: A library which implements the client VNC protocol to control VNC servers.
|
23
98
|
email: aquasync@gmail.com
|
24
99
|
executables: []
|
25
|
-
|
26
100
|
extensions: []
|
27
|
-
|
28
|
-
|
29
|
-
-
|
30
|
-
|
31
|
-
files:
|
32
|
-
- Rakefile
|
33
|
-
- README
|
101
|
+
extra_rdoc_files:
|
102
|
+
- README.rdoc
|
103
|
+
- Changelog.rdoc
|
104
|
+
files:
|
34
105
|
- COPYING
|
35
|
-
-
|
106
|
+
- Changelog.rdoc
|
107
|
+
- README.rdoc
|
108
|
+
- Rakefile
|
36
109
|
- data/keys.yaml
|
37
|
-
- lib/cipher/
|
110
|
+
- lib/cipher/vncdes.rb
|
111
|
+
- lib/net/rfb/frame_buffer.rb
|
38
112
|
- lib/net/vnc.rb
|
39
113
|
- lib/net/vnc/version.rb
|
114
|
+
- spec/cipher_vncdes_spec.rb
|
40
115
|
- spec/net_vnc_spec.rb
|
41
|
-
- spec/
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
post_install_message:
|
47
|
-
rdoc_options:
|
48
|
-
- --main
|
49
|
-
- README
|
50
|
-
- --title
|
116
|
+
- spec/real_net_vnc_spec.rb
|
117
|
+
homepage: https://github.com/kaspergrubbe/ruby-vnc
|
118
|
+
licenses:
|
119
|
+
- MIT
|
120
|
+
metadata: {}
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options:
|
123
|
+
- "--main"
|
124
|
+
- README.rdoc
|
125
|
+
- "--title"
|
51
126
|
- ruby-vnc documentation
|
52
|
-
- --tab-width
|
53
|
-
-
|
54
|
-
require_paths:
|
127
|
+
- "--tab-width"
|
128
|
+
- '2'
|
129
|
+
require_paths:
|
55
130
|
- lib
|
56
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
-
|
58
|
-
requirements:
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
59
133
|
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
version: "0"
|
65
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
-
none: false
|
67
|
-
requirements:
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: 2.5.0
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
68
138
|
- - ">="
|
69
|
-
- !ruby/object:Gem::Version
|
70
|
-
|
71
|
-
segments:
|
72
|
-
- 0
|
73
|
-
version: "0"
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
74
141
|
requirements: []
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
signing_key:
|
79
|
-
specification_version: 3
|
142
|
+
rubygems_version: 3.2.3
|
143
|
+
signing_key:
|
144
|
+
specification_version: 4
|
80
145
|
summary: Ruby VNC library.
|
81
146
|
test_files: []
|
82
|
-
|