def test_frontend_overrides_backend(mocker: "pytest_mock.MockFixture"): """ class Renderer inherits from (RendererFrontend, backend). RendererFrontend.get_frame() is a wrapper around backend.get_frame() and should override it (RendererFrontend should come first in MRO). Make sure RendererFrontend methods overshadow backend methods. """ # If RendererFrontend.get_frame() override is removed, delete this entire test. frontend_get_frame = mocker.spy(RendererFrontend, "get_frame") backend_get_frame = mocker.spy(AbstractMatplotlibRenderer, "get_frame") corr_cfg = template_config() chan_cfg = ChannelConfig("tests/sine440.wav") channel = Channel(chan_cfg, corr_cfg, channel_idx=0) data = channel.get_render_around(0) renderer = Renderer(corr_cfg.render, corr_cfg.layout, [data], [chan_cfg], [channel]) renderer.update_main_lines([data]) renderer.get_frame() assert frontend_get_frame.call_count == 1 assert backend_get_frame.call_count == 1
def verify_res_divisor_rounding( target_int: int, res_divisor: float, speed_hack: bool, ): """Ensure that pathological-case float rounding errors don't cause inconsistent dimensions and assertion errors.""" target_dim = target_int + 0.5 undivided_dim = round(target_dim * res_divisor) cfg = RendererConfig(undivided_dim, undivided_dim, res_divisor=res_divisor) cfg.before_preview() with ExitStack() as stack: if speed_hack: stack.enter_context( patch.object(AbstractMatplotlibRenderer, "_save_background") ) datas = [] else: datas = [RENDER_Y_ZEROS] try: renderer = Renderer(cfg, LayoutConfig(), datas, None, None) if not speed_hack: renderer.update_main_lines(datas) renderer.get_frame() except Exception: perr(cfg.divided_width) raise
def test_label_render(label_position: LabelPosition, data, hide_lines): """Test that text labels are drawn: - in the correct quadrant - with the correct color (defaults to init_line_color) - even if no lines are drawn at all """ font_str = "#FF00FF" font_u8 = color_to_bytes(font_str) # If hide_lines: set line color to purple, draw text using the line color. # Otherwise: draw lines white, draw text purple, cfg_kwargs = {} if hide_lines: cfg_kwargs.update(init_line_color=font_str) cfg = RendererConfig( WIDTH, HEIGHT, antialiasing=False, label_font=Font(size=16, bold=True), label_position=label_position, label_color_override=font_str, **cfg_kwargs, ) lcfg = LayoutConfig() nplots = 1 labels = ["#"] * nplots datas = [data] * nplots r = Renderer(cfg, lcfg, datas, None, None) r.add_labels(labels) if not hide_lines: r.update_main_lines(datas) frame_buffer: np.ndarray = np.frombuffer(r.get_frame(), dtype=np.uint8).reshape( (r.h, r.w, BYTES_PER_PIXEL) ) # Allow mutation frame_buffer = frame_buffer.copy() yslice = label_position.y.match( top=slice(None, r.h // 2), bottom=slice(r.h // 2, None) ) xslice = label_position.x.match( left=slice(None, r.w // 2), right=slice(r.w // 2, None) ) quadrant = frame_buffer[yslice, xslice] assert np.prod(quadrant == font_u8, axis=-1).any(), "Missing text" quadrant[:] = 0 assert not np.prod( frame_buffer == font_u8, axis=-1 ).any(), "Text appeared in wrong area of screen"
def test_render_output(): """ Ensure rendering to output does not raise exceptions. """ datas = [RENDER_Y_ZEROS] renderer = Renderer(CFG.render, CFG.layout, datas, None, None) out: FFmpegOutput = NULL_FFMPEG_OUTPUT(CFG) renderer.update_main_lines(datas) out.write_frame(renderer.get_frame()) assert out.close() == 0
def test_renderer_layout(): # 2 columns cfg = RendererConfig(WIDTH, HEIGHT) lcfg = LayoutConfig(ncols=2) nplots = 15 datas = [RENDER_Y_ZEROS] * nplots r = Renderer(cfg, lcfg, datas, None, None) r.update_main_lines(RenderInput.wrap_datas(datas), [0] * nplots) layout = r.layout # 2 columns, 8 rows assert layout.wave_ncol == 2 assert layout.wave_nrow == 8
def verify(r: Renderer, appear: Appearance, datas: List[Optional[np.ndarray]]): bg_str = appear.bg.color fg_str = appear.fg.color draw_fg = appear.fg.draw_fg fg_line_width = appear.fg.line_width grid_str = appear.grid.color grid_line_width = appear.grid.line_width viewport_width = appear.debug.viewport_width if draw_fg: r.update_main_lines(datas) frame_colors: np.ndarray = np.frombuffer(r.get_frame(), dtype=np.uint8).reshape( (-1, BYTES_PER_PIXEL) ) bg_u8 = color_to_bytes(bg_str) all_colors = [bg_u8] fg_u8 = color_to_bytes(fg_str) if draw_fg: all_colors.append(fg_u8) is_grid = bool(grid_str and grid_line_width >= 1) if is_grid: grid_u8 = color_to_bytes(grid_str) all_colors.append(grid_u8) else: grid_u8 = np.array([1000] * BYTES_PER_PIXEL) data = datas[0] assert (data.shape[1] > 1) == (data is RENDER_Y_STEREO) is_stereo = is_grid and data.shape[1] > 1 if is_stereo: stereo_grid_u8 = (grid_u8 * OPACITY + bg_u8 * (1 - OPACITY)).astype(int) all_colors.append(stereo_grid_u8) # Ensure background is correct bg_frame = frame_colors[0] assert ( bg_frame == bg_u8 ).all(), f"incorrect background, it might be grid_str={grid_str}" # Ensure foreground is present does_fg_appear_here = np.prod(frame_colors == fg_u8, axis=-1) does_fg_appear = does_fg_appear_here.any() # it might be 136 == #888888 == init_line_color assert does_fg_appear == draw_fg, f"{does_fg_appear} != {draw_fg}" if draw_fg: expected_fg_pixels = NPLOTS * (WIDTH / viewport_width) * fg_line_width assert does_fg_appear_here.sum() == pytest.approx( expected_fg_pixels, abs=expected_fg_pixels * 0.1 ) # Ensure grid color is present does_grid_appear_here = np.prod(frame_colors == grid_u8, axis=-1) does_grid_appear = does_grid_appear_here.any() assert does_grid_appear == is_grid, f"{does_grid_appear} != {is_grid}" if is_grid: assert np.sum(does_grid_appear_here) == pytest.approx( GRID_NPIXEL * grid_line_width, abs=GRID_NPIXEL * 0.1 ) # Ensure stereo grid color is present if is_stereo: assert ( np.min(np.sum(np.abs(frame_colors - stereo_grid_u8), axis=-1)) < TOLERANCE ), "Missing stereo gridlines" assert (np.amax(frame_colors, axis=0) == np.amax(all_colors, axis=0)).all() assert (np.amin(frame_colors, axis=0) == np.amin(all_colors, axis=0)).all()