mirror of
https://github.com/scito/extract_otp_secret_keys.git
synced 2025-12-10 00:35:02 +01:00
test extract_otps_from_camera()
This commit is contained in:
parent
851cb6532c
commit
b215b78dad
2 changed files with 127 additions and 37 deletions
|
|
@ -220,42 +220,6 @@ def extract_otps(args: Args) -> Otps:
|
|||
return extract_otps_from_files(args)
|
||||
|
||||
|
||||
def get_color(new_otps_count: int, otp_url: str) -> ColorBGR:
|
||||
if new_otps_count:
|
||||
return SUCCESS_COLOR
|
||||
else:
|
||||
if otp_url:
|
||||
return FAILURE_COLOR
|
||||
else:
|
||||
return NORMAL_COLOR
|
||||
|
||||
|
||||
# TODO use cv2 types if available
|
||||
def cv2_draw_box(img: Any, raw_pts: Any, color: ColorBGR) -> Any:
|
||||
pts = np.array([raw_pts], np.int32)
|
||||
pts = pts.reshape((-1, 1, 2))
|
||||
cv2.polylines(img, [pts], True, color, BOX_THICKNESS)
|
||||
return pts
|
||||
|
||||
|
||||
# TODO use cv2 types if available
|
||||
def cv2_print_text(img: Any, text: str, line_number: int, position: TextPosition, color: ColorBGR, opposite_len: Optional[int] = None) -> None:
|
||||
window_dim = cv2.getWindowImageRect(WINDOW_NAME)
|
||||
out_text = text
|
||||
if opposite_len:
|
||||
text_dim, _ = cv2.getTextSize(out_text, FONT, FONT_SCALE, FONT_THICKNESS)
|
||||
actual_width = text_dim[TEXT_WIDTH] + opposite_len * CHAR_DX + 4 * BORDER
|
||||
if actual_width >= window_dim[WINDOW_WIDTH]:
|
||||
out_text = out_text[:(window_dim[WINDOW_WIDTH] - actual_width) // CHAR_DX] + '.'
|
||||
text_dim, _ = cv2.getTextSize(out_text, FONT, FONT_SCALE, FONT_THICKNESS)
|
||||
if position == TextPosition.LEFT:
|
||||
pos = BORDER, START_Y + line_number * FONT_DY
|
||||
else:
|
||||
pos = window_dim[WINDOW_WIDTH] - text_dim[TEXT_WIDTH] - BORDER, START_Y + line_number * FONT_DY
|
||||
|
||||
cv2.putText(img, out_text, pos, FONT, FONT_SCALE, color, FONT_THICKNESS, FONT_LINE_STYLE)
|
||||
|
||||
|
||||
def extract_otps_from_camera(args: Args) -> Otps:
|
||||
if verbose: print("Capture QR codes from camera")
|
||||
otp_urls: OtpUrls = []
|
||||
|
|
@ -325,6 +289,42 @@ def extract_otps_from_camera(args: Args) -> Otps:
|
|||
return otps
|
||||
|
||||
|
||||
def get_color(new_otps_count: int, otp_url: str) -> ColorBGR:
|
||||
if new_otps_count:
|
||||
return SUCCESS_COLOR
|
||||
else:
|
||||
if otp_url:
|
||||
return FAILURE_COLOR
|
||||
else:
|
||||
return NORMAL_COLOR
|
||||
|
||||
|
||||
# TODO use cv2 types if available
|
||||
def cv2_draw_box(img: Any, raw_pts: Any, color: ColorBGR) -> Any:
|
||||
pts = np.array([raw_pts], np.int32)
|
||||
pts = pts.reshape((-1, 1, 2))
|
||||
cv2.polylines(img, [pts], True, color, BOX_THICKNESS)
|
||||
return pts
|
||||
|
||||
|
||||
# TODO use cv2 types if available
|
||||
def cv2_print_text(img: Any, text: str, line_number: int, position: TextPosition, color: ColorBGR, opposite_len: Optional[int] = None) -> None:
|
||||
window_dim = cv2.getWindowImageRect(WINDOW_NAME)
|
||||
out_text = text
|
||||
if opposite_len:
|
||||
text_dim, _ = cv2.getTextSize(out_text, FONT, FONT_SCALE, FONT_THICKNESS)
|
||||
actual_width = text_dim[TEXT_WIDTH] + opposite_len * CHAR_DX + 4 * BORDER
|
||||
if actual_width >= window_dim[WINDOW_WIDTH]:
|
||||
out_text = out_text[:(window_dim[WINDOW_WIDTH] - actual_width) // CHAR_DX] + '.'
|
||||
text_dim, _ = cv2.getTextSize(out_text, FONT, FONT_SCALE, FONT_THICKNESS)
|
||||
if position == TextPosition.LEFT:
|
||||
pos = BORDER, START_Y + line_number * FONT_DY
|
||||
else:
|
||||
pos = window_dim[WINDOW_WIDTH] - text_dim[TEXT_WIDTH] - BORDER, START_Y + line_number * FONT_DY
|
||||
|
||||
cv2.putText(img, out_text, pos, FONT, FONT_SCALE, color, FONT_THICKNESS, FONT_LINE_STYLE)
|
||||
|
||||
|
||||
def cv2_handle_pressed_keys(qr_mode: QRMode) -> Tuple[bool, QRMode]:
|
||||
key = cv2.waitKey(1) & 0xFF
|
||||
quit = False
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ import pathlib
|
|||
import re
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional
|
||||
from enum import Enum
|
||||
from typing import Any, List, Optional, Tuple
|
||||
|
||||
import colorama
|
||||
import pytest
|
||||
|
|
@ -37,6 +38,12 @@ from utils import (count_files_in_dir, file_exits, read_binary_file_as_stream,
|
|||
|
||||
import extract_otp_secrets
|
||||
|
||||
try:
|
||||
import cv2 # type: ignore
|
||||
except ImportError:
|
||||
# ignore
|
||||
pass
|
||||
|
||||
qreader_available: bool = extract_otp_secrets.qreader_available
|
||||
|
||||
|
||||
|
|
@ -494,6 +501,89 @@ def test_extract_no_arguments(capsys: pytest.CaptureFixture[str], mocker: Mocker
|
|||
assert e.type == SystemExit
|
||||
|
||||
|
||||
MockMode = Enum('MockMode', ['REPEAT_FIRST_ENDLESS', 'LOOP_LIST'])
|
||||
|
||||
|
||||
class MockCam:
|
||||
|
||||
read_counter: int = 0
|
||||
read_files: List[str] = []
|
||||
mock_mode: MockMode
|
||||
|
||||
def __init__(self, files: List[str] = ['example_export.png'], mock_mode: MockMode = MockMode.REPEAT_FIRST_ENDLESS):
|
||||
self.read_files = files
|
||||
self.image_mode = mock_mode
|
||||
|
||||
def read(self) -> Tuple[bool, Any]:
|
||||
if self.image_mode == MockMode.REPEAT_FIRST_ENDLESS:
|
||||
file = self.read_files[0]
|
||||
elif self.image_mode == MockMode.LOOP_LIST:
|
||||
file = self.read_files[self.read_counter]
|
||||
self.read_counter += 1
|
||||
|
||||
if file:
|
||||
img = cv2.imread(file)
|
||||
return True, img
|
||||
else:
|
||||
return False, None
|
||||
|
||||
def release(self) -> None:
|
||||
# ignore
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.parametrize("qr_reader", [
|
||||
None,
|
||||
'ZBAR',
|
||||
'QREADER',
|
||||
'QREADER_DEEP',
|
||||
'CV2',
|
||||
'CV2_WECHAT'
|
||||
])
|
||||
def test_extract_otps_from_camera(qr_reader: Optional[str], capsys: pytest.CaptureFixture[str], mocker: MockerFixture) -> None:
|
||||
if qreader_available:
|
||||
# Arrange
|
||||
mockCam = MockCam()
|
||||
mocker.patch('cv2.VideoCapture', return_value=mockCam)
|
||||
mocker.patch('cv2.namedWindow')
|
||||
mocker.patch('cv2.rectangle')
|
||||
mocker.patch('cv2.polylines')
|
||||
mocker.patch('cv2.imshow')
|
||||
mocker.patch('cv2.getTextSize', return_value=([8, 200], False))
|
||||
mocker.patch('cv2.putText')
|
||||
mocker.patch('cv2.getWindowImageRect', return_value=[0, 0, 640, 480])
|
||||
mocker.patch('cv2.waitKey', return_value=27)
|
||||
mocker.patch('cv2.getWindowProperty', return_value=False)
|
||||
mocker.patch('cv2.destroyAllWindows')
|
||||
|
||||
args = []
|
||||
if qr_reader:
|
||||
args.append('-Q')
|
||||
args.append(qr_reader)
|
||||
# Act
|
||||
extract_otp_secrets.main(args)
|
||||
|
||||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT_PNG
|
||||
assert captured.err == ''
|
||||
else:
|
||||
# Act
|
||||
with pytest.raises(SystemExit) as e:
|
||||
extract_otp_secrets.main([])
|
||||
|
||||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
|
||||
expected_err_msg = 'error: the following arguments are required: infile'
|
||||
|
||||
assert expected_err_msg in captured.err
|
||||
assert captured.out == ''
|
||||
assert e.value.code == 2
|
||||
assert e.type == SystemExit
|
||||
|
||||
|
||||
def test_verbose_and_quiet(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
with pytest.raises(SystemExit) as e:
|
||||
# Act
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue