diff --git a/kotlin/Dockerfile b/kotlin/Dockerfile index 306fb2ceb7c..3ee977be2bc 100644 --- a/kotlin/Dockerfile +++ b/kotlin/Dockerfile @@ -1,18 +1,31 @@ -# syntax=docker/dockerfile:1 -FROM gradle:latest +# Use Gradle base image with JDK 21 +FROM gradle:8.5.0-jdk21 -# Update image -RUN apt-get update && \ - apt-get upgrade -y && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* +# Install system dependencies +RUN apt-get update -qq && \ + DEBIAN_FRONTEND=noninteractive apt-get install -qq -y \ + python3 \ + python3-pip \ + git \ + curl \ + vim \ + less \ + ca-certificates && \ + apt-get clean && rm -rf /var/lib/apt/lists/* -# Copy source code -COPY . /kotlin +# Install Python dependencies +RUN pip3 install --quiet --no-cache-dir boto3 + +# Set working directory +WORKDIR /app + +# Copy only the Python script (it clones code later) +COPY run_tests.py . # Set non-root user RUN useradd -m automation && \ - chown -R automation:automation /kotlin + chown -R automation:automation /app USER automation:automation -CMD ["/kotlin/run_tests.sh"] +# Default entrypoint (optional: run script automatically) +CMD ["python3", "run_tests.py"] diff --git a/kotlin/run_tests.py b/kotlin/run_tests.py new file mode 100644 index 00000000000..89b38adb83f --- /dev/null +++ b/kotlin/run_tests.py @@ -0,0 +1,274 @@ +import os +import subprocess +import json +import datetime +import boto3 +import time +import re +from botocore.exceptions import ClientError + +# DynamoDB setup +# dynamodb = boto3.resource('dynamodb', region_name='us-east-1') +SUMMARY_TABLE = 'TestRunSummaries' # Table for test run summaries + +def run_command(command, cwd=None): + """Run a shell command and return the returncode and output.""" + try: + result = subprocess.run(command, cwd=cwd, check=True, text=True, capture_output=True) + return result.returncode, result.stdout + result.stderr + except subprocess.CalledProcessError as e: + #print(f"Error running command: {e.cmd}\nReturn code: {e.returncode}\n{e.stdout or e.stderr}") + return e.returncode, (e.stdout or '') + (e.stderr or '') + +def parse_gradle_test_results(output): + """Parse Gradle test results and return counts and failure summaries.""" + passed = failed = skipped = 0 + failure_summary_lines = [] + + lines = output.splitlines() + in_failure_block = False + current_failure = "" + + for line in lines: + stripped = line.strip() + + # Count test result types + if re.match(r'.+ > .+ PASSED', stripped): + passed += 1 + elif re.match(r'.+ > .+ FAILED', stripped): + failed += 1 + in_failure_block = True + failure_summary_lines.append(f"Failed: {stripped}") + elif re.match(r'.+ > .+ SKIPPED', stripped): + skipped += 1 + + # Capture stack trace or explanation for the failure + if in_failure_block: + if stripped == '' or stripped.startswith('BUILD') or re.match(r'^\d+\) ', stripped): + in_failure_block = False + else: + failure_summary_lines.append(line) + + # Capture the test class and method that failed + match = re.match(r'(.+) \((.+)\)', stripped) # Match "TestClass (testMethod)" + if match: + failure_details = f"Test Failed: {match.group(1)} - Method: {match.group(2)}" + if failure_details not in failure_summary_lines: + failure_summary_lines.append(failure_details) + + failure_summary = "\n".join(failure_summary_lines) if failure_summary_lines else "No failure details found." + return passed, failed, skipped, failure_summary + +def log_failure_to_dynamodb(service_name, service_path, failure_summary, run_id): + """Log failed test results to DynamoDB.""" +# client = boto3.client('dynamodb', region_name='us-east-1') + table_name = 'TestFailures' + try: + pass +# client.put_item( +# TableName=table_name, +# Item={ +# 'ServiceName': {'S': service_name}, +# 'ServicePath': {'S': service_path}, +# 'ErrorSummary': {'S': failure_summary}, +# 'RunId': {'S': run_id}, +# 'Timestamp': {'S': str(datetime.datetime.utcnow())} +# } +# ) + #print(f"šŸ›‘ Logged failure to DynamoDB: {service_name}") + except ClientError as e: + pass + #print(f"ā— Failed to log failure: {e}") + +def has_test_annotations(service_path): + """Check if the service contains any test files with @Test annotation.""" + test_dirs = [ + os.path.join(service_path, "src", "test", "kotlin"), + os.path.join(service_path, "src", "test", "java") + ] + for test_dir in test_dirs: + for root, _, files in os.walk(test_dir): + for file in files: + if file.endswith(".kt") or file.endswith(".java"): + full_path = os.path.join(root, file) + try: + with open(full_path, 'r', encoding='utf-8') as f: + content = f.read() + if "@Test" in content: + return True + except Exception as e: + pass + #print(f"āš ļø Could not read {full_path}: {e}") + return False + +def run_gradle_tests(service_path, run_id): + """Run Gradle tests for a given service if test files with @Test exist.""" + #print(f"\nšŸ”§ Testing: {service_path}") + + if not has_test_annotations(service_path): + #print(f"āš ļø No test files with @Test found in {service_path}. Skipping.") + return 0, 0, 0 + + gradlew = os.path.join(service_path, "gradlew") + if os.path.exists(gradlew): + gradle_command = "./gradlew" + os.chmod(gradlew, 0o775) + else: + build_file_path = os.path.join(service_path, "build.gradle.kts") + if not os.path.isfile(build_file_path): + #print(f"āŒ No Gradle build file found in {service_path}. Skipping test for this service.") + return 0, 0, 0 + gradle_command = "gradle" + + returncode, output = run_command([gradle_command, "test"], cwd=service_path) + passed, failed, skipped, failure_summary = parse_gradle_test_results(output) + status = "āœ… Passed" if failed == 0 else "āŒ Failed" + #print(f"šŸ“Š Result: {status} — Passed: {passed}, Failed: {failed}, Skipped: {skipped}") + + if failed > 0: + service_name = os.path.basename(service_path) + # log_failure_to_dynamodb(service_name, service_path, failure_summary, run_id) + + return passed, failed, skipped + +def get_service_folders(base_dir): + """Get a list of all service directories sorted alphabetically.""" + service_folders = [] + for root, dirs, files in os.walk(base_dir): + for dir_name in dirs: + gradle_file = os.path.join(root, dir_name, 'build.gradle.kts') + if os.path.isfile(gradle_file): + service_folders.append(os.path.join(root, dir_name)) + return sorted(service_folders) + +def run_tests_for_all_services(base_dir, run_id): + """Run tests for all services in alphabetical order.""" + service_folders = get_service_folders(base_dir) + passed_total = failed_total = skipped_total = 0 + services_tested = 0 + + current = 0 + limit = 1 + for service in service_folders: + if current >= limit: + break + current += 1 + passed, failed, skipped = run_gradle_tests(service, run_id) + passed_total += passed + failed_total += failed + skipped_total += skipped + services_tested += 1 + + return passed_total, failed_total, skipped_total, services_tested + +def log_test_run_summary(run_id, tested_services, total_passed, total_failed, total_skipped, elapsed_seconds): +# summary_table = dynamodb.Table(SUMMARY_TABLE) + timestamp = datetime.datetime.utcnow().isoformat() + + item = { + "RunId": run_id, + "Timestamp": timestamp, + "ServicesTested": tested_services, + "TotalPassed": total_passed, + "TotalFailed": total_failed, + "TotalSkipped": total_skipped, + "TotalTimeSeconds": int(elapsed_seconds), + "Language": "Kotlin" + } + + try: + pass +# summary_table.put_item(Item=item) + #print(f"šŸ—ƒļø Test run summary logged to DynamoDB with RunId: {run_id}") + except ClientError as e: + pass + #print(f"ā— Failed to log test summary: {e}") + +def write_language_test_stats(language: str, total_tests: int, passed_tests: int): + """ + Write test statistics to the LanguageTestStats DynamoDB table. + + Parameters: + language (str): Programming language name (e.g., "Kotlin"). + total_tests (int): Total number of tests run. + passed_tests (int): Number of tests that passed. + """ + pass_rate = (passed_tests / total_tests) * 100 if total_tests > 0 else 0 + +# dynamodb = boto3.client("dynamodb", "us-east-1") + + item = { + 'Language': {'S': language}, + 'TotalTests': {'N': str(total_tests)}, + 'PassedTests': {'N': str(passed_tests)}, + 'PassRate': {'N': str(round(pass_rate, 2))} + } + + try: + pass +# dynamodb.put_item( +# TableName="LanguageTestStats", +# Item=item +# ) + #print(f"āœ… Wrote stats for {language}: {passed_tests}/{total_tests} passed ({pass_rate:.2f}%)") + except ClientError as e: + pass + #print(f"āŒ Failed to write stats for {language} to DynamoDB: {e}") + +def main(): + repo_url = "https://github.com/awsdocs/aws-doc-sdk-examples.git" + clone_dir = "/app/aws-doc-sdk-examples" + ROOT_TEST_DIR = "kotlin/services" + start_time = time.time() + + if not os.path.exists(clone_dir): + #print(f"šŸ“„ Cloning repo: {repo_url}") + returncode, output = run_command(["git", "clone", repo_url, clone_dir]) + if returncode != 0: + #print("āŒ Failed to clone repository.") + return + + base_dir = os.path.join(clone_dir, ROOT_TEST_DIR) + run_id = str(int(time.time())) + passed_total, failed_total, skipped_total, services_tested = run_tests_for_all_services(base_dir, run_id) + + elapsed = time.time() - start_time +# #print("\n===== āœ… Final Test Summary =====") + #print(f"Services Tested: {services_tested}", file=sys.stderr) + #print(f"Total Tests Passed: {passed_total}") + #print(f"Total Tests Failed: {failed_total}") + #print(f"Total Tests Skipped: {skipped_total}") + #print(f"Total Time: {int(elapsed // 60)} min {int(elapsed % 60)} sec") + print( +{ + "schema-version": "0.0.1", + "results": { + "tool": "gradle", + "summary": { + "tests": services_tested, + "passed": passed_total, + "failed": failed_total, + "skipped": skipped_total, + "start_time": 0, + "stop_time": int(elapsed) + }, + "tests": [ + { + "name": "test 1", + "status": "failed", + "duration": int(elapsed), + "message": "apigateway", + "log": "probably something to do with credentials" + } + ] + } +}) + + # 🪪 Log to DynamoDB at the END of all testing +# log_test_run_summary(run_id, services_tested, passed_total, failed_total, skipped_total, elapsed) + + total = passed_total + failed_total + write_language_test_stats("Kotlin", total, passed_total) + +if __name__ == "__main__": + main() diff --git a/php/Dockerfile b/php/Dockerfile index ce6aeb30abf..33fc950f654 100644 --- a/php/Dockerfile +++ b/php/Dockerfile @@ -21,4 +21,5 @@ RUN addgroup -S automation && \ USER automation:automation -CMD ["/php/testing", "--integ"] +#CMD ["/php/testing", "--integ"] +CMD ["/php/testing", "--test-pass"] diff --git a/php/example_code/composer.json b/php/example_code/composer.json index 8d22c1e064d..e2dbca6c586 100644 --- a/php/example_code/composer.json +++ b/php/example_code/composer.json @@ -6,7 +6,8 @@ }, "require-dev": { "phpunit/phpunit": "^9.6.13", - "squizlabs/php_codesniffer": "3.*" + "squizlabs/php_codesniffer": "3.*", + "sempro/phpunit-pretty-print": ">=1.4.0" }, "autoload": { "psr-0": { diff --git a/php/example_code/composer.lock b/php/example_code/composer.lock index 8a8d6099e10..976c6b7fd20 100644 --- a/php/example_code/composer.lock +++ b/php/example_code/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "58a8f75f90fe5dc5f6ad5719e942f8b3", + "content-hash": "0ef4572e1e382545ebd66194b5e8e6ee", "packages": [ { "name": "aws/aws-crt-php", - "version": "v1.2.6", + "version": "v1.2.7", "source": { "type": "git", "url": "https://github.com/awslabs/aws-crt-php.git", - "reference": "a63485b65b6b3367039306496d49737cf1995408" + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/a63485b65b6b3367039306496d49737cf1995408", - "reference": "a63485b65b6b3367039306496d49737cf1995408", + "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/d71d9906c7bb63a28295447ba12e74723bd3730e", + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e", "shasum": "" }, "require": { @@ -56,22 +56,22 @@ ], "support": { "issues": "https://github.com/awslabs/aws-crt-php/issues", - "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.6" + "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.7" }, - "time": "2024-06-13T17:21:28+00:00" + "time": "2024-10-18T22:15:13+00:00" }, { "name": "aws/aws-sdk-php", - "version": "3.323.2", + "version": "3.343.9", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "030b93340f0e7d6fd20276037087d3eab8f16575" + "reference": "6ca5eb1c60b879cf516e5fadefec87afc6219e74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/030b93340f0e7d6fd20276037087d3eab8f16575", - "reference": "030b93340f0e7d6fd20276037087d3eab8f16575", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/6ca5eb1c60b879cf516e5fadefec87afc6219e74", + "reference": "6ca5eb1c60b879cf516e5fadefec87afc6219e74", "shasum": "" }, "require": { @@ -79,31 +79,30 @@ "ext-json": "*", "ext-pcre": "*", "ext-simplexml": "*", - "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", - "guzzlehttp/promises": "^1.4.0 || ^2.0", - "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", - "mtdowling/jmespath.php": "^2.6", - "php": ">=7.2.5", - "psr/http-message": "^1.0 || ^2.0" + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/promises": "^2.0", + "guzzlehttp/psr7": "^2.4.5", + "mtdowling/jmespath.php": "^2.8.0", + "php": ">=8.1", + "psr/http-message": "^2.0" }, "require-dev": { "andrewsville/php-token-reflection": "^1.4", "aws/aws-php-sns-message-validator": "~1.0", "behat/behat": "~3.0", - "composer/composer": "^1.10.22", + "composer/composer": "^2.7.8", "dms/phpunit-arraysubset-asserts": "^0.4.0", "doctrine/cache": "~1.4", "ext-dom": "*", "ext-openssl": "*", "ext-pcntl": "*", "ext-sockets": "*", - "nette/neon": "^2.3", - "paragonie/random_compat": ">= 2", "phpunit/phpunit": "^5.6.3 || ^8.5 || ^9.5", - "psr/cache": "^1.0", - "psr/simple-cache": "^1.0", - "sebastian/comparator": "^1.2.3 || ^4.0", - "yoast/phpunit-polyfills": "^1.0" + "psr/cache": "^2.0 || ^3.0", + "psr/simple-cache": "^2.0 || ^3.0", + "sebastian/comparator": "^1.2.3 || ^4.0 || ^5.0", + "symfony/filesystem": "^v6.4.0 || ^v7.1.0", + "yoast/phpunit-polyfills": "^2.0" }, "suggest": { "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", @@ -152,24 +151,24 @@ "sdk" ], "support": { - "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", + "forum": "https://github.com/aws/aws-sdk-php/discussions", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.323.2" + "source": "https://github.com/aws/aws-sdk-php/tree/3.343.9" }, - "time": "2024-10-07T18:11:24+00:00" + "time": "2025-05-12T18:11:31+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "7.9.2", + "version": "7.9.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "d281ed313b989f213357e3be1a179f02196ac99b" + "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", - "reference": "d281ed313b989f213357e3be1a179f02196ac99b", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", "shasum": "" }, "require": { @@ -266,7 +265,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.9.2" + "source": "https://github.com/guzzle/guzzle/tree/7.9.3" }, "funding": [ { @@ -282,20 +281,20 @@ "type": "tidelift" } ], - "time": "2024-07-24T11:22:20+00:00" + "time": "2025-03-27T13:37:11+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.3", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8" + "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", - "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", + "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c", + "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c", "shasum": "" }, "require": { @@ -349,7 +348,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.3" + "source": "https://github.com/guzzle/promises/tree/2.2.0" }, "funding": [ { @@ -365,20 +364,20 @@ "type": "tidelift" } ], - "time": "2024-07-18T10:29:17+00:00" + "time": "2025-03-27T13:27:01+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.7.0", + "version": "2.7.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", "shasum": "" }, "require": { @@ -465,7 +464,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.0" + "source": "https://github.com/guzzle/psr7/tree/2.7.1" }, "funding": [ { @@ -481,7 +480,7 @@ "type": "tidelift" } ], - "time": "2024-07-18T11:15:46+00:00" + "time": "2025-03-27T12:30:47+00:00" }, { "name": "mtdowling/jmespath.php", @@ -755,16 +754,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", "shasum": "" }, "require": { @@ -772,12 +771,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -802,7 +801,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" }, "funding": [ { @@ -818,23 +817,24 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.31.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { + "ext-iconv": "*", "php": ">=7.2" }, "provide": { @@ -846,8 +846,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -882,7 +882,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" }, "funding": [ { @@ -898,7 +898,7 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-12-23T08:48:59+00:00" } ], "packages-dev": [ @@ -974,16 +974,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.12.0", + "version": "1.13.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", "shasum": "" }, "require": { @@ -1022,7 +1022,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" }, "funding": [ { @@ -1030,20 +1030,20 @@ "type": "tidelift" } ], - "time": "2024-06-12T14:39:25+00:00" + "time": "2025-04-29T12:36:36+00:00" }, { "name": "nikic/php-parser", - "version": "v5.3.0", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "3abf7425cd284141dc5d8d14a9ee444de3345d1a" + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3abf7425cd284141dc5d8d14a9ee444de3345d1a", - "reference": "3abf7425cd284141dc5d8d14a9ee444de3345d1a", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", "shasum": "" }, "require": { @@ -1086,9 +1086,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" }, - "time": "2024-09-29T13:56:26+00:00" + "time": "2024-12-30T11:07:19+00:00" }, { "name": "phar-io/manifest", @@ -1529,16 +1529,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.21", + "version": "9.6.23", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa" + "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", - "reference": "de6abf3b6f8dd955fac3caad3af7a9504e8c2ffa", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", + "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", "shasum": "" }, "require": { @@ -1549,7 +1549,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.0", + "myclabs/deep-copy": "^1.13.1", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", @@ -1612,7 +1612,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.21" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.23" }, "funding": [ { @@ -1623,12 +1623,20 @@ "url": "https://github.com/sebastianbergmann", "type": "github" }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2024-09-19T10:50:18+00:00" + "time": "2025-05-02T06:40:34+00:00" }, { "name": "sebastian/cli-parser", @@ -2593,18 +2601,56 @@ ], "time": "2020-09-28T06:39:44+00:00" }, + { + "name": "sempro/phpunit-pretty-print", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/s360digital/phpunit-pretty-print.git", + "reference": "fa623aa8a17aece4a2b69e54b07a5e37572d1f1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/s360digital/phpunit-pretty-print/zipball/fa623aa8a17aece4a2b69e54b07a5e37572d1f1d", + "reference": "fa623aa8a17aece4a2b69e54b07a5e37572d1f1d", + "shasum": "" + }, + "require": { + "php": ">=7.1.0", + "phpunit/phpunit": "^7 || ^8 || ^9" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Sempro\\PHPUnitPrettyPrinter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Prettify PHPUnit output", + "support": { + "issues": "https://github.com/s360digital/phpunit-pretty-print/issues", + "source": "https://github.com/s360digital/phpunit-pretty-print/tree/1.4.0" + }, + "time": "2021-01-04T13:25:10+00:00" + }, { "name": "squizlabs/php_codesniffer", - "version": "3.10.3", + "version": "3.13.0", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "62d32998e820bddc40f99f8251958aed187a5c9c" + "reference": "65ff2489553b83b4597e89c3b8b721487011d186" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/62d32998e820bddc40f99f8251958aed187a5c9c", - "reference": "62d32998e820bddc40f99f8251958aed187a5c9c", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/65ff2489553b83b4597e89c3b8b721487011d186", + "reference": "65ff2489553b83b4597e89c3b8b721487011d186", "shasum": "" }, "require": { @@ -2669,9 +2715,13 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" } ], - "time": "2024-09-18T10:38:58+00:00" + "time": "2025-05-11T03:36:00+00:00" }, { "name": "theseer/tokenizer", diff --git a/php/example_code/kms/HelloKMS.php b/php/example_code/kms/HelloKMS.php index 56ec6d178ee..31aa9d9d0bb 100644 --- a/php/example_code/kms/HelloKMS.php +++ b/php/example_code/kms/HelloKMS.php @@ -9,7 +9,7 @@ echo "This file shows how to connect to the KmsClient, uses a paginator to get the keys for the account, and lists the KeyIds for up to 10 keys.\n"; -$client = new KmsClient([]); +$client = new KmsClient(["region" => "us-east-1"]); $pageLength = 10; // Change this value to change the number of records shown, or to break up the result into pages. diff --git a/php/example_code/kms/KmsBasics.php b/php/example_code/kms/KmsBasics.php index 9ff09a859d6..2edf626f631 100644 --- a/php/example_code/kms/KmsBasics.php +++ b/php/example_code/kms/KmsBasics.php @@ -40,7 +40,7 @@ public function runExample() echo "--------------------------------------\n"; $this->pressEnter(); - $this->kmsClient = new KmsClient([]); + $this->kmsClient = new KmsClient(["region" => "us-east-1"]); // Initialize the KmsService class with the client. This allows you to override any defaults in the client before giving it to the service class. $this->kmsService = new KmsService($this->kmsClient); @@ -184,7 +184,7 @@ public function runExample() echo "Every KMS key must have exactly one key policy. The statements in the key policy determine who has permission to use the KMS key and how they can use it.\n"; echo " You can also use IAM policies and grants to control access to the KMS key, but every KMS key must have a key policy.\n"; echo "We will replace the key's policy with a new one:\n"; - $stsClient = new StsClient([]); + $stsClient = new StsClient(["region" => "us-east-1"]); $result = $stsClient->getCallerIdentity(); $accountId = $result['Account']; $keyPolicy = <<< KEYPOLICY diff --git a/php/example_code/phpunit.xml b/php/example_code/phpunit.xml index 81db3b294a1..f340e938069 100644 --- a/php/example_code/phpunit.xml +++ b/php/example_code/phpunit.xml @@ -7,33 +7,42 @@ convertDeprecationsToExceptions="true" failOnRisky="true" failOnWarning="true" - verbose="true"> - - - - - - - - - - - - - - - - - - - - - - - - - - + verbose="true" + printerClass="Sempro\PHPUnitPrettyPrinter\PrettyPrinterForPhpUnit9"> + + + kms/tests/KmsBasicsTests.php + + + kms/tests/KmsBasicsTests.php + + lambda/tests/LambdaTest.php + + + kms/tests/KmsBasicsTests.php + + + s3/express/tests/S3ExpressBasicsTest.php + + + dynamodb/dynamodb_basics/tests/DynamoDBBasicsTest.php + + + dynamodb/partiql_basics/tests/PartiQLBasicsTest.php + + + iam/tests/ + + + glue/tests/GlueBasicsTest.php + + + lambda/tests/LambdaTest.php + + + auto-scaling/tests/AutoScalingBasicsTest.php + + diff --git a/php/example_code/s3/express/S3ExpressBasics.php b/php/example_code/s3/express/S3ExpressBasics.php index a757518df71..940f5aec312 100644 --- a/php/example_code/s3/express/S3ExpressBasics.php +++ b/php/example_code/s3/express/S3ExpressBasics.php @@ -109,7 +109,7 @@ public function runExample() echo "Now, we'll set up some policies, roles, and a user. This user will only have permissions to do S3 Express One Zone actions.\n"; pressEnter(); - $this->cloudFormationClient = new CloudFormationClient([]); + $this->cloudFormationClient = new CloudFormationClient(["region" => "us-east-1"]); $stackName = "cfn-stack-s3-express-basics-" . uniqid(); $file = file_get_contents(__DIR__ . "/../../../../resources/cfn/s3_express_basics/s3_express_template.yml"); $result = $this->cloudFormationClient->createStack([ @@ -150,13 +150,13 @@ public function runExample() echo "This client is created with the credentials associated with the user account with the S3 Express policy attached, so it can perform S3 Express operations.\n"; pressEnter(); $s3RegularClient = new S3Client([ - 'Region' => $region, - 'Credentials' => $regularCredentials, + 'region' => $region, + 'credentials' => $regularCredentials, ]); $s3RegularService = new S3Service($s3RegularClient); $s3ExpressClient = new S3Client([ - 'Region' => $region, - 'Credentials' => $expressCredentials, + 'region' => $region, + 'credentials' => $expressCredentials, ]); $s3ExpressService = new S3Service($s3ExpressClient); echo "All the roles and policies were created an attached to the user. Then, a new S3 Client and Service were created using that user's credentials.\n"; diff --git a/php/example_code/s3/express/tests/S3ExpressBasicsTest.php b/php/example_code/s3/express/tests/S3ExpressBasicsTest.php index 7b0e9d14456..b768c712b8f 100644 --- a/php/example_code/s3/express/tests/S3ExpressBasicsTest.php +++ b/php/example_code/s3/express/tests/S3ExpressBasicsTest.php @@ -41,12 +41,13 @@ public static function setUpBeforeClass(): void public function testItRunsWithoutThrowingAnException() { + $start = new S3ExpressBasics(); try { - $start = new S3ExpressBasics(); $start->runExample(); self::assertTrue(true); // This asserts that we made it to this line with no exceptions thrown. }catch(S3Exception $caught){ echo "There was a problem running the tests: {$caught->getAwsErrorMessage()}\n"; + self::fail(); }finally{ $start->cleanUp(); } diff --git a/php/parseOutput.php b/php/parseOutput.php new file mode 100644 index 00000000000..b373359ae37 --- /dev/null +++ b/php/parseOutput.php @@ -0,0 +1,72 @@ + "0.0.1", + "results" => [ + "tool" => "", + "summary" => [ + "tests" => "", + "passed" => "", + "failed" => "", + "start_time" => 0, + "stop_time" => "", + ], + "tests" => [ + [ + "name" => "test1", + "status" => "passed", + "duration" => 1, + "message" => "passed", + "log" => "none", + ], + ], + ], +]; + +$runtime_loc = strpos($file,"Runtime:"); +$configuration_loc = strpos($file, "Configuration:", $runtime_loc); +$tool = trim(substr($file, $runtime_loc + 8, $configuration_loc - $runtime_loc - 8)); + +$output['results']['tool'] = $tool; + +$time_loc = strpos($file, "Time:"); +$memory_loc = strpos($file, "Memory:", $time_loc); +$time = trim(substr($file, $time_loc + 5, $memory_loc - $time_loc - 7)); +$timeInSeconds = strtotime("1970-01-01T00:".$time, 0); // #TODO add support for hour+ long runs + +$output['results']['summary']['stop_time'] = $timeInSeconds; + +// Check for OK, which is all passed +$ok_loc = strpos($file, "OK ("); +if($ok_loc !== false) { + $tests_loc = strpos($file, "tests,", $ok_loc); + $total_tests = trim(substr($file, $ok_loc + 4, $tests_loc - $ok_loc - 4)); + + $output['results']['summary']['tests'] = (int)$total_tests; + $output['results']['summary']['passed'] = (int)$total_tests; + $output['results']['summary']['failed'] = 0; +}else{ + //look for failure state + $tests_loc = strpos($file, "Tests: "); + $end_tests_loc = strpos($file, ",", $tests_loc); + $total_tests = substr($file, $tests_loc + 6, $end_tests_loc - $tests_loc - 6); + + $assertions_loc = strpos($file, "Assertions: ", $tests_loc); + $end_assertions_loc = strpos($file, ",", $assertions_loc); + $assertions = substr($file, $assertions_loc + 12, $end_assertions_loc - $assertions_loc - 12); + + $failures_loc = strpos($file, "Errors: ", $assertions_loc); + $end_failures_loc = strpos($file, ".", $failures_loc); + $failures = trim(substr($file, $failures_loc + 7, $end_failures_loc - $failures_loc - 7)); + + $output['results']['summary']['tests'] = (int)$assertions; + $output['results']['summary']['passed'] = $assertions - $failures; + $output['results']['summary']['failed'] = (int)$failures; +} +echo json_encode($output); diff --git a/php/testing b/php/testing index 33dfd50a74c..bc800354876 100755 --- a/php/testing +++ b/php/testing @@ -7,6 +7,20 @@ then echo " --unit - Only run Unit tests." echo " --integ - Only run Integration tests." fi +if [ "$1" = '--test-pass' ] +then + example_code/vendor/phpunit/phpunit/phpunit --configuration /Users/beqqrry/hackathon-2025/aws-doc-sdk-examples/php/example_code/phpunit.xml --testsuite hackathon-pass &> "output.txt" + php parseOutput.php + echo "" + exit 0 +fi +if [ "$1" = '--test-fail' ] +then + example_code/vendor/phpunit/phpunit/phpunit --configuration /Users/beqqrry/hackathon-2025/aws-doc-sdk-examples/php/example_code/phpunit.xml --testsuite hackathon-fail &> "output.txt" + php parseOutput.php + echo "" + exit 0 +fi if [ "$1" = '--unit' ] then echo "Running Unit tests..."