def _taking_screenshot_loop( self, user_inputs, # type: UserInputs region, # type: Region check_settings, # type: CheckSettings retry_timeout_ms, # type: int retry_ms, # type: int start, # type: datetime source, # type: Optional[Text] screenshot, # type: Optional[EyesScreenshot] ): # type: (...) -> Optional[EyesScreenshot] if retry_ms >= retry_timeout_ms: return screenshot datetime_utils.sleep(self._MATCH_INTERVAL_MS) new_screenshot = self._try_take_screenshot(user_inputs, region, check_settings, source) if self._match_result.as_expected: return new_screenshot retry_ms = (start - datetime.now()).seconds return self._taking_screenshot_loop( user_inputs, region, check_settings, retry_timeout_ms, retry_ms, start, source, new_screenshot, )
def create_dom_snapshot_loop(script, deadline_time, poll_interval_ms, chunk_byte_length, **script_args): # type: (DomSnapshotScript, float, int, int, **Any) -> Dict chunks = [] result = script.run(**script_args) while result.status is ProcessPageStatus.WIP or ( result.status is ProcessPageStatus.SUCCESS_CHUNKED and not result.done): if time() > deadline_time: raise DomSnapshotTimeout result = script.poll_result(chunk_byte_length) if result.status is ProcessPageStatus.WIP: datetime_utils.sleep(poll_interval_ms, "Waiting for the end of DOM extraction") elif result.status is ProcessPageStatus.SUCCESS_CHUNKED: logger.info("Snapshot chunk {}, {}B".format( len(chunks), len(result.value))) chunks.append(result.value) if result.status is ProcessPageStatus.SUCCESS: return result.value elif result.status.SUCCESS_CHUNKED and result.done: return json.loads("".join(chunks)) elif result.status is ProcessPageStatus.ERROR: raise DomSnapshotScriptError(result.error) else: raise DomSnapshotFailure("Unexpected script result", result)
def set_browser_size(driver, required_size): # type: (AnyWebDriver, ViewPort) -> bool retries_left = _RETRIES # set browser size for mobile devices isn't working if is_mobile_platform(driver): return True while True: logger.debug("Trying to set browser size to: " + str(required_size)) set_window_size(driver, required_size) datetime_utils.sleep(_SLEEP_MS) current_size = get_window_size(driver) logger.debug("Current browser size: " + str(current_size)) if retries_left or current_size != required_size: break retries_left -= 1 if current_size == required_size: logger.debug("Browser size has been successfully set to {}".format( current_size)) return True logger.debug("Browser size wasn't set: {}".format(current_size)) return False
def poll_render_status(self, requests): # type: (List[RenderRequest]) -> List[RenderStatusResults] logger.debug( "poll_render_status call with Requests {}".format(requests)) iterations = 0 statuses = [] # type: List[RenderStatusResults] fails_count = 0 finished = False while True: if finished: break while True: try: statuses = self.eyes_connector.render_status_by_id( *[rq.render_id for rq in requests]) except Exception as e: logger.exception(e) datetime_utils.sleep( 1000, msg="/render-status throws exception... sleeping for 1s" ) fails_count += 1 finally: iterations += 1 datetime_utils.sleep(1500, msg="Rendering...") if statuses or 0 < fails_count < 3: break finished = bool(statuses and (all(s.status != RenderStatus.RENDERING for s in statuses) or iterations > self.MAX_ITERATIONS or False)) return statuses
def poll_render_status(self, render_request): # type: (RenderRequest) -> List[RenderStatusResults] iterations = 0 statuses = [] # type: List[RenderStatusResults] if not render_request.render_id: raise EyesError("RenderStatus: Got empty renderId!") fails_count = 0 finished = False while True: if finished: break while True: try: statuses = self.eyes_connector.render_status_by_id( render_request.render_id) except Exception as e: logger.exception(e) datetime_utils.sleep(1000) fails_count += 1 finally: iterations += 1 datetime_utils.sleep(500) if statuses or 0 < fails_count < 3: break finished = bool(statuses and statuses[0] is not None and (statuses[0].status != RenderStatus.RENDERING or iterations > self.MAX_ITERATIONS or False)) if statuses[0].status == RenderStatus.ERROR: raise EyesError("Got error during rendering: \n\t{}".format( statuses[0].error)) return statuses
def set_browser_size(driver, required_size): # type: (AnyWebDriver, ViewPort) -> bool if is_mobile_platform(driver): return True previous_size = get_window_size(driver) current_size = None set_window_size(driver, required_size) for _ in range(10): # Poll up to 1 second until window is resized datetime_utils.sleep(100) current_size = get_window_size(driver) if current_size != previous_size: if current_size == required_size: logger.debug("set_browser_size succeeded", current_size=current_size) return True else: # Window size probably have exceeded minimal size or screen resolution # and was adjusted to fit these constraints break logger.info( "set_browser_size failed", required_size=required_size, previous_size=previous_size, current_size=current_size, ) return False
def _create_dom_snapshot_loop(self): # type: () -> Dict chunks = [] result = self._script.run(**self._script_args) while result.status is ProcessPageStatus.WIP or ( result.status is ProcessPageStatus.SUCCESS_CHUNKED and not result.done): if time() > self._deadline_time: raise DomSnapshotTimeout result = self._script.poll_result(self._chunk_byte_length) if result.status is ProcessPageStatus.WIP: datetime_utils.sleep( self.POLL_INTERVAL_MS, "Waiting for the end of DOM extraction", ) elif result.status is ProcessPageStatus.SUCCESS_CHUNKED: self._logger.info("Snapshot chunk {}, {}B".format( len(chunks), len(result.value))) chunks.append(result.value) if result.status is ProcessPageStatus.SUCCESS: return result.value elif result.status.SUCCESS_CHUNKED and result.done: return json.loads("".join(chunks)) elif result.status is ProcessPageStatus.ERROR: raise DomSnapshotScriptError(result.error) else: raise DomSnapshotFailure("Unexpected script result", result)
def _take_screenshot( self, user_inputs, # type: UserInputs region, # type: Region tag, # type: Text should_run_once_on_timeout, # type: bool check_settings, # type: CheckSettings retry_timeout, # type: int source, # type: Optional[Text], ): # type: (...) -> EyesScreenshot time_start = datetime.now() if retry_timeout == 0 or should_run_once_on_timeout: if should_run_once_on_timeout: datetime_utils.sleep(retry_timeout) screenshot = self._try_take_screenshot(user_inputs, region, tag, check_settings, source) else: screenshot = self._retry_taking_screenshot(user_inputs, region, tag, check_settings, retry_timeout, source) time_end = datetime.now() summary_ms = datetime_utils.to_ms((time_end - time_start).seconds) logger.debug( "MatchWindowTask._take_screenshot completed in {} ms".format( summary_ms)) return screenshot
def _run_render_status_requests(self): status_tasks = [] while True: with self._have_status_tasks: while not (self._shutdown or status_tasks or self._status_tasks): self._have_status_tasks.wait() if self._shutdown: break new_tasks, self._status_tasks = self._status_tasks, [] status_tasks.extend(new_tasks) ids = [t.render_id for t in status_tasks] logger.debug("render_status_by_id call with ids: {}".format(ids)) try: statuses = self._server_connector.render_status_by_id(*ids) except Exception as e: for task in status_tasks: task.on_error(e) continue rendering = [] current_time = time() for status, task in zip(statuses, status_tasks): if status.status == RenderStatus.RENDERING: if current_time > task.timeout_time: task.on_error(EyesError("Render time out")) else: rendering.append(task) else: task.on_success(status) status_tasks = rendering if status_tasks: datetime_utils.sleep(1000, "Render status polling delay")
def get_all_test_results_impl(self, should_raise_exception=True): # type: (bool) -> TestResultsSummary while True: states = list(set([t.state for t in self.all_running_tests])) if len(states) == 1 and states[0] == "completed": break datetime_utils.sleep(500) all_results = [] for test, test_result in iteritems(self._all_test_result): exception = None if test.test_result is None: exception = TestFailedError("Test haven't finished correctly") scenario_id_or_name = test_result.name app_id_or_name = test_result.app_name if test_result and test_result.is_unresolved and not test_result.is_new: exception = DiffsFoundError( test_result, scenario_id_or_name, app_id_or_name ) if test_result and test_result.is_new: exception = NewTestError( test_result, scenario_id_or_name, app_id_or_name ) if test_result and test_result.is_failed: exception = TestFailedError( test_result, scenario_id_or_name, app_id_or_name ) all_results.append( TestResultContainer(test_result, test.browser_info, exception) ) if exception and should_raise_exception: raise exception return TestResultsSummary(all_results)
def poll_render_status(self, requests): # type: (List[RenderRequest]) -> List[RenderStatusResults] iterations = 0 statuses = [] # type: List[RenderStatusResults] fails_count = 0 finished = False while True: if finished: break while True: try: statuses = self.eyes_connector.render_status_by_id( *[rq.render_id for rq in requests]) except Exception as e: logger.exception(e) datetime_utils.sleep(1000) fails_count += 1 finally: iterations += 1 datetime_utils.sleep(500) if statuses or 0 < fails_count < 3: break finished = bool(statuses and statuses[0] is not None and (statuses[0].status != RenderStatus.RENDERING or iterations > self.MAX_ITERATIONS or False)) return statuses
def perform(self): # noqa # type: () -> List[RenderStatusResults] def get_and_put_resource(url, running_render): # type: (str, RunningRender) -> VGResource logger.debug( "get_and_put_resource({0}, render_id={1}) call".format( url, running_render.render_id)) resource = self.request_resources.get(url) self.eyes_connector.render_put_resource(running_render, resource) return resource requests = self.prepare_data_for_rg(self.script) fetch_fails = 0 render_requests = None already_force_putted = False while True: try: self.put_cache.process_all() render_requests = self.eyes_connector.render(*requests) except Exception as e: logger.exception(e) fetch_fails += 1 datetime_utils.sleep( 1500, msg="/render throws exception... sleeping for 1.5s") if not render_requests: logger.error("running_renders is null") continue need_more_dom = need_more_resources = False for i, running_render in enumerate(render_requests): requests[i].render_id = running_render.render_id need_more_dom = running_render.need_more_dom need_more_resources = (running_render.render_status == RenderStatus.NEED_MORE_RESOURCE) get_and_put_resource_wtih_render = partial( get_and_put_resource, running_render=running_render) dom_resource = requests[i].dom.resource if self.is_force_put_needed and not already_force_putted: for url in self.request_resources: self.put_cache.fetch_and_store( url, get_and_put_resource_wtih_render) already_force_putted = True if need_more_resources: for url in running_render.need_more_resources: self.put_cache.fetch_and_store( url, get_and_put_resource_wtih_render) if need_more_dom: self.eyes_connector.render_put_resource( running_render, dom_resource) still_running = (need_more_resources or need_more_dom or fetch_fails > self.MAX_FAILS_COUNT) if not still_running: break return self.poll_render_status(requests)
def _stitch_screenshot( self, original_stitch_state, # type: PositionMemento stitch_provider, # type: PositionProvider screenshot_parts, # type: List[SubregionForStitching] stitched_image, # type: Image.Image scale_ratio, # type: float scaled_cut_provider, # type: CutProvider ): # type: (...) -> Image logger.info("enter: scale_ratio {}".format(scale_ratio)) for part_region in screenshot_parts: logger.debug("Part: {}".format(part_region)) # Scroll to the part's top/left part_region_location = part_region.scroll_to.offset( original_stitch_state.position) origin_position = stitch_provider.set_position( part_region_location) target_position = part_region.paste_physical_location.offset( part_region_location - origin_position) # Actually taking the screenshot. datetime_utils.sleep(self.wait_before_screenshots) part_image = self.image_provider.get_image() self.debug_screenshot_provider.save(part_image, self._debug_msg("part_image")) cut_part = scaled_cut_provider.cut(part_image) self.debug_screenshot_provider.save(cut_part, self._debug_msg("cut_part")) r = part_region.physical_crop_area if not r.is_size_empty: cropped_part = image_utils.crop_image(cut_part, r) else: cropped_part = cut_part self.debug_screenshot_provider.save( cropped_part, self._debug_msg("cropped_part")) scaled_part_image = image_utils.scale_image( cropped_part, scale_ratio) self.debug_screenshot_provider.save( scaled_part_image, self._debug_msg("scaled_part_image")) r2 = part_region.logical_crop_area scaled_cropped_part_image = image_utils.crop_image( scaled_part_image, r2) self.debug_screenshot_provider.save( scaled_cropped_part_image, self._debug_msg("scaled_cropped_part_image")) logger.debug("pasting part at {}".format(target_position)) stitched_image.paste(scaled_cropped_part_image, box=(target_position.x, target_position.y)) self.debug_screenshot_provider.save( stitched_image, self._debug_msg("stitched_image")) return stitched_image
def test_agent_id(driver_mock, monkeypatch, library, runner, agent_id): monkeypatch.setattr(eyes_selenium_utils, "set_viewport_size", lambda *_: None) eyes = Eyes(runner()) # avoid use custom setter on Eyes object # eyes._current_eyes.server_connector = fake_connector_class() eyes.open(driver_mock, "A", "B", {"width": 100, "height": 100}) eyes.abort_async() while "start_session" not in eyes.server_connector.calls: sleep(1) # wait until runner opens session in background thread assert eyes.server_connector.calls["start_session"].agent_id.startswith(agent_id)
def _element_screenshot(self, scale_provider): # type: (ScaleProvider) -> EyesWebDriverScreenshot logger.info("Element screenshot requested") with self._ensure_element_visible(self._target_element): datetime_utils.sleep(self.configure.wait_before_screenshots) image = self._get_scaled_cropped_image(scale_provider) if not self._is_check_region and not self._driver.is_mobile_platform: # Some browsers return always full page screenshot (IE). # So we cut such images to viewport size image = cut_to_viewport_size_if_required(self.driver, image) return EyesWebDriverScreenshot.create_viewport(self._driver, image)
def _run(self): logger.debug("VisualGridRunner.run()") while self.still_running: try: task = self._task_queue.pop() logger.debug("VisualGridRunner got task %s" % task) except IndexError: datetime_utils.sleep(1000, msg="Waiting for task") continue future = self._executor.submit(lambda task: task(), task) self._future_to_task[future] = task
def _run(self): logger.debug("VisualGridRunner.run()") for test_queue in self._get_parallel_tests_by_round_robin(): try: task = test_queue.popleft() logger.debug("VisualGridRunner got task %s" % task) except IndexError: datetime_utils.sleep(10, msg="Waiting for task", verbose=False) continue future = self._executor.submit(task) self._future_to_task[future] = task logger.debug("VisualGridRunner.run() done")
def perform(self): # type: () -> RenderStatusResults def get_and_put_resource(url): # type: (str) -> VGResource resource = self.request_resources.get(url) self.eyes_connector.render_put_resource(running_render, resource) return resource requests = [] rq = self.prepare_data_for_rg(self.script) requests.append(rq) fetch_fails = 0 render_requests = None while True: try: render_requests = self.eyes_connector.render(*requests) except Exception as e: logger.exception(e) fetch_fails += 1 datetime_utils.sleep(1500) if not render_requests: continue running_render = render_requests[0] rq.render_id = running_render.render_id need_more_dom = running_render.need_more_dom need_more_resources = (running_render.render_status == RenderStatus.NEED_MORE_RESOURCE) still_running = (need_more_resources or need_more_dom or fetch_fails > self.MAX_FAILS_COUNT) dom_resource = rq.dom.resource if need_more_resources: for url in running_render.need_more_resources: self.put_cache.fetch_and_store(url, get_and_put_resource) if need_more_dom: self.eyes_connector.render_put_resource( running_render, dom_resource) if not still_running: break statuses = self.poll_render_status(rq) if statuses and statuses[0].status == RenderStatus.ERROR: raise EyesError("Render failed for {} with the message: {}".format( statuses[0].status, statuses[0].error)) return statuses[0]
def _long_request_loop(self, url, delay=LONG_REQUEST_DELAY_MS): delay = min( self.MAX_LONG_REQUEST_DELAY_MS, math.floor(delay * self.LONG_REQUEST_DELAY_MULTIPLICATIVE_INCREASE_FACTOR), ) logger.debug("Long request. Still running... Retrying in {} ms".format(delay)) datetime_utils.sleep(delay) response = self.request( "get", url, headers={"Eyes-Date": datetime_utils.current_time_in_rfc1123()}, ) if response.status_code != requests.codes.ok: return response return self._long_request_loop(url, delay)
def wait_till_tests_completed(test_provider): # type: (Union[Callable, List]) -> None def get_tests(provider): if isinstance(test_provider, list): return test_provider return test_provider() while True: states = list(set(t.state for t in get_tests(test_provider))) if not states: # probably some exception is happened during execution break if len(states) == 1 and states[0] == "completed": break datetime_utils.sleep(1500, msg="Waiting for state completed!")
def test_special_rendering(url, test_name, batch_info, driver): runner = VisualGridRunner(30) eyes = Eyes(runner) sconf = Configuration( test_name=test_name, app_name="Visual Grid Render Test", batch=batch_info ) sconf.add_device_emulation(DeviceName.Galaxy_S5) sconf.add_browser(1200, 800, BrowserType.CHROME) sconf.add_browser(1200, 800, BrowserType.FIREFOX) eyes.set_configuration(sconf) eyes.open(driver) driver.get(url) datetime_utils.sleep(500) eyes.check(test_name, Target.window().fully()) eyes.close(False) all_results = runner.get_all_test_results(False)
def close(self, raise_ex=True): # noqa # type: (Optional[bool]) -> Optional[TestResults] if self.configuration.is_disabled: logger.debug("close(): ignored (disabled)") return TestResults() if not self.test_list: return TestResults() logger.debug("VisualGridEyes.close()\n\t test_list %s" % self.test_list) self.close_async() while True: states = list(set([t.state for t in self.test_list])) if len(states) == 1 and states[0] == "completed": break datetime_utils.sleep(500) self._is_opened = False for test in self.test_list: if test.pending_exceptions: raise EyesError( "During test execution above exception raised. \n {:s}". join(str(e) for e in test.pending_exceptions)) if raise_ex: for test in self.test_list: if test.test_result is None: raise TestFailedError("Test haven't finished correctly") results = test.test_result scenario_id_or_name = results.name app_id_or_name = results.app_name if results.is_unresolved and not results.is_new: raise DiffsFoundError(results, scenario_id_or_name, app_id_or_name) if results.is_new: raise NewTestError(results, scenario_id_or_name, app_id_or_name) if results.is_failed: raise TestFailedError(results, scenario_id_or_name, app_id_or_name) all_results = [t.test_result for t in self.test_list if t.test_result] if not all_results: return TestResults() return all_results[0]
def perform(self): # type: () -> RenderStatusResults def get_and_put_resource(url): # type: (str) -> VGResource resource = self.request_resources.get(url) self.eyes_connector.render_put_resource(running_render, resource) return resource requests = self.prepare_data_for_rg(self.script) fetch_fails = 0 render_requests = None while True: try: render_requests = self.eyes_connector.render(*requests) except Exception as e: logger.exception(e) fetch_fails += 1 datetime_utils.sleep(1500) if not render_requests: continue need_more_dom = need_more_resources = False for i, running_render in enumerate(render_requests): requests[i].render_id = running_render.render_id need_more_dom = running_render.need_more_dom need_more_resources = (running_render.render_status == RenderStatus.NEED_MORE_RESOURCE) dom_resource = requests[i].dom.resource if need_more_resources: for url in running_render.need_more_resources: self.put_cache.fetch_and_store(url, get_and_put_resource) if need_more_dom: self.eyes_connector.render_put_resource( running_render, dom_resource) still_running = (need_more_resources or need_more_dom or fetch_fails > self.MAX_FAILS_COUNT) if not still_running: break return self.poll_render_status(requests)
def _stop(self): # type: () -> None logger.debug("VisualGridRunner.stop()") while sum(r.score for r in self._get_all_running_tests()) > 0: datetime_utils.sleep(500, msg="Waiting for finishing tests in stop") self.still_running = False for future in concurrent.futures.as_completed(self._future_to_task): task = self._future_to_task[future] try: future.result() except Exception as exc: logger.exception("%r generated an exception: %s" % (task, exc)) else: logger.debug("%s task ran" % task) self.put_cache.executor.shutdown() self.resource_cache.executor.shutdown() self._executor.shutdown() self._thread.join()
def set_browser_size(driver, required_size): # type: (AnyWebDriver, ViewPort) -> bool retries_left = _RETRIES # set browser size for mobile devices isn't working if is_mobile_web(driver): return True while True: logger.info("Trying to set browser size to: " + str(required_size)) set_window_size(driver, required_size) datetime_utils.sleep(_SLEEP_MS) current_size = get_window_size(driver) logger.info("Current browser size: " + str(required_size)) if retries_left or current_size != required_size: break retries_left -= 1 return current_size == required_size
def _long_request_loop(self, url, request_id): delays = delays_gen(500, 5, 2, 5000) delay = next(delays) while True: datetime_utils.sleep( delay, "Long request {} still running".format(request_id) ) response = self.request( "get", url, request_id=request_id, headers={"Eyes-Date": datetime_utils.current_time_in_rfc1123()}, ) if response.status_code == requests.codes.ok: url = response.headers.get("Location", url) if "Retry-After" in response.headers: delay = int(response.headers["Retry-After"]) * 1000 else: delay = next(delays) else: return response
def _get_all_test_results_impl(self, should_raise_exception=True): # type: (bool) -> TestResultsSummary while True: states = list(set(t.state for t in self._get_all_running_tests())) logger.debug("Current test states: \n {}".format(states)) if len(states) == 1 and states[0] == "completed": break datetime_utils.sleep( 1500, msg="Waiting for state completed in get_all_test_results_impl", ) all_results = [] for test, test_result in iteritems(self._all_test_result): if test.pending_exceptions: logger.error( "During test execution above exception raised. \n {:s}". join(str(e) for e in test.pending_exceptions)) exception = None if test.test_result is None: exception = TestFailedError("Test haven't finished correctly") scenario_id_or_name = test_result.name app_id_or_name = test_result.app_name if test_result and test_result.is_unresolved and not test_result.is_new: exception = DiffsFoundError(test_result, scenario_id_or_name, app_id_or_name) if test_result and test_result.is_new: exception = NewTestError(test_result, scenario_id_or_name, app_id_or_name) if test_result and test_result.is_failed: exception = TestFailedError(test_result, scenario_id_or_name, app_id_or_name) all_results.append( TestResultContainer(test_result, test.browser_info, exception)) if exception and should_raise_exception: raise exception return TestResultsSummary(all_results)
def get_dom_script_result(driver, dom_extraction_timeout, timer_name, script_for_run): # type: (AnyWebDriver, int, Text, Text) -> Dict is_check_timer_timeout = [] script_response = {} status = None def start_timer(): def set_timer(): is_check_timer_timeout.append(True) timer = threading.Timer(datetime_utils.to_sec(dom_extraction_timeout), set_timer) timer.daemon = True timer.setName(timer_name) timer.start() return timer timer = start_timer() while True: if status == "SUCCESS" or is_check_timer_timeout: del is_check_timer_timeout[:] break script_result_string = driver.execute_script(script_for_run) try: script_response = json.loads(script_result_string, object_pairs_hook=OrderedDict) status = script_response.get("status") except Exception as e: logger.exception(e) datetime_utils.sleep(1000, "Waiting for the end of DOM extraction") timer.cancel() script_result = script_response.get("value") if script_result is None or status == "ERROR": raise EyesError("Failed to capture script_result") return script_result
def get_stitched_region(self, region, full_area, position_provider): # type: (Region, Optional[Region], Optional[PositionProvider]) -> Image.Image argument_guard.not_none(region) argument_guard.not_none(position_provider) logger.info( "region: %s ; full_area: %s ; position_provider: %s" % (region, full_area, position_provider.__class__.__name__) ) origin_state = self.origin_provider.get_state() if self.origin_provider != position_provider: self.origin_provider.set_position( Point.ZERO() ) # first scroll to 0,0 so CSS stitching works. # Saving the original position (in case we were already in the outermost frame). original_stitched_state = position_provider.get_state() datetime_utils.sleep(self.wait_before_screenshots) initial_screenshot = self.image_provider.get_image() initial_size = RectangleSize.from_(initial_screenshot) pixel_ratio = self._get_pixel_ratio(initial_screenshot) scaled_cut_provider = self.cut_provider.scale(pixel_ratio) cutted_initial_screenshot = self._cut_if_needed( initial_screenshot, scaled_cut_provider ) self.debug_screenshot_provider.save( cutted_initial_screenshot, self._debug_msg("cutted_initial_screenshot") ) region_in_initial_screenshot = self._get_region_in_screenshot( region, cutted_initial_screenshot, pixel_ratio ) cropped_initial_screenshot = self._crop_if_needed( cutted_initial_screenshot, region_in_initial_screenshot ) self.debug_screenshot_provider.save( cropped_initial_screenshot, self._debug_msg("cropped_initial_screenshot") ) scaled_initial_screenshot = image_utils.scale_image( cropped_initial_screenshot, self.scale_provider ) self.debug_screenshot_provider.save( scaled_initial_screenshot, self._debug_msg("scaled_initial_screenshot") ) if full_area is None or full_area.is_empty: entire_size = self._get_entire_size(initial_screenshot, position_provider) # Notice that this might still happen even if we used # "get_image_part", since "entire_page_size" might be that of a # frame if ( scaled_initial_screenshot.width >= entire_size.width and scaled_initial_screenshot.height >= entire_size.height ): self.origin_provider.restore_state(origin_state) return scaled_initial_screenshot full_area = Region.from_(Point.ZERO(), entire_size) scaled_cropped_location = full_area.location physical_crop_location = Point.from_(scaled_cropped_location).scale(pixel_ratio) if region_in_initial_screenshot.is_empty: physical_crop_size = RectangleSize( initial_size.width - physical_crop_location.x, initial_size.height - physical_crop_location.y, ) source_region = Region.from_(physical_crop_location, physical_crop_size) else: # Starting with the screenshot we already captured at (0,0). source_region = region_in_initial_screenshot scaled_cropped_source_rect = self.cut_provider.to_region(source_region.size) scaled_cropped_source_rect = scaled_cropped_source_rect.offset( source_region.left, source_region.top ) scaled_cropped_source_region = dict( x=int(math.ceil(scaled_cropped_source_rect.left / pixel_ratio)), y=int(math.ceil(scaled_cropped_source_rect.top / pixel_ratio)), width=int(math.ceil(scaled_cropped_source_rect.width / pixel_ratio)), height=int(math.ceil(scaled_cropped_source_rect.height / pixel_ratio)), ) scaled_cropped_size = dict( width=scaled_cropped_source_region["width"], height=scaled_cropped_source_region["height"], ) # Getting the list of viewport regions composing the page # (we'll take screenshot for each one). if region_in_initial_screenshot.is_empty: x = max(0, full_area.left) y = max(0, full_area.top) w = min(full_area.width, scaled_cropped_size["width"]) h = min(full_area.height, scaled_cropped_size["height"]) rect_in_initial_screenshot = Region( round(x * pixel_ratio), round(y * pixel_ratio), round(w * pixel_ratio), round(h * pixel_ratio), ) else: rect_in_initial_screenshot = region_in_initial_screenshot screenshot_parts = self._get_image_parts( full_area, scaled_cropped_size, pixel_ratio, rect_in_initial_screenshot ) # Starting with element region size part of the screenshot. Use it as a size # template. stitched_image = Image.new("RGBA", (full_area.width, full_area.height)) # Take screenshot and stitch for each screenshot part. stitched_image = self._stitch_screenshot( original_stitched_state, position_provider, screenshot_parts, stitched_image, self.scale_provider.scale_ratio, scaled_cut_provider, ) position_provider.restore_state(original_stitched_state) self.origin_provider.restore_state(origin_state) return stitched_image
def timeout(timeout_ms): # type: (Num) -> Generator datetime_utils.sleep(timeout_ms) yield