mirror of
https://github.com/matomo-org/matomo-icons.git
synced 2024-09-19 17:03:45 +02:00
rewrite test script
This commit is contained in:
parent
c7e970ca50
commit
d8b477df08
3 changed files with 133 additions and 112 deletions
|
@ -2,7 +2,7 @@ os: linux
|
||||||
dist: bionic
|
dist: bionic
|
||||||
language: python
|
language: python
|
||||||
python:
|
python:
|
||||||
"3.8"
|
"3.9"
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- git clone https://github.com/piwik/piwik-package.git tmp/piwik-package
|
- git clone https://github.com/piwik/piwik-package.git tmp/piwik-package
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
Pillow==8.1.2
|
Pillow==9.0.1
|
||||||
PyYAML==5.4.1
|
PyYAML==6.0
|
||||||
|
|
239
tests.py
239
tests.py
|
@ -16,9 +16,10 @@ import hashlib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from glob import glob
|
from pathlib import Path
|
||||||
from subprocess import Popen, PIPE
|
from typing import Dict, List, Iterator, Iterable
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
@ -30,104 +31,112 @@ min_image_size = 48
|
||||||
|
|
||||||
placeholder_icon_hash = "398a623a3b0b10eba6d1884b0ff1713ee12aeafaa8efaf67b60a4624f4dce48c"
|
placeholder_icon_hash = "398a623a3b0b10eba6d1884b0ff1713ee12aeafaa8efaf67b60a4624f4dce48c"
|
||||||
|
|
||||||
searchEnginesFile = "vendor/matomo/searchengine-and-social-list/SearchEngines.yml"
|
searchEnginesFile = Path("vendor/matomo/searchengine-and-social-list/SearchEngines.yml")
|
||||||
socialsEnginesFile = "vendor/matomo/searchengine-and-social-list/Socials.yml"
|
socialsEnginesFile = Path("vendor/matomo/searchengine-and-social-list/Socials.yml")
|
||||||
|
build_script_file = Path("tmp/piwik-package/scripts/build-package.sh")
|
||||||
|
|
||||||
|
src = Path("src/")
|
||||||
|
dist: Path = Path("dist/")
|
||||||
|
|
||||||
|
|
||||||
def print_warning(string):
|
def print_warning(string: str) -> None:
|
||||||
print("\033[33;1m⚠\033[0m " + string)
|
print("\033[33;1m⚠\033[0m " + string)
|
||||||
|
|
||||||
|
|
||||||
def print_error(string):
|
def print_error(string: str) -> None:
|
||||||
print("\033[31;1m⚠ " + string + "\033[0m")
|
print("\033[31;1m⚠ " + string + "\033[0m")
|
||||||
|
|
||||||
|
|
||||||
def load_yaml(file):
|
def load_yaml(file: Path):
|
||||||
with open(file, 'r') as stream:
|
with file.open() as stream:
|
||||||
return yaml.safe_load(stream)
|
return yaml.safe_load(stream)
|
||||||
|
|
||||||
|
|
||||||
def image_exists(pathslug):
|
def image_exists(pathslug: Path) -> bool:
|
||||||
for filetype in ["svg", "png", "gif", "jpg", "ico"]:
|
for filetype in ["svg", "png", "gif", "jpg", "ico"]:
|
||||||
if os.path.isfile(pathslug + "." + filetype):
|
if pathslug.with_suffix(pathslug.suffix + f".{filetype}").exists():
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def test_if_all_icons_are_converted(ignored_source_files):
|
def walk(path: Path) -> Iterator[Path]:
|
||||||
|
for p in path.iterdir():
|
||||||
|
if p.is_dir():
|
||||||
|
yield p
|
||||||
|
yield from walk(p)
|
||||||
|
continue
|
||||||
|
yield p.resolve()
|
||||||
|
|
||||||
|
|
||||||
|
def test_if_all_icons_are_converted(ignored_source_files) -> None:
|
||||||
global error
|
global error
|
||||||
for filetype in ["svg", "png", "gif", "jpg", "ico"]:
|
for filetype in ["svg", "png", "gif", "jpg", "ico"]:
|
||||||
for file in glob("src/**/*.{}".format(filetype)):
|
for file in src.glob(f"**/*.{filetype}"):
|
||||||
abs_dirname, filename = os.path.split(file)
|
distfile = Path("dist/") / Path(*file.parts[1:]).with_suffix(".png")
|
||||||
code = os.path.splitext(filename)[0]
|
|
||||||
distfolder = "dist/" + abs_dirname[4:]
|
|
||||||
distfile = "{folder}/{code}.png".format(folder=distfolder, code=code)
|
|
||||||
|
|
||||||
if not os.path.isfile(distfile) and file not in ignored_source_files:
|
if not distfile.exists() and file not in ignored_source_files:
|
||||||
print_error("{file} is missing (From {source})".format(file=distfile, source=file))
|
print_error(f"{distfile} is missing (From {file})")
|
||||||
error = True
|
error = True
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
def test_if_source_for_images() -> None:
|
||||||
def test_if_source_for_images():
|
|
||||||
global error
|
global error
|
||||||
for icontype in ["brand", "browsers", "os", "plugins", "SEO"]:
|
for icontype in ["brand", "browsers", "os", "plugins", "SEO"]:
|
||||||
for filetype in ["svg", "png", "gif", "jpg", "ico"]:
|
for filetype in ["svg", "png", "gif", "jpg", "ico"]:
|
||||||
for source_file in glob("src/{type}/*.{filetype}".format(type=icontype, filetype=filetype)):
|
for source_file in (src / icontype).glob(f"*.{filetype}"):
|
||||||
if not os.path.islink(source_file):
|
if (
|
||||||
if not os.path.isfile(source_file + ".source") and "UNK" not in source_file:
|
not source_file.is_symlink()
|
||||||
print_error("Source is missing for {file}".format(file=source_file))
|
and not source_file.with_suffix(source_file.suffix + ".source")
|
||||||
error = True
|
and "UNK" not in source_file
|
||||||
|
):
|
||||||
|
print_error(f"Source is missing for {source_file}")
|
||||||
|
error = True
|
||||||
|
|
||||||
|
|
||||||
def test_if_all_symlinks_are_valid():
|
def test_if_all_symlinks_are_valid() -> None:
|
||||||
global error
|
global error
|
||||||
for file in glob("src/**/*"):
|
for file in src.glob("**/*"):
|
||||||
if os.path.islink(file) and not os.path.exists(file):
|
if file.is_symlink():
|
||||||
print_error(
|
target = file.resolve()
|
||||||
"Symlink doesn't link to file (from {link} to {target}".format(link=file, target=os.readlink(file))
|
if not target.exists():
|
||||||
)
|
print_error(f"Symlink doesn't link to file (from {file} to {target} ({file.readlink()}))")
|
||||||
error = True
|
error = True
|
||||||
|
|
||||||
|
|
||||||
def test_if_placeholder_icon_exist(placeholder_icon_filenames):
|
def test_if_placeholder_icon_exist(placeholder_icon_filenames: Dict[str, str]) -> None:
|
||||||
global error
|
global error
|
||||||
for folder, filename in placeholder_icon_filenames.items():
|
for folder, filename in placeholder_icon_filenames.items():
|
||||||
file = "src/{folder}/{filename}".format(folder=folder, filename=filename)
|
file = src / folder / filename
|
||||||
if not (os.path.isfile(file) and hashlib.sha256(open(file, "rb").read()).hexdigest() == placeholder_icon_hash):
|
if not (file.exists() and hashlib.sha256(file.read_bytes()).hexdigest() == placeholder_icon_hash):
|
||||||
print_error("The placeholder icon {path} is missing or invalid".format(path=file))
|
print_error(f"The placeholder icon {file} is missing or invalid")
|
||||||
error = True
|
error = True
|
||||||
|
|
||||||
|
|
||||||
def test_if_icons_are_large_enough():
|
def test_if_icons_are_large_enough() -> None:
|
||||||
# ignore searchEngines and socials
|
# ignore searchEngines and socials
|
||||||
for filetype in ["png", "gif", "jpg", "ico"]:
|
for filetype in ["png", "gif", "jpg", "ico"]:
|
||||||
for source_file in glob("src/*/*.{filetype}".format(filetype=filetype)):
|
for source_file in src.glob(f"*/*.{filetype}"):
|
||||||
im = Image.open(source_file)
|
im = Image.open(source_file)
|
||||||
if im.size[0] < min_image_size or im.size[1] < min_image_size:
|
if im.size[0] < min_image_size or im.size[1] < min_image_size:
|
||||||
|
width, height = im.size
|
||||||
print_warning(
|
print_warning(
|
||||||
"{file} is smaller ({width}x{height}) that the target size ({target}x{target})".format(
|
f"{source_file} is smaller ({width}x{height}) that the target size ({min_image_size}x{min_image_size})"
|
||||||
file=source_file,
|
|
||||||
width=im.size[0],
|
|
||||||
height=im.size[1],
|
|
||||||
target=min_image_size
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
if filetype in ["jpg", "gif", "ico"]:
|
if filetype in ["jpg", "gif", "ico"]:
|
||||||
print_warning("{file} is saved in a lossy image format ({filetype}). ".format(
|
print_warning(
|
||||||
file=source_file,
|
f"{source_file} is saved in a lossy image format ({filetype}). "
|
||||||
filetype=filetype
|
"Maybe try to find an PNG or SVG from another source."
|
||||||
) + "Maybe try to find an PNG or SVG from another source.")
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_if_dist_icons_are_square(ignore_that_icon_isnt_square):
|
def test_if_dist_icons_are_square(ignore_that_icon_isnt_square: List[str]) -> None:
|
||||||
global error
|
global error
|
||||||
for file in glob("dist/**/*.png"):
|
for file in dist.glob("**/*.png"):
|
||||||
if "flags" not in file:
|
if (dist / "flags") not in file.parents:
|
||||||
im = Image.open(file)
|
im = Image.open(file)
|
||||||
if im.size[0] != im.size[1]:
|
width, height = im.size
|
||||||
string = "{file} isn't square ({width}x{height})".format(file=file, width=im.size[0], height=im.size[1])
|
if width != height:
|
||||||
|
string = f"{file} isn't square ({width}x{height})"
|
||||||
if file not in ignore_that_icon_isnt_square:
|
if file not in ignore_that_icon_isnt_square:
|
||||||
error = True
|
error = True
|
||||||
print_error(string)
|
print_error(string)
|
||||||
|
@ -135,36 +144,53 @@ def test_if_dist_icons_are_square(ignore_that_icon_isnt_square):
|
||||||
print_warning(string)
|
print_warning(string)
|
||||||
|
|
||||||
|
|
||||||
def test_if_build_script_is_deleting_all_unneeded_files():
|
def is_in_allowed_dir(file: Path) -> bool:
|
||||||
with open("tmp/piwik-package/scripts/build-package.sh") as f:
|
allowed_dirs = [dist, Path("node_modules"), Path("tmp"), Path("vendor"), Path(".idea")]
|
||||||
build_script = f.read()
|
for dir in allowed_dirs:
|
||||||
|
dir = dir.resolve()
|
||||||
|
if dir == file:
|
||||||
|
return True
|
||||||
|
if dir in file.parents:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_deleted(file: Path, deleted_files: Iterable[Path]) -> bool:
|
||||||
|
# print(file, deleted_files)
|
||||||
|
for del_file in deleted_files:
|
||||||
|
if del_file == file:
|
||||||
|
return True
|
||||||
|
if del_file in file.parents:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def test_if_build_script_is_deleting_all_unneeded_files() -> None:
|
||||||
global error
|
global error
|
||||||
deleted_files = []
|
build_script = build_script_file.read_text()
|
||||||
all_files = []
|
deleted_files = set()
|
||||||
for root, dirs, files in os.walk(".", topdown=False):
|
all_files = set(walk(Path(".").resolve()))
|
||||||
for name in files:
|
|
||||||
all_files.append(os.path.join(root, name))
|
|
||||||
for name in dirs:
|
|
||||||
all_files.append(os.path.join(root, name))
|
|
||||||
for pattern in build_script_regex.findall(build_script):
|
for pattern in build_script_regex.findall(build_script):
|
||||||
deleted_files.extend(glob(pattern))
|
deleted_files.update(Path(".").resolve().glob(pattern))
|
||||||
for file in all_files:
|
for file in all_files:
|
||||||
if not any(s in file for s in deleted_files) and not (
|
if is_deleted(file, deleted_files):
|
||||||
file.startswith("./dist") or file.startswith("./tmp") or file.startswith("./vendor")
|
continue
|
||||||
or file.startswith("./node_modules")
|
if is_in_allowed_dir(file):
|
||||||
) and file != "./README.md":
|
continue
|
||||||
print_error("{file} should be deleted by the build script".format(file=file))
|
if file == Path("README.md").resolve():
|
||||||
error = True
|
continue
|
||||||
|
print_error(f"{file} should be deleted by the build script")
|
||||||
|
error = True
|
||||||
|
|
||||||
|
|
||||||
def test_if_icons_are_indicated_to_be_improvable():
|
def test_if_icons_are_indicated_to_be_improvable() -> None:
|
||||||
for file in glob("src/**/*.todo"):
|
for file in src.glob("**/*.todo"):
|
||||||
print_warning("{icon} could be improved".format(icon=file[:-5]))
|
print_warning(f"{str(file)[:-5]} could be improved")
|
||||||
|
|
||||||
|
|
||||||
def look_for_search_and_social_icon(source, mode, outputdir):
|
def look_for_search_and_social_icon(source, mode, outputdir: Path) -> None:
|
||||||
global error
|
global error
|
||||||
correct_files = []
|
correct_files = set()
|
||||||
for i, element in source.items():
|
for i, element in source.items():
|
||||||
if mode == "searchengines":
|
if mode == "searchengines":
|
||||||
search_engine = element[0]
|
search_engine = element[0]
|
||||||
|
@ -173,32 +199,30 @@ def look_for_search_and_social_icon(source, mode, outputdir):
|
||||||
urls = element
|
urls = element
|
||||||
url = next((url for url in urls if "{}" not in url), False)
|
url = next((url for url in urls if "{}" not in url), False)
|
||||||
url = urlparse("https://" + url).netloc
|
url = urlparse("https://" + url).netloc
|
||||||
|
if url and not image_exists(outputdir / url):
|
||||||
if url and not image_exists(outputdir + url):
|
print_error(f"icon for {url} is missing")
|
||||||
print_error("icon for {icon} is missing".format(icon=url))
|
|
||||||
error = True
|
error = True
|
||||||
correct_files.append(url)
|
correct_files.add(url)
|
||||||
# print(correct_files)
|
|
||||||
for filetype in ["svg", "png", "gif", "jpg", "ico"]:
|
for filetype in ["svg", "png", "gif", "jpg", "ico"]:
|
||||||
for file in glob(outputdir + "*.{ext}".format(ext=filetype)):
|
for file in outputdir.glob(f"*.{filetype}"):
|
||||||
domain = os.path.splitext(os.path.basename(file))[0]
|
domain = file.stem
|
||||||
if domain not in correct_files and domain != "xx":
|
if domain not in correct_files and domain != "xx":
|
||||||
print_error("{file} is not necessary".format(file=file))
|
print_error(f"{file} is not necessary")
|
||||||
error = True
|
error = True
|
||||||
|
|
||||||
|
|
||||||
def test_if_all_search_and_social_sites_have_an_icon():
|
def test_if_all_search_and_social_sites_have_an_icon() -> None:
|
||||||
look_for_search_and_social_icon(load_yaml(searchEnginesFile), "searchengines", "src/searchEngines/")
|
look_for_search_and_social_icon(load_yaml(searchEnginesFile), "searchengines", Path("src/searchEngines/"))
|
||||||
look_for_search_and_social_icon(load_yaml(socialsEnginesFile), "socials", "src/socials/")
|
look_for_search_and_social_icon(load_yaml(socialsEnginesFile), "socials", Path("src/socials/"))
|
||||||
|
|
||||||
|
|
||||||
def test_if_there_are_icons_for_all_device_detector_categories(less_important_device_detector_icons):
|
def test_if_there_are_icons_for_all_device_detector_categories(
|
||||||
|
less_important_device_detector_icons: Dict[str, List[str]]
|
||||||
|
) -> None:
|
||||||
global error
|
global error
|
||||||
process = Popen(["php", "devicedetector.php"], stdout=PIPE)
|
output = subprocess.run(["php", "devicedetector.php"], capture_output=True)
|
||||||
(output, err) = process.communicate()
|
regex = re.compile(r"[^a-z0-9_\-]+", re.IGNORECASE)
|
||||||
process.wait()
|
categories = json.loads(output.stdout)
|
||||||
regex = re.compile(r"[^a-z0-9_\-]+",re.IGNORECASE)
|
|
||||||
categories = json.loads(output)
|
|
||||||
for icontype, category in categories.items():
|
for icontype, category in categories.items():
|
||||||
for code in category:
|
for code in category:
|
||||||
if icontype == "brand":
|
if icontype == "brand":
|
||||||
|
@ -207,12 +231,12 @@ def test_if_there_are_icons_for_all_device_detector_categories(less_important_de
|
||||||
slug = code
|
slug = code
|
||||||
found = False
|
found = False
|
||||||
for filetype in ["svg", "png", "gif", "jpg", "ico"]:
|
for filetype in ["svg", "png", "gif", "jpg", "ico"]:
|
||||||
if os.path.isfile("src/{type}/{slug}.{ext}".format(type=icontype, slug=slug, ext=filetype)):
|
file = Path(f"src/{icontype}/{slug}.{filetype}")
|
||||||
|
if file.exists():
|
||||||
found = True
|
found = True
|
||||||
|
break
|
||||||
if not found:
|
if not found:
|
||||||
warning = "icon for {icon} missing (should be at src/{type}/{slug}.{{png|svg}})".format(
|
warning = f"icon for {category[code]} missing (should be at src/{icontype}/{slug}.{{png|svg}})"
|
||||||
type=icontype, icon=category[code], slug=slug
|
|
||||||
)
|
|
||||||
if slug in less_important_device_detector_icons[icontype]:
|
if slug in less_important_device_detector_icons[icontype]:
|
||||||
print_warning(warning)
|
print_warning(warning)
|
||||||
else:
|
else:
|
||||||
|
@ -223,29 +247,26 @@ def test_if_there_are_icons_for_all_device_detector_categories(less_important_de
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
error = False
|
error = False
|
||||||
|
|
||||||
ignore = load_yaml("tests-ignore.yml")
|
ignore = load_yaml(Path("tests-ignore.yml"))
|
||||||
|
|
||||||
if "TRAVIS_PULL_REQUEST" not in os.environ or not os.environ["TRAVIS_PULL_REQUEST"]:
|
if "TRAVIS_PULL_REQUEST" not in os.environ or not os.environ["TRAVIS_PULL_REQUEST"]:
|
||||||
test_if_all_icons_are_converted(ignore["ignored_source_files"])
|
test_if_all_icons_are_converted(ignore["ignored_source_files"])
|
||||||
|
exit()
|
||||||
|
|
||||||
test_if_source_for_images()
|
test_if_source_for_images()
|
||||||
test_if_all_symlinks_are_valid()
|
test_if_all_symlinks_are_valid()
|
||||||
test_if_placeholder_icon_exist(ignore["placeholder_icon_filenames"])
|
test_if_placeholder_icon_exist(ignore["placeholder_icon_filenames"])
|
||||||
test_if_dist_icons_are_square(ignore["ignore_that_icon_isnt_square"])
|
test_if_dist_icons_are_square(ignore["ignore_that_icon_isnt_square"])
|
||||||
if "TRAVIS" in os.environ and os.environ["TRAVIS"]: # collapse on travis
|
travis = "TRAVIS" in os.environ and os.environ["TRAVIS"] # collapse on travis
|
||||||
|
if travis:
|
||||||
print("travis_fold:start:improvable_icons")
|
print("travis_fold:start:improvable_icons")
|
||||||
print("improvable icons: (click to expand)")
|
print("improvable icons: (click to expand)")
|
||||||
test_if_there_are_icons_for_all_device_detector_categories(ignore["less_important_device_detector_icons"])
|
test_if_there_are_icons_for_all_device_detector_categories(ignore["less_important_device_detector_icons"])
|
||||||
test_if_icons_are_indicated_to_be_improvable()
|
test_if_icons_are_indicated_to_be_improvable()
|
||||||
test_if_icons_are_large_enough()
|
test_if_icons_are_large_enough()
|
||||||
|
if travis:
|
||||||
print("travis_fold:end:improvable_icons")
|
print("travis_fold:end:improvable_icons")
|
||||||
test_if_all_search_and_social_sites_have_an_icon()
|
test_if_all_search_and_social_sites_have_an_icon()
|
||||||
test_if_build_script_is_deleting_all_unneeded_files()
|
test_if_build_script_is_deleting_all_unneeded_files()
|
||||||
else:
|
|
||||||
test_if_there_are_icons_for_all_device_detector_categories(ignore["less_important_device_detector_icons"])
|
|
||||||
test_if_icons_are_indicated_to_be_improvable()
|
|
||||||
test_if_icons_are_large_enough()
|
|
||||||
test_if_all_search_and_social_sites_have_an_icon()
|
|
||||||
test_if_build_script_is_deleting_all_unneeded_files()
|
|
||||||
|
|
||||||
sys.exit(error)
|
sys.exit(error)
|
||||||
|
|
Loading…
Reference in a new issue