test nix funcs with pytest via nix_ffi.py

This commit is contained in:
DavHau 2021-11-20 11:10:42 +07:00
parent d22d99ff96
commit 9d2d385273
10 changed files with 182 additions and 133 deletions

View File

@ -5,7 +5,7 @@ on:
jobs:
pure-tests:
tests-pure:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.4.0
@ -22,7 +22,8 @@ jobs:
- run: nix flake check
impure-tests:
tests-impure:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.4.0
@ -38,3 +39,17 @@ jobs:
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
- run: nix run .#tests-impure
tests-unit-nix:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.4.0
- uses: cachix/install-nix-action@v15
with:
install_url: https://nixos-nix-install-tests.cachix.org/serve/w659aglf1hfvkj5wj696q9x8r19p6b7k/install
install_options: '--tarball-url-prefix https://nixos-nix-install-tests.cachix.org/serve'
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- run: nix run .#tests-unit

View File

@ -132,12 +132,14 @@
# all apps including cli, install, etc.
apps = forAllSystems (system: pkgs:
dream2nixFor."${system}".apps.flakeApps // {
tests-impure = {
type = "app";
program =
b.toString
(dream2nixFor."${system}".callPackageDream ./tests/impure {});
};
tests-impure.type = "app";
tests-impure.program = b.toString
(dream2nixFor."${system}".callPackageDream ./tests/impure {});
tests-unit.type = "app";
tests-unit.program = b.toString
(dream2nixFor."${system}".callPackageDream ./tests/unit {
inherit self;
});
}
);

View File

@ -8,8 +8,8 @@ import tempfile
import networkx as nx
from cleo import Command, argument, option
from utils import config, dream2nix_src, checkLockJSON, callNixFunction, buildNixFunction, buildNixAttribute, \
list_translators_for_source, strip_hashes_from_lock
from utils import config, dream2nix_src, checkLockJSON, list_translators_for_source, strip_hashes_from_lock
from nix_ffi import callNixFunction, buildNixFunction, buildNixAttribute
class AddCommand(Command):

View File

@ -6,7 +6,8 @@ import tempfile
from cleo import Command, argument, option
from utils import config, buildNixFunction, callNixFunction
from utils import config
from nix_ffi import callNixFunction, buildNixFunction
class UpdateCommand(Command):

89
src/apps/cli/nix_ffi.py Normal file
View File

@ -0,0 +1,89 @@
import json
import os
import subprocess as sp
import sys
import tempfile
dream2nix_src = os.environ.get("dream2nixSrc")
def callNixFunction(function_path, **kwargs):
with tempfile.NamedTemporaryFile("w") as input_json_file:
json.dump(dict(**kwargs), input_json_file, indent=2)
input_json_file.seek(0) # flushes write cache
env = os.environ.copy()
env.update(dict(
FUNC_ARGS=input_json_file.name
))
proc = sp.run(
[
"nix", "eval", "--show-trace", "--impure", "--raw", "--expr",
f'''
let
d2n = (import {dream2nix_src} {{}});
in
builtins.toJSON (
(d2n.utils.callViaEnv d2n.{function_path})
)
''',
],
capture_output=True,
env=env
)
if proc.returncode:
print(f"Failed calling nix function '{function_path}'", file=sys.stderr)
print(proc.stderr.decode(), file=sys.stderr)
exit(1)
# parse result data
return json.loads(proc.stdout)
def buildNixFunction(function_path, **kwargs):
with tempfile.NamedTemporaryFile("w") as input_json_file:
json.dump(dict(**kwargs), input_json_file, indent=2)
input_json_file.seek(0) # flushes write cache
env = os.environ.copy()
env.update(dict(
FUNC_ARGS=input_json_file.name
))
proc = sp.run(
[
"nix", "build", "--show-trace", "--impure", "-o", "tmp-result", "--expr",
f'''
let
d2n = (import {dream2nix_src} {{}});
in
(d2n.utils.callVieEnv d2n.{function_path})
''',
],
capture_output=True,
env=env
)
if proc.returncode:
print(f"Failed calling nix function '{function_path}'", file=sys.stderr)
print(proc.stderr.decode(), file=sys.stderr)
exit(1)
# return store path of result
result = os.path.realpath("tmp-result")
os.remove("tmp-result")
return result
def buildNixAttribute(attribute_path):
proc = sp.run(
[
"nix", "build", "--show-trace", "--impure", "-o", "tmp-result", "--expr",
f"(import {dream2nix_src} {{}}).{attribute_path}",
],
capture_output=True,
)
if proc.returncode:
print(f"Failed to build '{attribute_path}'", file=sys.stderr)
print(proc.stderr.decode(), file=sys.stderr)
exit(1)
result = os.path.realpath("tmp-result")
os.remove("tmp-result")
return result

View File

@ -2,10 +2,11 @@ import json
import os
import subprocess as sp
import sys
import tempfile
from jsonschema import validate
from nix_ffi import callNixFunction
dream2nix_src = os.environ.get("dream2nixSrc")
def find_repo_root():
@ -45,88 +46,6 @@ def checkLockJSON(lock):
raise
def callNixFunction(function_path, **kwargs):
with tempfile.NamedTemporaryFile("w") as input_json_file:
json.dump(dict(**kwargs), input_json_file, indent=2)
input_json_file.seek(0) # flushes write cache
env = os.environ.copy()
env.update(dict(
FUNC_ARGS=input_json_file.name
))
proc = sp.run(
[
"nix", "eval", "--show-trace", "--impure", "--raw", "--expr",
f'''
let
d2n = (import {dream2nix_src} {{}});
in
builtins.toJSON (
(d2n.utils.makeCallableViaEnv d2n.{function_path}) {{}}
)
''',
],
capture_output=True,
env=env
)
if proc.returncode:
print(f"Failed calling nix function '{function_path}'", file=sys.stderr)
print(proc.stderr.decode(), file=sys.stderr)
exit(1)
# parse result data
return json.loads(proc.stdout)
def buildNixFunction(function_path, **kwargs):
with tempfile.NamedTemporaryFile("w") as input_json_file:
json.dump(dict(**kwargs), input_json_file, indent=2)
input_json_file.seek(0) # flushes write cache
env = os.environ.copy()
env.update(dict(
FUNC_ARGS=input_json_file.name
))
proc = sp.run(
[
"nix", "build", "--show-trace", "--impure", "-o", "tmp-result", "--expr",
f'''
let
d2n = (import {dream2nix_src} {{}});
in
(d2n.utils.makeCallableViaEnv d2n.{function_path}) {{}}
''',
],
capture_output=True,
env=env
)
if proc.returncode:
print(f"Failed calling nix function '{function_path}'", file=sys.stderr)
print(proc.stderr.decode(), file=sys.stderr)
exit(1)
# return store path of result
result = os.path.realpath("tmp-result")
os.remove("tmp-result")
return result
def buildNixAttribute(attribute_path):
proc = sp.run(
[
"nix", "build", "--show-trace", "--impure", "-o", "tmp-result", "--expr",
f"(import {dream2nix_src} {{}}).{attribute_path}",
],
capture_output=True,
)
if proc.returncode:
print(f"Failed to build '{attribute_path}'", file=sys.stderr)
print(proc.stderr.decode(), file=sys.stderr)
exit(1)
result = os.path.realpath("tmp-result")
os.remove("tmp-result")
return result
def list_translators_for_source(sourcePath):
translatorsList = callNixFunction(
"translators.translatorsForInput",

View File

@ -75,13 +75,33 @@ rec {
(pattern: lib.any (file: b.match pattern file != null) (listFiles dir))
patterns;
# allow a function to receive its input from an environment variable
# whenever an empty set is passed
makeCallableViaEnv = func: args:
if args == {} then
func (builtins.fromJSON (builtins.readFile (builtins.getEnv "FUNC_ARGS")))
else
func args;
# Calls any function with an attrset arugment, even if that function
# doesn't accept an attrset argument, in which case the arguments are
# recursively applied as parameters.
# For this to work, the function parameters defined by the called function
# must always be ordered alphabetically.
callWithAttrArgs = func: args:
let
applyParamsRec = func: params:
if b.length params == 1 then
func (b.head params)
else
applyParamsRec
(func (b.head params))
(b.tail params);
in
if lib.functionArgs func == {} then
applyParamsRec func (b.attrValues args)
else
func args;
# call a function using arguments defined by the env var FUNC_ARGS
callViaEnv = func:
let
funcArgs = b.fromJSON (b.readFile (b.getEnv "FUNC_ARGS"));
in
callWithAttrArgs func funcArgs;
# hash the contents of a path via `nix hash path`
hashPath = algo: path:
@ -147,42 +167,12 @@ rec {
{ inherit name version; };
# determines if version v1 is greater than version v2
versionGreater = v1: v2:
versionGreaterList
(lib.splitString "." v1)
(lib.splitString "." v2);
# internal helper for 'versionGreater'
versionGreaterList = v1: v2:
let
head1 = b.head v1;
head2 = b.head v2;
n1 =
if builtins.match ''[[:digit:]]*'' head1 != null then
lib.toInt head1
else
0;
n2 = if builtins.match ''[[:digit:]]*'' head2 != null then
lib.toInt head2
else
0;
in
if n1 > n2 then
true
else
# end recursion condition
if b.length v1 == 1 || b.length v1 == 1 then
false
else
# continue recursion
versionGreaterList (b.tail v1) (b.tail v2);
versionGreater = v1: v2: b.compareVersions v1 v2 == 1;
# picks the latest version from a list of version strings
latestVersion = versions:
b.head
(lib.sort
(v1: v2: versionGreater v1 v2)
versions);
(lib.sort versionGreater versions);
satisfiesSemver = poetry2nixSemver.satisfiesSemver;

23
tests/unit/default.nix Normal file
View File

@ -0,0 +1,23 @@
{
self,
lib,
nix,
python3,
utils,
dream2nixWithExternals,
...
}:
let
l = lib // builtins;
in
utils.writePureShellScript
[
nix
]
''
export dream2nixSrc=${dream2nixWithExternals}
${python3.pkgs.pytest}/bin/pytest ${self}/tests/unit
''

1
tests/unit/nix_ffi.py Symbolic link
View File

@ -0,0 +1 @@
../../src/apps/cli/nix_ffi.py

9
tests/unit/test_nix.py Normal file
View File

@ -0,0 +1,9 @@
import pytest
import nix_ffi
@pytest.mark.parametrize("expected, versions", [
('3', [ '2', '3', '1' ]),
])
def test_latestVersion(expected, versions):
result = nix_ffi.callNixFunction('utils.latestVersion', versions=versions)
assert result == expected