Skip to content

Commit 16b4f09

Browse files
authored
feat: add multiple and match_neighbour_threshold (#313)
* feat: add multiple and match_neighbour_threshold * add a test * revert ios version
1 parent d195a5b commit 16b4f09

File tree

5 files changed

+84
-4
lines changed

5 files changed

+84
-4
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Read `release_notes.md` for commit level details.
55
## [Unreleased]
66

77
### Enhancements
8+
- Add `multiple` and `match_neighbour_threshold` arguments for `Appium::Core::Base::Driver#find_image_occurrence`
89

910
### Bug fixes
1011

lib/appium_lib_core/common/base/driver.rb

+6-3
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def dialect
7171
#
7272
def update_sending_request_to(protocol:, host:, port:, path:)
7373
unless @bridge.http&.class&.method_defined? :update_sending_request_to
74-
::Appium::Logger.fatal "#{@bridge.http&.class} has no 'update_sending_request_to'. " \
74+
::Appium::Logger.warn "#{@bridge.http&.class} has no 'update_sending_request_to'. " \
7575
'It keeps current connection target.'
7676
return
7777
end
@@ -1006,11 +1006,14 @@ def match_images_features(first_image:,
10061006
visualize: visualize)
10071007
end
10081008

1009-
def find_image_occurrence(full_image:, partial_image:, visualize: false, threshold: nil)
1009+
def find_image_occurrence(full_image:, partial_image:, visualize: false, threshold: nil,
1010+
multiple: nil, match_neighbour_threshold: nil)
10101011
@bridge.find_image_occurrence(full_image: full_image,
10111012
partial_image: partial_image,
10121013
visualize: visualize,
1013-
threshold: threshold)
1014+
threshold: threshold,
1015+
multiple: multiple,
1016+
match_neighbour_threshold: match_neighbour_threshold)
10141017
end
10151018

10161019
def get_images_similarity(first_image:, second_image:, visualize: false)

lib/appium_lib_core/common/device/image_comparison.rb

+9-1
Original file line numberDiff line numberDiff line change
@@ -95,19 +95,27 @@ def match_images_features(first_image:,
9595
# @param [Bool] visualize Makes the endpoint to return an image, which contains the visualized result of
9696
# the corresponding picture matching operation. This option is disabled by default.
9797
# @param [Float, nil] threshold [0.5] At what normalized threshold to reject
98+
# @param [bool, nil] multiple Whether to enable the support of multiple image occurrences @since Appium 1.21.0.
99+
# @param [integer, nil] match_neighbour_threshold The pixel distance between matches we consider to be part of
100+
# the same template match @since Appium 1.21.0.
101+
# This option is only considered if multiple matches mode is enabled.
102+
# 10 pixels by default.
98103
#
99104
# @example
100105
# @driver.find_image_occurrence full_image: "image data 1", partial_image: "image data 2"
101106
#
102107
# visual = @@driver.find_image_occurrence full_image: image1, partial_image: image2, visualize: true
103108
# File.write 'find_result_visual.png', Base64.decode64(visual['visualization']) # if the image is PNG
104109
#
105-
def find_image_occurrence(full_image:, partial_image:, visualize: false, threshold: nil)
110+
def find_image_occurrence(full_image:, partial_image:, visualize: false, threshold: nil,
111+
multiple: nil, match_neighbour_threshold: nil)
106112
raise "visualize should be #{MATCH_TEMPLATE[:visualize]}" unless MATCH_TEMPLATE[:visualize].member?(visualize)
107113

108114
options = {}
109115
options[:visualize] = visualize
110116
options[:threshold] = threshold unless threshold.nil?
117+
options[:multiple] = multiple unless multiple.nil?
118+
options[:matchNeighbourThreshold] = match_neighbour_threshold unless match_neighbour_threshold.nil?
111119

112120
compare_images(mode: :matchTemplate, first_image: full_image, second_image: partial_image, options: options)
113121
end

test/unit/android/device/w3c/image_comparison_test.rb

+16
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,22 @@ def test_image_comparison_find_image_occurrence
7878
assert_requested(:post, "#{SESSION}/appium/compare_images", times: 1)
7979
end
8080

81+
def test_image_comparison_find_image_occurrence_multiple
82+
img1 = 'img1GgoAAAANSUhEUgAAAu4AAAU2CAIAAABFtaRRAAAAAXNSR0IArs4c6QAAABxpRE9UAAAAAgAAAAAAAA'
83+
img2 = 'img2GgoAAAANSUhEUgAAAu4AAAU2CAIAAABFtaRRAAAAAXNSR0IArs4c6QAAABxpRE9UAAAAAgAAAAAAAA'
84+
85+
stub_request(:post, "#{SESSION}/appium/compare_images")
86+
.with(body: { mode: :matchTemplate,
87+
firstImage: Base64.strict_encode64(img1),
88+
secondImage: Base64.strict_encode64(img2),
89+
options: { visualize: false, multiple: true, matchNeighbourThreshold: 0.6 } }.to_json)
90+
.to_return(headers: HEADER, status: 200, body: { value: '' }.to_json)
91+
92+
@driver.find_image_occurrence full_image: img1, partial_image: img2, multiple: true, match_neighbour_threshold: 0.6
93+
94+
assert_requested(:post, "#{SESSION}/appium/compare_images", times: 1)
95+
end
96+
8197
def test_image_comparison_get_images_similarity
8298
img1 = 'img1GgoAAAANSUhEUgAAAu4AAAU2CAIAAABFtaRRAAAAAXNSR0IArs4c6QAAABxpRE9UAAAAAgAAAAAAAA'
8399
img2 = 'img2GgoAAAANSUhEUgAAAu4AAAU2CAIAAABFtaRRAAAAAXNSR0IArs4c6QAAABxpRE9UAAAAAgAAAAAAAA'

test/unit/driver_test.rb

+52
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,58 @@ def android_mock_create_session_w3c_direct_no_path(core)
230230
assert_equal '/wd/hub/', uri.path
231231
end
232232

233+
def test_default_timeout_for_http_client_with_direct_no_supported_client
234+
def android_mock_create_session_w3c_direct_default_client(core)
235+
response = {
236+
value: {
237+
sessionId: '1234567890',
238+
capabilities: {
239+
platformName: :android,
240+
automationName: ENV['AUTOMATION_NAME_DROID'] || 'uiautomator2',
241+
app: 'test/functional/app/api.apk.zip',
242+
platformVersion: '7.1.1',
243+
deviceName: 'Android Emulator',
244+
appPackage: 'io.appium.android.apis',
245+
appActivity: 'io.appium.android.apis.ApiDemos',
246+
someCapability: 'some_capability',
247+
unicodeKeyboard: true,
248+
resetKeyboard: true,
249+
directConnectProtocol: 'http',
250+
directConnectHost: 'localhost',
251+
directConnectPort: '8888',
252+
directConnectPath: '/wd/hub'
253+
}
254+
}
255+
}.to_json
256+
257+
stub_request(:post, 'http://127.0.0.1:4723/wd/hub/session')
258+
.to_return(headers: HEADER, status: 200, body: response)
259+
260+
stub_request(:post, 'http://127.0.0.1:4723/wd/hub/session/1234567890/timeouts')
261+
.with(body: { implicit: 30_000 }.to_json)
262+
.to_return(headers: HEADER, status: 200, body: { value: nil }.to_json)
263+
264+
driver = core.start_driver http_client_ops: { http_client: Selenium::WebDriver::Remote::Http::Default.new }
265+
266+
assert_requested(:post, 'http://127.0.0.1:4723/wd/hub/session', times: 1)
267+
assert_requested(:post, 'http://127.0.0.1:4723/wd/hub/session/1234567890/timeouts',
268+
body: { implicit: 30_000 }.to_json, times: 1)
269+
driver
270+
end
271+
272+
core = ::Appium::Core.for(Caps.android_direct)
273+
driver = android_mock_create_session_w3c_direct_default_client(core)
274+
275+
assert_nil driver.send(:bridge).http.open_timeout
276+
assert_nil driver.send(:bridge).http.read_timeout
277+
uri = driver.send(:bridge).http.send(:server_url)
278+
assert core.direct_connect
279+
assert_equal 'http', uri.scheme
280+
assert_equal '127.0.0.1', uri.host
281+
assert_equal 4723, uri.port
282+
assert_equal '/wd/hub/', uri.path
283+
end
284+
233285
def test_default_timeout_for_http_client_with_enable_idempotency_header_false
234286
def _android_mock_create_session_w3c(core)
235287
response = {

0 commit comments

Comments
 (0)