Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .github/actions/behavior_test_binding_java/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

name: Test Core
description: 'Test Core with given setup and service'
inputs:
setup:
description: "The setup action for test"
service:
description: "The service to test"
feature:
description: "The feature to test"

runs:
using: "composite"
steps:
- name: Setup
shell: bash
run: |
mkdir -p ./dynamic_test_binding_java &&
cat <<EOF >./dynamic_test_binding_java/action.yml
runs:
using: composite
steps:
- name: Setup Test
uses: ./.github/services/${{ inputs.service }}/${{ inputs.setup }}
- name: Run Test Binding Java
shell: bash
working-directory: bindings/java
run: ./mvnw test -Dtest="behavior.*Test" -Dcargo-build.features=${{ inputs.feature }}
env:
OPENDAL_TEST: ${{ inputs.service }}
EOF
- name: Run
uses: ./dynamic_test_binding_java
1 change: 0 additions & 1 deletion .github/actions/behavior_test_core/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,4 @@ runs:
OPENDAL_TEST: ${{ inputs.service }}
EOF
- name: Run
id: run
uses: ./dynamic_test_core
186 changes: 143 additions & 43 deletions .github/scripts/behavior_test/plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
import sys
import json
import os
import re
from pathlib import Path
from dataclasses import dataclass, field

# The path for current script.
SCRIPT_PATH = Path(__file__).parent.absolute()
Expand All @@ -29,13 +31,14 @@
PROJECT_DIR = GITHUB_DIR.parent


def get_provided_cases():
def provided_cases():
root_dir = f"{GITHUB_DIR}/services"

cases = [
{
"service": service,
"setup": setup,
"feature": "services-{}".format(service.replace("_", "-")),
"content": Path(
os.path.join(root_dir, service, setup, "action.yml")
).read_text(),
Expand All @@ -50,64 +53,158 @@ def get_provided_cases():
if not os.getenv("GITHUB_HAS_SECRETS") == "true":
cases[:] = [v for v in cases if "secrets" not in v["content"]]

return cases
# Remove content from cases.
cases = [
{
"setup": v["setup"],
"service": v["service"],
"feature": v["feature"],
}
for v in cases
]

# Make sure the order is stable.
sorted_cases = sorted(cases, key=lambda x: (x["service"], x["setup"]))
return sorted_cases


@dataclass
class Hint:
# Is core affected?
core: bool = field(default=False, init=False)
# Is binding java affected?
binding_java: bool = field(default=False, init=False)

# Should we run all services test?
all_service: bool = field(default=False, init=False)
# affected services set.
services: set = field(default_factory=set, init=False)


def calculate_hint(changed_files):
hint = Hint()

# Remove all files that ends with `.md`
changed_files = [f for f in changed_files if not f.endswith(".md")]

service_pattern = r"core/src/services/([^/]+)/"
test_pattern = r".github/services/([^/]+)/"

for p in changed_files:
# workflow behavior tests affected
if p == ".github/workflows/behavior_test.yml":
hint.core = True
hint.binding_java = True
hint.all_service = True
if p == ".github/workflows/behavior_test_core.yml":
hint.core = True
hint.all_service = True
if p == ".github/workflows/behavior_test_binding_java.yml":
hint.binding_java = True
hint.all_service = True

# core affected
if p.startswith("core/") and not p.startswith("core/src/services/"):
hint.core = True
hint.binding_java = True
hint.all_service = True

# binding java affected.
if p.startswith("bindings/java/"):
hint.binding_java = True
hint.all_service = True

# core service affected
match = re.search(service_pattern, p)
if match:
hint.core = True
hint.binding_java = True
hint.services.add(match.group(1))

# core test affected
match = re.search(test_pattern, p)
if match:
hint.core = True
hint.binding_java = True
hint.services.add(match.group(1))

return hint


# unique_cases is used to only one setup for each service.
#
# We need this because we have multiple setup for each service and they have already been
# tested by `core` workflow. So we can only test unique setup for each service for bindings.
#
# We make sure that we return the first setup for each service in alphabet order.
def unique_cases(cases):
ucases = {}
for case in cases:
service = case["service"]
if service not in ucases:
ucases[service] = case

# Convert the dictionary back to a list if needed
return list(ucases.values())

def calculate_core_cases(cases, changed_files):
# If any of the core workflow changed, we will run all cases.
for p in [
".github/workflows/core_test.yml",
".github/workflows/test_planner.yml",
".github/actions/test-core",
]:
if p in changed_files:
return cases

def generate_core_cases(cases, hint):
# Always run all tests if it is a push event.
if os.getenv("GITHUB_IS_PUSH") == "true":
return cases

# If any of the core files changed, we will run all cases.
if any(
p.startswith("core/")
and not p.startswith("core/src/services/")
and not p.endswith(".md")
for p in changed_files
):
# Return empty if core is False
if not hint.core:
return []

# Return all services if all_service is True
if hint.all_service:
return cases

# Filter all cases that not shown un in changed files
cases = [v for v in cases if v["service"] in hint.services]
return cases


def generate_binding_java_cases(cases, hint):
cases = unique_cases(cases)

# Always run all tests if it is a push event.
if os.getenv("GITHUB_IS_PUSH") == "true":
return cases
if any(p.startswith("core/tests/") for p in changed_files):

# Return empty if core is False
if not hint.binding_java:
return []

# Return all services if all_service is True
if hint.all_service:
return cases

# Filter all cases that not shown un in changed files
cases = [v for v in cases if any(v["service"] in p for p in changed_files)]
cases = [v for v in cases if v["service"] in hint.services]
return cases


# Context is the github context: https://docs.github.com/en/actions/learn-github-actions/contexts#github-context
def plan(changed_files):
# TODO: add bindings/java, bindings/python in the future.
components = ["core"]
cases = get_provided_cases()
cases = provided_cases()
hint = calculate_hint(changed_files)

core_cases = calculate_core_cases(cases, changed_files)
core_cases = generate_core_cases(cases, hint)
binding_java_cases = generate_binding_java_cases(cases, hint)

jobs = {}
jobs = {
"components": {
"core": False,
"binding_java": False,
},
"core": [],
"binding_java": [],
}

if len(core_cases) > 0:
jobs["components"] = {"core": True}
jobs["core"] = [
{
"os": "ubuntu-latest",
"cases": [
{
"setup": v["setup"],
"service": v["service"],
"feature": "services-{}".format(v["service"].replace("_", "-")),
}
for v in core_cases
],
},
]
jobs["components"]["core"] = True
jobs["core"].append({"os": "ubuntu-latest", "cases": core_cases})

# fs is the only services need to run upon windows, let's hard code it here.
if "fs" in [v["service"] for v in core_cases]:
Expand All @@ -120,12 +217,15 @@ def plan(changed_files):
}
)

if len(binding_java_cases) > 0:
jobs["components"]["binding_java"] = True
jobs["binding_java"].append(
{"os": "ubuntu-latest", "cases": binding_java_cases}
)

return jobs


# For quick test:
#
# ./scripts/workflow_planner.py PATH
if __name__ == "__main__":
changed_files = sys.argv[1:]
result = plan(changed_files)
Expand Down
12 changes: 11 additions & 1 deletion .github/scripts/behavior_test/test_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@
class BehaviorTestPlan(unittest.TestCase):
def test_empty(self):
result = plan([])
self.assertEqual(result, {})
self.assertEqual(result["components"]["core"], False)
self.assertEqual(result["components"]["binding_java"], False)
self.assertEqual(len(result["core"]), 0)
self.assertEqual(len(result["binding_java"]), 0)

def test_core_cargo_toml(self):
result = plan(["core/Cargo.toml"])
Expand All @@ -39,6 +42,13 @@ def test_core_services_fs(self):
# Should not contain s3
self.assertFalse("s3" in cases)

def test_binding_java(self):
result = plan(["bindings/java/pom.xml"])
self.assertFalse(result["components"]["core"])
self.assertTrue(len(result["core"]) == 0)
self.assertTrue(result["components"]["binding_java"])
self.assertTrue(len(result["binding_java"]) > 0)


if __name__ == "__main__":
unittest.main()
35 changes: 35 additions & 0 deletions .github/services/redis/redis/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

name: redis
description: 'Behavior test for redis'

runs:
using: "composite"
steps:
- name: Setup Redis Server
shell: bash
working-directory: fixtures/redis
run: docker-compose -f docker-compose-redis.yml up -d
- name: Setup
shell: bash
run: |
cat << EOF >> $GITHUB_ENV
OPENDAL_REDIS_ENDPOINT=tcp://127.0.0.1:6379
OPENDAL_REDIS_ROOT=/
OPENDAL_REDIS_DB=0
EOF
35 changes: 35 additions & 0 deletions .github/services/redis/redis_with_cluster/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

name: redis_with_cluster
description: 'Behavior test for redis with cluster'

runs:
using: "composite"
steps:
- name: Setup Redis Cluster
shell: bash
working-directory: fixtures/redis
run: docker-compose -f docker-compose-redis-cluster.yml up -d
- name: Setup
shell: bash
run: |
cat << EOF >> $GITHUB_ENV
OPENDAL_REDIS_CLUSTER_ENDPOINTS=redis://127.0.0.1:6380/,redis://127.0.0.1:6381/,redis://127.0.0.1:6382/,redis://127.0.0.1:6383/,redis://127.0.0.1:6384/,redis://127.0.0.1:6385/
OPENDAL_REDIS_ROOT=/test/opendal
OPENDAL_REDIS_DB=0
EOF
Loading