How to use reoffset method in hypothesis

Best Python code snippet using hypothesis

Re2filter.py

Source:Re2filter.py Github

copy

Full Screen

1import ConfigParser2import json3import os4import sys5import time6import pika7import wtclib8if not os.path.isdir("../log/reret"):9 try:10 os.mkdir("../log/reret")11 except Exception, e:12 print str(e) + " in line: " + str(sys._getframe().f_lineno)13 os._exit()14mylogger = wtclib.create_logging("../log/reret/re2filter.log")15mylogger.info("start running")16while True:17 (cur, err) = wtclib.get_a_sql_cur("../conf/conf.conf","hvpd3db2")18 if None != cur:19 break20 else:21 mylogger.info("can not connect to db and sleep 20")22 time.sleep(20)23"""24cur.execute("insert into ReFilterTable(Spaceid, cameraID, cmosID)values('123456', '1315', 0)")25cur.execute("update ReFilterTable set PlateNumber1='wfbi' where Spaceid='123456'")26cur.execute("update ReFilterTable set PlateNumber1=NULL where Spaceid='123456'")27"""28"""29_pid = os.getpid()30try:31 cur.execute("insert into WatchdogTable(pid, runCMD,watchSeconds,renewTimet)values(%d, "32 "'python %s &', 300, %d)"%(_pid, __file__, (time.time())))33except Exception, e:34 mylogger.error(str(e)+time.asctime())35"""36msg_props = pika.BasicProperties()37msg_props.content_type = "text/plain"38pre_similar_table = ['0DQ','8BRH',39 'S53','2Z',40 'T71','PRF',41 'PRF','6G',42 '4A']43def match_same_process(cur, FilterSql, ReoutSql):44 cur.execute()45def match_process(src, dst):46 i = -147 cnt = 048 for cc in src:49 i += 150 if cc == dst[i]:51 cnt += 152 continue53 for list1 in pre_similar_table:54 if cc in list1 and dst[i] in list1:55 cnt += 156 break57 return cnt58def same_match_filter(filterTable):59 return 160def msg_consumer(channel, method, header, body):61 channel.basic_ack(delivery_tag=method.delivery_tag)62 global cur, mylogger, channelOut, msg_props63 _timeSec = int(time.time())64 """65 global _pid66 cur.execute("update WatchdogTable set renewTimet=%d where pid=%d" % (_timeSec, _pid))67 """68 mylogger.debug("msg recv" + body)69 try:70 _dic_info = json.loads(body)71 except Exception, e:72 mylogger.warning(str(e))73 mylogger.warning("len = %d"%len(body))74 mylogger.warning("json.loads "+body)75 return76 if "quit" in _dic_info:77 channel.basic_cancel(consumer_tag="hello-consumer2")78 channel.stop_consuming()79 mylogger.debug("quit")80 mylogger.debug(body)81 if not "cam_id" in _dic_info:82 mylogger.info("not cam_id in _dic_info")83 return84 if not "plateGet" in _dic_info:85 mylogger.info("not plateGet in _dic_info")86 return87 if not "CMOS" in _dic_info:88 mylogger.info("not CMOS in _dic_info")89 return90 if not type(_dic_info["CMOS"])==int:91 mylogger.debug("not type CMOS is int")92 return93 if not type(_dic_info["plateGet"])==int:94 mylogger.debug("not type plateGet is int")95 return96 _cmos = _dic_info["CMOS"]97 if _cmos > 1:98 mylogger.error("CMOS=%d"%_cmos)99 return100 if 0 == _cmos :101 if(_dic_info["plateGet"] > 3 or _dic_info["plateGet"] < 1):102 mylogger.info("CMOS=%d and plate=%d"%(_cmos,_dic_info["plateGet"]))103 return104 else:105 _Reoffset = (_dic_info["plateGet"]-1)*4+2106 if 1 == _cmos :107 if(_dic_info["plateGet"] > 6 or _dic_info["plateGet"] < 4):108 mylogger.info("CMOS=%d and plate=%d"%(_cmos,_dic_info["plateGet"]))109 return110 else:111 _Reoffset = (_dic_info["plateGet"] - 4) * 4+2112 _sql_str = "select * from ReOutTable where cameraID='%s' and cmosID=%d"%(_dic_info["cam_id"], _cmos)113 if 0 == cur.execute(_sql_str):114 mylogger.debug("0="+_sql_str)115 return116 try:117 _re_out = cur.fetchmany(1)[0]118 except:119 mylogger.debug("read select * from ReOutTable = 0")120 return121 _hvpd = _dic_info["cam_id"]122 try:123 _spaceID = _dic_info["cam_id"]+str(_dic_info["plateGet"])124 if 0 == cur.execute("select * from ReFilterTable where Spaceid='%s'"%_spaceID):125 cur.execute("insert into ReFilterTable(Spaceid, cameraID, cmosID)values('%s', '%s', %d)"126 %((_dic_info["cam_id"]+str(_dic_info["plateGet"])), _dic_info["cam_id"], _cmos))127 _ReFilterInfo = cur.fetchmany(cur.execute("select * from ReFilterTable where Spaceid='%s'"%_spaceID))[0]128 if None == _re_out[_Reoffset+1]:# no plate here129 _ret = 0130 for i in range(8):131 if None == _ReFilterInfo[4+i*6] and 0 != _ReFilterInfo[3+i*6]: #same with no plate132 cur.execute("update ReFilterTable set sameCnt%d=%d,matchCnt%d=%d,sameTimet%d=%d,matchTimet%d=%d "133 "where Spaceid='%s'"%(i+1, _ReFilterInfo[5+i*6],i+1, _ReFilterInfo[6+i*6],134 i+1, _timeSec,i+1, _timeSec, _spaceID))135 _ret = 1 #found a no plate, so update136 break137 if 0 == _ret:138 for i in range(8):139 if 0 == _ReFilterInfo[3+i*4]:#find a empty buffer140 cur.execute("update ReFilterTable set inuse%d=1,PlateNumber%d=NULL, sameCnt%d=1,matchCnt%d=1,sameTimet%d=%d,matchTimet%d=%d "141 "where Spaceid='%s'"%(i+1, i+1,i+1,i+1, i+1, _timeSec, i+1,_timeSec,_spaceID))142 _ret = 11143 break144 if 11 != _ret:#all is inuse145 _matchtimet = []146 for i in range(8):147 _matchtimet.append(_ReFilterInfo[6*i+8])#delete the oldest match148 _insert = _matchtimet.index(min(_matchtimet))149 cur.execute("update ReFilterTable set PlateNumber%d=NULL, sameCnt%d=1,matchCnt%d=1,sameTimet%d=%d,matchTimet%d=%d "150 "where Spaceid='%s'" % (_insert+1, _insert+1, _insert+1, _insert+1, _timeSec, _insert+1, _timeSec, _spaceID))151 else:152 match_same_process(cur, _ReFilterInfo, _re_out)153 _ret = 0154 for i in range(8): #same and match155 if 0 != _ReFilterInfo[6*i+3] and _re_out[_Reoffset+1] == _ReFilterInfo[6*i+4]:156 cur.execute("update ReFilterTable set sameCnt%d=%d,sameTimet%d=%d,matchCnt%d=%d,matchTimet%d=%d "157 "where Spaceid='%s'" % (i+1, _ReFilterInfo[6*i+5], i+1, _timeSec, i+1, _ReFilterInfo[6*i+6], i+1, _timeSec,_spaceID))158 _ret = 1159 continue160 if match_process(_re_out[_Reoffset+1], _ReFilterInfo[6*i+4])>3:161 cur.execute("update ReFilterTable set matchCnt%d=%d,matchTimet%d=%d "162 "where Spaceid='%s'" % (i + 1, _ReFilterInfo[6 * i + 5], i + 1, _timeSec, _spaceID))163 _ret = 1164 continue165 if 0 == _ret:166 _insert = 10167 for i in range(8):168 if 0 == _ReFilterInfo[6*i+3]:169 _insert = i170 break171 if 10 != _insert:172 cur.execute("update ReFilterTable set inuse%d=1, PlateNumber%d='%s', sameCnt%d=1, matchCnt%d=1,"173 "sameTimet%d=%d, matchTimet%d=%d where Spaceid='%s'"174 % (_insert + 1, _insert + 1,_re_out[_Reoffset+1], _insert + 1, _insert + 1, _insert + 1, _timeSec, _insert + 1,_timeSec, _spaceID))175 else:176 _matchtimet = []177 for i in range(8):178 _matchtimet.append(_ReFilterInfo[6 * i + 8])179 _insert = _matchtimet.index(min(_matchtimet))180 cur.execute("update ReFilterTable set inuse%d=1, PlateNumber%d='%s', sameCnt%d=1, matchCnt%d=1,"181 "sameTimet%d=%d, matchTimet%d=%d where Spaceid='%s'"182 % (183 _insert + 1, _insert + 1, _re_out[_Reoffset + 1], _insert + 1, _insert + 1, _insert + 1, _timeSec,184 _insert + 1, _timeSec, _spaceID))185 _ReFilterInfo = cur.fetchmany(cur.execute("select * from ReFilterTable where Spaceid='%s'"%_spaceID))[0]186 _insert = same_match_filter(_ReFilterInfo)187 if 0 == cur.execute("select * from ReOutFilterTable where cameraID='%s' and cmosID=%d"%(_dic_info["cam_id"], _cmos)):188 cur.execute("insert into ReOutFilterTable(cameraID, cmosID)values('%s',%d)"%(_dic_info["cam_id"], _cmos))189 cur.execute("update ReOutFilterTable")190 msg = json.dumps({"sn": _hvpd, "CMOS": _cmos})191 try:192 mylogger.debug("basic_publish:"+msg)193 channelOut.basic_publish(body=msg, exchange="ReOut.filter", properties=msg_props,routing_key="hola")194 except Exception, e:195 channel.basic_cancel(consumer_tag="hello-consumer2")196 channel.stop_consuming()197 mylogger.error("channelOut Error "+str(e))198 except:199 mylogger.error("end")200 pass201 return202if __name__ == '__main__':203 conf_dict = wtclib.get_user_config_ret_dict("../conf/conf.conf", "rabbitmq")204 if "reoutmq_server_addr" in conf_dict:205 _mq_host = conf_dict["reoutmq_server_addr"]206 else:207 _mq_host = wtclib.get_ip_addr1("eth0")208 if "reoutmq_server_port" in conf_dict:209 _mq_port = int(conf_dict["reoutmq_server_port"])210 else:211 _mq_port = 5672212 if "reoutmq_exchange" in conf_dict:213 _mq_exchange = conf_dict["reoutmq_exchange"]214 else:215 _mq_exchange = "ReOut.tr"216 if "reoutmq_user_name" in conf_dict:217 _mq_user_name = conf_dict["reoutmq_user_name"]218 else:219 _mq_user_name = "user1"220 if "reoutmq_passwd" in conf_dict:221 _mq_passwd = conf_dict["reoutmq_passwd"]222 else:223 _mq_passwd = "9876543"224 if "reoutmq_vhost" in conf_dict:225 _mq_vhost = conf_dict["reoutmq_vhost"]226 else:227 _mq_vhost = "OutTrig"228 while True:229 while True:230 try:231 credentials = pika.PlainCredentials(_mq_user_name, _mq_passwd)232 conn_params = pika.ConnectionParameters(host=_mq_host, virtual_host=_mq_vhost, credentials=credentials)233 conn_broker = pika.BlockingConnection(conn_params)234 break235 except Exception, e:236 mylogger.info(str(e))237 time.sleep(2)238 while True:239 try:240 channel = conn_broker.channel()241 # channel.exchange_declare(exchange="AiOut",exchange_type="fanout", passive=False, durable=False, auto_delete=True)242 channel.queue_declare(queue="refilter")243 channel.queue_bind(queue="refilter", exchange=_mq_exchange)244 break245 except Exception, e:246 mylogger.info(str(e))247 time.sleep(20)248 channelOut = conn_broker.channel()249 channelOut.exchange_declare(exchange="ReOut.filter", exchange_type="fanout",250 passive=False, durable=False, auto_delete=True)251 channel.basic_consume(msg_consumer, queue="refilter", consumer_tag="filter")252 try:253 channel.start_consuming()254 except Exception, e:255 mylogger.error(str(e))256 try:257 channel.close()258 except Exception, e:259 mylogger.error(str(e))260 try:261 channelOut.close()262 except Exception, e:...

Full Screen

Full Screen

SpatialAlignmentEva.py

Source:SpatialAlignmentEva.py Github

copy

Full Screen

1import numpy as np2import matplotlib.pyplot as plt3from scipy.optimize import curve_fit4from scipy.ndimage import interpolation5from photonscore.python.flim import read6from photonscore.python.histogram import histogram7from photonscore.python.vault import Vault8from matplotlib.colors import colorConverter9import matplotlib as mpl10from glob import glob11#from bokeh.models import HoverTool12#from bokeh.io import output_notebook13from Classes.EventPreProcessor import EventPreProcessor14from Classes.EinsteinCorrection import EinsteinCorrection15def oneDFringes(x, Amplitude, Phase, Period, Offset):16 #return 10000*np.sin(Period*x)+Offset17 return Amplitude*np.sin(Period*x+Phase)+Offset18data = Vault(r"E:\RawData\SpatialIonsMainz\Measurement\01_09_2018_FirstMeas\17uW53.photons")19masterX = data.data.master.photons.x[:].astype(np.int64) - 204820masterY = data.data.master.photons.y[:].astype(np.int64) - 204821masterCoords = np.array([22 masterX,23 masterY24]).T25rotAngle = (-29.72883115886924/180.)*np.pi26rotMatrix = np.array([27 [np.cos(rotAngle), -1*np.sin(rotAngle)],28 [np.sin(rotAngle), np.cos(rotAngle)]29])30rotatedMasterCoords = rotMatrix.dot(masterCoords.T).T + 204831#imagMaster = histogram(data.data.master.photons.x[:], 0, 4000, 400, data.data.master.photons.y[:])32#plt.imshow(imagMaster)33#plt.title("Master Image Unrotated")34#plt.show()35imagMaster = histogram(rotatedMasterCoords.T[0], 0, 4000, 400, rotatedMasterCoords.T[1])36#plt.imshow(imagMaster)37#plt.title("Master Image Rotated")38#plt.show()39#print("master finished")40slaveX = data.data.slave.photons.x[:].astype(np.int64) - 204841slaveY = data.data.slave.photons.y[:].astype(np.int64) - 204842slaveCoords = np.array([43 slaveX,44 slaveY45]).T46rotAngle = (-5.19673564264405/180.)*np.pi47rotMatrix = np.array([48 [np.cos(rotAngle), -1*np.sin(rotAngle)],49 [np.sin(rotAngle), np.cos(rotAngle)]50])51#rotate52rotatedSlaveCoords = rotMatrix.dot(slaveCoords.T).T53copiedRotatedSlaveCoords = rotatedSlaveCoords54#translate55translateX = -6556translateY = -5057#mirror x-axis58rotatedSlaveCoords = np.array([59 (np.array(rotatedSlaveCoords.T[0]) * -1) + translateX,60 np.array(rotatedSlaveCoords.T[1] + translateY),61]).T62#reOffset63rotatedSlaveCoords = rotatedSlaveCoords + 204864#imagSlave = histogram(data.data.slave.photons.x[:], 0, 4000, 400, data.data.slave.photons.y[:])65#plt.imshow(imagSlave)66#plt.title("Slave Image Unrotated")67#plt.show()68imagSlave = histogram(rotatedSlaveCoords.T[0], 0, 4000, 400, rotatedSlaveCoords.T[1])69#plt.imshow(imagSlave)70#plt.title("Slave Image Rotated")71#plt.show()72#optimzation process73Steps = range(-200,0, 2)74visibilities = []75for step in Steps:76 break77 print(str(step))78 temporalCopy = copiedRotatedSlaveCoords79 # translate80 translateX = step81 translateY = -5082 # mirror x-axis83 temporalCopy = np.array([84 (np.array(temporalCopy.T[0]) * -1) + translateX,85 np.array(temporalCopy.T[1] + translateY),86 ]).T87 # reOffset88 temporalCopy = temporalCopy + 204889 # imagSlave = histogram(data.data.slave.photons.x[:], 0, 4000, 400, data.data.slave.photons.y[:])90 # plt.imshow(imagSlave)91 # plt.title("Slave Image Unrotated")92 # plt.show()93 imagSlave = histogram(temporalCopy.T[0], 0, 4000, 400, temporalCopy.T[1])94 reducedData = np.sum(imagSlave[125:340, 150:340], axis=0)95 xdata = np.array(range(len(reducedData)))96 popt, pcov = curve_fit(oneDFringes, xdata, reducedData, p0=[4000, -5100, 0.15, 25200])97 #popt = [4000, -5100, 0.15, 25200]98 #plt.plot(xdata, reducedData)99 #plt.plot(xdata, oneDFringes(xdata, *popt))100 #plt.title("Rotation: " + str(angle))101 #plt.show()102 visibility = (popt[0])/(popt[3])103 visibilities.append(np.abs(visibility))104 #print("did " + str(angle) + " degrees")105#rough estimation106#visibilities = np.array(visibilities)107#plt.plot(Steps, visibilities)108#plt.show()109#minStep = Steps[visibilities.argmin()]110minStep = -69111#translate112translateX = minStep113translateY = -50114#mirror x-axis115rotatedSlaveCoords = np.array([116 (np.array(copiedRotatedSlaveCoords.T[0]) * -1) + translateX,117 np.array(copiedRotatedSlaveCoords.T[1] + translateY),118]).T119#reOffset120rotatedSlaveCoords = rotatedSlaveCoords + 2048121#imagSlave = histogram(data.data.slave.photons.x[:], 0, 4000, 400, data.data.slave.photons.y[:])122#plt.imshow(imagSlave)123#plt.title("Slave Image Unrotated")124#plt.show()125imagSlave = histogram(rotatedSlaveCoords.T[0], 0, 4000, 400, rotatedSlaveCoords.T[1])126#opaque stuff127# generate the colors for your colormap128color1 = colorConverter.to_rgba('white')129color2 = colorConverter.to_rgba('black')130# make the colormaps131cmap1 = mpl.colors.LinearSegmentedColormap.from_list('my_cmap',['green','blue'],256)132cmap2 = mpl.colors.LinearSegmentedColormap.from_list('my_cmap2',[color1,color2],256)133cmap2._init() # create the _lut array, with rgba values134# create your alpha array and fill the colormap with them.135# here it is progressive, but you can create whathever you want136alphas = np.linspace(0, 0.5, cmap2.N+3)137cmap2._lut[:,-1] = alphas138f = plt.figure()139f.add_subplot(2,2, 1)140plt.title("imag Master")141plt.imshow(imagMaster)142f.add_subplot(2,2, 2)143plt.title("imag Slave")144plt.imshow(imagSlave)145f.add_subplot(2,2,3)146plt.title("Overlay")147plt.imshow(imagMaster, interpolation='nearest', cmap=cmap1, origin='lower')148plt.imshow(imagSlave, interpolation='nearest', cmap=cmap2, origin='lower')149f.add_subplot(2,2,4)150plt.title("subtract")151plt.imshow(imagSlave/np.amax(imagSlave) - imagMaster/np.amax(imagMaster))152plt.show(block=True)...

Full Screen

Full Screen

neszip.py

Source:neszip.py Github

copy

Full Screen

1#!/usr/bin/env python32# Copyright (C) 2018, Vi Grey3# All rights reserved.4#5# Redistribution and use in source and binary forms, with or without6# modification, are permitted provided that the following conditions7# are met:8#9# 1. Redistributions of source code must retain the above copyright10# notice, this list of conditions and the following disclaimer.11# 2. Redistributions in binary form must reproduce the above copyright12# notice, this list of conditions and the following disclaimer in the13# documentation and/or other materials provided with the distribution.14#15# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS "AS IS" AND16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25# SUCH DAMAGE.26import os.path27import struct28import sys29VERSION = '0.0.3'30HOSTOS = b'\x05'31# Check if help or version flag was passed as an argument32if len(sys.argv) >= 2:33 if sys.argv[1] == '--version' or sys.argv[1] == '-v':34 print('neszip ' + VERSION)35 exit(0)36# Check if there are enough arguments otherwise37elif len(sys.argv) != 3:38 print('\x1b[91m2 NES file and ZIP file paths required\x1b[0m')39 exit(1)40# Get nes file and zip file inputs values from arguments41nes_input = sys.argv[1]42zip_input = sys.argv[2]43# Takes the zip file and modifies its contents to be correctly offset44# And also claims the zip file was made on an Atari ST45def reoffset_zip(n, z, i):46 new_z = b''47 z_cursor = 048 # get last central directory file header signature49 last_file_index = z.rfind(b'\x50\x4b\x01\x02')50 index_check = True51 if last_file_index == -1:52 index_check = False53 while index_check:54 next_file_index = z.index(b'\x50\x4b\x01\x02', z_cursor)55 if next_file_index == last_file_index:56 index_check = False57 new_z += z[z_cursor: next_file_index]58 z_cursor = next_file_index59 new_z += z[z_cursor: z_cursor + 5]60 new_z += HOSTOS61 new_z += z[z_cursor + 6: z_cursor + 42]62 old_z_offset = struct.unpack('<I', z[z_cursor + 42: z_cursor + 46])[0]63 new_z += struct.pack('<I', old_z_offset + i)64 z_cursor += 4665 new_z += z[z_cursor: -6]66 zip_comment_len = len(n) - i - len(z)67 new_zip_start_offset = struct.unpack('<I', z[-6: -2])[0] + i68 new_z += struct.pack('<I', new_zip_start_offset)69 new_z += struct.pack('<H', zip_comment_len)70 return(new_z)71# Takes an NES file's contents and a zip file's contents and merges72# them into a single file73def create_polyglot(n, z):74 new_nes_contents = b''75 if len(n) - 8214 >= 16378:76 # 16394 gives a 6 byte buffer for end of PRG77 i = n.rfind(b'\x00' * len(z), 16, 16394)78 if i != -1:79 new_nes_contents += n[:i]80 z_reoffset = reoffset_zip(n, z, i)81 new_nes_contents += z_reoffset82 new_nes_contents += n[i + len(z):]83 return(new_nes_contents)84 else:85 print('\x1b[91mNot enough space in NES file for ZIP file\x1b[0m')86 exit(1)87 else:88 print('\x1b[91mInvalid NES file size\x1b[0m')89 exit(1)90# Get contents of NES rom file and zip file91try:92 nes_file = open(nes_input, 'rb+')93 nes_content = nes_file.read()94 zip_file = open(zip_input, 'rb')95 zip_content = zip_file.read()96 zip_file.close()97except:98 print('\x1b[91mUnable to open or read an input file\x1b[0m')99 exit(1)100# Create the new NES file that has the embedded zip file101try:102 nes_file.seek(0)103 nes_file.write(create_polyglot(nes_content, zip_content))104 nes_file.close()105except:106 print('\x1b[91mUnable to write output file\x1b[0m')...

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run hypothesis automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful