skadategems-dev 0.0.9
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/.gitignore +16 -0
- data/.travis.yml +12 -0
- data/Gemfile +13 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +74 -0
- data/Rakefile +6 -0
- data/bin/skadate +9 -0
- data/bin/skadategems-dev +18 -0
- data/integration/README.md +97 -0
- data/integration/cli/skadate_init_spec.rb +86 -0
- data/integration/cli/skadate_spec.rb +50 -0
- data/integration/cli/spec_helper.rb +23 -0
- data/integration/lib/remote/download_file_spec.rb +136 -0
- data/integration/spec_helper.rb +101 -0
- data/lib/skadategems/dev/bundle.rb +115 -0
- data/lib/skadategems/dev/cli/remote.rb +364 -0
- data/lib/skadategems/dev/cli/skadate.rb +184 -0
- data/lib/skadategems/dev/remote.rb +170 -0
- data/lib/skadategems/dev/remote/clone_logger.rb +118 -0
- data/lib/skadategems/dev/remote/configs.rb +97 -0
- data/lib/skadategems/dev/remote/directory.rb +88 -0
- data/lib/skadategems/dev/remote/file.rb +157 -0
- data/lib/skadategems/dev/remote/includes/mysql_connect.php +8 -0
- data/lib/skadategems/dev/skadate.rb +30 -0
- data/lib/skadategems/dev/software_version.rb +66 -0
- data/lib/skadategems/dev/source_controller.rb +101 -0
- data/lib/skadategems/dev/version.rb +5 -0
- data/skadategems-dev.gemspec +33 -0
- data/spec/skadategems/dev/bundle_spec.rb +268 -0
- data/spec/skadategems/dev/cli/remote_spec.rb +623 -0
- data/spec/skadategems/dev/cli/skadate_spec.rb +182 -0
- data/spec/skadategems/dev/remote/clone_logger_spec.rb +324 -0
- data/spec/skadategems/dev/remote/directory_spec.rb +117 -0
- data/spec/skadategems/dev/remote/file_spec.rb +74 -0
- data/spec/skadategems/dev/remote_spec.rb +95 -0
- data/spec/skadategems/dev/skadate_spec.rb +62 -0
- data/spec/skadategems/dev/software_version_spec.rb +92 -0
- data/spec/skadategems/dev/source_controller_spec.rb +243 -0
- data/spec/spec_helper.rb +32 -0
- metadata +153 -0
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'skadategems/dev/cli/skadate'
|
3
|
+
|
4
|
+
module SkadateGems::Dev
|
5
|
+
module CLI
|
6
|
+
|
7
|
+
describe Skadate do
|
8
|
+
let(:skadate) { SkadateGems::Dev::Skadate.new('/path/to/skadate') }
|
9
|
+
let(:version) { '9.2.2960' }
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
allow(SkadateGems::Dev::Skadate)
|
13
|
+
.to receive(:new)
|
14
|
+
.and_return(skadate)
|
15
|
+
|
16
|
+
allow(skadate.source)
|
17
|
+
.to receive(:parse_version_definition)
|
18
|
+
.and_return(version)
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '$> skadate version' do
|
22
|
+
def invoke!(section)
|
23
|
+
@stdout = capture(:stdout) do
|
24
|
+
Skadate.start ['version', section]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'list #' do
|
29
|
+
it 'prints out SoftwareVersion::LIST' do
|
30
|
+
invoke! 'list'
|
31
|
+
|
32
|
+
lines = @stdout.lines.map { |l| l[2..-2] }
|
33
|
+
|
34
|
+
SoftwareVersion::LIST.each do |version|
|
35
|
+
expect(lines).to include "skadate-#{version.to_s} [#{version.release_date}]"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when current working directory is a Skadate source root' do
|
40
|
+
it 'marks current version with asterisk (*)' do
|
41
|
+
invoke! 'list'
|
42
|
+
|
43
|
+
expect(@stdout.lines).to include "* skadate-9.2.2960 [2013-04-16]\n"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'about #' do
|
49
|
+
it 'prints out all about' do
|
50
|
+
invoke! 'about'
|
51
|
+
|
52
|
+
expect(@stdout.lines).to eq([
|
53
|
+
"Skadate Software 9.2.2960 (Master) [previous]\n",
|
54
|
+
"Release date: April 16, 2013\n",
|
55
|
+
"Copyright (c) 2004-#{Date.today.year} Skalfa LLC\n"
|
56
|
+
])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'number #' do
|
61
|
+
it 'prints out the version number' do
|
62
|
+
invoke! 'number'
|
63
|
+
expect(@stdout).to eq "9.2.2960\n"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'build #' do
|
68
|
+
it 'prints out the build number' do
|
69
|
+
invoke! 'build'
|
70
|
+
expect(@stdout).to eq "2960\n"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when the `source#header_inc_php` file can not be found' do
|
75
|
+
before(:each) do
|
76
|
+
allow(skadate.source)
|
77
|
+
.to receive(:parse_version_definition)
|
78
|
+
.and_raise(Errno::ENOENT)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'failures with an error' do
|
82
|
+
expect { invoke! 'about' }.to raise_error Errno::ENOENT
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe '$> skadate init' do
|
88
|
+
def skadate_init!(arguments)
|
89
|
+
@stdout = capture(:stdout) do
|
90
|
+
Skadate.start( %w[init].concat(arguments) )
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
before(:each) do
|
95
|
+
allow(File)
|
96
|
+
.to receive(:exists?)
|
97
|
+
.with('/path/to/skadate/.dev')
|
98
|
+
.and_return(true)
|
99
|
+
|
100
|
+
allow(File)
|
101
|
+
.to receive(:exists?)
|
102
|
+
.with('/path/to/skadate/.dev/.htaccess')
|
103
|
+
.and_return(true)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'creates a `.dev/` directory if not exists' do
|
107
|
+
expect(File)
|
108
|
+
.to receive(:exists?)
|
109
|
+
.with('/path/to/skadate/.dev')
|
110
|
+
.and_return(false)
|
111
|
+
|
112
|
+
expect(FileUtils)
|
113
|
+
.to receive(:mkdir)
|
114
|
+
.with('/path/to/skadate/.dev')
|
115
|
+
|
116
|
+
skadate_init! []
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'generates a `.dev/.htaccess` file with the "Deny from all" instruction' do
|
120
|
+
expect(File)
|
121
|
+
.to receive(:exists?)
|
122
|
+
.with('/path/to/skadate/.dev/.htaccess')
|
123
|
+
.and_return(false)
|
124
|
+
|
125
|
+
expect(File)
|
126
|
+
.to receive(:write)
|
127
|
+
.with('/path/to/skadate/.dev/.htaccess', <<-HTACCESS)
|
128
|
+
Order Deny,Allow
|
129
|
+
Deny from all
|
130
|
+
HTACCESS
|
131
|
+
|
132
|
+
skadate_init! []
|
133
|
+
end
|
134
|
+
|
135
|
+
context 'when the `--remote-url` option is given' do
|
136
|
+
let(:execphp_ssa) do
|
137
|
+
double ExecPHP::ServerSideAccessor,
|
138
|
+
:access_token => 'super-secret',
|
139
|
+
:generate => nil
|
140
|
+
end
|
141
|
+
|
142
|
+
let(:execphp_config) do
|
143
|
+
double ExecPHP::RemoteServer,
|
144
|
+
:save_as => nil
|
145
|
+
end
|
146
|
+
|
147
|
+
before(:each) do
|
148
|
+
allow(ExecPHP::ServerSideAccessor)
|
149
|
+
.to receive(:new)
|
150
|
+
.and_return(execphp_ssa)
|
151
|
+
|
152
|
+
allow(ExecPHP::RemoteServer)
|
153
|
+
.to receive(:new)
|
154
|
+
.and_return(execphp_config)
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'generates a `.dev/remote-server.json` configuration file' do
|
158
|
+
expect(ExecPHP::RemoteServer)
|
159
|
+
.to receive(:new)
|
160
|
+
.with('http://skadate-driven-site.com/exec.php', 'super-secret')
|
161
|
+
.and_return(execphp_config)
|
162
|
+
|
163
|
+
expect(execphp_config)
|
164
|
+
.to receive(:save_as)
|
165
|
+
.with('/path/to/skadate/.dev/remote-server.json')
|
166
|
+
|
167
|
+
skadate_init! %w[--remote-url http://skadate-driven-site.com/]
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'generates an `exec.php` (server-side script runner) file' do
|
171
|
+
expect(execphp_ssa)
|
172
|
+
.to receive(:generate)
|
173
|
+
.with('/path/to/skadate/exec.php')
|
174
|
+
|
175
|
+
skadate_init! %w[--remote-url http://skadate-driven-site.com/]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,324 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'skadategems/dev/remote/clone_logger'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
module SkadateGems::Dev
|
6
|
+
class Remote
|
7
|
+
|
8
|
+
describe CloneLogger do
|
9
|
+
let(:io_probe) { StringIO.new }
|
10
|
+
let(:logger) { CloneLogger.new(io_probe) }
|
11
|
+
|
12
|
+
describe '.start' do
|
13
|
+
|
14
|
+
it 'opens a new log file for writing' do
|
15
|
+
expect(::File)
|
16
|
+
.to receive(:open)
|
17
|
+
.with('/path/to/file.log', 'w')
|
18
|
+
|
19
|
+
CloneLogger.start('/path/to/file.log') { }
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'instantiates a new logger giving a `log_file_io` reference' do
|
23
|
+
allow(::File).to receive(:open).and_yield(io_probe)
|
24
|
+
|
25
|
+
expect(CloneLogger).to receive(:new)
|
26
|
+
.with(io_probe).and_return(logger)
|
27
|
+
|
28
|
+
CloneLogger.start('...') { }
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'yields a logger instance' do
|
32
|
+
allow(::File).to receive(:open).and_yield(io_probe)
|
33
|
+
|
34
|
+
allow(CloneLogger).to receive(:new)
|
35
|
+
.with(io_probe).and_return(logger)
|
36
|
+
|
37
|
+
expect { |b| CloneLogger.start('...', &b) }.to yield_with_args(logger)
|
38
|
+
end
|
39
|
+
|
40
|
+
=begin
|
41
|
+
it 'ensures that the logger is flushed' do
|
42
|
+
allow(::File).to receive(:open).and_yield(io_probe)
|
43
|
+
|
44
|
+
allow(CloneLogger).to receive(:new)
|
45
|
+
.with(io_probe).and_return(logger)
|
46
|
+
|
47
|
+
expect(logger).to receive(:flush).twice
|
48
|
+
|
49
|
+
CloneLogger.start { } # 1
|
50
|
+
|
51
|
+
expect do
|
52
|
+
CloneLogger.start { raise StandardError } # 2
|
53
|
+
end.to raise_error(StandardError)
|
54
|
+
end
|
55
|
+
=end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#initialize' do
|
60
|
+
it 'assigns @file_io' do
|
61
|
+
expect(logger.instance_variable_get(:@file_io)).to eq io_probe
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'assigns @mute = false' do
|
65
|
+
expect(logger.instance_variable_get(:@mute)).to eq false
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'assigns @padding = 0' do
|
69
|
+
expect(logger.instance_variable_get(:@padding)).to be_zero
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#puts' do
|
74
|
+
def puts!(message, color = nil)
|
75
|
+
@stdout = capture(:stdout) do
|
76
|
+
logger.puts(message, color)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'prints out a given string' do
|
81
|
+
puts! '>> Yay!'
|
82
|
+
expect(@stdout).to eq ">> Yay!\n"
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'writes a given string to a log file' do
|
86
|
+
puts! '>> Yay!'
|
87
|
+
expect(io_probe.string).to eq ">> Yay!\n"
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'when a color is given' do
|
91
|
+
before(:each) do
|
92
|
+
expect(logger).to receive(:set_color)
|
93
|
+
.with('>> Yay!', :yellow)
|
94
|
+
.and_return('[>> Yay!]')
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'uses #set_color to colorize a string' do
|
98
|
+
puts! '>> Yay!', :yellow
|
99
|
+
expect(@stdout).to eq "[>> Yay!]\n"
|
100
|
+
end
|
101
|
+
|
102
|
+
it "doesn't affect to a written log" do
|
103
|
+
puts! '>> Yay!', :yellow
|
104
|
+
expect(io_probe.string).to eq ">> Yay!\n"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '#entry' do
|
110
|
+
let(:padding) { nil }
|
111
|
+
|
112
|
+
def remote_dir(path)
|
113
|
+
Directory.new(double, path)
|
114
|
+
end
|
115
|
+
|
116
|
+
def remote_file(path, size)
|
117
|
+
File.new(double, path, size)
|
118
|
+
end
|
119
|
+
|
120
|
+
def push!(entry, status = nil, label = nil, &block)
|
121
|
+
@stdout = capture(:stdout) do
|
122
|
+
logger.padding = padding if padding
|
123
|
+
logger.entry(entry, status, label, &block)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'for directory `/` (app root)' do
|
128
|
+
before(:each) do
|
129
|
+
push!(remote_dir '/') { }
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'prints "=> / ...\n"' do
|
133
|
+
expect(@stdout).to eq "=> / ...\n"
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'writes "=> /\n"' do
|
137
|
+
expect(io_probe.string).to eq "=> /\n"
|
138
|
+
end
|
139
|
+
|
140
|
+
context 'with @padding = 1' do
|
141
|
+
let(:padding) { 1 }
|
142
|
+
|
143
|
+
it 'prints " => / ...\n"' do
|
144
|
+
expect(@stdout).to eq " => / ...\n"
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'writes " => /\n"' do
|
148
|
+
expect(io_probe.string).to eq " => /\n"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'with @padding = 2' do
|
153
|
+
let(:padding) { 2 }
|
154
|
+
|
155
|
+
it 'prints " => / ...\n"' do
|
156
|
+
expect(@stdout).to eq " => / ...\n"
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'writes " => /\n"' do
|
160
|
+
expect(io_probe.string).to eq " => /\n"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context 'for directory `/internals/API`' do
|
166
|
+
before(:each) do
|
167
|
+
push!(remote_dir '/internals/API') { }
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'prints "=> /internals/API ...\n"' do
|
171
|
+
expect(@stdout).to eq "=> /internals/API ...\n"
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'writes "=> /internals/API\n"' do
|
175
|
+
expect(io_probe.string).to eq "=> /internals/API\n"
|
176
|
+
end
|
177
|
+
|
178
|
+
context 'with @padding = 1' do
|
179
|
+
let(:padding) { 1 }
|
180
|
+
|
181
|
+
it 'prints " => /internals/API ...\n"' do
|
182
|
+
expect(@stdout).to eq " => /internals/API ...\n"
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'writes " => /internals/API\n"' do
|
186
|
+
expect(io_probe.string).to eq " => /internals/API\n"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context 'with @padding = 2' do
|
191
|
+
let(:padding) { 2 }
|
192
|
+
|
193
|
+
it 'prints " => /internals/API ...\n"' do
|
194
|
+
expect(@stdout).to eq " => /internals/API ...\n"
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'writes " => /internals/API\n"' do
|
198
|
+
io_probe.seek(0)
|
199
|
+
expect(io_probe.read).to eq " => /internals/API\n"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context 'for directory `/internal_c` (skipped by pathname)' do
|
205
|
+
before(:each) do
|
206
|
+
push! remote_dir('/internal_c'), :skipped, 'skipped by pathname'
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'prints "*> /internal_c [skipped by pathname]\n"' do
|
210
|
+
expect(@stdout).to eq "*> /internal_c [skipped by pathname]\n"
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'writes "*> /internal_c [skipped by pathname]\n"' do
|
214
|
+
expect(io_probe.string).to eq "*> /internal_c [skipped by pathname]\n"
|
215
|
+
end
|
216
|
+
|
217
|
+
context 'with @padding = 1' do
|
218
|
+
let(:padding) { 1 }
|
219
|
+
|
220
|
+
it 'prints " *> /internal_c [skipped by pathname]\n"' do
|
221
|
+
expect(@stdout).to eq " *> /internal_c [skipped by pathname]\n"
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'writes " *> /internal_c [skipped by pathname]\n"' do
|
225
|
+
expect(io_probe.string).to eq " *> /internal_c [skipped by pathname]\n"
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
context 'with @padding = 2' do
|
230
|
+
let(:padding) { 2 }
|
231
|
+
|
232
|
+
it 'prints " *> /internal_c [skipped by pathname]\n"' do
|
233
|
+
expect(@stdout).to eq " *> /internal_c [skipped by pathname]\n"
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'writes " *> /internal_c [skipped by pathname]\n"' do
|
237
|
+
expect(io_probe.string).to eq " *> /internal_c [skipped by pathname]\n"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
context 'for file `/index.php` (95 B) [loading...]' do
|
243
|
+
before(:each) do
|
244
|
+
push!(remote_file('/index.php', 95)) { }
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'prints "~> index.php ...\n"' do
|
248
|
+
expect(@stdout).to eq "~> index.php (95 B) ...\n"
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'writes "~> index.php\n"' do
|
252
|
+
expect(io_probe.string).to eq "~> index.php (95 B)\n"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
context 'for file `/cron/heavy.php` (1,225) [downloaded]' do
|
257
|
+
before(:each) do
|
258
|
+
push!(remote_file('/cron/heavy.php', 1_225)) { :downloaded }
|
259
|
+
end
|
260
|
+
|
261
|
+
it 'prints "~> heavy.php (1.2 KiB) ... [downloaded]\n"' do
|
262
|
+
expect(@stdout).to eq "~> heavy.php (1.2 KiB) ... [downloaded]\n"
|
263
|
+
end
|
264
|
+
|
265
|
+
it 'writes "~> heavy.php (1.2 KiB) [downloaded]\n"' do
|
266
|
+
expect(io_probe.string).to eq "~> heavy.php (1.2 KiB) [downloaded]\n"
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
context 'for file `/install/skadate9.sql` (9,491,203) [unchanged]' do
|
271
|
+
before(:each) do
|
272
|
+
push!(remote_file('/install/skadate9.sql', 9_491_203)) { :unchanged }
|
273
|
+
end
|
274
|
+
|
275
|
+
it 'prints "~> skadate9.sql (9 MiB) ... [unchanged]\n"' do
|
276
|
+
expect(@stdout).to eq "~> skadate9.sql (9 MiB) ... [unchanged]\n"
|
277
|
+
end
|
278
|
+
|
279
|
+
it 'writes "~> skadate9.sql (9 MiB) [unchanged]\n"' do
|
280
|
+
expect(io_probe.string).to eq "~> skadate9.sql (9 MiB) [unchanged]\n"
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
context 'for the `/path/to/nonexistent.php` (0) [error]' do
|
285
|
+
|
286
|
+
class TestError < StandardError; end
|
287
|
+
|
288
|
+
it 'prints "~> nonexistent.php (0 B) [error]\n"' do
|
289
|
+
@stdout = $stdout
|
290
|
+
$stdout = StringIO.new
|
291
|
+
|
292
|
+
begin
|
293
|
+
file = remote_file('/path/to/nonexistent.php', 0)
|
294
|
+
logger.entry(file) { raise TestError }
|
295
|
+
rescue TestError
|
296
|
+
# just be quiet...
|
297
|
+
ensure
|
298
|
+
expect($stdout.string).to eq "~> nonexistent.php (0 B) ... [error]\n"
|
299
|
+
$stdout = @stdout
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'writes "~> nonexistent.php (0 B) [error]\n"' do
|
304
|
+
begin
|
305
|
+
file = remote_file('/path/to/nonexistent.php', 0)
|
306
|
+
push!(file) { raise TestError, 'Uh! Ogh!' }
|
307
|
+
rescue TestError
|
308
|
+
# ...
|
309
|
+
ensure
|
310
|
+
expect(io_probe.string).to eq <<-ERROR
|
311
|
+
~> nonexistent.php (0 B) [error]
|
312
|
+
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
313
|
+
[SkadateGems::Dev::Remote::TestError]: Uh! Ogh!
|
314
|
+
------------------------------
|
315
|
+
ERROR
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
end
|
324
|
+
end
|