Skip to content

Commit

Permalink
Add Unit Testing (#16)
Browse files Browse the repository at this point in the history
* add pytest and tox for unit testing

* add tests for regex checking

* add a base test class

* test Twitter downloads

* test Youtube downloads

* run tests in a PR and pushes

* test only py10 and higher due to used features

* half handle YouTube sign-in error by skipping
skipping is the way to fix this for now, without some OAuth login

* add Instagram tests

Had to modify Instagram login, which might cause issues for other branches we'll see...

* give env variables to tox

* make unit tests run only on main pushes and PRs

* use git lfs for should_be mp4 files

* fix path issue when just using pytest
  • Loading branch information
kytpbs authored Aug 21, 2024
1 parent d158d30 commit 09b6ef0
Show file tree
Hide file tree
Showing 26 changed files with 434 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.mp4 filter=lfs diff=lfs merge=lfs -text
40 changes: 40 additions & 0 deletions .github/workflows/run_tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: check
env:
INSTAGRAM_USERNAME: ${{ secrets.INSTAGRAM_USERNAME }}
INSTAGRAM_SESSION: ${{ secrets.INSTAGRAM_SESSION_JSON }}
on:
push:
branches:
- main
- master
pull_request:

jobs:
test:
name: test with ${{ matrix.py }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
py:
- "3.12"
- "3.11"
- "3.10"
os:
- ubuntu-latest
- macos-latest
- windows-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup python for test ${{ matrix.py }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.py }}
- name: Install tox
run: python -m pip install tox-gh>=1.3
- name: Setup test suite
run: tox -vv --notest
- name: Run test suite
run: tox --skip-pkg-install
21 changes: 21 additions & 0 deletions Tests/video_system/download_tester.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from src.downloader import VIDEO_RETURN_TYPE


class DownloadTester:
def download_single_video_test(self, videos: VIDEO_RETURN_TYPE, should_be_path: str):
assert len(videos) == 1
video = videos[0]

with open(video.path, "rb") as downloaded, open(
should_be_path, "rb"
) as should_be:
assert downloaded.read() == should_be.read(), "Downloaded file does not match the expected file"

def download_multiple_video_test(self, videos: VIDEO_RETURN_TYPE, should_be_paths: list[str]):
assert len(videos) == len(should_be_paths), f"len(videos)={len(videos)} len(should_be_paths)={len(should_be_paths)}"

for video, should_be_path in zip(videos, should_be_paths):
with open(video.path, "rb") as downloaded, open(
should_be_path, "rb"
) as should_be:
assert downloaded.read() == should_be.read(), "Downloaded file does not match the expected file"
3 changes: 3 additions & 0 deletions Tests/video_system/instagram/should_be_0.mp4
Git LFS file not shown
3 changes: 3 additions & 0 deletions Tests/video_system/instagram/should_be_1.mp4
Git LFS file not shown
3 changes: 3 additions & 0 deletions Tests/video_system/instagram/should_be_2.mp4
Git LFS file not shown
3 changes: 3 additions & 0 deletions Tests/video_system/instagram/should_be_3.mp4
Git LFS file not shown
3 changes: 3 additions & 0 deletions Tests/video_system/instagram/should_be_4.mp4
Git LFS file not shown
3 changes: 3 additions & 0 deletions Tests/video_system/instagram/should_be_5.mp4
Git LFS file not shown
3 changes: 3 additions & 0 deletions Tests/video_system/instagram/should_be_6.mp4
Git LFS file not shown
54 changes: 54 additions & 0 deletions Tests/video_system/instagram/test_instagram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import logging
import os
import shutil

import pytest

from Tests.video_system.download_tester import DownloadTester
from src.instagram import InstagramDownloader

DOWNLOAD_PATH = os.path.join("Tests", "video_system", "instagram", "downloads")

# no params
TEST_REEL_1 = "https://www.instagram.com/reel/C2-vIqjsosW/"
#extra params
TEST_REEL_2 = "https://www.instagram.com/reel/C2-vIqjsosW/?igsh=eGFma20zbWY0bDJs"
#multiple videos
TEST_REEL_3 = "https://www.instagram.com/p/C9tjADzPwDz/?igsh=MnF5dzhjajhxZ3Rs"

class TestInstagramDownloader(DownloadTester):
@pytest.fixture(autouse=True)
def remove_all_cache(self):
logging.basicConfig(level=logging.DEBUG)
os.makedirs(DOWNLOAD_PATH, exist_ok=True)
shutil.rmtree(DOWNLOAD_PATH, ignore_errors=True)
yield
# delete the downloads folder after the test.
assert os.path.exists(DOWNLOAD_PATH)
shutil.rmtree(DOWNLOAD_PATH, ignore_errors=True)

def test_instagram_single_video_download(self):
videos = InstagramDownloader.download_video_from_link(
TEST_REEL_1, path=DOWNLOAD_PATH
)
should_be_path = os.path.join("Tests", "video_system", "instagram", "should_be_0.mp4")

assert videos, "couldn't download, probably due to graphql error (login wasn't successful)"
self.download_single_video_test(videos, should_be_path)

def test_instagram_download_extra_params(self):
videos = InstagramDownloader.download_video_from_link(
TEST_REEL_2, path=DOWNLOAD_PATH
)
should_be_path = os.path.join("Tests", "video_system", "instagram", "should_be_0.mp4")

self.download_single_video_test(videos, should_be_path)

def test_instagram_download_multiple_videos(self):
videos = InstagramDownloader.download_video_from_link(
TEST_REEL_3, path=DOWNLOAD_PATH
)

expected_files = [os.path.join("Tests", "video_system", "instagram", should_be) for should_be in [f"should_be_{i}.mp4" for i in range(1, 7)]]

self.download_multiple_video_test(videos, expected_files)
55 changes: 55 additions & 0 deletions Tests/video_system/regex/test_instagram_regex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import pytest

from src.instagram import InstagramDownloader
from Tests.video_system.regex.test_regex_base import TestDownloaderRegex


class TestInstagramRegex(TestDownloaderRegex):
@pytest.fixture(autouse=True)
def setup_class(self):
super().setup(InstagramDownloader)

def test_direct_reel_link(self):
assert self.check_link("https://www.instagram.com/reel/C2-vIqjsosW")

def test_direct_post_link(self):
assert self.check_link("https://www.instagram.com/p/C2-vIqjsosW")

def test_direct_link_with_params(self):
assert self.check_link(
"https://www.instagram.com/p/C2-vIqjsosW/?igsh=eGFma20zbWY0bDJs"
)
assert self.check_link(
"https://www.instagram.com/reel/C2-vIqjsosW/?igsh=eGFma20zbWY0bDJs"
)

def test_shortened_link(self):
assert self.check_link("https://instagr.am/p/C2-vIqjsosW")
assert self.check_link("https://instagr.am/reel/C2-vIqjsosW")

def test_extra_text_link(self):
assert self.check_link(
"Check this out: https://www.instagram.com/p/C2-vIqjsosW"
)
assert self.check_link(
"Check this out: https://www.instagram.com/reel/C2-vIqjsosW"
)
assert self.check_link(
"Dude look at this video fr fr https://www.instagram.com/reel/C2-vIqjsosW"
)

def test_invalid_link(self):
assert not self.check_link("https://www.instagram.com")

def test_invalid_link_with_extra(self):
assert not self.check_link("Look at this: https://www.instagram.com")

def test_invalid_shortened_link(self):
assert not self.check_link("https://instagr.am")

def test_different_type_links(self):
assert not self.check_link("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
assert not self.check_link("https://x.com/MIT_CSAIL/status/1363172815315214336")

def test_non_link(self):
assert not self.check_link("This is not a link")
20 changes: 20 additions & 0 deletions Tests/video_system/regex/test_other_regex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from src.downloading_system import get_downloader


class TestOtherDownloaderRegex:
def check_link(self, link: str):
return get_downloader(link) is not None

def test_not_supported_links(self):
assert not self.check_link("https://facebook.com/1234")
assert not self.check_link(
"https://www.linkedin.com/posts/microsoft_microsoftlife-microsoftcareersemea-softwareengineerjobs-activity-7231661283509460992-3NLl"
)
assert not self.check_link("https://twitch.tv/1234")

def test_link_like_non_links(self):
assert not self.check_link("This is not a link")
assert not self.check_link("alink://link/link")
assert not self.check_link("https://")
assert not self.check_link("https://youtube./com/123")
assert not self.check_link("https://x./com/123")
13 changes: 13 additions & 0 deletions Tests/video_system/regex/test_regex_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from typing import Type

from src.downloader import VideoDownloader
from src.downloading_system import get_downloader


class TestDownloaderRegex:
def setup(self, downloader_type_to_check: Type[VideoDownloader]) -> None:
self.downloader_type = downloader_type_to_check

def check_link(self, link: str):
downloader = get_downloader(link)
return downloader is not None and isinstance(downloader(), self.downloader_type)
34 changes: 34 additions & 0 deletions Tests/video_system/regex/test_twitter_regex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import pytest

from src.twitter import TwitterDownloader
from Tests.video_system.regex.test_regex_base import TestDownloaderRegex


class TestTwitterRegex(TestDownloaderRegex):
@pytest.fixture(autouse=True)
def setup_class(self):
super().setup(TwitterDownloader)

def test_direct_link(self):
assert self.check_link("https://x.com/MIT_CSAIL/status/1363172815315214336")

def test_shortened_link(self):
assert self.check_link("https://t.co/1234")

def test_invalid_link(self):
assert not self.check_link("https://x.com")

def test_extra_text_link(self):
assert self.check_link(
"Dude check this out: https://x.com/MIT_CSAIL/status/1363172815315214336"
)

def test_non_link(self):
assert not self.check_link("This is not a link")

def test_not_supported_link(self):
assert not self.check_link("Check this out: https://facebook.com/1234")

def test_different_type_links(self):
assert not self.check_link("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
assert not self.check_link("https://www.instagram.com/p/CMJ1J1JjJjJ/")
40 changes: 40 additions & 0 deletions Tests/video_system/regex/test_youtube_regex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import pytest

from src.Youtube import YoutubeDownloader
from Tests.video_system.regex.test_regex_base import TestDownloaderRegex


class TestYoutubeRegex(TestDownloaderRegex):
@pytest.fixture(autouse=True)
def setup_class(self):
super().setup(YoutubeDownloader)

def test_direct_link(self):
assert self.check_link("https://www.youtube.com/watch?v=dQw4w9WgXcQ")

def test_direct_link_with_timestamp(self):
assert self.check_link("https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=10s")

def test_shortened_link(self):
assert self.check_link("https://youtu.be/dQw4w9WgXcQ")

def test_shortened_link_with_timestamp(self):
assert self.check_link("https://youtu.be/dQw4w9WgXcQ?t=10")

def test_share_parameter_link(self):
assert self.check_link("https://youtu.be/dQw4w9WgXcQ?si=T3vbuTjMmskB3x3d")

def test_extra_text_link(self):
assert self.check_link(
"Check this out: https://www.youtube.com/watch?v=dQw4w9WgXcQ"
)

def test_invalid_link(self):
assert not self.check_link("https://www.youtube.com")

def test_invalid_shortened_link(self):
assert not self.check_link("https://youtu.be")

def test_different_type_links(self):
assert not self.check_link("https://www.instagram.com/p/CMJ1J1JjJjJ/")
assert not self.check_link("https://x.com/MIT_CSAIL/status/1363172815315214336")
3 changes: 3 additions & 0 deletions Tests/video_system/twitter/should_be_0.mp4
Git LFS file not shown
3 changes: 3 additions & 0 deletions Tests/video_system/twitter/should_be_1.mp4
Git LFS file not shown
3 changes: 3 additions & 0 deletions Tests/video_system/twitter/should_be_2.mp4
Git LFS file not shown
53 changes: 53 additions & 0 deletions Tests/video_system/twitter/test_twitter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import os
import shutil

import pytest

from Tests.video_system.download_tester import DownloadTester
from src.twitter import TwitterDownloader

DOWNLOAD_PATH = os.path.join("Tests", "video_system", "twitter", "downloads")
SHOULD_BE_FILES_PATH = os.path.join("Tests", "video_system", "twitter")

# no lang=en
TEST_TWEET_1 = "https://x.com/MIT_CSAIL/status/1363172815315214336"
# lang=en added
TEST_TWEET_2 = "https://x.com/MIT_CSAIL/status/1363172815315214336?lang=en"
# multiple videos
TEST_TWEET_3 = "https://x.com/kytpbs1/status/1820227127532220806"


class TestTwitterDownloader(DownloadTester):
@pytest.fixture(autouse=True)
def remove_all_cache(self):
os.makedirs(DOWNLOAD_PATH, exist_ok=True)
shutil.rmtree(DOWNLOAD_PATH, ignore_errors=True)
yield
# delete the downloads folder after the test.
assert os.path.exists(DOWNLOAD_PATH)
shutil.rmtree(DOWNLOAD_PATH, ignore_errors=True)

def test_twitter_single_video_download(self):
videos = TwitterDownloader.download_video_from_link(
TEST_TWEET_1, path=DOWNLOAD_PATH
)
should_be_path = os.path.join(SHOULD_BE_FILES_PATH, "should_be_0.mp4")

self.download_single_video_test(videos, should_be_path)

def test_twitter_download_extra_params(self):
videos = TwitterDownloader.download_video_from_link(
TEST_TWEET_2, path=DOWNLOAD_PATH
)
should_be_path = os.path.join(SHOULD_BE_FILES_PATH, "should_be_0.mp4")

self.download_single_video_test(videos, should_be_path)

def test_twitter_download_multiple_videos(self):
videos = TwitterDownloader.download_video_from_link(
TEST_TWEET_3, path=DOWNLOAD_PATH
)

expected_files = [os.path.join(SHOULD_BE_FILES_PATH, should_be) for should_be in ["should_be_1.mp4", "should_be_2.mp4"]]

self.download_multiple_video_test(videos, expected_files)
3 changes: 3 additions & 0 deletions Tests/video_system/youtube/dQw4w9WgXcQ.mp4
Git LFS file not shown
Loading

0 comments on commit 09b6ef0

Please sign in to comment.