eyes_core 3.0.4
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/ext/eyes_core/extconf.rb +3 -0
- data/ext/eyes_core/eyes_core.c +80 -0
- data/ext/eyes_core/eyes_core.h +24 -0
- data/lib/applitools/capybara.rb +8 -0
- data/lib/applitools/chunky_png/resampling.rb +148 -0
- data/lib/applitools/chunky_png_patch.rb +8 -0
- data/lib/applitools/connectivity/proxy.rb +3 -0
- data/lib/applitools/connectivity/server_connector.rb +118 -0
- data/lib/applitools/core/app_environment.rb +29 -0
- data/lib/applitools/core/app_output.rb +17 -0
- data/lib/applitools/core/app_output_with_screenshot.rb +22 -0
- data/lib/applitools/core/argument_guard.rb +35 -0
- data/lib/applitools/core/batch_info.rb +18 -0
- data/lib/applitools/core/eyes_base.rb +463 -0
- data/lib/applitools/core/eyes_screenshot.rb +35 -0
- data/lib/applitools/core/fixed_cut_provider.rb +61 -0
- data/lib/applitools/core/fixed_scale_provider.rb +14 -0
- data/lib/applitools/core/helpers.rb +18 -0
- data/lib/applitools/core/location.rb +84 -0
- data/lib/applitools/core/match_result.rb +16 -0
- data/lib/applitools/core/match_results.rb +9 -0
- data/lib/applitools/core/match_window_data.rb +34 -0
- data/lib/applitools/core/match_window_task.rb +86 -0
- data/lib/applitools/core/mouse_trigger.rb +39 -0
- data/lib/applitools/core/rectangle_size.rb +46 -0
- data/lib/applitools/core/region.rb +180 -0
- data/lib/applitools/core/screenshot.rb +49 -0
- data/lib/applitools/core/session.rb +15 -0
- data/lib/applitools/core/session_start_info.rb +33 -0
- data/lib/applitools/core/test_results.rb +55 -0
- data/lib/applitools/core/text_trigger.rb +24 -0
- data/lib/applitools/core/trigger.rb +8 -0
- data/lib/applitools/extensions.rb +18 -0
- data/lib/applitools/eyes_logger.rb +45 -0
- data/lib/applitools/images/eyes.rb +204 -0
- data/lib/applitools/images/eyes_images_screenshot.rb +102 -0
- data/lib/applitools/method_tracer.rb +23 -0
- data/lib/applitools/sauce.rb +2 -0
- data/lib/applitools/utils/eyes_selenium_utils.rb +348 -0
- data/lib/applitools/utils/image_delta_compressor.rb +146 -0
- data/lib/applitools/utils/image_utils.rb +146 -0
- data/lib/applitools/utils/utils.rb +68 -0
- data/lib/applitools/version.rb +3 -0
- data/lib/eyes_core.rb +70 -0
- metadata +273 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2b1df68d352f994ee3111a658286083efd568c76
|
4
|
+
data.tar.gz: 4b7734495c6f07a2b63ee190f18cac3a064a23be
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3e568a25ce2eeeddb8ca7fcae76dbe037d963b30e738ce595b6a04a2681ec81aa34fc4fdbbceeddefb66c6ad429c0991fd416a63f1cc78d569c8fcae260a2738
|
7
|
+
data.tar.gz: 07adaf7c6c520f1d762d7ab42a1a3b18f14e4324e32c69fddb346ec6804dc5cde9d1e0a776c9a953b7a0ebbf4f09d2cc87152d7bc6a21e1b94c3186c527af469
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#include "eyes_core.h"
|
2
|
+
|
3
|
+
void Init_eyes_core() {
|
4
|
+
VALUE Applitools = rb_define_module("Applitools");
|
5
|
+
VALUE Resampling = rb_define_module_under(Applitools, "ResamplingFast");
|
6
|
+
rb_define_method(Resampling, "interpolate_cubic", c_interpolate_cubic, 1);
|
7
|
+
rb_define_method(Resampling, "merge_pixels", c_merge_pixels, 1);
|
8
|
+
};
|
9
|
+
|
10
|
+
|
11
|
+
VALUE c_interpolate_cubic(VALUE self, VALUE data) {
|
12
|
+
double t = NUM2DBL(rb_ary_entry(data, 1));
|
13
|
+
BYTE new_r, new_g, new_b, new_a;
|
14
|
+
VALUE p0, p1, p2, p3;
|
15
|
+
|
16
|
+
p0 = NUM2UINT(rb_ary_entry(data, 2));
|
17
|
+
p1 = NUM2UINT(rb_ary_entry(data, 3));
|
18
|
+
p2 = NUM2UINT(rb_ary_entry(data, 4));
|
19
|
+
p3 = NUM2UINT(rb_ary_entry(data, 5));
|
20
|
+
|
21
|
+
new_r = interpolate_char(t, R_BYTE(p0), R_BYTE(p1), R_BYTE(p2), R_BYTE(p3));
|
22
|
+
new_g = interpolate_char(t, G_BYTE(p0), G_BYTE(p1), G_BYTE(p2), G_BYTE(p3));
|
23
|
+
new_b = interpolate_char(t, B_BYTE(p0), B_BYTE(p1), B_BYTE(p2), B_BYTE(p3));
|
24
|
+
new_a = interpolate_char(t, A_BYTE(p0), A_BYTE(p1), A_BYTE(p2), A_BYTE(p3));
|
25
|
+
|
26
|
+
return UINT2NUM(BUILD_PIXEL(new_r, new_g, new_b, new_a));
|
27
|
+
};
|
28
|
+
|
29
|
+
BYTE interpolate_char(double t, BYTE c0, BYTE c1, BYTE c2, BYTE c3) {
|
30
|
+
double a, b, c, d, res;
|
31
|
+
a = - 0.5 * c0 + 1.5 * c1 - 1.5 * c2 + 0.5 * c3;
|
32
|
+
b = c0 - 2.5 * c1 + 2 * c2 - 0.5 * c3;
|
33
|
+
c = 0.5 * c2 - 0.5 * c0;
|
34
|
+
d = c1;
|
35
|
+
res = a * t * t * t + b * t * t + c * t + d + 0.5;
|
36
|
+
if(res < 0) {
|
37
|
+
res = 0;
|
38
|
+
} else if(res > 255) {
|
39
|
+
res = 255;
|
40
|
+
};
|
41
|
+
return (BYTE)(res);
|
42
|
+
};
|
43
|
+
|
44
|
+
VALUE c_merge_pixels(VALUE self, VALUE pixels) {
|
45
|
+
unsigned int i, size, real_colors, acum_r, acum_g, acum_b, acum_a;
|
46
|
+
BYTE new_r, new_g, new_b, new_a;
|
47
|
+
PIXEL pix;
|
48
|
+
|
49
|
+
acum_r = 0;
|
50
|
+
acum_g = 0;
|
51
|
+
acum_b = 0;
|
52
|
+
acum_a = 0;
|
53
|
+
|
54
|
+
new_r = 0;
|
55
|
+
new_g = 0;
|
56
|
+
new_b = 0;
|
57
|
+
new_a = 0;
|
58
|
+
|
59
|
+
size = NUM2UINT(rb_funcall(pixels, rb_intern("size"), 0, Qnil)) - 1;
|
60
|
+
real_colors = 0;
|
61
|
+
|
62
|
+
for(i=1; i < size; i++) {
|
63
|
+
pix = NUM2UINT(rb_ary_entry(pixels, i));
|
64
|
+
if(A_BYTE(pix) != 0) {
|
65
|
+
acum_r += R_BYTE(pix);
|
66
|
+
acum_g += G_BYTE(pix);
|
67
|
+
acum_b += B_BYTE(pix);
|
68
|
+
acum_a += A_BYTE(pix);
|
69
|
+
real_colors += 1;
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
if(real_colors > 0) {
|
74
|
+
new_r = (BYTE)(acum_r/real_colors + 0.5);
|
75
|
+
new_g = (BYTE)(acum_g/real_colors + 0.5);
|
76
|
+
new_b = (BYTE)(acum_b/real_colors + 0.5);
|
77
|
+
}
|
78
|
+
new_a = (BYTE)(acum_a/(size - 1) + 0.5);
|
79
|
+
return UINT2NUM(BUILD_PIXEL(new_r, new_g, new_b, new_a));
|
80
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#ifndef APPLITOOLS_RESAMPLING_EXT
|
2
|
+
#define APPLITOOLS_RESAMPLING_EXT
|
3
|
+
#include "ruby.h"
|
4
|
+
|
5
|
+
typedef uint32_t PIXEL; // Pixels use 32 bits unsigned integers
|
6
|
+
typedef unsigned char BYTE; // Bytes use 8 bits unsigned integers
|
7
|
+
|
8
|
+
#define R_BYTE(pixel) ((BYTE) (((pixel) & (PIXEL) 0xff000000) >> 24))
|
9
|
+
#define G_BYTE(pixel) ((BYTE) (((pixel) & (PIXEL) 0x00ff0000) >> 16))
|
10
|
+
#define B_BYTE(pixel) ((BYTE) (((pixel) & (PIXEL) 0x0000ff00) >> 8))
|
11
|
+
#define A_BYTE(pixel) ((BYTE) (((pixel) & (PIXEL) 0x000000ff)))
|
12
|
+
|
13
|
+
#define BUILD_PIXEL(r, g, b, a) (((PIXEL) (r) << 24) + ((PIXEL) (g) << 16) + ((PIXEL) (b) << 8) + (PIXEL) (a))
|
14
|
+
#define INT8_MULTIPLY(a, b) (((((a) * (b) + 0x80) >> 8) + ((a) * (b) + 0x80)) >> 8)
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
void Init_resampling();
|
19
|
+
|
20
|
+
VALUE c_interpolate_cubic(VALUE,VALUE);
|
21
|
+
VALUE c_merge_pixels(VALUE, VALUE);
|
22
|
+
BYTE interpolate_char(double, BYTE, BYTE, BYTE, BYTE);
|
23
|
+
|
24
|
+
#endif
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module Applitools::ChunkyPNG
|
2
|
+
module Resampling
|
3
|
+
def resample_bicubic!(dst_width, dst_height)
|
4
|
+
w_m = [1, width / dst_width].max
|
5
|
+
h_m = [1, height / dst_height].max
|
6
|
+
|
7
|
+
dst_width2 = dst_width * w_m
|
8
|
+
dst_height2 = dst_height * h_m
|
9
|
+
|
10
|
+
points = bicubic_x_points(dst_width2)
|
11
|
+
pixels = Array.new(points.size)
|
12
|
+
|
13
|
+
points.each do |interpolation_data|
|
14
|
+
pixels[interpolation_data[0]] = interpolate_cubic(interpolation_data)
|
15
|
+
end
|
16
|
+
replace_canvas!(dst_width2, height, pixels)
|
17
|
+
|
18
|
+
points = bicubic_y_points(dst_height2)
|
19
|
+
pixels = Array.new(points.size)
|
20
|
+
|
21
|
+
points.each do |interpolation_data|
|
22
|
+
pixels[interpolation_data[0]] = interpolate_cubic(interpolation_data)
|
23
|
+
end
|
24
|
+
replace_canvas!(dst_width2, dst_height2, pixels)
|
25
|
+
|
26
|
+
return self unless w_m * h_m > 1
|
27
|
+
|
28
|
+
points = scale_points(dst_width, dst_height, w_m, h_m)
|
29
|
+
pixels = Array.new(points.size)
|
30
|
+
|
31
|
+
points.each do |merge_data|
|
32
|
+
pixels[merge_data[0]] = merge_pixels(merge_data)
|
33
|
+
end
|
34
|
+
|
35
|
+
replace_canvas!(dst_width, dst_height, pixels)
|
36
|
+
end
|
37
|
+
|
38
|
+
def resample_bicubic(new_width, new_height)
|
39
|
+
dup.resample_bicubic!(new_width, new_height)
|
40
|
+
end
|
41
|
+
|
42
|
+
def bicubic_x_points(dst_width)
|
43
|
+
bicubic_points(width, dst_width, false)
|
44
|
+
end
|
45
|
+
|
46
|
+
def bicubic_y_points(dst_height)
|
47
|
+
bicubic_points(height, dst_height, true)
|
48
|
+
end
|
49
|
+
|
50
|
+
def bicubic_points(src_dimension, dst_dimension, direction, y_start_position = 0)
|
51
|
+
step = (src_dimension - 1).to_f / dst_dimension
|
52
|
+
y_bounds = direction ? width : height
|
53
|
+
raise ArgumentError.new 'Start position value is invalid!' unless y_start_position < y_bounds
|
54
|
+
pixels_size = (y_bounds - y_start_position) * dst_dimension
|
55
|
+
|
56
|
+
steps = Array.new(dst_dimension)
|
57
|
+
residues = Array.new(dst_dimension)
|
58
|
+
|
59
|
+
(0..dst_dimension - 1).each do |i|
|
60
|
+
steps[i] = (i * step).to_i
|
61
|
+
residues[i] = i * step - steps[i]
|
62
|
+
end
|
63
|
+
Enumerator.new(pixels_size) do |enum|
|
64
|
+
(y_start_position..y_bounds - 1).each do |y|
|
65
|
+
line = (direction ? column(y) : row(y))
|
66
|
+
|
67
|
+
line_with_bounds = [imaginable_point(line[0], line[1])] + line + [
|
68
|
+
imaginable_point(line[src_dimension - 2], line[src_dimension - 3]),
|
69
|
+
imaginable_point(line[src_dimension - 1], line[src_dimension - 2])
|
70
|
+
]
|
71
|
+
|
72
|
+
index_y = dst_dimension * y
|
73
|
+
(0..dst_dimension - 1).each do |x|
|
74
|
+
index = direction ? y_bounds * x + y : index_y + x
|
75
|
+
enum << ([index, residues[x]] + line_with_bounds.last(src_dimension + 3 - steps[x]).first(4))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def scale_points(dst_width, dst_height, w_m, h_m)
|
82
|
+
Enumerator.new(dst_width * dst_height) do |enum|
|
83
|
+
(0..dst_height - 1).each do |i|
|
84
|
+
(0..dst_width - 1).each do |j|
|
85
|
+
pixels_to_merge = []
|
86
|
+
(0..h_m - 1).each do |y|
|
87
|
+
y_pos = i * h_m + y
|
88
|
+
(0..w_m - 1).each do |x|
|
89
|
+
x_pos = j * w_m + x
|
90
|
+
pixels_to_merge << get_pixel(x_pos, y_pos)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
index = i * dst_width + j
|
94
|
+
enum << ([index] + pixels_to_merge)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def merge_pixels(merge_data)
|
101
|
+
pixels = merge_data[1..merge_data.size]
|
102
|
+
merged_data = pixels.each_with_object(r: 0, g: 0, b: 0, a: 0, real_colors: 0) do |pixel, result|
|
103
|
+
unless ChunkyPNG::Color.fully_transparent?(pixel)
|
104
|
+
result[:real_colors] += 1
|
105
|
+
[:r, :g, :b].each do |ch|
|
106
|
+
result[ch] += ChunkyPNG::Color.send(ch, pixel)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
result[:a] += ChunkyPNG::Color.a(pixel)
|
110
|
+
result
|
111
|
+
end
|
112
|
+
|
113
|
+
r = merged_data[:real_colors] > 0 ? merged_data[:r] / merged_data[:real_colors] : 0
|
114
|
+
g = merged_data[:real_colors] > 0 ? merged_data[:g] / merged_data[:real_colors] : 0
|
115
|
+
b = merged_data[:real_colors] > 0 ? merged_data[:b] / merged_data[:real_colors] : 0
|
116
|
+
a = merged_data[:a] / pixels.size
|
117
|
+
|
118
|
+
ChunkyPNG::Color.rgba(r, g, b, a)
|
119
|
+
end
|
120
|
+
|
121
|
+
def interpolate_cubic(data)
|
122
|
+
result = {}
|
123
|
+
t = data[1]
|
124
|
+
[:r, :g, :b, :a].each do |chan|
|
125
|
+
c0 = ChunkyPNG::Color.send(chan, data[2])
|
126
|
+
c1 = ChunkyPNG::Color.send(chan, data[3])
|
127
|
+
c2 = ChunkyPNG::Color.send(chan, data[4])
|
128
|
+
c3 = ChunkyPNG::Color.send(chan, data[5])
|
129
|
+
|
130
|
+
a = -0.5 * c0 + 1.5 * c1 - 1.5 * c2 + 0.5 * c3
|
131
|
+
b = c0 - 2.5 * c1 + 2 * c2 - 0.5 * c3
|
132
|
+
c = 0.5 * c2 - 0.5 * c0
|
133
|
+
d = c1
|
134
|
+
|
135
|
+
result[chan] = [0, [255, (a * t**3 + b * t**2 + c * t + d).to_i].min].max
|
136
|
+
end
|
137
|
+
ChunkyPNG::Color.rgba(result[:r], result[:g], result[:b], result[:a])
|
138
|
+
end
|
139
|
+
|
140
|
+
def imaginable_point(point1, point2)
|
141
|
+
r = [0, [255, ChunkyPNG::Color.r(point1) << 1].min - ChunkyPNG::Color.r(point2)].max
|
142
|
+
g = [0, [255, ChunkyPNG::Color.g(point1) << 1].min - ChunkyPNG::Color.g(point2)].max
|
143
|
+
b = [0, [255, ChunkyPNG::Color.b(point1) << 1].min - ChunkyPNG::Color.b(point2)].max
|
144
|
+
a = [0, [255, ChunkyPNG::Color.a(point1) << 1].min - ChunkyPNG::Color.a(point2)].max
|
145
|
+
ChunkyPNG::Color.rgba(r, g, b, a)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'oj'
|
3
|
+
Oj.default_options = { :mode => :compat }
|
4
|
+
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
module Applitools::Connectivity
|
8
|
+
module ServerConnector
|
9
|
+
extend self
|
10
|
+
|
11
|
+
DEFAULT_SERVER_URL = 'https://eyessdk.applitools.com'.freeze
|
12
|
+
|
13
|
+
SSL_CERT = File.join(File.dirname(File.expand_path(__FILE__)), '../../../certs/cacert.pem').to_s.freeze
|
14
|
+
DEFAULT_TIMEOUT = 300
|
15
|
+
|
16
|
+
API_SESSIONS_RUNNING = '/api/sessions/running/'.freeze
|
17
|
+
|
18
|
+
HTTP_STATUS_CODES = {
|
19
|
+
created: 201,
|
20
|
+
accepted: 202
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
attr_accessor :server_url, :api_key
|
24
|
+
attr_reader :endpoint_url
|
25
|
+
attr_accessor :proxy
|
26
|
+
|
27
|
+
def server_url=(url)
|
28
|
+
@server_url = url.nil? ? DEFAULT_SERVER_URL : url
|
29
|
+
unless @server_url.is_a? String
|
30
|
+
raise Applitools::EyesIllegalArgument.new 'You should pass server url as a String!' \
|
31
|
+
" (#{@server_url.class} is passed)"
|
32
|
+
end
|
33
|
+
@endpoint_url = URI.join(@server_url, API_SESSIONS_RUNNING).to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_proxy(uri, user = nil, password = nil)
|
37
|
+
self.proxy = Proxy.new uri, user, password
|
38
|
+
end
|
39
|
+
|
40
|
+
def match_window(session, data)
|
41
|
+
# Notice that this does not include the screenshot.
|
42
|
+
json_data = Oj.dump(Applitools::Utils.camelcase_hash_keys(data.to_hash)).force_encoding('BINARY')
|
43
|
+
body = [json_data.length].pack('L>') + json_data + data.screenshot
|
44
|
+
Applitools::EyesLogger.debug 'Sending match data...'
|
45
|
+
res = post(URI.join(endpoint_url, session.id.to_s), content_type: 'application/octet-stream', body: body)
|
46
|
+
raise Applitools::EyesError.new("Request failed: #{res.status}") unless res.success?
|
47
|
+
Applitools::MatchResult.new Oj.load(res.body)
|
48
|
+
end
|
49
|
+
|
50
|
+
def start_session(session_start_info)
|
51
|
+
res = post(endpoint_url, body: Oj.dump(startInfo:
|
52
|
+
Applitools::Utils.camelcase_hash_keys(session_start_info.to_hash)))
|
53
|
+
raise Applitools::EyesError.new("Request failed: #{res.status}") unless res.success?
|
54
|
+
|
55
|
+
response = Oj.load(res.body)
|
56
|
+
Applitools::Session.new(response['id'], response['url'], res.status == HTTP_STATUS_CODES[:created])
|
57
|
+
end
|
58
|
+
|
59
|
+
def stop_session(session, aborted = nil, save = false)
|
60
|
+
res = long_delete(URI.join(endpoint_url, session.id.to_s), query: { aborted: aborted, updateBaseline: save })
|
61
|
+
raise Applitools::EyesError.new("Request failed: #{res.status}") unless res.success?
|
62
|
+
|
63
|
+
response = Oj.load(res.body)
|
64
|
+
Applitools::TestResults.new(response)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
DEFAULT_HEADERS = {
|
70
|
+
'Accept' => 'application/json',
|
71
|
+
'Content-Type' => 'application/json'
|
72
|
+
}.freeze
|
73
|
+
|
74
|
+
LONG_REQUEST_DELAY = 2 # seconds
|
75
|
+
MAX_LONG_REQUEST_DELAY = 10 # seconds
|
76
|
+
LONG_REQUEST_DELAY_MULTIPLICATIVE_INCREASE_FACTOR = 1.5
|
77
|
+
|
78
|
+
[:get, :post, :delete].each do |method|
|
79
|
+
define_method method do |url, options = {}|
|
80
|
+
request(url, method, options)
|
81
|
+
end
|
82
|
+
|
83
|
+
define_method "long_#{method}" do |url, options = {}|
|
84
|
+
long_request(url, method, options)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def request(url, method, options = {})
|
89
|
+
Faraday::Connection.new(url, ssl: { ca_file: SSL_CERT }, proxy: @proxy || nil).send(method) do |req|
|
90
|
+
req.options.timeout = DEFAULT_TIMEOUT
|
91
|
+
req.headers = DEFAULT_HEADERS.merge(options[:headers] || {})
|
92
|
+
req.headers['Content-Type'] = options[:content_type] if options.key?(:content_type)
|
93
|
+
req.params = { apiKey: api_key }.merge(options[:query] || {})
|
94
|
+
req.body = options[:body]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def long_request(url, method, options = {})
|
99
|
+
delay = LONG_REQUEST_DELAY
|
100
|
+
(options[:headers] ||= {})['Eyes-Expect'] = '202-accepted'
|
101
|
+
|
102
|
+
loop do
|
103
|
+
# Date should be in RFC 1123 format.
|
104
|
+
options[:headers]['Eyes-Date'] = Time.now.utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
105
|
+
|
106
|
+
res = request(url, method, options)
|
107
|
+
return res unless res.status == HTTP_STATUS_CODES[:accepted]
|
108
|
+
|
109
|
+
Applitools::EyesLogger.debug "Still running... retrying in #{delay}s"
|
110
|
+
sleep delay
|
111
|
+
|
112
|
+
delay = [MAX_LONG_REQUEST_DELAY, (delay * LONG_REQUEST_DELAY_MULTIPLICATIVE_INCREASE_FACTOR).round].min
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
include Applitools::MethodTracer
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Applitools
|
2
|
+
class AppEnvironment
|
3
|
+
attr_accessor :os, :hosting_app, :display_size, :inferred_environment
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@os = options[:os]
|
7
|
+
@hosting_app = options[:hosting_app]
|
8
|
+
@display_size = options[:display_size]
|
9
|
+
@inferred = options[:inferred]
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_hash
|
13
|
+
{
|
14
|
+
os: @os,
|
15
|
+
hosting_app: @hosting_app,
|
16
|
+
display_size: @display_size.to_hash,
|
17
|
+
inferred: @inferred
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
result = ''
|
23
|
+
to_hash.each_pair do |k, v|
|
24
|
+
result << "#{k}: #{v}; "
|
25
|
+
end
|
26
|
+
result
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Applitools
|
2
|
+
class AppOutput
|
3
|
+
attr_reader :title, :screenshot64
|
4
|
+
|
5
|
+
def initialize(title, screenshot64)
|
6
|
+
@title = title
|
7
|
+
@screenshot64 = screenshot64
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_hash
|
11
|
+
{
|
12
|
+
title: title,
|
13
|
+
screenshot64: ''
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|