From 811399da4efb40bb70137f27f4b5d14ba757ade3 Mon Sep 17 00:00:00 2001 From: Nicolas Gelot Date: Thu, 24 Nov 2022 15:51:54 +0000 Subject: [PATCH 1/3] Bootstrap functional tests --- .flake8 | 3 ++ .gitlab-ci.yml | 59 +++++++++++++++++++++++--- tests/functional/conftest.py | 28 +++++++++++++ tests/functional/requirements.txt | 2 + tests/functional/test_api.py | 69 +++++++++++++++++++++++++++++++ 5 files changed, 155 insertions(+), 6 deletions(-) create mode 100644 .flake8 create mode 100644 tests/functional/conftest.py create mode 100644 tests/functional/requirements.txt create mode 100644 tests/functional/test_api.py diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..a529cce --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length = 88 +ignore = E203, E731 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 082ccae..9771ddf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,11 +8,10 @@ variables: PUBLISH_URL: ota.ecloud.global DOCKER_TLS_CERTDIR: "/certs" -services: -- docker:19.03.1-dind - build: stage: build + services: + - docker:19.03.1-dind script: - docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME || true - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY @@ -21,6 +20,52 @@ build: - if [ "${CI_COMMIT_REF_NAME}" = master ] ; then docker push $CI_REGISTRY_IMAGE:latest ; fi tags: - generic_privileged + rules: + - if: '$CI_PIPELINE_SOURCE != "schedule"' + +check:rules:tests: + stage: build + image: python:3.11-slim + before_script: + - pip install black==22.10.0 flake8==5.0.4 isort==5.10.1 + script: + - cd tests/functional + - black --check . + - flake8 . + - isort --check . + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + changes: + - tests/functional/* + +.tests:functional: + stage: test + image: python:3.11-slim + variables: + PYTEST_ADDOPTS: "--junitxml=report.xml" + before_script: + - cd tests/functional/ + - pip install -r requirements.txt + rules: + - if: '$CI_PIPELINE_SOURCE == "schedule"' + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + changes: + - tests/functional/* + artifacts: + when: always + reports: + junit: tests/functional/report.xml + + +tests:functional:staging: + extends: .tests:functional + script: + - pytest --ota-domain "test.${PUBLISH_URL}" + +tests:functional:production: + extends: .tests:functional + script: + - pytest --ota-domain "${PUBLISH_URL}" # Deploy stage .deploy:image: @@ -44,15 +89,17 @@ build: deploy:staging: extends: .deploy:image - when: manual + rules: + - if: '$CI_COMMIT_REF_NAME != "production" && $CI_PIPELINE_SOURCE != "schedule"' + when: manual environment: name: staging url: https://ota.eeo.one deploy:production: extends: .deploy:image - only: - - production + rules: + - if: '$CI_COMMIT_REF_NAME == "production" && $CI_PIPELINE_SOURCE != "schedule"' environment: name: production url: https://ota.ecloud.global diff --git a/tests/functional/conftest.py b/tests/functional/conftest.py new file mode 100644 index 0000000..15b0154 --- /dev/null +++ b/tests/functional/conftest.py @@ -0,0 +1,28 @@ +import pytest + + +class Context: + def __init__(self, config): + self.domain = config.getoption("--ota-domain") + assert self.domain is not None, "OTA server domain has to be defined" + self.device = config.getoption("--ota-device") + self.channel = config.getoption("--ota-channel") + self.url = f"https://{self.domain}" + + +def pytest_addoption(parser): + parser.addoption( + "--ota-domain", action="store", default=None, help="OTA server domain" + ) + parser.addoption( + "--ota-device", action="store", default="FP3", help="OTA device name" + ) + parser.addoption( + "--ota-channel", action="store", default="stable", help="OTA channel name" + ) + + +@pytest.fixture +def ctx(request): + context = Context(request.config) + yield context diff --git a/tests/functional/requirements.txt b/tests/functional/requirements.txt new file mode 100644 index 0000000..65a6411 --- /dev/null +++ b/tests/functional/requirements.txt @@ -0,0 +1,2 @@ +pytest==7.2.0 +httpx==0.23.1 diff --git a/tests/functional/test_api.py b/tests/functional/test_api.py new file mode 100644 index 0000000..8b266b8 --- /dev/null +++ b/tests/functional/test_api.py @@ -0,0 +1,69 @@ +import httpx +import pytest + + +@pytest.fixture +def ctx(ctx): + ctx.cli = httpx.Client(base_url=f"{ctx.url}/api/v1", timeout=60) + yield ctx + + +def test_list_device_builds(ctx): + res = ctx.cli.get(f"/{ctx.device}/{ctx.channel}") + assert res.status_code == httpx.codes.OK + data = res.json() + assert not data["error"] + assert len(data["response"]) + build = data["response"][-1] + assert build["channel"] == ctx.channel + assert build["romtype"] == ctx.channel + assert build["url"].startswith(ctx.url) + assert build["filename"].startswith("e-") + + +def test_list_build_with_incremental_update_field(ctx): + # list and sort all builds by datetime + res = ctx.cli.get(f"/{ctx.device}/{ctx.channel}") + assert res.status_code == httpx.codes.OK + data = sorted(res.json()["response"], key=lambda d: d["datetime"]) + assert len(data) >= 2 + + # list build with incremental field + build = data[-2] + res = ctx.cli.get(f"/{ctx.device}/{ctx.channel}/{build['incremental']}") + assert res.status_code == httpx.codes.OK + data = res.json() + assert len(data["response"]) == 1 + + +def test_FP4_upgrade_from_given_build(ctx): + """FP4 A12 upgrade is allowed only from 1.5-r build""" + # create a map about /e/OS builds + res = ctx.cli.get("/FP4/stable") + assert res.status_code == httpx.codes.OK + assert len(res.json()["response"]) + builds = { + f"{d['version']}-{d['pre_version']}-{d['android_version']}": d + for d in res.json()["response"] + } + + # ensure that A12 builds are not listed with version <= 1.4-r + build14r = builds["1.4--11"] + res = ctx.cli.get(f"/FP4/stable/{build14r['incremental']}") + assert res.status_code == httpx.codes.OK + data = res.json()["response"] + a12builds = [d for d in data if d["android_version"] == "12"] + assert len(a12builds) == 0 + + # ensure that A12 builds are listed with version == 1.5-r + build15r = builds["1.5--11"] + res = ctx.cli.get( + f"/FP4/stable/{build15r['incremental']}", + headers={"User-Agent": "eOS v1.5"}, + ) + assert res.status_code == httpx.codes.OK + data = res.json()["response"] + a12builds = [d for d in data if d["android_version"] == "12"] + a11builds = [d for d in data if d["android_version"] == "11"] + assert len(a11builds) == 0 + assert len(a12builds) >= 1 -- GitLab From ba160ca411b359b853ba349070419c52c02abd4e Mon Sep 17 00:00:00 2001 From: Nicolas Gelot Date: Thu, 24 Nov 2022 17:05:39 +0100 Subject: [PATCH 2/3] Fix junit report artifact --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9771ddf..3051ed6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -53,6 +53,8 @@ check:rules:tests: - tests/functional/* artifacts: when: always + paths: + - tests/functional/report.xml reports: junit: tests/functional/report.xml -- GitLab From efd1e9a108731dc5c2cc72518a88fd0156bcf99c Mon Sep 17 00:00:00 2001 From: Alexandre Roux Date: Wed, 25 Jan 2023 19:19:51 +0000 Subject: [PATCH 3/3] Get current version from build.prop when not available in header --- src/Helpers/Builds.php | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/Helpers/Builds.php b/src/Helpers/Builds.php index b7e84b4..75cc37e 100644 --- a/src/Helpers/Builds.php +++ b/src/Helpers/Builds.php @@ -174,7 +174,7 @@ // Get physical paths of where the files resides $path = Flight::cfg()->get('realBasePath') . '/builds/full'; - + $dir = $path . '/' . $channels . '/' . $device; if(!is_dir($dir)){ return; @@ -182,7 +182,7 @@ // Get the file list and parse it $files = scandir($dir); if (count($files) > 2) { - + $all_builds = array(); foreach ($files as $file) { // Skip all files except for the ones that match the pattern // filename starting with e- and ending with .zip are considered as valid files @@ -204,17 +204,27 @@ } else { $build = new Build($file, $dir, $this->logger, $shouldDisplayPatch); } - - if ($build->includeInResults($this->postData['params'], $this->currenteOSVersion)) { - array_push($this->builds, $build); - } - + array_push($all_builds, $build); + $sourceIncremental = isset($this->postData['params']['source_incremental']) ? $this->postData['params']['source_incremental'] : NULL; if ($build->isValid($this->postData['params']) && $sourceIncremental && strcmp($sourceIncremental, $build->getIncremental()) === 0) { $this->currentBuild = $build; $this->logger->info($build->getIncremental().' is the current build'); + if($this->currenteOSVersion === -1){ + //get current version from build.prop + $this->currenteOSVersion = $this->currentBuild->getVersion(); + } } } + + $this->logger->debug('Current version: '.$this->currenteOSVersion); + + foreach ($all_builds as $build){ + if ($build->includeInResults($this->postData['params'], $this->currenteOSVersion)) { + array_push($this->builds, $build); + } + } + } $this->logger->debug('Total execution time of getBuilds in seconds'); } -- GitLab