SurfCustomCalabash 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +68 -4
- data/SurfCustomCalabash.gemspec +3 -3
- data/bin/SurfCustomCalabash +54 -0
- data/bin/surf-calabash-console.rb +62 -0
- data/bin/surf-calabash-gen.rb +17 -0
- data/bin/surf-calabash-helpers.rb +34 -0
- data/lib/SurfCustomCalabash/CommonMethods.rb +3 -3
- data/lib/SurfCustomCalabash/DroidMethods.rb +2 -1
- data/lib/SurfCustomCalabash/IosMethods.rb +2 -2
- data/lib/SurfCustomCalabash/version.rb +1 -1
- data/sources/Backdoors list b/data/sources/Backdoors → list +0 -0
- data/sources/Gemfile +27 -0
- data/sources/Scripts/and.sh +5 -0
- data/sources/Scripts/ca.sh +5 -0
- data/sources/Scripts/ci.sh +5 -0
- data/sources/Scripts/data +7 -0
- data/sources/Scripts/fa.sh +6 -0
- data/sources/Scripts/fi.sh +5 -0
- data/sources/Scripts/get_apk.rb +68 -0
- data/sources/Scripts/get_scenarios.rb +193 -0
- data/sources/Scripts/import_scenarios.rb +172 -0
- data/sources/Scripts/ios.sh +5 -0
- data/sources/Scripts/parallel_android_smoke.sh +4 -0
- data/sources/Scripts/parallel_android_test.sh +4 -0
- data/sources/Scripts/ra.sh +12 -0
- data/sources/Scripts/ri.sh +9 -0
- data/sources/Scripts/smoke_and.sh +5 -0
- data/sources/Scripts/smoke_ios.sh +5 -0
- data/sources/Scripts/ta.sh +5 -0
- data/sources/Scripts/ti.sh +5 -0
- data/sources/Scripts/to_exec.sh +19 -0
- data/sources/Scripts/update.sh +24 -0
- data/sources/Scripts/update_exec.rb +96 -0
- data/sources/TestFolder/small.gif +0 -0
- data/sources/ci/JenkinsfileUiTestAndroid.groovy +22 -0
- data/sources/ci/JenkinsfileUiTestIos.groovy +23 -0
- data/sources/config/cucumber.yml +24 -0
- data/sources/features/android/pages/standard/DroidCommon.rb +33 -0
- data/sources/features/android/pages/standard/Init_android.rb +8 -0
- data/sources/features/android/pages/test.rb +43 -0
- data/sources/features/android/support/app_life_cycle_hooks.rb +147 -0
- data/sources/features/android/support/log_hooks_and.rb +40 -0
- data/sources/features/android/support/video_hooks_and.rb +110 -0
- data/sources/features/ios/pages/standard/Init_ios.rb +5 -0
- data/sources/features/ios/pages/standard/IosCommon.rb +30 -0
- data/sources/features/ios/pages/test.rb +37 -0
- data/sources/features/ios/support/01_launch.rb +194 -0
- data/sources/features/ios/support/log_hooks_ios.rb +40 -0
- data/sources/features/ios/support/video_hooks_ios.rb +58 -0
- data/sources/features/net/net.rb +28 -0
- data/sources/features/net/screentest_api.rb +186 -0
- data/sources/features/scenarios/eng.feature +8 -0
- data/sources/features/scenarios/rus.feature +8 -0
- data/sources/features/step_definitions/test.rb +43 -0
- data/sources/features/support/credentials.rb +11 -0
- data/sources/features/support/env.rb +8 -0
- data/sources/features/support/hooks.rb +21 -0
- data/sources/features/support/localization.rb +8 -0
- data/sources/find_id.rb +95 -0
- data/sources/group_steps.rb +39 -0
- data/sources/irbrcs/android/irbrc +105 -0
- data/sources/irbrcs/ios/irbrc +132 -0
- data/sources/start.sh +134 -0
- metadata +63 -6
- data/bin/console +0 -14
- data/bin/setup +0 -8
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
echo "введите название фича файла с обновленными тестами из папки features/scenarios/name.feature в формате <name>"
|
4
|
+
echo "enter feature file name with updated tests from dir features/scenarios/name.feature in format <name>"
|
5
|
+
|
6
|
+
read path
|
7
|
+
|
8
|
+
index=0
|
9
|
+
while read line; do
|
10
|
+
array[$index]="$line"
|
11
|
+
index=$(($index+1))
|
12
|
+
done < ./Scripts/data
|
13
|
+
|
14
|
+
first_string="${array[0]}"
|
15
|
+
username=${first_string/login /}
|
16
|
+
|
17
|
+
second_string="${array[1]}"
|
18
|
+
password=${second_string/jira_pass /}
|
19
|
+
|
20
|
+
third_string="${array[2]}"
|
21
|
+
code=${third_string/project_key /}
|
22
|
+
|
23
|
+
|
24
|
+
curl -H "Content-Type: multipart/form-data" -u $username:$password -F "file=@features/scenarios/$path.feature" https://jira.surfstudio.ru/rest/raven/1.0/import/feature?projectKey=$code
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'rest-client'
|
2
|
+
require 'json'
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
|
6
|
+
class Execution
|
7
|
+
|
8
|
+
# path - path to dir with autotests, пример - /Users/user_name/autotests/project-test/
|
9
|
+
def initialize(path)
|
10
|
+
|
11
|
+
# hash with data from file scripts/data
|
12
|
+
@all_data = get_all_data
|
13
|
+
|
14
|
+
# login in jira
|
15
|
+
@login = @all_data['login']
|
16
|
+
|
17
|
+
# pass in jira
|
18
|
+
@password = @all_data['jira_pass']
|
19
|
+
|
20
|
+
# project key
|
21
|
+
@key = @all_data['project_key']
|
22
|
+
|
23
|
+
# path to dir with autotests
|
24
|
+
@path = path
|
25
|
+
|
26
|
+
@auth = 'Basic ' + Base64.encode64( "#{@login}:#{@password}" ).chomp
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_accessor :path
|
30
|
+
|
31
|
+
# read user data from file
|
32
|
+
def get_all_data
|
33
|
+
Hash[*File.read("#{@path}scripts/data").split(/[, \n]+/)]
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_test_execution(test_exec)
|
37
|
+
|
38
|
+
key_arr = []
|
39
|
+
|
40
|
+
for i in 1..2
|
41
|
+
url = "https://jira.surfstudio.ru/rest/raven/1.0/api/testexec/#{test_exec}/test?page=#{i}"
|
42
|
+
|
43
|
+
response = RestClient.get url, {:Authorization => @auth}
|
44
|
+
# Kernel.puts(response)
|
45
|
+
parse_response = JSON.parse(response.body)
|
46
|
+
|
47
|
+
# Kernel.puts(parse_response)
|
48
|
+
|
49
|
+
parse_response.each do |keys|
|
50
|
+
key_arr << keys['key']
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
Kernel.puts(key_arr.sort)
|
55
|
+
|
56
|
+
$test_keys = key_arr.sort
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def delete_test_from_execution(test_exec)
|
61
|
+
|
62
|
+
p "Start Delete"
|
63
|
+
# begin
|
64
|
+
url = "https://jira.surfstudio.ru/rest/raven/1.0/api/testexec/#{test_exec}/test"
|
65
|
+
response = RestClient.post url, {:remove => $test_keys}.to_json,
|
66
|
+
{:Authorization => @auth,
|
67
|
+
content_type: :json,
|
68
|
+
accept: :json}
|
69
|
+
end
|
70
|
+
|
71
|
+
def add_test_to_execution(test_exec)
|
72
|
+
|
73
|
+
p "Start Update"
|
74
|
+
|
75
|
+
url = "https://jira.surfstudio.ru/rest/raven/1.0/api/testexec/#{test_exec}/test"
|
76
|
+
response = RestClient.post url, {:add => $test_keys}.to_json,
|
77
|
+
{:Authorization => @auth,
|
78
|
+
content_type: :json,
|
79
|
+
accept: :json}
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
def update_test_execution(test_exec)
|
84
|
+
get_test_execution(test_exec)
|
85
|
+
begin
|
86
|
+
delete_test_from_execution(test_exec)
|
87
|
+
rescue
|
88
|
+
p "Timeout error"
|
89
|
+
p "Retrying Attempt"
|
90
|
+
delete_test_from_execution(test_exec)
|
91
|
+
end
|
92
|
+
|
93
|
+
add_test_to_execution(test_exec)
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
Binary file
|
@@ -0,0 +1,22 @@
|
|
1
|
+
@Library('surf-lib@version-3.0.0-SNAPSHOT') // https://bitbucket.org/surfstudio/jenkins-pipeline-lib/
|
2
|
+
import ru.surfstudio.ci.pipeline.ui_test.UiTestPipelineAndroid
|
3
|
+
|
4
|
+
//init
|
5
|
+
def pipeline = new UiTestPipelineAndroid(this)
|
6
|
+
pipeline.init()
|
7
|
+
|
8
|
+
//configuration
|
9
|
+
pipeline.sourceRepoUrl = "TODO add repo source code app"
|
10
|
+
pipeline.jiraProjectKey = "TODO add key project in jira"
|
11
|
+
pipeline.testBranch = "master" // branch with tests
|
12
|
+
pipeline.defaultTaskKey = "TODO add key test execution in jira" //task for run periodically
|
13
|
+
pipeline.projectForBuild = “TODO add name project for build in jenkins”
|
14
|
+
|
15
|
+
|
16
|
+
//customization
|
17
|
+
pipeline.cronTimeTrigger = "00 02 * * *"
|
18
|
+
pipeline.notificationEnabled = false
|
19
|
+
pipeline.buildGradleTask = "clean assembleQa"
|
20
|
+
|
21
|
+
//run
|
22
|
+
pipeline.run()
|
@@ -0,0 +1,23 @@
|
|
1
|
+
@Library('surf-lib@version-3.0.0-SNAPSHOT') // https://bitbucket.org/surfstudio/jenkins-pipeline-lib/
|
2
|
+
import ru.surfstudio.ci.pipeline.ui_test.UiTestPipelineiOS
|
3
|
+
|
4
|
+
//init
|
5
|
+
def pipeline = new UiTestPipelineiOS(this)
|
6
|
+
pipeline.init()
|
7
|
+
|
8
|
+
//configuration
|
9
|
+
pipeline.sourceRepoUrl = "TODO add repo source code app"
|
10
|
+
pipeline.jiraProjectKey = "TODO add key project in jira"
|
11
|
+
pipeline.testBranch = "master" // branch with tests
|
12
|
+
pipeline.defaultTaskKey = "TODO add key test execution in jira" //task for run periodically
|
13
|
+
pipeline.testDeviceName = "iPhone 11"
|
14
|
+
pipeline.testOSVersion = "com.apple.CoreSimulator.SimRuntime.iOS-13-3"
|
15
|
+
pipeline.testiOSSDK = "iphonesimulator13.2"
|
16
|
+
pipeline.projectForBuild = "test"
|
17
|
+
|
18
|
+
//customization
|
19
|
+
pipeline.cronTimeTrigger = "01 04 * * *"
|
20
|
+
pipeline.notificationEnabled = false
|
21
|
+
|
22
|
+
//run
|
23
|
+
pipeline.run()
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# config/cucumber.yml
|
2
|
+
##YAML Template
|
3
|
+
|
4
|
+
android: PLATFORM=android -r features/support -r features/android/support -r features/android -r features/step_definitions -r features/android/pages -r features/android/pages/standard
|
5
|
+
ios: PLATFORM=ios -r features/support -r features/ios/support -r features/step_definitions -r features/ios/pages
|
6
|
+
<%
|
7
|
+
date = Time.now.strftime('%d_%m-%H:%M:%S')
|
8
|
+
default_report = "./output_#{date}_.html"
|
9
|
+
default_json = "./output_#{date}.json"
|
10
|
+
default_allure = "./output_#{date}.html"
|
11
|
+
default_rerun = "./rerun_#{date}.txt"
|
12
|
+
%>
|
13
|
+
|
14
|
+
common: NO_STOP='1'
|
15
|
+
rerun_out: -f rerun -o ./reports/<%= default_rerun %>
|
16
|
+
html_report: -f html -o ./reports/<%= default_report %>
|
17
|
+
json_report: -f json -o ./reports/<%= ENV['DEVICE_INFO']%>.json
|
18
|
+
json_xray: -f json -o ./reports/<%= ENV['DEVICE_INFO']%>_<%= ENV['ADB_DEVICE_ARG']%>.json
|
19
|
+
|
20
|
+
|
21
|
+
run: -p common -p rerun_out -p html_report -p json_report -p allure_report -p json_xray
|
22
|
+
|
23
|
+
|
24
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'calabash-android'
|
2
|
+
require 'SurfCustomCalabash/DroidMethods'
|
3
|
+
|
4
|
+
class DroidCommon
|
5
|
+
|
6
|
+
# system methods
|
7
|
+
def initialize(driver)
|
8
|
+
@driver = driver
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing (sym, *args, &block)
|
12
|
+
@driver.send sym, *args, &block
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.element(name, &block)
|
16
|
+
define_method(name.to_s, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
class << self
|
20
|
+
alias :value :element
|
21
|
+
alias :action :element
|
22
|
+
alias :trait :element
|
23
|
+
end
|
24
|
+
|
25
|
+
# custom methods
|
26
|
+
def go_back
|
27
|
+
sleep(1.5)
|
28
|
+
press_back_button
|
29
|
+
sleep(1)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative 'standard/DroidCommon.rb'
|
2
|
+
# all screens your application screens should be like this. Inheritance from DroidCommon is essential!
|
3
|
+
|
4
|
+
class Test < DroidCommon
|
5
|
+
def initialize(driver)
|
6
|
+
|
7
|
+
@driver = driver
|
8
|
+
|
9
|
+
##### elements on the screen #######
|
10
|
+
|
11
|
+
# elements locators and their x-platform nickname should be like that
|
12
|
+
|
13
|
+
@trait = "* id:'feed_rv'"
|
14
|
+
|
15
|
+
@auth_field = "* id:'auth_fld'"
|
16
|
+
|
17
|
+
@main_ok_button = "* id:'feed_vote_tutorial_go_btn'"
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
# all elements above should have getter for mention them from step definitions
|
22
|
+
attr_reader *Test.new(self).instance_variables.map {|s| s[1..-1]}
|
23
|
+
|
24
|
+
# another way to describe elements
|
25
|
+
|
26
|
+
def tape
|
27
|
+
"* id:'feed_rv'"
|
28
|
+
end
|
29
|
+
|
30
|
+
#### methods ###########
|
31
|
+
|
32
|
+
def authorize(data)
|
33
|
+
tap_on(@auth_field)
|
34
|
+
wait_for_keyboard
|
35
|
+
enter_text(data)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test
|
39
|
+
tap_on(@main_ok_button)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'calabash-android/operations'
|
2
|
+
require 'calabash-android/management/app_installation'
|
3
|
+
require 'calabash-android/cucumber'
|
4
|
+
require 'cucumber'
|
5
|
+
require 'json'
|
6
|
+
require_relative '../../../features/net/screentest_api'
|
7
|
+
require 'fileutils'
|
8
|
+
|
9
|
+
Before do |scenario|
|
10
|
+
|
11
|
+
ENV['SCREENSHOT_PATH'] = './reports/'
|
12
|
+
|
13
|
+
@scenario_name = scenario.name
|
14
|
+
|
15
|
+
@start_record = true
|
16
|
+
@start_log = true
|
17
|
+
|
18
|
+
scenario_tags = scenario.source_tag_names
|
19
|
+
|
20
|
+
reinstall_apps if scenario_tags.include?('@reinstall')
|
21
|
+
|
22
|
+
clear_app_data if scenario_tags.include?('@clear')
|
23
|
+
|
24
|
+
skip_this_scenario('Сценарий только для iOS') if scenario_tags.include?('@skip')
|
25
|
+
|
26
|
+
if scenario_tags.include?('@screentest')
|
27
|
+
clear_file_with_stack_trace
|
28
|
+
create_feature(@scenario_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
@start_record = true if scenario_tags.include?('@record')
|
32
|
+
|
33
|
+
start_test_server_in_background
|
34
|
+
|
35
|
+
@video_capture = nil
|
36
|
+
|
37
|
+
# start video record
|
38
|
+
if @start_record
|
39
|
+
@video_capture = AndroidVideoCapture.new
|
40
|
+
@video_capture.start_capture
|
41
|
+
@need_convert = false
|
42
|
+
# set video quality: low, medium, high
|
43
|
+
@video_capture.quality = "low"
|
44
|
+
end
|
45
|
+
|
46
|
+
@write_log = nil
|
47
|
+
|
48
|
+
# start write logs
|
49
|
+
if @start_log
|
50
|
+
@write_log = AndroidLogs.new
|
51
|
+
@write_log.start_log
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def file_name(ext)
|
56
|
+
"android_#{Time.now.strftime('%d_%m-%H:%M:%S')}.#{ext}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_folder
|
60
|
+
model = %x(adb devices -l | awk 'NR==2{print $5}')
|
61
|
+
model.slice! "model:"
|
62
|
+
|
63
|
+
@path = "#{ENV['SCREENSHOT_PATH']}#{@scenario_name}_#{model}_#{Time.now.strftime('%d_%m-%H:%M:%S')}/"
|
64
|
+
# Dir.mkdir(@path) unless File.directory?(@path)
|
65
|
+
FileUtils.mkdir_p @path
|
66
|
+
end
|
67
|
+
|
68
|
+
def convert
|
69
|
+
convert_file = "video_#{file_name('mp4')}"
|
70
|
+
@video_capture.convert_video(@video_capture.video_file, convert_file)
|
71
|
+
delete_file(@video_capture.video_file)
|
72
|
+
@video_capture.video_file = convert_file
|
73
|
+
end
|
74
|
+
|
75
|
+
def embed_video
|
76
|
+
unless @video_capture.nil?
|
77
|
+
@video_capture.acquire_capture
|
78
|
+
if File.exist?(@video_capture.video_file)
|
79
|
+
convert if @need_convert
|
80
|
+
embed(@video_capture.video_file, 'video/mp4', "video #{@video_capture.video_file}")
|
81
|
+
FileUtils.mv(@video_capture.video_file, @path)
|
82
|
+
@video_capture.video_file = @path + @video_capture.video_file
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def delete_file(file_name)
|
88
|
+
if File.exist?(file_name)
|
89
|
+
File.delete(file_name)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def embed_logs
|
94
|
+
unless @write_log.nil?
|
95
|
+
if File.exist?(@write_log.log_file)
|
96
|
+
embed(@write_log.log_file, 'log/txt', "log #{@write_log.log_file}")
|
97
|
+
FileUtils.mv(@write_log.log_file, @path)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
After do |scenario|
|
103
|
+
|
104
|
+
@video_capture.stop_capture unless @video_capture.nil?
|
105
|
+
|
106
|
+
@write_log.stop_log unless @write_log.nil?
|
107
|
+
|
108
|
+
scenario_tags = scenario.source_tag_names
|
109
|
+
|
110
|
+
if scenario_tags.include?('@record') && scenario.passed?
|
111
|
+
create_folder
|
112
|
+
embed_video
|
113
|
+
elsif scenario.passed?
|
114
|
+
@video_capture.dispose_capture unless @video_capture.nil?
|
115
|
+
delete_file(@write_log.log_file) unless @write_log.nil?
|
116
|
+
delete_file(@video_capture.video_file) unless @video_capture.nil?
|
117
|
+
end
|
118
|
+
|
119
|
+
if scenario.failed?
|
120
|
+
|
121
|
+
create_folder
|
122
|
+
|
123
|
+
mark_feature_as_not_finished if scenario.source_tag_names.include?('@screentest')
|
124
|
+
|
125
|
+
pretty = JSON.pretty_generate(query("*"))
|
126
|
+
parsed = JSON.parse(pretty)
|
127
|
+
|
128
|
+
for i in 0..query("*").count-1
|
129
|
+
id = parsed[i]['id'].to_s
|
130
|
+
text = parsed[i]['text'].to_s
|
131
|
+
if id != ''
|
132
|
+
File.open("#{@path}id_#{file_name('txt')}", 'a', crlf_newline: true) { |f| f.puts(id.encode(crlf_newline: true))}
|
133
|
+
end
|
134
|
+
if text != ''
|
135
|
+
File.open("#{@path}text_#{file_name('txt')}", 'a', crlf_newline: true) { |f| f.puts(text.encode(crlf_newline: true))}
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
screenshot(prefix: @path, name: "#{file_name('png')}")
|
140
|
+
embed_video
|
141
|
+
embed_logs
|
142
|
+
end
|
143
|
+
|
144
|
+
send_video(@video_capture.video_file) if scenario.source_tag_names.include?('@record')
|
145
|
+
shutdown_test_server
|
146
|
+
|
147
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'timeout'
|
3
|
+
require 'process'
|
4
|
+
require 'calabash-android/operations'
|
5
|
+
require 'calabash-android/env'
|
6
|
+
|
7
|
+
def adb_command
|
8
|
+
"\"#{Calabash::Android::Dependencies.adb_path}\""
|
9
|
+
end
|
10
|
+
|
11
|
+
class AndroidLogs
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@log_file = "#{ENV['SCREENSHOT_PATH']}log_#{Time.now.strftime('%d_%m-%H:%M:%S')}.txt"
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :log_file
|
18
|
+
|
19
|
+
def start_log
|
20
|
+
package = package_name('L.apk')
|
21
|
+
pid_app = %x{adb shell ps | grep #{package} | tr -s [:space:] ' ' | cut -d' ' -f2}
|
22
|
+
@logcat = Open3.popen2e("adb logcat | grep #{pid_app}")
|
23
|
+
@pid = @logcat[2].pid
|
24
|
+
end
|
25
|
+
|
26
|
+
def stop_log
|
27
|
+
%x{ps | grep logcat | grep -v grep | cut -d' ' -f1 | awk '{print $1}' | xargs kill -2}
|
28
|
+
|
29
|
+
begin
|
30
|
+
Timeout.timeout(30) { File.open(@log_file, 'w') { |f| f << @logcat[1].readlines.join("\n") } }
|
31
|
+
rescue Timeout::Error
|
32
|
+
Kernel.puts("Error write log file")
|
33
|
+
@logcat[0].close
|
34
|
+
@logcat[1].close
|
35
|
+
end
|
36
|
+
|
37
|
+
@logcat[0].close
|
38
|
+
@logcat[1].close
|
39
|
+
end
|
40
|
+
end
|