memory_tracker 1.0.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/Gemfile +13 -0
- data/Gemfile.lock +73 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +31 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/app/controllers/memory_tracker/dashboards_controller.rb +24 -0
- data/app/helpers/memory_tracker/dashboards_helper.rb +7 -0
- data/app/views/layouts/memory_tracker.html.erb +29 -0
- data/app/views/memory_tracker/dashboards/index.html.erb +21 -0
- data/config/routes.rb +3 -0
- data/docs/design.rb +111 -0
- data/lib/memory_tracker.rb +3 -0
- data/lib/memory_tracker/engine.rb +23 -0
- data/lib/memory_tracker/env.rb +14 -0
- data/lib/memory_tracker/gc_stat.rb +63 -0
- data/lib/memory_tracker/memory_tracker.rb +55 -0
- data/lib/memory_tracker/middleware.rb +20 -0
- data/lib/memory_tracker/request.rb +33 -0
- data/lib/memory_tracker/stores/gcstat_logfile_store.rb +39 -0
- data/lib/memory_tracker/stores/in_memory_store.rb +120 -0
- data/lib/memory_tracker/stores/url_logfile_store.rb +46 -0
- data/memory_tracker.gemspec +86 -0
- data/spec/lib/memory_tracker_spec.rb +47 -0
- data/spec/lib/request_spec.rb +30 -0
- data/spec/lib/stores/gcstat_logfile_store_spec.rb +17 -0
- data/spec/lib/stores/in_memory_store_spec.rb +161 -0
- data/spec/lib/stores/url_logfile_store_spec.rb +17 -0
- data/spec/spec_helper.rb +35 -0
- metadata +173 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module MemoryTracker
|
4
|
+
describe MemoryTracker do
|
5
|
+
before :each do
|
6
|
+
@env = double('env')
|
7
|
+
allow(@env).to receive(:controller) { 'Boat' }
|
8
|
+
allow(@env).to receive(:action) { 'sail' }
|
9
|
+
|
10
|
+
@memory_store = double('memory_store')
|
11
|
+
@logfile_store = double('logfile_store')
|
12
|
+
@tracker = MemoryTracker.instance
|
13
|
+
@tracker.stores.clear
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should be a singleton' do
|
17
|
+
lambda { MemoryTracker.new }.should raise_error(NoMethodError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should push requests to all stores' do
|
21
|
+
allow(@memory_store).to receive(:name) { :memory }
|
22
|
+
allow(@logfile_store).to receive(:name) { :gcstat_logfile }
|
23
|
+
expect(@memory_store).to receive(:push)
|
24
|
+
expect(@logfile_store).to receive(:push)
|
25
|
+
|
26
|
+
@tracker.add_store(@memory_store)
|
27
|
+
@tracker.add_store(@logfile_store)
|
28
|
+
|
29
|
+
@tracker.start_request(@env)
|
30
|
+
@tracker.end_request
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should populate livestore' do
|
34
|
+
@tracker.add_store(Stores::InMemoryStore::Manager.new)
|
35
|
+
|
36
|
+
Request.stub(:rss) { 100 }
|
37
|
+
@tracker.start_request(@env)
|
38
|
+
Request.stub(:rss) { 108 }
|
39
|
+
@tracker.end_request
|
40
|
+
|
41
|
+
stats = @tracker.stats(:memory)
|
42
|
+
stats.count('Boat', 'sail').should == 1
|
43
|
+
stats.count('Boat', 'moor').should == 0
|
44
|
+
stats.fetch('Boat', 'sail', :rss).should == 8
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module MemoryTracker
|
4
|
+
describe Request do
|
5
|
+
before :each do
|
6
|
+
@env = double('env')
|
7
|
+
@request = Request.new(@env)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should initalize start_gcstat' do
|
11
|
+
@request.start_gcstat.should be_a(GcStat)
|
12
|
+
@request.start_gcstat.stats.keys.should include :rss
|
13
|
+
@request.start_gcstat.stats.keys.should include :vsize
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should have a controller' do
|
17
|
+
allow(@env).to receive(:controller) { 'Foo' }
|
18
|
+
allow(@env).to receive(:action) { :bar }
|
19
|
+
@request.controller.should == 'Foo'
|
20
|
+
@request.action.should == :bar
|
21
|
+
end
|
22
|
+
|
23
|
+
context :close do
|
24
|
+
it 'should initialize end_gcstat' do
|
25
|
+
@request.close
|
26
|
+
@request.end_gcstat.should be_a(GcStat)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module MemoryTracker
|
2
|
+
module Stores
|
3
|
+
describe GcstatLogfileStore do
|
4
|
+
before :each do
|
5
|
+
logger_class = double("logger_class")
|
6
|
+
allow(logger_class).to receive(:new)
|
7
|
+
@logstore = GcstatLogfileStore.new logger_class, "foo/log"
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'implements the store role' do
|
11
|
+
@logstore.should respond_to(:name)
|
12
|
+
@logstore.should respond_to(:push)
|
13
|
+
@logstore.should respond_to(:stats)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
def sample_stats(factor=1)
|
4
|
+
base = {
|
5
|
+
:rss => 1,
|
6
|
+
:vsize => 2,
|
7
|
+
:count => 4,
|
8
|
+
:heap_used => 86,
|
9
|
+
:heap_length => 138,
|
10
|
+
:heap_increment =>52,
|
11
|
+
:heap_live_num =>34768,
|
12
|
+
:heap_free_num =>22099,
|
13
|
+
:heap_final_num =>0,
|
14
|
+
:total_allocated_object =>68540,
|
15
|
+
:total_freed_object =>33772
|
16
|
+
}
|
17
|
+
sample = {}
|
18
|
+
base.each { |k,v| sample[k] = v * factor }
|
19
|
+
sample
|
20
|
+
end
|
21
|
+
|
22
|
+
def stub_gcstat_delta(controller, action, factor=1)
|
23
|
+
allow(@env).to receive(:controller) { controller }
|
24
|
+
allow(@env).to receive(:action) { action }
|
25
|
+
MemoryTracker::GcStatDelta.any_instance.stub(:stats) { sample_stats(factor) }
|
26
|
+
end
|
27
|
+
|
28
|
+
module MemoryTracker
|
29
|
+
module Stores
|
30
|
+
module InMemoryStore
|
31
|
+
describe Manager do
|
32
|
+
def start_time
|
33
|
+
Time.new(2013,01,01,0,0,0)
|
34
|
+
end
|
35
|
+
|
36
|
+
before :each do
|
37
|
+
@env = double('env')
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should respond to name' do
|
41
|
+
manager = Manager.new(60)
|
42
|
+
manager.should respond_to(:name)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should return stats from older window' do
|
46
|
+
Time.stub(:now).and_return(start_time)
|
47
|
+
manager = Manager.new(60)
|
48
|
+
request = Request.new(@env)
|
49
|
+
stub_gcstat_delta('Boat', 'sail')
|
50
|
+
manager.push(request.close)
|
51
|
+
Time.stub(:now).and_return(start_time + 10)
|
52
|
+
request = Request.new(@env)
|
53
|
+
stub_gcstat_delta('Car', 'drive', 2)
|
54
|
+
manager.push(request.close)
|
55
|
+
Time.stub(:now).and_return(start_time + 40)
|
56
|
+
request = Request.new(@env)
|
57
|
+
stub_gcstat_delta('Boat', 'sail', 3)
|
58
|
+
manager.push(request.close)
|
59
|
+
|
60
|
+
stats = manager.stats
|
61
|
+
stats.fetch('Boat', 'sail', :rss).should == 4
|
62
|
+
stats.fetch('Car', 'drive', :rss).should == 2
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should rotate windows' do
|
66
|
+
Time.stub(:now).and_return(start_time)
|
67
|
+
manager = Manager.new(60)
|
68
|
+
request = Request.new(@env)
|
69
|
+
stub_gcstat_delta('Boat', 'sail')
|
70
|
+
manager.push(request.close)
|
71
|
+
Time.stub(:now).and_return(start_time + 10)
|
72
|
+
request = Request.new(@env)
|
73
|
+
stub_gcstat_delta('Car', 'drive', 2)
|
74
|
+
manager.push(request.close)
|
75
|
+
Time.stub(:now).and_return(start_time + 40)
|
76
|
+
request = Request.new(@env)
|
77
|
+
stub_gcstat_delta('Boat', 'sail', 3)
|
78
|
+
manager.push(request.close)
|
79
|
+
Time.stub(:now).and_return(start_time + 70)
|
80
|
+
|
81
|
+
stats = manager.stats
|
82
|
+
stats.fetch('Boat', 'sail', :rss).should == 3
|
83
|
+
stats.fetch('Car', 'drive', :rss).should be_nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe StatInterval do
|
88
|
+
|
89
|
+
before :each do
|
90
|
+
@interval = InMemoryStore::StatInterval.new(Time.now, 5*60)
|
91
|
+
@env = double('env')
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should accept requests' do
|
95
|
+
env = double('env')
|
96
|
+
request = Request.new(env)
|
97
|
+
allow(env).to receive(:controller) { :Foo }
|
98
|
+
allow(env).to receive(:action) { :bar }
|
99
|
+
request.close
|
100
|
+
request.controller.should == :Foo
|
101
|
+
request.action.should == :bar
|
102
|
+
@interval.push(request)
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should accumulate one request' do
|
106
|
+
req1 = Request.new(@env)
|
107
|
+
req1.close
|
108
|
+
stub_gcstat_delta('Boat', 'sail')
|
109
|
+
@interval.push(req1)
|
110
|
+
@interval.stats.should be_a(Stats)
|
111
|
+
@interval.stats.fetch('Boat', 'sail', :rss).should == 1
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should accumulate several requests' do
|
115
|
+
req1 = Request.new(@env)
|
116
|
+
req1.close
|
117
|
+
stub_gcstat_delta('Boat', 'sail')
|
118
|
+
@interval.push(req1)
|
119
|
+
req2 = Request.new(@env)
|
120
|
+
req2.close
|
121
|
+
stub_gcstat_delta('Boat', 'sail', 2)
|
122
|
+
@interval.push(req2)
|
123
|
+
req3 = Request.new(@env)
|
124
|
+
req3.close
|
125
|
+
stub_gcstat_delta('Boat', 'moor', 5)
|
126
|
+
@interval.push(req3)
|
127
|
+
req4 = Request.new(@env)
|
128
|
+
req4.close
|
129
|
+
stub_gcstat_delta('Car', 'drive', 1)
|
130
|
+
@interval.push(req4)
|
131
|
+
|
132
|
+
stats = @interval.stats
|
133
|
+
stats.count('Boat', 'sail').should == 2
|
134
|
+
stats.fetch('Boat', 'sail', :rss).should == 3
|
135
|
+
stats.fetch('Boat', 'sail', :count).should == 12
|
136
|
+
stats.count('Boat', 'moor').should == 1
|
137
|
+
stats.fetch('Boat', 'moor', :rss).should == 5
|
138
|
+
stats.fetch('Boat', 'moor', :count).should == 20
|
139
|
+
stats.count('Car', 'drive').should == 1
|
140
|
+
stats.fetch('Car', 'drive', :rss).should == 1
|
141
|
+
stats.fetch('Car', 'drive', :count).should == 4
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'should be enumerable' do
|
145
|
+
req1 = Request.new(@env)
|
146
|
+
req1.close
|
147
|
+
stub_gcstat_delta('Boat', 'sail')
|
148
|
+
@interval.push(req1)
|
149
|
+
req2 = Request.new(@env)
|
150
|
+
req2.close
|
151
|
+
stub_gcstat_delta('Boat', 'moor')
|
152
|
+
@interval.push(req2)
|
153
|
+
|
154
|
+
@interval.size.should == 2
|
155
|
+
@interval.to_a.should include(['Boat', 'sail', { :num => 1, :gcstat => sample_stats} ])
|
156
|
+
@interval.to_a.should include(['Boat', 'moor', { :num => 1, :gcstat => sample_stats} ])
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module MemoryTracker
|
2
|
+
module Stores
|
3
|
+
describe UrlLogfileStore do
|
4
|
+
before :each do
|
5
|
+
logger_class = double("logger_class")
|
6
|
+
allow(logger_class).to receive(:new)
|
7
|
+
@logstore = UrlLogfileStore.new logger_class, "foo/log"
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'implements the store role' do
|
11
|
+
@logstore.should respond_to(:name)
|
12
|
+
@logstore.should respond_to(:push)
|
13
|
+
@logstore.should respond_to(:stats)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'singleton'
|
5
|
+
|
6
|
+
require 'rspec'
|
7
|
+
require 'sys/proctable'
|
8
|
+
require 'memory_tracker'
|
9
|
+
require 'memory_tracker/gc_stat'
|
10
|
+
require 'memory_tracker/request'
|
11
|
+
require 'memory_tracker/memory_tracker'
|
12
|
+
require 'memory_tracker/stores/in_memory_store'
|
13
|
+
require 'memory_tracker/stores/gcstat_logfile_store'
|
14
|
+
require 'memory_tracker/stores/url_logfile_store'
|
15
|
+
|
16
|
+
# This file is copied to spec/ when you run 'rails generate rspec:install'
|
17
|
+
ENV["RAILS_ENV"] ||= 'test'
|
18
|
+
# require 'rspec/rails'
|
19
|
+
# require 'rspec/mocks/standalone'
|
20
|
+
|
21
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
22
|
+
# in spec/support/ and its subdirectories.
|
23
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
24
|
+
|
25
|
+
|
26
|
+
RSpec.configure do |config|
|
27
|
+
# == Mock Framework
|
28
|
+
#
|
29
|
+
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
|
30
|
+
#
|
31
|
+
# config.mock_with :mocha
|
32
|
+
# config.mock_with :flexmock
|
33
|
+
# config.mock_with :rr
|
34
|
+
# config.mock_with :rspec
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: memory_tracker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Philippe Le Rohellec
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-11-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sys-proctable
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: debugger
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.14.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.14.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rdoc
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.12'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.12'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: jeweler
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.8.7
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.8.7
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: sys-proctable
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: Collect and analyze memory usage data for each individual Rails action
|
112
|
+
controller.
|
113
|
+
email: philippe@lerohellec.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files:
|
117
|
+
- LICENSE.txt
|
118
|
+
- README.rdoc
|
119
|
+
files:
|
120
|
+
- Gemfile
|
121
|
+
- Gemfile.lock
|
122
|
+
- LICENSE.txt
|
123
|
+
- README.rdoc
|
124
|
+
- Rakefile
|
125
|
+
- VERSION
|
126
|
+
- app/controllers/memory_tracker/dashboards_controller.rb
|
127
|
+
- app/helpers/memory_tracker/dashboards_helper.rb
|
128
|
+
- app/views/layouts/memory_tracker.html.erb
|
129
|
+
- app/views/memory_tracker/dashboards/index.html.erb
|
130
|
+
- config/routes.rb
|
131
|
+
- docs/design.rb
|
132
|
+
- lib/memory_tracker.rb
|
133
|
+
- lib/memory_tracker/engine.rb
|
134
|
+
- lib/memory_tracker/env.rb
|
135
|
+
- lib/memory_tracker/gc_stat.rb
|
136
|
+
- lib/memory_tracker/memory_tracker.rb
|
137
|
+
- lib/memory_tracker/middleware.rb
|
138
|
+
- lib/memory_tracker/request.rb
|
139
|
+
- lib/memory_tracker/stores/gcstat_logfile_store.rb
|
140
|
+
- lib/memory_tracker/stores/in_memory_store.rb
|
141
|
+
- lib/memory_tracker/stores/url_logfile_store.rb
|
142
|
+
- memory_tracker.gemspec
|
143
|
+
- spec/lib/memory_tracker_spec.rb
|
144
|
+
- spec/lib/request_spec.rb
|
145
|
+
- spec/lib/stores/gcstat_logfile_store_spec.rb
|
146
|
+
- spec/lib/stores/in_memory_store_spec.rb
|
147
|
+
- spec/lib/stores/url_logfile_store_spec.rb
|
148
|
+
- spec/spec_helper.rb
|
149
|
+
homepage: http://github.com/plerohellec/memory_tracker
|
150
|
+
licenses:
|
151
|
+
- MIT
|
152
|
+
metadata: {}
|
153
|
+
post_install_message:
|
154
|
+
rdoc_options: []
|
155
|
+
require_paths:
|
156
|
+
- lib
|
157
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - '>='
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: '0'
|
162
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
requirements: []
|
168
|
+
rubyforge_project:
|
169
|
+
rubygems_version: 2.0.3
|
170
|
+
signing_key:
|
171
|
+
specification_version: 4
|
172
|
+
summary: Rails memory allocations tracker
|
173
|
+
test_files: []
|