working
This commit is contained in:
parent
eadbd8f14a
commit
b9fcf68fdf
22 changed files with 201 additions and 166 deletions
20
fuzzer.py
20
fuzzer.py
|
@ -166,10 +166,18 @@ def get_test_case_source(f_name: str, test_case: Params, i: int, indent: int):
|
|||
{space}{single_indent}assert {call_statement(f_name_orig, test_case)} == {repr(output)}"""
|
||||
|
||||
|
||||
def get_test_class(f_name: str, cases: set[Params]) -> str:
|
||||
f_name_orig = BranchTransformer.to_original_name(f_name)
|
||||
def get_test_import_stmt(names: list[str]):
|
||||
imports = ["from unittest import TestCase"]
|
||||
|
||||
test_class = (f"from unittest import TestCase\n\nfrom {module_of[f_name]} import {f_name_orig}\n\n\n"
|
||||
f"class Test_{f_name_orig}(TestCase):\n")
|
||||
test_class += "\n\n".join([get_test_case_source(f_name, case, i + 1, 1) for i, case in enumerate(cases)])
|
||||
return test_class
|
||||
for orig_f_name in names:
|
||||
f_name = BranchTransformer.to_instrumented_name(orig_f_name)
|
||||
imports.append(f"from {'.'.join(module_of[f_name])} import {orig_f_name}")
|
||||
|
||||
return "\n".join(imports) + "\n"
|
||||
|
||||
|
||||
def get_test_class(orig_f_name: str, cases: set[Params]) -> str:
|
||||
f_name = BranchTransformer.to_instrumented_name(orig_f_name)
|
||||
return (f"class Test_{orig_f_name}(TestCase):\n" +
|
||||
"\n\n".join([get_test_case_source(f_name, case, i + 1, 1) for i, case in enumerate(cases)]) +
|
||||
"\n")
|
||||
|
|
25
genetic.py
25
genetic.py
|
@ -1,7 +1,6 @@
|
|||
import argparse
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
from functools import partial
|
||||
|
||||
import frozendict
|
||||
|
@ -56,8 +55,8 @@ def init_deap():
|
|||
creator.create("Individual", list, fitness=creator.Fitness)
|
||||
|
||||
|
||||
def generate(f_name: str):
|
||||
orig_name = instrument.BranchTransformer.to_original_name(f_name)
|
||||
def generate(orig_name: str) -> set[instrument.Params]:
|
||||
f_name = instrument.BranchTransformer.to_instrumented_name(orig_name)
|
||||
args = instrument.functions[f_name]
|
||||
|
||||
range_start, range_end = instrument.n_of_branches[f_name]
|
||||
|
@ -106,6 +105,9 @@ def generate(f_name: str):
|
|||
top_result = archive.build_suite()
|
||||
top_coverage = cov
|
||||
|
||||
if tot_covered == total_branches:
|
||||
break
|
||||
|
||||
return top_result
|
||||
|
||||
|
||||
|
@ -150,11 +152,13 @@ def compute_fitness(f_name: str, archive: Archive, individual: list) -> tuple[fl
|
|||
return fitness,
|
||||
|
||||
|
||||
def build_suite(f_name: str):
|
||||
instr_name = instrument.BranchTransformer.to_instrumented_name(f_name)
|
||||
cases = generate(instr_name)
|
||||
with open(os.path.join(OUT_DIR, "test_" + f_name + ".py"), "w") as f:
|
||||
f.write(get_test_class(instr_name, cases))
|
||||
def build_suite(filename: str, f_names: list[str]):
|
||||
suite = [(name, generate(name)) for name in f_names]
|
||||
|
||||
with open(os.path.join(OUT_DIR, f"test_{filename}.py"), "w") as f:
|
||||
f.write(fuzzer.get_test_import_stmt(f_names))
|
||||
f.write("\n\n")
|
||||
f.write("\n\n".join([get_test_class(name, cases) for name, cases in suite]))
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -169,9 +173,8 @@ def main():
|
|||
instrument.load_benchmark(save_instrumented=False, files=parser.parse_args().file)
|
||||
init_deap()
|
||||
|
||||
for instr_f in tqdm.tqdm(sorted(instrument.functions.keys()), desc="Generating tests"):
|
||||
print("", file=sys.stderr)
|
||||
build_suite(instrument.BranchTransformer.to_original_name(instr_f))
|
||||
for file_name, functions in tqdm.tqdm(instrument.get_benchmark().items(), desc="Generating tests"):
|
||||
build_suite(file_name, functions)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import ast
|
||||
import os.path
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import Optional
|
||||
|
||||
import astunparse
|
||||
import tqdm
|
||||
from frozendict import frozendict
|
||||
|
||||
from operators import evaluate_condition
|
||||
|
||||
ROOT_DIR: str = os.path.dirname(__file__)
|
||||
|
@ -85,7 +87,7 @@ SignatureDict = dict[str, list[Arg]]
|
|||
|
||||
n_of_branches: dict[str, tuple[int, int]] = {}
|
||||
functions: SignatureDict = {}
|
||||
module_of: dict[str, str] = {}
|
||||
module_of: dict[str, list[str]] = {}
|
||||
|
||||
|
||||
def instrument(source_path: str, target_path: str, save_instrumented=True):
|
||||
|
@ -130,9 +132,7 @@ def instrument(source_path: str, target_path: str, save_instrumented=True):
|
|||
arg_types.append((arg.arg, arg_type))
|
||||
|
||||
functions[f.name] = arg_types
|
||||
module_of[f.name] = os.path.normpath(os.path.relpath(source_path, ROOT_DIR)) \
|
||||
.replace(".py", "") \
|
||||
.replace("/", ".")
|
||||
module_of[f.name] = os.path.splitext(os.path.normpath(os.path.relpath(source_path, ROOT_DIR)))[0].split("/")
|
||||
|
||||
|
||||
def invoke(f_name: str, f_args: Params) -> any:
|
||||
|
@ -176,5 +176,19 @@ def call_statement(f_name: str, f_args: Params) -> str:
|
|||
return f"{f_name}({', '.join(arg_list)})"
|
||||
|
||||
|
||||
def get_benchmark() -> dict[str, list[str]]:
|
||||
"""
|
||||
Returns a dictionary associated each source code file name loaded (without extension) with the list of
|
||||
(non-instrumented) function names defined within it
|
||||
"""
|
||||
|
||||
names: defaultdict[str, list[str]] = defaultdict(list)
|
||||
|
||||
for f in functions:
|
||||
names[module_of[f][-1]].append(BranchTransformer.to_original_name(f))
|
||||
|
||||
return dict(names)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
load_benchmark(save_instrumented=True)
|
||||
|
|
41
muttest.py
Normal file
41
muttest.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
import instrument
|
||||
|
||||
ROOT_DIR = os.path.dirname(__file__)
|
||||
IN_SOURCE_DIR = os.path.join(ROOT_DIR, "benchmark")
|
||||
IN_TEST_DIR = os.path.join(ROOT_DIR, "tests")
|
||||
OUT_DIR = os.path.join(ROOT_DIR, "tests")
|
||||
|
||||
|
||||
def run_mutpy(test_path: str, source_path: str):
|
||||
stream = os.popen(f'mut.py --target \'{source_path}\' --unit-test \'{test_path}\'')
|
||||
output = stream.read()
|
||||
score = re.search('Mutation score \[.*\]: (\d+\.\d+)\%', output).group(1)
|
||||
print(output, file=sys.stderr)
|
||||
print(f"Score is: {score}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(prog='muttest.py',
|
||||
description='Runs MutPy over generated test suite.')
|
||||
parser.add_argument('file', type=str, help="Source file to test",
|
||||
nargs="*")
|
||||
|
||||
files = parser.parse_args().file
|
||||
if len(files) == 0:
|
||||
to_test = instrument.get_benchmark().keys()
|
||||
else:
|
||||
to_test = [os.path.splitext(os.path.basename(file))[0] for file in files]
|
||||
|
||||
for filename in to_test:
|
||||
source_path = os.path.join(IN_SOURCE_DIR, f"{filename}.py")
|
||||
test_path = os.path.join(IN_TEST_DIR, f"test_{filename}.py")
|
||||
run_mutpy(test_path, source_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -2,4 +2,5 @@ nltk==3.8.1
|
|||
deap==1.4.1
|
||||
astunparse==1.6.3
|
||||
frozendict==2.3.8
|
||||
tqdm==4.66.1
|
||||
tqdm==4.66.1
|
||||
MutPy==0.6.1
|
|
@ -1,20 +1,16 @@
|
|||
from unittest import TestCase
|
||||
|
||||
from benchmark.anagram_check import anagram_check
|
||||
|
||||
|
||||
class Test_anagram_check(TestCase):
|
||||
def test_anagram_check_1(self):
|
||||
assert anagram_check(s1='B', s2='o8') == False
|
||||
assert anagram_check(s1='@', s2='{') == False
|
||||
|
||||
def test_anagram_check_2(self):
|
||||
assert anagram_check(s1=' ', s2='u') == False
|
||||
assert anagram_check(s1='', s2='X~|') == False
|
||||
|
||||
def test_anagram_check_3(self):
|
||||
assert anagram_check(s1='', s2='') == True
|
||||
assert anagram_check(s1='?kv|d', s2='43J!j') == False
|
||||
|
||||
def test_anagram_check_4(self):
|
||||
assert anagram_check(s1='?8H', s2='') == False
|
||||
|
||||
def test_anagram_check_5(self):
|
||||
assert anagram_check(s1='@m', s2='Pj') == False
|
||||
assert anagram_check(s1='U', s2='') == False
|
||||
|
|
19
tests/test_caesar_cipher.py
Normal file
19
tests/test_caesar_cipher.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
from unittest import TestCase
|
||||
from benchmark.caesar_cipher import encrypt
|
||||
from benchmark.caesar_cipher import decrypt
|
||||
|
||||
|
||||
class Test_encrypt(TestCase):
|
||||
def test_encrypt_1(self):
|
||||
assert encrypt(strng='(B{6M K', key=90) == '#=v1HzF'
|
||||
|
||||
def test_encrypt_2(self):
|
||||
assert encrypt(strng='t3Cv', key=84) == 'i(8k'
|
||||
|
||||
|
||||
class Test_decrypt(TestCase):
|
||||
def test_decrypt_1(self):
|
||||
assert decrypt(strng='4.J<IH{', key=11) == ')#?1>=p'
|
||||
|
||||
def test_decrypt_2(self):
|
||||
assert decrypt(strng='5v8K', key=32) == 'tVw+'
|
|
@ -1,23 +0,0 @@
|
|||
from unittest import TestCase
|
||||
|
||||
from benchmark.common_divisor_count import cd_count
|
||||
|
||||
|
||||
class Test_cd_count(TestCase):
|
||||
def test_cd_count_1(self):
|
||||
assert cd_count(a=-946, b=0) == 2
|
||||
|
||||
def test_cd_count_2(self):
|
||||
assert cd_count(a=873, b=164) == 1
|
||||
|
||||
def test_cd_count_3(self):
|
||||
assert cd_count(a=0, b=-90) == 2
|
||||
|
||||
def test_cd_count_4(self):
|
||||
assert cd_count(a=783, b=-749) == 1
|
||||
|
||||
def test_cd_count_5(self):
|
||||
assert cd_count(a=621, b=-23) == 2
|
||||
|
||||
def test_cd_count_6(self):
|
||||
assert cd_count(a=-635, b=444) == 1
|
|
@ -1,20 +1,16 @@
|
|||
from unittest import TestCase
|
||||
|
||||
from benchmark.check_armstrong import check_armstrong
|
||||
|
||||
|
||||
class Test_check_armstrong(TestCase):
|
||||
def test_check_armstrong_1(self):
|
||||
assert check_armstrong(n=370) == True
|
||||
assert check_armstrong(n=220) == False
|
||||
|
||||
def test_check_armstrong_2(self):
|
||||
assert check_armstrong(n=5) == False
|
||||
|
||||
def test_check_armstrong_3(self):
|
||||
assert check_armstrong(n=1) == True
|
||||
|
||||
def test_check_armstrong_4(self):
|
||||
assert check_armstrong(n=0) == True
|
||||
|
||||
def test_check_armstrong_5(self):
|
||||
assert check_armstrong(n=346) == False
|
||||
def test_check_armstrong_3(self):
|
||||
assert check_armstrong(n=35) == False
|
||||
|
||||
def test_check_armstrong_4(self):
|
||||
assert check_armstrong(n=1) == True
|
||||
|
|
19
tests/test_common_divisor_count.py
Normal file
19
tests/test_common_divisor_count.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
from unittest import TestCase
|
||||
from benchmark.common_divisor_count import cd_count
|
||||
|
||||
|
||||
class Test_cd_count(TestCase):
|
||||
def test_cd_count_1(self):
|
||||
assert cd_count(a=-741, b=-457) == 2
|
||||
|
||||
def test_cd_count_2(self):
|
||||
assert cd_count(a=111, b=99) == 6
|
||||
|
||||
def test_cd_count_3(self):
|
||||
assert cd_count(a=740, b=-169) == 3
|
||||
|
||||
def test_cd_count_4(self):
|
||||
assert cd_count(a=0, b=449) == 2
|
||||
|
||||
def test_cd_count_5(self):
|
||||
assert cd_count(a=910, b=0) == 2
|
|
@ -1,8 +0,0 @@
|
|||
from unittest import TestCase
|
||||
|
||||
from benchmark.caesar_cipher import decrypt
|
||||
|
||||
|
||||
class Test_decrypt(TestCase):
|
||||
def test_decrypt_1(self):
|
||||
assert decrypt(strng='\\nEGQ&', key=41) == '3E{}(ý'
|
|
@ -1,11 +0,0 @@
|
|||
from unittest import TestCase
|
||||
|
||||
from benchmark.caesar_cipher import encrypt
|
||||
|
||||
|
||||
class Test_encrypt(TestCase):
|
||||
def test_encrypt_1(self):
|
||||
assert encrypt(strng='ToU}[G4}', key=70) == ';V<dB.zd'
|
||||
|
||||
def test_encrypt_2(self):
|
||||
assert encrypt(strng='Ja%b7$x?S', key=91) == 'F]!^3 t;O'
|
|
@ -1,8 +1,7 @@
|
|||
from unittest import TestCase
|
||||
|
||||
from benchmark.exponentiation import exponentiation
|
||||
|
||||
|
||||
class Test_exponentiation(TestCase):
|
||||
def test_exponentiation_1(self):
|
||||
assert exponentiation(baseNumber=-963, power=53) == -135579247021508037271506970896774862890401757444424074712158036994660498207857413507120369768136011010001454548485109484383268428282840962739680900835831388403
|
||||
assert exponentiation(baseNumber=771, power=474) == 2909210833489491293378842744086198556807698873805727913089141407566444307615874153703385291756938871584773318376082715282560979576003239005635105383696346203211241092618871232636438256963156478107480938401494345016352663544251711600300410294173581492138819773752393733341176714058194552308171277586141676868377612692878963273168698528314579277083563280084863520035365678210031287889772088909455822915896837639257753201805916075921630683337539958393092565260283310668864760181646358235718551645281913578630788801289878777658471432050952913833670208688658984707550985045490960816912971550665977523223101342154277056798749106996358053949918115208536589659714855628074010414625147806448539987417123348274550207168187859690445981358534944856355827734611140152400206513027687952209274470811281719393462377048863468859856738711318208410712979094433620095183415992926567580010288921145076938532560376641152027545404240780516247328096495417952049358953162107534093353491634575118361354594270772437236185212631466579892318815456992018956388226428774320211702200525700236584879799246396547556437518901096156284575576838239123951159155896959994089575333138430692507022983208229342846303747301555381846496103057947746652402219370385064952595644297390849057715440768800731610673913446165042850598544860086460108426470227274128434108967174102649421640276420078514555815576331215899881
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
from unittest import TestCase
|
||||
|
||||
from benchmark.gcd import gcd
|
||||
|
||||
|
||||
class Test_gcd(TestCase):
|
||||
def test_gcd_1(self):
|
||||
assert gcd(a=531, b=338) == 1
|
||||
assert gcd(a=1, b=292) == 1
|
||||
|
||||
def test_gcd_2(self):
|
||||
assert gcd(a=1, b=395) == 1
|
||||
assert gcd(a=706, b=706) == 706
|
||||
|
||||
def test_gcd_3(self):
|
||||
assert gcd(a=71, b=496) == 1
|
||||
assert gcd(a=506, b=1) == 1
|
||||
|
||||
def test_gcd_4(self):
|
||||
assert gcd(a=644, b=644) == 644
|
||||
assert gcd(a=695, b=765) == 765
|
||||
|
||||
def test_gcd_5(self):
|
||||
assert gcd(a=581, b=1) == 1
|
||||
assert gcd(a=140, b=29) == 140
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
from unittest import TestCase
|
||||
|
||||
from benchmark.longest_substring import longest_sorted_substr
|
||||
|
||||
|
||||
class Test_longest_sorted_substr(TestCase):
|
||||
def test_longest_sorted_substr_1(self):
|
||||
assert longest_sorted_substr(s='YW|jsXG,u') == 'W|'
|
||||
assert longest_sorted_substr(s='sixBa') == 'ix'
|
16
tests/test_rabin_karp.py
Normal file
16
tests/test_rabin_karp.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
from unittest import TestCase
|
||||
from benchmark.rabin_karp import rabin_karp_search
|
||||
|
||||
|
||||
class Test_rabin_karp_search(TestCase):
|
||||
def test_rabin_karp_search_1(self):
|
||||
assert rabin_karp_search(pat='3gx!', txt='~*J~%eC') == [0]
|
||||
|
||||
def test_rabin_karp_search_2(self):
|
||||
assert rabin_karp_search(pat='`gO7Vq!kU', txt='=DLaH\\p~[') == []
|
||||
|
||||
def test_rabin_karp_search_3(self):
|
||||
assert rabin_karp_search(pat='', txt='%gxypQ7L') == []
|
||||
|
||||
def test_rabin_karp_search_4(self):
|
||||
assert rabin_karp_search(pat='@', txt='H@@|DPma"') == [1, 2]
|
|
@ -1,20 +0,0 @@
|
|||
from unittest import TestCase
|
||||
|
||||
from benchmark.rabin_karp import rabin_karp_search
|
||||
|
||||
|
||||
class Test_rabin_karp_search(TestCase):
|
||||
def test_rabin_karp_search_1(self):
|
||||
assert rabin_karp_search(pat='', txt='m 2') == []
|
||||
|
||||
def test_rabin_karp_search_2(self):
|
||||
assert rabin_karp_search(pat='.w5', txt='55[Ax5X') == []
|
||||
|
||||
def test_rabin_karp_search_3(self):
|
||||
assert rabin_karp_search(pat='h@=y', txt='JcEC') == []
|
||||
|
||||
def test_rabin_karp_search_4(self):
|
||||
assert rabin_karp_search(pat='X', txt='J@X"') == [2]
|
||||
|
||||
def test_rabin_karp_search_5(self):
|
||||
assert rabin_karp_search(pat='>0OPQ', txt='Dzxu8:(P') == []
|
|
@ -1,17 +0,0 @@
|
|||
from unittest import TestCase
|
||||
|
||||
from benchmark.railfence_cipher import raildecrypt
|
||||
|
||||
|
||||
class Test_raildecrypt(TestCase):
|
||||
def test_raildecrypt_1(self):
|
||||
assert raildecrypt(st='q338K a{.', k=9) == 'q338K a{.'
|
||||
|
||||
def test_raildecrypt_2(self):
|
||||
assert raildecrypt(st='q338K a{.', k=3) == 'q8{K3 .a3'
|
||||
|
||||
def test_raildecrypt_3(self):
|
||||
assert raildecrypt(st='Hi=BC/', k=557) == 'Hi=BC/'
|
||||
|
||||
def test_raildecrypt_4(self):
|
||||
assert raildecrypt(st='X61*8p', k=5) == 'X61*p8'
|
|
@ -1,14 +0,0 @@
|
|||
from unittest import TestCase
|
||||
|
||||
from benchmark.railfence_cipher import railencrypt
|
||||
|
||||
|
||||
class Test_railencrypt(TestCase):
|
||||
def test_railencrypt_1(self):
|
||||
assert railencrypt(st=')x8ro;BVm', k=865) == ')x8ro;BVm'
|
||||
|
||||
def test_railencrypt_2(self):
|
||||
assert railencrypt(st='q338K a{.', k=4) == 'qa3 {3K.8'
|
||||
|
||||
def test_railencrypt_3(self):
|
||||
assert railencrypt(st='X61*8p', k=5) == 'X61*p8'
|
25
tests/test_railfence_cipher.py
Normal file
25
tests/test_railfence_cipher.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
from unittest import TestCase
|
||||
from benchmark.railfence_cipher import railencrypt
|
||||
from benchmark.railfence_cipher import raildecrypt
|
||||
|
||||
|
||||
class Test_railencrypt(TestCase):
|
||||
def test_railencrypt_1(self):
|
||||
assert railencrypt(st='78l%K2', k=3) == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
|
||||
def test_railencrypt_2(self):
|
||||
assert railencrypt(st='f', k=25) == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
|
||||
def test_railencrypt_3(self):
|
||||
assert railencrypt(st='(la_UD^', k=2) == '\x00\x00\x00\x00\x00\x00\x00'
|
||||
|
||||
|
||||
class Test_raildecrypt(TestCase):
|
||||
def test_raildecrypt_1(self):
|
||||
assert raildecrypt(st='(la_UD^', k=2) == ''
|
||||
|
||||
def test_raildecrypt_2(self):
|
||||
assert raildecrypt(st="ZZ.B/8M'M", k=3) == ''
|
||||
|
||||
def test_raildecrypt_3(self):
|
||||
assert raildecrypt(st='9[HS4', k=960) == ''
|
|
@ -1,23 +0,0 @@
|
|||
from unittest import TestCase
|
||||
|
||||
from benchmark.zellers_birthday import zeller
|
||||
|
||||
|
||||
class Test_zeller(TestCase):
|
||||
def test_zeller_1(self):
|
||||
assert zeller(d=-134, m=797, y=67) == 'Sunday'
|
||||
|
||||
def test_zeller_2(self):
|
||||
assert zeller(d=-156, m=10, y=-57) == 'Wednesday'
|
||||
|
||||
def test_zeller_3(self):
|
||||
assert zeller(d=715, m=444, y=74) == 'Thursday'
|
||||
|
||||
def test_zeller_4(self):
|
||||
assert zeller(d=-726, m=864, y=-16) == 'Thursday'
|
||||
|
||||
def test_zeller_5(self):
|
||||
assert zeller(d=31, m=910, y=2) == 'Sunday'
|
||||
|
||||
def test_zeller_6(self):
|
||||
assert zeller(d=369, m=-917, y=-1000) == 'Sunday'
|
16
tests/test_zellers_birthday.py
Normal file
16
tests/test_zellers_birthday.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
from unittest import TestCase
|
||||
from benchmark.zellers_birthday import zeller
|
||||
|
||||
|
||||
class Test_zeller(TestCase):
|
||||
def test_zeller_1(self):
|
||||
assert zeller(d=-466, m=3, y=76) == 'Tuesday'
|
||||
|
||||
def test_zeller_2(self):
|
||||
assert zeller(d=626, m=-928, y=27) == 'Saturday'
|
||||
|
||||
def test_zeller_3(self):
|
||||
assert zeller(d=19, m=108, y=68) == 'Friday'
|
||||
|
||||
def test_zeller_4(self):
|
||||
assert zeller(d=284, m=255, y=-6) == 'Thursday'
|
Reference in a new issue