diff --git a/.buildkite/ci.mjs b/.buildkite/ci.mjs index 31144f19ed..70d4e44c1d 100755 --- a/.buildkite/ci.mjs +++ b/.buildkite/ci.mjs @@ -304,15 +304,23 @@ function getPipeline(options) { * @param {Target} target * @returns {Agent} */ - const getZigAgent = target => { - const { abi, arch } = target; - // if (abi === "musl") { - // const instanceType = arch === "aarch64" ? "c8g.large" : "c7i.large"; - // return getEmphemeralAgent("v2", target, instanceType); - // } + const getZigAgent = platform => { + const { arch } = platform; + const instanceType = arch === "aarch64" ? "c8g.2xlarge" : "c7i.2xlarge"; return { - queue: "build-zig", + robobun: true, + robobun2: true, + os: "linux", + arch, + distro: "debian", + release: "11", + "image-name": `linux-${arch}-debian-11-v5`, // v5 is not on main yet + "instance-type": instanceType, }; + // TODO: Temporarily disable due to configuration + // return { + // queue: "build-zig", + // }; }; /** @@ -443,7 +451,7 @@ function getPipeline(options) { label: `${getTargetLabel(platform)} - build-zig`, depends_on: getDependsOn(platform), agents: getZigAgent(platform), - retry: getRetry(1), // FIXME: Sometimes zig build hangs, so we need to retry once + retry: getRetry(), cancel_on_build_failing: isMergeQueue(), env: getBuildEnv(platform), command: `bun run build:ci --target bun-zig --toolchain ${toolchain}`, @@ -664,16 +672,18 @@ async function main() { } let changedFiles; - // FIXME: Fix various bugs when calculating changed files - // false -> !isFork() && !isMainBranch() - if (false) { + let changedFilesBranch; + if (!isFork() && !isMainBranch()) { console.log("Checking changed files..."); - const baseRef = lastBuild?.commit_id || getTargetBranch() || getMainBranch(); + const targetRef = getTargetBranch(); + console.log(" - Target Ref:", targetRef); + const baseRef = lastBuild?.commit_id || targetRef || getMainBranch(); console.log(" - Base Ref:", baseRef); const headRef = getCommit(); console.log(" - Head Ref:", headRef); changedFiles = await getChangedFiles(undefined, baseRef, headRef); + changedFilesBranch = await getChangedFiles(undefined, targetRef, headRef); if (changedFiles) { if (changedFiles.length) { changedFiles.forEach(filename => console.log(` - ${filename}`)); @@ -698,7 +708,7 @@ async function main() { forceBuild = true; } for (const coref of [".buildkite/ci.mjs", "scripts/utils.mjs", "scripts/bootstrap.sh", "scripts/machine.mjs"]) { - if (changedFiles && changedFiles.includes(coref)) { + if (changedFilesBranch && changedFilesBranch.includes(coref)) { console.log(" - Yes, because the list of changed files contains:", coref); forceBuild = true; ciFileChanged = true; diff --git a/.github/workflows/update-cares.yml b/.github/workflows/update-cares.yml new file mode 100644 index 0000000000..d5d111b663 --- /dev/null +++ b/.github/workflows/update-cares.yml @@ -0,0 +1,92 @@ +name: Update c-ares + +on: + schedule: + - cron: "0 4 * * 0" + workflow_dispatch: + +jobs: + check-update: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - uses: actions/checkout@v4 + + - name: Check c-ares version + id: check-version + run: | + set -euo pipefail + + # Extract the commit hash from the line after COMMIT + CURRENT_VERSION=$(awk '/[[:space:]]*COMMIT[[:space:]]*$/{getline; gsub(/^[[:space:]]+|[[:space:]]+$/,"",$0); print}' cmake/targets/BuildCares.cmake) + + if [ -z "$CURRENT_VERSION" ]; then + echo "Error: Could not find COMMIT line in BuildCares.cmake" + exit 1 + fi + + # Validate that it looks like a git hash + if ! [[ $CURRENT_VERSION =~ ^[0-9a-f]{40}$ ]]; then + echo "Error: Invalid git hash format in BuildCares.cmake" + echo "Found: $CURRENT_VERSION" + echo "Expected: 40 character hexadecimal string" + exit 1 + fi + + echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT + + LATEST_RELEASE=$(curl -sL https://api.github.com/repos/c-ares/c-ares/releases/latest) + if [ -z "$LATEST_RELEASE" ]; then + echo "Error: Failed to fetch latest release from GitHub API" + exit 1 + fi + + LATEST_TAG=$(echo "$LATEST_RELEASE" | jq -r '.tag_name') + if [ -z "$LATEST_TAG" ] || [ "$LATEST_TAG" = "null" ]; then + echo "Error: Could not extract tag name from GitHub API response" + exit 1 + fi + + LATEST_SHA=$(curl -sL "https://api.github.com/repos/c-ares/c-ares/git/ref/tags/$LATEST_TAG" | jq -r '.object.sha') + if [ -z "$LATEST_SHA" ] || [ "$LATEST_SHA" = "null" ]; then + echo "Error: Could not fetch SHA for tag $LATEST_TAG" + exit 1 + fi + + if ! [[ $LATEST_SHA =~ ^[0-9a-f]{40}$ ]]; then + echo "Error: Invalid SHA format received from GitHub" + echo "Found: $LATEST_SHA" + echo "Expected: 40 character hexadecimal string" + exit 1 + fi + + echo "latest=$LATEST_SHA" >> $GITHUB_OUTPUT + echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT + + - name: Update version if needed + if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest + run: | + set -euo pipefail + # Handle multi-line format where COMMIT and its value are on separate lines + sed -i -E '/[[:space:]]*COMMIT[[:space:]]*$/{n;s/[[:space:]]*([0-9a-f]+)[[:space:]]*$/ ${{ steps.check-version.outputs.latest }}/}' cmake/targets/BuildCares.cmake + + - name: Create Pull Request + if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest + uses: peter-evans/create-pull-request@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + add-paths: | + cmake/targets/BuildCares.cmake + commit-message: "deps: update c-ares to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})" + title: "deps: update c-ares to ${{ steps.check-version.outputs.tag }}" + delete-branch: true + branch: deps/update-cares-${{ github.run_number }} + body: | + ## What does this PR do? + + Updates c-ares to version ${{ steps.check-version.outputs.tag }} + + Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-cares.yml) diff --git a/.github/workflows/update-libarchive.yml b/.github/workflows/update-libarchive.yml new file mode 100644 index 0000000000..f4dce7cfce --- /dev/null +++ b/.github/workflows/update-libarchive.yml @@ -0,0 +1,92 @@ +name: Update libarchive + +on: + schedule: + - cron: "0 3 * * 0" + workflow_dispatch: + +jobs: + check-update: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - uses: actions/checkout@v4 + + - name: Check libarchive version + id: check-version + run: | + set -euo pipefail + + # Extract the commit hash from the line after COMMIT + CURRENT_VERSION=$(awk '/[[:space:]]*COMMIT[[:space:]]*$/{getline; gsub(/^[[:space:]]+|[[:space:]]+$/,"",$0); print}' cmake/targets/BuildLibArchive.cmake) + + if [ -z "$CURRENT_VERSION" ]; then + echo "Error: Could not find COMMIT line in BuildLibArchive.cmake" + exit 1 + fi + + # Validate that it looks like a git hash + if ! [[ $CURRENT_VERSION =~ ^[0-9a-f]{40}$ ]]; then + echo "Error: Invalid git hash format in BuildLibArchive.cmake" + echo "Found: $CURRENT_VERSION" + echo "Expected: 40 character hexadecimal string" + exit 1 + fi + + echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT + + LATEST_RELEASE=$(curl -sL https://api.github.com/repos/libarchive/libarchive/releases/latest) + if [ -z "$LATEST_RELEASE" ]; then + echo "Error: Failed to fetch latest release from GitHub API" + exit 1 + fi + + LATEST_TAG=$(echo "$LATEST_RELEASE" | jq -r '.tag_name') + if [ -z "$LATEST_TAG" ] || [ "$LATEST_TAG" = "null" ]; then + echo "Error: Could not extract tag name from GitHub API response" + exit 1 + fi + + LATEST_SHA=$(curl -sL "https://api.github.com/repos/libarchive/libarchive/git/ref/tags/$LATEST_TAG" | jq -r '.object.sha') + if [ -z "$LATEST_SHA" ] || [ "$LATEST_SHA" = "null" ]; then + echo "Error: Could not fetch SHA for tag $LATEST_TAG" + exit 1 + fi + + if ! [[ $LATEST_SHA =~ ^[0-9a-f]{40}$ ]]; then + echo "Error: Invalid SHA format received from GitHub" + echo "Found: $LATEST_SHA" + echo "Expected: 40 character hexadecimal string" + exit 1 + fi + + echo "latest=$LATEST_SHA" >> $GITHUB_OUTPUT + echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT + + - name: Update version if needed + if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest + run: | + set -euo pipefail + # Handle multi-line format where COMMIT and its value are on separate lines + sed -i -E '/[[:space:]]*COMMIT[[:space:]]*$/{n;s/[[:space:]]*([0-9a-f]+)[[:space:]]*$/ ${{ steps.check-version.outputs.latest }}/}' cmake/targets/BuildLibArchive.cmake + + - name: Create Pull Request + if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest + uses: peter-evans/create-pull-request@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + add-paths: | + cmake/targets/BuildLibArchive.cmake + commit-message: "deps: update libarchive to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})" + title: "deps: update libarchive to ${{ steps.check-version.outputs.tag }}" + delete-branch: true + branch: deps/update-libarchive-${{ github.run_number }} + body: | + ## What does this PR do? + + Updates libarchive to version ${{ steps.check-version.outputs.tag }} + + Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-libarchive.yml) diff --git a/.github/workflows/update-libdeflate.yml b/.github/workflows/update-libdeflate.yml new file mode 100644 index 0000000000..7e0be86a1c --- /dev/null +++ b/.github/workflows/update-libdeflate.yml @@ -0,0 +1,92 @@ +name: Update libdeflate + +on: + schedule: + - cron: "0 2 * * 0" + workflow_dispatch: + +jobs: + check-update: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - uses: actions/checkout@v4 + + - name: Check libdeflate version + id: check-version + run: | + set -euo pipefail + + # Extract the commit hash from the line after COMMIT + CURRENT_VERSION=$(awk '/[[:space:]]*COMMIT[[:space:]]*$/{getline; gsub(/^[[:space:]]+|[[:space:]]+$/,"",$0); print}' cmake/targets/BuildLibDeflate.cmake) + + if [ -z "$CURRENT_VERSION" ]; then + echo "Error: Could not find COMMIT line in BuildLibDeflate.cmake" + exit 1 + fi + + # Validate that it looks like a git hash + if ! [[ $CURRENT_VERSION =~ ^[0-9a-f]{40}$ ]]; then + echo "Error: Invalid git hash format in BuildLibDeflate.cmake" + echo "Found: $CURRENT_VERSION" + echo "Expected: 40 character hexadecimal string" + exit 1 + fi + + echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT + + LATEST_RELEASE=$(curl -sL https://api.github.com/repos/ebiggers/libdeflate/releases/latest) + if [ -z "$LATEST_RELEASE" ]; then + echo "Error: Failed to fetch latest release from GitHub API" + exit 1 + fi + + LATEST_TAG=$(echo "$LATEST_RELEASE" | jq -r '.tag_name') + if [ -z "$LATEST_TAG" ] || [ "$LATEST_TAG" = "null" ]; then + echo "Error: Could not extract tag name from GitHub API response" + exit 1 + fi + + LATEST_SHA=$(curl -sL "https://api.github.com/repos/ebiggers/libdeflate/git/ref/tags/$LATEST_TAG" | jq -r '.object.sha') + if [ -z "$LATEST_SHA" ] || [ "$LATEST_SHA" = "null" ]; then + echo "Error: Could not fetch SHA for tag $LATEST_TAG" + exit 1 + fi + + if ! [[ $LATEST_SHA =~ ^[0-9a-f]{40}$ ]]; then + echo "Error: Invalid SHA format received from GitHub" + echo "Found: $LATEST_SHA" + echo "Expected: 40 character hexadecimal string" + exit 1 + fi + + echo "latest=$LATEST_SHA" >> $GITHUB_OUTPUT + echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT + + - name: Update version if needed + if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest + run: | + set -euo pipefail + # Handle multi-line format where COMMIT and its value are on separate lines + sed -i -E '/[[:space:]]*COMMIT[[:space:]]*$/{n;s/[[:space:]]*([0-9a-f]+)[[:space:]]*$/ ${{ steps.check-version.outputs.latest }}/}' cmake/targets/BuildLibDeflate.cmake + + - name: Create Pull Request + if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest + uses: peter-evans/create-pull-request@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + add-paths: | + cmake/targets/BuildLibDeflate.cmake + commit-message: "deps: update libdeflate to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})" + title: "deps: update libdeflate to ${{ steps.check-version.outputs.tag }}" + delete-branch: true + branch: deps/update-libdeflate-${{ github.run_number }} + body: | + ## What does this PR do? + + Updates libdeflate to version ${{ steps.check-version.outputs.tag }} + + Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-libdeflate.yml) diff --git a/.github/workflows/update-lolhtml.yml b/.github/workflows/update-lolhtml.yml new file mode 100644 index 0000000000..f48c4b5ce9 --- /dev/null +++ b/.github/workflows/update-lolhtml.yml @@ -0,0 +1,92 @@ +name: Update lolhtml + +on: + schedule: + - cron: "0 1 * * 0" + workflow_dispatch: + +jobs: + check-update: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - uses: actions/checkout@v4 + + - name: Check lolhtml version + id: check-version + run: | + set -euo pipefail + + # Extract the commit hash from the line after COMMIT + CURRENT_VERSION=$(awk '/[[:space:]]*COMMIT[[:space:]]*$/{getline; gsub(/^[[:space:]]+|[[:space:]]+$/,"",$0); print}' cmake/targets/BuildLolHtml.cmake) + + if [ -z "$CURRENT_VERSION" ]; then + echo "Error: Could not find COMMIT line in BuildLolHtml.cmake" + exit 1 + fi + + # Validate that it looks like a git hash + if ! [[ $CURRENT_VERSION =~ ^[0-9a-f]{40}$ ]]; then + echo "Error: Invalid git hash format in BuildLolHtml.cmake" + echo "Found: $CURRENT_VERSION" + echo "Expected: 40 character hexadecimal string" + exit 1 + fi + + echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT + + LATEST_RELEASE=$(curl -sL https://api.github.com/repos/cloudflare/lol-html/releases/latest) + if [ -z "$LATEST_RELEASE" ]; then + echo "Error: Failed to fetch latest release from GitHub API" + exit 1 + fi + + LATEST_TAG=$(echo "$LATEST_RELEASE" | jq -r '.tag_name') + if [ -z "$LATEST_TAG" ] || [ "$LATEST_TAG" = "null" ]; then + echo "Error: Could not extract tag name from GitHub API response" + exit 1 + fi + + LATEST_SHA=$(curl -sL "https://api.github.com/repos/cloudflare/lol-html/git/ref/tags/$LATEST_TAG" | jq -r '.object.sha') + if [ -z "$LATEST_SHA" ] || [ "$LATEST_SHA" = "null" ]; then + echo "Error: Could not fetch SHA for tag $LATEST_TAG" + exit 1 + fi + + if ! [[ $LATEST_SHA =~ ^[0-9a-f]{40}$ ]]; then + echo "Error: Invalid SHA format received from GitHub" + echo "Found: $LATEST_SHA" + echo "Expected: 40 character hexadecimal string" + exit 1 + fi + + echo "latest=$LATEST_SHA" >> $GITHUB_OUTPUT + echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT + + - name: Update version if needed + if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest + run: | + set -euo pipefail + # Handle multi-line format where COMMIT and its value are on separate lines + sed -i -E '/[[:space:]]*COMMIT[[:space:]]*$/{n;s/[[:space:]]*([0-9a-f]+)[[:space:]]*$/ ${{ steps.check-version.outputs.latest }}/}' cmake/targets/BuildLolHtml.cmake + + - name: Create Pull Request + if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest + uses: peter-evans/create-pull-request@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + add-paths: | + cmake/targets/BuildLolHtml.cmake + commit-message: "deps: update lolhtml to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})" + title: "deps: update lolhtml to ${{ steps.check-version.outputs.tag }}" + delete-branch: true + branch: deps/update-lolhtml-${{ github.run_number }} + body: | + ## What does this PR do? + + Updates lolhtml to version ${{ steps.check-version.outputs.tag }} + + Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-lolhtml.yml) diff --git a/.github/workflows/update-lshpack.yml b/.github/workflows/update-lshpack.yml new file mode 100644 index 0000000000..ae917cc3e9 --- /dev/null +++ b/.github/workflows/update-lshpack.yml @@ -0,0 +1,92 @@ +name: Update lshpack + +on: + schedule: + - cron: "0 5 * * 0" + workflow_dispatch: + +jobs: + check-update: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - uses: actions/checkout@v4 + + - name: Check lshpack version + id: check-version + run: | + set -euo pipefail + + # Extract the commit hash from the line after COMMIT + CURRENT_VERSION=$(awk '/[[:space:]]*COMMIT[[:space:]]*$/{getline; gsub(/^[[:space:]]+|[[:space:]]+$/,"",$0); print}' cmake/targets/BuildLshpack.cmake) + + if [ -z "$CURRENT_VERSION" ]; then + echo "Error: Could not find COMMIT line in BuildLshpack.cmake" + exit 1 + fi + + # Validate that it looks like a git hash + if ! [[ $CURRENT_VERSION =~ ^[0-9a-f]{40}$ ]]; then + echo "Error: Invalid git hash format in BuildLshpack.cmake" + echo "Found: $CURRENT_VERSION" + echo "Expected: 40 character hexadecimal string" + exit 1 + fi + + echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT + + LATEST_RELEASE=$(curl -sL https://api.github.com/repos/litespeedtech/ls-hpack/releases/latest) + if [ -z "$LATEST_RELEASE" ]; then + echo "Error: Failed to fetch latest release from GitHub API" + exit 1 + fi + + LATEST_TAG=$(echo "$LATEST_RELEASE" | jq -r '.tag_name') + if [ -z "$LATEST_TAG" ] || [ "$LATEST_TAG" = "null" ]; then + echo "Error: Could not extract tag name from GitHub API response" + exit 1 + fi + + LATEST_SHA=$(curl -sL "https://api.github.com/repos/litespeedtech/ls-hpack/git/ref/tags/$LATEST_TAG" | jq -r '.object.sha') + if [ -z "$LATEST_SHA" ] || [ "$LATEST_SHA" = "null" ]; then + echo "Error: Could not fetch SHA for tag $LATEST_TAG" + exit 1 + fi + + if ! [[ $LATEST_SHA =~ ^[0-9a-f]{40}$ ]]; then + echo "Error: Invalid SHA format received from GitHub" + echo "Found: $LATEST_SHA" + echo "Expected: 40 character hexadecimal string" + exit 1 + fi + + echo "latest=$LATEST_SHA" >> $GITHUB_OUTPUT + echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT + + - name: Update version if needed + if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest + run: | + set -euo pipefail + # Handle multi-line format where COMMIT and its value are on separate lines + sed -i -E '/[[:space:]]*COMMIT[[:space:]]*$/{n;s/[[:space:]]*([0-9a-f]+)[[:space:]]*$/ ${{ steps.check-version.outputs.latest }}/}' cmake/targets/BuildLshpack.cmake + + - name: Create Pull Request + if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest + uses: peter-evans/create-pull-request@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + add-paths: | + cmake/targets/BuildLshpack.cmake + commit-message: "deps: update lshpack to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})" + title: "deps: update lshpack to ${{ steps.check-version.outputs.tag }}" + delete-branch: true + branch: deps/update-lshpack-${{ github.run_number }} + body: | + ## What does this PR do? + + Updates lshpack to version ${{ steps.check-version.outputs.tag }} + + Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-lshpack.yml) diff --git a/.github/workflows/update-sqlite3.yml b/.github/workflows/update-sqlite3.yml new file mode 100644 index 0000000000..66b5753cca --- /dev/null +++ b/.github/workflows/update-sqlite3.yml @@ -0,0 +1,109 @@ +name: Update SQLite3 + +on: + schedule: + - cron: "0 6 * * 0" # Run weekly + workflow_dispatch: + +jobs: + check-update: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - uses: actions/checkout@v4 + + - name: Check SQLite version + id: check-version + run: | + set -euo pipefail + + # Get current version from the header file using SQLITE_VERSION_NUMBER + CURRENT_VERSION_NUM=$(grep -o '#define SQLITE_VERSION_NUMBER [0-9]\+' src/bun.js/bindings/sqlite/sqlite3_local.h | awk '{print $3}' | tr -d '\n\r') + if [ -z "$CURRENT_VERSION_NUM" ]; then + echo "Error: Could not find SQLITE_VERSION_NUMBER in sqlite3_local.h" + exit 1 + fi + + # Convert numeric version to semantic version for display + CURRENT_MAJOR=$((CURRENT_VERSION_NUM / 1000000)) + CURRENT_MINOR=$((($CURRENT_VERSION_NUM / 1000) % 1000)) + CURRENT_PATCH=$((CURRENT_VERSION_NUM % 1000)) + CURRENT_VERSION="$CURRENT_MAJOR.$CURRENT_MINOR.$CURRENT_PATCH" + + echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT + echo "current_num=$CURRENT_VERSION_NUM" >> $GITHUB_OUTPUT + + # Fetch SQLite download page + DOWNLOAD_PAGE=$(curl -sL https://sqlite.org/download.html) + if [ -z "$DOWNLOAD_PAGE" ]; then + echo "Error: Failed to fetch SQLite download page" + exit 1 + fi + + # Extract latest version and year from the amalgamation link + LATEST_INFO=$(echo "$DOWNLOAD_PAGE" | grep -o 'sqlite-amalgamation-[0-9]\{7\}.zip' | head -n1) + LATEST_YEAR=$(echo "$DOWNLOAD_PAGE" | grep -o '[0-9]\{4\}/sqlite-amalgamation-[0-9]\{7\}.zip' | head -n1 | cut -d'/' -f1 | tr -d '\n\r') + LATEST_VERSION_NUM=$(echo "$LATEST_INFO" | grep -o '[0-9]\{7\}' | tr -d '\n\r') + + if [ -z "$LATEST_VERSION_NUM" ] || [ -z "$LATEST_YEAR" ]; then + echo "Error: Could not extract latest version info" + exit 1 + fi + + # Convert numeric version to semantic version for display + LATEST_MAJOR=$((10#$LATEST_VERSION_NUM / 1000000)) + LATEST_MINOR=$((($LATEST_VERSION_NUM / 1000) % 1000)) + LATEST_PATCH=$((10#$LATEST_VERSION_NUM % 1000)) + LATEST_VERSION="$LATEST_MAJOR.$LATEST_MINOR.$LATEST_PATCH" + + echo "latest=$LATEST_VERSION" >> $GITHUB_OUTPUT + echo "latest_year=$LATEST_YEAR" >> $GITHUB_OUTPUT + echo "latest_num=$LATEST_VERSION_NUM" >> $GITHUB_OUTPUT + + # Debug output + echo "Current version: $CURRENT_VERSION ($CURRENT_VERSION_NUM)" + echo "Latest version: $LATEST_VERSION ($LATEST_VERSION_NUM)" + + - name: Update SQLite if needed + if: success() && steps.check-version.outputs.current_num < steps.check-version.outputs.latest_num + run: | + set -euo pipefail + + TEMP_DIR=$(mktemp -d) + cd $TEMP_DIR + + echo "Downloading from: https://sqlite.org/${{ steps.check-version.outputs.latest_year }}/sqlite-amalgamation-${{ steps.check-version.outputs.latest_num }}.zip" + + # Download and extract latest version + wget "https://sqlite.org/${{ steps.check-version.outputs.latest_year }}/sqlite-amalgamation-${{ steps.check-version.outputs.latest_num }}.zip" + unzip "sqlite-amalgamation-${{ steps.check-version.outputs.latest_num }}.zip" + cd "sqlite-amalgamation-${{ steps.check-version.outputs.latest_num }}" + + # Add header comment and copy files + echo "// clang-format off" > $GITHUB_WORKSPACE/src/bun.js/bindings/sqlite/sqlite3.c + cat sqlite3.c >> $GITHUB_WORKSPACE/src/bun.js/bindings/sqlite/sqlite3.c + + echo "// clang-format off" > $GITHUB_WORKSPACE/src/bun.js/bindings/sqlite/sqlite3_local.h + cat sqlite3.h >> $GITHUB_WORKSPACE/src/bun.js/bindings/sqlite/sqlite3_local.h + + - name: Create Pull Request + if: success() && steps.check-version.outputs.current_num < steps.check-version.outputs.latest_num + uses: peter-evans/create-pull-request@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + add-paths: | + src/bun.js/bindings/sqlite/sqlite3.c + src/bun.js/bindings/sqlite/sqlite3_local.h + commit-message: "deps: update sqlite to ${{ steps.check-version.outputs.latest }}" + title: "deps: update sqlite to ${{ steps.check-version.outputs.latest }}" + delete-branch: true + branch: deps/update-sqlite-${{ steps.check-version.outputs.latest }} + body: | + ## What does this PR do? + + Updates SQLite to version ${{ steps.check-version.outputs.latest }} + + Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-sqlite3.yml) diff --git a/.gitignore b/.gitignore index 8491d87032..3822491fcb 100644 --- a/.gitignore +++ b/.gitignore @@ -175,4 +175,5 @@ test/js/third_party/prisma/prisma/sqlite/dev.db-journal # Generated files .buildkite/ci.yml -*.sock \ No newline at end of file +*.sock +scratch*.{js,ts,tsx,cjs,mjs} \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index da765c9c28..42d0c454a9 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,6 +5,5 @@ test/js/deno test/node.js src/react-refresh.js *.min.js -test/js/node/test/fixtures -test/js/node/test/common test/snippets +test/js/node/test diff --git a/.vscode/launch.json b/.vscode/launch.json index 191c0a815e..dc019a5445 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -224,8 +224,11 @@ "cwd": "${fileDirname}", "env": { "FORCE_COLOR": "1", + // "BUN_DEBUG_DEBUGGER": "1", + // "BUN_DEBUG_INTERNAL_DEBUGGER": "1", "BUN_DEBUG_QUIET_LOGS": "1", "BUN_GARBAGE_COLLECTOR_LEVEL": "2", + // "BUN_INSPECT": "ws+unix:///var/folders/jk/8fzl9l5119598vsqrmphsw7m0000gn/T/tl15npi7qtf.sock?report=1", }, "console": "internalConsole", // Don't pause when the GC runs while the debugger is open. @@ -1254,4 +1257,4 @@ "description": "Usage: bun test [...]", }, ], -} +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index faf1dc0d22..5ead186425 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,50 +2,57 @@ "version": "2.0.0", "tasks": [ { - "type": "process", - "label": "Install Dependencies", - "command": "scripts/all-dependencies.sh", - "windows": { - "command": "scripts/all-dependencies.ps1", - }, - "icon": { - "id": "arrow-down", - }, - "options": { - "cwd": "${workspaceFolder}", - }, - }, - { - "type": "process", - "label": "Setup Environment", - "dependsOn": ["Install Dependencies"], - "command": "scripts/setup.sh", - "windows": { - "command": "scripts/setup.ps1", - }, - "icon": { - "id": "check", - }, - "options": { - "cwd": "${workspaceFolder}", - }, - }, - { - "type": "process", "label": "Build Bun", - "dependsOn": ["Setup Environment"], - "command": "bun", - "args": ["run", "build"], - "icon": { - "id": "gear", + "type": "shell", + "command": "bun run build", + "group": { + "kind": "build", + "isDefault": true, }, - "options": { - "cwd": "${workspaceFolder}", - }, - "isBuildCommand": true, - "runOptions": { - "instanceLimit": 1, - "reevaluateOnRerun": true, + "problemMatcher": [ + { + "owner": "zig", + "fileLocation": ["relative", "${workspaceFolder}"], + "pattern": [ + { + "regexp": "^(.+?):(\\d+):(\\d+): (error|warning|note): (.+)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5, + }, + { + "regexp": "^\\s+(.+)$", + "message": 1, + "loop": true, + }, + ], + }, + { + "owner": "clang", + "fileLocation": ["relative", "${workspaceFolder}"], + "pattern": [ + { + "regexp": "^([^:]+):(\\d+):(\\d+):\\s+(warning|error|note|remark):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5, + }, + { + "regexp": "^\\s*(.*)$", + "message": 1, + "loop": true, + }, + ], + }, + ], + "presentation": { + "reveal": "always", + "panel": "shared", + "clear": true, }, }, ], diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0bf6e5cf59..1a7f41ae5c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ Configuring a development environment for Bun can take 10-30 minutes depending on your internet connection and computer speed. You will need ~10GB of free disk space for the repository and build artifacts. -If you are using Windows, please refer to [this guide](/docs/project/building-windows) +If you are using Windows, please refer to [this guide](/docs/project/building-windows.md) {% details summary="For Ubuntu users" %} TL;DR: Ubuntu 22.04 is suggested. diff --git a/LATEST b/LATEST index 474ad5be60..c99926d330 100644 --- a/LATEST +++ b/LATEST @@ -1 +1 @@ -1.1.36 \ No newline at end of file +1.1.38 \ No newline at end of file diff --git a/build.zig b/build.zig index fed6086672..cfc512ad8d 100644 --- a/build.zig +++ b/build.zig @@ -414,6 +414,15 @@ pub fn addBunObject(b: *Build, opts: *BunBuildOptions) *Compile { } addInternalPackages(b, obj, opts); obj.root_module.addImport("build_options", opts.buildOptionsModule(b)); + + const translate_plugin_api = b.addTranslateC(.{ + .root_source_file = b.path("./packages/bun-native-bundler-plugin-api/bundler_plugin.h"), + .target = opts.target, + .optimize = opts.optimize, + .link_libc = true, + }); + obj.root_module.addImport("bun-native-bundler-plugin-api", translate_plugin_api.createModule()); + return obj; } diff --git a/cmake/Globals.cmake b/cmake/Globals.cmake index 106e1285ea..3066bb2033 100644 --- a/cmake/Globals.cmake +++ b/cmake/Globals.cmake @@ -136,16 +136,6 @@ else() set(WARNING WARNING) endif() -if(LINUX) - if(EXISTS "/etc/alpine-release") - set(DEFAULT_ABI "musl") - else() - set(DEFAULT_ABI "gnu") - endif() - - optionx(ABI "musl|gnu" "The ABI to use (e.g. musl, gnu)" DEFAULT ${DEFAULT_ABI}) -endif() - # TODO: This causes flaky zig builds in CI, so temporarily disable it. # if(CI) # set(DEFAULT_VENDOR_PATH ${CACHE_PATH}/vendor) diff --git a/cmake/Options.cmake b/cmake/Options.cmake index 89cdaef0e8..d6cc8582ea 100644 --- a/cmake/Options.cmake +++ b/cmake/Options.cmake @@ -48,6 +48,16 @@ else() message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}") endif() +if(LINUX) + if(EXISTS "/etc/alpine-release") + set(DEFAULT_ABI "musl") + else() + set(DEFAULT_ABI "gnu") + endif() + + optionx(ABI "musl|gnu" "The ABI to use (e.g. musl, gnu)" DEFAULT ${DEFAULT_ABI}) +endif() + if(ARCH STREQUAL "x64") optionx(ENABLE_BASELINE BOOL "If baseline features should be used for older CPUs (e.g. disables AVX, AVX2)" DEFAULT OFF) endif() diff --git a/cmake/targets/BuildBun.cmake b/cmake/targets/BuildBun.cmake index c27d820afe..20cbb8293e 100644 --- a/cmake/targets/BuildBun.cmake +++ b/cmake/targets/BuildBun.cmake @@ -1163,7 +1163,7 @@ if(NOT BUN_CPP_ONLY) if(CI) set(bunTriplet bun-${OS}-${ARCH}) - if(ABI STREQUAL "musl") + if(LINUX AND ABI STREQUAL "musl") set(bunTriplet ${bunTriplet}-musl) endif() if(ENABLE_BASELINE) diff --git a/cmake/targets/BuildCares.cmake b/cmake/targets/BuildCares.cmake index e49d9a7ab9..373369b543 100644 --- a/cmake/targets/BuildCares.cmake +++ b/cmake/targets/BuildCares.cmake @@ -4,7 +4,7 @@ register_repository( REPOSITORY c-ares/c-ares COMMIT - d1722e6e8acaf10eb73fa995798a9cd421d9f85e + 41ee334af3e3d0027dca5e477855d0244936bd49 ) register_cmake_command( diff --git a/cmake/targets/BuildLibDeflate.cmake b/cmake/targets/BuildLibDeflate.cmake index 3faf5963a7..f629d52fe5 100644 --- a/cmake/targets/BuildLibDeflate.cmake +++ b/cmake/targets/BuildLibDeflate.cmake @@ -4,7 +4,7 @@ register_repository( REPOSITORY ebiggers/libdeflate COMMIT - dc76454a39e7e83b68c3704b6e3784654f8d5ac5 + 9d624d1d8ba82c690d6d6be1d0a961fc5a983ea4 ) register_cmake_command( diff --git a/cmake/targets/BuildLolHtml.cmake b/cmake/targets/BuildLolHtml.cmake index aeac571321..934f8d0be9 100644 --- a/cmake/targets/BuildLolHtml.cmake +++ b/cmake/targets/BuildLolHtml.cmake @@ -4,7 +4,7 @@ register_repository( REPOSITORY cloudflare/lol-html COMMIT - 8d4c273ded322193d017042d1f48df2766b0f88b + 4f8becea13a0021c8b71abd2dcc5899384973b66 ) set(LOLHTML_CWD ${VENDOR_PATH}/lolhtml/c-api) diff --git a/cmake/targets/BuildLshpack.cmake b/cmake/targets/BuildLshpack.cmake index c1cbb12ff5..845f9bf011 100644 --- a/cmake/targets/BuildLshpack.cmake +++ b/cmake/targets/BuildLshpack.cmake @@ -4,7 +4,7 @@ register_repository( REPOSITORY litespeedtech/ls-hpack COMMIT - 3d0f1fc1d6e66a642e7a98c55deb38aa986eb4b0 + 32e96f10593c7cb8553cd8c9c12721100ae9e924 ) if(WIN32) diff --git a/cmake/tools/SetupLLVM.cmake b/cmake/tools/SetupLLVM.cmake index 5e5fd3a953..9db637b60d 100644 --- a/cmake/tools/SetupLLVM.cmake +++ b/cmake/tools/SetupLLVM.cmake @@ -4,7 +4,7 @@ if(NOT ENABLE_LLVM) return() endif() -if(CMAKE_HOST_WIN32 OR CMAKE_HOST_APPLE OR ABI STREQUAL "musl") +if(CMAKE_HOST_WIN32 OR CMAKE_HOST_APPLE OR EXISTS "/etc/alpine-release") set(DEFAULT_LLVM_VERSION "18.1.8") else() set(DEFAULT_LLVM_VERSION "16.0.6") diff --git a/cmake/tools/SetupWebKit.cmake b/cmake/tools/SetupWebKit.cmake index dd263335c4..e7cb26be5e 100644 --- a/cmake/tools/SetupWebKit.cmake +++ b/cmake/tools/SetupWebKit.cmake @@ -63,7 +63,7 @@ else() message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}") endif() -if(ABI STREQUAL "musl") +if(LINUX AND ABI STREQUAL "musl") set(WEBKIT_SUFFIX "-musl") endif() diff --git a/docs/cli/test.md b/docs/cli/test.md index 29912ba2f5..e1b37f229b 100644 --- a/docs/cli/test.md +++ b/docs/cli/test.md @@ -75,8 +75,10 @@ jobs: name: build-app runs-on: ubuntu-latest steps: + - name: Checkout + uses: actions/checkout@v4 - name: Install bun - uses: oven-sh/setup-bun + uses: oven-sh/setup-bun@v2 - name: Install dependencies # (assuming your project has dependencies) run: bun install # You can use npm/yarn/pnpm instead if you prefer - name: Run tests diff --git a/docs/upgrading-webkit.md b/docs/contributing/upgrading-webkit.md similarity index 100% rename from docs/upgrading-webkit.md rename to docs/contributing/upgrading-webkit.md diff --git a/docs/guides/runtime/set-env.md b/docs/guides/runtime/set-env.md index 92cd5e27ec..c336a2c7a5 100644 --- a/docs/guides/runtime/set-env.md +++ b/docs/guides/runtime/set-env.md @@ -16,7 +16,7 @@ Set these variables in a `.env` file. Bun reads the following files automatically (listed in order of increasing precedence). - `.env` -- `.env.production` or `.env.development` (depending on value of `NODE_ENV`) +- `.env.production`, `.env.development`, `.env.test` (depending on value of `NODE_ENV`) - `.env.local` ```txt#.env diff --git a/docs/installation.md b/docs/installation.md index f52d4d5f5a..f98e3bbfa1 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -73,8 +73,7 @@ There are also image variants for different operating systems. $ docker pull oven/bun:debian $ docker pull oven/bun:slim $ docker pull oven/bun:distroless -# alpine not recommended until #918 is fixed -# $ docker pull oven/bun:alpine +$ docker pull oven/bun:alpine ``` ## Checking installation @@ -190,14 +189,19 @@ For convenience, here are download links for the latest version: - [`bun-linux-x64.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64.zip) - [`bun-linux-x64-baseline.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-baseline.zip) +- [`bun-linux-x64-musl.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-musl.zip) +- [`bun-linux-x64-musl-baseline.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-musl-baseline.zip) - [`bun-windows-x64.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-windows-x64.zip) - [`bun-windows-x64-baseline.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-windows-x64-baseline.zip) - [`bun-darwin-aarch64.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-darwin-aarch64.zip) - [`bun-linux-aarch64.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-aarch64.zip) +- [`bun-linux-aarch64-musl.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-aarch64-musl.zip) - [`bun-darwin-x64.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-darwin-x64.zip) - [`bun-darwin-x64-baseline.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-darwin-x64-baseline.zip) -The `baseline` binaries are built for older CPUs which may not support AVX2 instructions. If you run into an "Illegal Instruction" error when running Bun, try using the `baseline` binaries instead. Bun's install scripts automatically choose the correct binary for your system which helps avoid this issue. Baseline builds are slower than regular builds, so use them only if necessary. +The `musl` binaries are built for distributions that do not ship with the glibc libraries by default, instead relying on musl. The two most popular distros are Void Linux and Alpine Linux, with the latter is used heavily in Docker containers. If you encounter an error like the following: `bun: /lib/x86_64-linux-gnu/libm.so.6: version GLIBC_2.29' not found (required by bun)`, try using the musl binary. Bun's install script automatically chooses the correct binary for your system. + +The `baseline` binaries are built for older CPUs which may not support AVX2 instructions. If you run into an "Illegal Instruction" error when running Bun, try using the `baseline` binaries instead. Bun's install scripts automatically chooses the correct binary for your system which helps avoid this issue. Baseline builds are slower than regular builds, so use them only if necessary.

Hello world!

"); }); it("beep:boop returns 42", () => { @@ -296,9 +294,8 @@ describe("dynamic import", () => { it("SSRs `

Hello world!

` with Svelte", async () => { const { default: App }: any = await import("./hello.svelte"); - const { html } = App.render(); - - expect(html).toBe("

Hello world!

"); + const { body } = svelteRender(App); + expect(body).toBe("

Hello world!

"); }); it("beep:boop returns 42", async () => { @@ -327,9 +324,9 @@ import Hello from ${JSON.stringify(resolve(import.meta.dir, "hello2.svelte"))}; export default Hello; `; const { default: SvelteApp } = await import("delay:hello2.svelte"); - const { html } = SvelteApp.render(); + const { body } = svelteRender(SvelteApp); - expect(html).toBe("

Hello world!

"); + expect(body).toBe("

Hello world!

"); }); }); @@ -483,631 +480,3 @@ describe("errors", () => { expect(text).toBe(result); }); }); - -describe("start", () => { - { - let state: string = "Should not see this!"; - - itBundled("works", { - experimentalCss: true, - minifyWhitespace: true, - files: { - "/entry.css": /* css */ ` - body { - background: white; - color: blue; } - `, - }, - plugins: [ - { - name: "demo", - setup(build) { - build.onStart(() => { - state = "red"; - }); - - build.onLoad({ filter: /\.css/ }, async ({ path }) => { - console.log("[plugin] Path", path); - return { - contents: `body { color: ${state} }`, - loader: "css", - }; - }); - }, - }, - ], - outfile: "/out.js", - onAfterBundle(api) { - api.expectFile("/out.js").toEqualIgnoringWhitespace(`body{color:${state}}`); - }, - }); - } - - { - type Action = "onLoad" | "onStart"; - let actions: Action[] = []; - - itBundled("executes before everything", { - experimentalCss: true, - minifyWhitespace: true, - files: { - "/entry.css": /* css */ ` - body { - background: white; - color: blue; } - `, - }, - plugins: [ - { - name: "demo", - setup(build) { - build.onLoad({ filter: /\.css/ }, async ({ path }) => { - actions.push("onLoad"); - return { - contents: `body { color: red }`, - loader: "css", - }; - }); - - build.onStart(() => { - actions.push("onStart"); - }); - }, - }, - ], - outfile: "/out.js", - onAfterBundle(api) { - api.expectFile("/out.js").toEqualIgnoringWhitespace(`body{ color: red }`); - - expect(actions).toStrictEqual(["onStart", "onLoad"]); - }, - }); - } - - { - let action: string[] = []; - itBundled("executes after all plugins have been setup", { - experimentalCss: true, - minifyWhitespace: true, - files: { - "/entry.css": /* css */ ` - body { - background: white; - color: blue; } - `, - }, - plugins: [ - { - name: "onStart 1", - setup(build) { - build.onStart(async () => { - action.push("onStart 1 setup"); - await Bun.sleep(1000); - action.push("onStart 1 complete"); - }); - }, - }, - { - name: "onStart 2", - setup(build) { - build.onStart(async () => { - action.push("onStart 2 setup"); - await Bun.sleep(1000); - action.push("onStart 2 complete"); - }); - }, - }, - { - name: "onStart 3", - setup(build) { - build.onStart(async () => { - action.push("onStart 3 setup"); - await Bun.sleep(1000); - action.push("onStart 3 complete"); - }); - }, - }, - ], - outfile: "/out.js", - onAfterBundle(api) { - expect(action.slice(0, 3)).toStrictEqual(["onStart 1 setup", "onStart 2 setup", "onStart 3 setup"]); - expect(new Set(action.slice(3))).toStrictEqual( - new Set(["onStart 1 complete", "onStart 2 complete", "onStart 3 complete"]), - ); - }, - }); - } - - { - let action: string[] = []; - test("LMAO", async () => { - const folder = tempDirWithFiles("plz", { - "index.ts": "export const foo = {}", - }); - try { - const result = await Bun.build({ - entrypoints: [path.join(folder, "index.ts")], - experimentalCss: true, - minify: true, - plugins: [ - { - name: "onStart 1", - setup(build) { - build.onStart(async () => { - action.push("onStart 1 setup"); - throw new Error("WOOPS"); - // await Bun.sleep(1000); - }); - }, - }, - { - name: "onStart 2", - setup(build) { - build.onStart(async () => { - action.push("onStart 2 setup"); - await Bun.sleep(1000); - action.push("onStart 2 complete"); - }); - }, - }, - { - name: "onStart 3", - setup(build) { - build.onStart(async () => { - action.push("onStart 3 setup"); - await Bun.sleep(1000); - action.push("onStart 3 complete"); - }); - }, - }, - ], - }); - console.log(result); - } catch (err) { - expect(err).toBeDefined(); - return; - } - throw new Error("DIDNT GET ERRROR!"); - }); - } -}); - -describe("defer", () => { - { - type Action = { - type: "load" | "defer"; - path: string; - }; - let actions: Action[] = []; - function logLoad(path: string) { - actions.push({ type: "load", path: path.replaceAll("\\", "/") }); - } - function logDefer(path: string) { - actions.push({ type: "defer", path: path.replaceAll("\\", "/") }); - } - - itBundled("basic", { - experimentalCss: true, - files: { - "/index.ts": /* ts */ ` -import { lmao } from "./lmao.ts"; -import foo from "./a.css"; - -console.log("Foo", foo, lmao); - `, - "/lmao.ts": ` -import { foo } from "./foo.ts"; -export const lmao = "lolss"; -console.log(foo); - `, - "/foo.ts": ` - export const foo = 'lkdfjlsdf'; - console.log('hi')`, - "/a.css": ` - h1 { - color: blue; - } - `, - }, - entryPoints: ["index.ts"], - plugins: [ - { - name: "demo", - setup(build) { - build.onLoad({ filter: /\.(ts)/ }, async ({ defer, path }) => { - // console.log("Running on load plugin", path); - if (path.includes("index.ts")) { - logLoad(path); - return undefined; - } - logDefer(path); - await defer(); - logLoad(path); - return undefined; - }); - }, - }, - ], - outdir: "/out", - onAfterBundle(api) { - const expected_actions: Action[] = [ - { - type: "load", - path: "index.ts", - }, - { - type: "defer", - path: "lmao.ts", - }, - { - type: "load", - path: "lmao.ts", - }, - { - type: "defer", - path: "foo.ts", - }, - { - type: "load", - path: "foo.ts", - }, - ]; - - expect(actions.length).toBe(expected_actions.length); - for (let i = 0; i < expected_actions.length; i++) { - const expected = expected_actions[i]; - const action = actions[i]; - const filename = action.path.split("/").pop(); - - expect(action.type).toEqual(expected.type); - expect(filename).toEqual(expected.path); - } - }, - }); - } - - itBundled("edgecase", { - experimentalCss: true, - minifyWhitespace: true, - files: { - "/entry.css": /* css */ ` - body { - background: white; - color: black } - `, - }, - plugins: [ - { - name: "demo", - setup(build) { - build.onLoad({ filter: /\.css/ }, async ({ path }) => { - console.log("[plugin] Path", path); - return { - contents: 'h1 [this_worked="nice!"] { color: red; }', - loader: "css", - }; - }); - }, - }, - ], - outfile: "/out.js", - onAfterBundle(api) { - api.expectFile("/out.js").toContain(`h1 [this_worked=nice\\!]{color:red} -`); - }, - }); - - // encountered double free when CSS build has error - itBundled("shouldn't crash on CSS parse error", { - experimentalCss: true, - files: { - "/index.ts": /* ts */ ` - import { lmao } from "./lmao.ts"; - import foo from "./a.css"; - - console.log("Foo", foo, lmao); - `, - "/lmao.ts": ` - import { foo } from "./foo.ts"; - export const lmao = "lolss"; - console.log(foo); - `, - "/foo.ts": ` - export const foo = "LOL bro"; - console.log("FOOOO", foo); - `, - "/a.css": ` - /* helllooo friends */ - `, - }, - entryPoints: ["index.ts"], - plugins: [ - { - name: "demo", - setup(build) { - build.onLoad({ filter: /\.css/ }, async ({ path }) => { - console.log("[plugin] CSS path", path); - return { - // this fails, because it causes a Build error I think? - contents: `hello friends!`, - loader: "css", - }; - }); - - build.onLoad({ filter: /\.(ts)/ }, async ({ defer, path }) => { - // console.log("Running on load plugin", path); - if (path.includes("index.ts")) { - console.log("[plugin] Path", path); - return undefined; - } - await defer(); - return undefined; - }); - }, - }, - ], - outdir: "/out", - bundleErrors: { - "/a.css": ["Unexpected end of input"], - }, - }); - - itBundled("works as expected when onLoad error occurs after defer", { - experimentalCss: true, - files: { - "/index.ts": /* ts */ ` - import { lmao } from "./lmao.ts"; - import foo from "./a.css"; - - console.log("Foo", foo, lmao); - `, - "/lmao.ts": ` - import { foo } from "./foo.ts"; - export const lmao = "lolss"; - console.log(foo); - `, - "/foo.ts": ` - export const foo = "LOL bro"; - console.log("FOOOO", foo); - `, - "/a.css": ` - /* helllooo friends */ - `, - }, - entryPoints: ["index.ts"], - plugins: [ - { - name: "demo", - setup(build) { - build.onLoad({ filter: /\.css/ }, async ({ path }) => { - return { - // this fails, because it causes a Build error I think? - contents: `hello friends`, - loader: "css", - }; - }); - - build.onLoad({ filter: /\.(ts)/ }, async ({ defer, path }) => { - if (path.includes("index.ts")) { - return undefined; - } - await defer(); - throw new Error("woopsie"); - }); - }, - }, - ], - outdir: "/out", - bundleErrors: { - "/a.css": ["Unexpected end of input"], - "/lmao.ts": ["woopsie"], - }, - }); - - itBundled("calling defer more than once errors", { - experimentalCss: true, - files: { - "/index.ts": /* ts */ ` - import { lmao } from "./lmao.ts"; - import foo from "./a.css"; - - console.log("Foo", foo, lmao); - `, - "/lmao.ts": ` - import { foo } from "./foo.ts"; - export const lmao = "lolss"; - console.log(foo); - `, - "/foo.ts": ` - export const foo = "LOL bro"; - console.log("FOOOO", foo); - `, - "/a.css": ` - /* helllooo friends */ - `, - }, - entryPoints: ["index.ts"], - plugins: [ - { - name: "demo", - setup(build) { - build.onLoad({ filter: /\.css/ }, async ({ path }) => { - return { - // this fails, because it causes a Build error I think? - contents: `hello friends`, - loader: "css", - }; - }); - - build.onLoad({ filter: /\.(ts)/ }, async ({ defer, path }) => { - if (path.includes("index.ts")) { - return undefined; - } - await defer(); - await defer(); - }); - }, - }, - ], - outdir: "/out", - bundleErrors: { - "/a.css": ["Unexpected end of input"], - "/lmao.ts": ["can't call .defer() more than once within an onLoad plugin"], - }, - }); - - test("integration", async () => { - const folder = tempDirWithFiles("integration", { - "module_data.json": "{}", - "package.json": `{ - "name": "integration-test", - "version": "1.0.0", - "private": true, - "type": "module", - "dependencies": { - } - }`, - "src/index.ts": ` -import { greet } from "./utils/greetings"; -import { formatDate } from "./utils/dates"; -import { calculateTotal } from "./math/calculations"; -import { logger } from "./services/logger"; -import moduleData from "../module_data.json"; -import path from "path"; - - -await Bun.write(path.join(import.meta.dirname, 'output.json'), JSON.stringify(moduleData)) - -function main() { - const today = new Date(); - logger.info("Application started"); - - const total = calculateTotal([10, 20, 30, 40]); - console.log(greet("World")); - console.log(\`Today is \${formatDate(today)}\`); - console.log(\`Total: \${total}\`); -} -`, - "src/utils/greetings.ts": ` -export function greet(name: string): string { - return \`Hello \${name}!\`; -} -`, - "src/utils/dates.ts": ` -export function formatDate(date: Date): string { - return date.toLocaleDateString("en-US", { - weekday: "long", - year: "numeric", - month: "long", - day: "numeric" - }); -} -`, - "src/math/calculations.ts": ` -export function calculateTotal(numbers: number[]): number { - return numbers.reduce((sum, num) => sum + num, 0); -} - -export function multiply(a: number, b: number): number { - return a * b; -} -`, - "src/services/logger.ts": ` -export const logger = { - info: (msg: string) => console.log(\`[INFO] \${msg}\`), - error: (msg: string) => console.error(\`[ERROR] \${msg}\`), - warn: (msg: string) => console.warn(\`[WARN] \${msg}\`) -}; -`, - }); - - const entrypoint = path.join(folder, "src", "index.ts"); - await Bun.$`${bunExe()} install`.env(bunEnv).cwd(folder); - - const outdir = path.join(folder, "dist"); - - const result = await Bun.build({ - entrypoints: [entrypoint], - outdir, - plugins: [ - { - name: "xXx123_import_checker_321xXx", - setup(build) { - type Import = { - imported: string[]; - dep: string; - }; - type Export = { - ident: string; - }; - let imports_and_exports: Record; exports: Array }> = {}; - - build.onLoad({ filter: /\.ts/ }, async ({ path }) => { - const contents = await Bun.$`cat ${path}`.quiet().text(); - - const import_regex = /import\s+(?:([\s\S]*?)\s+from\s+)?['"]([^'"]+)['"];/g; - const imports: Array = [...contents.toString().matchAll(import_regex)].map(m => ({ - imported: m - .slice(1, m.length - 1) - .map(match => (match[0] === "{" ? match.slice(2, match.length - 2) : match)), - dep: m[m.length - 1], - })); - - const export_regex = - /export\s+(?:default\s+|const\s+|let\s+|var\s+|function\s+|class\s+|enum\s+|type\s+|interface\s+)?([\w$]+)?(?:\s*=\s*|(?:\s*{[^}]*})?)?[^;]*;/g; - const exports: Array = [...contents.matchAll(export_regex)].map(m => ({ - ident: m[1], - })); - - imports_and_exports[path.replaceAll("\\", "/").split("/").pop()!] = { imports, exports }; - return undefined; - }); - - build.onLoad({ filter: /module_data\.json/ }, async ({ defer }) => { - await defer(); - const contents = JSON.stringify(imports_and_exports); - - return { - contents, - loader: "json", - }; - }); - }, - }, - ], - }); - - expect(result.success).toBeTrue(); - await Bun.$`${bunExe()} run ${result.outputs[0].path}`; - const output = await Bun.$`cat ${path.join(folder, "dist", "output.json")}`.json(); - expect(output).toStrictEqual({ - "index.ts": { - "imports": [ - { "imported": ["greet"], "dep": "./utils/greetings" }, - { "imported": ["formatDate"], "dep": "./utils/dates" }, - { "imported": ["calculateTotal"], "dep": "./math/calculations" }, - { "imported": ["logger"], "dep": "./services/logger" }, - { "imported": ["moduleData"], "dep": "../module_data.json" }, - { "imported": ["path"], "dep": "path" }, - ], - "exports": [], - }, - "greetings.ts": { - "imports": [], - "exports": [{ "ident": "greet" }], - }, - "dates.ts": { - "imports": [], - "exports": [{ "ident": "formatDate" }], - }, - "calculations.ts": { - "imports": [], - "exports": [{ "ident": "calculateTotal" }, { "ident": "multiply" }], - }, - "logger.ts": { - "imports": [], - "exports": [{ "ident": "logger" }], - }, - }); - }); -}); diff --git a/test/js/bun/util/filesystem_router.test.ts b/test/js/bun/util/filesystem_router.test.ts index e297a57be6..96a503112b 100644 --- a/test/js/bun/util/filesystem_router.test.ts +++ b/test/js/bun/util/filesystem_router.test.ts @@ -344,6 +344,31 @@ it("reload() works", () => { expect(router.match("/posts")!.name).toBe("/posts"); }); +it("reload() works with new dirs/files", () => { + const { dir } = make(["posts.tsx"]); + + const router = new Bun.FileSystemRouter({ + dir, + style: "nextjs", + assetPrefix: "/_next/static/", + origin: "https://nextjs.org", + }); + + expect(router.match("/posts")!.name).toBe("/posts"); + createTree(dir, ['test/recursive/index.ts']); + router.reload(); + expect(router.match("/test/recursive")!.name).toBe("/test/recursive"); + rmSync(`${dir}/test/recursive`, { + recursive: true, + force: true + }); + router.reload(); + expect(router.match("/test/recursive")).toBe(null); + createTree(dir, ['test/test2/index.ts']); + router.reload(); + expect(router.match("/test/test2")!.name).toBe("/test/test2"); +}) + it(".query works with dynamic routes, including params", () => { // set up the test const { dir } = make(["posts/[id].tsx"]); diff --git a/test/js/bun/util/inspect.test.js b/test/js/bun/util/inspect.test.js index 301d267426..6ee2d89b6a 100644 --- a/test/js/bun/util/inspect.test.js +++ b/test/js/bun/util/inspect.test.js @@ -525,13 +525,39 @@ it("Bun.inspect array with non-indexed properties", () => { }); describe("console.logging function displays async and generator names", async () => { - const cases = [function a() {}, async function b() {}, function* c() {}, async function* d() {}]; + const cases = [ + function () {}, + function a() {}, + async function b() {}, + function* c() {}, + async function* d() {}, + async function* () {}, + ]; const expected_logs = [ + "[Function]", "[Function: a]", "[AsyncFunction: b]", "[GeneratorFunction: c]", "[AsyncGeneratorFunction: d]", + "[AsyncGeneratorFunction]", + ]; + + for (let i = 0; i < cases.length; i++) { + it(expected_logs[i], () => { + expect(Bun.inspect(cases[i])).toBe(expected_logs[i]); + }); + } +}); +describe("console.logging class displays names and extends", async () => { + class A {} + const cases = [A, class B extends A {}, class extends A {}, class {}]; + + const expected_logs = [ + "[class A]", + "[class B extends A]", + "[class (anonymous) extends A]", + "[class (anonymous)]", ]; for (let i = 0; i < cases.length; i++) { @@ -562,3 +588,14 @@ it("console.log on a Blob shows name", () => { `File (3 bytes) {\n name: "",\n type: "text/plain;charset=utf-8",\n lastModified: ${file.lastModified}\n}`, ); }); + +it("console.log on a arguments shows list", () => { + function fn() { + expect(Bun.inspect(arguments)).toBe(`[ 1, [ 1 ], [Function: fn] ]`); + } + fn(1, [1], fn); +}); + +it("console.log on null prototype", () => { + expect(Bun.inspect(Object.create(null))).toBe("[Object: null prototype] {}"); +}); diff --git a/test/js/bun/websocket/websocket-server.test.ts b/test/js/bun/websocket/websocket-server.test.ts index 6bcb231747..23022030c2 100644 --- a/test/js/bun/websocket/websocket-server.test.ts +++ b/test/js/bun/websocket/websocket-server.test.ts @@ -825,3 +825,13 @@ async function connect(server: Server): Promise { clients.push(client); await promise; } + +it("you can call server.subscriberCount() when its not a websocket server", async () => { + using server = serve({ + port: 0, + fetch(request, server) { + return new Response(); + }, + }); + expect(server.subscriberCount("boop")).toBe(0); +}); diff --git a/test/js/node/buffer.test.js b/test/js/node/buffer.test.js index f5622d093c..645d29ac9e 100644 --- a/test/js/node/buffer.test.js +++ b/test/js/node/buffer.test.js @@ -633,6 +633,37 @@ for (let withOverridenBufferWrite of [false, true]) { expect(dot.toString("base64url")).toBe("__4uAA"); }); + describe("writing with offset undefined", () => { + [ + ["writeUInt8", "readUInt8", 8, 1], + ["writeInt8", "readInt8", 8, 1], + ["writeUInt16LE", "readUInt16LE", 8, 2], + ["writeInt16LE", "readInt16LE", 8, 2], + ["writeUInt16BE", "readUInt16BE", 8, 2], + ["writeInt16BE", "readInt16BE", 8, 2], + ["writeUInt32LE", "readUInt32LE", 8, 4], + ["writeInt32LE", "readInt32LE", 8, 4], + ["writeUInt32BE", "readUInt32BE", 8, 4], + ["writeInt32BE", "readInt32BE", 8, 4], + ["writeFloatLE", "readFloatLE", 8, 4], + ["writeFloatBE", "readFloatBE", 8, 4], + ["writeDoubleLE", "readDoubleLE", 8, 8], + ["writeDoubleBE", "readDoubleBE", 8, 8], + ].forEach(([method, read, value, size]) => { + it(`${method} (implicit offset)`, () => { + const b = Buffer.alloc(10, 42); + expect(b[method](value)).toBe(size); + expect(b[read]()).toBe(value); + }); + + it(`${method} (explicit offset)`, () => { + const b = Buffer.alloc(10, 42); + expect(b[method](value, 0)).toBe(size); + expect(b[read]()).toBe(value); + }); + }); + }); + // https://github.com/joyent/node/issues/402 it("writing base64 at a position > 0 should not mangle the result", () => { const segments = ["TWFkbmVzcz8h", "IFRoaXM=", "IGlz", "IG5vZGUuanMh"]; diff --git a/test/js/node/child_process/child_process.test.ts b/test/js/node/child_process/child_process.test.ts index 11bd16de1d..f70772d421 100644 --- a/test/js/node/child_process/child_process.test.ts +++ b/test/js/node/child_process/child_process.test.ts @@ -454,3 +454,13 @@ it.if(!isWindows)("spawnSync correctly reports signal codes", () => { expect(signal).toBe("SIGTRAP"); }); + +it("spawnSync(does-not-exist)", () => { + const x = spawnSync("does-not-exist"); + expect(x.error?.code).toEqual("ENOENT"); + expect(x.error.path).toEqual("does-not-exist"); + expect(x.signal).toEqual(null); + expect(x.output).toEqual([null, null, null]); + expect(x.stdout).toEqual(null); + expect(x.stderr).toEqual(null); +}); diff --git a/test/js/node/cluster/common.ts b/test/js/node/cluster/common.ts deleted file mode 100644 index b99b402c49..0000000000 --- a/test/js/node/cluster/common.ts +++ /dev/null @@ -1,37 +0,0 @@ -import assert from "node:assert"; -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; -import process from "node:process"; -import util from "node:util"; - -export const isWindows = process.platform === "win32"; - -export function tmpdirSync(pattern: string = "bun.test.") { - return fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), pattern)); -} - -export function isAlive(pid) { - try { - process.kill(pid, "SIGCONT"); - return true; - } catch { - return false; - } -} - -export function mustNotCall(msg?) { - return function mustNotCall(...args) { - const argsInfo = args.length > 0 ? `\ncalled with arguments: ${args.map(arg => util.inspect(arg)).join(", ")}` : ""; - assert.fail(`${msg || "function should not have been called"} ` + argsInfo); - }; -} - -export function patchEmitter(emitter: any, prefix: string) { - var oldEmit = emitter.emit; - - emitter.emit = function () { - console.log([prefix, arguments[0]]); - oldEmit.apply(emitter, arguments); - }; -} diff --git a/test/js/node/cluster/test-worker-no-exit-http.ts b/test/js/node/cluster/test-worker-no-exit-http.ts index 661c439861..93156225b9 100644 --- a/test/js/node/cluster/test-worker-no-exit-http.ts +++ b/test/js/node/cluster/test-worker-no-exit-http.ts @@ -1,7 +1,15 @@ const assert = require("assert"); const cluster = require("cluster"); const http = require("http"); -import { patchEmitter } from "./common"; + +function patchEmitter(emitter: any, prefix: string) { + var oldEmit = emitter.emit; + + emitter.emit = function () { + console.log([prefix, arguments[0]]); + oldEmit.apply(emitter, arguments); + }; +} let destroyed; let success; diff --git a/test/js/node/cluster/upstream/common/countdown.js b/test/js/node/cluster/upstream/common/countdown.js deleted file mode 100644 index 9853c9fa47..0000000000 --- a/test/js/node/cluster/upstream/common/countdown.js +++ /dev/null @@ -1,27 +0,0 @@ -"use strict"; - -const assert = require("assert"); -const kLimit = Symbol("limit"); -const kCallback = Symbol("callback"); -const common = require("./"); - -class Countdown { - constructor(limit, cb) { - assert.strictEqual(typeof limit, "number"); - assert.strictEqual(typeof cb, "function"); - this[kLimit] = limit; - this[kCallback] = common.mustCall(cb); - } - - dec() { - assert(this[kLimit] > 0, "Countdown expired"); - if (--this[kLimit] === 0) this[kCallback](); - return this[kLimit]; - } - - get remaining() { - return this[kLimit]; - } -} - -module.exports = Countdown; diff --git a/test/js/node/cluster/upstream/common/index.js b/test/js/node/cluster/upstream/common/index.js deleted file mode 100644 index 46df521726..0000000000 --- a/test/js/node/cluster/upstream/common/index.js +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// /* eslint-disable node-core/crypto-check */ -"use strict"; -const process = global.process; // Some tests tamper with the process global. - -const assert = require("assert"); -const { exec, execSync, spawn, spawnSync } = require("child_process"); -const fs = require("fs"); -const net = require("net"); -// Do not require 'os' until needed so that test-os-checked-function can -// monkey patch it. If 'os' is required here, that test will fail. -const path = require("path"); -const { inspect } = require("util"); -const { isMainThread } = require("worker_threads"); - -// Some tests assume a umask of 0o022 so set that up front. Tests that need a -// different umask will set it themselves. -// -// Workers can read, but not set the umask, so check that this is the main -// thread. -if (isMainThread) process.umask(0o022); - -const noop = () => {}; - -const isWindows = process.platform === "win32"; -const isSunOS = process.platform === "sunos"; -const isFreeBSD = process.platform === "freebsd"; -const isOpenBSD = process.platform === "openbsd"; -const isLinux = process.platform === "linux"; -const isOSX = process.platform === "darwin"; -const isPi = (() => { - try { - // Normal Raspberry Pi detection is to find the `Raspberry Pi` string in - // the contents of `/sys/firmware/devicetree/base/model` but that doesn't - // work inside a container. Match the chipset model number instead. - const cpuinfo = fs.readFileSync("/proc/cpuinfo", { encoding: "utf8" }); - const ok = /^Hardware\s*:\s*(.*)$/im.exec(cpuinfo)?.[1] === "BCM2835"; - /^/.test(""); // Clear RegExp.$_, some tests expect it to be empty. - return ok; - } catch { - return false; - } -})(); - -const isDumbTerminal = process.env.TERM === "dumb"; - -const mustCallChecks = []; - -function runCallChecks(exitCode) { - if (exitCode !== 0) return; - - const failed = mustCallChecks.filter(function (context) { - if ("minimum" in context) { - context.messageSegment = `at least ${context.minimum}`; - return context.actual < context.minimum; - } - context.messageSegment = `exactly ${context.exact}`; - return context.actual !== context.exact; - }); - - failed.forEach(function (context) { - console.log( - "Mismatched %s function calls. Expected %s, actual %d.", - context.name, - context.messageSegment, - context.actual, - ); - console.log(context.stack.split("\n").slice(2).join("\n")); - }); - - if (failed.length) process.exit(1); -} - -function mustCall(fn, exact) { - return _mustCallInner(fn, exact, "exact"); -} - -function mustSucceed(fn, exact) { - return mustCall(function (err, ...args) { - assert.ifError(err); - if (typeof fn === "function") return fn.apply(this, args); - }, exact); -} - -function _mustCallInner(fn, criteria = 1, field) { - if (process._exiting) throw new Error("Cannot use common.mustCall*() in process exit handler"); - if (typeof fn === "number") { - criteria = fn; - fn = noop; - } else if (fn === undefined) { - fn = noop; - } - - if (typeof criteria !== "number") throw new TypeError(`Invalid ${field} value: ${criteria}`); - - const context = { - [field]: criteria, - actual: 0, - stack: inspect(new Error()), - name: fn.name || "", - }; - - // Add the exit listener only once to avoid listener leak warnings - if (mustCallChecks.length === 0) process.on("exit", runCallChecks); - - mustCallChecks.push(context); - - const _return = function () { - // eslint-disable-line func-style - context.actual++; - return fn.apply(this, arguments); - }; - // Function instances have own properties that may be relevant. - // Let's replicate those properties to the returned function. - // Refs: https://tc39.es/ecma262/#sec-function-instances - Object.defineProperties(_return, { - name: { - value: fn.name, - writable: false, - enumerable: false, - configurable: true, - }, - length: { - value: fn.length, - writable: false, - enumerable: false, - configurable: true, - }, - }); - return _return; -} - -function getCallSite(top) { - const originalStackFormatter = Error.prepareStackTrace; - Error.prepareStackTrace = (err, stack) => `${stack[0].getFileName()}:${stack[0].getLineNumber()}`; - const err = new Error(); - Error.captureStackTrace(err, top); - // With the V8 Error API, the stack is not formatted until it is accessed - err.stack; // eslint-disable-line no-unused-expressions - Error.prepareStackTrace = originalStackFormatter; - return err.stack; -} - -function mustNotCall(msg) { - const callSite = getCallSite(mustNotCall); - return function mustNotCall(...args) { - const argsInfo = args.length > 0 ? `\ncalled with arguments: ${args.map(arg => inspect(arg)).join(", ")}` : ""; - assert.fail(`${msg || "function should not have been called"} at ${callSite}` + argsInfo); - }; -} - -function printSkipMessage(msg) { - console.log(`1..0 # Skipped: ${msg}`); -} - -function skip(msg) { - printSkipMessage(msg); - process.exit(0); -} - -function isAlive(pid) { - try { - process.kill(pid, "SIGCONT"); - return true; - } catch { - return false; - } -} - -function skipIf32Bits() { - if (bits < 64) { - skip("The tested feature is not available in 32bit builds"); - } -} - -function skipIfWorker() { - if (!isMainThread) { - skip("This test only works on a main thread"); - } -} - -function skipIfDumbTerminal() { - if (isDumbTerminal) { - skip("skipping - dumb terminal"); - } -} - -const common = { - isAlive, - isDumbTerminal, - isFreeBSD, - isLinux, - isMainThread, - isOpenBSD, - isOSX, - isPi, - isSunOS, - isWindows, - mustCall, - mustNotCall, - mustSucceed, - printSkipMessage, - skip, - skipIf32Bits, - skipIfDumbTerminal, - // On IBMi, process.platform and os.platform() both return 'aix', - // when built with Python versions earlier than 3.9. - // It is not enough to differentiate between IBMi and real AIX system. - get isAIX() { - return require("os").type() === "AIX"; - }, - - get isIBMi() { - return require("os").type() === "OS400"; - }, - - get isLinuxPPCBE() { - return process.platform === "linux" && process.arch === "ppc64" && require("os").endianness() === "BE"; - }, -}; - -const validProperties = new Set(Object.keys(common)); -module.exports = new Proxy(common, { - get(obj, prop) { - if (!validProperties.has(prop)) throw new Error(`Using invalid common property: '${prop}'`); - return obj[prop]; - }, -}); diff --git a/test/js/node/cluster/upstream/common/tmpdir.js b/test/js/node/cluster/upstream/common/tmpdir.js deleted file mode 100644 index 7de0b113a3..0000000000 --- a/test/js/node/cluster/upstream/common/tmpdir.js +++ /dev/null @@ -1,88 +0,0 @@ -"use strict"; - -const { spawnSync } = require("child_process"); -const fs = require("fs"); -const path = require("path"); -const { pathToFileURL } = require("url"); -const { isMainThread } = require("worker_threads"); - -function rmSync(pathname, useSpawn) { - if (useSpawn) { - const escapedPath = pathname.replaceAll("\\", "\\\\"); - spawnSync(process.execPath, [ - "-e", - `require("fs").rmSync("${escapedPath}", { maxRetries: 3, recursive: true, force: true });`, - ]); - } else { - fs.rmSync(pathname, { maxRetries: 3, recursive: true, force: true }); - } -} - -const testRoot = process.env.NODE_TEST_DIR ? fs.realpathSync(process.env.NODE_TEST_DIR) : path.resolve(__dirname, ".."); - -// Using a `.` prefixed name, which is the convention for "hidden" on POSIX, -// gets tools to ignore it by default or by simple rules, especially eslint. -const tmpdirName = ".tmp." + (process.env.TEST_SERIAL_ID || process.env.TEST_THREAD_ID || "0"); -const tmpPath = path.join(testRoot, tmpdirName); - -let firstRefresh = true; -function refresh(useSpawn = false) { - rmSync(tmpPath, useSpawn); - fs.mkdirSync(tmpPath); - - if (firstRefresh) { - firstRefresh = false; - // Clean only when a test uses refresh. This allows for child processes to - // use the tmpdir and only the parent will clean on exit. - process.on("exit", () => { - return onexit(useSpawn); - }); - } -} - -function onexit(useSpawn) { - // Change directory to avoid possible EBUSY - if (isMainThread) process.chdir(testRoot); - - try { - rmSync(tmpPath, useSpawn); - } catch (e) { - console.error("Can't clean tmpdir:", tmpPath); - - const files = fs.readdirSync(tmpPath); - console.error("Files blocking:", files); - - if (files.some(f => f.startsWith(".nfs"))) { - // Warn about NFS "silly rename" - console.error('Note: ".nfs*" might be files that were open and ' + "unlinked but not closed."); - console.error("See http://nfs.sourceforge.net/#faq_d2 for details."); - } - - console.error(); - throw e; - } -} - -function resolve(...paths) { - return path.resolve(tmpPath, ...paths); -} - -function hasEnoughSpace(size) { - const { bavail, bsize } = fs.statfsSync(tmpPath); - return bavail >= Math.ceil(size / bsize); -} - -function fileURL(...paths) { - // When called without arguments, add explicit trailing slash - const fullPath = path.resolve(tmpPath + path.sep, ...paths); - - return pathToFileURL(fullPath); -} - -module.exports = { - fileURL, - hasEnoughSpace, - path: tmpPath, - refresh, - resolve, -}; diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-advanced-serialization.js b/test/js/node/cluster/upstream/parallel/test-cluster-advanced-serialization.js deleted file mode 100644 index 8a368d44c7..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-advanced-serialization.js +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isPrimary) { - cluster.settings.serialization = "advanced"; - const worker = cluster.fork(); - const circular = {}; - circular.circular = circular; - - worker.on( - "online", - common.mustCall(() => { - worker.send(circular); - - worker.on( - "message", - common.mustCall(msg => { - assert.deepStrictEqual(msg, circular); - worker.kill(); - }), - ); - }), - ); -} else { - process.on("message", msg => process.send(msg)); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-bind-privileged-port.js b/test/js/node/cluster/upstream/parallel/test-cluster-bind-privileged-port.js deleted file mode 100644 index f3a788984b..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-bind-privileged-port.js +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const common = require("../common"); -if (common.isLinux) return; // TODO: bun -const assert = require("assert"); -const cluster = require("cluster"); -const net = require("net"); -const { readFileSync } = require("fs"); - -if (common.isLinux) { - try { - const unprivilegedPortStart = parseInt(readFileSync("/proc/sys/net/ipv4/ip_unprivileged_port_start")); - if (unprivilegedPortStart <= 42) { - common.skip("Port 42 is unprivileged"); - } - } catch { - // Do nothing, feature doesn't exist, minimum is 1024 so 42 is usable. - // Continue... - } -} - -// Skip on OS X Mojave. https://github.com/nodejs/node/issues/21679 -if (common.isOSX) common.skip("macOS may allow ordinary processes to use any port"); - -if (common.isIBMi) common.skip("IBMi may allow ordinary processes to use any port"); - -if (common.isWindows) common.skip("not reliable on Windows."); - -if (process.getuid() === 0) common.skip("Test is not supposed to be run as root."); - -if (cluster.isPrimary) { - cluster.fork().on( - "exit", - common.mustCall(exitCode => { - assert.strictEqual(exitCode, 0); - }), - ); -} else { - const s = net.createServer(common.mustNotCall()); - s.listen(42, common.mustNotCall("listen should have failed")); - s.on( - "error", - common.mustCall(err => { - assert.strictEqual(err.code, "EACCES"); - process.disconnect(); - }), - ); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-call-and-destroy.js b/test/js/node/cluster/upstream/parallel/test-cluster-call-and-destroy.js deleted file mode 100644 index 6d9ff44e67..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-call-and-destroy.js +++ /dev/null @@ -1,18 +0,0 @@ -"use strict"; -const common = require("../common"); -const cluster = require("cluster"); -const assert = require("assert"); - -if (cluster.isPrimary) { - const worker = cluster.fork(); - worker.on( - "disconnect", - common.mustCall(() => { - assert.strictEqual(worker.isConnected(), false); - worker.destroy(); - }), - ); -} else { - assert.strictEqual(cluster.worker.isConnected(), true); - cluster.worker.disconnect(); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-child-index-dgram.js b/test/js/node/cluster/upstream/parallel/test-cluster-child-index-dgram.js deleted file mode 100644 index 426c8de9f4..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-child-index-dgram.js +++ /dev/null @@ -1,43 +0,0 @@ -"use strict"; -const common = require("../common"); -const Countdown = require("../common/countdown"); -if (common.isWindows) common.skip("dgram clustering is currently not supported on Windows."); - -const cluster = require("cluster"); -const dgram = require("dgram"); - -// Test an edge case when using `cluster` and `dgram.Socket.bind()` -// the port of `0`. -const kPort = 0; - -function child() { - const kTime = 2; - const countdown = new Countdown(kTime * 2, () => { - process.exit(0); - }); - for (let i = 0; i < kTime; i += 1) { - const socket = new dgram.Socket("udp4"); - socket.bind( - kPort, - common.mustCall(() => { - // `process.nextTick()` or `socket2.close()` would throw - // ERR_SOCKET_DGRAM_NOT_RUNNING - process.nextTick(() => { - socket.close(countdown.dec()); - const socket2 = new dgram.Socket("udp4"); - socket2.bind( - kPort, - common.mustCall(() => { - process.nextTick(() => { - socket2.close(countdown.dec()); - }); - }), - ); - }); - }), - ); - } -} - -if (cluster.isMaster) cluster.fork(__filename); -else child(); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-child-index-net.js b/test/js/node/cluster/upstream/parallel/test-cluster-child-index-net.js deleted file mode 100644 index 961924a2d6..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-child-index-net.js +++ /dev/null @@ -1,35 +0,0 @@ -"use strict"; -const common = require("../common"); -const Countdown = require("../common/countdown"); -const cluster = require("cluster"); -const net = require("net"); - -// Test an edge case when using `cluster` and `net.Server.listen()` to -// the port of `0`. -const kPort = 0; - -function child() { - const kTime = 2; - const countdown = new Countdown(kTime * 2, () => { - process.exit(0); - }); - for (let i = 0; i < kTime; i += 1) { - const server = net.createServer(); - server.listen( - kPort, - common.mustCall(() => { - server.close(countdown.dec()); - const server2 = net.createServer(); - server2.listen( - kPort, - common.mustCall(() => { - server2.close(countdown.dec()); - }), - ); - }), - ); - } -} - -if (cluster.isMaster) cluster.fork(__filename); -else child(); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-concurrent-disconnect.js b/test/js/node/cluster/upstream/parallel/test-cluster-concurrent-disconnect.js deleted file mode 100644 index 1e1daa9ef4..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-concurrent-disconnect.js +++ /dev/null @@ -1,58 +0,0 @@ -"use strict"; - -// Ref: https://github.com/nodejs/node/issues/32106 - -const common = require("../common"); -if (common.isLinux) return; // TODO: bun -if (common.isWindows) return; // TODO: bun - -const assert = require("assert"); -const cluster = require("cluster"); -const os = require("os"); - -if (cluster.isPrimary) { - const workers = []; - const numCPUs = os.availableParallelism(); - let waitOnline = numCPUs; - for (let i = 0; i < numCPUs; i++) { - const worker = cluster.fork(); - workers[i] = worker; - worker.once( - "online", - common.mustCall(() => { - if (--waitOnline === 0) - for (const worker of workers) if (worker.isConnected()) worker.send(i % 2 ? "disconnect" : "destroy"); - }), - ); - - // These errors can occur due to the nature of the test, we might be trying - // to send messages when the worker is disconnecting. - worker.on("error", err => { - assert.strictEqual(err.syscall, "write"); - if (common.isOSX) { - assert(["EPIPE", "ENOTCONN"].includes(err.code), err); - } else { - assert(["EPIPE", "ECONNRESET"].includes(err.code), err); - } - }); - - worker.once( - "disconnect", - common.mustCall(() => { - for (const worker of workers) if (worker.isConnected()) worker.send("disconnect"); - }), - ); - - worker.once( - "exit", - common.mustCall((code, signal) => { - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }), - ); - } -} else { - process.on("message", msg => { - if (cluster.worker.isConnected()) cluster.worker[msg](); - }); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-cwd.js b/test/js/node/cluster/upstream/parallel/test-cluster-cwd.js deleted file mode 100644 index d28bfdf545..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-cwd.js +++ /dev/null @@ -1,29 +0,0 @@ -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); -const tmpdir = require("../common/tmpdir"); - -if (cluster.isPrimary) { - tmpdir.refresh(); - - assert.strictEqual(cluster.settings.cwd, undefined); - cluster.fork().on( - "message", - common.mustCall(msg => { - assert.strictEqual(msg, process.cwd()); - }), - ); - - cluster.setupPrimary({ cwd: tmpdir.path }); - assert.strictEqual(cluster.settings.cwd, tmpdir.path); - cluster.fork().on( - "message", - common.mustCall(msg => { - assert.strictEqual(msg, tmpdir.path); - }), - ); -} else { - process.send(process.cwd()); - process.disconnect(); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-exitedAfterDisconnect-race.js b/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-exitedAfterDisconnect-race.js deleted file mode 100644 index f461734eb6..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-exitedAfterDisconnect-race.js +++ /dev/null @@ -1,33 +0,0 @@ -"use strict"; - -const common = require("../common"); - -// Test should fail in Node.js 5.4.1 and pass in later versions. - -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isPrimary) { - cluster.on("exit", (worker, code) => { - assert.strictEqual(code, 0, `worker exited with code: ${code}, expected 0`); - }); - - return cluster.fork(); -} - -let eventFired = false; - -cluster.worker.disconnect(); - -process.nextTick( - common.mustCall(() => { - assert.ok(!eventFired, "disconnect event should wait for ack"); - }), -); - -cluster.worker.on( - "disconnect", - common.mustCall(() => { - eventFired = true; - }), -); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-leak.js b/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-leak.js deleted file mode 100644 index d1b4812004..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-leak.js +++ /dev/null @@ -1,27 +0,0 @@ -"use strict"; - -// Test fails in Node v5.4.0 and passes in v5.4.1 and newer. - -const common = require("../common"); -const net = require("net"); -const cluster = require("cluster"); - -cluster.schedulingPolicy = cluster.SCHED_NONE; - -if (cluster.isPrimary) { - const worker = cluster.fork(); - - // This is the important part of the test: Confirm that `disconnect` fires. - worker.on("disconnect", common.mustCall()); - - // These are just some extra stuff we're checking for good measure... - worker.on("exit", common.mustCall()); - cluster.on("exit", common.mustCall()); - - cluster.disconnect(); - return; -} - -const server = net.createServer(); - -server.listen(0); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-fork-env.js b/test/js/node/cluster/upstream/parallel/test-cluster-fork-env.js deleted file mode 100644 index 8bf9ef15d7..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-fork-env.js +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -require("../common"); - -// This test checks that arguments provided to cluster.fork() will create -// new environment variables and override existing environment variables -// in the created worker process. - -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isWorker) { - const result = cluster.worker.send({ - prop: process.env.cluster_test_prop, - overwrite: process.env.cluster_test_overwrite, - }); - - assert.strictEqual(result, true); -} else if (cluster.isPrimary) { - const checks = { - using: false, - overwrite: false, - }; - - // To check that the cluster extend on the process.env we will overwrite a - // property - process.env.cluster_test_overwrite = "old"; - - // Fork worker - const worker = cluster.fork({ - "cluster_test_prop": "custom", - "cluster_test_overwrite": "new", - }); - - // Checks worker env - worker.on("message", function (data) { - checks.using = data.prop === "custom"; - checks.overwrite = data.overwrite === "new"; - process.exit(0); - }); - - process.once("exit", function () { - assert.ok(checks.using, "The worker did not receive the correct env."); - assert.ok(checks.overwrite, "The custom environment did not overwrite the existing environment."); - }); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-fork-windowsHide.js b/test/js/node/cluster/upstream/parallel/test-cluster-fork-windowsHide.js deleted file mode 100644 index 273e8146a7..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-fork-windowsHide.js +++ /dev/null @@ -1,76 +0,0 @@ -"use strict"; -const common = require("../common"); -if (common.isWindows) return; // TODO: bun -const assert = require("assert"); -const child_process = require("child_process"); -const cluster = require("cluster"); - -if (!process.argv[2]) { - // It seems Windows only allocate new console window for - // attaching processes spawned by detached processes. i.e. - // - If process D is spawned by process C with `detached: true`, - // and process W is spawned by process D with `detached: false`, - // W will get a new black console window popped up. - // - If D is spawned by C with `detached: false` or W is spawned - // by D with `detached: true`, no console window will pop up for W. - // - // So, we have to spawn a detached process first to run the actual test. - const primary = child_process.spawn(process.argv[0], [process.argv[1], "--cluster"], { - detached: true, - stdio: ["ignore", "ignore", "ignore", "ipc"], - }); - - const messageHandlers = { - workerOnline: common.mustCall(), - mainWindowHandle: common.mustCall(msg => { - assert.match(msg.value, /0\s*/); - }), - workerExit: common.mustCall(msg => { - assert.strictEqual(msg.code, 0); - assert.strictEqual(msg.signal, null); - }), - }; - - primary.on("message", msg => { - const handler = messageHandlers[msg.type]; - assert.ok(handler); - handler(msg); - }); - - primary.on( - "exit", - common.mustCall((code, signal) => { - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }), - ); -} else if (cluster.isPrimary) { - cluster.setupPrimary({ - silent: true, - windowsHide: true, - }); - - const worker = cluster.fork(); - worker.on("exit", (code, signal) => { - process.send({ type: "workerExit", code: code, signal: signal }); - }); - - worker.on("online", msg => { - process.send({ type: "workerOnline" }); - - let output = "0"; - if (process.platform === "win32") { - output = child_process.execSync( - "powershell -NoProfile -c " + `"(Get-Process -Id ${worker.process.pid}).MainWindowHandle"`, - { windowsHide: true, encoding: "utf8" }, - ); - } - - process.send({ type: "mainWindowHandle", value: output }); - worker.send("shutdown"); - }); -} else { - cluster.worker.on("message", msg => { - cluster.worker.disconnect(); - }); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-invalid-message.js b/test/js/node/cluster/upstream/parallel/test-cluster-invalid-message.js deleted file mode 100644 index fdfe1ada62..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-invalid-message.js +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isPrimary) { - const worker = cluster.fork(); - - worker.on( - "exit", - common.mustCall((code, signal) => { - assert.strictEqual(code, 0); - assert.strictEqual(signal, null); - }), - ); - - worker.on("online", () => { - worker.send( - { - cmd: "NODE_CLUSTER", - ack: -1, - }, - () => { - worker.disconnect(); - }, - ); - }); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-kill-disconnect.js b/test/js/node/cluster/upstream/parallel/test-cluster-kill-disconnect.js deleted file mode 100644 index e1c0a313e2..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-kill-disconnect.js +++ /dev/null @@ -1,34 +0,0 @@ -"use strict"; -const common = require("../common"); - -// Check that cluster works perfectly for both `kill` and `disconnect` cases. -// Also take into account that the `disconnect` event may be received after the -// `exit` event. -// https://github.com/nodejs/node/issues/3238 - -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isPrimary) { - function forkWorker(action) { - const worker = cluster.fork({ action }); - worker.on( - "disconnect", - common.mustCall(() => { - assert.strictEqual(worker.exitedAfterDisconnect, true); - }), - ); - - worker.on( - "exit", - common.mustCall(() => { - assert.strictEqual(worker.exitedAfterDisconnect, true); - }), - ); - } - - forkWorker("disconnect"); - forkWorker("kill"); -} else { - cluster.worker[process.env.action](); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-kill-infinite-loop.js b/test/js/node/cluster/upstream/parallel/test-cluster-kill-infinite-loop.js deleted file mode 100644 index 837b11f2a1..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-kill-infinite-loop.js +++ /dev/null @@ -1,27 +0,0 @@ -"use strict"; -const common = require("../common"); -const cluster = require("cluster"); -const assert = require("assert"); - -if (cluster.isPrimary) { - const worker = cluster.fork(); - - worker.on( - "online", - common.mustCall(() => { - // Use worker.process.kill() instead of worker.kill() because the latter - // waits for a graceful disconnect, which will never happen. - worker.process.kill(); - }), - ); - - worker.on( - "exit", - common.mustCall((code, signal) => { - assert.strictEqual(code, null); - assert.strictEqual(signal, "SIGTERM"); - }), - ); -} else { - while (true); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-listening-port.js b/test/js/node/cluster/upstream/parallel/test-cluster-listening-port.js deleted file mode 100644 index ecf9398cd7..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-listening-port.js +++ /dev/null @@ -1,22 +0,0 @@ -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); -const net = require("net"); - -if (cluster.isPrimary) { - cluster.fork(); - cluster.on( - "listening", - common.mustCall(function (worker, address) { - const port = address.port; - // Ensure that the port is not 0 or null - assert(port); - // Ensure that the port is numerical - assert.strictEqual(typeof port, "number"); - worker.kill(); - }), - ); -} else { - net.createServer(common.mustNotCall()).listen(0); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-primary-error.js b/test/js/node/cluster/upstream/parallel/test-cluster-primary-error.js deleted file mode 100644 index 763ae3eab3..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-primary-error.js +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -const totalWorkers = 2; - -// Cluster setup -if (cluster.isWorker) { - const http = require("http"); - http.Server(() => {}).listen(0, "127.0.0.1"); -} else if (process.argv[2] === "cluster") { - // Send PID to testcase process - let forkNum = 0; - cluster.on( - "fork", - common.mustCall(function forkEvent(worker) { - // Send PID - process.send({ - cmd: "worker", - workerPID: worker.process.pid, - }); - - // Stop listening when done - if (++forkNum === totalWorkers) { - cluster.removeListener("fork", forkEvent); - } - }, totalWorkers), - ); - - // Throw accidental error when all workers are listening - let listeningNum = 0; - cluster.on( - "listening", - common.mustCall(function listeningEvent() { - // When all workers are listening - if (++listeningNum === totalWorkers) { - // Stop listening - cluster.removeListener("listening", listeningEvent); - - // Throw accidental error - process.nextTick(() => { - throw new Error("accidental error"); - }); - } - }, totalWorkers), - ); - - // Startup a basic cluster - cluster.fork(); - cluster.fork(); -} else { - // This is the testcase - - const fork = require("child_process").fork; - - // List all workers - const workers = []; - - // Spawn a cluster process - const primary = fork(process.argv[1], ["cluster"], { silent: true }); - - // Handle messages from the cluster - primary.on( - "message", - common.mustCall(data => { - // Add worker pid to list and progress tracker - if (data.cmd === "worker") { - workers.push(data.workerPID); - } - }, totalWorkers), - ); - - // When cluster is dead - primary.on( - "exit", - common.mustCall(code => { - // Check that the cluster died accidentally (non-zero exit code) - assert.strictEqual(code, 1); - - // XXX(addaleax): The fact that this uses raw PIDs makes the test inherently - // flaky – another process might end up being started right after the - // workers finished and receive the same PID. - const pollWorkers = () => { - // When primary is dead all workers should be dead too - if (workers.some(pid => common.isAlive(pid))) { - setTimeout(pollWorkers, 50); - } - }; - - // Loop indefinitely until worker exit - pollWorkers(); - }), - ); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-primary-kill.js b/test/js/node/cluster/upstream/parallel/test-cluster-primary-kill.js deleted file mode 100644 index 1a3a26f34d..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-primary-kill.js +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isWorker) { - // Keep the worker alive - const http = require("http"); - http.Server().listen(0, "127.0.0.1"); -} else if (process.argv[2] === "cluster") { - const worker = cluster.fork(); - - // send PID info to testcase process - process.send({ - pid: worker.process.pid, - }); - - // Terminate the cluster process - worker.once( - "listening", - common.mustCall(() => { - setTimeout(() => { - process.exit(0); - }, 1000); - }), - ); -} else { - // This is the testcase - const fork = require("child_process").fork; - - // Spawn a cluster process - const primary = fork(process.argv[1], ["cluster"]); - - // get pid info - let pid = null; - primary.once("message", data => { - pid = data.pid; - }); - - // When primary is dead - let alive = true; - primary.on( - "exit", - common.mustCall(code => { - // Make sure that the primary died on purpose - assert.strictEqual(code, 0); - - // Check worker process status - const pollWorker = () => { - alive = common.isAlive(pid); - if (alive) { - setTimeout(pollWorker, 50); - } - }; - // Loop indefinitely until worker exit. - pollWorker(); - }), - ); - - process.once("exit", () => { - assert.strictEqual(typeof pid, "number", `got ${pid} instead of a worker pid`); - assert.strictEqual(alive, false, `worker was alive after primary died (alive = ${alive})`); - }); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-process-disconnect.js b/test/js/node/cluster/upstream/parallel/test-cluster-process-disconnect.js deleted file mode 100644 index bcaf7df146..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-process-disconnect.js +++ /dev/null @@ -1,24 +0,0 @@ -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isPrimary) { - const worker = cluster.fork(); - worker.on( - "exit", - common.mustCall((code, signal) => { - assert.strictEqual(code, 0, `Worker did not exit normally with code: ${code}`); - assert.strictEqual(signal, null, `Worker did not exit normally with signal: ${signal}`); - }), - ); -} else { - const net = require("net"); - const server = net.createServer(); - server.listen( - 0, - common.mustCall(() => { - process.disconnect(); - }), - ); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-rr-handle-keep-loop-alive.js b/test/js/node/cluster/upstream/parallel/test-cluster-rr-handle-keep-loop-alive.js deleted file mode 100644 index 8bb183af33..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-rr-handle-keep-loop-alive.js +++ /dev/null @@ -1,26 +0,0 @@ -"use strict"; - -const common = require("../common"); -const cluster = require("cluster"); -const net = require("net"); -const assert = require("assert"); - -cluster.schedulingPolicy = cluster.SCHED_RR; - -if (cluster.isPrimary) { - let exited = false; - const worker = cluster.fork(); - worker.on("exit", () => { - exited = true; - }); - setTimeout(() => { - assert.ok(!exited); - worker.kill(); - }, 3000); -} else { - const server = net.createServer(common.mustNotCall()); - server.listen( - 0, - common.mustCall(() => process.channel.unref()), - ); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-rr-ref.js b/test/js/node/cluster/upstream/parallel/test-cluster-rr-ref.js deleted file mode 100644 index d5f0cbd083..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-rr-ref.js +++ /dev/null @@ -1,20 +0,0 @@ -"use strict"; - -const common = require("../common"); -const cluster = require("cluster"); -const net = require("net"); - -if (cluster.isPrimary) { - cluster.fork().on("message", function (msg) { - if (msg === "done") this.kill(); - }); -} else { - const server = net.createServer(common.mustNotCall()); - server.listen(0, function () { - server.unref(); - server.ref(); - server.close(function () { - process.send("done"); - }); - }); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-send-deadlock.js b/test/js/node/cluster/upstream/parallel/test-cluster-send-deadlock.js deleted file mode 100644 index c5838a666c..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-send-deadlock.js +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Testing mutual send of handles: from primary to worker, and from worker to -// primary. - -require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); -const net = require("net"); - -if (cluster.isPrimary) { - const worker = cluster.fork(); - worker.on("exit", (code, signal) => { - assert.strictEqual(code, 0, `Worker exited with an error code: ${code}`); - assert(!signal, `Worker exited by a signal: ${signal}`); - server.close(); - }); - - const server = net.createServer(socket => { - worker.send("handle", socket); - }); - - server.listen(0, () => { - worker.send({ message: "listen", port: server.address().port }); - }); -} else { - process.on("message", (msg, handle) => { - if (msg.message && msg.message === "listen") { - assert(msg.port); - const client1 = net.connect( - { - host: "localhost", - port: msg.port, - }, - () => { - const client2 = net.connect( - { - host: "localhost", - port: msg.port, - }, - () => { - client1.on("close", onclose); - client2.on("close", onclose); - client1.end(); - client2.end(); - }, - ); - }, - ); - let waiting = 2; - const onclose = () => { - if (--waiting === 0) cluster.worker.disconnect(); - }; - } else { - process.send("reply", handle); - } - }); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-argv.js b/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-argv.js deleted file mode 100644 index 8908aa7372..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-argv.js +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -setTimeout(common.mustNotCall("setup not emitted"), 1000).unref(); - -cluster.on( - "setup", - common.mustCall(function () { - const clusterArgs = cluster.settings.args; - const realArgs = process.argv; - assert.strictEqual(clusterArgs[clusterArgs.length - 1], realArgs[realArgs.length - 1]); - }), -); - -assert.notStrictEqual(process.argv[process.argv.length - 1], "OMG,OMG"); -process.argv.push("OMG,OMG"); -process.argv.push("OMG,OMG"); -cluster.setupPrimary(); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-cumulative.js b/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-cumulative.js deleted file mode 100644 index f9b43121fb..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-cumulative.js +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -assert(cluster.isPrimary); - -// cluster.settings should not be initialized until needed -assert.deepStrictEqual(cluster.settings, {}); - -cluster.setupPrimary(); -assert.deepStrictEqual(cluster.settings, { - args: process.argv.slice(2), - exec: process.argv[1], - execArgv: process.execArgv, - silent: false, -}); -console.log("ok sets defaults"); - -cluster.setupPrimary({ exec: "overridden" }); -assert.strictEqual(cluster.settings.exec, "overridden"); -console.log("ok overrides defaults"); - -cluster.setupPrimary({ args: ["foo", "bar"] }); -assert.strictEqual(cluster.settings.exec, "overridden"); -assert.deepStrictEqual(cluster.settings.args, ["foo", "bar"]); - -cluster.setupPrimary({ execArgv: ["baz", "bang"] }); -assert.strictEqual(cluster.settings.exec, "overridden"); -assert.deepStrictEqual(cluster.settings.args, ["foo", "bar"]); -assert.deepStrictEqual(cluster.settings.execArgv, ["baz", "bang"]); -console.log("ok preserves unchanged settings on repeated calls"); - -cluster.setupPrimary(); -assert.deepStrictEqual(cluster.settings, { - args: ["foo", "bar"], - exec: "overridden", - execArgv: ["baz", "bang"], - silent: false, -}); -console.log("ok preserves current settings"); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-emit.js b/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-emit.js deleted file mode 100644 index 305ebfced2..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-emit.js +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -assert(cluster.isPrimary); - -function emitAndCatch(next) { - cluster.once( - "setup", - common.mustCall(function (settings) { - assert.strictEqual(settings.exec, "new-exec"); - setImmediate(next); - }), - ); - cluster.setupPrimary({ exec: "new-exec" }); -} - -function emitAndCatch2(next) { - cluster.once( - "setup", - common.mustCall(function (settings) { - assert("exec" in settings); - setImmediate(next); - }), - ); - cluster.setupPrimary(); -} - -emitAndCatch( - common.mustCall(function () { - emitAndCatch2(common.mustCall()); - }), -); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary.js b/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary.js deleted file mode 100644 index ccb103cf08..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary.js +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isWorker) { - // Just keep the worker alive - process.send(process.argv[2]); -} else if (cluster.isPrimary) { - const checks = { - args: false, - setupEvent: false, - settingsObject: false, - }; - - const totalWorkers = 2; - let settings; - - // Setup primary - cluster.setupPrimary({ - args: ["custom argument"], - silent: true, - }); - - cluster.once("setup", function () { - checks.setupEvent = true; - - settings = cluster.settings; - if ( - settings && - settings.args && - settings.args[0] === "custom argument" && - settings.silent === true && - settings.exec === process.argv[1] - ) { - checks.settingsObject = true; - } - }); - - let correctInput = 0; - - cluster.on( - "online", - common.mustCall(function listener(worker) { - worker.once("message", function (data) { - correctInput += data === "custom argument" ? 1 : 0; - if (correctInput === totalWorkers) { - checks.args = true; - } - worker.kill(); - }); - }, totalWorkers), - ); - - // Start all workers - cluster.fork(); - cluster.fork(); - - // Check all values - process.once("exit", function () { - const argsMsg = - "Arguments was not send for one or more worker. " + - `${correctInput} workers receive argument, ` + - `but ${totalWorkers} were expected.`; - assert.ok(checks.args, argsMsg); - - assert.ok(checks.setupEvent, "The setup event was never emitted"); - - const settingObjectMsg = "The settingsObject do not have correct " + `properties : ${JSON.stringify(settings)}`; - assert.ok(checks.settingsObject, settingObjectMsg); - }); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-shared-handle-bind-privileged-port.js b/test/js/node/cluster/upstream/parallel/test-cluster-shared-handle-bind-privileged-port.js deleted file mode 100644 index e69c79d697..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-shared-handle-bind-privileged-port.js +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const common = require("../common"); -if (common.isLinux) return; // TODO: bun - -// Skip on OS X Mojave. https://github.com/nodejs/node/issues/21679 -if (common.isOSX) common.skip("macOS may allow ordinary processes to use any port"); - -if (common.isIBMi) common.skip("IBMi may allow ordinary processes to use any port"); - -if (common.isWindows) common.skip("not reliable on Windows"); - -if (process.getuid() === 0) common.skip("as this test should not be run as `root`"); - -const assert = require("assert"); -const cluster = require("cluster"); -const net = require("net"); - -if (cluster.isPrimary) { - // Primary opens and binds the socket and shares it with the worker. - cluster.schedulingPolicy = cluster.SCHED_NONE; - cluster.fork().on( - "exit", - common.mustCall(function (exitCode) { - assert.strictEqual(exitCode, 0); - }), - ); -} else { - const s = net.createServer(common.mustNotCall()); - s.listen(42, common.mustNotCall("listen should have failed")); - s.on( - "error", - common.mustCall(function (err) { - assert.strictEqual(err.code, "EACCES"); - process.disconnect(); - }), - ); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-uncaught-exception.js b/test/js/node/cluster/upstream/parallel/test-cluster-uncaught-exception.js deleted file mode 100644 index ee1dee617e..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-uncaught-exception.js +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Installing a custom uncaughtException handler should override the default -// one that the cluster module installs. -// https://github.com/joyent/node/issues/2556 - -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); -const fork = require("child_process").fork; - -const MAGIC_EXIT_CODE = 42; - -const isTestRunner = process.argv[2] !== "child"; - -if (isTestRunner) { - const primary = fork(__filename, ["child"]); - primary.on( - "exit", - common.mustCall(code => { - assert.strictEqual(code, MAGIC_EXIT_CODE); - }), - ); -} else if (cluster.isPrimary) { - process.on( - "uncaughtException", - common.mustCall(() => { - process.nextTick(() => process.exit(MAGIC_EXIT_CODE)); - }), - ); - cluster.fork(); - throw new Error("kill primary"); -} else { - // worker - process.exit(); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-death.js b/test/js/node/cluster/upstream/parallel/test-cluster-worker-death.js deleted file mode 100644 index bab5c8df8a..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-death.js +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -if (!cluster.isPrimary) { - process.exit(42); -} else { - const worker = cluster.fork(); - worker.on( - "exit", - common.mustCall(function (exitCode, signalCode) { - assert.strictEqual(exitCode, 42); - assert.strictEqual(signalCode, null); - }), - ); - cluster.on( - "exit", - common.mustCall(function (worker_) { - assert.strictEqual(worker_, worker); - }), - ); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-disconnect-on-error.js b/test/js/node/cluster/upstream/parallel/test-cluster-worker-disconnect-on-error.js deleted file mode 100644 index f9e3a0de2c..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-disconnect-on-error.js +++ /dev/null @@ -1,44 +0,0 @@ -"use strict"; -const common = require("../common"); -const http = require("http"); -const cluster = require("cluster"); -const assert = require("assert"); - -cluster.schedulingPolicy = cluster.SCHED_NONE; - -const server = http.createServer(); -if (cluster.isPrimary) { - let worker; - - server.listen( - 0, - common.mustSucceed(() => { - assert(worker); - - worker.send({ port: server.address().port }); - }), - ); - - worker = cluster.fork(); - worker.on( - "exit", - common.mustCall(() => { - server.close(); - }), - ); -} else { - process.on( - "message", - common.mustCall(msg => { - assert(msg.port); - - server.listen(msg.port); - server.on( - "error", - common.mustCall(e => { - cluster.worker.disconnect(); - }), - ); - }), - ); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-disconnect.js b/test/js/node/cluster/upstream/parallel/test-cluster-worker-disconnect.js deleted file mode 100644 index 35cae334d9..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-disconnect.js +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isWorker) { - const http = require("http"); - http.Server(() => {}).listen(0, "127.0.0.1"); - - cluster.worker.on( - "disconnect", - common.mustCall(() => { - process.exit(42); - }), - ); -} else if (cluster.isPrimary) { - const checks = { - cluster: { - emitDisconnect: false, - emitExit: false, - callback: false, - }, - worker: { - emitDisconnect: false, - emitDisconnectInsideWorker: false, - emitExit: false, - state: false, - voluntaryMode: false, - died: false, - }, - }; - - // start worker - const worker = cluster.fork(); - - // Disconnect worker when it is ready - worker.once( - "listening", - common.mustCall(() => { - const w = worker.disconnect(); - assert.strictEqual(worker, w, `${worker.id} did not return a reference`); - }), - ); - - // Check cluster events - cluster.once( - "disconnect", - common.mustCall(() => { - checks.cluster.emitDisconnect = true; - }), - ); - cluster.once( - "exit", - common.mustCall(() => { - checks.cluster.emitExit = true; - }), - ); - - // Check worker events and properties - worker.once( - "disconnect", - common.mustCall(() => { - checks.worker.emitDisconnect = true; - checks.worker.voluntaryMode = worker.exitedAfterDisconnect; - checks.worker.state = worker.state; - }), - ); - - // Check that the worker died - worker.once( - "exit", - common.mustCall(code => { - checks.worker.emitExit = true; - checks.worker.died = !common.isAlive(worker.process.pid); - checks.worker.emitDisconnectInsideWorker = code === 42; - }), - ); - - process.once("exit", () => { - const w = checks.worker; - const c = checks.cluster; - - // events - assert.ok(w.emitDisconnect, "Disconnect event did not emit"); - assert.ok(w.emitDisconnectInsideWorker, "Disconnect event did not emit inside worker"); - assert.ok(c.emitDisconnect, "Disconnect event did not emit"); - assert.ok(w.emitExit, "Exit event did not emit"); - assert.ok(c.emitExit, "Exit event did not emit"); - - // flags - assert.strictEqual(w.state, "disconnected"); - assert.strictEqual(w.voluntaryMode, true); - - // is process alive - assert.ok(w.died, "The worker did not die"); - }); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-exit.js b/test/js/node/cluster/upstream/parallel/test-cluster-worker-exit.js deleted file mode 100644 index e6e61ca604..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-exit.js +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// test-cluster-worker-exit.js -// verifies that, when a child process exits (by calling `process.exit(code)`) -// - the primary receives the proper events in the proper order, no duplicates -// - the exitCode and signalCode are correct in the 'exit' event -// - the worker.exitedAfterDisconnect flag, and worker.state are correct -// - the worker process actually goes away - -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -const EXIT_CODE = 42; - -if (cluster.isWorker) { - const http = require("http"); - const server = http.Server(() => {}); - - server.once( - "listening", - common.mustCall(() => { - process.exit(EXIT_CODE); - }), - ); - server.listen(0, "127.0.0.1"); -} else if (cluster.isPrimary) { - const expected_results = { - cluster_emitDisconnect: [1, "the cluster did not emit 'disconnect'"], - cluster_emitExit: [1, "the cluster did not emit 'exit'"], - cluster_exitCode: [EXIT_CODE, "the cluster exited w/ incorrect exitCode"], - cluster_signalCode: [null, "the cluster exited w/ incorrect signalCode"], - worker_emitDisconnect: [1, "the worker did not emit 'disconnect'"], - worker_emitExit: [1, "the worker did not emit 'exit'"], - worker_state: ["disconnected", "the worker state is incorrect"], - worker_exitedAfterDisconnect: [false, "the .exitedAfterDisconnect flag is incorrect"], - worker_died: [true, "the worker is still running"], - worker_exitCode: [EXIT_CODE, "the worker exited w/ incorrect exitCode"], - worker_signalCode: [null, "the worker exited w/ incorrect signalCode"], - }; - const results = { - cluster_emitDisconnect: 0, - cluster_emitExit: 0, - worker_emitDisconnect: 0, - worker_emitExit: 0, - }; - - // start worker - const worker = cluster.fork(); - - // Check cluster events - cluster.on( - "disconnect", - common.mustCall(() => { - results.cluster_emitDisconnect += 1; - }), - ); - cluster.on( - "exit", - common.mustCall(worker => { - results.cluster_exitCode = worker.process.exitCode; - results.cluster_signalCode = worker.process.signalCode; - results.cluster_emitExit += 1; - }), - ); - - // Check worker events and properties - worker.on( - "disconnect", - common.mustCall(() => { - results.worker_emitDisconnect += 1; - results.worker_exitedAfterDisconnect = worker.exitedAfterDisconnect; - results.worker_state = worker.state; - if (results.worker_emitExit > 0) { - process.nextTick(() => finish_test()); - } - }), - ); - - // Check that the worker died - worker.once( - "exit", - common.mustCall((exitCode, signalCode) => { - results.worker_exitCode = exitCode; - results.worker_signalCode = signalCode; - results.worker_emitExit += 1; - results.worker_died = !common.isAlive(worker.process.pid); - if (results.worker_emitDisconnect > 0) { - process.nextTick(() => finish_test()); - } - }), - ); - - const finish_test = () => { - try { - checkResults(expected_results, results); - } catch (exc) { - if (exc.name !== "AssertionError") { - console.trace(exc); - } - - process.exit(1); - return; - } - process.exit(0); - }; -} - -// Some helper functions ... - -function checkResults(expected_results, results) { - for (const k in expected_results) { - const actual = results[k]; - const expected = expected_results[k]; - - assert.strictEqual( - actual, - expected && expected.length ? expected[0] : expected, - `${expected[1] || ""} [expected: ${expected[0]} / actual: ${actual}]`, - ); - } -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-isdead.js b/test/js/node/cluster/upstream/parallel/test-cluster-worker-isdead.js deleted file mode 100644 index 079a154443..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-isdead.js +++ /dev/null @@ -1,34 +0,0 @@ -"use strict"; -require("../common"); -const cluster = require("cluster"); -const assert = require("assert"); - -if (cluster.isPrimary) { - const worker = cluster.fork(); - let workerDead = worker.isDead(); - assert.ok( - !workerDead, - `isDead() returned ${workerDead}. isDead() should return ` + "false right after the worker has been created.", - ); - - worker.on("exit", function () { - workerDead = worker.isDead(); - assert.ok( - workerDead, - `isDead() returned ${workerDead}. After an event has been ` + "emitted, isDead should return true", - ); - }); - - worker.on("message", function (msg) { - if (msg === "readyToDie") { - worker.kill(); - } - }); -} else if (cluster.isWorker) { - const workerDead = cluster.worker.isDead(); - assert.ok( - !workerDead, - `isDead() returned ${workerDead}. isDead() should return ` + "false when called from within a worker", - ); - process.send("readyToDie"); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-kill-signal.js b/test/js/node/cluster/upstream/parallel/test-cluster-worker-kill-signal.js deleted file mode 100644 index 1562a5e9f3..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-kill-signal.js +++ /dev/null @@ -1,56 +0,0 @@ -"use strict"; -// test-cluster-worker-kill-signal.js -// verifies that when we're killing a worker using Worker.prototype.kill -// and the worker's process was killed with the given signal (SIGKILL) - -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isWorker) { - // Make the worker run something - const http = require("http"); - const server = http.Server(() => {}); - - server.once("listening", common.mustCall()); - server.listen(0, "127.0.0.1"); -} else if (cluster.isMaster) { - const KILL_SIGNAL = "SIGKILL"; - - // Start worker - const worker = cluster.fork(); - - // When the worker is up and running, kill it - worker.once( - "listening", - common.mustCall(() => { - worker.kill(KILL_SIGNAL); - }), - ); - - // Check worker events and properties - worker.on( - "disconnect", - common.mustCall(() => { - assert.strictEqual(worker.exitedAfterDisconnect, false); - assert.strictEqual(worker.state, "disconnected"); - }, 1), - ); - - // Check that the worker died - worker.once( - "exit", - common.mustCall((exitCode, signalCode) => { - const isWorkerProcessStillAlive = common.isAlive(worker.process.pid); - const numOfRunningWorkers = Object.keys(cluster.workers).length; - - assert.strictEqual(exitCode, null); - assert.strictEqual(signalCode, KILL_SIGNAL); - assert.strictEqual(isWorkerProcessStillAlive, false); - assert.strictEqual(numOfRunningWorkers, 0); - }, 1), - ); - - // Check if the cluster was killed as well - cluster.on("exit", common.mustCall(1)); -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-kill.js b/test/js/node/cluster/upstream/parallel/test-cluster-worker-kill.js deleted file mode 100644 index 1ba588b874..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-kill.js +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// test-cluster-worker-kill.js -// verifies that, when a child process is killed (we use SIGKILL) -// - the primary receives the proper events in the proper order, no duplicates -// - the exitCode and signalCode are correct in the 'exit' event -// - the worker.exitedAfterDisconnect flag, and worker.state are correct -// - the worker process actually goes away - -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); - -if (cluster.isWorker) { - const http = require("http"); - const server = http.Server(() => {}); - server.once("listening", common.mustCall()); - server.listen(0, "127.0.0.1"); -} else if (cluster.isPrimary) { - const KILL_SIGNAL = "SIGKILL"; - const expected_results = { - cluster_emitDisconnect: [1, "the cluster did not emit 'disconnect'"], - cluster_emitExit: [1, "the cluster did not emit 'exit'"], - cluster_exitCode: [null, "the cluster exited w/ incorrect exitCode"], - cluster_signalCode: [KILL_SIGNAL, "the cluster exited w/ incorrect signalCode"], - worker_emitDisconnect: [1, "the worker did not emit 'disconnect'"], - worker_emitExit: [1, "the worker did not emit 'exit'"], - worker_state: ["disconnected", "the worker state is incorrect"], - worker_exitedAfter: [false, "the .exitedAfterDisconnect flag is incorrect"], - worker_died: [true, "the worker is still running"], - worker_exitCode: [null, "the worker exited w/ incorrect exitCode"], - worker_signalCode: [KILL_SIGNAL, "the worker exited w/ incorrect signalCode"], - }; - const results = { - cluster_emitDisconnect: 0, - cluster_emitExit: 0, - worker_emitDisconnect: 0, - worker_emitExit: 0, - }; - - // start worker - const worker = cluster.fork(); - // When the worker is up and running, kill it - worker.once( - "listening", - common.mustCall(() => { - worker.process.kill(KILL_SIGNAL); - }), - ); - - // Check cluster events - cluster.on( - "disconnect", - common.mustCall(() => { - results.cluster_emitDisconnect += 1; - }), - ); - cluster.on( - "exit", - common.mustCall(worker => { - results.cluster_exitCode = worker.process.exitCode; - results.cluster_signalCode = worker.process.signalCode; - results.cluster_emitExit += 1; - }), - ); - - // Check worker events and properties - worker.on( - "disconnect", - common.mustCall(() => { - results.worker_emitDisconnect += 1; - results.worker_exitedAfter = worker.exitedAfterDisconnect; - results.worker_state = worker.state; - }), - ); - - // Check that the worker died - worker.once( - "exit", - common.mustCall((exitCode, signalCode) => { - results.worker_exitCode = exitCode; - results.worker_signalCode = signalCode; - results.worker_emitExit += 1; - results.worker_died = !common.isAlive(worker.process.pid); - }), - ); - - process.on("exit", () => { - checkResults(expected_results, results); - }); -} - -// Some helper functions ... - -function checkResults(expected_results, results) { - for (const k in expected_results) { - const actual = results[k]; - const expected = expected_results[k]; - - assert.strictEqual( - actual, - expected && expected.length ? expected[0] : expected, - `${expected[1] || ""} [expected: ${expected[0]} / actual: ${actual}]`, - ); - } -} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-no-exit.js b/test/js/node/cluster/upstream/parallel/test-cluster-worker-no-exit.js deleted file mode 100644 index 8dcfc45f2c..0000000000 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-no-exit.js +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const common = require("../common"); -if (common.isOSX) return; // TODO: bun -if (common.isLinux) return; // TODO: bun -if (common.isWindows) return; // TODO: bun -const assert = require("assert"); -const cluster = require("cluster"); -const net = require("net"); - -let destroyed; -let success; -let worker; -let server; - -// Workers do not exit on disconnect, they exit under normal node rules: when -// they have nothing keeping their loop alive, like an active connection -// -// test this by: -// -// 1 creating a server, so worker can make a connection to something -// 2 disconnecting worker -// 3 wait to confirm it did not exit -// 4 destroy connection -// 5 confirm it does exit -if (cluster.isPrimary) { - server = net - .createServer(function (conn) { - server.close(); - worker.disconnect(); - worker - .once("disconnect", function () { - setTimeout(function () { - conn.destroy(); - destroyed = true; - }, 1000); - }) - .once("exit", function () { - // Worker should not exit while it has a connection - assert(destroyed, "worker exited before connection destroyed"); - success = true; - }); - }) - .listen(0, function () { - const port = this.address().port; - - worker = cluster.fork().on("online", function () { - this.send({ port }); - }); - }); - process.on("exit", function () { - assert(success); - }); -} else { - process.on("message", function (msg) { - // We shouldn't exit, not while a network connection exists - net.connect(msg.port); - }); -} diff --git a/test/js/node/crypto/node-crypto.test.js b/test/js/node/crypto/node-crypto.test.js index ba7386a333..f8cb1fb62f 100644 --- a/test/js/node/crypto/node-crypto.test.js +++ b/test/js/node/crypto/node-crypto.test.js @@ -1,7 +1,7 @@ import { describe, expect, it } from "bun:test"; import crypto from "node:crypto"; -import { PassThrough } from "node:stream"; +import { PassThrough, Readable } from "node:stream"; import util from "node:util"; it("crypto.randomBytes should return a Buffer", () => { @@ -478,6 +478,27 @@ describe("createHash", () => { copy.update("world"); expect(copy.digest("hex")).toBe(hash.digest("hex")); }); + + it("uses the Transform options object", () => { + const hasher = crypto.createHash("sha256", { defaultEncoding: "binary" }); + hasher.on("readable", () => { + const data = hasher.read(); + if (data) { + expect(data.toString("hex")).toBe("4d4d75d742863ab9656f3d5f76dff8589c3922e95a24ea6812157ffe4aaa3b6b"); + } + }); + const stream = Readable.from("ï"); + stream.pipe(hasher); + }); +}); + +describe("Hash", () => { + it("should have correct method names", () => { + const hash = crypto.createHash("sha256"); + expect(hash.update.name).toBe("update"); + expect(hash.digest.name).toBe("digest"); + expect(hash.copy.name).toBe("copy"); + }); }); it("crypto.createHmac", () => { diff --git a/test/js/node/http/node-http.test.ts b/test/js/node/http/node-http.test.ts index 7400f06eb3..1457d2e862 100644 --- a/test/js/node/http/node-http.test.ts +++ b/test/js/node/http/node-http.test.ts @@ -2435,3 +2435,124 @@ it("should work when sending https.request with agent:false", async () => { await promise; }); +it("client should use chunked encoded if more than one write is called", async () => { + function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } + // Bun.serve is used here until #15576 or similar fix is merged + using server = Bun.serve({ + port: 0, + hostname: "127.0.0.1", + fetch(req) { + if (req.headers.get("transfer-encoding") !== "chunked") { + return new Response("should be chunked encoding", { status: 500 }); + } + return new Response(req.body); + }, + }); + + // Options for the HTTP request + const options = { + hostname: "127.0.0.1", // Replace with the target server + port: server.port, + path: "/api/data", + method: "POST", + headers: { + "Content-Type": "application/json", + }, + }; + + const { promise, resolve, reject } = Promise.withResolvers(); + + // Create the request + const req = http.request(options, res => { + if (res.statusCode !== 200) { + reject(new Error("Body should be chunked")); + } + const chunks = []; + // Collect the response data + res.on("data", chunk => { + chunks.push(chunk); + }); + + res.on("end", () => { + resolve(chunks); + }); + }); + + // Handle errors + req.on("error", reject); + + // Write chunks to the request body + req.write("Hello"); + req.write(" "); + await sleep(100); + req.write("World"); + req.write(" "); + await sleep(100); + req.write("BUN!"); + // End the request and signal no more data will be sent + req.end(); + + const chunks = await promise; + expect(chunks.length).toBeGreaterThan(1); + expect(chunks[chunks.length - 1]?.toString()).toEndWith("BUN!"); + expect(Buffer.concat(chunks).toString()).toBe("Hello World BUN!"); +}); + +it("client should use content-length if only one write is called", async () => { + await using server = http.createServer((req, res) => { + if (req.headers["transfer-encoding"] === "chunked") { + return res.writeHead(500).end(); + } + res.writeHead(200); + req.on("data", data => { + res.write(data); + }); + req.on("end", () => { + res.end(); + }); + }); + + await once(server.listen(0, "127.0.0.1"), "listening"); + + // Options for the HTTP request + const options = { + hostname: "127.0.0.1", // Replace with the target server + port: server.address().port, + path: "/api/data", + method: "POST", + headers: { + "Content-Type": "application/json", + }, + }; + + const { promise, resolve, reject } = Promise.withResolvers(); + + // Create the request + const req = http.request(options, res => { + if (res.statusCode !== 200) { + reject(new Error("Body should not be chunked")); + } + const chunks = []; + // Collect the response data + res.on("data", chunk => { + chunks.push(chunk); + }); + + res.on("end", () => { + resolve(chunks); + }); + }); + // Handle errors + req.on("error", reject); + // Write chunks to the request body + req.write("Hello World BUN!"); + // End the request and signal no more data will be sent + req.end(); + + const chunks = await promise; + expect(chunks.length).toBe(1); + expect(chunks[0]?.toString()).toBe("Hello World BUN!"); + expect(Buffer.concat(chunks).toString()).toBe("Hello World BUN!"); +}); diff --git a/test/js/node/net/node-net-allowHalfOpen.test.js b/test/js/node/net/node-net-allowHalfOpen.test.js new file mode 100644 index 0000000000..3485bdc37b --- /dev/null +++ b/test/js/node/net/node-net-allowHalfOpen.test.js @@ -0,0 +1,115 @@ +import net from "node:net"; +import { tempDirWithFiles, nodeExe } from "harness"; +import { expect, test } from "bun:test"; + +async function nodeRun(callback, clients = 1) { + const cwd = tempDirWithFiles("server", { + "index.mjs": ` + import net from "node:net"; + let clients = ${clients}; + const server = net.createServer({ allowHalfOpen: true }, socket => { + // Listen for data from the client + socket.on("data", data => { + console.log(data.toString()); + }); + + socket.on("end", () => { + console.log("Received FIN"); + if(--clients == 0) { + server.close(); + } + }); + socket.on("error", console.error); + + // start sending FIN + socket.end(); + }); + server.listen(0, "127.0.0.1", ()=> { + console.log(server.address().port?.toString()); + }) + `, + }); + const process = Bun.spawn([nodeExe(), "index.mjs"], { + cwd, + stdin: "ignore", + stdout: "pipe", + stderr: "pipe", + }); + + const reader = process.stdout.getReader(); + let continueReading = true; + let stdout = ""; + let port = 0; + do { + const { done, value } = await reader.read(); + + continueReading = !done; + const decoder = new TextDecoder(); + if (value) { + if (!port) { + port = parseInt(decoder.decode(value), 10); + callback(port); + } else { + stdout += decoder.decode(value); + } + } + } while (continueReading); + + return { + stdout, + stderr: (await Bun.readableStreamToText(process.stderr)).trim(), + code: await process.exited, + }; +} + +async function doHalfOpenRequest(port, allowHalfOpen) { + const { promise, resolve, reject } = Promise.withResolvers(); + + const client = net.connect({ host: "127.0.0.1", port, allowHalfOpen }, () => { + client.write("Hello, World"); + }); + client.on("error", reject); + client.on("close", resolve); + client.on("end", () => { + // delay the write response + setTimeout(() => { + client.write("Write after end"); + client.end(); + }, 10); + }); + await promise; +} + +test("allowHalfOpen: true should work on client-side", async () => { + const { promise: portPromise, resolve } = Promise.withResolvers(); + const process = nodeRun(resolve, 1); + + const port = await portPromise; + await doHalfOpenRequest(port, true); + const result = await process; + expect(result.code).toBe(0); + expect(result.stderr).toBe(""); + expect( + result.stdout + .split("\n") + .map(s => s.trim()) + .filter(s => s), + ).toEqual(["Hello, World", "Write after end", "Received FIN"]); +}); + +test("allowHalfOpen: false should work on client-side", async () => { + const { promise: portPromise, resolve } = Promise.withResolvers(); + const process = nodeRun(resolve, 1); + + const port = await portPromise; + await doHalfOpenRequest(port, false); + const result = await process; + expect(result.code).toBe(0); + expect(result.stderr).toBe(""); + expect( + result.stdout + .split("\n") + .map(s => s.trim()) + .filter(s => s), + ).toEqual(["Hello, World", "Received FIN"]); +}); diff --git a/test/js/node/process/process.test.js b/test/js/node/process/process.test.js index a2dac046fa..09f9bb28f4 100644 --- a/test/js/node/process/process.test.js +++ b/test/js/node/process/process.test.js @@ -118,11 +118,15 @@ it("process.chdir() on root dir", () => { } }); -it("process.hrtime()", () => { +it("process.hrtime()", async () => { const start = process.hrtime(); const end = process.hrtime(start); - const end2 = process.hrtime(); expect(end[0]).toBe(0); + + // Flaky on Ubuntu. + await Bun.sleep(0); + const end2 = process.hrtime(); + expect(end2[1] > start[1]).toBe(true); }); diff --git a/test/js/node/test/common/assertSnapshot.js b/test/js/node/test/common/assertSnapshot.js index 88f40281e0..a22455160b 100644 --- a/test/js/node/test/common/assertSnapshot.js +++ b/test/js/node/test/common/assertSnapshot.js @@ -25,7 +25,7 @@ function replaceWindowsPaths(str) { } function replaceFullPaths(str) { - return str.replaceAll(process.cwd(), ''); + return str.replaceAll(path.resolve(__dirname, '../..'), ''); } function transform(...args) { @@ -78,8 +78,11 @@ async function spawnAndAssert(filename, transform = (x) => x, { tty = false, ... return; } const flags = common.parseTestFlags(filename); - const executable = tty ? 'tools/pseudo-tty.py' : process.execPath; - const args = tty ? [process.execPath, ...flags, filename] : [...flags, filename]; + const executable = tty ? (process.env.PYTHON || 'python3') : process.execPath; + const args = + tty ? + [path.join(__dirname, '../..', 'tools/pseudo-tty.py'), process.execPath, ...flags, filename] : + [...flags, filename]; const { stdout, stderr } = await common.spawnPromisified(executable, args, options); await assertSnapshot(transform(`${stdout}${stderr}`), filename); } diff --git a/test/js/node/test/common/duplexpair.js b/test/js/node/test/common/duplexpair.js deleted file mode 100644 index 1f41ed32f1..0000000000 --- a/test/js/node/test/common/duplexpair.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; -const { Duplex } = require('stream'); -const assert = require('assert'); - -const kCallback = Symbol('Callback'); -const kOtherSide = Symbol('Other'); - -class DuplexSocket extends Duplex { - constructor() { - super(); - this[kCallback] = null; - this[kOtherSide] = null; - } - - _read() { - const callback = this[kCallback]; - if (callback) { - this[kCallback] = null; - callback(); - } - } - - _write(chunk, encoding, callback) { - assert.notStrictEqual(this[kOtherSide], null); - assert.strictEqual(this[kOtherSide][kCallback], null); - if (chunk.length === 0) { - process.nextTick(callback); - } else { - this[kOtherSide].push(chunk); - this[kOtherSide][kCallback] = callback; - } - } - - _final(callback) { - this[kOtherSide].on('end', callback); - this[kOtherSide].push(null); - } -} - -function makeDuplexPair() { - const clientSide = new DuplexSocket(); - const serverSide = new DuplexSocket(); - clientSide[kOtherSide] = serverSide; - serverSide[kOtherSide] = clientSide; - return { clientSide, serverSide }; -} - -module.exports = makeDuplexPair; diff --git a/test/js/node/test/common/globals.js b/test/js/node/test/common/globals.js index 42caece2b8..5d1c4415ee 100644 --- a/test/js/node/test/common/globals.js +++ b/test/js/node/test/common/globals.js @@ -79,7 +79,6 @@ const webIdlExposedWildcard = new Set([ 'TextDecoder', 'AbortController', 'AbortSignal', - 'CustomEvent', 'EventTarget', 'Event', 'URL', @@ -127,7 +126,6 @@ const webIdlExposedWindow = new Set([ 'Response', 'WebSocket', 'EventSource', - 'CloseEvent', ]); const nodeGlobals = new Set([ diff --git a/test/js/node/test/common/index.js b/test/js/node/test/common/index.js index c67a5b8a81..38a48e8901 100644 --- a/test/js/node/test/common/index.js +++ b/test/js/node/test/common/index.js @@ -141,7 +141,7 @@ const isSunOS = process.platform === 'sunos'; const isFreeBSD = process.platform === 'freebsd'; const isOpenBSD = process.platform === 'openbsd'; const isLinux = process.platform === 'linux'; -const isOSX = process.platform === 'darwin'; +const isMacOS = process.platform === 'darwin'; const isASan = process.config.variables.asan === 1; const isPi = (() => { try { @@ -338,10 +338,9 @@ if (global.structuredClone) { knownGlobals.push(global.structuredClone); } -// BUN:TODO: uncommenting this crashes bun -// if (global.EventSource) { -// knownGlobals.push(EventSource); -// } +if (global.EventSource) { + knownGlobals.push(EventSource); +} if (global.fetch) { knownGlobals.push(fetch); @@ -967,13 +966,18 @@ function getPrintedStackTrace(stderr) { * @param {object} mod result returned by require() * @param {object} expectation shape of expected namespace. */ -function expectRequiredModule(mod, expectation) { +function expectRequiredModule(mod, expectation, checkESModule = true) { + const clone = { ...mod }; + if (Object.hasOwn(mod, 'default') && checkESModule) { + assert.strictEqual(mod.__esModule, true); + delete clone.__esModule; + } assert(isModuleNamespaceObject(mod)); - assert.deepStrictEqual({ ...mod }, { ...expectation }); + assert.deepStrictEqual(clone, { ...expectation }); } const common = { - allowGlobals: [], + allowGlobals, buildType, canCreateSymLink, childShouldThrowAndAbort, @@ -1001,7 +1005,7 @@ const common = { isLinux, isMainThread, isOpenBSD, - isOSX, + isMacOS, isPi, isSunOS, isWindows, diff --git a/test/js/node/test/common/index.mjs b/test/js/node/test/common/index.mjs index 430527faf8..007ce233fb 100644 --- a/test/js/node/test/common/index.mjs +++ b/test/js/node/test/common/index.mjs @@ -30,7 +30,7 @@ const { isLinuxPPCBE, isMainThread, isOpenBSD, - isOSX, + isMacOS, isSunOS, isWindows, localIPv6Hosts, @@ -85,7 +85,7 @@ export { isLinuxPPCBE, isMainThread, isOpenBSD, - isOSX, + isMacOS, isSunOS, isWindows, localIPv6Hosts, diff --git a/test/js/node/test/common/process-exit-code-cases.js b/test/js/node/test/common/process-exit-code-cases.js new file mode 100644 index 0000000000..54cfe2655b --- /dev/null +++ b/test/js/node/test/common/process-exit-code-cases.js @@ -0,0 +1,138 @@ +'use strict'; + +const assert = require('assert'); + +function getTestCases(isWorker = false) { + const cases = []; + function exitsOnExitCodeSet() { + process.exitCode = 42; + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 42); + assert.strictEqual(code, 42); + }); + } + cases.push({ func: exitsOnExitCodeSet, result: 42 }); + + function changesCodeViaExit() { + process.exitCode = 99; + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 42); + assert.strictEqual(code, 42); + }); + process.exit(42); + } + cases.push({ func: changesCodeViaExit, result: 42 }); + + function changesCodeZeroExit() { + process.exitCode = 99; + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 0); + assert.strictEqual(code, 0); + }); + process.exit(0); + } + cases.push({ func: changesCodeZeroExit, result: 0 }); + + function exitWithOneOnUncaught() { + process.exitCode = 99; + process.on('exit', (code) => { + // Cannot use assert because it will be uncaughtException -> 1 exit code + // that will render this test useless + if (code !== 1 || process.exitCode !== 1) { + console.log('wrong code! expected 1 for uncaughtException'); + process.exit(99); + } + }); + throw new Error('ok'); + } + cases.push({ + func: exitWithOneOnUncaught, + result: 1, + error: /^Error: ok$/, + }); + + function changeCodeInsideExit() { + process.exitCode = 95; + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 95); + assert.strictEqual(code, 95); + process.exitCode = 99; + }); + } + cases.push({ func: changeCodeInsideExit, result: 99 }); + + function zeroExitWithUncaughtHandler() { + const noop = () => { }; + process.on('exit', (code) => { + process.off('uncaughtException', noop); + assert.strictEqual(process.exitCode, undefined); + assert.strictEqual(code, 0); + }); + process.on('uncaughtException', noop); + throw new Error('ok'); + } + cases.push({ func: zeroExitWithUncaughtHandler, result: 0 }); + + function changeCodeInUncaughtHandler() { + const modifyExitCode = () => { process.exitCode = 97; }; + process.on('exit', (code) => { + process.off('uncaughtException', modifyExitCode); + assert.strictEqual(process.exitCode, 97); + assert.strictEqual(code, 97); + }); + process.on('uncaughtException', modifyExitCode); + throw new Error('ok'); + } + cases.push({ func: changeCodeInUncaughtHandler, result: 97 }); + + function changeCodeInExitWithUncaught() { + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 1); + assert.strictEqual(code, 1); + process.exitCode = 98; + }); + throw new Error('ok'); + } + cases.push({ + func: changeCodeInExitWithUncaught, + result: 98, + error: /^Error: ok$/, + }); + + function exitWithZeroInExitWithUncaught() { + process.on('exit', (code) => { + assert.strictEqual(process.exitCode, 1); + assert.strictEqual(code, 1); + process.exitCode = 0; + }); + throw new Error('ok'); + } + cases.push({ + func: exitWithZeroInExitWithUncaught, + result: 0, + error: /^Error: ok$/, + }); + + function exitWithThrowInUncaughtHandler() { + process.on('uncaughtException', () => { + throw new Error('ok'); + }); + throw new Error('bad'); + } + cases.push({ + func: exitWithThrowInUncaughtHandler, + result: isWorker ? 1 : 7, + error: /^Error: ok$/, + }); + + function exitWithUndefinedFatalException() { + process._fatalException = undefined; + throw new Error('ok'); + } + cases.push({ + func: exitWithUndefinedFatalException, + result: 6, + }); + return cases; +} +exports.getTestCases = getTestCases; diff --git a/test/js/node/test/common/sea.js b/test/js/node/test/common/sea.js index 863047ab36..53bfd93d92 100644 --- a/test/js/node/test/common/sea.js +++ b/test/js/node/test/common/sea.js @@ -5,7 +5,7 @@ const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); const { inspect } = require('util'); -const { readFileSync, copyFileSync } = require('fs'); +const { readFileSync, copyFileSync, statSync } = require('fs'); const { spawnSyncAndExitWithoutError, } = require('../common/child_process'); @@ -50,12 +50,23 @@ function skipIfSingleExecutableIsNotSupported() { common.skip('UndefinedBehavior Sanitizer is not supported'); } + try { + readFileSync(process.execPath); + } catch (e) { + if (e.code === 'ERR_FS_FILE_TOO_LARGE') { + common.skip('The Node.js binary is too large to be supported by postject'); + } + } + tmpdir.refresh(); // The SEA tests involve making a copy of the executable and writing some fixtures - // to the tmpdir. To be safe, ensure that at least 120MB disk space is available. - if (!tmpdir.hasEnoughSpace(120 * 1024 * 1024)) { - common.skip('Available disk space < 120MB'); + // to the tmpdir. To be safe, ensure that the disk space has at least a copy of the + // executable and some extra space for blobs and configs is available. + const stat = statSync(process.execPath); + const expectedSpace = stat.size + 10 * 1024 * 1024; + if (!tmpdir.hasEnoughSpace(expectedSpace)) { + common.skip(`Available disk space < ${Math.floor(expectedSpace / 1024 / 1024)} MB`); } } diff --git a/test/js/node/test/common/shared-lib-util.js b/test/js/node/test/common/shared-lib-util.js index 3ecd38791c..b5d947a266 100644 --- a/test/js/node/test/common/shared-lib-util.js +++ b/test/js/node/test/common/shared-lib-util.js @@ -22,7 +22,7 @@ function addLibraryPath(env) { env.LIBPATH = (env.LIBPATH ? env.LIBPATH + path.delimiter : '') + kExecPath; - // For Mac OSX. + // For macOS. env.DYLD_LIBRARY_PATH = (env.DYLD_LIBRARY_PATH ? env.DYLD_LIBRARY_PATH + path.delimiter : '') + kExecPath; diff --git a/test/js/node/test/parallel/.gitignore b/test/js/node/test/parallel/.gitignore deleted file mode 100644 index fd3ec92e0c..0000000000 --- a/test/js/node/test/parallel/.gitignore +++ /dev/null @@ -1,67 +0,0 @@ -# Not working yet: -child-process-double-pipe.test.js -child-process-exec-cwd.test.js -child-process-exec-timeout-expire.test.js -child-process-spawn-controller.test.js -child-process-stdio-inherit.test.js -cluster-fork-env.test.js -cluster-kill-infinite-loop.test.js -file-write-stream4.test.js -file-write-stream5.test.js -filehandle-close.test.js -fs-existssync-false.test.js -fs-fmap.test.js -fs-read-stream-fd.test.js -fs-readdir-ucs2.test.js -fs-watch-recursive-add-file-to-new-folder.test.js -fs-watch-recursive-symlink.test.js -http-parser-finish-error.test.js -http-request-agent.test.js -http2-connect-options.test.js -https-server-connections-checking-leak.test.js -module-circular-symlinks.test.js -module-prototype-mutation.test.js -net-listen-error.test.js -net-server-close.test.js -permission-fs-windows-path.test.js -pipe-abstract-socket-http.test.js -pipe-file-to-http.test.js -process-ppid.test.js -require-invalid-package.test.js -require-long-path.test.js -snapshot-dns-lookup-localhost.test.js -trace-events-net-abstract-socket.test.js -trace-events-worker-metadata-with-name.test.js -windows-failed-heap-allocation.test.js -worker-dns-terminate.test.js -worker-esm-exit.test.js -worker-message-port-wasm-module.test.js - -# Failing on Windows: -pipe-head.test.js -http-client-response-domain.test.js -require-extensions-same-filename-as-dir-trailing-slash.test.js -fs-realpath-on-substed-drive.test.js -fs-symlink-dir-junction.test.js - -# macOS not working yet -node-dns.test.js - -# Things we don't support: -repl* -inspector* -npm* -*changelog* -permission* -shadow-realm* -trace-events* -cli-node-options* -corepack* -eslint* -coverage* -buffer-zero-fill-cli* -icu-minimum-version.test.js -release-npm.test.js - -# Bad tests -tls-wrap-no-abort.test.js diff --git a/test/js/node/test/parallel/arm-math-illegal-instruction.test.js b/test/js/node/test/parallel/arm-math-illegal-instruction.test.js deleted file mode 100644 index 58199f8742..0000000000 --- a/test/js/node/test/parallel/arm-math-illegal-instruction.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-arm-math-illegal-instruction.js -//#SHA1: 08aea7234b93dfe296564c6dd21a58bc91acd9dd -//----------------- -"use strict"; - -// This test ensures Math functions don't fail with an "illegal instruction" -// error on ARM devices (primarily on the Raspberry Pi 1) -// See https://github.com/nodejs/node/issues/1376 -// and https://code.google.com/p/v8/issues/detail?id=4019 - -test("Math functions do not fail with illegal instruction on ARM devices", () => { - // Iterate over all Math functions - Object.getOwnPropertyNames(Math).forEach(functionName => { - if (!/[A-Z]/.test(functionName)) { - // The function names don't have capital letters. - expect(() => Math[functionName](-0.5)).not.toThrow(); - } - }); -}); - -//<#END_FILE: test-arm-math-illegal-instruction.js diff --git a/test/js/node/test/parallel/assert-esm-cjs-message-verify.test.js b/test/js/node/test/parallel/assert-esm-cjs-message-verify.test.js deleted file mode 100644 index 93537273a8..0000000000 --- a/test/js/node/test/parallel/assert-esm-cjs-message-verify.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-assert-esm-cjs-message-verify.js -//#SHA1: 3d120c4813c4051523045df80fc501e9921b878f -//----------------- -"use strict"; - -const { spawnPromisified } = require("../common"); -const tmpdir = require("../common/tmpdir"); -const assert = require("assert"); -const { writeFileSync, unlink } = require("fs"); -const { join } = require("path"); - -tmpdir.refresh(); - -const fileImports = { - cjs: 'const assert = require("assert");', - mjs: 'import assert from "assert";', -}; - -const fileNames = []; - -for (const [ext, header] of Object.entries(fileImports)) { - const fileName = `test-file.${ext}`; - // Store the generated filesnames in an array - fileNames.push(join(tmpdir.path, fileName)); - - writeFileSync(tmpdir.resolve(fileName), `${header}\nassert.ok(0 === 2);`); -} - -describe("ensure the assert.ok throwing similar error messages for esm and cjs files", () => { - const nodejsPath = process.execPath; - const errorsMessages = []; - - test("should return code 1 for each command", async () => { - for (const fileName of fileNames) { - const { stderr, code } = await spawnPromisified(nodejsPath, [fileName]); - expect(code).toBe(1); - // For each error message, filter the lines which will starts with AssertionError - errorsMessages.push(stderr.split("\n").find(s => s.startsWith("AssertionError"))); - } - }); - - afterAll(() => { - expect(errorsMessages).toHaveLength(2); - expect(errorsMessages[0]).toEqual(errorsMessages[1]); - - for (const fileName of fileNames) { - unlink(fileName, () => {}); - } - - tmpdir.refresh(); - }); -}); - -//<#END_FILE: test-assert-esm-cjs-message-verify.js diff --git a/test/js/node/test/parallel/assert-strict-exists.test.js b/test/js/node/test/parallel/assert-strict-exists.test.js deleted file mode 100644 index 3595cf38ec..0000000000 --- a/test/js/node/test/parallel/assert-strict-exists.test.js +++ /dev/null @@ -1,13 +0,0 @@ -//#FILE: test-assert-strict-exists.js -//#SHA1: 390d3a53b3e79630cbb673eed78ac5857a49352f -//----------------- -"use strict"; - -test("assert/strict is the same as assert.strict", () => { - const assert = require("assert"); - const assertStrict = require("assert/strict"); - - expect(assertStrict).toBe(assert.strict); -}); - -//<#END_FILE: test-assert-strict-exists.js diff --git a/test/js/node/test/parallel/async-hooks-recursive-stack-runinasyncscope.test.js b/test/js/node/test/parallel/async-hooks-recursive-stack-runinasyncscope.test.js deleted file mode 100644 index 30f03f8332..0000000000 --- a/test/js/node/test/parallel/async-hooks-recursive-stack-runinasyncscope.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-async-hooks-recursive-stack-runInAsyncScope.js -//#SHA1: 7258dfd5a442e34e60920fb484336420db8754e2 -//----------------- -"use strict"; - -const async_hooks = require("async_hooks"); - -// This test verifies that the async ID stack can grow indefinitely. - -function recurse(n) { - const a = new async_hooks.AsyncResource("foobar"); - a.runInAsyncScope(() => { - expect(a.asyncId()).toBe(async_hooks.executionAsyncId()); - expect(a.triggerAsyncId()).toBe(async_hooks.triggerAsyncId()); - if (n >= 0) recurse(n - 1); - expect(a.asyncId()).toBe(async_hooks.executionAsyncId()); - expect(a.triggerAsyncId()).toBe(async_hooks.triggerAsyncId()); - }); -} - -test("async ID stack can grow indefinitely", () => { - expect(() => recurse(1000)).not.toThrow(); -}); - -//<#END_FILE: test-async-hooks-recursive-stack-runInAsyncScope.js diff --git a/test/js/node/test/parallel/async-hooks-run-in-async-scope-this-arg.test.js b/test/js/node/test/parallel/async-hooks-run-in-async-scope-this-arg.test.js deleted file mode 100644 index 41479c5c05..0000000000 --- a/test/js/node/test/parallel/async-hooks-run-in-async-scope-this-arg.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-async-hooks-run-in-async-scope-this-arg.js -//#SHA1: a716f9818bdd704e1cf7ca188ffd4ccb9501a8a7 -//----------------- -"use strict"; - -// Test that passing thisArg to runInAsyncScope() works. - -const { AsyncResource } = require("async_hooks"); - -const thisArg = {}; - -const res = new AsyncResource("fhqwhgads"); - -function callback() { - expect(this).toBe(thisArg); -} - -test("runInAsyncScope with thisArg", () => { - const callbackSpy = jest.fn(callback); - res.runInAsyncScope(callbackSpy, thisArg); - expect(callbackSpy).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-async-hooks-run-in-async-scope-this-arg.js diff --git a/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-1.test.js b/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-1.test.js deleted file mode 100644 index c8988c5655..0000000000 --- a/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-1.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-async-hooks-worker-asyncfn-terminate-1.js -//#SHA1: 556f29e8c45107ff3448d713145f6f4e070e8073 -//----------------- -"use strict"; - -const { Worker } = require("worker_threads"); - -test("Worker with async function and async_hooks terminates correctly", () => { - const w = new Worker( - ` - const { createHook } = require('async_hooks'); - - setImmediate(async () => { - createHook({ init() {} }).enable(); - await 0; - process.exit(); - }); - `, - { eval: true }, - ); - - return new Promise(resolve => { - w.on("exit", () => { - expect(true).toBe(true); // Ensures the 'exit' event was called - resolve(); - }); - }); -}); - -//<#END_FILE: test-async-hooks-worker-asyncfn-terminate-1.js diff --git a/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-2.test.js b/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-2.test.js deleted file mode 100644 index 2140f81307..0000000000 --- a/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-2.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-async-hooks-worker-asyncfn-terminate-2.js -//#SHA1: 2329c972e1256f3aadc37e92f0ff1219d8519328 -//----------------- -"use strict"; - -const { Worker } = require("worker_threads"); - -// Like test-async-hooks-worker-promise.js but with the `await` and `createHook` -// lines switched, because that resulted in different assertion failures -// (one a Node.js assertion and one a V8 DCHECK) and it seems prudent to -// cover both of those failures. - -test("Worker with async function and createHook", () => { - const w = new Worker( - ` - const { createHook } = require('async_hooks'); - - setImmediate(async () => { - await 0; - createHook({ init() {} }).enable(); - process.exit(); - }); - `, - { eval: true }, - ); - - w.postMessage({}); - - return new Promise(resolve => { - w.on("exit", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - resolve(); - }); - }); -}); - -//<#END_FILE: test-async-hooks-worker-asyncfn-terminate-2.js diff --git a/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-3.test.js b/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-3.test.js deleted file mode 100644 index ecc905292a..0000000000 --- a/test/js/node/test/parallel/async-hooks-worker-asyncfn-terminate-3.test.js +++ /dev/null @@ -1,33 +0,0 @@ -//#FILE: test-async-hooks-worker-asyncfn-terminate-3.js -//#SHA1: bd68d52f5ecd5cb22738f78ee855706ed424cbf0 -//----------------- -"use strict"; - -const { Worker } = require("worker_threads"); - -// Like test-async-hooks-worker-promise.js but with an additional statement -// after the `process.exit()` call, that shouldn't really make a difference -// but apparently does. - -test("Worker with async function and process.exit()", done => { - const w = new Worker( - ` - const { createHook } = require('async_hooks'); - - setImmediate(async () => { - createHook({ init() {} }).enable(); - await 0; - process.exit(); - process._rawDebug('THIS SHOULD NEVER BE REACHED'); - }); - `, - { eval: true }, - ); - - w.on("exit", () => { - expect(true).toBe(true); // Ensure the exit event is called - done(); - }); -}); - -//<#END_FILE: test-async-hooks-worker-asyncfn-terminate-3.js diff --git a/test/js/node/test/parallel/async-local-storage-deep-stack.test.js b/test/js/node/test/parallel/async-local-storage-deep-stack.test.js deleted file mode 100644 index 9c3926b18b..0000000000 --- a/test/js/node/test/parallel/async-local-storage-deep-stack.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-async-local-storage-deep-stack.js -//#SHA1: 305d85dc794f55b19fffebfbb720ba0c83714f63 -//----------------- -"use strict"; - -const { AsyncLocalStorage } = require("async_hooks"); - -// Regression test for: https://github.com/nodejs/node/issues/34556 - -test("AsyncLocalStorage deep stack", () => { - const als = new AsyncLocalStorage(); - - const done = jest.fn(); - - function run(count) { - if (count !== 0) return als.run({}, run, --count); - done(); - } - - run(1000); - - expect(done).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-async-local-storage-deep-stack.js diff --git a/test/js/node/test/parallel/async-local-storage-http-multiclients.test.js b/test/js/node/test/parallel/async-local-storage-http-multiclients.test.js deleted file mode 100644 index b90b19ea6b..0000000000 --- a/test/js/node/test/parallel/async-local-storage-http-multiclients.test.js +++ /dev/null @@ -1,89 +0,0 @@ -//#FILE: test-async-local-storage-http-multiclients.js -//#SHA1: adf8feaec8fa034cbb22fd0f3e2a5ed224c1905e -//----------------- -"use strict"; - -const { AsyncLocalStorage } = require("async_hooks"); -const http = require("http"); - -const NUM_CLIENTS = 10; - -// Run multiple clients that receive data from a server -// in multiple chunks, in a single non-closure function. -// Use the AsyncLocalStorage (ALS) APIs to maintain the context -// and data download. Make sure that individual clients -// receive their respective data, with no conflicts. - -describe("AsyncLocalStorage with multiple HTTP clients", () => { - const cls = new AsyncLocalStorage(); - let server; - let index = 0; - - beforeAll(() => { - // Set up a server that sends large buffers of data, filled - // with cardinal numbers, increasing per request - server = http.createServer((q, r) => { - // Send a large chunk as response, otherwise the data - // may be sent in a single chunk, and the callback in the - // client may be called only once, defeating the purpose of test - r.end((index++ % 10).toString().repeat(1024 * 1024)); - }); - }); - - afterAll(() => { - server.close(); - }); - - it("should handle multiple clients correctly", async () => { - const clientPromises = []; - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - for (let i = 0; i < NUM_CLIENTS; i++) { - clientPromises.push( - new Promise(resolve => { - cls.run(new Map(), () => { - const options = { port: server.address().port }; - const req = http.get(options, res => { - const store = cls.getStore(); - store.set("data", ""); - - // Make ondata and onend non-closure - // functions and fully dependent on ALS - res.setEncoding("utf8"); - res.on("data", ondata); - res.on("end", () => { - onend(); - resolve(); - }); - }); - req.end(); - }); - }), - ); - } - - await Promise.all(clientPromises); - }); - - // Accumulate the current data chunk with the store data - function ondata(d) { - const store = cls.getStore(); - expect(store).not.toBeUndefined(); - let chunk = store.get("data"); - chunk += d; - store.set("data", chunk); - } - - // Retrieve the store data, and test for homogeneity - function onend() { - const store = cls.getStore(); - expect(store).not.toBeUndefined(); - const data = store.get("data"); - expect(data).toBe(data[0].repeat(data.length)); - } -}); - -//<#END_FILE: test-async-local-storage-http-multiclients.js diff --git a/test/js/node/test/parallel/async-local-storage-snapshot.test.js b/test/js/node/test/parallel/async-local-storage-snapshot.test.js deleted file mode 100644 index 8b4d0b80bd..0000000000 --- a/test/js/node/test/parallel/async-local-storage-snapshot.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-async-local-storage-snapshot.js -//#SHA1: f8d967194bfb0b73994d296b03c0c43afa5127e5 -//----------------- -"use strict"; - -const { AsyncLocalStorage } = require("async_hooks"); - -describe("AsyncLocalStorage snapshot", () => { - test("should preserve the original context when using snapshot", () => { - const asyncLocalStorage = new AsyncLocalStorage(); - - const runInAsyncScope = asyncLocalStorage.run(123, () => AsyncLocalStorage.snapshot()); - - const result = asyncLocalStorage.run(321, () => { - return runInAsyncScope(() => { - return asyncLocalStorage.getStore(); - }); - }); - - expect(result).toBe(123); - }); -}); - -//<#END_FILE: test-async-local-storage-snapshot.js diff --git a/test/js/node/test/parallel/atomics-wake.test.js b/test/js/node/test/parallel/atomics-wake.test.js deleted file mode 100644 index 7c690a086a..0000000000 --- a/test/js/node/test/parallel/atomics-wake.test.js +++ /dev/null @@ -1,11 +0,0 @@ -//#FILE: test-atomics-wake.js -//#SHA1: 311b66a7cd5fbc08a20b77de98a66f9cba763f8f -//----------------- -"use strict"; - -// https://github.com/nodejs/node/issues/21219 -test("Atomics.wake should be undefined", () => { - expect(Atomics.wake).toBeUndefined(); -}); - -//<#END_FILE: test-atomics-wake.js diff --git a/test/js/node/test/parallel/bad-unicode.test.js b/test/js/node/test/parallel/bad-unicode.test.js deleted file mode 100644 index 26c70dbf53..0000000000 --- a/test/js/node/test/parallel/bad-unicode.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-bad-unicode.js -//#SHA1: e9b8765f74af6588aff1bd7bfcbc6a19187d100e -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -test("eval with bad unicode throws SyntaxError", () => { - expect(() => { - eval('"\\uc/ef"'); - }).toThrow( - expect.objectContaining({ - name: "SyntaxError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-bad-unicode.js diff --git a/test/js/node/test/parallel/beforeexit-event-exit.test.js b/test/js/node/test/parallel/beforeexit-event-exit.test.js deleted file mode 100644 index c7a0ad2a9c..0000000000 --- a/test/js/node/test/parallel/beforeexit-event-exit.test.js +++ /dev/null @@ -1,44 +0,0 @@ -//#FILE: test-beforeexit-event-exit.js -//#SHA1: 27b6351612c5ec4f51d3317ca1c89511b833eaab -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -test("beforeExit event should not be called when process.exit() is called", () => { - const mockBeforeExit = jest.fn(); - process.on("beforeExit", mockBeforeExit); - - // Spy on process.exit to prevent actual exit - const exitSpy = jest.spyOn(process, "exit").mockImplementation(() => {}); - - process.exit(); - - expect(mockBeforeExit).not.toHaveBeenCalled(); - expect(exitSpy).toHaveBeenCalled(); - - // Clean up - process.removeListener("beforeExit", mockBeforeExit); - exitSpy.mockRestore(); -}); - -//<#END_FILE: test-beforeexit-event-exit.js diff --git a/test/js/node/test/parallel/binding-constants.test.js b/test/js/node/test/parallel/binding-constants.test.js deleted file mode 100644 index e3cabf4e2b..0000000000 --- a/test/js/node/test/parallel/binding-constants.test.js +++ /dev/null @@ -1,44 +0,0 @@ -//#FILE: test-binding-constants.js -//#SHA1: 84b14e2a54ec767074f2a4103eaa0b419655cf8b -//----------------- -"use strict"; - -// Note: This test originally used internal bindings which are not recommended for use in tests. -// The test has been modified to focus on the public API and behavior that can be tested without internals. - -test("constants object structure", () => { - const constants = process.binding("constants"); - - expect(Object.keys(constants).sort()).toEqual(["crypto", "fs", "os", "trace", "zlib"]); - - expect(Object.keys(constants.os).sort()).toEqual(["UV_UDP_REUSEADDR", "dlopen", "errno", "priority", "signals"]); -}); - -test("constants objects do not inherit from Object.prototype", () => { - const constants = process.binding("constants"); - const inheritedProperties = Object.getOwnPropertyNames(Object.prototype); - - function testObject(obj) { - expect(obj).toBeTruthy(); - expect(Object.prototype.toString.call(obj)).toBe("[object Object]"); - expect(Object.getPrototypeOf(obj)).toBeNull(); - - inheritedProperties.forEach(property => { - expect(property in obj).toBe(false); - }); - } - - [ - constants, - constants.crypto, - constants.fs, - constants.os, - constants.trace, - constants.zlib, - constants.os.dlopen, - constants.os.errno, - constants.os.signals, - ].forEach(testObject); -}); - -//<#END_FILE: test-binding-constants.js diff --git a/test/js/node/test/parallel/blob-createobjecturl.test.js b/test/js/node/test/parallel/blob-createobjecturl.test.js deleted file mode 100644 index 9f94ba18f5..0000000000 --- a/test/js/node/test/parallel/blob-createobjecturl.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-blob-createobjecturl.js -//#SHA1: d2030ca0ad6757dd9d338bc2e65cd3ff8917009d -//----------------- -// Flags: --no-warnings -"use strict"; - -// Because registering a Blob URL requires generating a random -// UUID, it can only be done if crypto support is enabled. -if (typeof crypto === "undefined") { - test.skip("missing crypto"); -} - -const { URL } = require("url"); -const { Blob, resolveObjectURL } = require("buffer"); - -test("Blob URL creation and resolution", async () => { - const blob = new Blob(["hello"]); - const id = URL.createObjectURL(blob); - expect(typeof id).toBe("string"); - const otherBlob = resolveObjectURL(id); - expect(otherBlob).toBeInstanceOf(Blob); - expect(otherBlob.constructor).toBe(Blob); - expect(otherBlob.size).toBe(5); - expect(Buffer.from(await otherBlob.arrayBuffer()).toString()).toBe("hello"); - URL.revokeObjectURL(id); - - // should do nothing - URL.revokeObjectURL(id); - - expect(resolveObjectURL(id)).toBeUndefined(); - - // Leaving a Blob registered should not cause an assert - // when Node.js exists - URL.createObjectURL(new Blob()); -}); - -test("resolveObjectURL with invalid inputs", () => { - ["not a url", undefined, 1, "blob:nodedata:1:wrong", {}].forEach(i => { - expect(resolveObjectURL(i)).toBeUndefined(); - }); -}); - -test("createObjectURL with invalid inputs", () => { - [undefined, 1, "", false, {}].forEach(i => { - expect(() => URL.createObjectURL(i)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-blob-createobjecturl.js diff --git a/test/js/node/test/parallel/buffer-arraybuffer.test.js b/test/js/node/test/parallel/buffer-arraybuffer.test.js deleted file mode 100644 index d33487198f..0000000000 --- a/test/js/node/test/parallel/buffer-arraybuffer.test.js +++ /dev/null @@ -1,158 +0,0 @@ -//#FILE: test-buffer-arraybuffer.js -//#SHA1: 2297240ef18399097bd3383db051d8e37339a123 -//----------------- -"use strict"; - -const LENGTH = 16; - -test("Buffer from ArrayBuffer", () => { - const ab = new ArrayBuffer(LENGTH); - const dv = new DataView(ab); - const ui = new Uint8Array(ab); - const buf = Buffer.from(ab); - - expect(buf).toBeInstanceOf(Buffer); - expect(buf.parent).toBe(buf.buffer); - expect(buf.buffer).toBe(ab); - expect(buf.length).toBe(ab.byteLength); - - buf.fill(0xc); - for (let i = 0; i < LENGTH; i++) { - expect(ui[i]).toBe(0xc); - ui[i] = 0xf; - expect(buf[i]).toBe(0xf); - } - - buf.writeUInt32LE(0xf00, 0); - buf.writeUInt32BE(0xb47, 4); - buf.writeDoubleLE(3.1415, 8); - - expect(dv.getUint32(0, true)).toBe(0xf00); - expect(dv.getUint32(4)).toBe(0xb47); - expect(dv.getFloat64(8, true)).toBe(3.1415); -}); - -test.todo("Buffer.from with invalid ArrayBuffer", () => { - expect(() => { - function AB() {} - Object.setPrototypeOf(AB, ArrayBuffer); - Object.setPrototypeOf(AB.prototype, ArrayBuffer.prototype); - Buffer.from(new AB()); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringContaining( - "The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object.", - ), - }), - ); -}); - -test("Buffer.from with byteOffset and length arguments", () => { - const ab = new Uint8Array(5); - ab[0] = 1; - ab[1] = 2; - ab[2] = 3; - ab[3] = 4; - ab[4] = 5; - const buf = Buffer.from(ab.buffer, 1, 3); - expect(buf.length).toBe(3); - expect(buf[0]).toBe(2); - expect(buf[1]).toBe(3); - expect(buf[2]).toBe(4); - buf[0] = 9; - expect(ab[1]).toBe(9); - - expect(() => Buffer.from(ab.buffer, 6)).toThrow( - expect.objectContaining({ - name: "RangeError", - // code: "ERR_BUFFER_OUT_OF_BOUNDS", - // message: expect.stringContaining('"offset" is outside of buffer bounds'), - }), - ); - - expect(() => Buffer.from(ab.buffer, 3, 6)).toThrow( - expect.objectContaining({ - name: "RangeError", - // code: "ERR_BUFFER_OUT_OF_BOUNDS", - // message: expect.stringContaining('"length" is outside of buffer bounds'), - }), - ); -}); - -test("Deprecated Buffer() constructor", () => { - const ab = new Uint8Array(5); - ab[0] = 1; - ab[1] = 2; - ab[2] = 3; - ab[3] = 4; - ab[4] = 5; - const buf = Buffer(ab.buffer, 1, 3); - expect(buf.length).toBe(3); - expect(buf[0]).toBe(2); - expect(buf[1]).toBe(3); - expect(buf[2]).toBe(4); - buf[0] = 9; - expect(ab[1]).toBe(9); - - expect(() => Buffer(ab.buffer, 6)).toThrow( - expect.objectContaining({ - name: "RangeError", - // code: "ERR_BUFFER_OUT_OF_BOUNDS", - // message: expect.stringContaining('"offset" is outside of buffer bounds'), - }), - ); - - expect(() => Buffer(ab.buffer, 3, 6)).toThrow( - expect.objectContaining({ - name: "RangeError", - // code: "ERR_BUFFER_OUT_OF_BOUNDS", - // message: expect.stringContaining('"length" is outside of buffer bounds'), - }), - ); -}); - -test("Buffer.from with non-numeric byteOffset", () => { - const ab = new ArrayBuffer(10); - const expected = Buffer.from(ab, 0); - expect(Buffer.from(ab, "fhqwhgads")).toEqual(expected); - expect(Buffer.from(ab, NaN)).toEqual(expected); - expect(Buffer.from(ab, {})).toEqual(expected); - expect(Buffer.from(ab, [])).toEqual(expected); - - expect(Buffer.from(ab, [1])).toEqual(Buffer.from(ab, 1)); - - expect(() => Buffer.from(ab, Infinity)).toThrow( - expect.objectContaining({ - name: "RangeError", - // code: "ERR_BUFFER_OUT_OF_BOUNDS", - // message: expect.stringContaining('"offset" is outside of buffer bounds'), - }), - ); -}); - -test("Buffer.from with non-numeric length", () => { - const ab = new ArrayBuffer(10); - const expected = Buffer.from(ab, 0, 0); - expect(Buffer.from(ab, 0, "fhqwhgads")).toEqual(expected); - expect(Buffer.from(ab, 0, NaN)).toEqual(expected); - expect(Buffer.from(ab, 0, {})).toEqual(expected); - expect(Buffer.from(ab, 0, [])).toEqual(expected); - - expect(Buffer.from(ab, 0, [1])).toEqual(Buffer.from(ab, 0, 1)); - - expect(() => Buffer.from(ab, 0, Infinity)).toThrow( - expect.objectContaining({ - name: "RangeError", - // code: "ERR_BUFFER_OUT_OF_BOUNDS", - // message: expect.stringContaining('"length" is outside of buffer bounds'), - }), - ); -}); - -test("Buffer.from with array-like entry and NaN length", () => { - expect(Buffer.from({ length: NaN })).toEqual(Buffer.alloc(0)); -}); - -//<#END_FILE: test-buffer-arraybuffer.js diff --git a/test/js/node/test/parallel/buffer-bytelength.test.js b/test/js/node/test/parallel/buffer-bytelength.test.js deleted file mode 100644 index 5934db1dc8..0000000000 --- a/test/js/node/test/parallel/buffer-bytelength.test.js +++ /dev/null @@ -1,131 +0,0 @@ -//#FILE: test-buffer-bytelength.js -//#SHA1: bcc75ad2f868ac9414c789c29f23ee9c806c749d -//----------------- -"use strict"; - -const SlowBuffer = require("buffer").SlowBuffer; -const vm = require("vm"); - -test("Buffer.byteLength with invalid arguments", () => { - [[32, "latin1"], [NaN, "utf8"], [{}, "latin1"], []].forEach(args => { - expect(() => Buffer.byteLength(...args)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringContaining( - 'The "string" argument must be of type string or an instance of Buffer or ArrayBuffer.', - ), - }), - ); - }); -}); - -test("ArrayBuffer.isView for various Buffer types", () => { - expect(ArrayBuffer.isView(new Buffer(10))).toBe(true); - expect(ArrayBuffer.isView(new SlowBuffer(10))).toBe(true); - expect(ArrayBuffer.isView(Buffer.alloc(10))).toBe(true); - expect(ArrayBuffer.isView(Buffer.allocUnsafe(10))).toBe(true); - expect(ArrayBuffer.isView(Buffer.allocUnsafeSlow(10))).toBe(true); - expect(ArrayBuffer.isView(Buffer.from(""))).toBe(true); -}); - -test("Buffer.byteLength for various buffer types", () => { - const incomplete = Buffer.from([0xe4, 0xb8, 0xad, 0xe6, 0x96]); - expect(Buffer.byteLength(incomplete)).toBe(5); - - const ascii = Buffer.from("abc"); - expect(Buffer.byteLength(ascii)).toBe(3); - - const buffer = new ArrayBuffer(8); - expect(Buffer.byteLength(buffer)).toBe(8); -}); - -test("Buffer.byteLength for TypedArrays", () => { - expect(Buffer.byteLength(new Int8Array(8))).toBe(8); - expect(Buffer.byteLength(new Uint8Array(8))).toBe(8); - expect(Buffer.byteLength(new Uint8ClampedArray(2))).toBe(2); - expect(Buffer.byteLength(new Int16Array(8))).toBe(16); - expect(Buffer.byteLength(new Uint16Array(8))).toBe(16); - expect(Buffer.byteLength(new Int32Array(8))).toBe(32); - expect(Buffer.byteLength(new Uint32Array(8))).toBe(32); - expect(Buffer.byteLength(new Float32Array(8))).toBe(32); - expect(Buffer.byteLength(new Float64Array(8))).toBe(64); -}); - -test("Buffer.byteLength for DataView", () => { - const dv = new DataView(new ArrayBuffer(2)); - expect(Buffer.byteLength(dv)).toBe(2); -}); - -test("Buffer.byteLength for zero length string", () => { - expect(Buffer.byteLength("", "ascii")).toBe(0); - expect(Buffer.byteLength("", "HeX")).toBe(0); -}); - -test("Buffer.byteLength for utf8", () => { - expect(Buffer.byteLength("∑éllö wørl∂!", "utf-8")).toBe(19); - expect(Buffer.byteLength("κλμνξο", "utf8")).toBe(12); - expect(Buffer.byteLength("挵挶挷挸挹", "utf-8")).toBe(15); - expect(Buffer.byteLength("𠝹𠱓𠱸", "UTF8")).toBe(12); - expect(Buffer.byteLength("hey there")).toBe(9); - expect(Buffer.byteLength("𠱸挶νξ#xx :)")).toBe(17); - expect(Buffer.byteLength("hello world", "")).toBe(11); - expect(Buffer.byteLength("hello world", "abc")).toBe(11); - expect(Buffer.byteLength("ßœ∑≈", "unkn0wn enc0ding")).toBe(10); -}); - -test("Buffer.byteLength for base64", () => { - expect(Buffer.byteLength("aGVsbG8gd29ybGQ=", "base64")).toBe(11); - expect(Buffer.byteLength("aGVsbG8gd29ybGQ=", "BASE64")).toBe(11); - expect(Buffer.byteLength("bm9kZS5qcyByb2NrcyE=", "base64")).toBe(14); - expect(Buffer.byteLength("aGkk", "base64")).toBe(3); - expect(Buffer.byteLength("bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw==", "base64")).toBe(25); -}); - -test("Buffer.byteLength for base64url", () => { - expect(Buffer.byteLength("aGVsbG8gd29ybGQ", "base64url")).toBe(11); - expect(Buffer.byteLength("aGVsbG8gd29ybGQ", "BASE64URL")).toBe(11); - expect(Buffer.byteLength("bm9kZS5qcyByb2NrcyE", "base64url")).toBe(14); - expect(Buffer.byteLength("aGkk", "base64url")).toBe(3); - expect(Buffer.byteLength("bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw", "base64url")).toBe(25); -}); - -test("Buffer.byteLength for special padding", () => { - expect(Buffer.byteLength("aaa=", "base64")).toBe(2); - expect(Buffer.byteLength("aaaa==", "base64")).toBe(3); - expect(Buffer.byteLength("aaa=", "base64url")).toBe(2); - expect(Buffer.byteLength("aaaa==", "base64url")).toBe(3); -}); - -test("Buffer.byteLength for various encodings", () => { - expect(Buffer.byteLength("Il était tué")).toBe(14); - expect(Buffer.byteLength("Il était tué", "utf8")).toBe(14); - - ["ascii", "latin1", "binary"] - .reduce((es, e) => es.concat(e, e.toUpperCase()), []) - .forEach(encoding => { - expect(Buffer.byteLength("Il était tué", encoding)).toBe(12); - }); - - ["ucs2", "ucs-2", "utf16le", "utf-16le"] - .reduce((es, e) => es.concat(e, e.toUpperCase()), []) - .forEach(encoding => { - expect(Buffer.byteLength("Il était tué", encoding)).toBe(24); - }); -}); - -test("Buffer.byteLength for ArrayBuffer from different context", () => { - const arrayBuf = vm.runInNewContext("new ArrayBuffer()"); - expect(Buffer.byteLength(arrayBuf)).toBe(0); -}); - -test("Buffer.byteLength for invalid encodings", () => { - for (let i = 1; i < 10; i++) { - const encoding = String(i).repeat(i); - - expect(Buffer.isEncoding(encoding)).toBe(false); - expect(Buffer.byteLength("foo", encoding)).toBe(Buffer.byteLength("foo", "utf8")); - } -}); - -//<#END_FILE: test-buffer-bytelength.js diff --git a/test/js/node/test/parallel/buffer-compare-offset.test.js b/test/js/node/test/parallel/buffer-compare-offset.test.js deleted file mode 100644 index df674d2f59..0000000000 --- a/test/js/node/test/parallel/buffer-compare-offset.test.js +++ /dev/null @@ -1,95 +0,0 @@ -//#FILE: test-buffer-compare-offset.js -//#SHA1: 460e187ac1a40db0dbc00801ad68f1272d27c3cd -//----------------- -"use strict"; - -const assert = require("assert"); - -describe("Buffer.compare with offset", () => { - const a = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); - const b = Buffer.from([5, 6, 7, 8, 9, 0, 1, 2, 3, 4]); - - test("basic comparison", () => { - expect(a.compare(b)).toBe(-1); - }); - - test("comparison with default arguments", () => { - expect(a.compare(b, 0)).toBe(-1); - expect(() => a.compare(b, "0")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - expect(a.compare(b, undefined)).toBe(-1); - }); - - test("comparison with specified ranges", () => { - expect(a.compare(b, 0, undefined, 0)).toBe(-1); - expect(a.compare(b, 0, 0, 0)).toBe(1); - expect(() => a.compare(b, 0, "0", "0")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - expect(a.compare(b, 6, 10)).toBe(1); - expect(a.compare(b, 6, 10, 0, 0)).toBe(-1); - expect(a.compare(b, 0, 0, 0, 0)).toBe(0); - expect(a.compare(b, 1, 1, 2, 2)).toBe(0); - expect(a.compare(b, 0, 5, 4)).toBe(1); - expect(a.compare(b, 5, undefined, 1)).toBe(1); - expect(a.compare(b, 2, 4, 2)).toBe(-1); - expect(a.compare(b, 0, 7, 4)).toBe(-1); - expect(a.compare(b, 0, 7, 4, 6)).toBe(-1); - }); - - test("invalid arguments", () => { - expect(() => a.compare(b, 0, null)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - expect(() => a.compare(b, 0, { valueOf: () => 5 })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - expect(() => a.compare(b, Infinity, -Infinity)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - }), - ); - expect(a.compare(b, 0xff)).toBe(1); - expect(() => a.compare(b, "0xff")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - expect(() => a.compare(b, 0, "0xff")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - }); - - test("out of range arguments", () => { - const oor = expect.objectContaining({ code: "ERR_OUT_OF_RANGE" }); - expect(() => a.compare(b, 0, 100, 0)).toThrow(oor); - expect(() => a.compare(b, 0, 1, 0, 100)).toThrow(oor); - expect(() => a.compare(b, -1)).toThrow(oor); - expect(() => a.compare(b, 0, Infinity)).toThrow(oor); - expect(() => a.compare(b, 0, 1, -1)).toThrow(oor); - expect(() => a.compare(b, -Infinity, Infinity)).toThrow(oor); - }); - - test("missing target argument", () => { - expect(() => a.compare()).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringContaining('The "target" argument must be an instance of Buffer or Uint8Array'), - }), - ); - }); -}); - -//<#END_FILE: test-buffer-compare-offset.js diff --git a/test/js/node/test/parallel/buffer-compare.test.js b/test/js/node/test/parallel/buffer-compare.test.js deleted file mode 100644 index 9f6d0c70be..0000000000 --- a/test/js/node/test/parallel/buffer-compare.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-buffer-compare.js -//#SHA1: eab68d7262240af3d53eabedb0e7a515b2d84adf -//----------------- -"use strict"; - -test("Buffer compare", () => { - const b = Buffer.alloc(1, "a"); - const c = Buffer.alloc(1, "c"); - const d = Buffer.alloc(2, "aa"); - const e = new Uint8Array([0x61, 0x61]); // ASCII 'aa', same as d - - expect(b.compare(c)).toBe(-1); - expect(c.compare(d)).toBe(1); - expect(d.compare(b)).toBe(1); - expect(d.compare(e)).toBe(0); - expect(b.compare(d)).toBe(-1); - expect(b.compare(b)).toBe(0); - - expect(Buffer.compare(b, c)).toBe(-1); - expect(Buffer.compare(c, d)).toBe(1); - expect(Buffer.compare(d, b)).toBe(1); - expect(Buffer.compare(b, d)).toBe(-1); - expect(Buffer.compare(c, c)).toBe(0); - expect(Buffer.compare(e, e)).toBe(0); - expect(Buffer.compare(d, e)).toBe(0); - expect(Buffer.compare(d, b)).toBe(1); - - expect(Buffer.compare(Buffer.alloc(0), Buffer.alloc(0))).toBe(0); - expect(Buffer.compare(Buffer.alloc(0), Buffer.alloc(1))).toBe(-1); - expect(Buffer.compare(Buffer.alloc(1), Buffer.alloc(0))).toBe(1); - - expect(() => Buffer.compare(Buffer.alloc(1), "abc")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.stringContaining('The "buf2" argument must be an instance of Buffer or Uint8Array.'), - }), - ); - - expect(() => Buffer.compare("abc", Buffer.alloc(1))).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.stringContaining('The "buf1" argument must be an instance of Buffer or Uint8Array.'), - }), - ); - - expect(() => Buffer.alloc(1).compare("abc")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringContaining('The "target" argument must be an instance of Buffer or Uint8Array.'), - }), - ); -}); - -//<#END_FILE: test-buffer-compare.js diff --git a/test/js/node/test/parallel/buffer-constants.test.js b/test/js/node/test/parallel/buffer-constants.test.js deleted file mode 100644 index 5c7c8a18d1..0000000000 --- a/test/js/node/test/parallel/buffer-constants.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-buffer-constants.js -//#SHA1: a5818d34d1588306e48d574ec76b69b2ee4dc51c -//----------------- -"use strict"; - -const { kMaxLength, kStringMaxLength } = require("buffer"); -const { MAX_LENGTH, MAX_STRING_LENGTH } = require("buffer").constants; - -test("Buffer constants", () => { - expect(typeof MAX_LENGTH).toBe("number"); - expect(typeof MAX_STRING_LENGTH).toBe("number"); - expect(MAX_STRING_LENGTH).toBeLessThanOrEqual(MAX_LENGTH); - - expect(() => " ".repeat(MAX_STRING_LENGTH + 1)).toThrow( - expect.objectContaining({ - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => " ".repeat(MAX_STRING_LENGTH)).not.toThrow(); - - // Legacy values match: - expect(kMaxLength).toBe(MAX_LENGTH); - expect(kStringMaxLength).toBe(MAX_STRING_LENGTH); -}); - -//<#END_FILE: test-buffer-constants.js diff --git a/test/js/node/test/parallel/buffer-copy.test.js b/test/js/node/test/parallel/buffer-copy.test.js deleted file mode 100644 index afb49923d2..0000000000 --- a/test/js/node/test/parallel/buffer-copy.test.js +++ /dev/null @@ -1,204 +0,0 @@ -//#FILE: test-buffer-copy.js -//#SHA1: bff8bfe75b7289a279d9fc1a1bf2293257282d27 -//----------------- -"use strict"; - -test("Buffer copy operations", () => { - const b = Buffer.allocUnsafe(1024); - const c = Buffer.allocUnsafe(512); - - let cntr = 0; - - // Copy 512 bytes, from 0 to 512. - b.fill(++cntr); - c.fill(++cntr); - const copied = b.copy(c, 0, 0, 512); - expect(copied).toBe(512); - for (let i = 0; i < c.length; i++) { - expect(c[i]).toBe(b[i]); - } - - // Current behavior is to coerce values to integers. - b.fill(++cntr); - c.fill(++cntr); - const copiedWithStrings = b.copy(c, "0", "0", "512"); - expect(copiedWithStrings).toBe(512); - for (let i = 0; i < c.length; i++) { - expect(c[i]).toBe(b[i]); - } - - // Floats will be converted to integers via `Math.floor` - b.fill(++cntr); - c.fill(++cntr); - const copiedWithFloat = b.copy(c, 0, 0, 512.5); - expect(copiedWithFloat).toBe(512); - for (let i = 0; i < c.length; i++) { - expect(c[i]).toBe(b[i]); - } - - // Copy c into b, without specifying sourceEnd - b.fill(++cntr); - c.fill(++cntr); - const copiedWithoutSourceEnd = c.copy(b, 0, 0); - expect(copiedWithoutSourceEnd).toBe(c.length); - for (let i = 0; i < c.length; i++) { - expect(b[i]).toBe(c[i]); - } - - // Copy c into b, without specifying sourceStart - b.fill(++cntr); - c.fill(++cntr); - const copiedWithoutSourceStart = c.copy(b, 0); - expect(copiedWithoutSourceStart).toBe(c.length); - for (let i = 0; i < c.length; i++) { - expect(b[i]).toBe(c[i]); - } - - // Copied source range greater than source length - b.fill(++cntr); - c.fill(++cntr); - const copiedWithGreaterRange = c.copy(b, 0, 0, c.length + 1); - expect(copiedWithGreaterRange).toBe(c.length); - for (let i = 0; i < c.length; i++) { - expect(b[i]).toBe(c[i]); - } - - // Copy longer buffer b to shorter c without targetStart - b.fill(++cntr); - c.fill(++cntr); - const copiedLongerToShorter = b.copy(c); - expect(copiedLongerToShorter).toBe(c.length); - for (let i = 0; i < c.length; i++) { - expect(c[i]).toBe(b[i]); - } - - // Copy starting near end of b to c - b.fill(++cntr); - c.fill(++cntr); - const copiedNearEnd = b.copy(c, 0, b.length - Math.floor(c.length / 2)); - expect(copiedNearEnd).toBe(Math.floor(c.length / 2)); - for (let i = 0; i < Math.floor(c.length / 2); i++) { - expect(c[i]).toBe(b[b.length - Math.floor(c.length / 2) + i]); - } - for (let i = Math.floor(c.length / 2) + 1; i < c.length; i++) { - expect(c[c.length - 1]).toBe(c[i]); - } - - // Try to copy 513 bytes, and check we don't overrun c - b.fill(++cntr); - c.fill(++cntr); - const copiedOverrun = b.copy(c, 0, 0, 513); - expect(copiedOverrun).toBe(c.length); - for (let i = 0; i < c.length; i++) { - expect(c[i]).toBe(b[i]); - } - - // Copy 768 bytes from b into b - b.fill(++cntr); - b.fill(++cntr, 256); - const copiedIntoSelf = b.copy(b, 0, 256, 1024); - expect(copiedIntoSelf).toBe(768); - for (let i = 0; i < b.length; i++) { - expect(b[i]).toBe(cntr); - } - - // Copy string longer than buffer length (failure will segfault) - const bb = Buffer.allocUnsafe(10); - bb.fill("hello crazy world"); - - // Try to copy from before the beginning of b. Should not throw. - expect(() => b.copy(c, 0, 100, 10)).not.toThrow(); - - // Throw with invalid source type - expect(() => Buffer.prototype.copy.call(0)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", //TODO:"ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - // Copy throws at negative targetStart - expect(() => Buffer.allocUnsafe(10).copy(Buffer.allocUnsafe(5), -1, 0)).toThrow({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: `The value of "targetStart" is out of range. It must be >= 0 and <= 5. Received -1`, - }); - - // Copy throws at negative sourceStart - expect(() => Buffer.allocUnsafe(10).copy(Buffer.allocUnsafe(5), 0, -1)).toThrow({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: `The value of "sourceStart" is out of range. It must be >= 0 and <= 10. Received -1`, - }); - - // Copy throws if sourceStart is greater than length of source - expect(() => Buffer.allocUnsafe(10).copy(Buffer.allocUnsafe(5), 0, 100)).toThrow({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: `The value of "sourceStart" is out of range. It must be >= 0 and <= 10. Received 100`, - }); - - // Check sourceEnd resets to targetEnd if former is greater than the latter - b.fill(++cntr); - c.fill(++cntr); - b.copy(c, 0, 0, 1025); - for (let i = 0; i < c.length; i++) { - expect(c[i]).toBe(b[i]); - } - - // Throw with negative sourceEnd - expect(() => b.copy(c, 0, 0, -1)).toThrow({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: `The value of "sourceEnd" is out of range. It must be >= 0 and <= 1024. Received -1`, - }); - - // When sourceStart is greater than sourceEnd, zero copied - expect(b.copy(c, 0, 100, 10)).toBe(0); - - // When targetStart > targetLength, zero copied - expect(b.copy(c, 512, 0, 10)).toBe(0); - - // Test that the `target` can be a Uint8Array. - const d = new Uint8Array(c); - // copy 512 bytes, from 0 to 512. - b.fill(++cntr); - d.fill(++cntr); - const copiedToUint8Array = b.copy(d, 0, 0, 512); - expect(copiedToUint8Array).toBe(512); - for (let i = 0; i < d.length; i++) { - expect(d[i]).toBe(b[i]); - } - - // Test that the source can be a Uint8Array, too. - const e = new Uint8Array(b); - // copy 512 bytes, from 0 to 512. - e.fill(++cntr); - c.fill(++cntr); - const copiedFromUint8Array = Buffer.prototype.copy.call(e, c, 0, 0, 512); - expect(copiedFromUint8Array).toBe(512); - for (let i = 0; i < c.length; i++) { - expect(c[i]).toBe(e[i]); - } - - // https://github.com/nodejs/node/issues/23668: Do not crash for invalid input. - c.fill("c"); - b.copy(c, "not a valid offset"); - // Make sure this acted like a regular copy with `0` offset. - expect(c).toEqual(b.slice(0, c.length)); - - c.fill("C"); - expect(c.toString()).toBe("C".repeat(c.length)); - expect(() => { - b.copy(c, { - [Symbol.toPrimitive]() { - throw new Error("foo"); - }, - }); - }).toThrow("foo"); - // No copying took place: - expect(c.toString()).toBe("C".repeat(c.length)); -}); - -//<#END_FILE: test-buffer-copy.js diff --git a/test/js/node/test/parallel/buffer-equals.test.js b/test/js/node/test/parallel/buffer-equals.test.js deleted file mode 100644 index 8fbd4c13c4..0000000000 --- a/test/js/node/test/parallel/buffer-equals.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-buffer-equals.js -//#SHA1: 917344b9c4ba47f1e30d02ec6adfad938b2d342a -//----------------- -"use strict"; - -test("Buffer.equals", () => { - const b = Buffer.from("abcdf"); - const c = Buffer.from("abcdf"); - const d = Buffer.from("abcde"); - const e = Buffer.from("abcdef"); - - expect(b.equals(c)).toBe(true); - expect(c.equals(d)).toBe(false); - expect(d.equals(e)).toBe(false); - expect(d.equals(d)).toBe(true); - expect(d.equals(new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65]))).toBe(true); - - expect(() => Buffer.alloc(1).equals("abc")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringContaining( - `The "otherBuffer" argument must be an instance of Buffer or Uint8Array. Received`, - ), - }), - ); -}); - -//<#END_FILE: test-buffer-equals.js diff --git a/test/js/node/test/parallel/buffer-failed-alloc-typed-arrays.test.js b/test/js/node/test/parallel/buffer-failed-alloc-typed-arrays.test.js deleted file mode 100644 index 8dfcd1d03a..0000000000 --- a/test/js/node/test/parallel/buffer-failed-alloc-typed-arrays.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-buffer-failed-alloc-typed-arrays.js -//#SHA1: caa3a29c5ca1921e9ab5324d464067a364b8e687 -//----------------- -"use strict"; - -const { Buffer } = require("buffer"); -const SlowBuffer = require("buffer").SlowBuffer; - -// Test failed or zero-sized Buffer allocations not affecting typed arrays. -// This test exists because of a regression that occurred. Because Buffer -// instances are allocated with the same underlying allocator as TypedArrays, -// but Buffer's can optional be non-zero filled, there was a regression that -// occurred when a Buffer allocated failed, the internal flag specifying -// whether or not to zero-fill was not being reset, causing TypedArrays to -// allocate incorrectly. - -test("failed or zero-sized Buffer allocations do not affect typed arrays", () => { - const zeroArray = new Uint32Array(10).fill(0); - const sizes = [1e20, 0, 0.1, -1, "a", undefined, null, NaN]; - const allocators = [Buffer, SlowBuffer, Buffer.alloc, Buffer.allocUnsafe, Buffer.allocUnsafeSlow]; - - for (const allocator of allocators) { - for (const size of sizes) { - try { - // Some of these allocations are known to fail. If they do, - // Uint32Array should still produce a zeroed out result. - allocator(size); - } catch { - expect(new Uint32Array(10)).toEqual(zeroArray); - } - } - } -}); - -//<#END_FILE: test-buffer-failed-alloc-typed-arrays.js diff --git a/test/js/node/test/parallel/buffer-fill.test.js b/test/js/node/test/parallel/buffer-fill.test.js deleted file mode 100644 index f045645d93..0000000000 --- a/test/js/node/test/parallel/buffer-fill.test.js +++ /dev/null @@ -1,428 +0,0 @@ -//#FILE: test-buffer-fill.js -//#SHA1: 983940aa8a47c4d0985c2c4b4d1bc323a4e7d0f5 -//----------------- -"use strict"; - -const SIZE = 28; - -let buf1, buf2; - -beforeEach(() => { - buf1 = Buffer.allocUnsafe(SIZE); - buf2 = Buffer.allocUnsafe(SIZE); -}); - -// Helper functions -function genBuffer(size, args) { - const b = Buffer.allocUnsafe(size); - return b.fill(0).fill.apply(b, args); -} - -function bufReset() { - buf1.fill(0); - buf2.fill(0); -} - -function writeToFill(string, offset, end, encoding) { - if (typeof offset === "string") { - encoding = offset; - offset = 0; - end = buf2.length; - } else if (typeof end === "string") { - encoding = end; - end = buf2.length; - } else if (end === undefined) { - end = buf2.length; - } - - if (offset < 0 || end > buf2.length) throw new RangeError("ERR_OUT_OF_RANGE"); - - if (end <= offset) return buf2; - - offset >>>= 0; - end >>>= 0; - expect(offset).toBeLessThanOrEqual(buf2.length); - - const length = end - offset < 0 ? 0 : end - offset; - - let wasZero = false; - do { - const written = buf2.write(string, offset, length, encoding); - offset += written; - if (written === 0) { - if (wasZero) throw new Error("Could not write all data to Buffer"); - else wasZero = true; - } - } while (offset < buf2.length); - - return buf2; -} - -function testBufs(string, offset, length, encoding) { - bufReset(); - buf1.fill.apply(buf1, arguments); - expect(buf1.fill.apply(buf1, arguments)).toEqual(writeToFill.apply(null, arguments)); -} - -// Tests -test("Default encoding", () => { - testBufs("abc"); - testBufs("\u0222aa"); - testBufs("a\u0234b\u0235c\u0236"); - testBufs("abc", 4); - testBufs("abc", 5); - testBufs("abc", SIZE); - testBufs("\u0222aa", 2); - testBufs("\u0222aa", 8); - testBufs("a\u0234b\u0235c\u0236", 4); - testBufs("a\u0234b\u0235c\u0236", 12); - testBufs("abc", 4, 1); - testBufs("abc", 5, 1); - testBufs("\u0222aa", 8, 1); - testBufs("a\u0234b\u0235c\u0236", 4, 1); - testBufs("a\u0234b\u0235c\u0236", 12, 1); -}); - -test("UTF8 encoding", () => { - testBufs("abc", "utf8"); - testBufs("\u0222aa", "utf8"); - testBufs("a\u0234b\u0235c\u0236", "utf8"); - testBufs("abc", 4, "utf8"); - testBufs("abc", 5, "utf8"); - testBufs("abc", SIZE, "utf8"); - testBufs("\u0222aa", 2, "utf8"); - testBufs("\u0222aa", 8, "utf8"); - testBufs("a\u0234b\u0235c\u0236", 4, "utf8"); - testBufs("a\u0234b\u0235c\u0236", 12, "utf8"); - testBufs("abc", 4, 1, "utf8"); - testBufs("abc", 5, 1, "utf8"); - testBufs("\u0222aa", 8, 1, "utf8"); - testBufs("a\u0234b\u0235c\u0236", 4, 1, "utf8"); - testBufs("a\u0234b\u0235c\u0236", 12, 1, "utf8"); - expect(Buffer.allocUnsafe(1).fill(0).fill("\u0222")[0]).toBe(0xc8); -}); - -test("BINARY encoding", () => { - testBufs("abc", "binary"); - testBufs("\u0222aa", "binary"); - testBufs("a\u0234b\u0235c\u0236", "binary"); - testBufs("abc", 4, "binary"); - testBufs("abc", 5, "binary"); - testBufs("abc", SIZE, "binary"); - testBufs("\u0222aa", 2, "binary"); - testBufs("\u0222aa", 8, "binary"); - testBufs("a\u0234b\u0235c\u0236", 4, "binary"); - testBufs("a\u0234b\u0235c\u0236", 12, "binary"); - testBufs("abc", 4, 1, "binary"); - testBufs("abc", 5, 1, "binary"); - testBufs("\u0222aa", 8, 1, "binary"); - testBufs("a\u0234b\u0235c\u0236", 4, 1, "binary"); - testBufs("a\u0234b\u0235c\u0236", 12, 1, "binary"); -}); - -test("LATIN1 encoding", () => { - testBufs("abc", "latin1"); - testBufs("\u0222aa", "latin1"); - testBufs("a\u0234b\u0235c\u0236", "latin1"); - testBufs("abc", 4, "latin1"); - testBufs("abc", 5, "latin1"); - testBufs("abc", SIZE, "latin1"); - testBufs("\u0222aa", 2, "latin1"); - testBufs("\u0222aa", 8, "latin1"); - testBufs("a\u0234b\u0235c\u0236", 4, "latin1"); - testBufs("a\u0234b\u0235c\u0236", 12, "latin1"); - testBufs("abc", 4, 1, "latin1"); - testBufs("abc", 5, 1, "latin1"); - testBufs("\u0222aa", 8, 1, "latin1"); - testBufs("a\u0234b\u0235c\u0236", 4, 1, "latin1"); - testBufs("a\u0234b\u0235c\u0236", 12, 1, "latin1"); -}); - -test("UCS2 encoding", () => { - testBufs("abc", "ucs2"); - testBufs("\u0222aa", "ucs2"); - testBufs("a\u0234b\u0235c\u0236", "ucs2"); - testBufs("abc", 4, "ucs2"); - testBufs("abc", SIZE, "ucs2"); - testBufs("\u0222aa", 2, "ucs2"); - testBufs("\u0222aa", 8, "ucs2"); - testBufs("a\u0234b\u0235c\u0236", 4, "ucs2"); - testBufs("a\u0234b\u0235c\u0236", 12, "ucs2"); - testBufs("abc", 4, 1, "ucs2"); - testBufs("abc", 5, 1, "ucs2"); - testBufs("\u0222aa", 8, 1, "ucs2"); - testBufs("a\u0234b\u0235c\u0236", 4, 1, "ucs2"); - testBufs("a\u0234b\u0235c\u0236", 12, 1, "ucs2"); - expect(Buffer.allocUnsafe(1).fill("\u0222", "ucs2")[0]).toBe(0x22); -}); - -test("HEX encoding", () => { - testBufs("616263", "hex"); - testBufs("c8a26161", "hex"); - testBufs("61c8b462c8b563c8b6", "hex"); - testBufs("616263", 4, "hex"); - testBufs("616263", 5, "hex"); - testBufs("616263", SIZE, "hex"); - testBufs("c8a26161", 2, "hex"); - testBufs("c8a26161", 8, "hex"); - testBufs("61c8b462c8b563c8b6", 4, "hex"); - testBufs("61c8b462c8b563c8b6", 12, "hex"); - testBufs("616263", 4, 1, "hex"); - testBufs("616263", 5, 1, "hex"); - testBufs("c8a26161", 8, 1, "hex"); - testBufs("61c8b462c8b563c8b6", 4, 1, "hex"); - testBufs("61c8b462c8b563c8b6", 12, 1, "hex"); -}); - -test("Invalid HEX encoding", () => { - expect(() => { - const buf = Buffer.allocUnsafe(SIZE); - buf.fill("yKJh", "hex"); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_VALUE", - name: "TypeError", - }), - ); - - expect(() => { - const buf = Buffer.allocUnsafe(SIZE); - buf.fill("\u0222", "hex"); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_VALUE", - name: "TypeError", - }), - ); -}); - -test("BASE64 encoding", () => { - testBufs("YWJj", "base64"); - testBufs("yKJhYQ==", "base64"); - testBufs("Yci0Ysi1Y8i2", "base64"); - testBufs("YWJj", 4, "base64"); - testBufs("YWJj", SIZE, "base64"); - testBufs("yKJhYQ==", 2, "base64"); - testBufs("yKJhYQ==", 8, "base64"); - testBufs("Yci0Ysi1Y8i2", 4, "base64"); - testBufs("Yci0Ysi1Y8i2", 12, "base64"); - testBufs("YWJj", 4, 1, "base64"); - testBufs("YWJj", 5, 1, "base64"); - testBufs("yKJhYQ==", 8, 1, "base64"); - testBufs("Yci0Ysi1Y8i2", 4, 1, "base64"); - testBufs("Yci0Ysi1Y8i2", 12, 1, "base64"); -}); - -test("BASE64URL encoding", () => { - testBufs("YWJj", "base64url"); - testBufs("yKJhYQ", "base64url"); - testBufs("Yci0Ysi1Y8i2", "base64url"); - testBufs("YWJj", 4, "base64url"); - testBufs("YWJj", SIZE, "base64url"); - testBufs("yKJhYQ", 2, "base64url"); - testBufs("yKJhYQ", 8, "base64url"); - testBufs("Yci0Ysi1Y8i2", 4, "base64url"); - testBufs("Yci0Ysi1Y8i2", 12, "base64url"); - testBufs("YWJj", 4, 1, "base64url"); - testBufs("YWJj", 5, 1, "base64url"); - testBufs("yKJhYQ", 8, 1, "base64url"); - testBufs("Yci0Ysi1Y8i2", 4, 1, "base64url"); - testBufs("Yci0Ysi1Y8i2", 12, 1, "base64url"); -}); - -test("Buffer fill", () => { - function deepStrictEqualValues(buf, arr) { - for (const [index, value] of buf.entries()) { - expect(value).toBe(arr[index]); - } - } - - const buf2Fill = Buffer.allocUnsafe(1).fill(2); - deepStrictEqualValues(genBuffer(4, [buf2Fill]), [2, 2, 2, 2]); - deepStrictEqualValues(genBuffer(4, [buf2Fill, 1]), [0, 2, 2, 2]); - deepStrictEqualValues(genBuffer(4, [buf2Fill, 1, 3]), [0, 2, 2, 0]); - deepStrictEqualValues(genBuffer(4, [buf2Fill, 1, 1]), [0, 0, 0, 0]); - const hexBufFill = Buffer.allocUnsafe(2).fill(0).fill("0102", "hex"); - deepStrictEqualValues(genBuffer(4, [hexBufFill]), [1, 2, 1, 2]); - deepStrictEqualValues(genBuffer(4, [hexBufFill, 1]), [0, 1, 2, 1]); - deepStrictEqualValues(genBuffer(4, [hexBufFill, 1, 3]), [0, 1, 2, 0]); - deepStrictEqualValues(genBuffer(4, [hexBufFill, 1, 1]), [0, 0, 0, 0]); -}); - -test("Check exceptions", () => { - [ - [0, -1], - [0, 0, buf1.length + 1], - ["", -1], - ["", 0, buf1.length + 1], - ["", 1, -1], - ].forEach(args => { - expect(() => buf1.fill(...args)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - }), - ); - }); - - expect(() => buf1.fill("a", 0, buf1.length, "node rocks!")).toThrow( - expect.objectContaining({ - code: "ERR_UNKNOWN_ENCODING", - name: "TypeError", - message: "Unknown encoding: node rocks!", - }), - ); - - [ - ["a", 0, 0, NaN], - ["a", 0, 0, false], - ].forEach(args => { - expect(() => buf1.fill(...args)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.stringContaining('The "encoding" argument must be of type string'), - }), - ); - }); - - expect(() => buf1.fill("a", 0, 0, "foo")).toThrow( - expect.objectContaining({ - code: "ERR_UNKNOWN_ENCODING", - name: "TypeError", - message: "Unknown encoding: foo", - }), - ); -}); - -test("Out of range errors", () => { - expect(() => Buffer.allocUnsafe(8).fill("a", -1)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - }), - ); - expect(() => Buffer.allocUnsafe(8).fill("a", 0, 9)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - }), - ); -}); - -test("Empty fill", () => { - Buffer.allocUnsafe(8).fill(""); - Buffer.alloc(8, ""); -}); - -test("Buffer allocation and fill", () => { - const buf = Buffer.alloc(64, 10); - for (let i = 0; i < buf.length; i++) expect(buf[i]).toBe(10); - - buf.fill(11, 0, buf.length >> 1); - for (let i = 0; i < buf.length >> 1; i++) expect(buf[i]).toBe(11); - for (let i = (buf.length >> 1) + 1; i < buf.length; i++) expect(buf[i]).toBe(10); - - buf.fill("h"); - for (let i = 0; i < buf.length; i++) expect(buf[i]).toBe("h".charCodeAt(0)); - - buf.fill(0); - for (let i = 0; i < buf.length; i++) expect(buf[i]).toBe(0); - - buf.fill(null); - for (let i = 0; i < buf.length; i++) expect(buf[i]).toBe(0); - - buf.fill(1, 16, 32); - for (let i = 0; i < 16; i++) expect(buf[i]).toBe(0); - for (let i = 16; i < 32; i++) expect(buf[i]).toBe(1); - for (let i = 32; i < buf.length; i++) expect(buf[i]).toBe(0); -}); - -test("Buffer fill with string", () => { - const buf = Buffer.alloc(10, "abc"); - expect(buf.toString()).toBe("abcabcabca"); - buf.fill("է"); - expect(buf.toString()).toBe("էէէէէ"); -}); - -test("Buffer fill with invalid end", () => { - expect(() => { - const end = { - [Symbol.toPrimitive]() { - return 1; - }, - }; - Buffer.alloc(1).fill(Buffer.alloc(1), 0, end); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.stringContaining('The "end" argument must be of type number. Received'), - }), - ); -}); - -test.todo("Buffer fill with invalid length", () => { - expect(() => { - const buf = Buffer.from("w00t"); - Object.defineProperty(buf, "length", { - value: 1337, - enumerable: true, - }); - buf.fill(""); - }).toThrow( - expect.objectContaining({ - code: "ERR_BUFFER_OUT_OF_BOUNDS", - name: "RangeError", - message: "Attempt to access memory outside buffer bounds", - }), - ); -}); - -test("Buffer fill with utf16le encoding", () => { - expect(Buffer.allocUnsafeSlow(16).fill("ab", "utf16le")).toEqual( - Buffer.from("61006200610062006100620061006200", "hex"), - ); - - expect(Buffer.allocUnsafeSlow(15).fill("ab", "utf16le")).toEqual( - Buffer.from("610062006100620061006200610062", "hex"), - ); - - expect(Buffer.allocUnsafeSlow(16).fill("ab", "utf16le")).toEqual( - Buffer.from("61006200610062006100620061006200", "hex"), - ); - expect(Buffer.allocUnsafeSlow(16).fill("a", "utf16le")).toEqual( - Buffer.from("61006100610061006100610061006100", "hex"), - ); - - expect(Buffer.allocUnsafeSlow(16).fill("a", "utf16le").toString("utf16le")).toBe("a".repeat(8)); - expect(Buffer.allocUnsafeSlow(16).fill("a", "latin1").toString("latin1")).toBe("a".repeat(16)); - expect(Buffer.allocUnsafeSlow(16).fill("a", "utf8").toString("utf8")).toBe("a".repeat(16)); - - expect(Buffer.allocUnsafeSlow(16).fill("Љ", "utf16le").toString("utf16le")).toBe("Љ".repeat(8)); - expect(Buffer.allocUnsafeSlow(16).fill("Љ", "latin1").toString("latin1")).toBe("\t".repeat(16)); - expect(Buffer.allocUnsafeSlow(16).fill("Љ", "utf8").toString("utf8")).toBe("Љ".repeat(8)); -}); - -test("Buffer fill with invalid hex encoding", () => { - expect(() => { - const buf = Buffer.from("a".repeat(1000)); - buf.fill("This is not correctly encoded", "hex"); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_VALUE", - name: "TypeError", - }), - ); -}); - -test("Buffer fill with empty values", () => { - const bufEmptyString = Buffer.alloc(5, ""); - expect(bufEmptyString.toString()).toBe("\x00\x00\x00\x00\x00"); - - const bufEmptyArray = Buffer.alloc(5, []); - expect(bufEmptyArray.toString()).toBe("\x00\x00\x00\x00\x00"); - - const bufEmptyBuffer = Buffer.alloc(5, Buffer.alloc(5)); - expect(bufEmptyBuffer.toString()).toBe("\x00\x00\x00\x00\x00"); - - const bufZero = Buffer.alloc(5, 0); - expect(bufZero.toString()).toBe("\x00\x00\x00\x00\x00"); -}); - -//<#END_FILE: test-buffer-fill.js diff --git a/test/js/node/test/parallel/buffer-from.test.js b/test/js/node/test/parallel/buffer-from.test.js deleted file mode 100644 index 0d089d4e8c..0000000000 --- a/test/js/node/test/parallel/buffer-from.test.js +++ /dev/null @@ -1,168 +0,0 @@ -//#FILE: test-buffer-from.js -//#SHA1: fdbb08fe98b94d1566ade587f17bb970130e1edd -//----------------- -"use strict"; - -const { runInNewContext } = require("vm"); - -const checkString = "test"; - -const check = Buffer.from(checkString); - -class MyString extends String { - constructor() { - super(checkString); - } -} - -class MyPrimitive { - [Symbol.toPrimitive]() { - return checkString; - } -} - -class MyBadPrimitive { - [Symbol.toPrimitive]() { - return 1; - } -} - -test("Buffer.from with various string-like inputs", () => { - expect(Buffer.from(new String(checkString))).toStrictEqual(check); - expect(Buffer.from(new MyString())).toStrictEqual(check); - expect(Buffer.from(new MyPrimitive())).toStrictEqual(check); - // expect(Buffer.from(runInNewContext("new String(checkString)", { checkString }))).toStrictEqual(check); //TODO: -}); - -describe("Buffer.from with invalid inputs", () => { - const invalidInputs = [ - {}, - new Boolean(true), - { - valueOf() { - return null; - }, - }, - { - valueOf() { - return undefined; - }, - }, - { valueOf: null }, - { __proto__: null }, - new Number(true), - new MyBadPrimitive(), - Symbol(), - 5n, - (one, two, three) => {}, - undefined, - null, - ]; - - for (const input of invalidInputs) { - test(`${Bun.inspect(input)}`, () => { - expect(() => Buffer.from(input)).toThrow( - expect.objectContaining({ - // code: "ERR_INVALID_ARG_TYPE", //TODO: - name: "TypeError", - message: expect.any(String), - }), - ); - expect(() => Buffer.from(input, "hex")).toThrow( - expect.objectContaining({ - // code: "ERR_INVALID_ARG_TYPE", //TODO: - name: "TypeError", - message: expect.any(String), - }), - ); - }); - } -}); - -test("Buffer.allocUnsafe and Buffer.from with valid inputs", () => { - expect(() => Buffer.allocUnsafe(10)).not.toThrow(); - expect(() => Buffer.from("deadbeaf", "hex")).not.toThrow(); -}); - -test("Buffer.copyBytesFrom with Uint16Array", () => { - const u16 = new Uint16Array([0xffff]); - const b16 = Buffer.copyBytesFrom(u16); - u16[0] = 0; - expect(b16.length).toBe(2); - expect(b16[0]).toBe(255); - expect(b16[1]).toBe(255); -}); - -test("Buffer.copyBytesFrom with Uint16Array and offset", () => { - const u16 = new Uint16Array([0, 0xffff]); - const b16 = Buffer.copyBytesFrom(u16, 1, 5); - u16[0] = 0xffff; - u16[1] = 0; - expect(b16.length).toBe(2); - expect(b16[0]).toBe(255); - expect(b16[1]).toBe(255); -}); - -test("Buffer.copyBytesFrom with Uint32Array", () => { - const u32 = new Uint32Array([0xffffffff]); - const b32 = Buffer.copyBytesFrom(u32); - u32[0] = 0; - expect(b32.length).toBe(4); - expect(b32[0]).toBe(255); - expect(b32[1]).toBe(255); - expect(b32[2]).toBe(255); - expect(b32[3]).toBe(255); -}); - -test("Buffer.copyBytesFrom with invalid inputs", () => { - expect(() => Buffer.copyBytesFrom()).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - - const invalidInputs = ["", Symbol(), true, false, {}, [], () => {}, 1, 1n, null, undefined]; - invalidInputs.forEach(notTypedArray => { - expect(() => Buffer.copyBytesFrom(notTypedArray)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - }); - - const invalidSecondArgs = ["", Symbol(), true, false, {}, [], () => {}, 1n]; - invalidSecondArgs.forEach(notANumber => { - expect(() => Buffer.copyBytesFrom(new Uint8Array(1), notANumber)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - }); - - const outOfRangeInputs = [-1, NaN, 1.1, -Infinity]; - outOfRangeInputs.forEach(outOfRange => { - expect(() => Buffer.copyBytesFrom(new Uint8Array(1), outOfRange)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - }), - ); - }); - - invalidSecondArgs.forEach(notANumber => { - expect(() => Buffer.copyBytesFrom(new Uint8Array(1), 0, notANumber)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - }); - - outOfRangeInputs.forEach(outOfRange => { - expect(() => Buffer.copyBytesFrom(new Uint8Array(1), 0, outOfRange)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - }), - ); - }); -}); - -//<#END_FILE: test-buffer-from.js diff --git a/test/js/node/test/parallel/buffer-inheritance.test.js b/test/js/node/test/parallel/buffer-inheritance.test.js deleted file mode 100644 index 5939621c9f..0000000000 --- a/test/js/node/test/parallel/buffer-inheritance.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-buffer-inheritance.js -//#SHA1: 01cba7d2cb76cb1d00fa91b3666dc58333b66e1b -//----------------- -"use strict"; - -test("Buffer inheritance", () => { - function T(n) { - const ui8 = new Uint8Array(n); - Object.setPrototypeOf(ui8, T.prototype); - return ui8; - } - Object.setPrototypeOf(T.prototype, Buffer.prototype); - Object.setPrototypeOf(T, Buffer); - - T.prototype.sum = function sum() { - let cntr = 0; - for (let i = 0; i < this.length; i++) cntr += this[i]; - return cntr; - }; - - const vals = [new T(4), T(4)]; - - vals.forEach(function (t) { - expect(t.constructor).toBe(T); - expect(Object.getPrototypeOf(t)).toBe(T.prototype); - expect(Object.getPrototypeOf(Object.getPrototypeOf(t))).toBe(Buffer.prototype); - - t.fill(5); - let cntr = 0; - for (let i = 0; i < t.length; i++) cntr += t[i]; - expect(cntr).toBe(t.length * 5); - - // Check this does not throw - expect(() => t.toString()).not.toThrow(); - }); -}); - -//<#END_FILE: test-buffer-inheritance.js diff --git a/test/js/node/test/parallel/buffer-inspect.test.js b/test/js/node/test/parallel/buffer-inspect.test.js deleted file mode 100644 index d1ba515755..0000000000 --- a/test/js/node/test/parallel/buffer-inspect.test.js +++ /dev/null @@ -1,98 +0,0 @@ -//#FILE: test-buffer-inspect.js -//#SHA1: 8578a4ec2de348a758e5c4dcbaa13a2ee7005451 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const util = require("util"); -const buffer = require("buffer"); - -describe("Buffer inspect", () => { - beforeEach(() => { - buffer.INSPECT_MAX_BYTES = 2; - }); - - afterEach(() => { - buffer.INSPECT_MAX_BYTES = Infinity; - }); - - test("Buffer and SlowBuffer inspection with INSPECT_MAX_BYTES = 2", () => { - const b = Buffer.allocUnsafe(4); - b.fill("1234"); - - const s = buffer.SlowBuffer(4); - s.fill("1234"); - - const expected = "Buffer(4) [Uint8Array] [ 49, 50, ... 2 more items ]"; - - expect(util.inspect(b)).toBe(expected); - expect(util.inspect(s)).toBe(expected); - }); - - test("Buffer and SlowBuffer inspection with 2 bytes", () => { - const b = Buffer.allocUnsafe(2); - b.fill("12"); - - const s = buffer.SlowBuffer(2); - s.fill("12"); - - const expected = "Buffer(2) [Uint8Array] [ 49, 50 ]"; - - expect(util.inspect(b)).toBe(expected); - expect(util.inspect(s)).toBe(expected); - }); - - test("Buffer and SlowBuffer inspection with INSPECT_MAX_BYTES = Infinity", () => { - const b = Buffer.allocUnsafe(2); - b.fill("12"); - - const s = buffer.SlowBuffer(2); - s.fill("12"); - - const expected = "Buffer(2) [Uint8Array] [ 49, 50 ]"; - - buffer.INSPECT_MAX_BYTES = Infinity; - - expect(util.inspect(b)).toBe(expected); - expect(util.inspect(s)).toBe(expected); - }); - - test("Buffer inspection with custom properties", () => { - const b = Buffer.allocUnsafe(2); - b.fill("12"); - b.inspect = undefined; - b.prop = new Uint8Array(0); - - expect(util.inspect(b)).toBe( - "Buffer(2) [Uint8Array] [\n 49,\n 50,\n inspect: undefined,\n prop: Uint8Array(0) []\n]", - ); - }); - - test("Empty Buffer inspection with custom property", () => { - const b = Buffer.alloc(0); - b.prop = 123; - - expect(util.inspect(b)).toBe("Buffer(0) [Uint8Array] [ prop: 123 ]"); - }); -}); - -//<#END_FILE: test-buffer-inspect.js diff --git a/test/js/node/test/parallel/buffer-isascii.test.js b/test/js/node/test/parallel/buffer-isascii.test.js deleted file mode 100644 index a8fde2110a..0000000000 --- a/test/js/node/test/parallel/buffer-isascii.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-buffer-isascii.js -//#SHA1: e49cbd0752feaa8042a90129dfb38610eb002ee6 -//----------------- -"use strict"; - -const { isAscii, Buffer } = require("buffer"); -const { TextEncoder } = require("util"); - -const encoder = new TextEncoder(); - -test("isAscii function", () => { - expect(isAscii(encoder.encode("hello"))).toBe(true); - expect(isAscii(encoder.encode("ğ"))).toBe(false); - expect(isAscii(Buffer.from([]))).toBe(true); -}); - -test("isAscii with invalid inputs", () => { - const invalidInputs = [undefined, "", "hello", false, true, 0, 1, 0n, 1n, Symbol(), () => {}, {}, [], null]; - - invalidInputs.forEach(input => { - expect(() => isAscii(input)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - }); -}); - -test("isAscii with detached array buffer", () => { - const arrayBuffer = new ArrayBuffer(1024); - structuredClone(arrayBuffer, { transfer: [arrayBuffer] }); - - expect(() => isAscii(arrayBuffer)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_STATE", - }), - ); -}); - -//<#END_FILE: test-buffer-isascii.js diff --git a/test/js/node/test/parallel/buffer-isencoding.test.js b/test/js/node/test/parallel/buffer-isencoding.test.js deleted file mode 100644 index 010d80ca3a..0000000000 --- a/test/js/node/test/parallel/buffer-isencoding.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-buffer-isencoding.js -//#SHA1: 438625bd1ca2a23aa8716bea5334f3ac07eb040f -//----------------- -"use strict"; - -describe("Buffer.isEncoding", () => { - describe("should return true for valid encodings", () => { - const validEncodings = [ - "hex", - "utf8", - "utf-8", - "ascii", - "latin1", - "binary", - "base64", - "base64url", - "ucs2", - "ucs-2", - "utf16le", - "utf-16le", - ]; - - for (const enc of validEncodings) { - test(`${enc}`, () => { - expect(Buffer.isEncoding(enc)).toBe(true); - }); - } - }); - - describe("should return false for invalid encodings", () => { - const invalidEncodings = ["utf9", "utf-7", "Unicode-FTW", "new gnu gun", false, NaN, {}, Infinity, [], 1, 0, -1]; - - for (const enc of invalidEncodings) { - test(`${enc}`, () => { - expect(Buffer.isEncoding(enc)).toBe(false); - }); - } - }); -}); - -//<#END_FILE: test-buffer-isencoding.js diff --git a/test/js/node/test/parallel/buffer-isutf8.test.js b/test/js/node/test/parallel/buffer-isutf8.test.js deleted file mode 100644 index fa55fb4c5b..0000000000 --- a/test/js/node/test/parallel/buffer-isutf8.test.js +++ /dev/null @@ -1,90 +0,0 @@ -//#FILE: test-buffer-isutf8.js -//#SHA1: 47438bb1ade853e71f1266144d24188ebe75e714 -//----------------- -"use strict"; - -const { isUtf8, Buffer } = require("buffer"); -const { TextEncoder } = require("util"); - -const encoder = new TextEncoder(); - -test("isUtf8 validation", () => { - expect(isUtf8(encoder.encode("hello"))).toBe(true); - expect(isUtf8(encoder.encode("ğ"))).toBe(true); - expect(isUtf8(Buffer.from([]))).toBe(true); -}); - -// Taken from test/fixtures/wpt/encoding/textdecoder-fatal.any.js -test("invalid UTF-8 sequences", () => { - const invalidSequences = [ - [0xff], // 'invalid code' - [0xc0], // 'ends early' - [0xe0], // 'ends early 2' - [0xc0, 0x00], // 'invalid trail' - [0xc0, 0xc0], // 'invalid trail 2' - [0xe0, 0x00], // 'invalid trail 3' - [0xe0, 0xc0], // 'invalid trail 4' - [0xe0, 0x80, 0x00], // 'invalid trail 5' - [0xe0, 0x80, 0xc0], // 'invalid trail 6' - [0xfc, 0x80, 0x80, 0x80, 0x80, 0x80], // '> 0x10FFFF' - [0xfe, 0x80, 0x80, 0x80, 0x80, 0x80], // 'obsolete lead byte' - - // Overlong encodings - [0xc0, 0x80], // 'overlong U+0000 - 2 bytes' - [0xe0, 0x80, 0x80], // 'overlong U+0000 - 3 bytes' - [0xf0, 0x80, 0x80, 0x80], // 'overlong U+0000 - 4 bytes' - [0xf8, 0x80, 0x80, 0x80, 0x80], // 'overlong U+0000 - 5 bytes' - [0xfc, 0x80, 0x80, 0x80, 0x80, 0x80], // 'overlong U+0000 - 6 bytes' - - [0xc1, 0xbf], // 'overlong U+007F - 2 bytes' - [0xe0, 0x81, 0xbf], // 'overlong U+007F - 3 bytes' - [0xf0, 0x80, 0x81, 0xbf], // 'overlong U+007F - 4 bytes' - [0xf8, 0x80, 0x80, 0x81, 0xbf], // 'overlong U+007F - 5 bytes' - [0xfc, 0x80, 0x80, 0x80, 0x81, 0xbf], // 'overlong U+007F - 6 bytes' - - [0xe0, 0x9f, 0xbf], // 'overlong U+07FF - 3 bytes' - [0xf0, 0x80, 0x9f, 0xbf], // 'overlong U+07FF - 4 bytes' - [0xf8, 0x80, 0x80, 0x9f, 0xbf], // 'overlong U+07FF - 5 bytes' - [0xfc, 0x80, 0x80, 0x80, 0x9f, 0xbf], // 'overlong U+07FF - 6 bytes' - - [0xf0, 0x8f, 0xbf, 0xbf], // 'overlong U+FFFF - 4 bytes' - [0xf8, 0x80, 0x8f, 0xbf, 0xbf], // 'overlong U+FFFF - 5 bytes' - [0xfc, 0x80, 0x80, 0x8f, 0xbf, 0xbf], // 'overlong U+FFFF - 6 bytes' - - [0xf8, 0x84, 0x8f, 0xbf, 0xbf], // 'overlong U+10FFFF - 5 bytes' - [0xfc, 0x80, 0x84, 0x8f, 0xbf, 0xbf], // 'overlong U+10FFFF - 6 bytes' - - // UTF-16 surrogates encoded as code points in UTF-8 - [0xed, 0xa0, 0x80], // 'lead surrogate' - [0xed, 0xb0, 0x80], // 'trail surrogate' - [0xed, 0xa0, 0x80, 0xed, 0xb0, 0x80], // 'surrogate pair' - ]; - - invalidSequences.forEach(input => { - expect(isUtf8(Buffer.from(input))).toBe(false); - }); -}); - -test("invalid input types", () => { - const invalidInputs = [null, undefined, "hello", true, false]; - - invalidInputs.forEach(input => { - expect(() => isUtf8(input)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - }); -}); - -test("detached array buffer", () => { - const arrayBuffer = new ArrayBuffer(1024); - structuredClone(arrayBuffer, { transfer: [arrayBuffer] }); - expect(() => isUtf8(arrayBuffer)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_STATE", - }), - ); -}); - -//<#END_FILE: test-buffer-isutf8.js diff --git a/test/js/node/test/parallel/buffer-iterator.test.js b/test/js/node/test/parallel/buffer-iterator.test.js deleted file mode 100644 index ca9cacb93d..0000000000 --- a/test/js/node/test/parallel/buffer-iterator.test.js +++ /dev/null @@ -1,72 +0,0 @@ -//#FILE: test-buffer-iterator.js -//#SHA1: cd9bcdf671dc11d86bd194aa3e7500041f03eb4c -//----------------- -"use strict"; - -// Buffers should be iterable -test("Buffers are iterable", () => { - const buffer = Buffer.from([1, 2, 3, 4, 5]); - const arr = []; - - for (const b of buffer) { - arr.push(b); - } - - expect(arr).toEqual([1, 2, 3, 4, 5]); -}); - -// Buffer iterators should be iterable -test("Buffer iterators are iterable", () => { - const buffer = Buffer.from([1, 2, 3, 4, 5]); - const arr = []; - - for (const b of buffer[Symbol.iterator]()) { - arr.push(b); - } - - expect(arr).toEqual([1, 2, 3, 4, 5]); -}); - -// buffer#values() should return iterator for values -test("buffer.values() returns iterator for values", () => { - const buffer = Buffer.from([1, 2, 3, 4, 5]); - const arr = []; - - for (const b of buffer.values()) { - arr.push(b); - } - - expect(arr).toEqual([1, 2, 3, 4, 5]); -}); - -// buffer#keys() should return iterator for keys -test("buffer.keys() returns iterator for keys", () => { - const buffer = Buffer.from([1, 2, 3, 4, 5]); - const arr = []; - - for (const b of buffer.keys()) { - arr.push(b); - } - - expect(arr).toEqual([0, 1, 2, 3, 4]); -}); - -// buffer#entries() should return iterator for entries -test("buffer.entries() returns iterator for entries", () => { - const buffer = Buffer.from([1, 2, 3, 4, 5]); - const arr = []; - - for (const b of buffer.entries()) { - arr.push(b); - } - - expect(arr).toEqual([ - [0, 1], - [1, 2], - [2, 3], - [3, 4], - [4, 5], - ]); -}); - -//<#END_FILE: test-buffer-iterator.js diff --git a/test/js/node/test/parallel/buffer-new.test.js b/test/js/node/test/parallel/buffer-new.test.js deleted file mode 100644 index 7f85579624..0000000000 --- a/test/js/node/test/parallel/buffer-new.test.js +++ /dev/null @@ -1,14 +0,0 @@ -//#FILE: test-buffer-new.js -//#SHA1: 56270fc6342f4ac15433cce1e1b1252ac4dcbb98 -//----------------- -"use strict"; - -test("Buffer constructor with invalid arguments", () => { - expect(() => new Buffer(42, "utf8")).toThrow({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: `The "string" argument must be of type string. Received 42`, - }); -}); - -//<#END_FILE: test-buffer-new.js diff --git a/test/js/node/test/parallel/buffer-no-negative-allocation.test.js b/test/js/node/test/parallel/buffer-no-negative-allocation.test.js deleted file mode 100644 index 2158402336..0000000000 --- a/test/js/node/test/parallel/buffer-no-negative-allocation.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-buffer-no-negative-allocation.js -//#SHA1: c7f13ec857490bc5d1ffbf8da3fff19049c421f8 -//----------------- -"use strict"; - -const { SlowBuffer } = require("buffer"); - -// Test that negative Buffer length inputs throw errors. - -const msg = expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), -}); - -test("Buffer constructor throws on negative or NaN length", () => { - expect(() => Buffer(-Buffer.poolSize)).toThrow(msg); - expect(() => Buffer(-100)).toThrow(msg); - expect(() => Buffer(-1)).toThrow(msg); - expect(() => Buffer(NaN)).toThrow(msg); -}); - -test("Buffer.alloc throws on negative or NaN length", () => { - expect(() => Buffer.alloc(-Buffer.poolSize)).toThrow(msg); - expect(() => Buffer.alloc(-100)).toThrow(msg); - expect(() => Buffer.alloc(-1)).toThrow(msg); - expect(() => Buffer.alloc(NaN)).toThrow(msg); -}); - -test("Buffer.allocUnsafe throws on negative or NaN length", () => { - expect(() => Buffer.allocUnsafe(-Buffer.poolSize)).toThrow(msg); - expect(() => Buffer.allocUnsafe(-100)).toThrow(msg); - expect(() => Buffer.allocUnsafe(-1)).toThrow(msg); - expect(() => Buffer.allocUnsafe(NaN)).toThrow(msg); -}); - -test("Buffer.allocUnsafeSlow throws on negative or NaN length", () => { - expect(() => Buffer.allocUnsafeSlow(-Buffer.poolSize)).toThrow(msg); - expect(() => Buffer.allocUnsafeSlow(-100)).toThrow(msg); - expect(() => Buffer.allocUnsafeSlow(-1)).toThrow(msg); - expect(() => Buffer.allocUnsafeSlow(NaN)).toThrow(msg); -}); - -test("SlowBuffer throws on negative or NaN length", () => { - expect(() => SlowBuffer(-Buffer.poolSize)).toThrow(msg); - expect(() => SlowBuffer(-100)).toThrow(msg); - expect(() => SlowBuffer(-1)).toThrow(msg); - expect(() => SlowBuffer(NaN)).toThrow(msg); -}); - -//<#END_FILE: test-buffer-no-negative-allocation.js diff --git a/test/js/node/test/parallel/buffer-nopendingdep-map.test.js b/test/js/node/test/parallel/buffer-nopendingdep-map.test.js deleted file mode 100644 index 5fc9d4efad..0000000000 --- a/test/js/node/test/parallel/buffer-nopendingdep-map.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-buffer-nopendingdep-map.js -//#SHA1: 908b5747ec3c5873c180b66b6e50221fd29169e3 -//----------------- -// Flags: --no-warnings --pending-deprecation -"use strict"; - -test("Buffer methods should not emit deprecation warnings with --pending-deprecation", () => { - const warningListener = jest.fn(); - process.on("warning", warningListener); - - // With the --pending-deprecation flag, the deprecation warning for - // new Buffer() should not be emitted when Uint8Array methods are called. - - Buffer.from("abc").map(i => i); - Buffer.from("abc").filter(i => i); - Buffer.from("abc").slice(1, 2); - - expect(warningListener).not.toHaveBeenCalled(); - - process.removeListener("warning", warningListener); -}); - -//<#END_FILE: test-buffer-nopendingdep-map.js diff --git a/test/js/node/test/parallel/buffer-of-no-deprecation.test.js b/test/js/node/test/parallel/buffer-of-no-deprecation.test.js deleted file mode 100644 index 53c31f103c..0000000000 --- a/test/js/node/test/parallel/buffer-of-no-deprecation.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-buffer-of-no-deprecation.js -//#SHA1: 7c233f8a82411a5d1c293daecef6494d02d7dabf -//----------------- -"use strict"; - -test("Buffer.of() should not emit deprecation warning", () => { - const warningListener = jest.fn(); - process.on("warning", warningListener); - - Buffer.of(0, 1); - - expect(warningListener).not.toHaveBeenCalled(); - - // Clean up the listener - process.removeListener("warning", warningListener); -}); - -//<#END_FILE: test-buffer-of-no-deprecation.js diff --git a/test/js/node/test/parallel/buffer-over-max-length.test.js b/test/js/node/test/parallel/buffer-over-max-length.test.js deleted file mode 100644 index 5ba6d6af4e..0000000000 --- a/test/js/node/test/parallel/buffer-over-max-length.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-buffer-over-max-length.js -//#SHA1: 797cb237a889a5f09d34b2554a46eb4c545f885e -//----------------- -"use strict"; - -const buffer = require("buffer"); -const SlowBuffer = buffer.SlowBuffer; - -const kMaxLength = buffer.kMaxLength; -const bufferMaxSizeMsg = expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.stringContaining(`The value of "size" is out of range.`), -}); - -test("Buffer creation with over max length", () => { - expect(() => Buffer(kMaxLength + 1)).toThrow(bufferMaxSizeMsg); - expect(() => SlowBuffer(kMaxLength + 1)).toThrow(bufferMaxSizeMsg); - expect(() => Buffer.alloc(kMaxLength + 1)).toThrow(bufferMaxSizeMsg); - expect(() => Buffer.allocUnsafe(kMaxLength + 1)).toThrow(bufferMaxSizeMsg); - expect(() => Buffer.allocUnsafeSlow(kMaxLength + 1)).toThrow(bufferMaxSizeMsg); -}); - -//<#END_FILE: test-buffer-over-max-length.js diff --git a/test/js/node/test/parallel/buffer-parent-property.test.js b/test/js/node/test/parallel/buffer-parent-property.test.js deleted file mode 100644 index ebf02d3652..0000000000 --- a/test/js/node/test/parallel/buffer-parent-property.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-buffer-parent-property.js -//#SHA1: 1496dde41464d188eecd053b64a320c71f62bd7d -//----------------- -"use strict"; - -// Fix for https://github.com/nodejs/node/issues/8266 -// -// Zero length Buffer objects should expose the `buffer` property of the -// TypedArrays, via the `parent` property. - -test("Buffer parent property", () => { - // If the length of the buffer object is zero - expect(Buffer.alloc(0).parent).toBeInstanceOf(ArrayBuffer); - - // If the length of the buffer object is equal to the underlying ArrayBuffer - expect(Buffer.alloc(Buffer.poolSize).parent).toBeInstanceOf(ArrayBuffer); - - // Same as the previous test, but with user created buffer - const arrayBuffer = new ArrayBuffer(0); - expect(Buffer.from(arrayBuffer).parent).toBe(arrayBuffer); - expect(Buffer.from(arrayBuffer).buffer).toBe(arrayBuffer); - expect(Buffer.from(arrayBuffer).parent).toBe(arrayBuffer); - expect(Buffer.from(arrayBuffer).buffer).toBe(arrayBuffer); -}); - -//<#END_FILE: test-buffer-parent-property.js diff --git a/test/js/node/test/parallel/buffer-prototype-inspect.test.js b/test/js/node/test/parallel/buffer-prototype-inspect.test.js deleted file mode 100644 index f6bb9a8915..0000000000 --- a/test/js/node/test/parallel/buffer-prototype-inspect.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-buffer-prototype-inspect.js -//#SHA1: 3809d957d94134495a61469120087c12580fa3f3 -//----------------- -"use strict"; - -// lib/buffer.js defines Buffer.prototype.inspect() to override how buffers are -// presented by util.inspect(). - -const util = require("util"); -const buffer = require("buffer"); -buffer.INSPECT_MAX_BYTES = 50; - -test("Buffer.prototype.inspect() for non-empty buffer", () => { - const buf = Buffer.from("fhqwhgads"); - expect(util.inspect(buf)).toBe("Buffer(9) [Uint8Array] [\n 102, 104, 113, 119,\n 104, 103, 97, 100,\n 115\n]"); -}); - -test("Buffer.prototype.inspect() for empty buffer", () => { - const buf = Buffer.from(""); - expect(util.inspect(buf)).toBe("Buffer(0) [Uint8Array] []"); -}); - -test("Buffer.prototype.inspect() for large buffer", () => { - const buf = Buffer.from("x".repeat(51)); - expect(util.inspect(buf)).toBe( - `Buffer(51) [Uint8Array] [\n` + - ` 120, 120, 120, 120, 120, 120, 120, 120, 120,\n` + - ` 120, 120, 120, 120, 120, 120, 120, 120, 120,\n` + - ` 120, 120, 120, 120, 120, 120, 120, 120, 120,\n` + - ` 120, 120, 120, 120, 120, 120, 120, 120, 120,\n` + - ` 120, 120, 120, 120, 120, 120, 120, 120, 120,\n` + - ` 120, 120, 120, 120, 120,\n` + - ` ... 1 more item\n` + - `]`, - ); -}); - -//<#END_FILE: test-buffer-prototype-inspect.js diff --git a/test/js/node/test/parallel/buffer-safe-unsafe.test.js b/test/js/node/test/parallel/buffer-safe-unsafe.test.js deleted file mode 100644 index 84e1db9096..0000000000 --- a/test/js/node/test/parallel/buffer-safe-unsafe.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-buffer-safe-unsafe.js -//#SHA1: 87831e463ab52a79fca3ac2e28eec57666ea9e5e -//----------------- -"use strict"; - -test("Buffer safe and unsafe allocations", () => { - const safe = Buffer.alloc(10); - - function isZeroFilled(buf) { - for (let n = 0; n < buf.length; n++) if (buf[n] !== 0) return false; - return true; - } - - expect(isZeroFilled(safe)).toBe(true); - - // Test that unsafe allocations doesn't affect subsequent safe allocations - Buffer.allocUnsafe(10); - expect(isZeroFilled(new Float64Array(10))).toBe(true); - - new Buffer(10); - expect(isZeroFilled(new Float64Array(10))).toBe(true); - - Buffer.allocUnsafe(10); - expect(isZeroFilled(Buffer.alloc(10))).toBe(true); -}); - -//<#END_FILE: test-buffer-safe-unsafe.js diff --git a/test/js/node/test/parallel/buffer-set-inspect-max-bytes.test.js b/test/js/node/test/parallel/buffer-set-inspect-max-bytes.test.js deleted file mode 100644 index 306fa0f81b..0000000000 --- a/test/js/node/test/parallel/buffer-set-inspect-max-bytes.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-buffer-set-inspect-max-bytes.js -//#SHA1: de73b2a241585e1cf17a057d21cdbabbadf963bb -//----------------- -"use strict"; - -const buffer = require("buffer"); - -describe("buffer.INSPECT_MAX_BYTES", () => { - const rangeErrorObjs = [NaN, -1]; - const typeErrorObj = "and even this"; - - test.each(rangeErrorObjs)("throws RangeError for invalid value: %p", obj => { - expect(() => { - buffer.INSPECT_MAX_BYTES = obj; - }).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - }); - - test("throws TypeError for invalid type", () => { - expect(() => { - buffer.INSPECT_MAX_BYTES = typeErrorObj; - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-buffer-set-inspect-max-bytes.js diff --git a/test/js/node/test/parallel/buffer-slice.test.js b/test/js/node/test/parallel/buffer-slice.test.js deleted file mode 100644 index 5fde7bdf62..0000000000 --- a/test/js/node/test/parallel/buffer-slice.test.js +++ /dev/null @@ -1,114 +0,0 @@ -//#FILE: test-buffer-slice.js -//#SHA1: 1f7289de3a6dd5167f3659cd5119e6489b059d43 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -test("Buffer slice operations", () => { - expect(Buffer.from("hello", "utf8").slice(0, 0).length).toBe(0); - expect(Buffer("hello", "utf8").slice(0, 0).length).toBe(0); - - const buf = Buffer.from("0123456789", "utf8"); - const expectedSameBufs = [ - [buf.slice(-10, 10), Buffer.from("0123456789", "utf8")], - [buf.slice(-20, 10), Buffer.from("0123456789", "utf8")], - [buf.slice(-20, -10), Buffer.from("", "utf8")], - [buf.slice(), Buffer.from("0123456789", "utf8")], - [buf.slice(0), Buffer.from("0123456789", "utf8")], - [buf.slice(0, 0), Buffer.from("", "utf8")], - [buf.slice(undefined), Buffer.from("0123456789", "utf8")], - [buf.slice("foobar"), Buffer.from("0123456789", "utf8")], - [buf.slice(undefined, undefined), Buffer.from("0123456789", "utf8")], - [buf.slice(2), Buffer.from("23456789", "utf8")], - [buf.slice(5), Buffer.from("56789", "utf8")], - [buf.slice(10), Buffer.from("", "utf8")], - [buf.slice(5, 8), Buffer.from("567", "utf8")], - [buf.slice(8, -1), Buffer.from("8", "utf8")], - [buf.slice(-10), Buffer.from("0123456789", "utf8")], - [buf.slice(0, -9), Buffer.from("0", "utf8")], - [buf.slice(0, -10), Buffer.from("", "utf8")], - [buf.slice(0, -1), Buffer.from("012345678", "utf8")], - [buf.slice(2, -2), Buffer.from("234567", "utf8")], - [buf.slice(0, 65536), Buffer.from("0123456789", "utf8")], - [buf.slice(65536, 0), Buffer.from("", "utf8")], - [buf.slice(-5, -8), Buffer.from("", "utf8")], - [buf.slice(-5, -3), Buffer.from("56", "utf8")], - [buf.slice(-10, 10), Buffer.from("0123456789", "utf8")], - [buf.slice("0", "1"), Buffer.from("0", "utf8")], - [buf.slice("-5", "10"), Buffer.from("56789", "utf8")], - [buf.slice("-10", "10"), Buffer.from("0123456789", "utf8")], - [buf.slice("-10", "-5"), Buffer.from("01234", "utf8")], - [buf.slice("-10", "-0"), Buffer.from("", "utf8")], - [buf.slice("111"), Buffer.from("", "utf8")], - [buf.slice("0", "-111"), Buffer.from("", "utf8")], - ]; - - for (let i = 0, s = buf.toString(); i < buf.length; ++i) { - expectedSameBufs.push( - [buf.slice(i), Buffer.from(s.slice(i))], - [buf.slice(0, i), Buffer.from(s.slice(0, i))], - [buf.slice(-i), Buffer.from(s.slice(-i))], - [buf.slice(0, -i), Buffer.from(s.slice(0, -i))], - ); - } - - for (const [buf1, buf2] of expectedSameBufs) { - expect(Buffer.compare(buf1, buf2)).toBe(0); - } - - const utf16Buf = Buffer.from("0123456789", "utf16le"); - expect(utf16Buf.slice(0, 6)).toStrictEqual(Buffer.from("012", "utf16le")); - - // Try to slice a zero length Buffer. - // See https://github.com/joyent/node/issues/5881 - expect(Buffer.alloc(0).slice(0, 1).length).toBe(0); - - // Single argument slice - expect(Buffer.from("abcde", "utf8").slice(1).toString("utf8")).toBe("bcde"); - - // slice(0,0).length === 0 - expect(Buffer.from("hello", "utf8").slice(0, 0).length).toBe(0); - - // Regression tests for https://github.com/nodejs/node/issues/9096 - const regressionBuf = Buffer.from("abcd", "utf8"); - expect(regressionBuf.slice(regressionBuf.length / 3).toString("utf8")).toBe("bcd"); - expect(regressionBuf.slice(regressionBuf.length / 3, regressionBuf.length).toString()).toBe("bcd"); - - const largeSliceBuf = Buffer.from("abcdefg", "utf8"); - expect(largeSliceBuf.slice(-(-1 >>> 0) - 1).toString("utf8")).toBe(largeSliceBuf.toString("utf8")); - - const floatSliceBuf = Buffer.from("abc", "utf8"); - expect(floatSliceBuf.slice(-0.5).toString("utf8")).toBe(floatSliceBuf.toString("utf8")); - - const complexBuf = Buffer.from([ - 1, 29, 0, 0, 1, 143, 216, 162, 92, 254, 248, 63, 0, 0, 0, 18, 184, 6, 0, 175, 29, 0, 8, 11, 1, 0, 0, - ]); - const chunk1 = Buffer.from([1, 29, 0, 0, 1, 143, 216, 162, 92, 254, 248, 63, 0]); - const chunk2 = Buffer.from([0, 0, 18, 184, 6, 0, 175, 29, 0, 8, 11, 1, 0, 0]); - const middle = complexBuf.length / 2; - - expect(complexBuf.slice(0, middle)).toStrictEqual(chunk1); - expect(complexBuf.slice(middle)).toStrictEqual(chunk2); -}); - -//<#END_FILE: test-buffer-slice.js diff --git a/test/js/node/test/parallel/buffer-slow.test.js b/test/js/node/test/parallel/buffer-slow.test.js deleted file mode 100644 index 85f35f68e6..0000000000 --- a/test/js/node/test/parallel/buffer-slow.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-buffer-slow.js -//#SHA1: fadf639fe26752f00488a41a29f1977f95fc1c79 -//----------------- -"use strict"; - -const buffer = require("buffer"); -const SlowBuffer = buffer.SlowBuffer; - -const ones = [1, 1, 1, 1]; - -test("SlowBuffer should create a Buffer", () => { - let sb = SlowBuffer(4); - expect(sb).toBeInstanceOf(Buffer); - expect(sb.length).toBe(4); - sb.fill(1); - for (const [key, value] of sb.entries()) { - expect(value).toBe(ones[key]); - } - - // underlying ArrayBuffer should have the same length - expect(sb.buffer.byteLength).toBe(4); -}); - -test("SlowBuffer should work without new", () => { - let sb = SlowBuffer(4); - expect(sb).toBeInstanceOf(Buffer); - expect(sb.length).toBe(4); - sb.fill(1); - for (const [key, value] of sb.entries()) { - expect(value).toBe(ones[key]); - } -}); - -test("SlowBuffer should work with edge cases", () => { - expect(SlowBuffer(0).length).toBe(0); -}); - -test("SlowBuffer should throw with invalid length type", () => { - const bufferInvalidTypeMsg = expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }); - - expect(() => SlowBuffer()).toThrow(bufferInvalidTypeMsg); - expect(() => SlowBuffer({})).toThrow(bufferInvalidTypeMsg); - expect(() => SlowBuffer("6")).toThrow(bufferInvalidTypeMsg); - expect(() => SlowBuffer(true)).toThrow(bufferInvalidTypeMsg); -}); - -test("SlowBuffer should throw with invalid length value", () => { - const bufferMaxSizeMsg = expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }); - - expect(() => SlowBuffer(NaN)).toThrow(bufferMaxSizeMsg); - expect(() => SlowBuffer(Infinity)).toThrow(bufferMaxSizeMsg); - expect(() => SlowBuffer(-1)).toThrow(bufferMaxSizeMsg); - expect(() => SlowBuffer(buffer.kMaxLength + 1)).toThrow(bufferMaxSizeMsg); -}); - -//<#END_FILE: test-buffer-slow.js diff --git a/test/js/node/test/parallel/buffer-swap.test.js b/test/js/node/test/parallel/buffer-swap.test.js deleted file mode 100644 index 89eef58dce..0000000000 --- a/test/js/node/test/parallel/buffer-swap.test.js +++ /dev/null @@ -1,153 +0,0 @@ -//#FILE: test-buffer-swap.js -//#SHA1: 589e4ee82ab5f00e1cffdd4d326e21cc2f06b065 -//----------------- -"use strict"; - -describe("Buffer swap operations", () => { - test("Test buffers small enough to use the JS implementation", () => { - const buf = Buffer.from([ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, - ]); - - expect(buf.swap16()).toBe(buf); - expect(buf).toEqual( - Buffer.from([0x02, 0x01, 0x04, 0x03, 0x06, 0x05, 0x08, 0x07, 0x0a, 0x09, 0x0c, 0x0b, 0x0e, 0x0d, 0x10, 0x0f]), - ); - buf.swap16(); // restore - - expect(buf.swap32()).toBe(buf); - expect(buf).toEqual( - Buffer.from([0x04, 0x03, 0x02, 0x01, 0x08, 0x07, 0x06, 0x05, 0x0c, 0x0b, 0x0a, 0x09, 0x10, 0x0f, 0x0e, 0x0d]), - ); - buf.swap32(); // restore - - expect(buf.swap64()).toBe(buf); - expect(buf).toEqual( - Buffer.from([0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09]), - ); - }); - - test("Operates in-place", () => { - const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7]); - buf.slice(1, 5).swap32(); - expect(buf).toEqual(Buffer.from([0x1, 0x5, 0x4, 0x3, 0x2, 0x6, 0x7])); - buf.slice(1, 5).swap16(); - expect(buf).toEqual(Buffer.from([0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7])); - - // Length assertions - const re16 = /Buffer size must be a multiple of 16-bits/; - const re32 = /Buffer size must be a multiple of 32-bits/; - const re64 = /Buffer size must be a multiple of 64-bits/; - - expect(() => Buffer.from(buf).swap16()).toThrow(expect.objectContaining({ message: expect.any(String) })); - expect(() => Buffer.alloc(1025).swap16()).toThrow(expect.objectContaining({ message: expect.any(String) })); - expect(() => Buffer.from(buf).swap32()).toThrow(expect.objectContaining({ message: expect.any(String) })); - expect(() => buf.slice(1, 3).swap32()).toThrow(expect.objectContaining({ message: expect.any(String) })); - expect(() => Buffer.alloc(1025).swap32()).toThrow(expect.objectContaining({ message: expect.any(String) })); - expect(() => buf.slice(1, 3).swap64()).toThrow(expect.objectContaining({ message: expect.any(String) })); - expect(() => Buffer.alloc(1025).swap64()).toThrow(expect.objectContaining({ message: expect.any(String) })); - }); - - test("Swap64 on a slice", () => { - const buf = Buffer.from([ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x01, 0x02, 0x03, - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, - ]); - - buf.slice(2, 18).swap64(); - - expect(buf).toEqual( - Buffer.from([ - 0x01, 0x02, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, - 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, - ]), - ); - }); - - test("Force use of native code (Buffer size above threshold limit for js impl)", () => { - const bufData = new Uint32Array(256).fill(0x04030201); - const buf = Buffer.from(bufData.buffer, bufData.byteOffset); - const otherBufData = new Uint32Array(256).fill(0x03040102); - const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); - buf.swap16(); - expect(buf).toEqual(otherBuf); - }); - - test("Force use of native code for swap32", () => { - const bufData = new Uint32Array(256).fill(0x04030201); - const buf = Buffer.from(bufData.buffer); - const otherBufData = new Uint32Array(256).fill(0x01020304); - const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); - buf.swap32(); - expect(buf).toEqual(otherBuf); - }); - - test("Force use of native code for swap64", () => { - const bufData = new Uint8Array(256 * 8); - const otherBufData = new Uint8Array(256 * 8); - for (let i = 0; i < bufData.length; i++) { - bufData[i] = i % 8; - otherBufData[otherBufData.length - i - 1] = i % 8; - } - const buf = Buffer.from(bufData.buffer, bufData.byteOffset); - const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); - buf.swap64(); - expect(buf).toEqual(otherBuf); - }); - - test("Test native code with buffers that are not memory-aligned (swap16)", () => { - const bufData = new Uint8Array(256 * 8); - const otherBufData = new Uint8Array(256 * 8 - 2); - for (let i = 0; i < bufData.length; i++) { - bufData[i] = i % 2; - } - for (let i = 1; i < otherBufData.length; i++) { - otherBufData[otherBufData.length - i] = (i + 1) % 2; - } - const buf = Buffer.from(bufData.buffer, bufData.byteOffset); - // 0|1 0|1 0|1... - const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); - // 0|0 1|0 1|0... - - buf.slice(1, buf.length - 1).swap16(); - expect(buf.slice(0, otherBuf.length)).toEqual(otherBuf); - }); - - test("Test native code with buffers that are not memory-aligned (swap32)", () => { - const bufData = new Uint8Array(256 * 8); - const otherBufData = new Uint8Array(256 * 8 - 4); - for (let i = 0; i < bufData.length; i++) { - bufData[i] = i % 4; - } - for (let i = 1; i < otherBufData.length; i++) { - otherBufData[otherBufData.length - i] = (i + 1) % 4; - } - const buf = Buffer.from(bufData.buffer, bufData.byteOffset); - // 0|1 2 3 0|1 2 3... - const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); - // 0|0 3 2 1|0 3 2... - - buf.slice(1, buf.length - 3).swap32(); - expect(buf.slice(0, otherBuf.length)).toEqual(otherBuf); - }); - - test("Test native code with buffers that are not memory-aligned (swap64)", () => { - const bufData = new Uint8Array(256 * 8); - const otherBufData = new Uint8Array(256 * 8 - 8); - for (let i = 0; i < bufData.length; i++) { - bufData[i] = i % 8; - } - for (let i = 1; i < otherBufData.length; i++) { - otherBufData[otherBufData.length - i] = (i + 1) % 8; - } - const buf = Buffer.from(bufData.buffer, bufData.byteOffset); - // 0|1 2 3 4 5 6 7 0|1 2 3 4... - const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); - // 0|0 7 6 5 4 3 2 1|0 7 6 5... - - buf.slice(1, buf.length - 7).swap64(); - expect(buf.slice(0, otherBuf.length)).toEqual(otherBuf); - }); -}); - -//<#END_FILE: test-buffer-swap.js diff --git a/test/js/node/test/parallel/buffer-tostring-range.test.js b/test/js/node/test/parallel/buffer-tostring-range.test.js deleted file mode 100644 index a1e72ba714..0000000000 --- a/test/js/node/test/parallel/buffer-tostring-range.test.js +++ /dev/null @@ -1,115 +0,0 @@ -//#FILE: test-buffer-tostring-range.js -//#SHA1: 2bc09c70e84191e47ae345cc3178f28458b10ec2 -//----------------- -"use strict"; - -const rangeBuffer = Buffer.from("abc"); - -test("Buffer.toString range behavior", () => { - // If start >= buffer's length, empty string will be returned - expect(rangeBuffer.toString("ascii", 3)).toBe(""); - expect(rangeBuffer.toString("ascii", +Infinity)).toBe(""); - expect(rangeBuffer.toString("ascii", 3.14, 3)).toBe(""); - expect(rangeBuffer.toString("ascii", "Infinity", 3)).toBe(""); - - // If end <= 0, empty string will be returned - expect(rangeBuffer.toString("ascii", 1, 0)).toBe(""); - expect(rangeBuffer.toString("ascii", 1, -1.2)).toBe(""); - expect(rangeBuffer.toString("ascii", 1, -100)).toBe(""); - expect(rangeBuffer.toString("ascii", 1, -Infinity)).toBe(""); - - // If start < 0, start will be taken as zero - expect(rangeBuffer.toString("ascii", -1, 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", -1.99, 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", -Infinity, 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", "-1", 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", "-1.99", 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", "-Infinity", 3)).toBe("abc"); - - // If start is an invalid integer, start will be taken as zero - expect(rangeBuffer.toString("ascii", "node.js", 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", {}, 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", [], 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", NaN, 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", null, 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", undefined, 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", false, 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", "", 3)).toBe("abc"); - - // But, if start is an integer when coerced, then it will be coerced and used. - expect(rangeBuffer.toString("ascii", "-1", 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", "1", 3)).toBe("bc"); - expect(rangeBuffer.toString("ascii", "-Infinity", 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", "3", 3)).toBe(""); - expect(rangeBuffer.toString("ascii", Number(3), 3)).toBe(""); - expect(rangeBuffer.toString("ascii", "3.14", 3)).toBe(""); - expect(rangeBuffer.toString("ascii", "1.99", 3)).toBe("bc"); - expect(rangeBuffer.toString("ascii", "-1.99", 3)).toBe("abc"); - expect(rangeBuffer.toString("ascii", 1.99, 3)).toBe("bc"); - expect(rangeBuffer.toString("ascii", true, 3)).toBe("bc"); - - // If end > buffer's length, end will be taken as buffer's length - expect(rangeBuffer.toString("ascii", 0, 5)).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, 6.99)).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, Infinity)).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, "5")).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, "6.99")).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, "Infinity")).toBe("abc"); - - // If end is an invalid integer, end will be taken as buffer's length - expect(rangeBuffer.toString("ascii", 0, "node.js")).toBe(""); - expect(rangeBuffer.toString("ascii", 0, {})).toBe(""); - expect(rangeBuffer.toString("ascii", 0, NaN)).toBe(""); - expect(rangeBuffer.toString("ascii", 0, undefined)).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0)).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, null)).toBe(""); - expect(rangeBuffer.toString("ascii", 0, [])).toBe(""); - expect(rangeBuffer.toString("ascii", 0, false)).toBe(""); - expect(rangeBuffer.toString("ascii", 0, "")).toBe(""); - - // But, if end is an integer when coerced, then it will be coerced and used. - expect(rangeBuffer.toString("ascii", 0, "-1")).toBe(""); - expect(rangeBuffer.toString("ascii", 0, "1")).toBe("a"); - expect(rangeBuffer.toString("ascii", 0, "-Infinity")).toBe(""); - expect(rangeBuffer.toString("ascii", 0, "3")).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, Number(3))).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, "3.14")).toBe("abc"); - expect(rangeBuffer.toString("ascii", 0, "1.99")).toBe("a"); - expect(rangeBuffer.toString("ascii", 0, "-1.99")).toBe(""); - expect(rangeBuffer.toString("ascii", 0, 1.99)).toBe("a"); - expect(rangeBuffer.toString("ascii", 0, true)).toBe("a"); -}); - -test("toString() with an object as an encoding", () => { - expect( - rangeBuffer.toString({ - toString: function () { - return "ascii"; - }, - }), - ).toBe("abc"); -}); - -test("toString() with 0 and null as the encoding", () => { - expect(() => { - rangeBuffer.toString(0, 1, 2); - }).toThrow( - expect.objectContaining({ - code: "ERR_UNKNOWN_ENCODING", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => { - rangeBuffer.toString(null, 1, 2); - }).toThrow( - expect.objectContaining({ - code: "ERR_UNKNOWN_ENCODING", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-buffer-tostring-range.js diff --git a/test/js/node/test/parallel/buffer-tostring-rangeerror.test.js b/test/js/node/test/parallel/buffer-tostring-rangeerror.test.js deleted file mode 100644 index 0e88759c45..0000000000 --- a/test/js/node/test/parallel/buffer-tostring-rangeerror.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-buffer-tostring-rangeerror.js -//#SHA1: c5bd04a7b4f3b7ecfb3898262dd73da29a9ad162 -//----------------- -"use strict"; - -// This test ensures that Node.js throws an Error when trying to convert a -// large buffer into a string. -// Regression test for https://github.com/nodejs/node/issues/649. - -const { - SlowBuffer, - constants: { MAX_STRING_LENGTH }, -} = require("buffer"); - -const len = MAX_STRING_LENGTH + 1; -const errorMatcher = expect.objectContaining({ - code: "ERR_STRING_TOO_LONG", - name: "Error", - message: `Cannot create a string longer than 2147483647 characters`, -}); - -test("Buffer toString with large buffer throws RangeError", () => { - expect(() => Buffer(len).toString("utf8")).toThrow(errorMatcher); - expect(() => SlowBuffer(len).toString("utf8")).toThrow(errorMatcher); - expect(() => Buffer.alloc(len).toString("utf8")).toThrow(errorMatcher); - expect(() => Buffer.allocUnsafe(len).toString("utf8")).toThrow(errorMatcher); - expect(() => Buffer.allocUnsafeSlow(len).toString("utf8")).toThrow(errorMatcher); -}); - -//<#END_FILE: test-buffer-tostring-rangeerror.js diff --git a/test/js/node/test/parallel/buffer-tostring.test.js b/test/js/node/test/parallel/buffer-tostring.test.js deleted file mode 100644 index eb48074506..0000000000 --- a/test/js/node/test/parallel/buffer-tostring.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-buffer-tostring.js -//#SHA1: 0a6490b6dd4c343c01828d1c4ff81b745b6b1552 -//----------------- -"use strict"; - -// utf8, ucs2, ascii, latin1, utf16le -const encodings = ["utf8", "utf-8", "ucs2", "ucs-2", "ascii", "latin1", "binary", "utf16le", "utf-16le"]; - -test("Buffer.from().toString() with various encodings", () => { - encodings - .reduce((es, e) => es.concat(e, e.toUpperCase()), []) - .forEach(encoding => { - expect(Buffer.from("foo", encoding).toString(encoding)).toBe("foo"); - }); -}); - -test("Buffer.from().toString() with base64 encoding", () => { - ["base64", "BASE64"].forEach(encoding => { - expect(Buffer.from("Zm9v", encoding).toString(encoding)).toBe("Zm9v"); - }); -}); - -test("Buffer.from().toString() with hex encoding", () => { - ["hex", "HEX"].forEach(encoding => { - expect(Buffer.from("666f6f", encoding).toString(encoding)).toBe("666f6f"); - }); -}); - -test("Buffer.from().toString() with invalid encodings", () => { - for (let i = 1; i < 10; i++) { - const encoding = String(i).repeat(i); - expect(Buffer.isEncoding(encoding)).toBe(false); - expect(() => Buffer.from("foo").toString(encoding)).toThrow( - expect.objectContaining({ - code: "ERR_UNKNOWN_ENCODING", - name: "TypeError", - message: expect.any(String), - }), - ); - } -}); - -//<#END_FILE: test-buffer-tostring.js diff --git a/test/js/node/test/parallel/buffer-write.test.js b/test/js/node/test/parallel/buffer-write.test.js deleted file mode 100644 index ceb7123d5f..0000000000 --- a/test/js/node/test/parallel/buffer-write.test.js +++ /dev/null @@ -1,119 +0,0 @@ -//#FILE: test-buffer-write.js -//#SHA1: 9577e31a533888b164b0abf4ebececbe04e381cb -//----------------- -"use strict"; - -[-1, 10].forEach(offset => { - test(`Buffer.alloc(9).write('foo', ${offset}) throws RangeError`, () => { - expect(() => Buffer.alloc(9).write("foo", offset)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - }); -}); - -const resultMap = new Map([ - ["utf8", Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], - ["ucs2", Buffer.from([102, 0, 111, 0, 111, 0, 0, 0, 0])], - ["ascii", Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], - ["latin1", Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], - ["binary", Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], - ["utf16le", Buffer.from([102, 0, 111, 0, 111, 0, 0, 0, 0])], - ["base64", Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], - ["base64url", Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], - ["hex", Buffer.from([102, 111, 111, 0, 0, 0, 0, 0, 0])], -]); - -// utf8, ucs2, ascii, latin1, utf16le -const encodings = ["utf8", "utf-8", "ucs2", "ucs-2", "ascii", "latin1", "binary", "utf16le", "utf-16le"]; - -encodings - .reduce((es, e) => es.concat(e, e.toUpperCase()), []) - .forEach(encoding => { - test(`Buffer.write with encoding ${encoding}`, () => { - const buf = Buffer.alloc(9); - const len = Buffer.byteLength("foo", encoding); - expect(buf.write("foo", 0, len, encoding)).toBe(len); - - if (encoding.includes("-")) encoding = encoding.replace("-", ""); - - expect(buf).toEqual(resultMap.get(encoding.toLowerCase())); - }); - }); - -// base64 -["base64", "BASE64", "base64url", "BASE64URL"].forEach(encoding => { - test(`Buffer.write with encoding ${encoding}`, () => { - const buf = Buffer.alloc(9); - const len = Buffer.byteLength("Zm9v", encoding); - - expect(buf.write("Zm9v", 0, len, encoding)).toBe(len); - expect(buf).toEqual(resultMap.get(encoding.toLowerCase())); - }); -}); - -// hex -["hex", "HEX"].forEach(encoding => { - test(`Buffer.write with encoding ${encoding}`, () => { - const buf = Buffer.alloc(9); - const len = Buffer.byteLength("666f6f", encoding); - - expect(buf.write("666f6f", 0, len, encoding)).toBe(len); - expect(buf).toEqual(resultMap.get(encoding.toLowerCase())); - }); -}); - -// Invalid encodings -for (let i = 1; i < 10; i++) { - const encoding = String(i).repeat(i); - - test(`Invalid encoding ${encoding}`, () => { - expect(Buffer.isEncoding(encoding)).toBe(false); - expect(() => Buffer.alloc(9).write("foo", encoding)).toThrow( - expect.objectContaining({ - code: "ERR_UNKNOWN_ENCODING", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -} - -// UCS-2 overflow CVE-2018-12115 -for (let i = 1; i < 4; i++) { - test(`UCS-2 overflow test ${i}`, () => { - // Allocate two Buffers sequentially off the pool. Run more than once in case - // we hit the end of the pool and don't get sequential allocations - const x = Buffer.allocUnsafe(4).fill(0); - const y = Buffer.allocUnsafe(4).fill(1); - // Should not write anything, pos 3 doesn't have enough room for a 16-bit char - expect(x.write("ыыыыыы", 3, "ucs2")).toBe(0); - // CVE-2018-12115 experienced via buffer overrun to next block in the pool - expect(Buffer.compare(y, Buffer.alloc(4, 1))).toBe(0); - }); -} - -test("Should not write any data when there is no space for 16-bit chars", () => { - const z = Buffer.alloc(4, 0); - expect(z.write("\u0001", 3, "ucs2")).toBe(0); - expect(Buffer.compare(z, Buffer.alloc(4, 0))).toBe(0); - // Make sure longer strings are written up to the buffer end. - expect(z.write("abcd", 2)).toBe(2); - expect([...z]).toEqual([0, 0, 0x61, 0x62]); -}); - -test("Large overrun should not corrupt the process", () => { - expect(Buffer.alloc(4).write("ыыыыыы".repeat(100), 3, "utf16le")).toBe(0); -}); - -test(".write() does not affect the byte after the written-to slice of the Buffer", () => { - // Refs: https://github.com/nodejs/node/issues/26422 - const buf = Buffer.alloc(8); - expect(buf.write("ыы", 1, "utf16le")).toBe(4); - expect([...buf]).toEqual([0, 0x4b, 0x04, 0x4b, 0x04, 0, 0, 0]); -}); - -//<#END_FILE: test-buffer-write.js diff --git a/test/js/node/test/parallel/buffer-zero-fill-reset.test.js b/test/js/node/test/parallel/buffer-zero-fill-reset.test.js deleted file mode 100644 index d4055fb35a..0000000000 --- a/test/js/node/test/parallel/buffer-zero-fill-reset.test.js +++ /dev/null @@ -1,20 +0,0 @@ -//#FILE: test-buffer-zero-fill-reset.js -//#SHA1: 2278dd06a2e113e875c1f0f46580c0fcc4fc5254 -//----------------- -"use strict"; - -test("Uint8Array is zero-filled after Buffer.alloc(0)", () => { - function testUint8Array(ui) { - const length = ui.length; - for (let i = 0; i < length; i++) if (ui[i] !== 0) return false; - return true; - } - - for (let i = 0; i < 100; i++) { - Buffer.alloc(0); - const ui = new Uint8Array(65); - expect(testUint8Array(ui)).toBe(true); - } -}); - -//<#END_FILE: test-buffer-zero-fill-reset.js diff --git a/test/js/node/test/parallel/buffer-zero-fill.test.js b/test/js/node/test/parallel/buffer-zero-fill.test.js deleted file mode 100644 index 003b4ffe08..0000000000 --- a/test/js/node/test/parallel/buffer-zero-fill.test.js +++ /dev/null @@ -1,20 +0,0 @@ -//#FILE: test-buffer-zero-fill.js -//#SHA1: b710e0c9405c90f7526cf0efabd4c61ede37b1f7 -//----------------- -"use strict"; - -// Tests deprecated Buffer API on purpose -test("Buffer zero-fill", () => { - const buf1 = Buffer(100); - const buf2 = new Buffer(100); - - for (let n = 0; n < buf1.length; n++) { - expect(buf1[n]).toBe(0); - } - - for (let n = 0; n < buf2.length; n++) { - expect(buf2[n]).toBe(0); - } -}); - -//<#END_FILE: test-buffer-zero-fill.js diff --git a/test/js/node/test/parallel/child-process-exec-encoding.test.js b/test/js/node/test/parallel/child-process-exec-encoding.test.js deleted file mode 100644 index 46ae2d5276..0000000000 --- a/test/js/node/test/parallel/child-process-exec-encoding.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-child-process-exec-encoding.js -//#SHA1: 3ad6878126678aa6ad2c38a43264e5684dae6a72 -//----------------- -"use strict"; - -const stdoutData = "foo"; -const stderrData = "bar"; - -if (process.argv[2] === "child") { - // The following console calls are part of the test. - console.log(stdoutData); - console.error(stderrData); -} else { - const cp = require("child_process"); - const expectedStdout = `${stdoutData}\n`; - const expectedStderr = `${stderrData}\n`; - - function run(options) { - const cmd = `"${process.execPath}" "${__filename}" child`; - - return new Promise((resolve, reject) => { - cp.exec(cmd, options, (error, stdout, stderr) => { - if (error) { - reject(error); - } else { - resolve({ stdout, stderr }); - } - }); - }); - } - - test("Test default encoding, which should be utf8", async () => { - const { stdout, stderr } = await run({}); - expect(typeof stdout).toBe("string"); - expect(typeof stderr).toBe("string"); - expect(stdout).toBe(expectedStdout); - expect(stderr).toBe(expectedStderr); - }); - - test("Test explicit utf8 encoding", async () => { - const { stdout, stderr } = await run({ encoding: "utf8" }); - expect(typeof stdout).toBe("string"); - expect(typeof stderr).toBe("string"); - expect(stdout).toBe(expectedStdout); - expect(stderr).toBe(expectedStderr); - }); - - test("Test cases that result in buffer encodings", async () => { - const encodings = [undefined, null, "buffer", "invalid"]; - - for (const encoding of encodings) { - const { stdout, stderr } = await run({ encoding }); - expect(stdout).toBeInstanceOf(Buffer); - expect(stderr).toBeInstanceOf(Buffer); - expect(stdout.toString()).toBe(expectedStdout); - expect(stderr.toString()).toBe(expectedStderr); - } - }); -} - -//<#END_FILE: test-child-process-exec-encoding.js diff --git a/test/js/node/test/parallel/child-process-exec-env.test.js b/test/js/node/test/parallel/child-process-exec-env.test.js deleted file mode 100644 index 1649fad830..0000000000 --- a/test/js/node/test/parallel/child-process-exec-env.test.js +++ /dev/null @@ -1,50 +0,0 @@ -//#FILE: test-child-process-exec-env.js -//#SHA1: cba9b5f7a9d1ab7cd674bde00c1c4184470e4899 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const { isWindows } = require("../common"); -const { exec } = require("child_process"); -const util = require("util"); - -const execPromise = util.promisify(exec); - -test("exec with custom environment", async () => { - let command, options; - - if (!isWindows) { - command = "/usr/bin/env"; - options = { env: { HELLO: "WORLD" } }; - } else { - command = "set"; - options = { env: { ...process.env, HELLO: "WORLD" } }; - } - - const { stdout, stderr } = await execPromise(command, options); - - expect(stdout).not.toBe(""); - expect(stdout).toContain("HELLO=WORLD"); - expect(stderr).toBe(""); -}); - -//<#END_FILE: test-child-process-exec-env.js diff --git a/test/js/node/test/parallel/child-process-exec-kill-throws.test.js b/test/js/node/test/parallel/child-process-exec-kill-throws.test.js deleted file mode 100644 index b0b8482f24..0000000000 --- a/test/js/node/test/parallel/child-process-exec-kill-throws.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-child-process-exec-kill-throws.js -//#SHA1: 968879ddf3244351dea40c681343ea8defc02a0b -//----------------- -"use strict"; - -const cp = require("child_process"); - -if (process.argv[2] === "child") { - // Since maxBuffer is 0, this should trigger an error. - console.log("foo"); -} else { - const originalKill = cp.ChildProcess.prototype.kill; - - beforeEach(() => { - // Monkey patch ChildProcess#kill() to kill the process and then throw. - cp.ChildProcess.prototype.kill = function () { - originalKill.apply(this, arguments); - throw new Error("mock error"); - }; - }); - - afterEach(() => { - // Restore original kill method - cp.ChildProcess.prototype.kill = originalKill; - }); - - test("ChildProcess#kill() throws error", done => { - const cmd = `"${process.execPath}" "${__filename}" child`; - const options = { maxBuffer: 0, killSignal: "SIGKILL" }; - - const child = cp.exec(cmd, options, (err, stdout, stderr) => { - // Verify that if ChildProcess#kill() throws, the error is reported. - expect(err).toEqual( - expect.objectContaining({ - message: "mock error", - }), - ); - expect(stdout).toBe(""); - expect(stderr).toBe(""); - expect(child.killed).toBe(true); - done(); - }); - }); -} - -//<#END_FILE: test-child-process-exec-kill-throws.js diff --git a/test/js/node/test/parallel/child-process-exec-std-encoding.test.js b/test/js/node/test/parallel/child-process-exec-std-encoding.test.js deleted file mode 100644 index 010bc7097e..0000000000 --- a/test/js/node/test/parallel/child-process-exec-std-encoding.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-child-process-exec-std-encoding.js -//#SHA1: fa74583780f5256e46fd7a5ad02aed4b20bb0b76 -//----------------- -"use strict"; - -const cp = require("child_process"); - -const stdoutData = "foo"; -const stderrData = "bar"; -const expectedStdout = `${stdoutData}\n`; -const expectedStderr = `${stderrData}\n`; - -if (process.argv[2] === "child") { - // The following console calls are part of the test. - console.log(stdoutData); - console.error(stderrData); -} else { - test("child process exec with stdout and stderr encoding", done => { - const cmd = `"${process.execPath}" "${__filename}" child`; - const child = cp.exec(cmd, (error, stdout, stderr) => { - expect(error).toBeNull(); - expect(stdout).toBe(expectedStdout); - expect(stderr).toBe(expectedStderr); - done(); - }); - child.stdout.setEncoding("utf-8"); - child.stderr.setEncoding("utf-8"); - }); -} - -//<#END_FILE: test-child-process-exec-std-encoding.js diff --git a/test/js/node/test/parallel/child-process-exec-stdout-stderr-data-string.test.js b/test/js/node/test/parallel/child-process-exec-stdout-stderr-data-string.test.js deleted file mode 100644 index 42a1a555ab..0000000000 --- a/test/js/node/test/parallel/child-process-exec-stdout-stderr-data-string.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-child-process-exec-stdout-stderr-data-string.js -//#SHA1: 342c40f3dbb506150172c2471ae228fd8632b900 -//----------------- -"use strict"; -// Refs: https://github.com/nodejs/node/issues/7342 -const { exec } = require("child_process"); - -const command = process.platform === "win32" ? "dir" : "ls"; - -test("exec stdout data is called at least once", done => { - const child = exec(command); - const onData = jest.fn(); - child.stdout.on("data", onData); - - child.on("close", () => { - expect(onData).toHaveBeenCalled(); - done(); - }); -}); - -test("exec stderr data is called at least once and receives string", done => { - const child = exec("fhqwhgads"); - const onData = jest.fn(data => { - expect(typeof data).toBe("string"); - }); - child.stderr.on("data", onData); - - child.on("close", () => { - expect(onData).toHaveBeenCalled(); - done(); - }); -}); - -//<#END_FILE: test-child-process-exec-stdout-stderr-data-string.js diff --git a/test/js/node/test/parallel/child-process-exec-timeout-kill.test.js b/test/js/node/test/parallel/child-process-exec-timeout-kill.test.js deleted file mode 100644 index e3bc2b8e9b..0000000000 --- a/test/js/node/test/parallel/child-process-exec-timeout-kill.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-child-process-exec-timeout-kill.js -//#SHA1: 01bc25d258b4d8905a2387e9a08b9ceb8c38c141 -//----------------- -"use strict"; - -// Test exec() with both a timeout and a killSignal. - -const cp = require("child_process"); -const path = require("path"); - -const { kExpiringChildRunTime, kExpiringParentTimer } = require("../common/child_process"); - -const logAfterTime = time => { - setTimeout(() => { - console.log(`Logged after ${time}ms`); - }, time); -}; - -if (process.argv[2] === "child") { - logAfterTime(kExpiringChildRunTime); - process.exit(0); -} - -const cmd = `"${process.execPath}" "${__filename}" child`; - -test("exec with timeout and killSignal", done => { - // Test with a different kill signal. - cp.exec( - cmd, - { - timeout: kExpiringParentTimer, - killSignal: "SIGKILL", - }, - (err, stdout, stderr) => { - console.log("[stdout]", stdout.trim()); - console.log("[stderr]", stderr.trim()); - expect(stdout.trim()).toBe(""); - expect(stderr.trim()).toBe(""); - - expect(err?.killed).toBe(true); - expect(err?.code).toBeNull(); - expect(err?.signal).toBe("SIGKILL"); - expect(err?.cmd).toBe(cmd); - done(); - }, - ); -}); - -//<#END_FILE: test-child-process-exec-timeout-kill.js diff --git a/test/js/node/test/parallel/child-process-execfile-promisified-abortcontroller.test.js b/test/js/node/test/parallel/child-process-execfile-promisified-abortcontroller.test.js deleted file mode 100644 index acebc661e9..0000000000 --- a/test/js/node/test/parallel/child-process-execfile-promisified-abortcontroller.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-child-process-execFile-promisified-abortController.js -//#SHA1: 133445acf9aaafea4be11eb7965f222c5827f2f3 -//----------------- -"use strict"; - -const { promisify } = require("util"); -const execFile = require("child_process").execFile; -const fixtures = require("../common/fixtures"); - -const echoFixture = fixtures.path("echo.js"); -const promisified = promisify(execFile); -const invalidArgTypeError = { - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", -}; - -test("Verify that the signal option works properly", async () => { - const ac = new AbortController(); - const signal = ac.signal; - const promise = promisified(process.execPath, [echoFixture, 0], { signal }); - - ac.abort(); - - await expect(promise).rejects.toThrow( - expect.objectContaining({ - name: "AbortError", - message: expect.any(String), - }), - ); -}); - -test("Verify that the signal option works properly when already aborted", async () => { - const signal = AbortSignal.abort(); - - await expect(promisified(process.execPath, [echoFixture, 0], { signal })).rejects.toThrow( - expect.objectContaining({ - name: "AbortError", - message: expect.any(String), - }), - ); -}); - -test("Verify that if something different than Abortcontroller.signal is passed, ERR_INVALID_ARG_TYPE is thrown", () => { - const signal = {}; - expect(() => { - promisified(process.execPath, [echoFixture, 0], { signal }); - }).toThrow(expect.objectContaining(invalidArgTypeError)); -}); - -test("Verify that if a string is passed as signal, ERR_INVALID_ARG_TYPE is thrown", () => { - const signal = "world!"; - expect(() => { - promisified(process.execPath, [echoFixture, 0], { signal }); - }).toThrow(expect.objectContaining(invalidArgTypeError)); -}); - -//<#END_FILE: test-child-process-execFile-promisified-abortController.js diff --git a/test/js/node/test/parallel/child-process-flush-stdio.test.js b/test/js/node/test/parallel/child-process-flush-stdio.test.js deleted file mode 100644 index 53e4419b66..0000000000 --- a/test/js/node/test/parallel/child-process-flush-stdio.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-child-process-flush-stdio.js -//#SHA1: 42d6ab8508587f13b81d2ec70d28fd88efe8fe05 -//----------------- -"use strict"; - -const cp = require("child_process"); - -// Windows' `echo` command is a built-in shell command and not an external -// executable like on *nix -const opts = { shell: process.platform === "win32" }; - -test("spawn echo without arguments", done => { - const p = cp.spawn("echo", [], opts); - - p.on("close", (code, signal) => { - expect(code).toBe(0); - expect(signal).toBeNull(); - done(); - }); - - p.stdout.read(); -}); - -test("spawn echo with argument", done => { - const buffer = []; - const p = cp.spawn("echo", ["123"], opts); - - p.on("close", (code, signal) => { - expect(code).toBe(0); - expect(signal).toBeNull(); - expect(Buffer.concat(buffer).toString().trim()).toBe("123"); - done(); - }); - - p.stdout.on("readable", () => { - let buf; - while ((buf = p.stdout.read()) !== null) buffer.push(buf); - }); -}); - -//<#END_FILE: test-child-process-flush-stdio.js diff --git a/test/js/node/test/parallel/child-process-fork-abort-signal.test.js b/test/js/node/test/parallel/child-process-fork-abort-signal.test.js deleted file mode 100644 index f3c1dcfe65..0000000000 --- a/test/js/node/test/parallel/child-process-fork-abort-signal.test.js +++ /dev/null @@ -1,115 +0,0 @@ -//#FILE: test-child-process-fork-abort-signal.js -//#SHA1: 4805d5dd4e3cb22ffd5a21fd9d92b6ccd6bc73cf -//----------------- -"use strict"; - -const fixtures = require("../common/fixtures"); -const { fork } = require("child_process"); - -test("aborting a forked child_process after calling fork", done => { - const ac = new AbortController(); - const { signal } = ac; - const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), { - signal, - }); - cp.on("exit", (code, killSignal) => { - expect(code).toBeNull(); - expect(killSignal).toBe("SIGTERM"); - done(); - }); - cp.on("error", err => { - expect(err.name).toBe("AbortError"); - done(); - }); - process.nextTick(() => ac.abort()); -}); - -test("aborting with custom error", done => { - const ac = new AbortController(); - const { signal } = ac; - const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), { - signal, - }); - cp.on("exit", (code, killSignal) => { - expect(code).toBeNull(); - expect(killSignal).toBe("SIGTERM"); - done(); - }); - cp.on("error", err => { - expect(err.name).toBe("AbortError"); - expect(err.cause.name).toBe("Error"); - expect(err.cause.message).toBe("boom"); - done(); - }); - process.nextTick(() => ac.abort(new Error("boom"))); -}); - -test("passing an already aborted signal to a forked child_process", done => { - const signal = AbortSignal.abort(); - const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), { - signal, - }); - cp.on("exit", (code, killSignal) => { - expect(code).toBeNull(); - expect(killSignal).toBe("SIGTERM"); - done(); - }); - cp.on("error", err => { - expect(err.name).toBe("AbortError"); - done(); - }); -}); - -test("passing an aborted signal with custom error to a forked child_process", done => { - const signal = AbortSignal.abort(new Error("boom")); - const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), { - signal, - }); - cp.on("exit", (code, killSignal) => { - expect(code).toBeNull(); - expect(killSignal).toBe("SIGTERM"); - done(); - }); - cp.on("error", err => { - expect(err.name).toBe("AbortError"); - expect(err.cause.name).toBe("Error"); - expect(err.cause.message).toBe("boom"); - done(); - }); -}); - -test("passing a different kill signal", done => { - const signal = AbortSignal.abort(); - const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), { - signal, - killSignal: "SIGKILL", - }); - cp.on("exit", (code, killSignal) => { - expect(code).toBeNull(); - expect(killSignal).toBe("SIGKILL"); - done(); - }); - cp.on("error", err => { - expect(err.name).toBe("AbortError"); - done(); - }); -}); - -test("aborting a cp before close but after exit", done => { - const ac = new AbortController(); - const { signal } = ac; - const cp = fork(fixtures.path("child-process-stay-alive-forever.js"), { - signal, - }); - cp.on("exit", () => { - ac.abort(); - done(); - }); - cp.on("error", () => { - done(new Error("Should not have errored")); - }); - - setTimeout(() => cp.kill(), 1); -}); - -//<#END_FILE: test-child-process-fork-abort-signal.js diff --git a/test/js/node/test/parallel/child-process-fork-args.test.js b/test/js/node/test/parallel/child-process-fork-args.test.js deleted file mode 100644 index f2efa98aa5..0000000000 --- a/test/js/node/test/parallel/child-process-fork-args.test.js +++ /dev/null @@ -1,88 +0,0 @@ -//#FILE: test-child-process-fork-args.js -//#SHA1: 172297ab2ed7887ced1b830b8c36d2d6a508deed -//----------------- -"use strict"; -const fixtures = require("../common/fixtures"); -const { fork } = require("child_process"); - -// This test check the arguments of `fork` method -// Refs: https://github.com/nodejs/node/issues/20749 -const expectedEnv = { foo: "bar" }; - -// Ensure that first argument `modulePath` must be provided -// and be of type string -test("fork modulePath argument must be a string", () => { - const invalidModulePath = [0, true, undefined, null, [], {}, () => {}, Symbol("t")]; - invalidModulePath.forEach(modulePath => { - expect(() => fork(modulePath)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringMatching(/^The "modulePath" argument must be of type string/), - }), - ); - }); -}); - -test("fork with valid modulePath", done => { - const cp = fork(fixtures.path("child-process-echo-options.js")); - cp.on("exit", code => { - expect(code).toBe(0); - done(); - }); -}); - -// Ensure that the second argument of `fork` -// and `fork` should parse options -// correctly if args is undefined or null -test("fork second argument validation", () => { - const invalidSecondArgs = [0, true, () => {}, Symbol("t")]; - invalidSecondArgs.forEach(arg => { - expect(() => { - fork(fixtures.path("child-process-echo-options.js"), arg); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - }); -}); - -test("fork with valid second argument", async () => { - const argsLists = [undefined, null, []]; - - for (const args of argsLists) { - const cp = fork(fixtures.path("child-process-echo-options.js"), args, { - env: { ...process.env, ...expectedEnv }, - }); - - await new Promise(resolve => { - cp.on("message", ({ env }) => { - expect(env.foo).toBe(expectedEnv.foo); - }); - - cp.on("exit", code => { - expect(code).toBe(0); - resolve(); - }); - }); - } -}); - -// Ensure that the third argument should be type of object if provided -test("fork third argument must be an object if provided", () => { - const invalidThirdArgs = [0, true, () => {}, Symbol("t")]; - invalidThirdArgs.forEach(arg => { - expect(() => { - fork(fixtures.path("child-process-echo-options.js"), [], arg); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - }); -}); - -//<#END_FILE: test-child-process-fork-args.js diff --git a/test/js/node/test/parallel/child-process-fork-close.test.js b/test/js/node/test/parallel/child-process-fork-close.test.js deleted file mode 100644 index d7dd94759a..0000000000 --- a/test/js/node/test/parallel/child-process-fork-close.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-child-process-fork-close.js -//#SHA1: d196e4b4f06991be7c2a48169cb89b815c0f172b -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const { fork } = require("child_process"); -const path = require("path"); - -test("child process fork close events", async () => { - const cp = fork(path.resolve(__dirname, "../fixtures/child-process-message-and-exit.js")); - - let gotMessage = false; - let gotExit = false; - let gotClose = false; - - const messagePromise = new Promise(resolve => { - cp.on("message", message => { - expect(gotMessage).toBe(false); - expect(gotClose).toBe(false); - expect(message).toBe("hello"); - gotMessage = true; - resolve(); - }); - }); - - const exitPromise = new Promise(resolve => { - cp.on("exit", () => { - expect(gotExit).toBe(false); - expect(gotClose).toBe(false); - gotExit = true; - resolve(); - }); - }); - - const closePromise = new Promise(resolve => { - cp.on("close", () => { - expect(gotMessage).toBe(true); - expect(gotExit).toBe(true); - expect(gotClose).toBe(false); - gotClose = true; - resolve(); - }); - }); - - await Promise.all([messagePromise, exitPromise, closePromise]); -}); - -//<#END_FILE: test-child-process-fork-close.js diff --git a/test/js/node/test/parallel/child-process-fork-detached.test.js b/test/js/node/test/parallel/child-process-fork-detached.test.js deleted file mode 100644 index 18c3f6a0cb..0000000000 --- a/test/js/node/test/parallel/child-process-fork-detached.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-child-process-fork-detached.js -//#SHA1: 289d7a5f7c5d58ae50a06e7427730c57d78fe39c -//----------------- -"use strict"; - -const { fork } = require("child_process"); -const path = require("path"); - -const fixturesPath = path.resolve(__dirname, "..", "fixtures"); - -test("fork detached child process", done => { - const nonPersistentNode = fork(path.join(fixturesPath, "parent-process-nonpersistent-fork.js"), [], { silent: true }); - - let childId = -1; - - nonPersistentNode.stdout.on("data", data => { - childId = parseInt(data, 10); - nonPersistentNode.kill(); - }); - - nonPersistentNode.on("exit", () => { - expect(childId).not.toBe(-1); - - // Killing the child process should not throw an error - expect(() => process.kill(childId)).not.toThrow(); - - done(); - }); -}); - -//<#END_FILE: test-child-process-fork-detached.js diff --git a/test/js/node/test/parallel/child-process-fork-ref2.test.js b/test/js/node/test/parallel/child-process-fork-ref2.test.js deleted file mode 100644 index ecea4bfd3a..0000000000 --- a/test/js/node/test/parallel/child-process-fork-ref2.test.js +++ /dev/null @@ -1,69 +0,0 @@ -//#FILE: test-child-process-fork-ref2.js -//#SHA1: 6d8a2bec4198f9d9f71ce9f2cc880cfcd1c9d883 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const { fork } = require("child_process"); -const { debuglog } = require("util"); -const debug = debuglog("test"); - -const platformTimeout = ms => ms; - -if (process.argv[2] === "child") { - test("child process", () => { - debug("child -> call disconnect"); - process.disconnect(); - - return new Promise(resolve => { - setTimeout(() => { - debug("child -> will this keep it alive?"); - const mockFn = jest.fn(); - process.on("message", mockFn); - expect(mockFn).not.toHaveBeenCalled(); - resolve(); - }, platformTimeout(400)); - }); - }); -} else { - test("parent process", () => { - return new Promise(resolve => { - const child = fork(__filename, ["child"]); - - const disconnectSpy = jest.fn(() => { - debug("parent -> disconnect"); - }); - child.on("disconnect", disconnectSpy); - - const exitSpy = jest.fn(() => { - debug("parent -> exit"); - expect(disconnectSpy).toHaveBeenCalledTimes(1); - resolve(); - }); - child.once("exit", exitSpy); - - expect(exitSpy).toHaveBeenCalledTimes(0); - }); - }); -} - -//<#END_FILE: test-child-process-fork-ref2.js diff --git a/test/js/node/test/parallel/child-process-fork3.test.js b/test/js/node/test/parallel/child-process-fork3.test.js deleted file mode 100644 index ab5558264a..0000000000 --- a/test/js/node/test/parallel/child-process-fork3.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-child-process-fork3.js -//#SHA1: afa7c58bf22c64585426b2b3ec4358a415f4ff47 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const child_process = require("child_process"); -const path = require("path"); - -test("child_process.fork should not hang", () => { - const emptyScriptPath = path.join(__dirname, "fixtures", "empty.js"); - expect(() => { - child_process.fork(emptyScriptPath); - }).not.toThrow(); -}); - -//<#END_FILE: test-child-process-fork3.js diff --git a/test/js/node/test/parallel/child-process-kill.test.js b/test/js/node/test/parallel/child-process-kill.test.js deleted file mode 100644 index 5a9ece5e38..0000000000 --- a/test/js/node/test/parallel/child-process-kill.test.js +++ /dev/null @@ -1,58 +0,0 @@ -//#FILE: test-child-process-kill.js -//#SHA1: 308c453d51a13e0e2c640969456c0092ab00967d -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const { spawn } = require("child_process"); -const isWindows = process.platform === "win32"; - -test("child process kill behavior", async () => { - const cat = spawn(isWindows ? "cmd" : "cat"); - - const stdoutEndPromise = new Promise(resolve => { - cat.stdout.on("end", resolve); - }); - - const stderrDataPromise = new Promise((resolve, reject) => { - cat.stderr.on("data", () => reject(new Error("stderr should not receive data"))); - cat.stderr.on("end", resolve); - }); - - const exitPromise = new Promise(resolve => { - cat.on("exit", (code, signal) => { - expect(code).toBeNull(); - expect(signal).toBe("SIGTERM"); - expect(cat.signalCode).toBe("SIGTERM"); - resolve(); - }); - }); - - expect(cat.signalCode).toBeNull(); - expect(cat.killed).toBe(false); - cat.kill(); - expect(cat.killed).toBe(true); - - await Promise.all([stdoutEndPromise, stderrDataPromise, exitPromise]); -}); - -//<#END_FILE: test-child-process-kill.js diff --git a/test/js/node/test/parallel/child-process-send-type-error.test.js b/test/js/node/test/parallel/child-process-send-type-error.test.js deleted file mode 100644 index cc4908b7da..0000000000 --- a/test/js/node/test/parallel/child-process-send-type-error.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-child-process-send-type-error.js -//#SHA1: 85b82f9c15ca3d5368e22ccd1e7f44672ce2fb0c -//----------------- -"use strict"; - -const cp = require("child_process"); - -function fail(proc, args) { - expect(() => { - proc.send.apply(proc, args); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); -} - -let target = process; - -if (process.argv[2] !== "child") { - test("child process send type error", () => { - target = cp.fork(__filename, ["child"]); - target.on("exit", (code, signal) => { - expect(code).toBe(0); - expect(signal).toBeNull(); - }); - - fail(target, ["msg", null, null]); - fail(target, ["msg", null, ""]); - fail(target, ["msg", null, "foo"]); - fail(target, ["msg", null, 0]); - fail(target, ["msg", null, NaN]); - fail(target, ["msg", null, 1]); - fail(target, ["msg", null, null, jest.fn()]); - }); -} else { - test("process send type error", () => { - fail(target, ["msg", null, null]); - fail(target, ["msg", null, ""]); - fail(target, ["msg", null, "foo"]); - fail(target, ["msg", null, 0]); - fail(target, ["msg", null, NaN]); - fail(target, ["msg", null, 1]); - fail(target, ["msg", null, null, jest.fn()]); - }); -} - -//<#END_FILE: test-child-process-send-type-error.js diff --git a/test/js/node/test/parallel/child-process-set-blocking.test.js b/test/js/node/test/parallel/child-process-set-blocking.test.js deleted file mode 100644 index a8288fc114..0000000000 --- a/test/js/node/test/parallel/child-process-set-blocking.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-child-process-set-blocking.js -//#SHA1: 2855d3d616c7af61fc6b84705cd05515f02bcb47 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const { spawn } = require("child_process"); -const os = require("os"); - -const SIZE = 100000; -const python = process.env.PYTHON || (os.platform() === "win32" ? "python" : "python3"); - -test("child process set blocking", done => { - const cp = spawn(python, ["-c", `print(${SIZE} * "C")`], { - stdio: "inherit", - }); - - cp.on("exit", code => { - expect(code).toBe(0); - done(); - }); -}); - -//<#END_FILE: test-child-process-set-blocking.js diff --git a/test/js/node/test/parallel/child-process-spawnsync-env.test.js b/test/js/node/test/parallel/child-process-spawnsync-env.test.js deleted file mode 100644 index e6368f7e38..0000000000 --- a/test/js/node/test/parallel/child-process-spawnsync-env.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-child-process-spawnsync-env.js -//#SHA1: 21ad31214e1261fb3c2636bd98d36946e5be67de -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const cp = require("child_process"); - -if (process.argv[2] === "child") { - console.log(process.env.foo); -} else { - test("spawnSync with custom environment", () => { - const expected = "bar"; - const child = cp.spawnSync(process.execPath, [__filename, "child"], { - env: Object.assign({}, process.env, { foo: expected }), - }); - - expect(child.stdout.toString().trim()).toBe(expected); - }); -} - -//<#END_FILE: test-child-process-spawnsync-env.js diff --git a/test/js/node/test/parallel/child-process-stdio-big-write-end.test.js b/test/js/node/test/parallel/child-process-stdio-big-write-end.test.js deleted file mode 100644 index ff1ea0cc16..0000000000 --- a/test/js/node/test/parallel/child-process-stdio-big-write-end.test.js +++ /dev/null @@ -1,92 +0,0 @@ -//#FILE: test-child-process-stdio-big-write-end.js -//#SHA1: 728a12ebb5484fcc628e82386c3b521ab95e0456 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const { spawn } = require("child_process"); -const assert = require("assert"); -const debug = require("util").debuglog("test"); - -let bufsize = 0; - -function runParent() { - return new Promise(resolve => { - const child = spawn(process.execPath, [__filename, "child"]); - let sent = 0; - - let n = ""; - child.stdout.setEncoding("ascii"); - child.stdout.on("data", c => { - n += c; - }); - child.stdout.on("end", () => { - expect(+n).toBe(sent); - debug("ok"); - resolve(); - }); - - // Write until the buffer fills up. - let buf; - do { - bufsize += 1024; - buf = Buffer.alloc(bufsize, "."); - sent += bufsize; - } while (child.stdin.write(buf)); - - // Then write a bunch more times. - for (let i = 0; i < 100; i++) { - const buf = Buffer.alloc(bufsize, "."); - sent += bufsize; - child.stdin.write(buf); - } - - // Now end, before it's all flushed. - child.stdin.end(); - - // now we wait... - }); -} - -function runChild() { - return new Promise(resolve => { - let received = 0; - process.stdin.on("data", c => { - received += c.length; - }); - process.stdin.on("end", () => { - // This console.log is part of the test. - console.log(received); - resolve(); - }); - }); -} - -if (process.argv[2] === "child") { - runChild(); -} else { - test("child process stdio big write end", async () => { - await runParent(); - }); -} - -//<#END_FILE: test-child-process-stdio-big-write-end.js diff --git a/test/js/node/test/parallel/child-process-stdio-overlapped.test.js b/test/js/node/test/parallel/child-process-stdio-overlapped.test.js deleted file mode 100644 index e37232fce3..0000000000 --- a/test/js/node/test/parallel/child-process-stdio-overlapped.test.js +++ /dev/null @@ -1,91 +0,0 @@ -//#FILE: test-child-process-stdio-overlapped.js -//#SHA1: 20ead82f2ea74983af7bead33605fe8f33fccf6d -//----------------- -// Test for "overlapped" stdio option. This test uses the "overlapped-checker" -// helper program which basically a specialized echo program. -// -// The test has two goals: -// -// - Verify that overlapped I/O works on windows. The test program will deadlock -// if stdin doesn't have the FILE_FLAG_OVERLAPPED flag set on startup (see -// test/overlapped-checker/main_win.c for more details). -// - Verify that "overlapped" stdio option works transparently as a pipe (on -// unix/windows) -// -// This is how the test works: -// -// - This script assumes only numeric strings are written to the test program -// stdout. -// - The test program will be spawned with "overlapped" set on stdin and "pipe" -// set on stdout/stderr and at startup writes a number to its stdout -// - When this script receives some data, it will parse the number, add 50 and -// write to the test program's stdin. -// - The test program will then echo the number back to us which will repeat the -// cycle until the number reaches 200, at which point we send the "exit" -// string, which causes the test program to exit. -// - Extra assertion: Every time the test program writes a string to its stdout, -// it will write the number of bytes written to stderr. -// - If overlapped I/O is not setup correctly, this test is going to hang. -"use strict"; -const path = require("path"); -const child_process = require("child_process"); -const fs = require("fs"); - -const exeExtension = process.platform === "win32" ? ".exe" : ""; -const exe = "overlapped-checker" + exeExtension; -const exePath = path.join(path.dirname(process.execPath), exe); - -test("overlapped stdio option", async () => { - if (!fs.existsSync(exePath)) { - console.log(exe + " binary is not available"); - return; - } - - const child = child_process.spawn(exePath, [], { - stdio: ["overlapped", "pipe", "pipe"], - }); - - child.stdin.setEncoding("utf8"); - child.stdout.setEncoding("utf8"); - child.stderr.setEncoding("utf8"); - - function writeNext(n) { - child.stdin.write((n + 50).toString()); - } - - child.stdout.on("data", s => { - const n = Number(s); - if (n >= 200) { - child.stdin.write("exit"); - return; - } - writeNext(n); - }); - - let stderr = ""; - child.stderr.on("data", s => { - stderr += s; - }); - - await new Promise(resolve => { - child.stderr.on("end", resolve); - }); - - // This is the sequence of numbers sent to us: - // - 0 (1 byte written) - // - 50 (2 bytes written) - // - 100 (3 bytes written) - // - 150 (3 bytes written) - // - 200 (3 bytes written) - expect(stderr).toBe("12333"); - - const exitPromise = new Promise(resolve => { - child.on("exit", resolve); - }); - - const status = await exitPromise; - // The test program will return the number of writes as status code. - expect(status).toBe(0); -}); - -//<#END_FILE: test-child-process-stdio-overlapped.js diff --git a/test/js/node/test/parallel/cli-eval-event.test.js b/test/js/node/test/parallel/cli-eval-event.test.js deleted file mode 100644 index 152c3da4dc..0000000000 --- a/test/js/node/test/parallel/cli-eval-event.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-cli-eval-event.js -//#SHA1: d64fa25056a9ecf2e87e46560d477b42d3e32909 -//----------------- -"use strict"; - -const { spawn } = require("child_process"); - -test("CLI eval event", () => { - return new Promise(resolve => { - const child = spawn(process.execPath, [ - "-e", - ` - const server = require('net').createServer().listen(0); - server.once('listening', server.close); - `, - ]); - - child.once("exit", (exitCode, signalCode) => { - expect(exitCode).toBe(0); - expect(signalCode).toBeNull(); - resolve(); - }); - }); -}); - -//<#END_FILE: test-cli-eval-event.js diff --git a/test/js/node/test/parallel/client-request-destroy.test.js b/test/js/node/test/parallel/client-request-destroy.test.js deleted file mode 100644 index 165fbbdd33..0000000000 --- a/test/js/node/test/parallel/client-request-destroy.test.js +++ /dev/null @@ -1,19 +0,0 @@ -//#FILE: test-client-request-destroy.js -//#SHA1: 343919bc022f2956e9aab5c9a215cbadca2364f1 -//----------------- -"use strict"; - -// Test that http.ClientRequest.prototype.destroy() returns `this`. - -const http = require("http"); - -test("http.ClientRequest.prototype.destroy() returns `this`", () => { - const clientRequest = new http.ClientRequest({ createConnection: () => {} }); - - expect(clientRequest.destroyed).toBe(false); - expect(clientRequest.destroy()).toBe(clientRequest); - expect(clientRequest.destroyed).toBe(true); - expect(clientRequest.destroy()).toBe(clientRequest); -}); - -//<#END_FILE: test-client-request-destroy.js diff --git a/test/js/node/test/parallel/cluster-bind-privileged-port.test.js b/test/js/node/test/parallel/cluster-bind-privileged-port.test.js deleted file mode 100644 index c83b370b56..0000000000 --- a/test/js/node/test/parallel/cluster-bind-privileged-port.test.js +++ /dev/null @@ -1,79 +0,0 @@ -//#FILE: test-cluster-bind-privileged-port.js -//#SHA1: f3a12e75717db9c1a1edd4f51fb2985787a50706 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const cluster = require("cluster"); -const net = require("net"); -const { readFileSync } = require("fs"); - -const isLinux = process.platform === "linux"; -const isOSX = process.platform === "darwin"; -const isIBMi = process.platform === "os400"; -const isWindows = process.platform === "win32"; - -const skipTest = () => { - test.skip("Skipping test", () => {}); - process.exit(0); -}; - -if (isLinux) { - try { - const unprivilegedPortStart = parseInt(readFileSync("/proc/sys/net/ipv4/ip_unprivileged_port_start")); - if (unprivilegedPortStart <= 42) { - skipTest(); - } - } catch { - // Do nothing, feature doesn't exist, minimum is 1024 so 42 is usable. - // Continue... - } -} - -// Skip on OS X Mojave. https://github.com/nodejs/node/issues/21679 -if (isOSX) skipTest(); - -if (isIBMi) skipTest(); - -if (isWindows) skipTest(); - -if (process.getuid() === 0) skipTest(); - -if (cluster.isPrimary) { - test("primary cluster process", () => { - const worker = cluster.fork(); - worker.on("exit", exitCode => { - expect(exitCode).toBe(0); - }); - }); -} else { - test("worker cluster process", () => { - const s = net.createServer(jest.fn()); - s.listen(42, jest.fn()); - s.on("error", err => { - expect(err.code).toBe("EACCES"); - process.disconnect(); - }); - }); -} - -//<#END_FILE: test-cluster-bind-privileged-port.js diff --git a/test/js/node/test/parallel/cluster-call-and-destroy.test.js b/test/js/node/test/parallel/cluster-call-and-destroy.test.js deleted file mode 100644 index 1e2914833f..0000000000 --- a/test/js/node/test/parallel/cluster-call-and-destroy.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-cluster-call-and-destroy.js -//#SHA1: 840777cd738f6257dd874035b1c0291ebe16e326 -//----------------- -"use strict"; -const cluster = require("cluster"); - -if (cluster.isPrimary) { - test("worker disconnection and destruction", () => { - const worker = cluster.fork(); - - return new Promise(resolve => { - worker.on("disconnect", () => { - expect(worker.isConnected()).toBe(false); - worker.destroy(); - resolve(); - }); - }); - }); -} else { - test("worker connection in child process", () => { - expect(cluster.worker.isConnected()).toBe(true); - cluster.worker.disconnect(); - }); -} - -//<#END_FILE: test-cluster-call-and-destroy.js diff --git a/test/js/node/test/parallel/cluster-dgram-reuse.test.js b/test/js/node/test/parallel/cluster-dgram-reuse.test.js deleted file mode 100644 index 143db050b6..0000000000 --- a/test/js/node/test/parallel/cluster-dgram-reuse.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-cluster-dgram-reuse.js -//#SHA1: b7bfc0764ebc95fa5ef85ce1d860aebd3f7df539 -//----------------- -"use strict"; - -const cluster = require("cluster"); -const dgram = require("dgram"); - -if (process.platform === "win32") { - test.skip("dgram clustering is currently not supported on windows."); -} else { - if (cluster.isPrimary) { - test("Primary process", () => { - const worker = cluster.fork(); - worker.on("exit", code => { - expect(code).toBe(0); - }); - }); - } else { - test("Worker process", async () => { - let waiting = 2; - function close() { - if (--waiting === 0) cluster.worker.disconnect(); - } - - const options = { type: "udp4", reuseAddr: true }; - const socket1 = dgram.createSocket(options); - const socket2 = dgram.createSocket(options); - - await new Promise(resolve => { - socket1.bind(0, () => { - socket2.bind(socket1.address().port, () => { - // Work around health check issue - process.nextTick(() => { - socket1.close(close); - socket2.close(close); - resolve(); - }); - }); - }); - }); - }); - } -} - -//<#END_FILE: test-cluster-dgram-reuse.js diff --git a/test/js/node/test/parallel/cluster-disconnect-idle-worker.test.js b/test/js/node/test/parallel/cluster-disconnect-idle-worker.test.js deleted file mode 100644 index 217060fd12..0000000000 --- a/test/js/node/test/parallel/cluster-disconnect-idle-worker.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-cluster-disconnect-idle-worker.js -//#SHA1: f559db612db77271be32bab2d7cb9a4e38f28670 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const cluster = require("cluster"); -const fork = cluster.fork; - -if (cluster.isPrimary) { - test("cluster disconnect idle worker", async () => { - fork(); // It is intentionally called `fork` instead of - fork(); // `cluster.fork` to test that `this` is not used - - const disconnectCallback = jest.fn(() => { - expect(Object.keys(cluster.workers)).toEqual([]); - }); - - await new Promise(resolve => { - cluster.disconnect(() => { - disconnectCallback(); - resolve(); - }); - }); - - expect(disconnectCallback).toHaveBeenCalledTimes(1); - }); -} - -//<#END_FILE: test-cluster-disconnect-idle-worker.js diff --git a/test/js/node/test/parallel/cluster-disconnect-with-no-workers.test.js b/test/js/node/test/parallel/cluster-disconnect-with-no-workers.test.js deleted file mode 100644 index 01c67b2877..0000000000 --- a/test/js/node/test/parallel/cluster-disconnect-with-no-workers.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-cluster-disconnect-with-no-workers.js -//#SHA1: 06b4a0a662491bd9593c303307535a635d69d53e -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const cluster = require("cluster"); - -let disconnected = false; - -test("cluster.disconnect with no workers", async () => { - const disconnectPromise = new Promise(resolve => { - cluster.disconnect(() => { - disconnected = true; - resolve(); - }); - }); - - // Assert that callback is not sometimes synchronous - expect(disconnected).toBe(false); - - await disconnectPromise; - - // Assert that the callback was called - expect(disconnected).toBe(true); -}); - -//<#END_FILE: test-cluster-disconnect-with-no-workers.js diff --git a/test/js/node/test/parallel/cluster-eaddrinuse.test.js b/test/js/node/test/parallel/cluster-eaddrinuse.test.js deleted file mode 100644 index c72c6d6af5..0000000000 --- a/test/js/node/test/parallel/cluster-eaddrinuse.test.js +++ /dev/null @@ -1,94 +0,0 @@ -//#FILE: test-cluster-eaddrinuse.js -//#SHA1: a17cbbd83e23565c1cb547999357c14f03be6efa -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Check that having a worker bind to a port that's already taken doesn't -// leave the primary process in a confused state. Releasing the port and -// trying again should Just Work[TM]. - -const { fork } = require("child_process"); -const net = require("net"); - -const id = String(process.argv[2]); -const port = String(process.argv[3]); - -if (id === "undefined") { - test("primary process", async () => { - const server = net.createServer(() => { - throw new Error("Server should not receive connections"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const worker = fork(__filename, ["worker", server.address().port]); - worker.on("message", msg => { - if (msg !== "stop-listening") return; - server.close(() => { - worker.send("stopped-listening"); - }); - }); - resolve(); - }); - }); - }); -} else if (id === "worker") { - test("worker process", async () => { - let server = net.createServer(() => { - throw new Error("Server should not receive connections"); - }); - - await expect( - new Promise((resolve, reject) => { - server.listen(port, () => reject(new Error("Server should not listen"))); - server.on("error", resolve); - }), - ).resolves.toMatchObject({ - code: "EADDRINUSE", - message: expect.any(String), - }); - - process.send("stop-listening"); - - await new Promise(resolve => { - process.once("message", msg => { - if (msg !== "stopped-listening") return; - server = net.createServer(() => { - throw new Error("Server should not receive connections"); - }); - server.listen(port, () => { - server.close(); - resolve(); - }); - }); - }); - }); -} else { - test("invalid argument", () => { - expect(() => { - throw new Error("Bad argument"); - }).toThrow("Bad argument"); - }); -} - -//<#END_FILE: test-cluster-eaddrinuse.js diff --git a/test/js/node/test/parallel/cluster-listen-pipe-readable-writable.test.js b/test/js/node/test/parallel/cluster-listen-pipe-readable-writable.test.js deleted file mode 100644 index ea008eda16..0000000000 --- a/test/js/node/test/parallel/cluster-listen-pipe-readable-writable.test.js +++ /dev/null @@ -1,44 +0,0 @@ -//#FILE: test-cluster-listen-pipe-readable-writable.js -//#SHA1: ec8b0021cb41214529af900b088dce4d31db708d -//----------------- -"use strict"; - -const cluster = require("cluster"); -const net = require("net"); -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const PIPE = path.join(os.tmpdir(), "test.sock"); - -if (process.platform === "win32") { - test.skip("skip on Windows", () => {}); -} else { - if (cluster.isPrimary) { - test("cluster worker can listen on pipe with readable and writable permissions", () => { - const worker = cluster.fork(); - worker.on("exit", code => { - expect(code).toBe(0); - }); - }); - } else { - test("server listens on pipe with correct permissions", done => { - const server = net.createServer().listen( - { - path: PIPE, - readableAll: true, - writableAll: true, - }, - () => { - const stat = fs.statSync(PIPE); - expect(stat.mode & 0o777).toBe(0o777); - server.close(); - process.disconnect(); - done(); - }, - ); - }); - } -} - -//<#END_FILE: test-cluster-listen-pipe-readable-writable.js diff --git a/test/js/node/test/parallel/cluster-send-handle-twice.test.js b/test/js/node/test/parallel/cluster-send-handle-twice.test.js deleted file mode 100644 index 06cb47d583..0000000000 --- a/test/js/node/test/parallel/cluster-send-handle-twice.test.js +++ /dev/null @@ -1,75 +0,0 @@ -//#FILE: test-cluster-send-handle-twice.js -//#SHA1: d667e299035da70aa7831cb6964ba4e974c6bdc8 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Testing to send an handle twice to the primary process. - -const cluster = require("cluster"); -const net = require("net"); - -const workers = { - toStart: 1, -}; - -if (cluster.isPrimary) { - test("primary process", () => { - for (let i = 0; i < workers.toStart; ++i) { - const worker = cluster.fork(); - worker.on("exit", (code, signal) => { - expect(code).toBe(0); - expect(signal).toBeNull(); - }); - } - }); -} else { - test("worker process", async () => { - const server = net.createServer(socket => { - process.send("send-handle-1", socket); - process.send("send-handle-2", socket); - }); - - await new Promise((resolve, reject) => { - server.listen(0, () => { - const client = net.connect({ - host: "localhost", - port: server.address().port, - }); - client.on("close", () => { - cluster.worker.disconnect(); - resolve(); - }); - client.on("connect", () => { - client.end(); - }); - }); - - server.on("error", e => { - console.error(e); - reject(new Error("server.listen failed")); - }); - }); - }); -} - -//<#END_FILE: test-cluster-send-handle-twice.js diff --git a/test/js/node/test/parallel/cluster-setup-primary-cumulative.test.js b/test/js/node/test/parallel/cluster-setup-primary-cumulative.test.js deleted file mode 100644 index 178c7f77ab..0000000000 --- a/test/js/node/test/parallel/cluster-setup-primary-cumulative.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-cluster-setup-primary-cumulative.js -//#SHA1: 8a64228ac6d42c930b2426bbc53009f5d8b96a17 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const assert = require("assert"); -const cluster = require("cluster"); - -test("cluster setup primary cumulative", () => { - expect(cluster.isPrimary).toBe(true); - - // cluster.settings should not be initialized until needed - expect(cluster.settings).toEqual({}); - - cluster.setupPrimary(); - expect(cluster.settings).toEqual({ - args: process.argv.slice(2), - exec: process.argv[1], - execArgv: process.execArgv, - silent: false, - }); - - cluster.setupPrimary({ exec: "overridden" }); - expect(cluster.settings.exec).toBe("overridden"); - - cluster.setupPrimary({ args: ["foo", "bar"] }); - expect(cluster.settings.exec).toBe("overridden"); - expect(cluster.settings.args).toEqual(["foo", "bar"]); - - cluster.setupPrimary({ execArgv: ["baz", "bang"] }); - expect(cluster.settings.exec).toBe("overridden"); - expect(cluster.settings.args).toEqual(["foo", "bar"]); - expect(cluster.settings.execArgv).toEqual(["baz", "bang"]); - - cluster.setupPrimary(); - expect(cluster.settings).toEqual({ - args: ["foo", "bar"], - exec: "overridden", - execArgv: ["baz", "bang"], - silent: false, - }); -}); - -//<#END_FILE: test-cluster-setup-primary-cumulative.js diff --git a/test/js/node/test/parallel/cluster-setup-primary-emit.test.js b/test/js/node/test/parallel/cluster-setup-primary-emit.test.js deleted file mode 100644 index dc036034bd..0000000000 --- a/test/js/node/test/parallel/cluster-setup-primary-emit.test.js +++ /dev/null @@ -1,67 +0,0 @@ -//#FILE: test-cluster-setup-primary-emit.js -//#SHA1: 965f86ef2ea557510e956759d3f540edfa7b03de -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const cluster = require("cluster"); - -expect(cluster.isPrimary).toBe(true); - -function emitAndCatch(next) { - return new Promise(resolve => { - cluster.once("setup", settings => { - expect(settings.exec).toBe("new-exec"); - setImmediate(() => { - next(); - resolve(); - }); - }); - cluster.setupPrimary({ exec: "new-exec" }); - }); -} - -function emitAndCatch2(next) { - return new Promise(resolve => { - cluster.once("setup", settings => { - expect(settings).toHaveProperty("exec"); - setImmediate(() => { - next(); - resolve(); - }); - }); - cluster.setupPrimary(); - }); -} - -test("cluster setup primary emit", async () => { - const nextSpy1 = jest.fn(); - const nextSpy2 = jest.fn(); - - await emitAndCatch(nextSpy1); - expect(nextSpy1).toHaveBeenCalledTimes(1); - - await emitAndCatch2(nextSpy2); - expect(nextSpy2).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-cluster-setup-primary-emit.js diff --git a/test/js/node/test/parallel/cluster-setup-primary-multiple.test.js b/test/js/node/test/parallel/cluster-setup-primary-multiple.test.js deleted file mode 100644 index 05a3ca9cf5..0000000000 --- a/test/js/node/test/parallel/cluster-setup-primary-multiple.test.js +++ /dev/null @@ -1,78 +0,0 @@ -//#FILE: test-cluster-setup-primary-multiple.js -//#SHA1: a0b16cb2b01b0265f98508f2a6a9974396b6b03a -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const cluster = require("cluster"); -const debug = require("util").debuglog("test"); - -test("cluster setup primary multiple times", async () => { - expect(cluster.isPrimary).toBe(true); - - // The cluster.settings object is cloned even though the current implementation - // makes that unnecessary. This is to make the test less fragile if the - // implementation ever changes such that cluster.settings is mutated instead of - // replaced. - const cheapClone = obj => JSON.parse(JSON.stringify(obj)); - - const configs = []; - - // Capture changes - cluster.on("setup", () => { - debug(`"setup" emitted ${JSON.stringify(cluster.settings)}`); - configs.push(cheapClone(cluster.settings)); - }); - - const execs = ["node-next", "node-next-2", "node-next-3"]; - - // Make changes to cluster settings - for (let i = 0; i < execs.length; i++) { - await new Promise(resolve => { - setTimeout(() => { - cluster.setupPrimary({ exec: execs[i] }); - resolve(); - }, i * 100); - }); - } - - // Cluster emits 'setup' asynchronously, so we must stay alive long - // enough for that to happen - await new Promise(resolve => { - setTimeout( - () => { - debug("cluster setup complete"); - resolve(); - }, - (execs.length + 1) * 100, - ); - }); - - // Tests that "setup" is emitted for every call to setupPrimary - expect(configs.length).toBe(execs.length); - - expect(configs[0].exec).toBe(execs[0]); - expect(configs[1].exec).toBe(execs[1]); - expect(configs[2].exec).toBe(execs[2]); -}); - -//<#END_FILE: test-cluster-setup-primary-multiple.js diff --git a/test/js/node/test/parallel/cluster-worker-constructor.test.js b/test/js/node/test/parallel/cluster-worker-constructor.test.js deleted file mode 100644 index 470481560e..0000000000 --- a/test/js/node/test/parallel/cluster-worker-constructor.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-cluster-worker-constructor.js -//#SHA1: ef3237d09cf6339e487f14c40d4c047c6871ead2 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// test-cluster-worker-constructor.js -// validates correct behavior of the cluster.Worker constructor - -const cluster = require("cluster"); - -describe("cluster.Worker constructor", () => { - test("creates a worker with default values", () => { - const worker = new cluster.Worker(); - expect(worker.exitedAfterDisconnect).toBeUndefined(); - expect(worker.state).toBe("none"); - expect(worker.id).toBe(0); - expect(worker.process).toBeUndefined(); - }); - - test("creates a worker with custom values", () => { - const worker = new cluster.Worker({ - id: 3, - state: "online", - process: process, - }); - expect(worker.exitedAfterDisconnect).toBeUndefined(); - expect(worker.state).toBe("online"); - expect(worker.id).toBe(3); - expect(worker.process).toBe(process); - }); - - test("creates a worker using call method", () => { - const worker = cluster.Worker.call({}, { id: 5 }); - expect(worker).toBeInstanceOf(cluster.Worker); - expect(worker.id).toBe(5); - }); -}); - -//<#END_FILE: test-cluster-worker-constructor.js diff --git a/test/js/node/test/parallel/cluster-worker-destroy.test.js b/test/js/node/test/parallel/cluster-worker-destroy.test.js deleted file mode 100644 index 607cbbb866..0000000000 --- a/test/js/node/test/parallel/cluster-worker-destroy.test.js +++ /dev/null @@ -1,73 +0,0 @@ -//#FILE: test-cluster-worker-destroy.js -//#SHA1: 277a85b7c8fdda347d1f753601ac4b843a9a1c8d -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -// The goal of this test is to cover the Workers' implementation of -// Worker.prototype.destroy. Worker.prototype.destroy is called within -// the worker's context: once when the worker is still connected to the -// primary, and another time when it's not connected to it, so that we cover -// both code paths. - -const assert = require("assert"); -const cluster = require("cluster"); - -let worker1, worker2; - -if (cluster.isPrimary) { - test("primary process", () => { - worker1 = cluster.fork(); - worker2 = cluster.fork(); - - [worker1, worker2].forEach(worker => { - const disconnectSpy = jest.fn(); - const exitSpy = jest.fn(); - - worker.on("disconnect", disconnectSpy); - worker.on("exit", exitSpy); - - worker.on("exit", () => { - expect(disconnectSpy).toHaveBeenCalledTimes(1); - expect(exitSpy).toHaveBeenCalledTimes(1); - }); - }); - }); -} else if (cluster.worker.id === 1) { - test("worker 1: call destroy when worker is disconnected", () => { - // Call destroy when worker is disconnected - cluster.worker.process.on("disconnect", () => { - cluster.worker.destroy(); - }); - - const w = cluster.worker.disconnect(); - expect(w).toBe(cluster.worker); - }); -} else { - test("worker 2: call destroy when worker is not disconnected yet", () => { - // Call destroy when worker is not disconnected yet - cluster.worker.destroy(); - }); -} - -//<#END_FILE: test-cluster-worker-destroy.js diff --git a/test/js/node/test/parallel/cluster-worker-handle-close.test.js b/test/js/node/test/parallel/cluster-worker-handle-close.test.js deleted file mode 100644 index f6f608e0ac..0000000000 --- a/test/js/node/test/parallel/cluster-worker-handle-close.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-cluster-worker-handle-close.js -//#SHA1: 8aa4bcd8641fe9274b97853b80734ea6f18eafbb -//----------------- -"use strict"; -const cluster = require("cluster"); -const net = require("net"); - -if (cluster.isPrimary) { - test("Primary process", () => { - cluster.schedulingPolicy = cluster.SCHED_RR; - expect(cluster.fork).not.toThrow(); - }); -} else { - let server; - - beforeAll(() => { - server = net.createServer(jest.fn()); - }); - - test("Worker process", done => { - const serverListenSpy = jest.spyOn(server, "listen"); - const netConnectSpy = jest.spyOn(net, "connect"); - - server.listen(0, () => { - expect(serverListenSpy).toHaveBeenCalledTimes(1); - expect(netConnectSpy).toHaveBeenCalledWith(server.address().port); - done(); - }); - }); - - test("Internal message handling", done => { - const handleCloseSpy = jest.fn(callback => callback()); - const handle = { close: handleCloseSpy }; - - const messageHandler = (message, messageHandle) => { - if (message.act !== "newconn") { - return; - } - - server.close(); - messageHandle.close = jest.fn(() => { - handleCloseSpy.call(messageHandle, () => { - expect(handleCloseSpy).toHaveBeenCalledTimes(1); - process.exit(); - }); - }); - - expect(messageHandle.close).toHaveBeenCalledTimes(1); - done(); - }; - - process.prependListener("internalMessage", messageHandler); - - // Simulate an internal message - process.emit("internalMessage", { act: "newconn" }, handle); - }); -} - -//<#END_FILE: test-cluster-worker-handle-close.js diff --git a/test/js/node/test/parallel/cluster-worker-isconnected.test.js b/test/js/node/test/parallel/cluster-worker-isconnected.test.js deleted file mode 100644 index 4ca42ebe61..0000000000 --- a/test/js/node/test/parallel/cluster-worker-isconnected.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-cluster-worker-isconnected.js -//#SHA1: cf1e0243c030fe4cf872716099e517daec3efffc -//----------------- -"use strict"; -const cluster = require("cluster"); - -if (cluster.isPrimary) { - test("worker isConnected() in primary", () => { - const worker = cluster.fork(); - - expect(worker.isConnected()).toBe(true); - - worker.on("disconnect", () => { - expect(worker.isConnected()).toBe(false); - }); - - worker.on("message", function (msg) { - if (msg === "readyToDisconnect") { - worker.disconnect(); - } - }); - }); -} else { - test("worker isConnected() in worker", () => { - function assertNotConnected() { - expect(cluster.worker.isConnected()).toBe(false); - } - - expect(cluster.worker.isConnected()).toBe(true); - - cluster.worker.on("disconnect", assertNotConnected); - cluster.worker.process.on("disconnect", assertNotConnected); - - process.send("readyToDisconnect"); - }); -} - -//<#END_FILE: test-cluster-worker-isconnected.js diff --git a/test/js/node/test/parallel/common-countdown.test.js b/test/js/node/test/parallel/common-countdown.test.js deleted file mode 100644 index 24c268588e..0000000000 --- a/test/js/node/test/parallel/common-countdown.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-common-countdown.js -//#SHA1: ba753878e7b8cbeaede6057bc05a7d3b542949a5 -//----------------- -"use strict"; - -const assert = require("assert"); -const Countdown = require("../common/countdown"); -const fixtures = require("../common/fixtures"); -const { execFile } = require("child_process"); - -test("Countdown functionality", () => { - let done = ""; - const countdown = new Countdown(2, () => (done = true)); - expect(countdown.remaining).toBe(2); - countdown.dec(); - expect(countdown.remaining).toBe(1); - countdown.dec(); - expect(countdown.remaining).toBe(0); - expect(done).toBe(true); -}); - -const failFixtures = [ - [fixtures.path("failcounter.js"), "Mismatched function calls. Expected exactly 1, actual 0."], -]; - -test.each(failFixtures)("Fail fixture: %s", async (file, expected) => { - await new Promise(resolve => { - execFile(process.argv[0], [file], (ex, stdout, stderr) => { - expect(ex).toBeTruthy(); - expect(stderr).toBe(""); - const firstLine = stdout.split("\n").shift(); - expect(firstLine).toBe(expected); - resolve(); - }); - }); -}); - -//<#END_FILE: test-common-countdown.js diff --git a/test/js/node/test/parallel/console-assign-undefined.test.js b/test/js/node/test/parallel/console-assign-undefined.test.js deleted file mode 100644 index cc46f41b42..0000000000 --- a/test/js/node/test/parallel/console-assign-undefined.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-console-assign-undefined.js -//#SHA1: ccd5cd3087520e692e5123679c1753d168d310f0 -//----------------- -"use strict"; - -// Patch global.console before importing modules that may modify the console -// object. - -let originalConsole; - -beforeAll(() => { - originalConsole = global.console; - global.console = 42; -}); - -afterAll(() => { - // Reset the console - global.console = originalConsole; -}); - -test("console can be assigned a non-object value", () => { - // Originally the console had a getter. Test twice to verify it had no side - // effect. - expect(global.console).toBe(42); - expect(global.console).toBe(42); - - expect(() => console.log("foo")).toThrow( - expect.objectContaining({ - name: "TypeError", - message: expect.any(String), - }), - ); - - global.console = 1; - expect(global.console).toBe(1); - expect(console).toBe(1); -}); - -test("console can be reset and used", () => { - global.console = originalConsole; - const consoleSpy = jest.spyOn(console, "log"); - console.log("foo"); - expect(consoleSpy).toHaveBeenCalledWith("foo"); - consoleSpy.mockRestore(); -}); - -//<#END_FILE: test-console-assign-undefined.js diff --git a/test/js/node/test/parallel/console-issue-43095.test.js b/test/js/node/test/parallel/console-issue-43095.test.js deleted file mode 100644 index 815c2d86ad..0000000000 --- a/test/js/node/test/parallel/console-issue-43095.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-console-issue-43095.js -//#SHA1: 1c0c5cce62bcee4d50c6b716dd1430db2784c3f4 -//----------------- -"use strict"; - -const { inspect } = require("node:util"); - -test("console output for revoked proxy", () => { - const consoleSpy = { - dir: jest.spyOn(console, "dir").mockImplementation(), - log: jest.spyOn(console, "log").mockImplementation(), - }; - - const r = Proxy.revocable({}, {}); - r.revoke(); - - console.dir(r); - console.dir(r.proxy); - console.log(r.proxy); - console.log(inspect(r.proxy, { showProxy: true })); - - expect(consoleSpy.dir).toHaveBeenCalledTimes(2); - expect(consoleSpy.log).toHaveBeenCalledTimes(2); - - // Check that console.dir was called with the revoked proxy object - expect(consoleSpy.dir.mock.calls[0][0]).toBe(r); - expect(consoleSpy.dir.mock.calls[1][0]).toBe(r.proxy); - - // Check that console.log was called with the revoked proxy - expect(consoleSpy.log.mock.calls[0][0]).toBe(r.proxy); - - // Check that console.log was called with the inspected revoked proxy - expect(consoleSpy.log.mock.calls[1][0]).toBe(inspect(r.proxy, { showProxy: true })); - - // Clean up - consoleSpy.dir.mockRestore(); - consoleSpy.log.mockRestore(); -}); - -//<#END_FILE: test-console-issue-43095.js diff --git a/test/js/node/test/parallel/console-log-stdio-broken-dest.test.js b/test/js/node/test/parallel/console-log-stdio-broken-dest.test.js deleted file mode 100644 index b93dfe3824..0000000000 --- a/test/js/node/test/parallel/console-log-stdio-broken-dest.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-console-log-stdio-broken-dest.js -//#SHA1: c2c2e85eeb28db4ace2c4bb0a86f46f7e7bf2682 -//----------------- -"use strict"; - -const { Writable } = require("stream"); -const { Console } = require("console"); -const { EventEmitter } = require("events"); - -test("Console log with broken destination", done => { - const stream = new Writable({ - write(chunk, enc, cb) { - cb(); - }, - writev(chunks, cb) { - setTimeout(cb, 10, new Error("kaboom")); - }, - }); - const myConsole = new Console(stream, stream); - - const warningListener = jest.fn(); - process.on("warning", warningListener); - - stream.cork(); - for (let i = 0; i < EventEmitter.defaultMaxListeners + 1; i++) { - myConsole.log("a message"); - } - stream.uncork(); - - // We need to wait for the next tick to ensure the error has time to propagate - process.nextTick(() => { - expect(warningListener).not.toHaveBeenCalled(); - process.removeListener("warning", warningListener); - done(); - }); -}); - -//<#END_FILE: test-console-log-stdio-broken-dest.js diff --git a/test/js/node/test/parallel/console-log-throw-primitive.test.js b/test/js/node/test/parallel/console-log-throw-primitive.test.js deleted file mode 100644 index e318eaaf4d..0000000000 --- a/test/js/node/test/parallel/console-log-throw-primitive.test.js +++ /dev/null @@ -1,22 +0,0 @@ -//#FILE: test-console-log-throw-primitive.js -//#SHA1: a1889badf1058f6fadc8984a5075f3d048e2948c -//----------------- -"use strict"; - -const { Writable } = require("stream"); -const { Console } = require("console"); - -test("Console.log should not throw when stream throws null", () => { - const stream = new Writable({ - write() { - throw null; // eslint-disable-line no-throw-literal - }, - }); - - const console = new Console({ stdout: stream }); - - // Should not throw - expect(() => console.log("test")).not.toThrow(); -}); - -//<#END_FILE: test-console-log-throw-primitive.js diff --git a/test/js/node/test/parallel/console-not-call-tostring.test.js b/test/js/node/test/parallel/console-not-call-tostring.test.js deleted file mode 100644 index f43e7dbde6..0000000000 --- a/test/js/node/test/parallel/console-not-call-tostring.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-console-not-call-toString.js -//#SHA1: e0bf3a601442b76f12f657e53df54690d2fe21fa -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -test("util.inspect does not call toString", () => { - function func() {} - let toStringCalled = false; - func.toString = function () { - toStringCalled = true; - }; - - require("util").inspect(func); - - expect(toStringCalled).toBe(false); -}); - -//<#END_FILE: test-console-not-call-toString.js diff --git a/test/js/node/test/parallel/console-self-assign.test.js b/test/js/node/test/parallel/console-self-assign.test.js deleted file mode 100644 index e746e9a47a..0000000000 --- a/test/js/node/test/parallel/console-self-assign.test.js +++ /dev/null @@ -1,13 +0,0 @@ -//#FILE: test-console-self-assign.js -//#SHA1: 7ed2fd07a18f0485f2592ada8e97e9c33753e691 -//----------------- -"use strict"; - -// Assigning to itself should not throw. -test("console self-assignment", () => { - expect(() => { - global.console = global.console; // eslint-disable-line no-self-assign - }).not.toThrow(); -}); - -//<#END_FILE: test-console-self-assign.js diff --git a/test/js/node/test/parallel/crypto-dh-shared.test.js b/test/js/node/test/parallel/crypto-dh-shared.test.js deleted file mode 100644 index f811ca4df7..0000000000 --- a/test/js/node/test/parallel/crypto-dh-shared.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-crypto-dh-shared.js -//#SHA1: 8d5e31de4aa93f435c4c6d05d7b394156a38fb8e -//----------------- -"use strict"; - -const crypto = require("crypto"); - -test("Diffie-Hellman shared secret computation", () => { - const alice = crypto.createDiffieHellmanGroup("modp5"); - const bob = crypto.createDiffieHellmanGroup("modp5"); - - alice.generateKeys(); - bob.generateKeys(); - - const aSecret = alice.computeSecret(bob.getPublicKey()).toString("hex"); - const bSecret = bob.computeSecret(alice.getPublicKey()).toString("hex"); - - expect(aSecret).toBe(bSecret); -}); - -//<#END_FILE: test-crypto-dh-shared.js diff --git a/test/js/node/test/parallel/crypto-from-binary.test.js b/test/js/node/test/parallel/crypto-from-binary.test.js deleted file mode 100644 index 3c015ec6b6..0000000000 --- a/test/js/node/test/parallel/crypto-from-binary.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-crypto-from-binary.js -//#SHA1: 2f3b186e9b549c6910a58dea98e6bdeb7c540afa -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// This is the same as test/simple/test-crypto, but from before the shift -// to use buffers by default. - -const crypto = require("crypto"); - -const EXTERN_APEX = 0xfbee9; - -// Manually controlled string for checking binary output -let ucs2_control = "a\u0000"; - -// Grow the strings to proper length -while (ucs2_control.length <= EXTERN_APEX) { - ucs2_control = ucs2_control.repeat(2); -} - -// Check resultant buffer and output string -const b = Buffer.from(ucs2_control + ucs2_control, "ucs2"); - -describe("Crypto from binary", () => { - beforeAll(() => { - if (!crypto) { - return test.skip("missing crypto"); - } - }); - - test("Update from binary data - small slice", () => { - const datum1 = b.slice(700000); - const hash1_converted = crypto.createHash("sha1").update(datum1.toString("base64"), "base64").digest("hex"); - const hash1_direct = crypto.createHash("sha1").update(datum1).digest("hex"); - expect(hash1_direct).toBe(hash1_converted); - }); - - test("Update from binary data - full buffer", () => { - const datum2 = b; - const hash2_converted = crypto.createHash("sha1").update(datum2.toString("base64"), "base64").digest("hex"); - const hash2_direct = crypto.createHash("sha1").update(datum2).digest("hex"); - expect(hash2_direct).toBe(hash2_converted); - }); -}); - -//<#END_FILE: test-crypto-from-binary.js diff --git a/test/js/node/test/parallel/crypto-keygen-async-elliptic-curve-jwk-rsa.test.js b/test/js/node/test/parallel/crypto-keygen-async-elliptic-curve-jwk-rsa.test.js deleted file mode 100644 index f19582afbd..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-async-elliptic-curve-jwk-rsa.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-crypto-keygen-async-elliptic-curve-jwk-rsa.js -//#SHA1: 4337f1e36680f21ed3439d7ab546ea855a0a842e -//----------------- -"use strict"; - -const { generateKeyPair } = require("crypto"); - -// Test async elliptic curve key generation with 'jwk' encoding and RSA. -test("async elliptic curve key generation with jwk encoding and RSA", async () => { - const { publicKey, privateKey } = await new Promise((resolve, reject) => { - generateKeyPair( - "rsa", - { - modulusLength: 1024, - publicKeyEncoding: { - format: "jwk", - }, - privateKeyEncoding: { - format: "jwk", - }, - }, - (err, publicKey, privateKey) => { - if (err) reject(err); - else resolve({ publicKey, privateKey }); - }, - ); - }); - - expect(typeof publicKey).toBe("object"); - expect(typeof privateKey).toBe("object"); - expect(publicKey.kty).toBe("RSA"); - expect(publicKey.kty).toBe(privateKey.kty); - expect(typeof publicKey.n).toBe("string"); - expect(publicKey.n).toBe(privateKey.n); - expect(typeof publicKey.e).toBe("string"); - expect(publicKey.e).toBe(privateKey.e); - expect(typeof privateKey.d).toBe("string"); - expect(typeof privateKey.p).toBe("string"); - expect(typeof privateKey.q).toBe("string"); - expect(typeof privateKey.dp).toBe("string"); - expect(typeof privateKey.dq).toBe("string"); - expect(typeof privateKey.qi).toBe("string"); -}); - -//<#END_FILE: test-crypto-keygen-async-elliptic-curve-jwk-rsa.js diff --git a/test/js/node/test/parallel/crypto-keygen-async-encrypted-private-key-der.test.js b/test/js/node/test/parallel/crypto-keygen-async-encrypted-private-key-der.test.js deleted file mode 100644 index 01e5b30494..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-async-encrypted-private-key-der.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-crypto-keygen-async-encrypted-private-key-der.js -//#SHA1: 30f86c68619f3f24294d5f062eed48b13c116b0c -//----------------- -"use strict"; - -const crypto = require("crypto"); -const { assertApproximateSize, testEncryptDecrypt, testSignVerify } = require("../common/crypto"); - -// Test async RSA key generation with an encrypted private key, but encoded as DER. -test("RSA key generation with encrypted private key (DER)", async () => { - const { publicKey: publicKeyDER, privateKey: privateKeyDER } = await new Promise((resolve, reject) => { - crypto.generateKeyPair( - "rsa", - { - publicExponent: 0x10001, - modulusLength: 512, - publicKeyEncoding: { - type: "pkcs1", - format: "der", - }, - privateKeyEncoding: { - type: "pkcs8", - format: "der", - }, - }, - (err, publicKey, privateKey) => { - if (err) reject(err); - else resolve({ publicKey, privateKey }); - }, - ); - }); - - expect(Buffer.isBuffer(publicKeyDER)).toBe(true); - assertApproximateSize(publicKeyDER, 74); - - expect(Buffer.isBuffer(privateKeyDER)).toBe(true); - - const publicKey = { - key: publicKeyDER, - type: "pkcs1", - format: "der", - }; - const privateKey = { - key: privateKeyDER, - format: "der", - type: "pkcs8", - passphrase: "secret", - }; - - await testEncryptDecrypt(publicKey, privateKey); - await testSignVerify(publicKey, privateKey); -}); - -//<#END_FILE: test-crypto-keygen-async-encrypted-private-key-der.js diff --git a/test/js/node/test/parallel/crypto-keygen-async-explicit-elliptic-curve.test.js b/test/js/node/test/parallel/crypto-keygen-async-explicit-elliptic-curve.test.js deleted file mode 100644 index 4d6f637147..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-async-explicit-elliptic-curve.test.js +++ /dev/null @@ -1,50 +0,0 @@ -//#FILE: test-crypto-keygen-async-explicit-elliptic-curve.js -//#SHA1: be1eabf816f52e5f53cb2535d47050b2552d21cd -//----------------- -"use strict"; - -const crypto = require("crypto"); - -// Skip the test if crypto support is not available -if (!crypto.generateKeyPair) { - test.skip("missing crypto support", () => {}); -} else { - const { generateKeyPair } = crypto; - - const { testSignVerify, spkiExp, sec1Exp } = require("../common/crypto"); - - // Test async explicit elliptic curve key generation, e.g. for ECDSA, - // with a SEC1 private key with paramEncoding explicit. - test("async explicit elliptic curve key generation", async () => { - await new Promise((resolve, reject) => { - generateKeyPair( - "ec", - { - namedCurve: "prime256v1", - paramEncoding: "explicit", - publicKeyEncoding: { - type: "spki", - format: "pem", - }, - privateKeyEncoding: { - type: "sec1", - format: "pem", - }, - }, - (err, publicKey, privateKey) => { - if (err) reject(err); - else resolve({ publicKey, privateKey }); - }, - ); - }).then(({ publicKey, privateKey }) => { - expect(typeof publicKey).toBe("string"); - expect(publicKey).toMatch(spkiExp); - expect(typeof privateKey).toBe("string"); - expect(privateKey).toMatch(sec1Exp); - - return testSignVerify(publicKey, privateKey); - }); - }); -} - -//<#END_FILE: test-crypto-keygen-async-explicit-elliptic-curve.js diff --git a/test/js/node/test/parallel/crypto-keygen-async-named-elliptic-curve.test.js b/test/js/node/test/parallel/crypto-keygen-async-named-elliptic-curve.test.js deleted file mode 100644 index 8721a4551f..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-async-named-elliptic-curve.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-crypto-keygen-async-named-elliptic-curve.js -//#SHA1: 77822175b9b2c2206ec4ab8a3e1182e3576b23bd -//----------------- -"use strict"; - -const crypto = require("crypto"); -const { testSignVerify } = require("../common/crypto"); - -if (!crypto.generateKeyPair) { - test.skip("missing crypto.generateKeyPair"); -} - -const spkiExp = - /^-----BEGIN PUBLIC KEY-----\n(?:[A-Za-z0-9+/=]{64}\n)*[A-Za-z0-9+/=]{1,64}\n-----END PUBLIC KEY-----\n$/; -const sec1Exp = - /^-----BEGIN EC PRIVATE KEY-----\n(?:[A-Za-z0-9+/=]{64}\n)*[A-Za-z0-9+/=]{1,64}\n-----END EC PRIVATE KEY-----\n$/; - -// Test async named elliptic curve key generation, e.g. for ECDSA, -// with a SEC1 private key. -test("async named elliptic curve key generation with SEC1 private key", async () => { - const { publicKey, privateKey } = await new Promise((resolve, reject) => { - crypto.generateKeyPair( - "ec", - { - namedCurve: "prime256v1", - paramEncoding: "named", - publicKeyEncoding: { - type: "spki", - format: "pem", - }, - privateKeyEncoding: { - type: "sec1", - format: "pem", - }, - }, - (err, publicKey, privateKey) => { - if (err) reject(err); - else resolve({ publicKey, privateKey }); - }, - ); - }); - - expect(typeof publicKey).toBe("string"); - expect(publicKey).toMatch(spkiExp); - expect(typeof privateKey).toBe("string"); - expect(privateKey).toMatch(sec1Exp); - - await testSignVerify(publicKey, privateKey); -}); - -//<#END_FILE: test-crypto-keygen-async-named-elliptic-curve.js diff --git a/test/js/node/test/parallel/crypto-keygen-empty-passphrase-no-error.test.js b/test/js/node/test/parallel/crypto-keygen-empty-passphrase-no-error.test.js deleted file mode 100644 index ed029ce08d..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-empty-passphrase-no-error.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-crypto-keygen-empty-passphrase-no-error.js -//#SHA1: a949b38385a5dd05507975cbc44b3beba764bd95 -//----------------- -"use strict"; - -const crypto = require("crypto"); - -// Skip the test if crypto is not available -if (typeof crypto.generateKeyPair !== "function") { - test.skip("missing crypto", () => {}); -} else { - test("generateKeyPair with empty passphrase should not throw ERR_OSSL_CRYPTO_MALLOC_FAILURE", async () => { - // Passing an empty passphrase string should not throw ERR_OSSL_CRYPTO_MALLOC_FAILURE even on OpenSSL 3. - // Regression test for https://github.com/nodejs/node/issues/41428. - await expect( - new Promise((resolve, reject) => { - crypto.generateKeyPair( - "rsa", - { - modulusLength: 1024, - publicKeyEncoding: { - type: "spki", - format: "pem", - }, - privateKeyEncoding: { - type: "pkcs8", - format: "pem", - cipher: "aes-256-cbc", - passphrase: "", - }, - }, - (err, publicKey, privateKey) => { - if (err) reject(err); - else resolve({ publicKey, privateKey }); - }, - ); - }), - ).resolves.toEqual( - expect.objectContaining({ - publicKey: expect.any(String), - privateKey: expect.any(String), - }), - ); - }); -} - -//<#END_FILE: test-crypto-keygen-empty-passphrase-no-error.js diff --git a/test/js/node/test/parallel/crypto-keygen-key-object-without-encoding.test.js b/test/js/node/test/parallel/crypto-keygen-key-object-without-encoding.test.js deleted file mode 100644 index 53d01c929f..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-key-object-without-encoding.test.js +++ /dev/null @@ -1,90 +0,0 @@ -//#FILE: test-crypto-keygen-key-object-without-encoding.js -//#SHA1: da408ed128f913dc7343ef88743b362c16420ba0 -//----------------- -"use strict"; - -const crypto = require("crypto"); - -// Skip the test if crypto is not available -if (!crypto.generateKeyPair) { - test.skip("missing crypto"); -} - -const { generateKeyPair } = crypto; - -// Helper functions for testing encryption/decryption and signing/verifying -const testEncryptDecrypt = (publicKey, privateKey) => { - const plaintext = "Hello, World!"; - const encrypted = crypto.publicEncrypt(publicKey, Buffer.from(plaintext)); - const decrypted = crypto.privateDecrypt(privateKey, encrypted); - expect(decrypted.toString()).toBe(plaintext); -}; - -const testSignVerify = (publicKey, privateKey) => { - const data = "Hello, World!"; - const sign = crypto.createSign("SHA256"); - sign.update(data); - const signature = sign.sign(privateKey); - const verify = crypto.createVerify("SHA256"); - verify.update(data); - expect(verify.verify(publicKey, signature)).toBe(true); -}; - -// Tests key objects are returned when key encodings are not specified. -describe("generateKeyPair without encoding", () => { - // If no publicKeyEncoding is specified, a key object should be returned. - test("returns key object for public key when no encoding specified", done => { - generateKeyPair( - "rsa", - { - modulusLength: 1024, - privateKeyEncoding: { - type: "pkcs1", - format: "pem", - }, - }, - (err, publicKey, privateKey) => { - expect(err).toBe(null); - expect(typeof publicKey).toBe("object"); - expect(publicKey.type).toBe("public"); - expect(publicKey.asymmetricKeyType).toBe("rsa"); - - // The private key should still be a string. - expect(typeof privateKey).toBe("string"); - - testEncryptDecrypt(publicKey, privateKey); - testSignVerify(publicKey, privateKey); - done(); - }, - ); - }); - - // If no privateKeyEncoding is specified, a key object should be returned. - test("returns key object for private key when no encoding specified", done => { - generateKeyPair( - "rsa", - { - modulusLength: 1024, - publicKeyEncoding: { - type: "pkcs1", - format: "pem", - }, - }, - (err, publicKey, privateKey) => { - expect(err).toBe(null); - // The public key should still be a string. - expect(typeof publicKey).toBe("string"); - - expect(typeof privateKey).toBe("object"); - expect(privateKey.type).toBe("private"); - expect(privateKey.asymmetricKeyType).toBe("rsa"); - - testEncryptDecrypt(publicKey, privateKey); - testSignVerify(publicKey, privateKey); - done(); - }, - ); - }); -}); - -//<#END_FILE: test-crypto-keygen-key-object-without-encoding.js diff --git a/test/js/node/test/parallel/crypto-keygen-key-objects.test.js b/test/js/node/test/parallel/crypto-keygen-key-objects.test.js deleted file mode 100644 index 29d4bbe5f8..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-key-objects.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-crypto-keygen-key-objects.js -//#SHA1: 7a2dce611ba70e533ebb73d9f281896bdf75051f -//----------------- -"use strict"; - -if (!process.versions.bun) { - const common = require("../common"); - if (!common.hasCrypto) common.skip("missing crypto"); -} - -const { generateKeyPairSync } = require("crypto"); - -// Test sync key generation with key objects. -test("generateKeyPairSync with RSA", () => { - const { publicKey, privateKey } = generateKeyPairSync("rsa", { - modulusLength: 512, - }); - - expect(typeof publicKey).toBe("object"); - expect(publicKey.type).toBe("public"); - expect(publicKey.asymmetricKeyType).toBe("rsa"); - expect(publicKey.asymmetricKeyDetails).toEqual({ - modulusLength: 512, - publicExponent: 65537n, - }); - - expect(typeof privateKey).toBe("object"); - expect(privateKey.type).toBe("private"); - expect(privateKey.asymmetricKeyType).toBe("rsa"); - expect(privateKey.asymmetricKeyDetails).toEqual({ - modulusLength: 512, - publicExponent: 65537n, - }); -}); - -//<#END_FILE: test-crypto-keygen-key-objects.js diff --git a/test/js/node/test/parallel/crypto-keygen-missing-oid.test.js b/test/js/node/test/parallel/crypto-keygen-missing-oid.test.js deleted file mode 100644 index b91248c3a6..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-missing-oid.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-crypto-keygen-missing-oid.js -//#SHA1: ffcbc53b115cce8795ca1a0e73a533b04789a930 -//----------------- -"use strict"; - -const { generateKeyPair, generateKeyPairSync, getCurves } = require("crypto"); - -// This test creates EC key pairs on curves without associated OIDs. -// Specifying a key encoding should not crash. -test("EC key pairs on curves without associated OIDs", () => { - if (process.versions.openssl >= "1.1.1i") { - const curves = ["Oakley-EC2N-3", "Oakley-EC2N-4"]; - const availableCurves = getCurves(); - - for (const namedCurve of curves) { - if (!availableCurves.includes(namedCurve)) continue; - - const expectedErrorCode = process.versions.openssl.startsWith("3.") - ? "ERR_OSSL_MISSING_OID" - : "ERR_OSSL_EC_MISSING_OID"; - - const params = { - namedCurve, - publicKeyEncoding: { - format: "der", - type: "spki", - }, - }; - - expect(() => { - generateKeyPairSync("ec", params); - }).toThrow( - expect.objectContaining({ - code: expectedErrorCode, - message: expect.any(String), - }), - ); - - return new Promise(resolve => { - generateKeyPair("ec", params, err => { - expect(err).toMatchObject({ - code: expectedErrorCode, - message: expect.any(String), - }); - resolve(); - }); - }); - } - } else { - // Skip test if OpenSSL version is less than 1.1.1i - test.skip("OpenSSL version is less than 1.1.1i"); - } -}); - -//<#END_FILE: test-crypto-keygen-missing-oid.js diff --git a/test/js/node/test/parallel/crypto-keygen-non-standard-public-exponent.test.js b/test/js/node/test/parallel/crypto-keygen-non-standard-public-exponent.test.js deleted file mode 100644 index 46fc35ffdb..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-non-standard-public-exponent.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-crypto-keygen-non-standard-public-exponent.js -//#SHA1: 955e956a08102b75fcf0571213a1dd939d1f51ac -//----------------- -"use strict"; - -const crypto = require("crypto"); - -if (!crypto.generateKeyPairSync) { - test.skip("missing crypto.generateKeyPairSync"); -} - -// Test sync key generation with key objects with a non-standard -// publicExponent -test("generateKeyPairSync with non-standard publicExponent", () => { - const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", { - publicExponent: 3, - modulusLength: 512, - }); - - expect(typeof publicKey).toBe("object"); - expect(publicKey.type).toBe("public"); - expect(publicKey.asymmetricKeyType).toBe("rsa"); - expect(publicKey.asymmetricKeyDetails).toEqual({ - modulusLength: 512, - publicExponent: 3n, - }); - - expect(typeof privateKey).toBe("object"); - expect(privateKey.type).toBe("private"); - expect(privateKey.asymmetricKeyType).toBe("rsa"); - expect(privateKey.asymmetricKeyDetails).toEqual({ - modulusLength: 512, - publicExponent: 3n, - }); -}); - -//<#END_FILE: test-crypto-keygen-non-standard-public-exponent.js diff --git a/test/js/node/test/parallel/crypto-keygen-promisify.test.js b/test/js/node/test/parallel/crypto-keygen-promisify.test.js deleted file mode 100644 index 845d68a970..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-promisify.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-crypto-keygen-promisify.js -//#SHA1: 70eb7089a04950a2ce28a3af9949105eefd420fa -//----------------- -"use strict"; - -const crypto = require("crypto"); -const util = require("util"); - -const { - assertApproximateSize, - testEncryptDecrypt, - testSignVerify, - pkcs1PubExp, - pkcs1PrivExp, -} = require("../common/crypto"); - -// Skip the test if crypto support is not available -if (!crypto.generateKeyPair) { - test.skip("missing crypto support", () => {}); -} else { - // Test the util.promisified API with async RSA key generation. - test("util.promisified generateKeyPair with RSA", async () => { - const generateKeyPairPromise = util.promisify(crypto.generateKeyPair); - const keys = await generateKeyPairPromise("rsa", { - publicExponent: 0x10001, - modulusLength: 512, - publicKeyEncoding: { - type: "pkcs1", - format: "pem", - }, - privateKeyEncoding: { - type: "pkcs1", - format: "pem", - }, - }); - - const { publicKey, privateKey } = keys; - expect(typeof publicKey).toBe("string"); - expect(publicKey).toMatch(pkcs1PubExp); - assertApproximateSize(publicKey, 180); - - expect(typeof privateKey).toBe("string"); - expect(privateKey).toMatch(pkcs1PrivExp); - assertApproximateSize(privateKey, 512); - - testEncryptDecrypt(publicKey, privateKey); - testSignVerify(publicKey, privateKey); - }); -} - -//<#END_FILE: test-crypto-keygen-promisify.js diff --git a/test/js/node/test/parallel/crypto-keygen-sync.test.js b/test/js/node/test/parallel/crypto-keygen-sync.test.js deleted file mode 100644 index 26d3a218fb..0000000000 --- a/test/js/node/test/parallel/crypto-keygen-sync.test.js +++ /dev/null @@ -1,50 +0,0 @@ -//#FILE: test-crypto-keygen-sync.js -//#SHA1: 57749dc903b0d5f9b64a3d61f313be6e5549323f -//----------------- -"use strict"; - -const crypto = require("crypto"); -const { - assertApproximateSize, - testEncryptDecrypt, - testSignVerify, - pkcs1PubExp, - pkcs8Exp, -} = require("../common/crypto"); - -// Skip the test if crypto support is not available -if (typeof crypto.generateKeyPairSync !== "function") { - test.skip("missing crypto support", () => {}); -} else { - // To make the test faster, we will only test sync key generation once and - // with a relatively small key. - test("generateKeyPairSync", () => { - const ret = crypto.generateKeyPairSync("rsa", { - publicExponent: 3, - modulusLength: 512, - publicKeyEncoding: { - type: "pkcs1", - format: "pem", - }, - privateKeyEncoding: { - type: "pkcs8", - format: "pem", - }, - }); - - expect(Object.keys(ret)).toHaveLength(2); - const { publicKey, privateKey } = ret; - - expect(typeof publicKey).toBe("string"); - expect(publicKey).toMatch(pkcs1PubExp); - assertApproximateSize(publicKey, 162); - expect(typeof privateKey).toBe("string"); - expect(privateKey).toMatch(pkcs8Exp); - assertApproximateSize(privateKey, 512); - - testEncryptDecrypt(publicKey, privateKey); - testSignVerify(publicKey, privateKey); - }); -} - -//<#END_FILE: test-crypto-keygen-sync.js diff --git a/test/js/node/test/parallel/crypto-lazy-transform-writable.test.js b/test/js/node/test/parallel/crypto-lazy-transform-writable.test.js deleted file mode 100644 index 6df20ee83f..0000000000 --- a/test/js/node/test/parallel/crypto-lazy-transform-writable.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-crypto-lazy-transform-writable.js -//#SHA1: 29f694c4ea89a94302b3aa84677b5e41c73077d7 -//----------------- -"use strict"; - -const crypto = require("crypto"); -const Stream = require("stream"); - -if (!crypto) it.skip("missing crypto", () => {}); - -test("crypto lazy transform writable", done => { - const hasher1 = crypto.createHash("sha256"); - const hasher2 = crypto.createHash("sha256"); - - // Calculate the expected result. - hasher1.write(Buffer.from("hello world")); - hasher1.end(); - - const expected = hasher1.read().toString("hex"); - - class OldStream extends Stream { - constructor() { - super(); - this.readable = true; - } - } - - const stream = new OldStream(); - - stream.pipe(hasher2).on("finish", () => { - const hash = hasher2.read().toString("hex"); - expect(hash).toBe(expected); - done(); - }); - - stream.emit("data", Buffer.from("hello")); - stream.emit("data", Buffer.from(" world")); - stream.emit("end"); -}); - -//<#END_FILE: test-crypto-lazy-transform-writable.js diff --git a/test/js/node/test/parallel/crypto-padding-aes256.test.js b/test/js/node/test/parallel/crypto-padding-aes256.test.js deleted file mode 100644 index 079e7a15b6..0000000000 --- a/test/js/node/test/parallel/crypto-padding-aes256.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-crypto-padding-aes256.js -//#SHA1: 96fb5beb94bedbc768788ba2726dcd0e61733c5a -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const crypto = require("crypto"); - -const iv = Buffer.from("00000000000000000000000000000000", "hex"); -const key = Buffer.from("0123456789abcdef0123456789abcdef" + "0123456789abcdef0123456789abcdef", "hex"); - -function encrypt(val, pad) { - const c = crypto.createCipheriv("aes256", key, iv); - c.setAutoPadding(pad); - return c.update(val, "utf8", "latin1") + c.final("latin1"); -} - -function decrypt(val, pad) { - const c = crypto.createDecipheriv("aes256", key, iv); - c.setAutoPadding(pad); - return c.update(val, "latin1", "utf8") + c.final("utf8"); -} - -test("AES256 encryption and decryption with no padding (multiple of block size)", () => { - // echo 0123456789abcdef0123456789abcdef \ - // | openssl enc -e -aes256 -nopad -K -iv \ - // | openssl enc -d -aes256 -nopad -K -iv - const plaintext = "0123456789abcdef0123456789abcdef"; // Multiple of block size - const encrypted = encrypt(plaintext, false); - const decrypted = decrypt(encrypted, false); - expect(decrypted).toBe(plaintext); -}); - -test("AES256 encryption and decryption with padding (not a multiple of block size)", () => { - // echo 0123456789abcdef0123456789abcde \ - // | openssl enc -e -aes256 -K -iv \ - // | openssl enc -d -aes256 -K -iv - const plaintext = "0123456789abcdef0123456789abcde"; // not a multiple - const encrypted = encrypt(plaintext, true); - const decrypted = decrypt(encrypted, true); - expect(decrypted).toBe(plaintext); -}); - -//<#END_FILE: test-crypto-padding-aes256.js diff --git a/test/js/node/test/parallel/crypto-randomfillsync-regression.test.js b/test/js/node/test/parallel/crypto-randomfillsync-regression.test.js deleted file mode 100644 index aa57030f2a..0000000000 --- a/test/js/node/test/parallel/crypto-randomfillsync-regression.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-crypto-randomfillsync-regression.js -//#SHA1: f37bc7cc1eab82ab93665b246c0d44e9fce8d112 -//----------------- -"use strict"; - -// Skip the test if crypto is not available -let randomFillSync; -try { - ({ randomFillSync } = require("crypto")); -} catch { - test.skip("missing crypto", () => {}); -} - -if (randomFillSync) { - test("randomFillSync regression test", () => { - const ab = new ArrayBuffer(20); - const buf = Buffer.from(ab, 10); - - const before = buf.toString("hex"); - - randomFillSync(buf); - - const after = buf.toString("hex"); - - expect(before).not.toBe(after); - }); -} - -//<#END_FILE: test-crypto-randomfillsync-regression.js diff --git a/test/js/node/test/parallel/crypto-subtle-zero-length.test.js b/test/js/node/test/parallel/crypto-subtle-zero-length.test.js deleted file mode 100644 index 9113969480..0000000000 --- a/test/js/node/test/parallel/crypto-subtle-zero-length.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-crypto-subtle-zero-length.js -//#SHA1: aa21bbc5fd9db7bc09dad3ec61cd743d655f5e3b -//----------------- -"use strict"; - -// Skip test if crypto is not available -if (typeof crypto === "undefined" || !crypto.subtle) { - test.skip("missing crypto"); -} - -test("SubtleCrypto with zero-length input", async () => { - const { subtle } = globalThis.crypto; - - const k = await subtle.importKey("raw", new Uint8Array(32), { name: "AES-GCM" }, false, ["encrypt", "decrypt"]); - expect(k).toBeInstanceOf(CryptoKey); - - const e = await subtle.encrypt( - { - name: "AES-GCM", - iv: new Uint8Array(12), - }, - k, - new Uint8Array(0), - ); - expect(e).toBeInstanceOf(ArrayBuffer); - expect(Buffer.from(e)).toEqual( - Buffer.from([0x53, 0x0f, 0x8a, 0xfb, 0xc7, 0x45, 0x36, 0xb9, 0xa9, 0x63, 0xb4, 0xf1, 0xc4, 0xcb, 0x73, 0x8b]), - ); - - const v = await subtle.decrypt( - { - name: "AES-GCM", - iv: new Uint8Array(12), - }, - k, - e, - ); - expect(v).toBeInstanceOf(ArrayBuffer); - expect(v.byteLength).toBe(0); -}); - -//<#END_FILE: test-crypto-subtle-zero-length.js diff --git a/test/js/node/test/parallel/crypto-update-encoding.test.js b/test/js/node/test/parallel/crypto-update-encoding.test.js deleted file mode 100644 index 1c0d269234..0000000000 --- a/test/js/node/test/parallel/crypto-update-encoding.test.js +++ /dev/null @@ -1,33 +0,0 @@ -//#FILE: test-crypto-update-encoding.js -//#SHA1: dfe3c7e71e22a772cf6b2e6a6540be161fda3418 -//----------------- -"use strict"; - -const crypto = require("crypto"); - -const zeros = Buffer.alloc; -const key = zeros(16); -const iv = zeros(16); - -const cipher = () => crypto.createCipheriv("aes-128-cbc", key, iv); -const decipher = () => crypto.createDecipheriv("aes-128-cbc", key, iv); -const hash = () => crypto.createSign("sha256"); -const hmac = () => crypto.createHmac("sha256", key); -const sign = () => crypto.createSign("sha256"); -const verify = () => crypto.createVerify("sha256"); - -test("crypto update ignores inputEncoding for Buffer input", () => { - const functions = [cipher, decipher, hash, hmac, sign, verify]; - const sizes = [15, 16]; - - functions.forEach(f => { - sizes.forEach(n => { - const instance = f(); - expect(() => { - instance.update(zeros(n), "hex"); - }).not.toThrow(); - }); - }); -}); - -//<#END_FILE: test-crypto-update-encoding.js diff --git a/test/js/node/test/parallel/crypto-webcrypto-aes-decrypt-tag-too-small.test.js b/test/js/node/test/parallel/crypto-webcrypto-aes-decrypt-tag-too-small.test.js deleted file mode 100644 index de241c13af..0000000000 --- a/test/js/node/test/parallel/crypto-webcrypto-aes-decrypt-tag-too-small.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-crypto-webcrypto-aes-decrypt-tag-too-small.js -//#SHA1: e58d2e4e7dcfc3a29a6e9acbe177f32a1d6bf280 -//----------------- -"use strict"; - -if (!globalThis.crypto?.subtle) { - test.skip("missing crypto"); -} - -test("AES-GCM decrypt with tag too small", async () => { - const { subtle } = globalThis.crypto; - - const key = await subtle.importKey( - "raw", - new Uint8Array(32), - { - name: "AES-GCM", - }, - false, - ["encrypt", "decrypt"], - ); - - await expect( - subtle.decrypt( - { - name: "AES-GCM", - iv: new Uint8Array(12), - }, - key, - new Uint8Array(0), - ), - ).rejects.toThrow( - expect.objectContaining({ - name: "OperationError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-crypto-webcrypto-aes-decrypt-tag-too-small.js diff --git a/test/js/node/test/parallel/dgram-abort-closed.test.js b/test/js/node/test/parallel/dgram-abort-closed.test.js deleted file mode 100644 index eb09ac67eb..0000000000 --- a/test/js/node/test/parallel/dgram-abort-closed.test.js +++ /dev/null @@ -1,20 +0,0 @@ -//#FILE: test-dgram-abort-closed.js -//#SHA1: 8d3ab4d13dda99cdccb6994f165f2ddacf58360c -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("AbortController with closed dgram socket", () => { - const controller = new AbortController(); - const socket = dgram.createSocket({ type: "udp4", signal: controller.signal }); - - socket.close(); - - // This should not throw or cause any issues - expect(() => { - controller.abort(); - }).not.toThrow(); -}); - -//<#END_FILE: test-dgram-abort-closed.js diff --git a/test/js/node/test/parallel/dgram-bind-default-address.test.js b/test/js/node/test/parallel/dgram-bind-default-address.test.js deleted file mode 100644 index 542f335c6e..0000000000 --- a/test/js/node/test/parallel/dgram-bind-default-address.test.js +++ /dev/null @@ -1,82 +0,0 @@ -//#FILE: test-dgram-bind-default-address.js -//#SHA1: f29269b15b1205e37cc43e02b76cc5d8eb3b70be -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const dgram = require("dgram"); - -// Skip test in FreeBSD jails since 0.0.0.0 will resolve to default interface -const inFreeBSDJail = process.platform === "freebsd" && process.env.CI === "true"; -if (inFreeBSDJail) { - test.skip("In a FreeBSD jail"); -} - -test("UDP4 socket bind to default address", async () => { - const socket = dgram.createSocket("udp4"); - - await new Promise(resolve => { - socket.bind(0, () => { - const address = socket.address(); - expect(typeof address.port).toBe("number"); - expect(isFinite(address.port)).toBe(true); - expect(address.port).toBeGreaterThan(0); - expect(address.address).toBe("0.0.0.0"); - socket.close(); - resolve(); - }); - }); -}); - -const hasIPv6 = (() => { - try { - const socket = dgram.createSocket("udp6"); - socket.close(); - return true; - } catch { - return false; - } -})(); - -if (!hasIPv6) { - test.skip("udp6 part of test, because no IPv6 support"); -} else { - test("UDP6 socket bind to default address", async () => { - const socket = dgram.createSocket("udp6"); - - await new Promise(resolve => { - socket.bind(0, () => { - const address = socket.address(); - expect(typeof address.port).toBe("number"); - expect(isFinite(address.port)).toBe(true); - expect(address.port).toBeGreaterThan(0); - let addressValue = address.address; - if (addressValue === "::ffff:0.0.0.0") addressValue = "::"; - expect(addressValue).toBe("::"); - socket.close(); - resolve(); - }); - }); - }); -} - -//<#END_FILE: test-dgram-bind-default-address.js diff --git a/test/js/node/test/parallel/dgram-bind.test.js b/test/js/node/test/parallel/dgram-bind.test.js deleted file mode 100644 index 0bf412718a..0000000000 --- a/test/js/node/test/parallel/dgram-bind.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-dgram-bind.js -//#SHA1: 748fcd0fcb3ed5103b9072bba3019e560fb2799b -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const dgram = require("dgram"); - -test("dgram socket bind", async () => { - const socket = dgram.createSocket("udp4"); - - await new Promise(resolve => { - socket.on("listening", () => { - expect(() => { - socket.bind(); - }).toThrow( - expect.objectContaining({ - code: "ERR_SOCKET_ALREADY_BOUND", - name: "Error", - message: expect.stringMatching(/^Socket is already bound$/), - }), - ); - - socket.close(); - resolve(); - }); - - const result = socket.bind(); // Should not throw. - - expect(result).toBe(socket); // Should have returned itself. - }); -}); - -//<#END_FILE: test-dgram-bind.js diff --git a/test/js/node/test/parallel/dgram-bytes-length.test.js b/test/js/node/test/parallel/dgram-bytes-length.test.js deleted file mode 100644 index 1c1bd1e219..0000000000 --- a/test/js/node/test/parallel/dgram-bytes-length.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-dgram-bytes-length.js -//#SHA1: f899cc14c13e8c913645e204819cf99b867aec5c -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const dgram = require("dgram"); - -test("dgram bytes length", async () => { - const message = Buffer.from("Some bytes"); - const client = dgram.createSocket("udp4"); - - await new Promise((resolve, reject) => { - client.send(message, 0, message.length, 41234, "localhost", function (err, bytes) { - if (err) reject(err); - expect(bytes).toBe(message.length); - client.close(); - resolve(); - }); - }); -}); - -//<#END_FILE: test-dgram-bytes-length.js diff --git a/test/js/node/test/parallel/dgram-close-in-listening.test.js b/test/js/node/test/parallel/dgram-close-in-listening.test.js deleted file mode 100644 index e669bea76c..0000000000 --- a/test/js/node/test/parallel/dgram-close-in-listening.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-dgram-close-in-listening.js -//#SHA1: b37e742b092d70824b67c4ad4d3e1bb17a8c5cd5 -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram socket closed before sendQueue is drained does not crash", done => { - const buf = Buffer.alloc(1024, 42); - - const socket = dgram.createSocket("udp4"); - - socket.on("listening", function () { - socket.close(); - }); - - // Get a random port for send - const portGetter = dgram.createSocket("udp4").bind(0, "localhost", () => { - // Adds a listener to 'listening' to send the data when - // the socket is available - socket.send(buf, 0, buf.length, portGetter.address().port, portGetter.address().address); - - portGetter.close(); - done(); // Signal test completion - }); -}); - -//<#END_FILE: test-dgram-close-in-listening.js diff --git a/test/js/node/test/parallel/dgram-close.test.js b/test/js/node/test/parallel/dgram-close.test.js deleted file mode 100644 index fe89cc0f66..0000000000 --- a/test/js/node/test/parallel/dgram-close.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-dgram-close.js -//#SHA1: c396ba7a9c9ef45206989b36e4b5db0b95503e38 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// Flags: --expose-internals -"use strict"; -// Ensure that if a dgram socket is closed before the DNS lookup completes, it -// won't crash. - -const dgram = require("dgram"); - -const buf = Buffer.alloc(1024, 42); - -test("dgram socket close before DNS lookup completes", done => { - let socket = dgram.createSocket("udp4"); - - // Get a random port for send - const portGetter = dgram.createSocket("udp4"); - - portGetter.bind(0, "localhost", () => { - socket.send(buf, 0, buf.length, portGetter.address().port, portGetter.address().address); - - expect(socket.close()).toBe(socket); - - socket.on("close", () => { - socket = null; - - // Verify that accessing handle after closure doesn't throw - setImmediate(() => { - setImmediate(() => { - // We can't access internal symbols, so we'll just check if this doesn't throw - expect(() => { - console.log("Handle fd is: ", "placeholder"); - }).not.toThrow(); - - portGetter.close(); - done(); - }); - }); - }); - }); -}); - -//<#END_FILE: test-dgram-close.js diff --git a/test/js/node/test/parallel/dgram-cluster-close-in-listening.test.js b/test/js/node/test/parallel/dgram-cluster-close-in-listening.test.js deleted file mode 100644 index 3a882a49d2..0000000000 --- a/test/js/node/test/parallel/dgram-cluster-close-in-listening.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-dgram-cluster-close-in-listening.js -//#SHA1: f288642fce76ef0138f8e44cd8eb09ded9dc4640 -//----------------- -"use strict"; -// Ensure that closing dgram sockets in 'listening' callbacks of cluster workers -// won't throw errors. - -const dgram = require("dgram"); -const cluster = require("cluster"); - -if (process.platform === "win32") { - it.skip("dgram clustering is currently not supported on windows.", () => {}); -} else { - if (cluster.isPrimary) { - test("Primary cluster forks workers", () => { - for (let i = 0; i < 3; i += 1) { - expect(() => cluster.fork()).not.toThrow(); - } - }); - } else { - test("Worker handles dgram socket lifecycle", done => { - const socket = dgram.createSocket("udp4"); - - socket.on("error", () => { - done(new Error("Error event should not be called")); - }); - - socket.on("listening", () => { - socket.close(); - }); - - socket.on("close", () => { - cluster.worker.disconnect(); - done(); - }); - - socket.bind(0); - }); - } -} - -//<#END_FILE: test-dgram-cluster-close-in-listening.js diff --git a/test/js/node/test/parallel/dgram-connect-send-callback-multi-buffer.test.js b/test/js/node/test/parallel/dgram-connect-send-callback-multi-buffer.test.js deleted file mode 100644 index 2d014e97c6..0000000000 --- a/test/js/node/test/parallel/dgram-connect-send-callback-multi-buffer.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-dgram-connect-send-callback-multi-buffer.js -//#SHA1: f30fbed996bbcd2adb268e9e3412a5f83119f8ae -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram connect send callback multi buffer", done => { - const client = dgram.createSocket("udp4"); - - const messageSent = jest.fn((err, bytes) => { - expect(bytes).toBe(buf1.length + buf2.length); - }); - - const buf1 = Buffer.alloc(256, "x"); - const buf2 = Buffer.alloc(256, "y"); - - client.on("listening", () => { - const port = client.address().port; - client.connect(port, () => { - client.send([buf1, buf2], messageSent); - }); - }); - - client.on("message", (buf, info) => { - const expected = Buffer.concat([buf1, buf2]); - expect(buf.equals(expected)).toBe(true); - client.close(); - expect(messageSent).toHaveBeenCalledTimes(1); - done(); - }); - - client.bind(0); -}); - -//<#END_FILE: test-dgram-connect-send-callback-multi-buffer.js diff --git a/test/js/node/test/parallel/dgram-connect-send-default-host.test.js b/test/js/node/test/parallel/dgram-connect-send-default-host.test.js deleted file mode 100644 index 6e597bae7c..0000000000 --- a/test/js/node/test/parallel/dgram-connect-send-default-host.test.js +++ /dev/null @@ -1,69 +0,0 @@ -//#FILE: test-dgram-connect-send-default-host.js -//#SHA1: 78d734d664f2bf2f6376846bba7c909d8253c4dc -//----------------- -"use strict"; - -const dgram = require("dgram"); - -const toSend = [Buffer.alloc(256, "x"), Buffer.alloc(256, "y"), Buffer.alloc(256, "z"), "hello"]; - -const received = []; - -test("dgram connect and send with default host", async () => { - const client = dgram.createSocket("udp4"); - const server = dgram.createSocket("udp4"); - - const serverListening = new Promise(resolve => { - server.on("listening", resolve); - }); - - server.on("message", (buf, info) => { - received.push(buf.toString()); - - if (received.length === toSend.length * 2) { - // The replies may arrive out of order -> sort them before checking. - received.sort(); - - const expected = toSend.concat(toSend).map(String).sort(); - expect(received).toEqual(expected); - client.close(); - server.close(); - } - }); - - server.bind(0); - - await serverListening; - - const port = server.address().port; - await new Promise((resolve, reject) => { - client.connect(port, err => { - if (err) reject(err); - else resolve(); - }); - }); - - client.send(toSend[0], 0, toSend[0].length); - client.send(toSend[1]); - client.send([toSend[2]]); - client.send(toSend[3], 0, toSend[3].length); - - client.send(new Uint8Array(toSend[0]), 0, toSend[0].length); - client.send(new Uint8Array(toSend[1])); - client.send([new Uint8Array(toSend[2])]); - client.send(new Uint8Array(Buffer.from(toSend[3])), 0, toSend[3].length); - - // Wait for all messages to be received - await new Promise(resolve => { - const checkInterval = setInterval(() => { - if (received.length === toSend.length * 2) { - clearInterval(checkInterval); - resolve(); - } - }, 100); - }); - - expect(received.length).toBe(toSend.length * 2); -}); - -//<#END_FILE: test-dgram-connect-send-default-host.js diff --git a/test/js/node/test/parallel/dgram-connect-send-empty-array.test.js b/test/js/node/test/parallel/dgram-connect-send-empty-array.test.js deleted file mode 100644 index 32b665f932..0000000000 --- a/test/js/node/test/parallel/dgram-connect-send-empty-array.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-dgram-connect-send-empty-array.js -//#SHA1: 81de5b211c0e3be3158d2c06178577f39e62f0d1 -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram.connect() and send empty array", () => { - const client = dgram.createSocket("udp4"); - - expect.assertions(1); - - return new Promise(resolve => { - client.on("message", (buf, info) => { - const expected = Buffer.alloc(0); - expect(buf).toEqual(expected); - client.close(); - resolve(); - }); - - client.on("listening", () => { - client.connect(client.address().port, "127.0.0.1", () => client.send([])); - }); - - client.bind(0); - }); -}); - -//<#END_FILE: test-dgram-connect-send-empty-array.js diff --git a/test/js/node/test/parallel/dgram-connect-send-empty-buffer.test.js b/test/js/node/test/parallel/dgram-connect-send-empty-buffer.test.js deleted file mode 100644 index 7c4209dbf1..0000000000 --- a/test/js/node/test/parallel/dgram-connect-send-empty-buffer.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-dgram-connect-send-empty-buffer.js -//#SHA1: 08e8b667af8e6f97e6df2c95360a3a3aec05d435 -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram connect and send empty buffer", done => { - const client = dgram.createSocket("udp4"); - - client.bind(0, () => { - const port = client.address().port; - client.connect(port, () => { - const buf = Buffer.alloc(0); - client.send(buf, 0, 0, err => { - expect(err).toBeNull(); - }); - }); - - client.on("message", buffer => { - expect(buffer.length).toBe(0); - client.close(); - done(); - }); - }); -}); - -//<#END_FILE: test-dgram-connect-send-empty-buffer.js diff --git a/test/js/node/test/parallel/dgram-connect-send-empty-packet.test.js b/test/js/node/test/parallel/dgram-connect-send-empty-packet.test.js deleted file mode 100644 index d5f56ddb5d..0000000000 --- a/test/js/node/test/parallel/dgram-connect-send-empty-packet.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-dgram-connect-send-empty-packet.js -//#SHA1: 107d20a1e7a2628097091471ffdad75fc714b1fb -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram connect and send empty packet", done => { - const client = dgram.createSocket("udp4"); - - client.bind(0, () => { - expect.hasAssertions(); - client.connect(client.address().port, () => { - client.on("message", callback); - const buf = Buffer.alloc(1); - - const interval = setInterval(() => { - client.send(buf, 0, 0, callback); - }, 10); - - function callback(firstArg) { - // If client.send() callback, firstArg should be null. - // If client.on('message') listener, firstArg should be a 0-length buffer. - if (firstArg instanceof Buffer) { - expect(firstArg.length).toBe(0); - clearInterval(interval); - client.close(); - done(); - } - } - }); - }); -}); - -//<#END_FILE: test-dgram-connect-send-empty-packet.js diff --git a/test/js/node/test/parallel/dgram-connect-send-multi-string-array.test.js b/test/js/node/test/parallel/dgram-connect-send-multi-string-array.test.js deleted file mode 100644 index 7ae5672c0f..0000000000 --- a/test/js/node/test/parallel/dgram-connect-send-multi-string-array.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-dgram-connect-send-multi-string-array.js -//#SHA1: 611c15bc8089ffcae85adaa91bff5031c776a8ab -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram.createSocket can send multi-string array", done => { - const socket = dgram.createSocket("udp4"); - const data = ["foo", "bar", "baz"]; - - socket.on("message", (msg, rinfo) => { - socket.close(); - expect(msg.toString()).toBe(data.join("")); - done(); - }); - - socket.bind(0, () => { - socket.connect(socket.address().port, () => { - socket.send(data); - }); - }); -}); - -//<#END_FILE: test-dgram-connect-send-multi-string-array.js diff --git a/test/js/node/test/parallel/dgram-implicit-bind.test.js b/test/js/node/test/parallel/dgram-implicit-bind.test.js deleted file mode 100644 index d733e2109f..0000000000 --- a/test/js/node/test/parallel/dgram-implicit-bind.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-dgram-implicit-bind.js -//#SHA1: 3b390facaac3ee5e617c9fdc11acdb9f019fabfa -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const dgram = require("dgram"); - -test("dgram implicit bind", async () => { - const source = dgram.createSocket("udp4"); - const target = dgram.createSocket("udp4"); - let messages = 0; - - const messageHandler = jest.fn(buf => { - if (buf.toString() === "abc") ++messages; - if (buf.toString() === "def") ++messages; - if (messages === 2) { - source.close(); - target.close(); - } - }); - - target.on("message", messageHandler); - - await new Promise(resolve => { - target.on("listening", resolve); - target.bind(0); - }); - - // Second .send() call should not throw a bind error. - const port = target.address().port; - source.send(Buffer.from("abc"), 0, 3, port, "127.0.0.1"); - source.send(Buffer.from("def"), 0, 3, port, "127.0.0.1"); - - // Wait for the messages to be processed - await new Promise(resolve => setTimeout(resolve, 100)); - - expect(messageHandler).toHaveBeenCalledTimes(2); - expect(messages).toBe(2); -}); - -//<#END_FILE: test-dgram-implicit-bind.js diff --git a/test/js/node/test/parallel/dgram-listen-after-bind.test.js b/test/js/node/test/parallel/dgram-listen-after-bind.test.js deleted file mode 100644 index fd8faa3fe8..0000000000 --- a/test/js/node/test/parallel/dgram-listen-after-bind.test.js +++ /dev/null @@ -1,50 +0,0 @@ -//#FILE: test-dgram-listen-after-bind.js -//#SHA1: c1a91f2b83b502dd1abc4b46f023df6677fdf465 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const dgram = require("dgram"); - -test("dgram listen after bind", done => { - const socket = dgram.createSocket("udp4"); - - socket.bind(); - - let fired = false; - const timer = setTimeout(() => { - socket.close(); - }, 100); - - socket.on("listening", () => { - clearTimeout(timer); - fired = true; - socket.close(); - }); - - socket.on("close", () => { - expect(fired).toBe(true); - done(); - }); -}); - -//<#END_FILE: test-dgram-listen-after-bind.js diff --git a/test/js/node/test/parallel/dgram-oob-buffer.test.js b/test/js/node/test/parallel/dgram-oob-buffer.test.js deleted file mode 100644 index 4d1eae8968..0000000000 --- a/test/js/node/test/parallel/dgram-oob-buffer.test.js +++ /dev/null @@ -1,63 +0,0 @@ -//#FILE: test-dgram-oob-buffer.js -//#SHA1: a851da9a2178e92ce8315294d7cebf6eb78eb4bd -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Some operating systems report errors when an UDP message is sent to an -// unreachable host. This error can be reported by sendto() and even by -// recvfrom(). Node should not propagate this error to the user. - -const dgram = require("dgram"); - -test("UDP message sent to unreachable host should not propagate error", async () => { - const socket = dgram.createSocket("udp4"); - const buf = Buffer.from([1, 2, 3, 4]); - - const portGetter = dgram.createSocket("udp4"); - - await new Promise(resolve => { - portGetter.bind(0, "localhost", () => { - const { address, port } = portGetter.address(); - - portGetter.close(() => { - const sendCallback = jest.fn(); - - socket.send(buf, 0, 0, port, address, sendCallback); - socket.send(buf, 0, 4, port, address, sendCallback); - socket.send(buf, 1, 3, port, address, sendCallback); - socket.send(buf, 3, 1, port, address, sendCallback); - // Since length of zero means nothing, don't error despite OOB. - socket.send(buf, 4, 0, port, address, sendCallback); - - socket.close(); - - // We expect the sendCallback to not be called - expect(sendCallback).not.toHaveBeenCalled(); - - resolve(); - }); - }); - }); -}); - -//<#END_FILE: test-dgram-oob-buffer.js diff --git a/test/js/node/test/parallel/dgram-ref.test.js b/test/js/node/test/parallel/dgram-ref.test.js deleted file mode 100644 index bbb6602414..0000000000 --- a/test/js/node/test/parallel/dgram-ref.test.js +++ /dev/null @@ -1,44 +0,0 @@ -//#FILE: test-dgram-ref.js -//#SHA1: b1a50859a1784815d575d8203f7da20fe8d07e50 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const dgram = require("dgram"); - -test("Should not hang when creating UDP sockets", () => { - // Should not hang, see https://github.com/nodejs/node-v0.x-archive/issues/1282 - expect(() => dgram.createSocket("udp4")).not.toThrow(); - expect(() => dgram.createSocket("udp6")).not.toThrow(); -}); - -test("Test ref() on a closed socket", done => { - // Test the case of ref()'ing a socket with no handle. - const s = dgram.createSocket("udp4"); - - s.close(() => { - expect(() => s.ref()).not.toThrow(); - done(); - }); -}); - -//<#END_FILE: test-dgram-ref.js diff --git a/test/js/node/test/parallel/dgram-send-callback-buffer-empty-address.test.js b/test/js/node/test/parallel/dgram-send-callback-buffer-empty-address.test.js deleted file mode 100644 index de86cfc036..0000000000 --- a/test/js/node/test/parallel/dgram-send-callback-buffer-empty-address.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-dgram-send-callback-buffer-empty-address.js -//#SHA1: 5c76ad150693dcec8921099fa994f61aa783713c -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram send callback with buffer and empty address", done => { - const client = dgram.createSocket("udp4"); - - const buf = Buffer.alloc(256, "x"); - - const onMessage = jest.fn(bytes => { - expect(bytes).toBe(buf.length); - client.close(); - done(); - }); - - client.bind(0, () => { - client.send(buf, client.address().port, error => { - expect(error).toBeFalsy(); - onMessage(buf.length); - }); - }); -}); - -//<#END_FILE: test-dgram-send-callback-buffer-empty-address.js diff --git a/test/js/node/test/parallel/dgram-send-callback-multi-buffer-empty-address.test.js b/test/js/node/test/parallel/dgram-send-callback-multi-buffer-empty-address.test.js deleted file mode 100644 index 429e4cd9eb..0000000000 --- a/test/js/node/test/parallel/dgram-send-callback-multi-buffer-empty-address.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-dgram-send-callback-multi-buffer-empty-address.js -//#SHA1: 61d00ee31b25f144989e0d3ced884a70f4e7d07a -//----------------- -"use strict"; - -const dgram = require("dgram"); - -let client; - -beforeEach(() => { - client = dgram.createSocket("udp4"); -}); - -afterEach(() => { - client.close(); -}); - -test("send callback multi buffer empty address", done => { - const buf1 = Buffer.alloc(256, "x"); - const buf2 = Buffer.alloc(256, "y"); - - client.on("listening", function () { - const port = this.address().port; - client.send([buf1, buf2], port, (err, bytes) => { - expect(err).toBeNull(); - expect(bytes).toBe(buf1.length + buf2.length); - }); - }); - - client.on("message", buf => { - const expected = Buffer.concat([buf1, buf2]); - expect(buf.equals(expected)).toBe(true); - done(); - }); - - client.bind(0); -}); - -//<#END_FILE: test-dgram-send-callback-multi-buffer-empty-address.js diff --git a/test/js/node/test/parallel/dgram-send-callback-multi-buffer.test.js b/test/js/node/test/parallel/dgram-send-callback-multi-buffer.test.js deleted file mode 100644 index 36cf0d1eaa..0000000000 --- a/test/js/node/test/parallel/dgram-send-callback-multi-buffer.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-dgram-send-callback-multi-buffer.js -//#SHA1: 622d513f7897c216601b50a2960a8a36259b2595 -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram send callback with multiple buffers", done => { - const client = dgram.createSocket("udp4"); - - const messageSent = jest.fn((err, bytes) => { - expect(err).toBeNull(); - expect(bytes).toBe(buf1.length + buf2.length); - }); - - const buf1 = Buffer.alloc(256, "x"); - const buf2 = Buffer.alloc(256, "y"); - - client.on("listening", () => { - const port = client.address().port; - client.send([buf1, buf2], port, "localhost", messageSent); - }); - - client.on("message", (buf, info) => { - const expected = Buffer.concat([buf1, buf2]); - expect(buf.equals(expected)).toBe(true); - expect(messageSent).toHaveBeenCalledTimes(1); - client.close(); - done(); - }); - - client.bind(0); -}); - -//<#END_FILE: test-dgram-send-callback-multi-buffer.js diff --git a/test/js/node/test/parallel/dgram-send-callback-recursive.test.js b/test/js/node/test/parallel/dgram-send-callback-recursive.test.js deleted file mode 100644 index e42d990c9f..0000000000 --- a/test/js/node/test/parallel/dgram-send-callback-recursive.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-dgram-send-callback-recursive.js -//#SHA1: fac7c8b29bd2122d4de273c54128b5a6100ad437 -//----------------- -"use strict"; - -const dgram = require("dgram"); - -let received = 0; -let sent = 0; -const limit = 10; -let async = false; -let port; -const chunk = "abc"; - -test("dgram send callback recursive", done => { - const client = dgram.createSocket("udp4"); - - function onsend() { - if (sent++ < limit) { - client.send(chunk, 0, chunk.length, port, "127.0.0.1", onsend); - } else { - expect(async).toBe(true); - } - } - - client.on("listening", function () { - port = this.address().port; - - process.nextTick(() => { - async = true; - }); - - onsend(); - }); - - client.on("message", (buf, info) => { - received++; - if (received === limit) { - client.close(); - } - }); - - client.on("close", () => { - expect(received).toBe(limit); - done(); - }); - - client.bind(0); -}); - -//<#END_FILE: test-dgram-send-callback-recursive.js diff --git a/test/js/node/test/parallel/dgram-send-cb-quelches-error.test.js b/test/js/node/test/parallel/dgram-send-cb-quelches-error.test.js deleted file mode 100644 index 96364d73fd..0000000000 --- a/test/js/node/test/parallel/dgram-send-cb-quelches-error.test.js +++ /dev/null @@ -1,44 +0,0 @@ -//#FILE: test-dgram-send-cb-quelches-error.js -//#SHA1: 7525b0a8af0df192c36a848b23332424245d2937 -//----------------- -"use strict"; - -const assert = require("assert"); -const dgram = require("dgram"); -const dns = require("dns"); - -test("dgram send callback quelches error", () => { - const socket = dgram.createSocket("udp4"); - const buffer = Buffer.from("gary busey"); - - dns.setServers([]); - - const onEvent = jest.fn(() => { - throw new Error("Error should not be emitted if there is callback"); - }); - - socket.once("error", onEvent); - - // assert that: - // * callbacks act as "error" listeners if given. - // * error is never emitter for missing dns entries - // if a callback that handles error is present - // * error is emitted if a callback with no argument is passed - socket.send(buffer, 0, buffer.length, 100, "dne.example.com", callbackOnly); - - function callbackOnly(err) { - expect(err).toBeTruthy(); - socket.removeListener("error", onEvent); - socket.on("error", onError); - socket.send(buffer, 0, buffer.length, 100, "dne.invalid"); - } - - function onError(err) { - expect(err).toBeTruthy(); - socket.close(); - } - - expect(onEvent).not.toHaveBeenCalled(); -}); - -//<#END_FILE: test-dgram-send-cb-quelches-error.js diff --git a/test/js/node/test/parallel/dgram-send-empty-buffer.test.js b/test/js/node/test/parallel/dgram-send-empty-buffer.test.js deleted file mode 100644 index 3dcd6ffe3b..0000000000 --- a/test/js/node/test/parallel/dgram-send-empty-buffer.test.js +++ /dev/null @@ -1,50 +0,0 @@ -//#FILE: test-dgram-send-empty-buffer.js -//#SHA1: ac60fc545252e681b648a7038d1bebe46ffbbac0 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const dgram = require("dgram"); - -test("dgram send empty buffer", done => { - const client = dgram.createSocket("udp4"); - - client.bind(0, () => { - const port = client.address().port; - - client.on("message", buffer => { - expect(buffer.length).toBe(0); - clearInterval(interval); - client.close(); - done(); - }); - - const buf = Buffer.alloc(0); - const interval = setInterval(() => { - client.send(buf, 0, 0, port, "127.0.0.1", () => { - // This callback is expected to be called - }); - }, 10); - }); -}); - -//<#END_FILE: test-dgram-send-empty-buffer.js diff --git a/test/js/node/test/parallel/dgram-send-empty-packet.test.js b/test/js/node/test/parallel/dgram-send-empty-packet.test.js deleted file mode 100644 index 07802512c7..0000000000 --- a/test/js/node/test/parallel/dgram-send-empty-packet.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-dgram-send-empty-packet.js -//#SHA1: f39fb8a7245893f0f6f55aeb110d458e2f265013 -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("send empty packet", done => { - const client = dgram.createSocket("udp4"); - - client.bind(0, () => { - client.on("message", jest.fn(callback)); - - const port = client.address().port; - const buf = Buffer.alloc(1); - - const interval = setInterval(() => { - client.send(buf, 0, 0, port, "127.0.0.1", jest.fn(callback)); - }, 10); - - function callback(firstArg) { - // If client.send() callback, firstArg should be null. - // If client.on('message') listener, firstArg should be a 0-length buffer. - if (firstArg instanceof Buffer) { - expect(firstArg.length).toBe(0); - clearInterval(interval); - client.close(); - done(); - } - } - }); -}); - -//<#END_FILE: test-dgram-send-empty-packet.js diff --git a/test/js/node/test/parallel/dgram-send-multi-buffer-copy.test.js b/test/js/node/test/parallel/dgram-send-multi-buffer-copy.test.js deleted file mode 100644 index b9baba6443..0000000000 --- a/test/js/node/test/parallel/dgram-send-multi-buffer-copy.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-dgram-send-multi-buffer-copy.js -//#SHA1: 6adf8291a5dd40cb6a71ad3779f0d26d2150249a -//----------------- -"use strict"; - -const dgram = require("dgram"); - -let client; - -beforeEach(() => { - client = dgram.createSocket("udp4"); -}); - -afterEach(() => { - client.close(); -}); - -test("dgram send multi buffer copy", done => { - const onMessage = jest.fn((err, bytes) => { - expect(bytes).toBe(buf1.length + buf2.length); - }); - - const buf1 = Buffer.alloc(256, "x"); - const buf2 = Buffer.alloc(256, "y"); - - client.on("listening", function () { - const toSend = [buf1, buf2]; - client.send(toSend, this.address().port, "127.0.0.1", onMessage); - toSend.splice(0, 2); - }); - - client.on("message", (buf, info) => { - const expected = Buffer.concat([buf1, buf2]); - expect(buf.equals(expected)).toBe(true); - expect(onMessage).toHaveBeenCalledTimes(1); - done(); - }); - - client.bind(0); -}); - -//<#END_FILE: test-dgram-send-multi-buffer-copy.js diff --git a/test/js/node/test/parallel/dgram-send-multi-string-array.test.js b/test/js/node/test/parallel/dgram-send-multi-string-array.test.js deleted file mode 100644 index 804ea7c285..0000000000 --- a/test/js/node/test/parallel/dgram-send-multi-string-array.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-dgram-send-multi-string-array.js -//#SHA1: 8ea2007ac52bfde3742aabe352aab19bf91a4ac2 -//----------------- -"use strict"; - -const dgram = require("dgram"); - -test("dgram send multiple strings as array", done => { - const socket = dgram.createSocket("udp4"); - const data = ["foo", "bar", "baz"]; - - socket.on("message", (msg, rinfo) => { - socket.close(); - expect(msg.toString()).toBe(data.join("")); - done(); - }); - - socket.bind(() => { - socket.send(data, socket.address().port, "localhost"); - }); -}); - -//<#END_FILE: test-dgram-send-multi-string-array.js diff --git a/test/js/node/test/parallel/dgram-sendto.test.js b/test/js/node/test/parallel/dgram-sendto.test.js deleted file mode 100644 index e3b3d88c2a..0000000000 --- a/test/js/node/test/parallel/dgram-sendto.test.js +++ /dev/null @@ -1,70 +0,0 @@ -//#FILE: test-dgram-sendto.js -//#SHA1: 8047210c86bed6536f5ff3132e228a2ee9d0bb11 -//----------------- -"use strict"; - -const dgram = require("dgram"); - -describe("dgram.sendto", () => { - let socket; - - beforeEach(() => { - socket = dgram.createSocket("udp4"); - }); - - afterEach(() => { - socket.close(); - }); - - test("throws when called with no arguments", () => { - expect(() => socket.sendto()).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); - - test('throws when "length" argument is invalid', () => { - expect(() => socket.sendto("buffer", 1, "offset", "port", "address", "cb")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); - - test('throws when "offset" argument is invalid', () => { - expect(() => socket.sendto("buffer", "offset", 1, "port", "address", "cb")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); - - test('throws when "address" argument is invalid', () => { - expect(() => socket.sendto("buffer", 1, 1, 10, false, "cb")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); - - test('throws when "port" argument is invalid', () => { - expect(() => socket.sendto("buffer", 1, 1, false, "address", "cb")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-dgram-sendto.js diff --git a/test/js/node/test/parallel/dgram-udp4.test.js b/test/js/node/test/parallel/dgram-udp4.test.js deleted file mode 100644 index 27be2e63fc..0000000000 --- a/test/js/node/test/parallel/dgram-udp4.test.js +++ /dev/null @@ -1,81 +0,0 @@ -//#FILE: test-dgram-udp4.js -//#SHA1: 588735591046212fc512f69d9001ccb820c57a71 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const dgram = require("dgram"); - -const message_to_send = "A message to send"; -const localhostIPv4 = "127.0.0.1"; - -test("UDP4 server and client communication", async () => { - const server = dgram.createSocket("udp4"); - - const serverMessagePromise = new Promise(resolve => { - server.on("message", (msg, rinfo) => { - expect(rinfo.address).toBe(localhostIPv4); - expect(msg.toString()).toBe(message_to_send); - server.send(msg, 0, msg.length, rinfo.port, rinfo.address); - resolve(); - }); - }); - - const listeningPromise = new Promise(resolve => { - server.on("listening", resolve); - }); - - server.bind(0); - - await listeningPromise; - - const client = dgram.createSocket("udp4"); - const port = server.address().port; - - const clientMessagePromise = new Promise(resolve => { - client.on("message", (msg, rinfo) => { - expect(rinfo.address).toBe(localhostIPv4); - expect(rinfo.port).toBe(port); - expect(msg.toString()).toBe(message_to_send); - resolve(); - }); - }); - - client.send(message_to_send, 0, message_to_send.length, port, "localhost"); - - await Promise.all([serverMessagePromise, clientMessagePromise]); - - const clientClosePromise = new Promise(resolve => { - client.on("close", resolve); - }); - - const serverClosePromise = new Promise(resolve => { - server.on("close", resolve); - }); - - client.close(); - server.close(); - - await Promise.all([clientClosePromise, serverClosePromise]); -}); - -//<#END_FILE: test-dgram-udp4.js diff --git a/test/js/node/test/parallel/dgram-unref-in-cluster.test.js b/test/js/node/test/parallel/dgram-unref-in-cluster.test.js deleted file mode 100644 index dcca2428e3..0000000000 --- a/test/js/node/test/parallel/dgram-unref-in-cluster.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-dgram-unref-in-cluster.js -//#SHA1: eca71c33b1bf5be34a28e6cc82df49c73e775153 -//----------------- -"use strict"; - -const dgram = require("dgram"); -const cluster = require("cluster"); - -if (process.platform === "win32") { - test.skip("dgram clustering is currently not supported on Windows."); -} else { - if (cluster.isPrimary) { - test("dgram unref in cluster", () => { - cluster.fork(); - }); - } else { - test("dgram unref in cluster worker", () => { - const socket = dgram.createSocket("udp4"); - socket.unref(); - socket.bind(); - - return new Promise(resolve => { - socket.on("listening", () => { - const sockets = process.getActiveResourcesInfo().filter(item => { - return item === "UDPWrap"; - }); - expect(sockets.length).toBe(0); - process.disconnect(); - resolve(); - }); - }); - }); - } -} - -//<#END_FILE: test-dgram-unref-in-cluster.js diff --git a/test/js/node/test/parallel/dgram-unref.test.js b/test/js/node/test/parallel/dgram-unref.test.js deleted file mode 100644 index ff11989d7b..0000000000 --- a/test/js/node/test/parallel/dgram-unref.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-dgram-unref.js -//#SHA1: 97b218a9107def7e2cc28e595f84f9a05a606850 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const dgram = require("dgram"); - -test("unref() a socket with a handle", () => { - const s = dgram.createSocket("udp4"); - s.bind(); - s.unref(); - // No assertion needed, just checking that it doesn't throw -}); - -test("unref() a socket with no handle", done => { - const s = dgram.createSocket("udp4"); - s.close(() => { - s.unref(); - done(); - }); -}); - -test("setTimeout should not be called", () => { - const mockCallback = jest.fn(); - setTimeout(mockCallback, 1000).unref(); - expect(mockCallback).not.toHaveBeenCalled(); -}); - -//<#END_FILE: test-dgram-unref.js diff --git a/test/js/node/test/parallel/diagnostics-channel-has-subscribers.test.js b/test/js/node/test/parallel/diagnostics-channel-has-subscribers.test.js deleted file mode 100644 index 2e2f119c73..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-has-subscribers.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-diagnostics-channel-has-subscribers.js -//#SHA1: 8040abda8d37916d6f6d5f3966e8c531d1770e1a -//----------------- -"use strict"; - -const { channel, hasSubscribers } = require("diagnostics_channel"); - -describe("diagnostics_channel", () => { - test("hasSubscribers returns correct state", () => { - const dc = channel("test"); - expect(hasSubscribers("test")).toBe(false); - - dc.subscribe(() => {}); - expect(hasSubscribers("test")).toBe(true); - }); -}); - -//<#END_FILE: test-diagnostics-channel-has-subscribers.js diff --git a/test/js/node/test/parallel/diagnostics-channel-http-server-start.test.js b/test/js/node/test/parallel/diagnostics-channel-http-server-start.test.js deleted file mode 100644 index a5446eec21..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-http-server-start.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-diagnostics-channel-http-server-start.js -//#SHA1: 5540b17246152983b832ddafa0be55502ee83a23 -//----------------- -"use strict"; - -const { AsyncLocalStorage } = require("async_hooks"); -const dc = require("diagnostics_channel"); -const http = require("http"); - -const als = new AsyncLocalStorage(); -let context; - -describe("diagnostics_channel http server start", () => { - let server; - let request; - let response; - - beforeAll(() => { - // Bind requests to an AsyncLocalStorage context - dc.subscribe("http.server.request.start", message => { - als.enterWith(message); - context = message; - }); - - // When the request ends, verify the context has been maintained - // and that the messages contain the expected data - dc.subscribe("http.server.response.finish", message => { - const data = { - request, - response, - server, - socket: request.socket, - }; - - // Context is maintained - compare(als.getStore(), context); - - compare(context, data); - compare(message, data); - }); - - server = http.createServer((req, res) => { - request = req; - response = res; - - setTimeout(() => { - res.end("done"); - }, 1); - }); - }); - - afterAll(() => { - server.close(); - }); - - it("should maintain context and contain expected data", done => { - server.listen(() => { - const { port } = server.address(); - http.get(`http://localhost:${port}`, res => { - res.resume(); - res.on("end", () => { - done(); - }); - }); - }); - }); -}); - -function compare(a, b) { - expect(a.request).toBe(b.request); - expect(a.response).toBe(b.response); - expect(a.socket).toBe(b.socket); - expect(a.server).toBe(b.server); -} - -//<#END_FILE: test-diagnostics-channel-http-server-start.js diff --git a/test/js/node/test/parallel/diagnostics-channel-object-channel-pub-sub.test.js b/test/js/node/test/parallel/diagnostics-channel-object-channel-pub-sub.test.js deleted file mode 100644 index ae95ea487b..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-object-channel-pub-sub.test.js +++ /dev/null @@ -1,60 +0,0 @@ -//#FILE: test-diagnostics-channel-object-channel-pub-sub.js -//#SHA1: 185a8134da179dfda5f6b2d1a1a2622752431a90 -//----------------- -"use strict"; - -const dc = require("diagnostics_channel"); -const { Channel } = dc; - -const input = { - foo: "bar", -}; - -test("Channel creation and subscription", () => { - // Should not have named channel - expect(dc.hasSubscribers("test")).toBe(false); - - // Individual channel objects can be created to avoid future lookups - const channel = dc.channel("test"); - expect(channel).toBeInstanceOf(Channel); - - // No subscribers yet, should not publish - expect(channel.hasSubscribers).toBe(false); - - const subscriber = jest.fn((message, name) => { - expect(name).toBe(channel.name); - expect(message).toEqual(input); - }); - - // Now there's a subscriber, should publish - channel.subscribe(subscriber); - expect(channel.hasSubscribers).toBe(true); - - // The ActiveChannel prototype swap should not fail instanceof - expect(channel).toBeInstanceOf(Channel); - - // Should trigger the subscriber once - channel.publish(input); - expect(subscriber).toHaveBeenCalledTimes(1); - - // Should not publish after subscriber is unsubscribed - expect(channel.unsubscribe(subscriber)).toBe(true); - expect(channel.hasSubscribers).toBe(false); - - // unsubscribe() should return false when subscriber is not found - expect(channel.unsubscribe(subscriber)).toBe(false); -}); - -test("Invalid subscriber", () => { - const channel = dc.channel("test"); - expect(() => { - channel.subscribe(null); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-diagnostics-channel-object-channel-pub-sub.js diff --git a/test/js/node/test/parallel/diagnostics-channel-pub-sub.test.js b/test/js/node/test/parallel/diagnostics-channel-pub-sub.test.js deleted file mode 100644 index f20d19d136..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-pub-sub.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-diagnostics-channel-pub-sub.js -//#SHA1: cc5eee7f44117c2fd8446e8050ac19f5341f85a5 -//----------------- -"use strict"; - -const dc = require("diagnostics_channel"); -const { Channel } = dc; - -const name = "test"; -const input = { - foo: "bar", -}; - -test("diagnostics_channel pub/sub functionality", () => { - // Individual channel objects can be created to avoid future lookups - const channel = dc.channel(name); - expect(channel).toBeInstanceOf(Channel); - - // No subscribers yet, should not publish - expect(channel.hasSubscribers).toBeFalsy(); - - const subscriber = jest.fn((message, channelName) => { - expect(channelName).toBe(channel.name); - expect(message).toEqual(input); - }); - - // Now there's a subscriber, should publish - dc.subscribe(name, subscriber); - expect(channel.hasSubscribers).toBeTruthy(); - - // The ActiveChannel prototype swap should not fail instanceof - expect(channel).toBeInstanceOf(Channel); - - // Should trigger the subscriber once - channel.publish(input); - expect(subscriber).toHaveBeenCalledTimes(1); - - // Should not publish after subscriber is unsubscribed - expect(dc.unsubscribe(name, subscriber)).toBeTruthy(); - expect(channel.hasSubscribers).toBeFalsy(); - - // unsubscribe() should return false when subscriber is not found - expect(dc.unsubscribe(name, subscriber)).toBeFalsy(); - - expect(() => { - dc.subscribe(name, null); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - - // Reaching zero subscribers should not delete from the channels map as there - // will be no more weakref to incRef if another subscribe happens while the - // channel object itself exists. - channel.subscribe(subscriber); - channel.unsubscribe(subscriber); - channel.subscribe(subscriber); -}); - -//<#END_FILE: test-diagnostics-channel-pub-sub.js diff --git a/test/js/node/test/parallel/diagnostics-channel-symbol-named.test.js b/test/js/node/test/parallel/diagnostics-channel-symbol-named.test.js deleted file mode 100644 index d2d6065687..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-symbol-named.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-diagnostics-channel-symbol-named.js -//#SHA1: e0ae87b803333891439e11fa2306eefd23b507e4 -//----------------- -"use strict"; - -const dc = require("diagnostics_channel"); - -const input = { - foo: "bar", -}; - -const symbol = Symbol("test"); - -// Individual channel objects can be created to avoid future lookups -const channel = dc.channel(symbol); - -test("diagnostics channel with symbol name", () => { - // Expect two successful publishes later - const subscriber = jest.fn((message, name) => { - expect(name).toBe(symbol); - expect(message).toEqual(input); - }); - - channel.subscribe(subscriber); - - channel.publish(input); - - expect(subscriber).toHaveBeenCalledTimes(1); -}); - -test("channel creation with invalid argument", () => { - expect(() => { - dc.channel(null); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-diagnostics-channel-symbol-named.js diff --git a/test/js/node/test/parallel/diagnostics-channel-sync-unsubscribe.test.js b/test/js/node/test/parallel/diagnostics-channel-sync-unsubscribe.test.js deleted file mode 100644 index eb5ceea25b..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-sync-unsubscribe.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-diagnostics-channel-sync-unsubscribe.js -//#SHA1: 85d73bd9ce82a4293daca05617b2aece359745ff -//----------------- -"use strict"; - -const dc = require("node:diagnostics_channel"); - -const channel_name = "test:channel"; -const published_data = "some message"; - -test("diagnostics channel sync unsubscribe", () => { - const onMessageHandler = jest.fn(() => dc.unsubscribe(channel_name, onMessageHandler)); - - dc.subscribe(channel_name, onMessageHandler); - - // This must not throw. - expect(() => { - dc.channel(channel_name).publish(published_data); - }).not.toThrow(); - - expect(onMessageHandler).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-diagnostics-channel-sync-unsubscribe.js diff --git a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-callback-run-stores.test.js b/test/js/node/test/parallel/diagnostics-channel-tracing-channel-callback-run-stores.test.js deleted file mode 100644 index 15fcdedaf9..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-callback-run-stores.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-diagnostics-channel-tracing-channel-callback-run-stores.js -//#SHA1: 8ed3a87eb9d6c1a3a624245ce5f430b7e3730d2d -//----------------- -"use strict"; - -const { AsyncLocalStorage } = require("async_hooks"); -const dc = require("diagnostics_channel"); - -const channel = dc.tracingChannel("test"); -const store = new AsyncLocalStorage(); - -const firstContext = { foo: "bar" }; -const secondContext = { baz: "buz" }; - -test("tracing channel callback run stores", async () => { - const startBindStoreMock = jest.fn(() => firstContext); - const asyncStartBindStoreMock = jest.fn(() => secondContext); - - channel.start.bindStore(store, startBindStoreMock); - channel.asyncStart.bindStore(store, asyncStartBindStoreMock); - - expect(store.getStore()).toBeUndefined(); - - await new Promise(resolve => { - channel.traceCallback( - cb => { - expect(store.getStore()).toEqual(firstContext); - setImmediate(cb); - }, - 0, - {}, - null, - () => { - expect(store.getStore()).toEqual(secondContext); - resolve(); - }, - ); - }); - - expect(store.getStore()).toBeUndefined(); - expect(startBindStoreMock).toHaveBeenCalledTimes(1); - expect(asyncStartBindStoreMock).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-diagnostics-channel-tracing-channel-callback-run-stores.js diff --git a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-promise-run-stores.test.js b/test/js/node/test/parallel/diagnostics-channel-tracing-channel-promise-run-stores.test.js deleted file mode 100644 index 6c75c54789..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-promise-run-stores.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-diagnostics-channel-tracing-channel-promise-run-stores.js -//#SHA1: 4e5abb65d92cb3e649073803971180493c1a30ca -//----------------- -"use strict"; - -const { setTimeout } = require("node:timers/promises"); -const { AsyncLocalStorage } = require("async_hooks"); -const dc = require("diagnostics_channel"); - -const channel = dc.tracingChannel("test"); -const store = new AsyncLocalStorage(); - -const firstContext = { foo: "bar" }; -const secondContext = { baz: "buz" }; - -test("tracingChannel promise run stores", async () => { - const startBindStoreMock = jest.fn(() => firstContext); - const asyncStartBindStoreMock = jest.fn(() => secondContext); - - channel.start.bindStore(store, startBindStoreMock); - channel.asyncStart.bindStore(store, asyncStartBindStoreMock); - - expect(store.getStore()).toBeUndefined(); - - await channel.tracePromise(async () => { - expect(store.getStore()).toEqual(firstContext); - await setTimeout(1); - // Should _not_ switch to second context as promises don't have an "after" - // point at which to do a runStores. - expect(store.getStore()).toEqual(firstContext); - }); - - expect(store.getStore()).toBeUndefined(); - - expect(startBindStoreMock).toHaveBeenCalledTimes(1); - expect(asyncStartBindStoreMock).not.toHaveBeenCalled(); -}); - -//<#END_FILE: test-diagnostics-channel-tracing-channel-promise-run-stores.js diff --git a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-promise.test.js b/test/js/node/test/parallel/diagnostics-channel-tracing-channel-promise.test.js deleted file mode 100644 index 032a552cb6..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-promise.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-diagnostics-channel-tracing-channel-promise.js -//#SHA1: 6a692d8400685da6c930b662562adb3e36b2da9a -//----------------- -"use strict"; - -const dc = require("diagnostics_channel"); - -const channel = dc.tracingChannel("test"); - -const expectedResult = { foo: "bar" }; -const input = { foo: "bar" }; -const thisArg = { baz: "buz" }; - -function check(found) { - expect(found).toEqual(input); -} - -function checkAsync(found) { - check(found); - expect(found.error).toBeUndefined(); - expect(found.result).toEqual(expectedResult); -} - -const handlers = { - start: jest.fn(check), - end: jest.fn(check), - asyncStart: jest.fn(checkAsync), - asyncEnd: jest.fn(checkAsync), - error: jest.fn(), -}; - -test("diagnostics_channel tracing channel promise", async () => { - channel.subscribe(handlers); - - await channel.tracePromise( - function (value) { - expect(this).toEqual(thisArg); - return Promise.resolve(value); - }, - input, - thisArg, - expectedResult, - ); - - expect(handlers.start).toHaveBeenCalledTimes(1); - expect(handlers.end).toHaveBeenCalledTimes(1); - expect(handlers.asyncStart).toHaveBeenCalledTimes(1); - expect(handlers.asyncEnd).toHaveBeenCalledTimes(1); - expect(handlers.error).not.toHaveBeenCalled(); - - const value = await channel.tracePromise( - function (value) { - expect(this).toEqual(thisArg); - return Promise.resolve(value); - }, - input, - thisArg, - expectedResult, - ); - - expect(value).toEqual(expectedResult); -}); - -//<#END_FILE: test-diagnostics-channel-tracing-channel-promise.js diff --git a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync-error.test.js b/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync-error.test.js deleted file mode 100644 index 2906e447cf..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync-error.test.js +++ /dev/null @@ -1,52 +0,0 @@ -//#FILE: test-diagnostics-channel-tracing-channel-sync-error.js -//#SHA1: faf279d48c76f3c97ddf7e258df9ed49154a4cac -//----------------- -"use strict"; - -const dc = require("diagnostics_channel"); - -const channel = dc.tracingChannel("test"); - -const expectedError = new Error("test"); -const input = { foo: "bar" }; -const thisArg = { baz: "buz" }; - -function check(found) { - expect(found).toEqual(input); -} - -test("traceSync with error", () => { - const handlers = { - start: jest.fn(check), - end: jest.fn(check), - asyncStart: jest.fn(), - asyncEnd: jest.fn(), - error: jest.fn(found => { - check(found); - expect(found.error).toBe(expectedError); - }), - }; - - channel.subscribe(handlers); - - expect(() => { - channel.traceSync( - function (err) { - expect(this).toEqual(thisArg); - expect(err).toBe(expectedError); - throw err; - }, - input, - thisArg, - expectedError, - ); - }).toThrow(expectedError); - - expect(handlers.start).toHaveBeenCalledTimes(1); - expect(handlers.end).toHaveBeenCalledTimes(1); - expect(handlers.asyncStart).not.toHaveBeenCalled(); - expect(handlers.asyncEnd).not.toHaveBeenCalled(); - expect(handlers.error).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-diagnostics-channel-tracing-channel-sync-error.js diff --git a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync-run-stores.test.js b/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync-run-stores.test.js deleted file mode 100644 index ed7212e92d..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync-run-stores.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-diagnostics-channel-tracing-channel-sync-run-stores.js -//#SHA1: 51ffe2c7cb7160b565bfe6bbca0d29005a6bf876 -//----------------- -"use strict"; - -const { AsyncLocalStorage } = require("async_hooks"); -const dc = require("diagnostics_channel"); - -const channel = dc.tracingChannel("test"); -const store = new AsyncLocalStorage(); - -const context = { foo: "bar" }; - -test("diagnostics channel tracing channel sync run stores", () => { - const startCallback = jest.fn(() => context); - channel.start.bindStore(store, startCallback); - - expect(store.getStore()).toBeUndefined(); - - const traceCallback = jest.fn(() => { - expect(store.getStore()).toEqual(context); - }); - - channel.traceSync(traceCallback); - - expect(store.getStore()).toBeUndefined(); - - expect(startCallback).toHaveBeenCalledTimes(1); - expect(traceCallback).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-diagnostics-channel-tracing-channel-sync-run-stores.js diff --git a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync.test.js b/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync.test.js deleted file mode 100644 index f4a4c3b923..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-tracing-channel-sync.test.js +++ /dev/null @@ -1,80 +0,0 @@ -//#FILE: test-diagnostics-channel-tracing-channel-sync.js -//#SHA1: fad9bfb35032ed67643b026f8e79611d8197a131 -//----------------- -"use strict"; - -const dc = require("diagnostics_channel"); - -const channel = dc.tracingChannel("test"); - -const expectedResult = { foo: "bar" }; -const input = { foo: "bar" }; -const thisArg = { baz: "buz" }; -const arg = { baz: "buz" }; - -function check(found) { - expect(found).toBe(input); -} - -describe("diagnostics_channel tracing channel sync", () => { - const handlers = { - start: jest.fn(check), - end: jest.fn(found => { - check(found); - expect(found.result).toBe(expectedResult); - }), - asyncStart: jest.fn(), - asyncEnd: jest.fn(), - error: jest.fn(), - }; - - beforeEach(() => { - jest.clearAllMocks(); - }); - - test("channel subscription and traceSync", () => { - expect(channel.start.hasSubscribers).toBe(false); - channel.subscribe(handlers); - expect(channel.start.hasSubscribers).toBe(true); - - const result1 = channel.traceSync( - function (arg1) { - expect(arg1).toBe(arg); - expect(this).toBe(thisArg); - return expectedResult; - }, - input, - thisArg, - arg, - ); - - expect(result1).toBe(expectedResult); - expect(handlers.start).toHaveBeenCalledTimes(1); - expect(handlers.end).toHaveBeenCalledTimes(1); - expect(handlers.asyncStart).not.toHaveBeenCalled(); - expect(handlers.asyncEnd).not.toHaveBeenCalled(); - expect(handlers.error).not.toHaveBeenCalled(); - }); - - test("channel unsubscription", () => { - channel.unsubscribe(handlers); - expect(channel.start.hasSubscribers).toBe(false); - - const result2 = channel.traceSync( - function (arg1) { - expect(arg1).toBe(arg); - expect(this).toBe(thisArg); - return expectedResult; - }, - input, - thisArg, - arg, - ); - - expect(result2).toBe(expectedResult); - expect(handlers.start).not.toHaveBeenCalled(); - expect(handlers.end).not.toHaveBeenCalled(); - }); -}); - -//<#END_FILE: test-diagnostics-channel-tracing-channel-sync.js diff --git a/test/js/node/test/parallel/diagnostics-channel-udp.test.js b/test/js/node/test/parallel/diagnostics-channel-udp.test.js deleted file mode 100644 index 449df21666..0000000000 --- a/test/js/node/test/parallel/diagnostics-channel-udp.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-diagnostics-channel-udp.js -//#SHA1: 13d46f3f2404ee7cd53a551b90fc1ced191d4a81 -//----------------- -"use strict"; - -const dgram = require("dgram"); -const dc = require("diagnostics_channel"); - -const udpSocketChannel = dc.channel("udp.socket"); - -const isUDPSocket = socket => socket instanceof dgram.Socket; - -test("udp.socket channel emits UDP socket", () => { - const channelCallback = jest.fn(({ socket }) => { - expect(isUDPSocket(socket)).toBe(true); - }); - - udpSocketChannel.subscribe(channelCallback); - - const socket = dgram.createSocket("udp4"); - socket.close(); - - expect(channelCallback).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-diagnostics-channel-udp.js diff --git a/test/js/node/test/parallel/dns-resolve-promises.test.js b/test/js/node/test/parallel/dns-resolve-promises.test.js deleted file mode 100644 index 737ae7e467..0000000000 --- a/test/js/node/test/parallel/dns-resolve-promises.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-dns-resolve-promises.js -//#SHA1: ca56d4fe55a765a3e9db340661c96d8b1fd7a7a9 -//----------------- -"use strict"; - -const dns = require("dns"); - -test("DNS resolve promises error handling", async () => { - // Mock the dns.promises.resolve function to simulate an error - dns.promises.resolve = jest.fn().mockRejectedValue({ - code: "EPERM", - syscall: "queryA", - hostname: "example.org", - }); - - await expect(dns.promises.resolve("example.org")).rejects.toMatchObject({ - code: "EPERM", - syscall: "queryA", - hostname: "example.org", - }); - - expect(dns.promises.resolve).toHaveBeenCalledWith("example.org"); -}); - -//<#END_FILE: test-dns-resolve-promises.js diff --git a/test/js/node/test/parallel/domain-crypto.test.js b/test/js/node/test/parallel/domain-crypto.test.js deleted file mode 100644 index 406da90236..0000000000 --- a/test/js/node/test/parallel/domain-crypto.test.js +++ /dev/null @@ -1,87 +0,0 @@ -//#FILE: test-domain-crypto.js -//#SHA1: d7d6352f0f2684220baef0cfa1029278c3f05f8f -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const crypto = require("crypto"); - -// Pollution of global is intentional as part of test. -// See https://github.com/nodejs/node/commit/d1eff9ab -global.domain = require("domain"); - -describe("domain-crypto", () => { - beforeAll(() => { - if (!crypto) { - test.skip("node compiled without OpenSSL."); - } - }); - - test("crypto.randomBytes should not throw", () => { - expect(() => crypto.randomBytes(8)).not.toThrow(); - }); - - test("crypto.randomBytes with callback should succeed", () => { - return new Promise(resolve => { - crypto.randomBytes(8, (err, buffer) => { - expect(err).toBeNull(); - expect(buffer).toBeInstanceOf(Buffer); - expect(buffer.length).toBe(8); - resolve(); - }); - }); - }); - - test("crypto.randomFillSync should not throw", () => { - const buf = Buffer.alloc(8); - expect(() => crypto.randomFillSync(buf)).not.toThrow(); - }); - - test("crypto.pseudoRandomBytes should not throw", () => { - expect(() => crypto.pseudoRandomBytes(8)).not.toThrow(); - }); - - test("crypto.pseudoRandomBytes with callback should succeed", () => { - return new Promise(resolve => { - crypto.pseudoRandomBytes(8, (err, buffer) => { - expect(err).toBeNull(); - expect(buffer).toBeInstanceOf(Buffer); - expect(buffer.length).toBe(8); - resolve(); - }); - }); - }); - - test("crypto.pbkdf2 should succeed", () => { - return new Promise(resolve => { - crypto.pbkdf2("password", "salt", 8, 8, "sha1", (err, derivedKey) => { - expect(err).toBeNull(); - expect(derivedKey).toBeInstanceOf(Buffer); - expect(derivedKey.length).toBe(8); - resolve(); - }); - }); - }); -}); - -//<#END_FILE: test-domain-crypto.js diff --git a/test/js/node/test/parallel/domain-dep0097.test.js b/test/js/node/test/parallel/domain-dep0097.test.js deleted file mode 100644 index 03a74830e1..0000000000 --- a/test/js/node/test/parallel/domain-dep0097.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-domain-dep0097.js -//#SHA1: d0eeaeed86d045c8deba99d4ca589d35b368f17d -//----------------- -"use strict"; - -// Skip this test if inspector is disabled -if (typeof inspector === "undefined") { - test.skip("Inspector is disabled", () => {}); -} else { - const domain = require("domain"); - const inspector = require("inspector"); - - test("DEP0097 warning is emitted", () => { - const warningHandler = jest.fn(warning => { - expect(warning.code).toBe("DEP0097"); - expect(warning.message).toMatch(/Triggered by calling emit on process/); - }); - - process.on("warning", warningHandler); - - domain.create().run(() => { - inspector.open(0); - }); - - expect(warningHandler).toHaveBeenCalledTimes(1); - - // Clean up - process.removeListener("warning", warningHandler); - }); -} - -//<#END_FILE: test-domain-dep0097.js diff --git a/test/js/node/test/parallel/domain-thrown-error-handler-stack.test.js b/test/js/node/test/parallel/domain-thrown-error-handler-stack.test.js deleted file mode 100644 index f3c8d0b8b7..0000000000 --- a/test/js/node/test/parallel/domain-thrown-error-handler-stack.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-domain-thrown-error-handler-stack.js -//#SHA1: fd9aef2a4c852c0d092708bb6e712ad345cd4d2d -//----------------- -"use strict"; - -const domain = require("domain"); - -// Make sure that when an error is thrown from a nested domain, its error -// handler runs outside of that domain, but within the context of any parent -// domain. - -test("nested domain error handling", () => { - const d = domain.create(); - const d2 = domain.create(); - - d2.on("error", err => { - expect(domain._stack.length).toBe(1); - expect(process.domain).toBe(d); - - process.nextTick(() => { - expect(domain._stack.length).toBe(1); - expect(process.domain).toBe(d); - }); - }); - - expect(() => { - d.run(() => { - d2.run(() => { - throw new Error("oops"); - }); - }); - }).toThrow( - expect.objectContaining({ - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-domain-thrown-error-handler-stack.js diff --git a/test/js/node/test/parallel/domain-vm-promise-isolation.test.js b/test/js/node/test/parallel/domain-vm-promise-isolation.test.js deleted file mode 100644 index fe9ef2198e..0000000000 --- a/test/js/node/test/parallel/domain-vm-promise-isolation.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-domain-vm-promise-isolation.js -//#SHA1: 866cac427030e1696a6583c234815beed16bf5c8 -//----------------- -"use strict"; - -const domain = require("domain"); -const vm = require("vm"); - -// A promise created in a VM should not include a domain field but -// domains should still be able to propagate through them. -// -// See; https://github.com/nodejs/node/issues/40999 - -const context = vm.createContext({}); - -function run(code) { - const d = domain.createDomain(); - d.run(() => { - const p = vm.runInContext(code, context)(); - expect(p.domain).toBeUndefined(); - return p.then(() => { - expect(process.domain).toBe(d); - }); - }); -} - -test("VM promise isolation and domain propagation", async () => { - const runPromises = []; - for (let i = 0; i < 1000; i++) { - runPromises.push(run("async () => null")); - } - await Promise.all(runPromises); -}, 30000); // Increased timeout for multiple iterations - -//<#END_FILE: test-domain-vm-promise-isolation.js diff --git a/test/js/node/test/parallel/double-tls-client.test.js b/test/js/node/test/parallel/double-tls-client.test.js deleted file mode 100644 index 0913a2a5b7..0000000000 --- a/test/js/node/test/parallel/double-tls-client.test.js +++ /dev/null @@ -1,85 +0,0 @@ -//#FILE: test-double-tls-client.js -//#SHA1: f0d01e282b2d7efc1e1770700f742345216a8bb3 -//----------------- -"use strict"; - -const assert = require("assert"); -const fixtures = require("../common/fixtures"); -const tls = require("tls"); - -// In reality, this can be a HTTP CONNECT message, signaling the incoming -// data is TLS encrypted -const HEAD = "XXXX"; - -let subserver; -let server; - -beforeAll(() => { - subserver = tls.createServer({ - key: fixtures.readKey("agent1-key.pem"), - cert: fixtures.readKey("agent1-cert.pem"), - }); - - subserver.on("secureConnection", () => { - process.exit(0); - }); - - server = tls.createServer({ - key: fixtures.readKey("agent1-key.pem"), - cert: fixtures.readKey("agent1-cert.pem"), - }); - - server.on("secureConnection", serverTlsSock => { - serverTlsSock.on("data", chunk => { - expect(chunk.toString()).toBe(HEAD); - subserver.emit("connection", serverTlsSock); - }); - }); -}); - -afterAll(() => { - server.close(); - subserver.close(); -}); - -test("double TLS client", done => { - const onSecureConnect = jest.fn(); - - server.listen(() => { - const down = tls.connect({ - host: "127.0.0.1", - port: server.address().port, - rejectUnauthorized: false, - }); - - down.on("secureConnect", () => { - onSecureConnect(); - down.write(HEAD, err => { - expect(err).toBeFalsy(); - - // Sending tls data on a client TLSSocket with an active write led to a crash: - // - // node[16862]: ../src/crypto/crypto_tls.cc:963:virtual int node::crypto::TLSWrap::DoWrite(node::WriteWrap*, - // uv_buf_t*, size_t, uv_stream_t*): Assertion `!current_write_' failed. - // 1: 0xb090e0 node::Abort() [node] - // 2: 0xb0915e [node] - // 3: 0xca8413 node::crypto::TLSWrap::DoWrite(node::WriteWrap*, uv_buf_t*, unsigned long, uv_stream_s*) [node] - // 4: 0xcaa549 node::StreamBase::Write(uv_buf_t*, unsigned long, uv_stream_s*, v8::Local) [node] - // 5: 0xca88d7 node::crypto::TLSWrap::EncOut() [node] - // 6: 0xd3df3e [node] - // 7: 0xd3f35f v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) [node] - // 8: 0x15d9ef9 [node] - // Aborted - tls.connect({ - socket: down, - rejectUnauthorized: false, - }); - - expect(onSecureConnect).toHaveBeenCalledTimes(1); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-double-tls-client.js diff --git a/test/js/node/test/parallel/error-format-list.test.js b/test/js/node/test/parallel/error-format-list.test.js deleted file mode 100644 index ea5fb2dfc9..0000000000 --- a/test/js/node/test/parallel/error-format-list.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-error-format-list.js -//#SHA1: ed33f55f6c42ff9671add8288b1d2984393bdfc1 -//----------------- -"use strict"; - -if (!Intl) { - test.skip("missing Intl", () => {}); -} else { - test("formatList function", () => { - const and = new Intl.ListFormat("en", { style: "long", type: "conjunction" }); - const or = new Intl.ListFormat("en", { style: "long", type: "disjunction" }); - - const input = ["apple", "banana", "orange", "pear"]; - for (let i = 0; i < input.length; i++) { - const slicedInput = input.slice(0, i); - expect(formatList(slicedInput)).toBe(and.format(slicedInput)); - expect(formatList(slicedInput, "or")).toBe(or.format(slicedInput)); - } - }); -} - -// Helper function to replicate the behavior of internal/errors formatList -function formatList(list, type = "and") { - const formatter = new Intl.ListFormat("en", { - style: "long", - type: type === "and" ? "conjunction" : "disjunction", - }); - return formatter.format(list); -} - -//<#END_FILE: test-error-format-list.js diff --git a/test/js/node/test/parallel/errors-systemerror-stacktracelimit-deleted-and-error-sealed.test.js b/test/js/node/test/parallel/errors-systemerror-stacktracelimit-deleted-and-error-sealed.test.js deleted file mode 100644 index f2b27f0e1f..0000000000 --- a/test/js/node/test/parallel/errors-systemerror-stacktracelimit-deleted-and-error-sealed.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-errors-systemerror-stackTraceLimit-deleted-and-Error-sealed.js -//#SHA1: 5d9d37ff8651fd7b7ffd5e9ae1b54e4ebc900355 -//----------------- -"use strict"; - -test("SystemError with deleted stackTraceLimit and sealed Error", () => { - delete Error.stackTraceLimit; - Object.seal(Error); - - const ctx = { - code: "ETEST", - message: "code message", - syscall: "syscall_test", - path: "/str", - dest: "/str2", - }; - - const errorThrowingFunction = () => { - const error = new Error("custom message"); - error.code = "ERR_TEST"; - error.name = "SystemError"; - error.info = ctx; - throw error; - }; - - expect(errorThrowingFunction).toThrow( - expect.objectContaining({ - code: "ERR_TEST", - name: "SystemError", - info: ctx, - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-errors-systemerror-stackTraceLimit-deleted-and-Error-sealed.js diff --git a/test/js/node/test/parallel/eval.test.js b/test/js/node/test/parallel/eval.test.js deleted file mode 100644 index 97bc6069a4..0000000000 --- a/test/js/node/test/parallel/eval.test.js +++ /dev/null @@ -1,11 +0,0 @@ -//#FILE: test-eval.js -//#SHA1: 3ea606b33a3ee4b40ef918317b32008e0f5d5e49 -//----------------- -"use strict"; - -// Verify that eval is allowed by default. -test("eval is allowed by default", () => { - expect(eval('"eval"')).toBe("eval"); -}); - -//<#END_FILE: test-eval.js diff --git a/test/js/node/test/parallel/event-emitter-add-listeners.test.js b/test/js/node/test/parallel/event-emitter-add-listeners.test.js deleted file mode 100644 index fec7a8fdd2..0000000000 --- a/test/js/node/test/parallel/event-emitter-add-listeners.test.js +++ /dev/null @@ -1,91 +0,0 @@ -//#FILE: test-event-emitter-add-listeners.js -//#SHA1: 25d8611a8cf3694d26e53bb90f760beeb3bb1946 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const EventEmitter = require("events"); - -test("EventEmitter addListener functionality", () => { - const ee = new EventEmitter(); - const events_new_listener_emitted = []; - const listeners_new_listener_emitted = []; - - // Sanity check - expect(ee.addListener).toBe(ee.on); - - ee.on("newListener", function (event, listener) { - // Don't track newListener listeners. - if (event === "newListener") return; - - events_new_listener_emitted.push(event); - listeners_new_listener_emitted.push(listener); - }); - - const hello = jest.fn((a, b) => { - expect(a).toBe("a"); - expect(b).toBe("b"); - }); - - ee.once("newListener", function (name, listener) { - expect(name).toBe("hello"); - expect(listener).toBe(hello); - expect(this.listeners("hello")).toEqual([]); - }); - - ee.on("hello", hello); - ee.once("foo", () => { - throw new Error("This should not be called"); - }); - expect(events_new_listener_emitted).toEqual(["hello", "foo"]); - expect(listeners_new_listener_emitted).toEqual([hello, expect.any(Function)]); - - ee.emit("hello", "a", "b"); - expect(hello).toHaveBeenCalledTimes(1); -}); - -test("setMaxListeners with 0 does not throw", () => { - const f = new EventEmitter(); - expect(() => { - f.setMaxListeners(0); - }).not.toThrow(); -}); - -test("newListener event and listener order", () => { - const listen1 = () => {}; - const listen2 = () => {}; - const ee = new EventEmitter(); - - ee.once("newListener", function () { - expect(ee.listeners("hello")).toEqual([]); - ee.once("newListener", function () { - expect(ee.listeners("hello")).toEqual([]); - }); - ee.on("hello", listen2); - }); - ee.on("hello", listen1); - // The order of listeners on an event is not always the order in which the - // listeners were added. - expect(ee.listeners("hello")).toEqual([listen2, listen1]); -}); - -//<#END_FILE: test-event-emitter-add-listeners.js diff --git a/test/js/node/test/parallel/event-emitter-check-listener-leaks.test.js b/test/js/node/test/parallel/event-emitter-check-listener-leaks.test.js deleted file mode 100644 index 49c67758b6..0000000000 --- a/test/js/node/test/parallel/event-emitter-check-listener-leaks.test.js +++ /dev/null @@ -1,103 +0,0 @@ -//#FILE: test-event-emitter-check-listener-leaks.js -//#SHA1: c9d313a0879cc331d8ad1afafe5b9f482597bc7c -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const events = require("events"); - -test("default", () => { - const e = new events.EventEmitter(); - - for (let i = 0; i < 10; i++) { - e.on("default", jest.fn()); - } - expect(Object.hasOwn(e._events.default, "warned")).toBe(false); - e.on("default", jest.fn()); - expect(e._events.default.warned).toBe(true); - - // symbol - const symbol = Symbol("symbol"); - e.setMaxListeners(1); - e.on(symbol, jest.fn()); - expect(Object.hasOwn(e._events[symbol], "warned")).toBe(false); - e.on(symbol, jest.fn()); - expect(Object.hasOwn(e._events[symbol], "warned")).toBe(true); - - // specific - e.setMaxListeners(5); - for (let i = 0; i < 5; i++) { - e.on("specific", jest.fn()); - } - expect(Object.hasOwn(e._events.specific, "warned")).toBe(false); - e.on("specific", jest.fn()); - expect(e._events.specific.warned).toBe(true); - - // only one - e.setMaxListeners(1); - e.on("only one", jest.fn()); - expect(Object.hasOwn(e._events["only one"], "warned")).toBe(false); - e.on("only one", jest.fn()); - expect(Object.hasOwn(e._events["only one"], "warned")).toBe(true); - - // unlimited - e.setMaxListeners(0); - for (let i = 0; i < 1000; i++) { - e.on("unlimited", jest.fn()); - } - expect(Object.hasOwn(e._events.unlimited, "warned")).toBe(false); -}); - -test("process-wide", () => { - events.EventEmitter.defaultMaxListeners = 42; - const e = new events.EventEmitter(); - - for (let i = 0; i < 42; ++i) { - e.on("fortytwo", jest.fn()); - } - expect(Object.hasOwn(e._events.fortytwo, "warned")).toBe(false); - e.on("fortytwo", jest.fn()); - expect(Object.hasOwn(e._events.fortytwo, "warned")).toBe(true); - delete e._events.fortytwo.warned; - - events.EventEmitter.defaultMaxListeners = 44; - e.on("fortytwo", jest.fn()); - expect(Object.hasOwn(e._events.fortytwo, "warned")).toBe(false); - e.on("fortytwo", jest.fn()); - expect(Object.hasOwn(e._events.fortytwo, "warned")).toBe(true); -}); - -test("_maxListeners precedence over defaultMaxListeners", () => { - events.EventEmitter.defaultMaxListeners = 42; - const e = new events.EventEmitter(); - e.setMaxListeners(1); - e.on("uno", jest.fn()); - expect(Object.hasOwn(e._events.uno, "warned")).toBe(false); - e.on("uno", jest.fn()); - expect(Object.hasOwn(e._events.uno, "warned")).toBe(true); - - // chainable - expect(e.setMaxListeners(1)).toBe(e); -}); - -//<#END_FILE: test-event-emitter-check-listener-leaks.js diff --git a/test/js/node/test/parallel/event-emitter-emit-context.test.js b/test/js/node/test/parallel/event-emitter-emit-context.test.js deleted file mode 100644 index b39c26258e..0000000000 --- a/test/js/node/test/parallel/event-emitter-emit-context.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-event-emitter-emit-context.js -//#SHA1: 66f963c1a1351deff53d036d86f821c5e932c832 -//----------------- -"use strict"; -const EventEmitter = require("events"); - -// Test emit called by other context -const EE = new EventEmitter(); - -// Works as expected if the context has no `constructor.name` -test("emit called with context having no constructor.name", () => { - const ctx = { __proto__: null }; - expect(() => EE.emit.call(ctx, "error", new Error("foo"))).toThrow( - expect.objectContaining({ - name: "Error", - message: expect.any(String), - }), - ); -}); - -test("emit called with empty object context", () => { - expect(EE.emit.call({}, "foo")).toBe(false); -}); - -//<#END_FILE: test-event-emitter-emit-context.js diff --git a/test/js/node/test/parallel/event-emitter-get-max-listeners.test.js b/test/js/node/test/parallel/event-emitter-get-max-listeners.test.js deleted file mode 100644 index 612a3f1323..0000000000 --- a/test/js/node/test/parallel/event-emitter-get-max-listeners.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-event-emitter-get-max-listeners.js -//#SHA1: ff5c2f7b9525ae4137ea8eddd742572bd399c5ce -//----------------- -"use strict"; - -const EventEmitter = require("events"); - -test("EventEmitter getMaxListeners", () => { - const emitter = new EventEmitter(); - - expect(emitter.getMaxListeners()).toBe(EventEmitter.defaultMaxListeners); - - emitter.setMaxListeners(0); - expect(emitter.getMaxListeners()).toBe(0); - - emitter.setMaxListeners(3); - expect(emitter.getMaxListeners()).toBe(3); -}); - -// https://github.com/nodejs/node/issues/523 - second call should not throw. -test("EventEmitter.prototype.on should not throw on second call", () => { - const recv = {}; - expect(() => { - EventEmitter.prototype.on.call(recv, "event", () => {}); - EventEmitter.prototype.on.call(recv, "event", () => {}); - }).not.toThrow(); -}); - -//<#END_FILE: test-event-emitter-get-max-listeners.js diff --git a/test/js/node/test/parallel/event-emitter-listener-count.test.js b/test/js/node/test/parallel/event-emitter-listener-count.test.js deleted file mode 100644 index b9772949ad..0000000000 --- a/test/js/node/test/parallel/event-emitter-listener-count.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-event-emitter-listener-count.js -//#SHA1: 0f4b7f14fe432472b51524b3853db6d8615614f1 -//----------------- -"use strict"; - -const EventEmitter = require("events"); - -describe("EventEmitter.listenerCount and emitter.listenerCount", () => { - let emitter; - - beforeEach(() => { - emitter = new EventEmitter(); - emitter.on("foo", () => {}); - emitter.on("foo", () => {}); - emitter.on("baz", () => {}); - // Allow any type - emitter.on(123, () => {}); - }); - - test("EventEmitter.listenerCount returns correct count", () => { - expect(EventEmitter.listenerCount(emitter, "foo")).toBe(2); - }); - - test("emitter.listenerCount returns correct counts for various events", () => { - expect(emitter.listenerCount("foo")).toBe(2); - expect(emitter.listenerCount("bar")).toBe(0); - expect(emitter.listenerCount("baz")).toBe(1); - expect(emitter.listenerCount(123)).toBe(1); - }); -}); - -//<#END_FILE: test-event-emitter-listener-count.js diff --git a/test/js/node/test/parallel/event-emitter-modify-in-emit.test.js b/test/js/node/test/parallel/event-emitter-modify-in-emit.test.js deleted file mode 100644 index 422054923c..0000000000 --- a/test/js/node/test/parallel/event-emitter-modify-in-emit.test.js +++ /dev/null @@ -1,89 +0,0 @@ -//#FILE: test-event-emitter-modify-in-emit.js -//#SHA1: 6d378f9c7700ce7946f7d59bbc32b7cb82efc836 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const events = require("events"); - -describe("EventEmitter modify in emit", () => { - let callbacks_called; - let e; - - function callback1() { - callbacks_called.push("callback1"); - e.on("foo", callback2); - e.on("foo", callback3); - e.removeListener("foo", callback1); - } - - function callback2() { - callbacks_called.push("callback2"); - e.removeListener("foo", callback2); - } - - function callback3() { - callbacks_called.push("callback3"); - e.removeListener("foo", callback3); - } - - beforeEach(() => { - callbacks_called = []; - e = new events.EventEmitter(); - }); - - test("listeners are modified during emit", () => { - e.on("foo", callback1); - expect(e.listeners("foo").length).toBe(1); - - e.emit("foo"); - expect(e.listeners("foo").length).toBe(2); - expect(callbacks_called).toEqual(["callback1"]); - - e.emit("foo"); - expect(e.listeners("foo").length).toBe(0); - expect(callbacks_called).toEqual(["callback1", "callback2", "callback3"]); - - e.emit("foo"); - expect(e.listeners("foo").length).toBe(0); - expect(callbacks_called).toEqual(["callback1", "callback2", "callback3"]); - }); - - test("removeAllListeners removes all listeners", () => { - e.on("foo", callback1); - e.on("foo", callback2); - expect(e.listeners("foo").length).toBe(2); - e.removeAllListeners("foo"); - expect(e.listeners("foo").length).toBe(0); - }); - - test("removing callbacks during emit allows emits to propagate to all listeners", () => { - e.on("foo", callback2); - e.on("foo", callback3); - expect(e.listeners("foo").length).toBe(2); - e.emit("foo"); - expect(callbacks_called).toEqual(["callback2", "callback3"]); - expect(e.listeners("foo").length).toBe(0); - }); -}); - -//<#END_FILE: test-event-emitter-modify-in-emit.js diff --git a/test/js/node/test/parallel/event-emitter-num-args.test.js b/test/js/node/test/parallel/event-emitter-num-args.test.js deleted file mode 100644 index e295eb022c..0000000000 --- a/test/js/node/test/parallel/event-emitter-num-args.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-event-emitter-num-args.js -//#SHA1: b17b5bfd071180f4c53c8c408dc14ec860fd4225 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const events = require("events"); - -describe("EventEmitter number of arguments", () => { - let e; - let num_args_emitted; - - beforeEach(() => { - e = new events.EventEmitter(); - num_args_emitted = []; - - e.on("numArgs", function () { - const numArgs = arguments.length; - num_args_emitted.push(numArgs); - }); - - e.on("foo", function () { - num_args_emitted.push(arguments.length); - }); - - e.on("foo", function () { - num_args_emitted.push(arguments.length); - }); - }); - - it("should emit correct number of arguments", () => { - e.emit("numArgs"); - e.emit("numArgs", null); - e.emit("numArgs", null, null); - e.emit("numArgs", null, null, null); - e.emit("numArgs", null, null, null, null); - e.emit("numArgs", null, null, null, null, null); - - e.emit("foo", null, null, null, null); - - expect(num_args_emitted).toEqual([0, 1, 2, 3, 4, 5, 4, 4]); - }); -}); - -//<#END_FILE: test-event-emitter-num-args.js diff --git a/test/js/node/test/parallel/event-emitter-prepend.test.js b/test/js/node/test/parallel/event-emitter-prepend.test.js deleted file mode 100644 index f8f7d47b8c..0000000000 --- a/test/js/node/test/parallel/event-emitter-prepend.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-event-emitter-prepend.js -//#SHA1: 9a753f44fc304ff6584a31c20e95037e622579d7 -//----------------- -"use strict"; - -const EventEmitter = require("events"); -const stream = require("stream"); - -describe("EventEmitter prepend", () => { - test("prepend listeners in correct order", () => { - const myEE = new EventEmitter(); - let m = 0; - - // This one comes last. - myEE.on("foo", () => { - expect(m).toBe(2); - }); - - // This one comes second. - myEE.prependListener("foo", () => { - expect(m).toBe(1); - m++; - }); - - // This one comes first. - myEE.prependOnceListener("foo", () => { - expect(m).toBe(0); - m++; - }); - - myEE.emit("foo"); - expect.assertions(3); - }); - - test("fallback if prependListener is undefined", () => { - // Test fallback if prependListener is undefined. - delete EventEmitter.prototype.prependListener; - - function Writable() { - this.writable = true; - stream.Stream.call(this); - } - Object.setPrototypeOf(Writable.prototype, stream.Stream.prototype); - Object.setPrototypeOf(Writable, stream.Stream); - - function Readable() { - this.readable = true; - stream.Stream.call(this); - } - Object.setPrototypeOf(Readable.prototype, stream.Stream.prototype); - Object.setPrototypeOf(Readable, stream.Stream); - - const w = new Writable(); - const r = new Readable(); - r.pipe(w); - - // If we reach this point without throwing, the test passes - expect(true).toBe(true); - }); -}); - -//<#END_FILE: test-event-emitter-prepend.js diff --git a/test/js/node/test/parallel/event-emitter-set-max-listeners-side-effects.test.js b/test/js/node/test/parallel/event-emitter-set-max-listeners-side-effects.test.js deleted file mode 100644 index 101ccfa80b..0000000000 --- a/test/js/node/test/parallel/event-emitter-set-max-listeners-side-effects.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-event-emitter-set-max-listeners-side-effects.js -//#SHA1: 319371e261c5cc46348475cdd789eb9ee56839d8 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const events = require("events"); - -test("EventEmitter setMaxListeners should not have side effects", () => { - const e = new events.EventEmitter(); - - expect(e._events).not.toBeInstanceOf(Object); - expect(Object.keys(e._events)).toEqual([]); - e.setMaxListeners(5); - expect(Object.keys(e._events)).toEqual([]); -}); - -//<#END_FILE: test-event-emitter-set-max-listeners-side-effects.js diff --git a/test/js/node/test/parallel/event-emitter-special-event-names.test.js b/test/js/node/test/parallel/event-emitter-special-event-names.test.js deleted file mode 100644 index 60d8071a33..0000000000 --- a/test/js/node/test/parallel/event-emitter-special-event-names.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-event-emitter-special-event-names.js -//#SHA1: 3ae93e3a9f5cd01560264a5c297a2fae25c5c18f -//----------------- -"use strict"; - -const EventEmitter = require("events"); - -describe("EventEmitter with special event names", () => { - let ee; - - beforeEach(() => { - ee = new EventEmitter(); - }); - - test("initial eventNames() is empty", () => { - expect(ee.eventNames()).toEqual([]); - }); - - test("_events does not have hasOwnProperty or toString", () => { - expect(ee._events.hasOwnProperty).toBeUndefined(); - expect(ee._events.toString).toBeUndefined(); - }); - - test("can add and list special event names", () => { - const handler = jest.fn(); - - ee.on("__proto__", handler); - ee.on("__defineGetter__", handler); - ee.on("toString", handler); - - expect(ee.eventNames()).toEqual(["__proto__", "__defineGetter__", "toString"]); - - expect(ee.listeners("__proto__")).toEqual([handler]); - expect(ee.listeners("__defineGetter__")).toEqual([handler]); - expect(ee.listeners("toString")).toEqual([handler]); - }); - - test("can emit __proto__ event", () => { - const handler = jest.fn(); - ee.on("__proto__", handler); - ee.emit("__proto__", 1); - expect(handler).toHaveBeenCalledWith(1); - }); - - test("process can emit __proto__ event", () => { - const handler = jest.fn(); - process.on("__proto__", handler); - process.emit("__proto__", 1); - expect(handler).toHaveBeenCalledWith(1); - }); -}); - -//<#END_FILE: test-event-emitter-special-event-names.js diff --git a/test/js/node/test/parallel/event-emitter-subclass.test.js b/test/js/node/test/parallel/event-emitter-subclass.test.js deleted file mode 100644 index a48d9e8fdd..0000000000 --- a/test/js/node/test/parallel/event-emitter-subclass.test.js +++ /dev/null @@ -1,81 +0,0 @@ -//#FILE: test-event-emitter-subclass.js -//#SHA1: e7f7e379fcf89318168b9e5d9f38c4af371ada8e -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const EventEmitter = require("events").EventEmitter; - -Object.setPrototypeOf(MyEE.prototype, EventEmitter.prototype); -Object.setPrototypeOf(MyEE, EventEmitter); - -function MyEE(cb) { - this.once(1, cb); - this.emit(1); - this.removeAllListeners(); - EventEmitter.call(this); -} - -test("MyEE callback is called", () => { - const callback = jest.fn(); - const myee = new MyEE(callback); - expect(callback).toHaveBeenCalledTimes(1); -}); - -Object.setPrototypeOf(ErrorEE.prototype, EventEmitter.prototype); -Object.setPrototypeOf(ErrorEE, EventEmitter); -function ErrorEE() { - this.emit("error", new Error("blerg")); -} - -test("ErrorEE throws error", () => { - expect(() => { - new ErrorEE(); - }).toThrow( - expect.objectContaining({ - message: expect.any(String), - }), - ); -}); - -test("MyEE _events is empty after removeAllListeners", () => { - const myee = new MyEE(() => {}); - expect(myee._events).not.toBeInstanceOf(Object); - expect(Object.keys(myee._events)).toEqual([]); -}); - -function MyEE2() { - EventEmitter.call(this); -} - -MyEE2.prototype = new EventEmitter(); - -test("MyEE2 instances do not share listeners", () => { - const ee1 = new MyEE2(); - const ee2 = new MyEE2(); - - ee1.on("x", () => {}); - - expect(ee2.listenerCount("x")).toBe(0); -}); - -//<#END_FILE: test-event-emitter-subclass.js diff --git a/test/js/node/test/parallel/event-emitter-symbols.test.js b/test/js/node/test/parallel/event-emitter-symbols.test.js deleted file mode 100644 index bfb0e2b774..0000000000 --- a/test/js/node/test/parallel/event-emitter-symbols.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-event-emitter-symbols.js -//#SHA1: c3fa4a8db31f2a88317d3c60fb52aa2921eaa20d -//----------------- -"use strict"; - -const EventEmitter = require("events"); - -test("EventEmitter with Symbol events", () => { - const ee = new EventEmitter(); - const foo = Symbol("foo"); - const listener = jest.fn(); - - ee.on(foo, listener); - expect(ee.listeners(foo)).toEqual([listener]); - - ee.emit(foo); - expect(listener).toHaveBeenCalledTimes(1); - - ee.removeAllListeners(); - expect(ee.listeners(foo)).toEqual([]); - - ee.on(foo, listener); - expect(ee.listeners(foo)).toEqual([listener]); - - ee.removeListener(foo, listener); - expect(ee.listeners(foo)).toEqual([]); -}); - -//<#END_FILE: test-event-emitter-symbols.js diff --git a/test/js/node/test/parallel/event-target.test.js b/test/js/node/test/parallel/event-target.test.js deleted file mode 100644 index 0b5fc40712..0000000000 --- a/test/js/node/test/parallel/event-target.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-event-target.js -//#SHA1: 3e70912640aa5270e13723e1895636bfd238420a -//----------------- -"use strict"; - -const eventPhases = { - NONE: 0, - CAPTURING_PHASE: 1, - AT_TARGET: 2, - BUBBLING_PHASE: 3, -}; - -describe("Event phases", () => { - test.each(Object.entries(eventPhases))("Event.%s should be %d", (prop, value) => { - // Check if the value of the property matches the expected value - expect(Event[prop]).toBe(value); - - const desc = Object.getOwnPropertyDescriptor(Event, prop); - expect(desc.writable).toBe(false); - expect(desc.configurable).toBe(false); - expect(desc.enumerable).toBe(true); - }); -}); - -//<#END_FILE: test-event-target.js diff --git a/test/js/node/test/parallel/events-list.test.js b/test/js/node/test/parallel/events-list.test.js deleted file mode 100644 index bde2b1a0e6..0000000000 --- a/test/js/node/test/parallel/events-list.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-events-list.js -//#SHA1: 946973d6c9e19c6410b4486cbef3e2ed032715fc -//----------------- -"use strict"; - -const EventEmitter = require("events"); - -test("EventEmitter.eventNames()", () => { - const EE = new EventEmitter(); - const m = () => {}; - - EE.on("foo", () => {}); - expect(EE.eventNames()).toEqual(["foo"]); - - EE.on("bar", m); - expect(EE.eventNames()).toEqual(["foo", "bar"]); - - EE.removeListener("bar", m); - expect(EE.eventNames()).toEqual(["foo"]); - - const s = Symbol("s"); - EE.on(s, m); - expect(EE.eventNames()).toEqual(["foo", s]); - - EE.removeListener(s, m); - expect(EE.eventNames()).toEqual(["foo"]); -}); - -//<#END_FILE: test-events-list.js diff --git a/test/js/node/test/parallel/events-on-async-iterator.test.js b/test/js/node/test/parallel/events-on-async-iterator.test.js deleted file mode 100644 index eb67f58c5b..0000000000 --- a/test/js/node/test/parallel/events-on-async-iterator.test.js +++ /dev/null @@ -1,417 +0,0 @@ -import { test, expect } from "bun:test"; -const common = require("../common"); -const assert = require("assert"); -const { on, EventEmitter, listenerCount } = require("events"); - -test("basic", async () => { - const ee = new EventEmitter(); - process.nextTick(() => { - ee.emit("foo", "bar"); - // 'bar' is a spurious event, we are testing - // that it does not show up in the iterable - ee.emit("bar", 24); - ee.emit("foo", 42); - }); - - const iterable = on(ee, "foo"); - - const expected = [["bar"], [42]]; - - for await (const event of iterable) { - const current = expected.shift(); - - expect(current).toStrictEqual(event); - - if (expected.length === 0) { - break; - } - } - expect(ee.listenerCount("foo")).toBe(0); - expect(ee.listenerCount("error")).toBe(0); -}); - -test("invalidArgType", async () => { - assert.throws( - () => on({}, "foo"), - common.expectsError({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - const ee = new EventEmitter(); - - [1, "hi", null, false, () => {}, Symbol(), 1n].map(options => { - return assert.throws( - () => on(ee, "foo", options), - common.expectsError({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - }); -}); - -test("error", async () => { - const ee = new EventEmitter(); - const _err = new Error("kaboom"); - process.nextTick(() => { - ee.emit("error", _err); - }); - - const iterable = on(ee, "foo"); - let looped = false; - let thrown = false; - - try { - // eslint-disable-next-line no-unused-vars - for await (const event of iterable) { - looped = true; - } - } catch (err) { - thrown = true; - expect(err).toStrictEqual(_err); - } - expect(thrown).toBe(true); - expect(looped).toBe(false); -}); - -test("errorDelayed", async () => { - const ee = new EventEmitter(); - const _err = new Error("kaboom"); - process.nextTick(() => { - ee.emit("foo", 42); - ee.emit("error", _err); - }); - - const iterable = on(ee, "foo"); - const expected = [[42]]; - let thrown = false; - - try { - for await (const event of iterable) { - const current = expected.shift(); - assert.deepStrictEqual(current, event); - } - } catch (err) { - thrown = true; - expect(err).toStrictEqual(_err); - } - expect(thrown).toBe(true); - expect(ee.listenerCount("foo")).toBe(0); - expect(ee.listenerCount("error")).toBe(0); -}); - -test("throwInLoop", async () => { - const ee = new EventEmitter(); - const _err = new Error("kaboom"); - - process.nextTick(() => { - ee.emit("foo", 42); - }); - - try { - for await (const event of on(ee, "foo")) { - assert.deepStrictEqual(event, [42]); - throw _err; - } - } catch (err) { - expect(err).toStrictEqual(_err); - } - - expect(ee.listenerCount("foo")).toBe(0); - expect(ee.listenerCount("error")).toBe(0); -}); - -test("next", async () => { - const ee = new EventEmitter(); - const iterable = on(ee, "foo"); - - process.nextTick(function () { - ee.emit("foo", "bar"); - ee.emit("foo", 42); - iterable.return(); - }); - - const results = await Promise.all([iterable.next(), iterable.next(), iterable.next()]); - - expect(results).toStrictEqual([ - { - value: ["bar"], - done: false, - }, - { - value: [42], - done: false, - }, - { - value: undefined, - done: true, - }, - ]); - - expect(await iterable.next()).toStrictEqual({ - value: undefined, - done: true, - }); -}); - -test("nextError", async () => { - const ee = new EventEmitter(); - const iterable = on(ee, "foo"); - const _err = new Error("kaboom"); - process.nextTick(function () { - ee.emit("error", _err); - }); - const results = await Promise.allSettled([iterable.next(), iterable.next(), iterable.next()]); - assert.deepStrictEqual(results, [ - { - status: "rejected", - reason: _err, - }, - { - status: "fulfilled", - value: { - value: undefined, - done: true, - }, - }, - { - status: "fulfilled", - value: { - value: undefined, - done: true, - }, - }, - ]); - expect(ee.listeners("error").length).toBe(0); -}); - -test("iterableThrow", async () => { - const ee = new EventEmitter(); - const iterable = on(ee, "foo"); - - process.nextTick(() => { - ee.emit("foo", "bar"); - ee.emit("foo", 42); // lost in the queue - iterable.throw(_err); - }); - - const _err = new Error("kaboom"); - let thrown = false; - - assert.throws( - () => { - // No argument - iterable.throw(); - }, - { - message: 'The "EventEmitter.AsyncIterator" argument must be' + " of type Error. Received: undefined", - name: "TypeError", - }, - ); - - const expected = [["bar"], [42]]; - - try { - for await (const event of iterable) { - assert.deepStrictEqual(event, expected.shift()); - } - } catch (err) { - thrown = true; - assert.strictEqual(err, _err); - } - assert.strictEqual(thrown, true); - assert.strictEqual(expected.length, 0); - assert.strictEqual(ee.listenerCount("foo"), 0); - assert.strictEqual(ee.listenerCount("error"), 0); -}); - -test("eventTarget", async () => { - const et = new EventTarget(); - const tick = () => et.dispatchEvent(new Event("tick")); - const interval = setInterval(tick, 0); - let count = 0; - for await (const [event] of on(et, "tick")) { - count++; - assert.strictEqual(event.type, "tick"); - if (count >= 5) { - break; - } - } - assert.strictEqual(count, 5); - clearInterval(interval); -}); - -test("errorListenerCount", async () => { - const et = new EventEmitter(); - on(et, "foo"); - assert.strictEqual(et.listenerCount("error"), 1); -}); - -test.skip("nodeEventTarget", async () => { - const et = new NodeEventTarget(); - const tick = () => et.dispatchEvent(new Event("tick")); - const interval = setInterval(tick, 0); - let count = 0; - for await (const [event] of on(et, "tick")) { - count++; - assert.strictEqual(event.type, "tick"); - if (count >= 5) { - break; - } - } - assert.strictEqual(count, 5); - clearInterval(interval); -}); - -test("abortableOnBefore", async () => { - const ee = new EventEmitter(); - const abortedSignal = AbortSignal.abort(); - [1, {}, null, false, "hi"].forEach(signal => { - assert.throws(() => on(ee, "foo", { signal }), { - code: "ERR_INVALID_ARG_TYPE", - }); - }); - assert.throws(() => on(ee, "foo", { signal: abortedSignal }), { - name: "AbortError", - }); -}); - -test("eventTargetAbortableOnBefore", async () => { - const et = new EventTarget(); - const abortedSignal = AbortSignal.abort(); - [1, {}, null, false, "hi"].forEach(signal => { - assert.throws(() => on(et, "foo", { signal }), { - code: "ERR_INVALID_ARG_TYPE", - }); - }); - assert.throws(() => on(et, "foo", { signal: abortedSignal }), { - name: "AbortError", - }); -}); - -test("abortableOnAfter", async () => { - const ee = new EventEmitter(); - const ac = new AbortController(); - - const i = setInterval(() => ee.emit("foo", "foo"), 10); - - async function foo() { - for await (const f of on(ee, "foo", { signal: ac.signal })) { - assert.strictEqual(f, "foo"); - } - } - - foo() - .catch( - common.mustCall(error => { - assert.strictEqual(error.name, "AbortError"); - }), - ) - .finally(() => { - clearInterval(i); - }); - - process.nextTick(() => ac.abort()); -}); - -test("eventTargetAbortableOnAfter", async () => { - const et = new EventTarget(); - const ac = new AbortController(); - - const i = setInterval(() => et.dispatchEvent(new Event("foo")), 10); - - async function foo() { - for await (const f of on(et, "foo", { signal: ac.signal })) { - assert(f); - } - } - - foo() - .catch( - common.mustCall(error => { - assert.strictEqual(error.name, "AbortError"); - }), - ) - .finally(() => { - clearInterval(i); - }); - - process.nextTick(() => ac.abort()); -}); - -test("eventTargetAbortableOnAfter2", async () => { - const et = new EventTarget(); - const ac = new AbortController(); - - const i = setInterval(() => et.dispatchEvent(new Event("foo")), 10); - - async function foo() { - for await (const f of on(et, "foo", { signal: ac.signal })) { - assert(f); - // Cancel after a single event has been triggered. - ac.abort(); - } - } - - foo() - .catch( - common.mustCall(error => { - assert.strictEqual(error.name, "AbortError"); - }), - ) - .finally(() => { - clearInterval(i); - }); -}); - -test("abortableOnAfterDone", async () => { - const ee = new EventEmitter(); - const ac = new AbortController(); - - const i = setInterval(() => ee.emit("foo", "foo"), 1); - let count = 0; - - async function foo() { - for await (const f of on(ee, "foo", { signal: ac.signal })) { - assert.strictEqual(f[0], "foo"); - if (++count === 5) break; - } - ac.abort(); // No error will occur - } - - foo().finally(() => { - clearInterval(i); - }); -}); - -test("abortListenerRemovedAfterComplete", async () => { - const ee = new EventEmitter(); - const ac = new AbortController(); - - const i = setInterval(() => ee.emit("foo", "foo"), 1); - try { - // Below: either the kEvents map is empty or the 'abort' listener list is empty - - // Return case - const endedIterator = on(ee, "foo", { signal: ac.signal }); - expect(listenerCount(ac.signal, "abort")).toBeGreaterThan(0); - endedIterator.return(); - expect(listenerCount(ac.signal, "abort")).toBe(0); - - // Throw case - const throwIterator = on(ee, "foo", { signal: ac.signal }); - expect(listenerCount(ac.signal, "abort")).toBeGreaterThan(0); - throwIterator.throw(new Error()); - expect(listenerCount(ac.signal, "abort")).toBe(0); - - // Abort case - on(ee, "foo", { signal: ac.signal }); - expect(listenerCount(ac.signal, "abort")).toBeGreaterThan(0); - ac.abort(new Error()); - expect(listenerCount(ac.signal, "abort")).toBe(0); - } finally { - clearInterval(i); - } -}); diff --git a/test/js/node/test/parallel/events-once.test.js b/test/js/node/test/parallel/events-once.test.js deleted file mode 100644 index 3e0c4c9ecb..0000000000 --- a/test/js/node/test/parallel/events-once.test.js +++ /dev/null @@ -1,254 +0,0 @@ -import { test, expect } from "bun:test"; -const { once, EventEmitter, listenerCount } = require("events"); -const { deepStrictEqual, fail, rejects, strictEqual } = require("assert"); -// const { kEvents } = require("internal/event_target"); - -test("onceAnEvent", async () => { - const ee = new EventEmitter(); - - process.nextTick(() => { - ee.emit("myevent", 42); - }); - - const [value] = await once(ee, "myevent"); - strictEqual(value, 42); - strictEqual(ee.listenerCount("error"), 0); - strictEqual(ee.listenerCount("myevent"), 0); -}); - -test("onceAnEventWithInvalidOptions", async () => { - const ee = new EventEmitter(); - - await Promise.all( - [1, "hi", null, false, () => {}, Symbol(), 1n].map(options => { - expect.toThrowWithCode(() => once(ee, "myevent", options), "ERR_INVALID_ARG_TYPE"); - }), - ); -}); - -test("onceAnEventWithTwoArgs", async () => { - const ee = new EventEmitter(); - - process.nextTick(() => { - ee.emit("myevent", 42, 24); - }); - - const value = await once(ee, "myevent"); - deepStrictEqual(value, [42, 24]); -}); - -test("catchesErrors", async () => { - const ee = new EventEmitter(); - - const expected = new Error("kaboom"); - let err; - process.nextTick(() => { - ee.emit("error", expected); - }); - - try { - await once(ee, "myevent"); - } catch (_e) { - err = _e; - } - strictEqual(err, expected); - strictEqual(ee.listenerCount("error"), 0); - strictEqual(ee.listenerCount("myevent"), 0); -}); - -test("catchesErrorsWithAbortSignal", async () => { - const ee = new EventEmitter(); - const ac = new AbortController(); - const signal = ac.signal; - - const expected = new Error("boom"); - let err; - process.nextTick(() => { - ee.emit("error", expected); - }); - - try { - const promise = once(ee, "myevent", { signal }); - strictEqual(ee.listenerCount("error"), 1); - strictEqual(listenerCount(signal, "abort"), 1); - - await promise; - } catch (e) { - err = e; - } - strictEqual(err, expected); - strictEqual(ee.listenerCount("error"), 0); - strictEqual(ee.listenerCount("myevent"), 0); - strictEqual(listenerCount(signal, "abort"), 0); -}); - -test("stopListeningAfterCatchingError", async () => { - const ee = new EventEmitter(); - - const expected = new Error("kaboom"); - let err; - process.nextTick(() => { - ee.emit("error", expected); - ee.emit("myevent", 42, 24); - }); - - try { - await once(ee, "myevent"); - } catch (_e) { - err = _e; - } - process.removeAllListeners("multipleResolves"); - strictEqual(err, expected); - strictEqual(ee.listenerCount("error"), 0); - strictEqual(ee.listenerCount("myevent"), 0); -}); - -test("onceError", async () => { - const ee = new EventEmitter(); - - const expected = new Error("kaboom"); - process.nextTick(() => { - ee.emit("error", expected); - }); - - const promise = once(ee, "error"); - strictEqual(ee.listenerCount("error"), 1); - const [err] = await promise; - strictEqual(err, expected); - strictEqual(ee.listenerCount("error"), 0); - strictEqual(ee.listenerCount("myevent"), 0); -}); - -test("onceWithEventTarget", async () => { - const et = new EventTarget(); - const event = new Event("myevent"); - process.nextTick(() => { - et.dispatchEvent(event); - }); - const [value] = await once(et, "myevent"); - strictEqual(value, event); -}); - -test("onceWithEventTargetError", async () => { - const et = new EventTarget(); - const error = new Event("error"); - process.nextTick(() => { - et.dispatchEvent(error); - }); - - const [err] = await once(et, "error"); - strictEqual(err, error); -}); - -test("onceWithInvalidEventEmmiter", async () => { - const ac = new AbortController(); - expect.toThrowWithCode(() => once(ac, "myevent"), "ERR_INVALID_ARG_TYPE"); -}); - -test("prioritizesEventEmitter", async () => { - const ee = new EventEmitter(); - ee.addEventListener = fail; - ee.removeAllListeners = fail; - process.nextTick(() => ee.emit("foo")); - await once(ee, "foo"); -}); - -test("abortSignalBefore", async () => { - const ee = new EventEmitter(); - ee.on("error", () => expect(false).toEqual(true)); - const abortedSignal = AbortSignal.abort(); - - await Promise.all( - [1, {}, "hi", null, false].map(signal => { - expect.toThrowWithCode(() => once(ee, "foo", { signal }), "ERR_INVALID_ARG_TYPE"); - }), - ); - - expect(() => once(ee, "foo", { signal: abortedSignal })).toThrow(); -}); - -test("abortSignalAfter", async () => { - const ee = new EventEmitter(); - const ac = new AbortController(); - ee.on("error", () => expect(false).toEqual(true)); - const r = rejects(once(ee, "foo", { signal: ac.signal }), { - name: "AbortError", - }); - process.nextTick(() => ac.abort()); - return r; -}); - -test("abortSignalAfterEvent", async () => { - const ee = new EventEmitter(); - const ac = new AbortController(); - process.nextTick(() => { - ee.emit("foo"); - ac.abort(); - }); - const promise = once(ee, "foo", { signal: ac.signal }); - strictEqual(listenerCount(ac.signal, "abort"), 1); - await promise; - strictEqual(listenerCount(ac.signal, "abort"), 0); -}); - -test("abortSignalRemoveListener", async () => { - const ee = new EventEmitter(); - const ac = new AbortController(); - - try { - process.nextTick(() => ac.abort()); - await once(ee, "test", { signal: ac.signal }); - } catch { - strictEqual(ee.listeners("test").length, 0); - strictEqual(ee.listeners("error").length, 0); - } -}); - -test.skip("eventTargetAbortSignalBefore", async () => { - const et = new EventTarget(); - const abortedSignal = AbortSignal.abort(); - - await Promise.all( - [1, {}, "hi", null, false].map(signal => { - return rejects(once(et, "foo", { signal }), { - code: "ERR_INVALID_ARG_TYPE", - }); - }), - ); - - return rejects(once(et, "foo", { signal: abortedSignal }), { - name: "AbortError", - }); -}); - -test.skip("eventTargetAbortSignalBeforeEvenWhenSignalPropagationStopped", async () => { - const et = new EventTarget(); - const ac = new AbortController(); - const { signal } = ac; - signal.addEventListener("abort", e => e.stopImmediatePropagation(), { once: true }); - - process.nextTick(() => ac.abort()); - return rejects(once(et, "foo", { signal }), { - name: "AbortError", - }); -}); - -test("eventTargetAbortSignalAfter", async () => { - const et = new EventTarget(); - const ac = new AbortController(); - const r = rejects(once(et, "foo", { signal: ac.signal }), { - name: "AbortError", - }); - process.nextTick(() => ac.abort()); - return r; -}); - -test("eventTargetAbortSignalAfterEvent", async () => { - const et = new EventTarget(); - const ac = new AbortController(); - process.nextTick(() => { - et.dispatchEvent(new Event("foo")); - ac.abort(); - }); - await once(et, "foo", { signal: ac.signal }); -}); diff --git a/test/js/node/test/parallel/eventsource-disabled.test.js b/test/js/node/test/parallel/eventsource-disabled.test.js deleted file mode 100644 index 0b7c76c1e2..0000000000 --- a/test/js/node/test/parallel/eventsource-disabled.test.js +++ /dev/null @@ -1,10 +0,0 @@ -//#FILE: test-eventsource-disabled.js -//#SHA1: ebb7581b56a8dd86c5385eba1befa9ef984a8065 -//----------------- -"use strict"; - -test("EventSource is undefined", () => { - expect(typeof EventSource).toBe("undefined"); -}); - -//<#END_FILE: test-eventsource-disabled.js diff --git a/test/js/node/test/parallel/eventtarget-once-twice.test.js b/test/js/node/test/parallel/eventtarget-once-twice.test.js deleted file mode 100644 index 74f19e01b0..0000000000 --- a/test/js/node/test/parallel/eventtarget-once-twice.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-eventtarget-once-twice.js -//#SHA1: dd2fea0f3c839d77c64423ed3c2cbdd319365141 -//----------------- -"use strict"; - -const { once } = require("events"); - -test("once can be called twice on EventTarget", async () => { - const et = new EventTarget(); - - const promise = (async () => { - await once(et, "foo"); - await once(et, "foo"); - })(); - - et.dispatchEvent(new Event("foo")); - setImmediate(() => { - et.dispatchEvent(new Event("foo")); - }); - - await expect(promise).resolves.toBeUndefined(); -}); - -//<#END_FILE: test-eventtarget-once-twice.js diff --git a/test/js/node/test/parallel/fetch-mock.test.js b/test/js/node/test/parallel/fetch-mock.test.js deleted file mode 100644 index 84bce7c9a7..0000000000 --- a/test/js/node/test/parallel/fetch-mock.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-fetch-mock.js -//#SHA1: 92268be36b00e63e18a837088b9718344c3bff4f -//----------------- -"use strict"; - -test("should correctly stub globalThis.fetch", async () => { - const customFetch = async url => { - return { - text: async () => "foo", - }; - }; - - const originalFetch = globalThis.fetch; - globalThis.fetch = jest.fn(customFetch); - - const response = await globalThis.fetch("some-url"); - const text = await response.text(); - - expect(text).toBe("foo"); - expect(globalThis.fetch).toHaveBeenCalledWith("some-url"); - - // Restore the original fetch - globalThis.fetch = originalFetch; -}); - -//<#END_FILE: test-fetch-mock.js diff --git a/test/js/node/test/parallel/file-read-noexist.test.js b/test/js/node/test/parallel/file-read-noexist.test.js deleted file mode 100644 index 199afac796..0000000000 --- a/test/js/node/test/parallel/file-read-noexist.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-file-read-noexist.js -//#SHA1: c4cbb5dc6abca53c1dd513a4ece2741c1fc2235a -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const fs = require("fs"); -const path = require("path"); - -const filename = path.join(__dirname, "fixtures", "does_not_exist.txt"); - -test("fs.readFile with non-existent file", async () => { - await expect( - new Promise((resolve, reject) => { - fs.readFile(filename, "latin1", (err, content) => { - if (err) reject(err); - else resolve(content); - }); - }), - ).rejects.toMatchObject({ - code: "ENOENT", - message: expect.any(String), - }); -}); - -//<#END_FILE: test-file-read-noexist.js diff --git a/test/js/node/test/parallel/finalization-registry-shutdown.test.js b/test/js/node/test/parallel/finalization-registry-shutdown.test.js deleted file mode 100644 index 9dfd3c99c2..0000000000 --- a/test/js/node/test/parallel/finalization-registry-shutdown.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-finalization-registry-shutdown.js -//#SHA1: c5da440f8ea977c501bb5920f55ce69b1da6e28d -//----------------- -// Flags: --expose-gc -"use strict"; - -// This test verifies that when a V8 FinalizationRegistryCleanupTask is queue -// at the last moment when JavaScript can be executed, the callback of a -// FinalizationRegistry will not be invoked and the process should exit -// normally. - -test("FinalizationRegistry callback should not be called during shutdown", () => { - const mockCallback = jest.fn(); - const reg = new FinalizationRegistry(mockCallback); - - function register() { - // Create a temporary object in a new function scope to allow it to be GC-ed. - reg.register({}); - } - - const exitHandler = () => { - // This is the final chance to execute JavaScript. - register(); - // Queue a FinalizationRegistryCleanupTask by a testing gc request. - global.gc(); - }; - - process.on("exit", exitHandler); - - // Simulate the exit process - exitHandler(); - - // Verify that the callback was not called - expect(mockCallback).not.toHaveBeenCalled(); - - // Clean up - process.removeListener("exit", exitHandler); -}); - -//<#END_FILE: test-finalization-registry-shutdown.js diff --git a/test/js/node/test/parallel/fs-chown-type-check.test.js b/test/js/node/test/parallel/fs-chown-type-check.test.js deleted file mode 100644 index 4a899dd46e..0000000000 --- a/test/js/node/test/parallel/fs-chown-type-check.test.js +++ /dev/null @@ -1,60 +0,0 @@ -//#FILE: test-fs-chown-type-check.js -//#SHA1: 6ecbd3ae2da32793768e64e9252d749ddf36c85a -//----------------- -"use strict"; - -const fs = require("fs"); - -describe("fs.chown and fs.chownSync type checks", () => { - test("invalid path argument", () => { - [false, 1, {}, [], null, undefined].forEach(invalidPath => { - expect(() => fs.chown(invalidPath, 1, 1, jest.fn())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - expect(() => fs.chownSync(invalidPath, 1, 1)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - }); - }); - - test("invalid uid or gid arguments", () => { - [false, "test", {}, [], null, undefined].forEach(invalidId => { - expect(() => fs.chown("not_a_file_that_exists", invalidId, 1, jest.fn())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - expect(() => fs.chown("not_a_file_that_exists", 1, invalidId, jest.fn())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - expect(() => fs.chownSync("not_a_file_that_exists", invalidId, 1)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - expect(() => fs.chownSync("not_a_file_that_exists", 1, invalidId)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - }); - }); -}); - -//<#END_FILE: test-fs-chown-type-check.js diff --git a/test/js/node/test/parallel/fs-constants.test.js b/test/js/node/test/parallel/fs-constants.test.js deleted file mode 100644 index 0d1332c1bb..0000000000 --- a/test/js/node/test/parallel/fs-constants.test.js +++ /dev/null @@ -1,13 +0,0 @@ -//#FILE: test-fs-constants.js -//#SHA1: 6113ac0dd5e6b3d59252a25e6ddae61b589ca362 -//----------------- -"use strict"; - -const fs = require("fs"); - -test("fs constants for Windows chmod() are defined", () => { - expect(fs.constants.S_IRUSR).toBeDefined(); - expect(fs.constants.S_IWUSR).toBeDefined(); -}); - -//<#END_FILE: test-fs-constants.js diff --git a/test/js/node/test/parallel/fs-copyfile-respect-permissions.test.js b/test/js/node/test/parallel/fs-copyfile-respect-permissions.test.js deleted file mode 100644 index 39d72c9cd6..0000000000 --- a/test/js/node/test/parallel/fs-copyfile-respect-permissions.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-fs-copyfile-respect-permissions.js -//#SHA1: 5a5d15dd2a31fab3f8cffa86fcaedc7dab528c9f -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const isWindows = process.platform === 'win32'; -const isIBMi = process.platform === 'os400'; - -if (!isWindows && process.getuid() === 0) { - it.skip('should not run as root', () => {}); -} else if (isIBMi) { - it.skip('IBMi has a different access permission mechanism', () => {}); -} else { - const tmpdir = path.join(os.tmpdir(), 'test-fs-copyfile-respect-permissions'); - - beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); - }); - - afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); - }); - - let n = 0; - - function beforeEach() { - n++; - const source = path.join(tmpdir, `source${n}`); - const dest = path.join(tmpdir, `dest${n}`); - fs.writeFileSync(source, 'source'); - fs.writeFileSync(dest, 'dest'); - fs.chmodSync(dest, '444'); - - const check = (err) => { - const expected = ['EACCES', 'EPERM']; - expect(expected).toContain(err.code); - expect(fs.readFileSync(dest, 'utf8')).toBe('dest'); - return true; - }; - - return { source, dest, check }; - } - - test('synchronous API', () => { - const { source, dest, check } = beforeEach(); - expect(() => { fs.copyFileSync(source, dest); }).toThrow(expect.objectContaining({ - message: expect.any(String), - code: expect.stringMatching(/^(EACCES|EPERM)$/) - })); - }); - - test('promises API', async () => { - const { source, dest, check } = beforeEach(); - await expect(fs.promises.copyFile(source, dest)).rejects.toThrow(expect.objectContaining({ - message: expect.any(String), - code: expect.stringMatching(/^(EACCES|EPERM)$/) - })); - }); - - test('callback API', (done) => { - const { source, dest, check } = beforeEach(); - fs.copyFile(source, dest, (err) => { - expect(check(err)).toBe(true); - done(); - }); - }); -} - -//<#END_FILE: test-fs-copyfile-respect-permissions.js diff --git a/test/js/node/test/parallel/fs-empty-readstream.test.js b/test/js/node/test/parallel/fs-empty-readstream.test.js deleted file mode 100644 index cc018a90b7..0000000000 --- a/test/js/node/test/parallel/fs-empty-readstream.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-fs-empty-readStream.js -//#SHA1: 979f558eb3c86e2d897cb766be1f300bbb0cbf8c -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; -const fs = require('fs'); -const path = require('path'); - -const emptyFile = path.join(__dirname, '..', 'fixtures', 'empty.txt'); - -test('read stream on empty file should not emit data event', (done) => { - fs.open(emptyFile, 'r', (err, fd) => { - expect(err).toBeNull(); - const read = fs.createReadStream(emptyFile, { fd }); - - const dataHandler = jest.fn(); - read.once('data', dataHandler); - - read.once('end', () => { - expect(dataHandler).not.toHaveBeenCalled(); - done(); - }); - }); -}); - -test('paused read stream on empty file should not emit data or end events', (done) => { - fs.open(emptyFile, 'r', (err, fd) => { - expect(err).toBeNull(); - const read = fs.createReadStream(emptyFile, { fd }); - - read.pause(); - - const dataHandler = jest.fn(); - read.once('data', dataHandler); - - const endHandler = jest.fn(); - read.once('end', endHandler); - - setTimeout(() => { - expect(read.isPaused()).toBe(true); - expect(dataHandler).not.toHaveBeenCalled(); - expect(endHandler).not.toHaveBeenCalled(); - done(); - }, 50); - }); -}); - -//<#END_FILE: test-fs-empty-readStream.js diff --git a/test/js/node/test/parallel/fs-exists.test.js b/test/js/node/test/parallel/fs-exists.test.js deleted file mode 100644 index 3cea91fc1c..0000000000 --- a/test/js/node/test/parallel/fs-exists.test.js +++ /dev/null @@ -1,74 +0,0 @@ -//#FILE: test-fs-exists.js -//#SHA1: b7dcbc226b81e0ae4a111cbab39b87672a02172c -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const fs = require("fs"); -const f = __filename; - -test("fs.exists throws with invalid arguments", () => { - expect(() => fs.exists(f)).toThrow(expect.objectContaining({ code: "ERR_INVALID_ARG_TYPE" })); - expect(() => fs.exists()).toThrow(expect.objectContaining({ code: "ERR_INVALID_ARG_TYPE" })); - expect(() => fs.exists(f, {})).toThrow(expect.objectContaining({ code: "ERR_INVALID_ARG_TYPE" })); -}); - -test("fs.exists with valid file", done => { - fs.exists(f, y => { - expect(y).toBe(true); - done(); - }); -}); - -test("fs.exists with non-existent file", done => { - fs.exists(`${f}-NO`, y => { - expect(y).toBe(false); - done(); - }); -}); - -test("fs.exists with invalid path", done => { - fs.exists(new URL("https://foo"), y => { - expect(y).toBe(false); - done(); - }); -}); - -test("fs.exists with object as path", done => { - fs.exists({}, y => { - expect(y).toBe(false); - done(); - }); -}); - -test("fs.existsSync", () => { - expect(fs.existsSync(f)).toBe(true); - expect(fs.existsSync(`${f}-NO`)).toBe(false); -}); - -test("fs.existsSync never throws", () => { - expect(fs.existsSync()).toBe(false); - expect(fs.existsSync({})).toBe(false); - expect(fs.existsSync(new URL("https://foo"))).toBe(false); -}); - -//<#END_FILE: test-fs-exists.js diff --git a/test/js/node/test/parallel/fs-fsync.test.js b/test/js/node/test/parallel/fs-fsync.test.js deleted file mode 100644 index b4d17d9ac9..0000000000 --- a/test/js/node/test/parallel/fs-fsync.test.js +++ /dev/null @@ -1,79 +0,0 @@ -//#FILE: test-fs-fsync.js -//#SHA1: 4225be75eaedfd17c32e0472e6739ba232b7f28e -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const fileFixture = path.join(__dirname, '..', 'fixtures', 'a.js'); -const tmpdir = path.join(os.tmpdir(), 'test-fs-fsync'); -const fileTemp = path.join(tmpdir, 'a.js'); - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); - fs.copyFileSync(fileFixture, fileTemp); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test('fsync and fdatasync operations', (done) => { - fs.open(fileTemp, 'a', 0o777, (err, fd) => { - expect(err).toBeNull(); - - fs.fdatasyncSync(fd); - fs.fsyncSync(fd); - - fs.fdatasync(fd, (err) => { - expect(err).toBeNull(); - fs.fsync(fd, (err) => { - expect(err).toBeNull(); - fs.closeSync(fd); - done(); - }); - }); - }); -}); - -test('invalid inputs throw TypeError', () => { - const invalidInputs = ['', false, null, undefined, {}, []]; - const errObj = { - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError' - }; - - invalidInputs.forEach((input) => { - expect(() => fs.fdatasync(input)).toThrow(expect.objectContaining(errObj)); - expect(() => fs.fdatasyncSync(input)).toThrow(expect.objectContaining(errObj)); - expect(() => fs.fsync(input)).toThrow(expect.objectContaining(errObj)); - expect(() => fs.fsyncSync(input)).toThrow(expect.objectContaining(errObj)); - }); -}); - -//<#END_FILE: test-fs-fsync.js diff --git a/test/js/node/test/parallel/fs-link.test.js b/test/js/node/test/parallel/fs-link.test.js deleted file mode 100644 index 4b6be724f2..0000000000 --- a/test/js/node/test/parallel/fs-link.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-fs-link.js -//#SHA1: 255940f3f953a4bd693b3e475bc466d5f759875f -//----------------- -"use strict"; -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const tmpdir = { - refresh: () => { - // Implement a simple tmpdir.refresh() function - const testDir = path.join(os.tmpdir(), "test-fs-link"); - fs.rmSync(testDir, { recursive: true, force: true }); - fs.mkdirSync(testDir, { recursive: true }); - return testDir; - }, - resolve: filename => path.join(tmpdir.refresh(), filename), -}; - -test("Test creating and reading hard link", done => { - const srcPath = tmpdir.resolve("hardlink-target.txt"); - const dstPath = tmpdir.resolve("link1.js"); - fs.writeFileSync(srcPath, "hello world"); - - fs.link(srcPath, dstPath, err => { - expect(err).toBeFalsy(); - const dstContent = fs.readFileSync(dstPath, "utf8"); - expect(dstContent).toBe("hello world"); - done(); - }); -}); - -test("test error outputs", () => { - [false, 1, [], {}, null, undefined].forEach(i => { - expect(() => fs.link(i, "", () => {})).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - expect(() => fs.link("", i, () => {})).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - expect(() => fs.linkSync(i, "")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - expect(() => fs.linkSync("", i)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - }); -}); - -//<#END_FILE: test-fs-link.js diff --git a/test/js/node/test/parallel/fs-make-callback.test.js b/test/js/node/test/parallel/fs-make-callback.test.js deleted file mode 100644 index db3860cbd6..0000000000 --- a/test/js/node/test/parallel/fs-make-callback.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-fs-make-callback.js -//#SHA1: 40bbb673a0865f464a2b9e40110e903b6cc9e0d6 -//----------------- -"use strict"; - -const fs = require("fs"); -const { sep } = require("path"); -const tmpdir = require("../common/tmpdir"); - -const callbackThrowValues = [null, true, false, 0, 1, "foo", /foo/, [], {}]; - -beforeEach(() => { - tmpdir.refresh(); -}); - -function testMakeCallback(cb) { - return function () { - // fs.mkdtemp() calls makeCallback() on its third argument - return fs.mkdtemp(`${tmpdir.path}${sep}`, {}, cb); - }; -} - -describe("fs.makeCallback", () => { - test("invalid callbacks throw TypeError", () => { - callbackThrowValues.forEach(value => { - expect(testMakeCallback(value)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); - }); -}); - -//<#END_FILE: test-fs-make-callback.js diff --git a/test/js/node/test/parallel/fs-makestatscallback.test.js b/test/js/node/test/parallel/fs-makestatscallback.test.js deleted file mode 100644 index 839e3b4f45..0000000000 --- a/test/js/node/test/parallel/fs-makestatscallback.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-fs-makeStatsCallback.js -//#SHA1: e8c59eddd5ca920ba0a1aaa4dd87c3af879db3b1 -//----------------- -'use strict'; -const fs = require('fs'); - -function testMakeStatsCallback(cb) { - return function() { - // fs.stat() calls makeStatsCallback() on its second argument - fs.stat(__filename, cb); - }; -} - -test('Verify the case where a callback function is provided', (done) => { - testMakeStatsCallback(() => { - done(); - })(); -}); - -test('Invalid callback throws TypeError', () => { - const callbackThrowValues = [null, true, false, 0, 1, 'foo', /foo/, [], {}]; - - callbackThrowValues.forEach((value) => { - expect(testMakeStatsCallback(value)).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); - }); -}); - -//<#END_FILE: test-fs-makeStatsCallback.js diff --git a/test/js/node/test/parallel/fs-mkdir-recursive-eaccess.test.js b/test/js/node/test/parallel/fs-mkdir-recursive-eaccess.test.js deleted file mode 100644 index 570d2617ff..0000000000 --- a/test/js/node/test/parallel/fs-mkdir-recursive-eaccess.test.js +++ /dev/null @@ -1,81 +0,0 @@ -//#FILE: test-fs-mkdir-recursive-eaccess.js -//#SHA1: 1e0e4f480b7573549c130b4177759bd60adc1890 -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const { execSync } = require('child_process'); -const os = require('os'); - -const isWindows = process.platform === 'win32'; -const isIBMi = process.platform === 'os400'; - -if (isIBMi) { - console.log('Skipped: IBMi has a different access permission mechanism'); - process.exit(0); -} - -const tmpdir = path.join(os.tmpdir(), 'test-fs-mkdir-recursive-eaccess'); - -let n = 0; - -function makeDirectoryReadOnly(dir) { - let accessErrorCode = 'EACCES'; - if (isWindows) { - accessErrorCode = 'EPERM'; - execSync(`icacls ${dir} /deny "everyone:(OI)(CI)(DE,DC,AD,WD)"`); - } else { - fs.chmodSync(dir, '444'); - } - return accessErrorCode; -} - -function makeDirectoryWritable(dir) { - if (isWindows) { - execSync(`icacls ${dir} /remove:d "everyone"`); - } -} - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test('Synchronous API should return an EACCES/EPERM error with path populated', () => { - const dir = path.join(tmpdir, `mkdirp_${n++}`); - fs.mkdirSync(dir); - const codeExpected = makeDirectoryReadOnly(dir); - - expect(() => { - fs.mkdirSync(path.join(dir, '/foo'), { recursive: true }); - }).toThrow(expect.objectContaining({ - code: codeExpected, - path: expect.any(String) - })); - - makeDirectoryWritable(dir); -}); - -test('Asynchronous API should return an EACCES/EPERM error with path populated', (done) => { - const dir = path.join(tmpdir, `mkdirp_${n++}`); - fs.mkdirSync(dir); - const codeExpected = makeDirectoryReadOnly(dir); - - fs.mkdir(path.join(dir, '/bar'), { recursive: true }, (err) => { - makeDirectoryWritable(dir); - expect(err).toEqual(expect.objectContaining({ - code: codeExpected, - path: expect.any(String) - })); - done(); - }); -}); - -//<#END_FILE: test-fs-mkdir-recursive-eaccess.js diff --git a/test/js/node/test/parallel/fs-mkdtemp-prefix-check.test.js b/test/js/node/test/parallel/fs-mkdtemp-prefix-check.test.js deleted file mode 100644 index 81690d9c17..0000000000 --- a/test/js/node/test/parallel/fs-mkdtemp-prefix-check.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-fs-mkdtemp-prefix-check.js -//#SHA1: f17d58f63200ae9b8c855b9def7da223fc5db531 -//----------------- -"use strict"; -const fs = require("fs"); - -const prefixValues = [undefined, null, 0, true, false, 1]; - -describe("fs.mkdtempSync prefix check", () => { - test.each(prefixValues)("should throw for invalid prefix: %p", value => { - expect(() => { - fs.mkdtempSync(value, {}); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -describe("fs.mkdtemp prefix check", () => { - test.each(prefixValues)("should throw for invalid prefix: %p", value => { - expect(() => { - fs.mkdtemp(value, jest.fn()); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-fs-mkdtemp-prefix-check.js diff --git a/test/js/node/test/parallel/fs-non-number-arguments-throw.test.js b/test/js/node/test/parallel/fs-non-number-arguments-throw.test.js deleted file mode 100644 index fa7ff3127d..0000000000 --- a/test/js/node/test/parallel/fs-non-number-arguments-throw.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-fs-non-number-arguments-throw.js -//#SHA1: 65db5c653216831bc16d38c5d659fbffa296d3d8 -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const tmpdir = path.join(os.tmpdir(), 'test-fs-non-number-arguments-throw'); -const tempFile = path.join(tmpdir, 'fs-non-number-arguments-throw'); - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); - fs.writeFileSync(tempFile, 'abc\ndef'); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test('createReadStream with valid number arguments', (done) => { - const sanity = 'def'; - const saneEmitter = fs.createReadStream(tempFile, { start: 4, end: 6 }); - - saneEmitter.on('data', (data) => { - expect(data.toString('utf8')).toBe(sanity); - done(); - }); -}); - -test('createReadStream throws with string start argument', () => { - expect(() => { - fs.createReadStream(tempFile, { start: '4', end: 6 }); - }).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); -}); - -test('createReadStream throws with string end argument', () => { - expect(() => { - fs.createReadStream(tempFile, { start: 4, end: '6' }); - }).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); -}); - -test('createWriteStream throws with string start argument', () => { - expect(() => { - fs.createWriteStream(tempFile, { start: '4' }); - }).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); -}); - -//<#END_FILE: test-fs-non-number-arguments-throw.js diff --git a/test/js/node/test/parallel/fs-open-mode-mask.test.js b/test/js/node/test/parallel/fs-open-mode-mask.test.js deleted file mode 100644 index 798005d296..0000000000 --- a/test/js/node/test/parallel/fs-open-mode-mask.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-fs-open-mode-mask.js -//#SHA1: d290e7c1bced1fc3a98f27e2aeb463051581376c -//----------------- -"use strict"; - -// This tests that the lower bits of mode > 0o777 still works in fs.open(). - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const mode = process.platform === "win32" ? 0o444 : 0o644; - -const maskToIgnore = 0o10000; - -const tmpdir = path.join(os.tmpdir(), "test-fs-open-mode-mask"); - -beforeAll(() => { - try { - fs.mkdirSync(tmpdir, { recursive: true }); - } catch (err) { - // Directory might already exist - } -}); - -afterAll(() => { - try { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } catch (err) { - // Ignore errors during cleanup - } -}); - -function test(mode, asString) { - const suffix = asString ? "str" : "num"; - const input = asString ? (mode | maskToIgnore).toString(8) : mode | maskToIgnore; - - it(`should work with ${suffix} input`, () => { - const file = path.join(tmpdir, `openSync-${suffix}.txt`); - const fd = fs.openSync(file, "w+", input); - expect(fs.fstatSync(fd).mode & 0o777).toBe(mode); - fs.closeSync(fd); - expect(fs.statSync(file).mode & 0o777).toBe(mode); - }); - - it(`should work with ${suffix} input using callback`, done => { - const file = path.join(tmpdir, `open-${suffix}.txt`); - fs.open(file, "w+", input, (err, fd) => { - expect(err).toBeNull(); - expect(fs.fstatSync(fd).mode & 0o777).toBe(mode); - fs.closeSync(fd); - expect(fs.statSync(file).mode & 0o777).toBe(mode); - done(); - }); - }); -} - -test(mode, true); -test(mode, false); - -//<#END_FILE: test-fs-open-mode-mask.js diff --git a/test/js/node/test/parallel/fs-open-no-close.test.js b/test/js/node/test/parallel/fs-open-no-close.test.js deleted file mode 100644 index fbd17dc78c..0000000000 --- a/test/js/node/test/parallel/fs-open-no-close.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-fs-open-no-close.js -//#SHA1: 3f09a04c65d9a376e5d9b82882d375ab1dc99ad9 -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const debuglog = (arg) => { - console.log(new Date().toLocaleString(), arg); -}; - -const tmpdir = path.join(os.tmpdir(), 'test-fs-open-no-close'); - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test('fs.open should not keep the event loop open if file is not closed', (done) => { - let openFd; - - fs.open(path.join(tmpdir, 'dummy'), 'wx+', (err, fd) => { - debuglog('fs open() callback'); - expect(err).toBeFalsy(); - openFd = fd; - done(); - }); - - debuglog('waiting for callback'); - - // Simulate process.on('beforeExit') behavior - process.nextTick(() => { - if (openFd) { - fs.closeSync(openFd); - } - }); -}); - -//<#END_FILE: test-fs-open-no-close.js diff --git a/test/js/node/test/parallel/fs-open-numeric-flags.test.js b/test/js/node/test/parallel/fs-open-numeric-flags.test.js deleted file mode 100644 index 4edec0e495..0000000000 --- a/test/js/node/test/parallel/fs-open-numeric-flags.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-fs-open-numeric-flags.js -//#SHA1: 31a49fd78cbd63ab0b41de5f051d029bbe22fded -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -// Create a temporary directory for our tests -const tmpdir = path.join(os.tmpdir(), "test-fs-open-numeric-flags"); - -beforeEach(() => { - // Ensure the temporary directory exists and is empty - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); -}); - -afterEach(() => { - // Clean up the temporary directory after each test - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } -}); - -test("O_WRONLY without O_CREAT shall fail with ENOENT", () => { - const pathNE = path.join(tmpdir, "file-should-not-exist"); - - expect(() => { - fs.openSync(pathNE, fs.constants.O_WRONLY); - }).toThrow( - expect.objectContaining({ - code: "ENOENT", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-fs-open-numeric-flags.js diff --git a/test/js/node/test/parallel/fs-open.test.js b/test/js/node/test/parallel/fs-open.test.js deleted file mode 100644 index c8c102d7a3..0000000000 --- a/test/js/node/test/parallel/fs-open.test.js +++ /dev/null @@ -1,102 +0,0 @@ -//#FILE: test-fs-open.js -//#SHA1: 0466ad8882a3256fdd8da5fc8da3167f6dde4fd6 -//----------------- -'use strict'; -const fs = require('fs'); -const path = require('path'); - -test('fs.openSync throws ENOENT for non-existent file', () => { - expect(() => { - fs.openSync('/8hvftyuncxrt/path/to/file/that/does/not/exist', 'r'); - }).toThrow(expect.objectContaining({ - code: 'ENOENT', - message: expect.any(String) - })); -}); - -test('fs.openSync succeeds for existing file', () => { - expect(() => fs.openSync(__filename)).not.toThrow(); -}); - -test('fs.open succeeds with various valid arguments', async () => { - await expect(fs.promises.open(__filename)).resolves.toBeDefined(); - await expect(fs.promises.open(__filename, 'r')).resolves.toBeDefined(); - await expect(fs.promises.open(__filename, 'rs')).resolves.toBeDefined(); - await expect(fs.promises.open(__filename, 'r', 0)).resolves.toBeDefined(); - await expect(fs.promises.open(__filename, 'r', null)).resolves.toBeDefined(); -}); - -test('fs.open throws for invalid mode argument', () => { - expect(() => fs.open(__filename, 'r', 'boom', () => {})).toThrow(({ - code: 'ERR_INVALID_ARG_VALUE', - name: 'TypeError', - message: `The argument 'mode' must be a 32-bit unsigned integer or an octal string. Received boom` - })); - expect(() => fs.open(__filename, 'r', 5.5, () => {})).toThrow(({ - code: 'ERR_OUT_OF_RANGE', - name: 'RangeError', - message: `The value of "mode" is out of range. It must be an integer. Received 5.5` - })); - expect(() => fs.open(__filename, 'r', -7, () => {})).toThrow(({ - code: 'ERR_OUT_OF_RANGE', - name: 'RangeError', - message: `The value of "mode" is out of range. It must be >= 0 and <= 4294967295. Received -7` - })); - expect(() => fs.open(__filename, 'r', 4304967295, () => {})).toThrow(({ - code: 'ERR_OUT_OF_RANGE', - name: 'RangeError', - message: `The value of "mode" is out of range. It must be >= 0 and <= 4294967295. Received 4304967295` - })); -}); - -test('fs.open throws for invalid argument combinations', () => { - const invalidArgs = [[], ['r'], ['r', 0], ['r', 0, 'bad callback']]; - invalidArgs.forEach(args => { - expect(() => fs.open(__filename, ...args)).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); - }); -}); - -test('fs functions throw for invalid path types', () => { - const invalidPaths = [false, 1, [], {}, null, undefined]; - invalidPaths.forEach(path => { - expect(() => fs.open(path, 'r', () => {})).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); - expect(() => fs.openSync(path, 'r')).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); - expect(fs.promises.open(path, 'r')).rejects.toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); - }); -}); - -test('fs functions throw for invalid modes', () => { - const invalidModes = [false, [], {}]; - invalidModes.forEach(mode => { - expect(() => fs.open(__filename, 'r', mode, () => {})).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - message: expect.any(String) - })); - expect(() => fs.openSync(__filename, 'r', mode)).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - message: expect.any(String) - })); - expect(fs.promises.open(__filename, 'r', mode)).rejects.toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - message: expect.any(String) - })); - }); -}); - -//<#END_FILE: test-fs-open.js diff --git a/test/js/node/test/parallel/fs-operations-with-surrogate-pairs.test.js b/test/js/node/test/parallel/fs-operations-with-surrogate-pairs.test.js deleted file mode 100644 index 54c9021472..0000000000 --- a/test/js/node/test/parallel/fs-operations-with-surrogate-pairs.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-fs-operations-with-surrogate-pairs.js -//#SHA1: c59fe103e9ec4edee50c9186d341f5fdd32e0af4 -//----------------- -"use strict"; - -const fs = require("node:fs"); -const path = require("node:path"); -const tmpdir = require("../common/tmpdir"); - -tmpdir.refresh(); - -describe("File operations with filenames containing surrogate pairs", () => { - it("should write, read, and delete a file with surrogate pairs in the filename", () => { - // Create a temporary directory - const tempdir = fs.mkdtempSync(tmpdir.resolve("emoji-fruit-🍇 🍈 🍉 🍊 🍋")); - expect(fs.existsSync(tempdir)).toBe(true); - - const filename = "🚀🔥🛸.txt"; - const content = "Test content"; - - // Write content to a file - fs.writeFileSync(path.join(tempdir, filename), content); - - // Read content from the file - const readContent = fs.readFileSync(path.join(tempdir, filename), "utf8"); - - // Check if the content matches - expect(readContent).toBe(content); - }); -}); - -//<#END_FILE: test-fs-operations-with-surrogate-pairs.js diff --git a/test/js/node/test/parallel/fs-options-immutable.test.js b/test/js/node/test/parallel/fs-options-immutable.test.js deleted file mode 100644 index 9beef4c128..0000000000 --- a/test/js/node/test/parallel/fs-options-immutable.test.js +++ /dev/null @@ -1,148 +0,0 @@ -//#FILE: test-fs-options-immutable.js -//#SHA1: 3e986f4e0d29505ada9980c8af5146abd307ddb7 -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -// These tests make sure that the `options` object passed to these functions are -// never altered. -// -// Refer: https://github.com/nodejs/node/issues/7655 - -const originalOptions = {}; -let options; - -beforeEach(() => { - options = JSON.parse(JSON.stringify(originalOptions)); -}); - -const tmpdir = { - path: path.join(os.tmpdir(), "node-test-fs-options-immutable"), - refresh: () => { - try { - fs.rmSync(tmpdir.path, { recursive: true, force: true }); - } catch (error) { - // Ignore errors - } - fs.mkdirSync(tmpdir.path, { recursive: true }); - }, - resolve: filename => path.join(tmpdir.path, filename), -}; - -tmpdir.refresh(); - -test("fs.readFile", async () => { - await fs.promises.readFile(__filename, options); - expect(options).toEqual(originalOptions); -}); - -test("fs.readFileSync", () => { - fs.readFileSync(__filename, options); - expect(options).toEqual(originalOptions); -}); - -test("fs.readdir", async () => { - await fs.promises.readdir(__dirname, options); - expect(options).toEqual(originalOptions); -}); - -test("fs.readdirSync", () => { - fs.readdirSync(__dirname, options); - expect(options).toEqual(originalOptions); -}); - -test("fs.readlink and fs.readlinkSync", async () => { - const canCreateSymLink = await new Promise(resolve => { - fs.symlink(__filename, "dummy-symlink", err => { - if (err) resolve(false); - fs.unlink("dummy-symlink", () => resolve(true)); - }); - }); - - if (canCreateSymLink) { - const sourceFile = tmpdir.resolve("test-readlink"); - const linkFile = tmpdir.resolve("test-readlink-link"); - - await fs.promises.writeFile(sourceFile, ""); - await fs.promises.symlink(sourceFile, linkFile); - - await fs.promises.readlink(linkFile, options); - expect(options).toEqual(originalOptions); - - fs.readlinkSync(linkFile, options); - expect(options).toEqual(originalOptions); - } else { - test.skip("Symlink tests skipped - cannot create symlinks", () => {}); - } -}); - -test("fs.writeFile and fs.writeFileSync", async () => { - const fileName = tmpdir.resolve("writeFile"); - fs.writeFileSync(fileName, "ABCD", options); - expect(options).toEqual(originalOptions); - - await fs.promises.writeFile(fileName, "ABCD", options); - expect(options).toEqual(originalOptions); -}); - -test("fs.appendFile and fs.appendFileSync", async () => { - const fileName = tmpdir.resolve("appendFile"); - fs.appendFileSync(fileName, "ABCD", options); - expect(options).toEqual(originalOptions); - - await fs.promises.appendFile(fileName, "ABCD", options); - expect(options).toEqual(originalOptions); -}); - -test("fs.watch", () => { - if (process.platform === "os400") { - return test.skip("IBMi does not support fs.watch()"); - } - - const watch = fs.watch(__filename, options, () => {}); - watch.close(); - expect(options).toEqual(originalOptions); -}); - -test("fs.watchFile and fs.unwatchFile", () => { - fs.watchFile(__filename, options, () => {}); - fs.unwatchFile(__filename); - expect(options).toEqual(originalOptions); -}); - -test("fs.realpath and fs.realpathSync", async () => { - fs.realpathSync(__filename, options); - expect(options).toEqual(originalOptions); - - await fs.promises.realpath(__filename, options); - expect(options).toEqual(originalOptions); -}); - -test("fs.mkdtemp and fs.mkdtempSync", async () => { - const tempFileName = tmpdir.resolve("mkdtemp-"); - fs.mkdtempSync(tempFileName, options); - expect(options).toEqual(originalOptions); - - await fs.promises.mkdtemp(tempFileName, options); - expect(options).toEqual(originalOptions); -}); - -test("fs.WriteStream and fs.ReadStream", done => { - const fileName = tmpdir.resolve("streams"); - const writeStream = fs.createWriteStream(fileName, options); - writeStream.once("open", () => { - expect(options).toEqual(originalOptions); - const readStream = fs.createReadStream(fileName, options); - readStream.once("open", () => { - expect(options).toEqual(originalOptions); - readStream.destroy(); - writeStream.end(); - done(); - }); - }); -}); - -//<#END_FILE: test-fs-options-immutable.js diff --git a/test/js/node/test/parallel/fs-promises-exists.test.js b/test/js/node/test/parallel/fs-promises-exists.test.js deleted file mode 100644 index 303718b0ef..0000000000 --- a/test/js/node/test/parallel/fs-promises-exists.test.js +++ /dev/null @@ -1,14 +0,0 @@ -//#FILE: test-fs-promises-exists.js -//#SHA1: 3766c49e29d13338f3124165428e3a8a37d47fab -//----------------- -"use strict"; - -const fs = require("fs"); -const fsPromises = require("fs/promises"); - -test("fs.promises exists and is correctly linked", () => { - expect(fsPromises).toBe(fs.promises); - expect(fsPromises.constants).toBe(fs.constants); -}); - -//<#END_FILE: test-fs-promises-exists.js diff --git a/test/js/node/test/parallel/fs-promises-file-handle-append-file.test.js b/test/js/node/test/parallel/fs-promises-file-handle-append-file.test.js deleted file mode 100644 index 746b7614a8..0000000000 --- a/test/js/node/test/parallel/fs-promises-file-handle-append-file.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-fs-promises-file-handle-append-file.js -//#SHA1: 2a1932450418ea18ef00a890342f29ab307006e7 -//----------------- -'use strict'; - -const fs = require('fs'); -const { open } = fs.promises; -const path = require('path'); -const os = require('os'); - -const tmpDir = path.join(os.tmpdir(), 'test-fs-promises-file-handle-append-file'); - -beforeAll(() => { - if (fs.existsSync(tmpDir)) { - fs.rmSync(tmpDir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpDir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(tmpDir, { recursive: true, force: true }); -}); - -test('FileHandle.appendFile with buffer', async () => { - const filePath = path.resolve(tmpDir, 'tmp-append-file-buffer.txt'); - const fileHandle = await open(filePath, 'a'); - const buffer = Buffer.from('a&Dp'.repeat(100), 'utf8'); - - await fileHandle.appendFile(buffer); - const appendedFileData = fs.readFileSync(filePath); - expect(appendedFileData).toEqual(buffer); - - await fileHandle.close(); -}); - -test('FileHandle.appendFile with string', async () => { - const filePath = path.resolve(tmpDir, 'tmp-append-file-string.txt'); - const fileHandle = await open(filePath, 'a'); - const string = 'x~yz'.repeat(100); - - await fileHandle.appendFile(string); - const stringAsBuffer = Buffer.from(string, 'utf8'); - const appendedFileData = fs.readFileSync(filePath); - expect(appendedFileData).toEqual(stringAsBuffer); - - await fileHandle.close(); -}); - -//<#END_FILE: test-fs-promises-file-handle-append-file.js diff --git a/test/js/node/test/parallel/fs-promises-file-handle-chmod.test.js b/test/js/node/test/parallel/fs-promises-file-handle-chmod.test.js deleted file mode 100644 index 8bd751f1d1..0000000000 --- a/test/js/node/test/parallel/fs-promises-file-handle-chmod.test.js +++ /dev/null @@ -1,50 +0,0 @@ -//#FILE: test-fs-promises-file-handle-chmod.js -//#SHA1: 50a28df8df34deeca4b2f9d7598fb596894d7541 -//----------------- -"use strict"; - -const fs = require("fs"); -const { open } = fs.promises; -const path = require("path"); -const os = require("os"); - -const tmpDir = os.tmpdir(); - -beforeEach(() => { - jest.spyOn(fs, "statSync"); -}); - -afterEach(() => { - jest.restoreAllMocks(); -}); - -test("FileHandle.chmod base functionality", async () => { - const filePath = path.resolve(tmpDir, "tmp-chmod.txt"); - const fileHandle = await open(filePath, "w+", 0o444); - - // File created with r--r--r-- 444 - const statsBeforeMod = fs.statSync(filePath); - expect(statsBeforeMod.mode & 0o444).toBe(0o444); - - let expectedAccess; - const newPermissions = 0o765; - - if (process.platform === "win32") { - // Chmod in Windows will only toggle read only/write access. The - // fs.Stats.mode in Windows is computed using read/write - // bits (not exec). Read-only at best returns 444; r/w 666. - // Refer: /deps/uv/src/win/fs.cfs; - expectedAccess = 0o664; - } else { - expectedAccess = newPermissions; - } - - // Change the permissions to rwxr--r-x - await fileHandle.chmod(newPermissions); - const statsAfterMod = fs.statSync(filePath); - expect(statsAfterMod.mode & expectedAccess).toBe(expectedAccess); - - await fileHandle.close(); -}); - -//<#END_FILE: test-fs-promises-file-handle-chmod.js diff --git a/test/js/node/test/parallel/fs-promises-file-handle-write.test.js b/test/js/node/test/parallel/fs-promises-file-handle-write.test.js deleted file mode 100644 index 1652a75a05..0000000000 --- a/test/js/node/test/parallel/fs-promises-file-handle-write.test.js +++ /dev/null @@ -1,93 +0,0 @@ -//#FILE: test-fs-promises-file-handle-write.js -//#SHA1: 6ca802494e0ce0ee3187b1661322f115cfd7340c -//----------------- -"use strict"; - -const fs = require("fs"); -const { open } = fs.promises; -const path = require("path"); -const os = require("os"); - -const tmpDir = path.join(os.tmpdir(), "test-fs-promises-file-handle-write"); - -beforeAll(() => { - if (fs.existsSync(tmpDir)) { - fs.rmSync(tmpDir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpDir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(tmpDir, { recursive: true, force: true }); -}); - -test("validateWrite", async () => { - const filePathForHandle = path.resolve(tmpDir, "tmp-write.txt"); - const fileHandle = await open(filePathForHandle, "w+"); - const buffer = Buffer.from("Hello world".repeat(100), "utf8"); - - await fileHandle.write(buffer, 0, buffer.length); - const readFileData = fs.readFileSync(filePathForHandle); - expect(readFileData).toEqual(buffer); - - await fileHandle.close(); -}); - -test("validateEmptyWrite", async () => { - const filePathForHandle = path.resolve(tmpDir, "tmp-empty-write.txt"); - const fileHandle = await open(filePathForHandle, "w+"); - const buffer = Buffer.from(""); // empty buffer - - await fileHandle.write(buffer, 0, buffer.length); - const readFileData = fs.readFileSync(filePathForHandle); - expect(readFileData).toEqual(buffer); - - await fileHandle.close(); -}); - -test("validateNonUint8ArrayWrite", async () => { - const filePathForHandle = path.resolve(tmpDir, "tmp-data-write.txt"); - const fileHandle = await open(filePathForHandle, "w+"); - const buffer = Buffer.from("Hello world", "utf8").toString("base64"); - - await fileHandle.write(buffer, 0, buffer.length); - const readFileData = fs.readFileSync(filePathForHandle); - expect(readFileData).toEqual(Buffer.from(buffer, "utf8")); - - await fileHandle.close(); -}); - -test("validateNonStringValuesWrite", async () => { - const filePathForHandle = path.resolve(tmpDir, "tmp-non-string-write.txt"); - const fileHandle = await open(filePathForHandle, "w+"); - const nonStringValues = [ - 123, - {}, - new Map(), - null, - undefined, - 0n, - () => {}, - Symbol(), - true, - new String("notPrimitive"), - { - toString() { - return "amObject"; - }, - }, - { [Symbol.toPrimitive]: hint => "amObject" }, - ]; - for (const nonStringValue of nonStringValues) { - await expect(fileHandle.write(nonStringValue)).rejects.toThrow( - expect.objectContaining({ - message: expect.stringMatching(/"buffer"/), - code: "ERR_INVALID_ARG_TYPE", - }), - ); - } - - await fileHandle.close(); -}); - -//<#END_FILE: test-fs-promises-file-handle-write.js diff --git a/test/js/node/test/parallel/fs-promises-readfile-empty.test.js b/test/js/node/test/parallel/fs-promises-readfile-empty.test.js deleted file mode 100644 index dc9d291e5f..0000000000 --- a/test/js/node/test/parallel/fs-promises-readfile-empty.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-fs-promises-readfile-empty.js -//#SHA1: 6fcec9b5d3c9617426d46c79fb79244bc236574b -//----------------- -"use strict"; - -const fs = require("fs").promises; -const path = require("path"); - -const fixturesPath = path.resolve(__dirname, "..", "fixtures"); -const fn = path.join(fixturesPath, "empty.txt"); - -test("fs.readFile on empty file", async () => { - const content = await fs.readFile(fn); - expect(content).toBeTruthy(); -}); - -test("fs.readFile on empty file with utf8 encoding", async () => { - const content = await fs.readFile(fn, "utf8"); - expect(content).toBe(""); -}); - -test("fs.readFile on empty file with options object", async () => { - const content = await fs.readFile(fn, { encoding: "utf8" }); - expect(content).toBe(""); -}); - -//<#END_FILE: test-fs-promises-readfile-empty.js diff --git a/test/js/node/test/parallel/fs-promises-readfile-with-fd.test.js b/test/js/node/test/parallel/fs-promises-readfile-with-fd.test.js deleted file mode 100644 index 1f286daab3..0000000000 --- a/test/js/node/test/parallel/fs-promises-readfile-with-fd.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-fs-promises-readfile-with-fd.js -//#SHA1: 041811f02dddcdb9eba7d97e3943e26ec6b881cd -//----------------- -'use strict'; - -const fs = require('fs'); -const fsPromises = require('fs').promises; -const path = require('path'); -const os = require('os'); - -const tmpdir = path.join(os.tmpdir(), 'test-fs-promises-readfile-with-fd'); -const fn = path.join(tmpdir, 'test.txt'); - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); - fs.writeFileSync(fn, 'Hello World'); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test('readFile() reads from current position of the file', async () => { - const handle = await fsPromises.open(fn, 'r'); - - // Read only five bytes, so that the position moves to five. - const buf = Buffer.alloc(5); - const { bytesRead } = await handle.read(buf, 0, 5, null); - expect(bytesRead).toBe(5); - expect(buf.toString()).toBe('Hello'); - - // readFile() should read from position five, instead of zero. - expect((await handle.readFile()).toString()).toBe(' World'); - - await handle.close(); -}); - -//<#END_FILE: test-fs-promises-readfile-with-fd.js diff --git a/test/js/node/test/parallel/fs-promises-writefile-typedarray.test.js b/test/js/node/test/parallel/fs-promises-writefile-typedarray.test.js deleted file mode 100644 index bece880bd3..0000000000 --- a/test/js/node/test/parallel/fs-promises-writefile-typedarray.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-fs-promises-writefile-typedarray.js -//#SHA1: 718d3827c56ad0b11c59a801bf9529a1e6e5ab89 -//----------------- -"use strict"; - -const fs = require("fs"); -const fsPromises = fs.promises; -const path = require("path"); -const os = require("os"); - -const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "test-")); - -beforeAll(() => { - // Ensure the temporary directory is clean - fs.rmSync(tmpDir, { recursive: true, force: true }); - fs.mkdirSync(tmpDir, { recursive: true }); -}); - -afterAll(() => { - // Clean up the temporary directory - fs.rmSync(tmpDir, { recursive: true, force: true }); -}); - -const dest = path.resolve(tmpDir, "tmp.txt"); -// Use a file size larger than `kReadFileMaxChunkSize`. -const buffer = Buffer.from("012".repeat(2 ** 14)); - -test("fsPromises.writeFile with TypedArrays", async () => { - const constructors = [Uint8Array, Uint16Array, Uint32Array]; - - for (const Constructor of constructors) { - const array = new Constructor(buffer.buffer); - await fsPromises.writeFile(dest, array); - const data = await fsPromises.readFile(dest); - expect(data).toEqual(buffer); - } -}); - -//<#END_FILE: test-fs-promises-writefile-typedarray.js diff --git a/test/js/node/test/parallel/fs-promises-writefile-with-fd.test.js b/test/js/node/test/parallel/fs-promises-writefile-with-fd.test.js deleted file mode 100644 index 17182bedb6..0000000000 --- a/test/js/node/test/parallel/fs-promises-writefile-with-fd.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-fs-promises-writefile-with-fd.js -//#SHA1: 55be58e0edcbdc914795c46280459a85071f28eb -//----------------- -"use strict"; - -// This test makes sure that `writeFile()` always writes from the current -// position of the file, instead of truncating the file. - -const fs = require("fs"); -const fsPromises = require("fs").promises; -const path = require("path"); -const os = require("os"); - -let tmpdir; - -beforeEach(() => { - tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "test-")); -}); - -afterEach(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test("writeFile() writes from current position", async () => { - const fn = path.join(tmpdir, "test.txt"); - - const handle = await fsPromises.open(fn, "w"); - - /* Write only five bytes, so that the position moves to five. */ - const buf = Buffer.from("Hello"); - const { bytesWritten } = await handle.write(buf, 0, 5, null); - expect(bytesWritten).toBe(5); - - /* Write some more with writeFile(). */ - await handle.writeFile("World"); - - /* New content should be written at position five, instead of zero. */ - expect(fs.readFileSync(fn, "utf8")).toBe("HelloWorld"); - - await handle.close(); -}); - -//<#END_FILE: test-fs-promises-writefile-with-fd.js diff --git a/test/js/node/test/parallel/fs-promisified.test.js b/test/js/node/test/parallel/fs-promisified.test.js deleted file mode 100644 index 3df07cc98e..0000000000 --- a/test/js/node/test/parallel/fs-promisified.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-fs-promisified.js -//#SHA1: 5366497c2a750295d2c5cf65c2938e27f573e8bb -//----------------- -"use strict"; - -const fs = require("fs"); -const { promisify } = require("util"); - -const read = promisify(fs.read); -const write = promisify(fs.write); -const exists = promisify(fs.exists); - -test("promisified fs.read", async () => { - const fd = fs.openSync(__filename, "r"); - const obj = await read(fd, Buffer.alloc(1024), 0, 1024, null); - expect(typeof obj.bytesRead).toBe("number"); - expect(obj.buffer).toBeInstanceOf(Buffer); - fs.closeSync(fd); -}); - -test("promisified fs.write", async () => { - const tmpdir = require("../common/tmpdir"); - tmpdir.refresh(); - const filename = tmpdir.resolve("write-promise.txt"); - const fd = fs.openSync(filename, "w"); - const obj = await write(fd, Buffer.from("foobar")); - expect(typeof obj.bytesWritten).toBe("number"); - expect(obj.buffer.toString()).toBe("foobar"); - fs.closeSync(fd); -}); - -test("promisified fs.exists", async () => { - const result = await exists(__filename); - expect(result).toBe(true); -}); - -//<#END_FILE: test-fs-promisified.js diff --git a/test/js/node/test/parallel/fs-read-empty-buffer.test.js b/test/js/node/test/parallel/fs-read-empty-buffer.test.js deleted file mode 100644 index 04fe94f967..0000000000 --- a/test/js/node/test/parallel/fs-read-empty-buffer.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-fs-read-empty-buffer.js -//#SHA1: a2dc2c25e5a712b62c41298f885df24dd6106646 -//----------------- -'use strict'; -const fs = require('fs'); -const path = require('path'); - -const filepath = path.resolve(__dirname, 'x.txt'); -let fd; - -beforeAll(() => { - // Create a test file - fs.writeFileSync(filepath, 'test content'); - fd = fs.openSync(filepath, 'r'); -}); - -afterAll(() => { - fs.closeSync(fd); - fs.unlinkSync(filepath); -}); - -const buffer = new Uint8Array(); - -test('fs.readSync throws ERR_INVALID_ARG_VALUE for empty buffer', () => { - expect(() => fs.readSync(fd, buffer, 0, 10, 0)).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_VALUE', - message: expect.stringContaining('The argument \'buffer\' is empty and cannot be written') - })); -}); - -test('fs.read throws ERR_INVALID_ARG_VALUE for empty buffer', () => { - expect(() => fs.read(fd, buffer, 0, 1, 0, () => {})).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_VALUE', - message: expect.stringContaining('The argument \'buffer\' is empty and cannot be written') - })); -}); - -test('fsPromises.filehandle.read rejects with ERR_INVALID_ARG_VALUE for empty buffer', async () => { - const filehandle = await fs.promises.open(filepath, 'r'); - await expect(filehandle.read(buffer, 0, 1, 0)).rejects.toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_VALUE', - message: expect.stringContaining('The argument \'buffer\' is empty and cannot be written') - })); - await filehandle.close(); -}); - -//<#END_FILE: test-fs-read-empty-buffer.js diff --git a/test/js/node/test/parallel/fs-read-file-sync-hostname.test.js b/test/js/node/test/parallel/fs-read-file-sync-hostname.test.js deleted file mode 100644 index 9c4eb90ad8..0000000000 --- a/test/js/node/test/parallel/fs-read-file-sync-hostname.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-fs-read-file-sync-hostname.js -//#SHA1: 6e8bd1a34277c7b98b985ba23843555b05f80ccb -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const fs = require("fs"); - -if (process.platform !== "linux") { - test.skip("Test is linux specific.", () => {}); -} else { - test("reading /proc/sys/kernel/hostname", () => { - // Test to make sure reading a file under the /proc directory works. See: - // https://groups.google.com/forum/#!topic/nodejs-dev/rxZ_RoH1Gn0 - const hostname = fs.readFileSync("/proc/sys/kernel/hostname"); - expect(hostname.length).toBeGreaterThan(0); - }); -} - -//<#END_FILE: test-fs-read-file-sync-hostname.js diff --git a/test/js/node/test/parallel/fs-read-optional-params.test.js b/test/js/node/test/parallel/fs-read-optional-params.test.js deleted file mode 100644 index 8abd08c20e..0000000000 --- a/test/js/node/test/parallel/fs-read-optional-params.test.js +++ /dev/null @@ -1,60 +0,0 @@ -//#FILE: test-fs-read-optional-params.js -//#SHA1: daea619faa084927d87381fc60aedde3068a13ca -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const filepath = path.join(os.tmpdir(), "x.txt"); -const expected = Buffer.from("xyz\n"); -const defaultBufferAsync = Buffer.alloc(16384); -const bufferAsOption = Buffer.allocUnsafe(expected.byteLength); - -beforeAll(() => { - fs.writeFileSync(filepath, expected); -}); - -afterAll(() => { - fs.unlinkSync(filepath); -}); - -function testValid(message, ...options) { - test(`${message} (as params)`, async () => { - const paramsFilehandle = fs.openSync(filepath, "r"); - await new Promise(resolve => { - fs.read(paramsFilehandle, ...options, (err, bytesRead, buffer) => { - expect(err).toBeNull(); - expect(bytesRead).toBe(expected.byteLength); - expect(buffer.byteLength).toBe(defaultBufferAsync.byteLength); - fs.closeSync(paramsFilehandle); - resolve(); - }); - }); - }); - - test(`${message} (as options)`, async () => { - const optionsFilehandle = fs.openSync(filepath, "r"); - await new Promise(resolve => { - fs.read(optionsFilehandle, bufferAsOption, ...options, (err, bytesRead, buffer) => { - expect(err).toBeNull(); - expect(bytesRead).toBe(expected.byteLength); - expect(buffer.byteLength).toBe(bufferAsOption.byteLength); - fs.closeSync(optionsFilehandle); - resolve(); - }); - }); - }); -} - -testValid("Not passing in any object"); -testValid("Passing in a null", null); -testValid("Passing in an empty object", {}); -testValid("Passing in an object", { - offset: 0, - length: bufferAsOption.byteLength, - position: 0, -}); - -//<#END_FILE: test-fs-read-optional-params.js diff --git a/test/js/node/test/parallel/fs-read-promises-optional-params.test.js b/test/js/node/test/parallel/fs-read-promises-optional-params.test.js deleted file mode 100644 index 60353b9226..0000000000 --- a/test/js/node/test/parallel/fs-read-promises-optional-params.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-fs-read-promises-optional-params.js -//#SHA1: bc986664534329fd86b9aafd4c73a0159f71d388 -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const { promisify } = require('util'); -const read = promisify(fs.read); - -const filepath = path.resolve(__dirname, 'x.txt'); -let fd; - -const expected = Buffer.from('xyz\n'); -const defaultBufferAsync = Buffer.alloc(16384); -const bufferAsOption = Buffer.allocUnsafe(expected.byteLength); - -beforeAll(() => { - // Create the test file - fs.writeFileSync(filepath, expected); - fd = fs.openSync(filepath, 'r'); -}); - -afterAll(() => { - fs.closeSync(fd); - fs.unlinkSync(filepath); -}); - -test('read with empty options object', async () => { - const { bytesRead, buffer } = await read(fd, {}); - expect(bytesRead).toBe(expected.byteLength); - expect(buffer.byteLength).toBe(defaultBufferAsync.byteLength); -}); - -test('read with buffer and position options', async () => { - const { bytesRead, buffer } = await read(fd, bufferAsOption, { position: 0 }); - expect(bytesRead).toBe(expected.byteLength); - expect(buffer.byteLength).toBe(bufferAsOption.byteLength); -}); - -//<#END_FILE: test-fs-read-promises-optional-params.js diff --git a/test/js/node/test/parallel/fs-read-stream-autoclose.test.js b/test/js/node/test/parallel/fs-read-stream-autoclose.test.js deleted file mode 100644 index 36ab013cd8..0000000000 --- a/test/js/node/test/parallel/fs-read-stream-autoclose.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-fs-read-stream-autoClose.js -//#SHA1: 0fbd57ecd5ae02143036c03cdca120bc7c3deea1 -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const writeFile = path.join(os.tmpdir(), "write-autoClose.txt"); - -beforeEach(() => { - // Clean up the temporary directory - try { - fs.unlinkSync(writeFile); - } catch (err) { - // Ignore errors if file doesn't exist - } -}); - -test("fs.createWriteStream with autoClose option", done => { - const file = fs.createWriteStream(writeFile, { autoClose: true }); - - file.on("finish", () => { - expect(file.destroyed).toBe(false); - done(); - }); - - file.end("asd"); -}); - -//<#END_FILE: test-fs-read-stream-autoClose.js diff --git a/test/js/node/test/parallel/fs-read-stream-double-close.test.js b/test/js/node/test/parallel/fs-read-stream-double-close.test.js deleted file mode 100644 index 73603d1f1c..0000000000 --- a/test/js/node/test/parallel/fs-read-stream-double-close.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-fs-read-stream-double-close.js -//#SHA1: 066b117ee2b44bedfdce77d06389406b2474eb2f -//----------------- -'use strict'; - -const fs = require('fs'); - -test('double close on ReadStream', (done) => { - const s = fs.createReadStream(__filename); - - let closeCount = 0; - const checkClose = () => { - closeCount++; - if (closeCount === 2) { - done(); - } - }; - - s.close(checkClose); - s.close(checkClose); -}); - -test('double destroy on ReadStream', (done) => { - const s = fs.createReadStream(__filename); - - let destroyCount = 0; - const checkDestroy = () => { - destroyCount++; - if (destroyCount === 2) { - done(); - } - }; - - // This is a private API, but it is worth testing. close calls this - s.destroy(null, checkDestroy); - s.destroy(null, checkDestroy); -}); - -//<#END_FILE: test-fs-read-stream-double-close.js diff --git a/test/js/node/test/parallel/fs-read-stream-fd-leak.test.js b/test/js/node/test/parallel/fs-read-stream-fd-leak.test.js deleted file mode 100644 index 5c4e0dd55b..0000000000 --- a/test/js/node/test/parallel/fs-read-stream-fd-leak.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-fs-read-stream-fd-leak.js -//#SHA1: fc07b42f524d6a2f9743a5a7665c92096f58505b -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); - -let openCount = 0; -const _fsopen = fs.open; -const _fsclose = fs.close; - -const loopCount = 50; -const totalCheck = 50; -const emptyTxt = path.join(__dirname, "../fixtures/empty.txt"); - -fs.open = function () { - openCount++; - return _fsopen.apply(null, arguments); -}; - -fs.close = function () { - openCount--; - return _fsclose.apply(null, arguments); -}; - -function testLeak(endFn) { - return new Promise(resolve => { - console.log(`testing for leaks from fs.createReadStream().${endFn}()...`); - - let i = 0; - let check = 0; - - function checkFunction() { - if (openCount !== 0 && check < totalCheck) { - check++; - setTimeout(checkFunction, 100); - return; - } - - expect(openCount).toBe(0); - openCount = 0; - resolve(); - } - - const interval = setInterval(() => { - const s = fs.createReadStream(emptyTxt); - s[endFn](); - - if (++i === loopCount) { - clearInterval(interval); - setTimeout(checkFunction, 100); - } - }, 2); - }); -} - -test("no leaked file descriptors using close()", async () => { - await testLeak("close"); -}, 10000); - -test("no leaked file descriptors using destroy()", async () => { - await testLeak("destroy"); -}, 10000); - -//<#END_FILE: test-fs-read-stream-fd-leak.js diff --git a/test/js/node/test/parallel/fs-read-stream-pos.test.js b/test/js/node/test/parallel/fs-read-stream-pos.test.js deleted file mode 100644 index bdc551a7c1..0000000000 --- a/test/js/node/test/parallel/fs-read-stream-pos.test.js +++ /dev/null @@ -1,103 +0,0 @@ -//#FILE: test-fs-read-stream-pos.js -//#SHA1: e44b357d8045cfa1e8129a160254dcfb9225d990 -//----------------- -"use strict"; - -// Refs: https://github.com/nodejs/node/issues/33940 - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const tmpdir = { - refresh: () => { - // Implement tmpdir.refresh() if needed - }, - resolve: filename => path.join(os.tmpdir(), filename), -}; - -tmpdir.refresh(); - -const file = tmpdir.resolve("read_stream_pos_test.txt"); - -fs.writeFileSync(file, ""); - -let counter = 0; - -const writeInterval = setInterval(() => { - counter = counter + 1; - const line = `hello at ${counter}\n`; - fs.writeFileSync(file, line, { flag: "a" }); -}, 1); - -const hwm = 10; -let bufs = []; -let isLow = false; -let cur = 0; -let stream; - -const readInterval = setInterval(() => { - if (stream) return; - - stream = fs.createReadStream(file, { - highWaterMark: hwm, - start: cur, - }); - stream.on( - "data", - jest.fn(chunk => { - cur += chunk.length; - bufs.push(chunk); - if (isLow) { - const brokenLines = Buffer.concat(bufs) - .toString() - .split("\n") - .filter(line => { - const s = "hello at".slice(0, line.length); - if (line && !line.startsWith(s)) { - return true; - } - return false; - }); - expect(brokenLines.length).toBe(0); - exitTest(); - return; - } - if (chunk.length !== hwm) { - isLow = true; - } - }), - ); - stream.on("end", () => { - stream = null; - isLow = false; - bufs = []; - }); -}, 10); - -// Time longer than 90 seconds to exit safely -const endTimer = setTimeout(() => { - exitTest(); -}, 90000); - -const exitTest = () => { - clearInterval(readInterval); - clearInterval(writeInterval); - clearTimeout(endTimer); - if (stream && !stream.destroyed) { - stream.on("close", () => { - process.exit(); - }); - stream.destroy(); - } else { - process.exit(); - } -}; - -test("fs read stream position", () => { - // This test is mostly about setting up the environment and running the intervals - // The actual assertions are made within the intervals - expect(true).toBe(true); -}); - -//<#END_FILE: test-fs-read-stream-pos.js diff --git a/test/js/node/test/parallel/fs-readdir-buffer.test.js b/test/js/node/test/parallel/fs-readdir-buffer.test.js deleted file mode 100644 index 4003405487..0000000000 --- a/test/js/node/test/parallel/fs-readdir-buffer.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-fs-readdir-buffer.js -//#SHA1: 333645cb13aa3c15d61428ecfa2794e7393ef91c -//----------------- -"use strict"; - -const fs = require("fs"); - -if (process.platform !== "darwin") { - it("skips test on non-MacOS platforms", () => { - test.skip("this test works only on MacOS"); - }); -} else { - test("readdir with buffer and withFileTypes options on MacOS", () => { - return new Promise(resolve => { - fs.readdir(Buffer.from("/dev"), { withFileTypes: true, encoding: "buffer" }, (err, files) => { - expect(err).toBeNull(); - resolve(); - }); - }); - }); -} - -//<#END_FILE: test-fs-readdir-buffer.js diff --git a/test/js/node/test/parallel/fs-readdir-recursive.test.js b/test/js/node/test/parallel/fs-readdir-recursive.test.js deleted file mode 100644 index 32c284e827..0000000000 --- a/test/js/node/test/parallel/fs-readdir-recursive.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-fs-readdir-recursive.js -//#SHA1: daa6ac8e46cd3d530e4546354a11f87f1b1c092d -//----------------- -"use strict"; - -const fs = require("fs"); -const net = require("net"); -const path = require("path"); -const os = require("os"); - -const tmpdir = path.join(os.tmpdir(), "node-test-fs-readdir-recursive"); - -beforeAll(() => { - // Refresh tmpdir - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); -}); - -afterAll(() => { - // Clean up tmpdir - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test("fs.readdirSync with recursive option should not crash", done => { - const server = net.createServer().listen(path.join(tmpdir, "test.sock"), () => { - // The process should not crash - // See https://github.com/nodejs/node/issues/52159 - expect(() => { - fs.readdirSync(tmpdir, { recursive: true }); - }).not.toThrow(); - - server.close(); - done(); - }); -}); - -//<#END_FILE: test-fs-readdir-recursive.js diff --git a/test/js/node/test/parallel/fs-readdir.test.js b/test/js/node/test/parallel/fs-readdir.test.js deleted file mode 100644 index 1e98f2f1b6..0000000000 --- a/test/js/node/test/parallel/fs-readdir.test.js +++ /dev/null @@ -1,90 +0,0 @@ -//#FILE: test-fs-readdir.js -//#SHA1: ce2c5a12cb271c5023f965afe712e78b1a484ad5 -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const readdirDir = path.join(os.tmpdir(), "test-fs-readdir"); -const files = ["empty", "files", "for", "just", "testing"]; - -beforeAll(() => { - // Make sure tmp directory is clean - if (fs.existsSync(readdirDir)) { - fs.rmSync(readdirDir, { recursive: true, force: true }); - } - fs.mkdirSync(readdirDir, { recursive: true }); - - // Create the necessary files - files.forEach(currentFile => { - fs.closeSync(fs.openSync(path.join(readdirDir, currentFile), "w")); - }); -}); - -afterAll(() => { - // Clean up - fs.rmSync(readdirDir, { recursive: true, force: true }); -}); - -test("fs.readdirSync returns correct files", () => { - expect(fs.readdirSync(readdirDir).sort()).toEqual(files); -}); - -test("fs.readdir returns correct files", async () => { - await new Promise(resolve => { - fs.readdir(readdirDir, (err, f) => { - expect(err).toBeNull(); - expect(f.sort()).toEqual(files); - resolve(); - }); - }); -}); - -test("fs.readdirSync throws ENOTDIR on file", () => { - expect(() => { - fs.readdirSync(__filename); - }).toThrow( - expect.objectContaining({ - code: "ENOTDIR", - message: expect.any(String), - }), - ); -}); - -test("fs.readdir throws ENOTDIR on file", async () => { - await new Promise(resolve => { - fs.readdir(__filename, e => { - expect(e).toEqual( - expect.objectContaining({ - code: "ENOTDIR", - message: expect.any(String), - }), - ); - resolve(); - }); - }); -}); - -test("fs.readdir and fs.readdirSync throw on invalid input", () => { - [false, 1, [], {}, null, undefined].forEach(i => { - expect(() => fs.readdir(i, () => {})).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => fs.readdirSync(i)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-fs-readdir.js diff --git a/test/js/node/test/parallel/fs-readfile-empty.test.js b/test/js/node/test/parallel/fs-readfile-empty.test.js deleted file mode 100644 index ae4bdb4eb5..0000000000 --- a/test/js/node/test/parallel/fs-readfile-empty.test.js +++ /dev/null @@ -1,67 +0,0 @@ -//#FILE: test-fs-readfile-empty.js -//#SHA1: a78ffc8186bc3e0a7d8d8dcf0f292ef4220817a5 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; - -const fs = require('fs'); -const path = require('path'); - -const fixturesPath = path.join(__dirname, '..', 'fixtures'); -const fn = path.join(fixturesPath, 'empty.txt'); - -test('fs.readFile on an empty file', (done) => { - fs.readFile(fn, (err, data) => { - expect(err).toBeNull(); - expect(data).toBeTruthy(); - done(); - }); -}); - -test('fs.readFile on an empty file with utf8 encoding', (done) => { - fs.readFile(fn, 'utf8', (err, data) => { - expect(err).toBeNull(); - expect(data).toBe(''); - done(); - }); -}); - -test('fs.readFile on an empty file with encoding option', (done) => { - fs.readFile(fn, { encoding: 'utf8' }, (err, data) => { - expect(err).toBeNull(); - expect(data).toBe(''); - done(); - }); -}); - -test('fs.readFileSync on an empty file', () => { - const data = fs.readFileSync(fn); - expect(data).toBeTruthy(); -}); - -test('fs.readFileSync on an empty file with utf8 encoding', () => { - const data = fs.readFileSync(fn, 'utf8'); - expect(data).toBe(''); -}); - -//<#END_FILE: test-fs-readfile-empty.js diff --git a/test/js/node/test/parallel/fs-readfile-eof.test.js b/test/js/node/test/parallel/fs-readfile-eof.test.js deleted file mode 100644 index f1b68ffccf..0000000000 --- a/test/js/node/test/parallel/fs-readfile-eof.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-fs-readfile-eof.js -//#SHA1: 89b7efe6c30d2316249bfae1d01f16f97e32be04 -//----------------- -"use strict"; - -const fs = require("fs/promises"); -const { exec } = require("child_process"); - -const childType = ["child-encoding", "child-non-encoding"]; - -if (process.argv[2] === childType[0]) { - fs.readFile("/dev/stdin", "utf8").then(data => { - process.stdout.write(data); - }); -} else if (process.argv[2] === childType[1]) { - fs.readFile("/dev/stdin").then(data => { - process.stdout.write(data); - }); -} else { - const data1 = "Hello"; - const data2 = "World"; - const expected = `${data1}\n${data2}\n`; - - const f = JSON.stringify(__filename); - const node = JSON.stringify(process.execPath); - - function testReadFile(child) { - return new Promise((resolve, reject) => { - const cmd = `(echo ${data1}; sleep 0.5; echo ${data2}) | ${node} ${f} ${child}`; - exec(cmd, (error, stdout, stderr) => { - if (error) reject(error); - else resolve({ stdout, stderr }); - }); - }); - } - - if (process.platform === "win32" || process.platform === "aix" || process.platform === "os400") { - test.skip(`No /dev/stdin on ${process.platform}.`, () => {}); - } else { - test("readFile with encoding", async () => { - const { stdout, stderr } = await testReadFile(childType[0]); - expect(stdout).toBe(expected); - expect(stderr).toBe(""); - }); - - test("readFile without encoding", async () => { - const { stdout, stderr } = await testReadFile(childType[1]); - expect(stdout).toBe(expected); - expect(stderr).toBe(""); - }); - } -} - -//<#END_FILE: test-fs-readfile-eof.js diff --git a/test/js/node/test/parallel/fs-readfile-fd.test.js b/test/js/node/test/parallel/fs-readfile-fd.test.js deleted file mode 100644 index b62d15b9e6..0000000000 --- a/test/js/node/test/parallel/fs-readfile-fd.test.js +++ /dev/null @@ -1,111 +0,0 @@ -//#FILE: test-fs-readfile-fd.js -//#SHA1: ec2bc78cb0bab7b8e9b23c1c44a77b227294d8b4 -//----------------- -'use strict'; -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const tmpdir = path.join(os.tmpdir(), 'test-fs-readfile-fd'); -const emptyFilePath = path.join(tmpdir, 'empty.txt'); - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); - fs.writeFileSync(emptyFilePath, ''); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -function tempFd(callback) { - fs.open(emptyFilePath, 'r', (err, fd) => { - expect(err).toBeFalsy(); - callback(fd, () => { - fs.close(fd, (err) => { - expect(err).toBeFalsy(); - }); - }); - }); -} - -function tempFdSync(callback) { - const fd = fs.openSync(emptyFilePath, 'r'); - callback(fd); - fs.closeSync(fd); -} - -test('fs.readFile with file descriptor', (done) => { - tempFd((fd, close) => { - fs.readFile(fd, (err, data) => { - expect(data).toBeTruthy(); - close(); - done(); - }); - }); -}); - -test('fs.readFile with file descriptor and utf8 encoding', (done) => { - tempFd((fd, close) => { - fs.readFile(fd, 'utf8', (err, data) => { - expect(data).toBe(''); - close(); - done(); - }); - }); -}); - -test('fs.readFileSync with file descriptor', () => { - tempFdSync((fd) => { - expect(fs.readFileSync(fd)).toBeTruthy(); - }); -}); - -test('fs.readFileSync with file descriptor and utf8 encoding', () => { - tempFdSync((fd) => { - expect(fs.readFileSync(fd, 'utf8')).toBe(''); - }); -}); - -test('readFile() reads from current position of file descriptor', (done) => { - const filename = path.join(tmpdir, 'test.txt'); - fs.writeFileSync(filename, 'Hello World'); - - fs.open(filename, 'r', (err, fd) => { - expect(err).toBeFalsy(); - const buf = Buffer.alloc(5); - - fs.read(fd, buf, 0, 5, null, (err, bytes) => { - expect(err).toBeFalsy(); - expect(bytes).toBe(5); - expect(buf.toString()).toBe('Hello'); - - fs.readFile(fd, (err, data) => { - expect(err).toBeFalsy(); - expect(data.toString()).toBe(' World'); - fs.closeSync(fd); - done(); - }); - }); - }); -}); - -test('readFileSync() reads from current position of file descriptor', () => { - const filename = path.join(tmpdir, 'test.txt'); - fs.writeFileSync(filename, 'Hello World'); - - const fd = fs.openSync(filename, 'r'); - - const buf = Buffer.alloc(5); - expect(fs.readSync(fd, buf, 0, 5)).toBe(5); - expect(buf.toString()).toBe('Hello'); - - expect(fs.readFileSync(fd).toString()).toBe(' World'); - - fs.closeSync(fd); -}); - -//<#END_FILE: test-fs-readfile-fd.js diff --git a/test/js/node/test/parallel/fs-readfile-pipe-large.test.js b/test/js/node/test/parallel/fs-readfile-pipe-large.test.js deleted file mode 100644 index 815db4cedd..0000000000 --- a/test/js/node/test/parallel/fs-readfile-pipe-large.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-fs-readfile-pipe-large.js -//#SHA1: 5e2fa068dc742cfe617ccf3f08df6725e92a51f6 -//----------------- -'use strict'; -const fs = require('fs'); -const path = require('path'); -const { exec } = require('child_process'); -const os = require('os'); - -const isWindows = process.platform === 'win32'; -const isAIX = process.platform === 'aix'; -const isIBMi = process.platform === 'os400'; - -const skipPlatforms = ['win32', 'aix', 'os400']; - -// Separate child process logic -if (process.argv[2] === 'child') { - fs.readFile('/dev/stdin', (err, data) => { - if (err) { - console.error(err); - process.exit(1); - } - process.stdout.write(data); - }); -} else { - // Jest test code - describe('fs.readFile pipe large', () => { - const tmpdir = os.tmpdir(); - const filename = path.join(tmpdir, 'readfile_pipe_large_test.txt'); - const dataExpected = 'a'.repeat(999999); - - beforeAll(() => { - if (!skipPlatforms.includes(process.platform)) { - fs.writeFileSync(filename, dataExpected); - } - }); - - afterAll(() => { - if (!skipPlatforms.includes(process.platform)) { - fs.unlinkSync(filename); - } - }); - - test('should read from /dev/stdin and write to stdout', () => { - if (skipPlatforms.includes(process.platform)) { - return test.skip(`No /dev/stdin on ${process.platform}.`); - } - - const f = JSON.stringify(__filename); - const node = JSON.stringify(process.execPath); - const cmd = `cat ${filename} | ${node} ${f} child`; - - return new Promise((resolve, reject) => { - exec(cmd, { maxBuffer: 1000000 }, (error, stdout, stderr) => { - if (error) { - reject(error); - return; - } - - expect(stdout).toBe(dataExpected); - expect(stderr).toBe(''); - resolve(); - }); - }); - }); - }); -} -//<#END_FILE: test-fs-readfile-pipe-large.js diff --git a/test/js/node/test/parallel/fs-readfile-pipe.test.js b/test/js/node/test/parallel/fs-readfile-pipe.test.js deleted file mode 100644 index 9d9cec5cb7..0000000000 --- a/test/js/node/test/parallel/fs-readfile-pipe.test.js +++ /dev/null @@ -1,52 +0,0 @@ -//#FILE: test-fs-readfile-pipe.js -//#SHA1: b78e6ea1bbcdaf74b6363f4740bdf2393ed28938 -//----------------- -'use strict'; -const fs = require('fs'); -const path = require('path'); -const { exec } = require('child_process'); - -const isWindows = process.platform === 'win32'; -const isAIX = process.platform === 'aix'; -const isIBMi = process.platform === 'os400'; - -const fixturesPath = path.join(__dirname, '..', 'fixtures'); - -if (isWindows || isAIX || isIBMi) { - test.skip(`No /dev/stdin on ${process.platform}.`, () => {}); -} else { - if (process.argv[2] === 'child') { - fs.readFile('/dev/stdin', (err, data) => { - if (err) { - console.error(err); - process.exit(1); - } - process.stdout.write(data); - }); - } else { - test('readFile pipe test', (done) => { - const filename = path.join(fixturesPath, 'readfile_pipe_test.txt'); - const dataExpected = fs.readFileSync(filename, 'utf8'); - - const f = JSON.stringify(__filename); - const node = JSON.stringify(process.execPath); - const cmd = `cat ${filename} | ${node} ${f} child`; - - exec(cmd, (error, stdout, stderr) => { - if (error) { - done(error); - return; - } - try { - expect(stdout).toBe(dataExpected); - expect(stderr).toBe(''); - done(); - } catch (error) { - done(error); - } - }); - }, 10000); // Increase timeout to 10 seconds - } -} - -//<#END_FILE: test-fs-readfile-pipe.js diff --git a/test/js/node/test/parallel/fs-readfile-unlink.test.js b/test/js/node/test/parallel/fs-readfile-unlink.test.js deleted file mode 100644 index 85475f6488..0000000000 --- a/test/js/node/test/parallel/fs-readfile-unlink.test.js +++ /dev/null @@ -1,52 +0,0 @@ -//#FILE: test-fs-readfile-unlink.js -//#SHA1: a7107747d7901dfcc1ffd0e7adbf548412a1016a -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -// Test that unlink succeeds immediately after readFile completes. - -test("unlink succeeds immediately after readFile completes", async () => { - const tmpdir = path.join(os.tmpdir(), "node-test-fs-readfile-unlink"); - await fs.promises.mkdir(tmpdir, { recursive: true }); - - const fileName = path.join(tmpdir, "test.bin"); - const buf = Buffer.alloc(512 * 1024, 42); - - await fs.promises.writeFile(fileName, buf); - - const data = await fs.promises.readFile(fileName); - - expect(data.length).toBe(buf.length); - expect(data[0]).toBe(42); - - // Unlink should not throw. This is part of the test. It used to throw on - // Windows due to a bug. - await expect(fs.promises.unlink(fileName)).resolves.toBeUndefined(); -}); - -//<#END_FILE: test-fs-readfile-unlink.js diff --git a/test/js/node/test/parallel/fs-readfile-zero-byte-liar.test.js b/test/js/node/test/parallel/fs-readfile-zero-byte-liar.test.js deleted file mode 100644 index 0e3799ec0e..0000000000 --- a/test/js/node/test/parallel/fs-readfile-zero-byte-liar.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-fs-readfile-zero-byte-liar.js -//#SHA1: ddca2f114cf32b03f36405a21c81058e7a1f0c18 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const fs = require("fs"); - -// Test that readFile works even when stat returns size 0. - -const dataExpected = fs.readFileSync(__filename, "utf8"); - -// Sometimes stat returns size=0, but it's a lie. -fs._fstat = fs.fstat; -fs._fstatSync = fs.fstatSync; - -fs.fstat = (fd, cb) => { - fs._fstat(fd, (er, st) => { - if (er) return cb(er); - st.size = 0; - return cb(er, st); - }); -}; - -fs.fstatSync = fd => { - const st = fs._fstatSync(fd); - st.size = 0; - return st; -}; - -test("readFileSync works with zero byte liar", () => { - const d = fs.readFileSync(__filename, "utf8"); - expect(d).toBe(dataExpected); -}); - -test("readFile works with zero byte liar", async () => { - const d = await fs.promises.readFile(__filename, "utf8"); - expect(d).toBe(dataExpected); -}); - -//<#END_FILE: test-fs-readfile-zero-byte-liar.js diff --git a/test/js/node/test/parallel/fs-readfilesync-pipe-large.test.js b/test/js/node/test/parallel/fs-readfilesync-pipe-large.test.js deleted file mode 100644 index 8df6c2e1f0..0000000000 --- a/test/js/node/test/parallel/fs-readfilesync-pipe-large.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-fs-readfilesync-pipe-large.js -//#SHA1: 669e419b344b375a028fa352c7a29eec2d5d52af -//----------------- -'use strict'; -const fs = require('fs'); -const path = require('path'); -const { exec } = require('child_process'); -const os = require('os'); -const { describe, test, expect, beforeAll, afterAll } = require('@jest/globals'); - -const isWindows = process.platform === 'win32'; -const isAIX = process.platform === 'aix'; -const isIBMi = process.platform === 'os400'; - -const shouldSkip = isWindows || isAIX || isIBMi; - -const tmpdir = os.tmpdir(); - -if (process.argv[2] === 'child') { - process.stdout.write(fs.readFileSync('/dev/stdin', 'utf8')); - process.exit(0); -} - -describe('fs.readFileSync pipe large', () => { - const filename = path.join(tmpdir, 'readfilesync_pipe_large_test.txt'); - const dataExpected = 'a'.repeat(999999); - - beforeAll(() => { - if (!shouldSkip) { - fs.writeFileSync(filename, dataExpected); - } - }); - - afterAll(() => { - if (!shouldSkip) { - fs.unlinkSync(filename); - } - }); - - const testFn = shouldSkip ? test.skip : test; - - testFn('should read large file through pipe', (done) => { - const childScriptPath = path.join(__dirname, 'child-script.js'); - fs.writeFileSync(childScriptPath, ` - const fs = require('fs'); - process.stdout.write(fs.readFileSync('/dev/stdin', 'utf8')); - `); - - const cmd = `cat ${filename} | "${process.execPath}" "${childScriptPath}"`; - - exec(cmd, { maxBuffer: 1000000 }, (error, stdout, stderr) => { - try { - expect(error).toBeNull(); - expect(stdout).toBe(dataExpected); - expect(stderr).toBe(''); - fs.unlinkSync(childScriptPath); - done(); - } catch (err) { - fs.unlinkSync(childScriptPath); - done(err); - } - }); - }, 30000); // Increase timeout to 30 seconds -}); - -//<#END_FILE: test-fs-readfilesync-pipe-large.js diff --git a/test/js/node/test/parallel/fs-readlink-type-check.test.js b/test/js/node/test/parallel/fs-readlink-type-check.test.js deleted file mode 100644 index c0ee6e8b00..0000000000 --- a/test/js/node/test/parallel/fs-readlink-type-check.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-fs-readlink-type-check.js -//#SHA1: dd36bda8e12e6c22c342325345dd8d1de2097d9c -//----------------- -"use strict"; - -const fs = require("fs"); - -[false, 1, {}, [], null, undefined].forEach(i => { - test(`fs.readlink throws for invalid input: ${JSON.stringify(i)}`, () => { - expect(() => fs.readlink(i, jest.fn())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); - - test(`fs.readlinkSync throws for invalid input: ${JSON.stringify(i)}`, () => { - expect(() => fs.readlinkSync(i)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-fs-readlink-type-check.js diff --git a/test/js/node/test/parallel/fs-readv-promises.test.js b/test/js/node/test/parallel/fs-readv-promises.test.js deleted file mode 100644 index ff66cc9354..0000000000 --- a/test/js/node/test/parallel/fs-readv-promises.test.js +++ /dev/null @@ -1,69 +0,0 @@ -//#FILE: test-fs-readv-promises.js -//#SHA1: 43d801fa8a2eabf438e98f5aa713eb9680fe798b -//----------------- -"use strict"; - -const fs = require("fs").promises; -const path = require("path"); -const os = require("os"); - -const expected = "ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف"; -const expectedBuff = Buffer.from(expected); - -let cnt = 0; -function getFileName() { - return path.join(os.tmpdir(), `readv_promises_${++cnt}.txt`); -} - -const allocateEmptyBuffers = combinedLength => { - const bufferArr = []; - // Allocate two buffers, each half the size of expectedBuff - bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)); - bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length); - - return bufferArr; -}; - -describe("fs.promises.readv", () => { - beforeEach(() => { - cnt = 0; - }); - - test("readv with position", async () => { - const filename = getFileName(); - await fs.writeFile(filename, expectedBuff); - const handle = await fs.open(filename, "r"); - const bufferArr = allocateEmptyBuffers(expectedBuff.length); - const expectedLength = expectedBuff.length; - - let { bytesRead, buffers } = await handle.readv([Buffer.from("")], null); - expect(bytesRead).toBe(0); - expect(buffers).toEqual([Buffer.from("")]); - - ({ bytesRead, buffers } = await handle.readv(bufferArr, null)); - expect(bytesRead).toBe(expectedLength); - expect(buffers).toEqual(bufferArr); - expect(Buffer.concat(bufferArr)).toEqual(await fs.readFile(filename)); - await handle.close(); - }); - - test("readv without position", async () => { - const filename = getFileName(); - await fs.writeFile(filename, expectedBuff); - const handle = await fs.open(filename, "r"); - const bufferArr = allocateEmptyBuffers(expectedBuff.length); - const expectedLength = expectedBuff.length; - - let { bytesRead, buffers } = await handle.readv([Buffer.from("")]); - expect(bytesRead).toBe(0); - expect(buffers).toEqual([Buffer.from("")]); - - ({ bytesRead, buffers } = await handle.readv(bufferArr)); - expect(bytesRead).toBe(expectedLength); - expect(buffers).toEqual(bufferArr); - expect(Buffer.concat(bufferArr)).toEqual(await fs.readFile(filename)); - await handle.close(); - }); -}); - -//<#END_FILE: test-fs-readv-promises.js diff --git a/test/js/node/test/parallel/fs-readv-sync.test.js b/test/js/node/test/parallel/fs-readv-sync.test.js deleted file mode 100644 index f4ab916f05..0000000000 --- a/test/js/node/test/parallel/fs-readv-sync.test.js +++ /dev/null @@ -1,104 +0,0 @@ -//#FILE: test-fs-readv-sync.js -//#SHA1: e9a4527b118e4a814a04c976eaafb5127f7c7c9d -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const expected = "ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف"; - -const exptectedBuff = Buffer.from(expected); -const expectedLength = exptectedBuff.length; - -let filename; -let tmpdir; - -beforeAll(() => { - tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "test-fs-readv-sync-")); - filename = path.join(tmpdir, "readv_sync.txt"); - fs.writeFileSync(filename, exptectedBuff); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -const allocateEmptyBuffers = combinedLength => { - const bufferArr = []; - // Allocate two buffers, each half the size of exptectedBuff - bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)); - bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length); - - return bufferArr; -}; - -// fs.readvSync with array of buffers with all parameters -test("fs.readvSync with array of buffers with all parameters", () => { - const fd = fs.openSync(filename, "r"); - - const bufferArr = allocateEmptyBuffers(exptectedBuff.length); - - let read = fs.readvSync(fd, [Buffer.from("")], 0); - expect(read).toBe(0); - - read = fs.readvSync(fd, bufferArr, 0); - expect(read).toBe(expectedLength); - - fs.closeSync(fd); - - expect(Buffer.concat(bufferArr)).toEqual(fs.readFileSync(filename)); -}); - -// fs.readvSync with array of buffers without position -test("fs.readvSync with array of buffers without position", () => { - const fd = fs.openSync(filename, "r"); - - const bufferArr = allocateEmptyBuffers(exptectedBuff.length); - - let read = fs.readvSync(fd, [Buffer.from("")]); - expect(read).toBe(0); - - read = fs.readvSync(fd, bufferArr); - expect(read).toBe(expectedLength); - - fs.closeSync(fd); - - expect(Buffer.concat(bufferArr)).toEqual(fs.readFileSync(filename)); -}); - -/** - * Testing with incorrect arguments - */ -const wrongInputs = [false, "test", {}, [{}], ["sdf"], null, undefined]; - -test("fs.readvSync with incorrect arguments", () => { - const fd = fs.openSync(filename, "r"); - - for (const wrongInput of wrongInputs) { - expect(() => fs.readvSync(fd, wrongInput, null)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - } - - fs.closeSync(fd); -}); - -test("fs.readvSync with wrong fd argument", () => { - for (const wrongInput of wrongInputs) { - expect(() => fs.readvSync(wrongInput)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - } -}); - -//<#END_FILE: test-fs-readv-sync.js diff --git a/test/js/node/test/parallel/fs-readv.test.js b/test/js/node/test/parallel/fs-readv.test.js deleted file mode 100644 index 58f9977c3b..0000000000 --- a/test/js/node/test/parallel/fs-readv.test.js +++ /dev/null @@ -1,110 +0,0 @@ -//#FILE: test-fs-readv.js -//#SHA1: 07d6fe434017163aea491c98db8127bc2c942b96 -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const expected = "ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف"; - -let cnt = 0; -const getFileName = () => path.join(os.tmpdir(), `readv_${++cnt}.txt`); -const expectedBuff = Buffer.from(expected); - -const allocateEmptyBuffers = combinedLength => { - const bufferArr = []; - // Allocate two buffers, each half the size of expectedBuff - bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)); - bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length); - - return bufferArr; -}; - -const getCallback = (fd, bufferArr) => { - return (err, bytesRead, buffers) => { - expect(err).toBeNull(); - expect(bufferArr).toEqual(buffers); - const expectedLength = expectedBuff.length; - expect(bytesRead).toBe(expectedLength); - fs.closeSync(fd); - - expect(Buffer.concat(bufferArr).equals(expectedBuff)).toBe(true); - }; -}; - -beforeEach(() => { - jest.spyOn(fs, "writeSync"); - jest.spyOn(fs, "writeFileSync"); - jest.spyOn(fs, "openSync"); - jest.spyOn(fs, "closeSync"); -}); - -afterEach(() => { - jest.restoreAllMocks(); -}); - -test("fs.readv with array of buffers with all parameters", done => { - const filename = getFileName(); - const fd = fs.openSync(filename, "w+"); - fs.writeSync(fd, expectedBuff); - - const bufferArr = allocateEmptyBuffers(expectedBuff.length); - const callback = getCallback(fd, bufferArr); - - fs.readv(fd, bufferArr, 0, (err, bytesRead, buffers) => { - callback(err, bytesRead, buffers); - done(); - }); -}); - -test("fs.readv with array of buffers without position", done => { - const filename = getFileName(); - fs.writeFileSync(filename, expectedBuff); - const fd = fs.openSync(filename, "r"); - - const bufferArr = allocateEmptyBuffers(expectedBuff.length); - const callback = getCallback(fd, bufferArr); - - fs.readv(fd, bufferArr, (err, bytesRead, buffers) => { - callback(err, bytesRead, buffers); - done(); - }); -}); - -describe("Testing with incorrect arguments", () => { - const wrongInputs = [false, "test", {}, [{}], ["sdf"], null, undefined]; - - test("fs.readv with wrong buffers argument", () => { - const filename = getFileName(); - fs.writeFileSync(filename, expectedBuff); - const fd = fs.openSync(filename, "r"); - - for (const wrongInput of wrongInputs) { - expect(() => fs.readv(fd, wrongInput, null, jest.fn())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - } - - fs.closeSync(fd); - }); - - test("fs.readv with wrong fd argument", () => { - for (const wrongInput of wrongInputs) { - expect(() => fs.readv(wrongInput, jest.fn())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - } - }); -}); - -//<#END_FILE: test-fs-readv.js diff --git a/test/js/node/test/parallel/fs-realpath-pipe.test.js b/test/js/node/test/parallel/fs-realpath-pipe.test.js deleted file mode 100644 index 6c461e9d36..0000000000 --- a/test/js/node/test/parallel/fs-realpath-pipe.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-fs-realpath-pipe.js -//#SHA1: 2a876967f5134cd77e2214f2abcbf753d46983cf -//----------------- -"use strict"; - -const { spawnSync } = require("child_process"); - -// Skip test for Windows, AIX, and IBMi -const isSkippedPlatform = ["win32", "aix", "os400"].includes(process.platform); -const testName = `No /dev/stdin on ${process.platform}.`; - -(isSkippedPlatform ? test.skip : test)(testName, () => { - const testCases = [ - `require('fs').realpath('/dev/stdin', (err, resolvedPath) => { - if (err) { - console.error(err); - process.exit(1); - } - if (resolvedPath) { - process.exit(2); - } - });`, - `try { - if (require('fs').realpathSync('/dev/stdin')) { - process.exit(2); - } - } catch (e) { - console.error(e); - process.exit(1); - }`, - ]; - - for (const code of testCases) { - const child = spawnSync(process.execPath, ["-e", code], { - stdio: "pipe", - }); - - if (child.status !== 2) { - console.log(code); - console.log(child.stderr.toString()); - } - - expect(child.status).toBe(2); - } -}); - -//<#END_FILE: test-fs-realpath-pipe.js diff --git a/test/js/node/test/parallel/fs-rmdir-type-check.test.js b/test/js/node/test/parallel/fs-rmdir-type-check.test.js deleted file mode 100644 index 3148f0ba3a..0000000000 --- a/test/js/node/test/parallel/fs-rmdir-type-check.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-fs-rmdir-type-check.js -//#SHA1: 2a00191160af6f0f76a82dcaef31d13c9b223d3b -//----------------- -"use strict"; - -const fs = require("fs"); - -test("fs.rmdir and fs.rmdirSync with invalid arguments", () => { - [false, 1, [], {}, null, undefined].forEach(i => { - expect(() => fs.rmdir(i, jest.fn())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => fs.rmdirSync(i)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-fs-rmdir-type-check.js diff --git a/test/js/node/test/parallel/fs-sir-writes-alot.test.js b/test/js/node/test/parallel/fs-sir-writes-alot.test.js deleted file mode 100644 index b8eefc6642..0000000000 --- a/test/js/node/test/parallel/fs-sir-writes-alot.test.js +++ /dev/null @@ -1,84 +0,0 @@ -//#FILE: test-fs-sir-writes-alot.js -//#SHA1: d6f4574d48b9a85ee1276e4e0499f3fc32096d24 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -'use strict'; -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const tmpdir = path.join(os.tmpdir(), 'test-fs-sir-writes-alot'); -const filename = path.join(tmpdir, 'out.txt'); - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test('multiple async writes to a file', async () => { - const fd = fs.openSync(filename, 'w'); - - const line = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n'; - const N = 10240; - let complete = 0; - let bytesChecked = 0; - - function testBuffer(b) { - for (let i = 0; i < b.length; i++) { - bytesChecked++; - if (b[i] !== 'a'.charCodeAt(0) && b[i] !== '\n'.charCodeAt(0)) { - throw new Error(`invalid char ${i},${b[i]}`); - } - } - } - - await new Promise((resolve) => { - for (let i = 0; i < N; i++) { - // Create a new buffer for each write. Before the write is actually - // executed by the thread pool, the buffer will be collected. - const buffer = Buffer.from(line); - fs.write(fd, buffer, 0, buffer.length, null, function(er, written) { - complete++; - if (complete === N) { - fs.closeSync(fd); - const s = fs.createReadStream(filename); - s.on('data', testBuffer); - s.on('end', resolve); - } - }); - } - }); - - // Probably some of the writes are going to overlap, so we can't assume - // that we get (N * line.length). Let's just make sure we've checked a - // few... - expect(bytesChecked).toBeGreaterThan(1000); -}); - -//<#END_FILE: test-fs-sir-writes-alot.js diff --git a/test/js/node/test/parallel/fs-stat-bigint.test.js b/test/js/node/test/parallel/fs-stat-bigint.test.js deleted file mode 100644 index d7eb29c3f0..0000000000 --- a/test/js/node/test/parallel/fs-stat-bigint.test.js +++ /dev/null @@ -1,209 +0,0 @@ -//#FILE: test-fs-stat-bigint.js -//#SHA1: c8ba0bacb927432a68a677cd3a304e8e058fb070 -//----------------- -"use strict"; - -const fs = require("fs"); -const promiseFs = require("fs").promises; -const tmpdir = require("../common/tmpdir"); -const { isDate } = require("util").types; -const { inspect } = require("util"); - -tmpdir.refresh(); - -let testIndex = 0; - -function getFilename() { - const filename = tmpdir.resolve(`test-file-${++testIndex}`); - fs.writeFileSync(filename, "test"); - return filename; -} - -function verifyStats(bigintStats, numStats, allowableDelta) { - // allowableDelta: It's possible that the file stats are updated between the - // two stat() calls so allow for a small difference. - for (const key of Object.keys(numStats)) { - const val = numStats[key]; - if (isDate(val)) { - const time = val.getTime(); - const time2 = bigintStats[key].getTime(); - expect(time - time2).toBeLessThanOrEqual(allowableDelta); - } else if (key === "mode") { - expect(bigintStats[key]).toBe(BigInt(val)); - expect(bigintStats.isBlockDevice()).toBe(numStats.isBlockDevice()); - expect(bigintStats.isCharacterDevice()).toBe(numStats.isCharacterDevice()); - expect(bigintStats.isDirectory()).toBe(numStats.isDirectory()); - expect(bigintStats.isFIFO()).toBe(numStats.isFIFO()); - expect(bigintStats.isFile()).toBe(numStats.isFile()); - expect(bigintStats.isSocket()).toBe(numStats.isSocket()); - expect(bigintStats.isSymbolicLink()).toBe(numStats.isSymbolicLink()); - } else if (key.endsWith("Ms")) { - const nsKey = key.replace("Ms", "Ns"); - const msFromBigInt = bigintStats[key]; - const nsFromBigInt = bigintStats[nsKey]; - const msFromBigIntNs = Number(nsFromBigInt / 10n ** 6n); - const msFromNum = numStats[key]; - - expect(msFromNum - Number(msFromBigInt)).toBeLessThanOrEqual(allowableDelta); - expect(msFromNum - Number(msFromBigIntNs)).toBeLessThanOrEqual(allowableDelta); - } else if (Number.isSafeInteger(val)) { - expect(bigintStats[key]).toBe(BigInt(val)); - } else { - expect(Number(bigintStats[key]) - val).toBeLessThan(1); - } - } -} - -const runSyncTest = (func, arg) => { - const startTime = process.hrtime.bigint(); - const bigintStats = func(arg, { bigint: true }); - const numStats = func(arg); - const endTime = process.hrtime.bigint(); - const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6); - verifyStats(bigintStats, numStats, allowableDelta); -}; - -test("fs.statSync", () => { - const filename = getFilename(); - runSyncTest(fs.statSync, filename); -}); - -if (!process.platform.startsWith("win")) { - test("fs.lstatSync", () => { - const filename = getFilename(); - const link = `${filename}-link`; - fs.symlinkSync(filename, link); - runSyncTest(fs.lstatSync, link); - }); -} - -test("fs.fstatSync", () => { - const filename = getFilename(); - const fd = fs.openSync(filename, "r"); - runSyncTest(fs.fstatSync, fd); - fs.closeSync(fd); -}); - -test("fs.statSync with non-existent file", () => { - expect(() => fs.statSync("does_not_exist")).toThrow(expect.objectContaining({ code: "ENOENT" })); - expect(fs.statSync("does_not_exist", { throwIfNoEntry: false })).toBeUndefined(); -}); - -test("fs.lstatSync with non-existent file", () => { - expect(() => fs.lstatSync("does_not_exist")).toThrow(expect.objectContaining({ code: "ENOENT" })); - expect(fs.lstatSync("does_not_exist", { throwIfNoEntry: false })).toBeUndefined(); -}); - -test("fs.fstatSync with invalid file descriptor", () => { - expect(() => fs.fstatSync(9999)).toThrow(expect.objectContaining({ code: "EBADF" })); - expect(() => fs.fstatSync(9999, { throwIfNoEntry: false })).toThrow(expect.objectContaining({ code: "EBADF" })); -}); - -const runCallbackTest = (func, arg) => { - return new Promise(resolve => { - const startTime = process.hrtime.bigint(); - func(arg, { bigint: true }, (err, bigintStats) => { - expect(err).toBeFalsy(); - func(arg, (err, numStats) => { - expect(err).toBeFalsy(); - const endTime = process.hrtime.bigint(); - const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6); - verifyStats(bigintStats, numStats, allowableDelta); - resolve(); - }); - }); - }); -}; - -test("fs.stat callback", async () => { - const filename = getFilename(); - await runCallbackTest(fs.stat, filename); -}); - -if (!process.platform.startsWith("win")) { - test("fs.lstat callback", async () => { - const filename = getFilename(); - const link = `${filename}-link`; - fs.symlinkSync(filename, link); - await runCallbackTest(fs.lstat, link); - }); -} - -test("fs.fstat callback", async () => { - const filename = getFilename(); - const fd = fs.openSync(filename, "r"); - await runCallbackTest(fs.fstat, fd); - fs.closeSync(fd); -}); - -const runPromiseTest = async (func, arg) => { - const startTime = process.hrtime.bigint(); - const bigintStats = await func(arg, { bigint: true }); - const numStats = await func(arg); - const endTime = process.hrtime.bigint(); - const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6); - verifyStats(bigintStats, numStats, allowableDelta); -}; - -test("promiseFs.stat", async () => { - const filename = getFilename(); - await runPromiseTest(promiseFs.stat, filename); -}); - -if (!process.platform.startsWith("win")) { - test("promiseFs.lstat", async () => { - const filename = getFilename(); - const link = `${filename}-link`; - fs.symlinkSync(filename, link); - await runPromiseTest(promiseFs.lstat, link); - }); -} - -test("promiseFs handle.stat", async () => { - const filename = getFilename(); - const handle = await promiseFs.open(filename, "r"); - const startTime = process.hrtime.bigint(); - const bigintStats = await handle.stat({ bigint: true }); - const numStats = await handle.stat(); - const endTime = process.hrtime.bigint(); - const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6); - verifyStats(bigintStats, numStats, allowableDelta); - await handle.close(); -}); - -test("BigIntStats Date properties can be set before reading them", done => { - fs.stat(__filename, { bigint: true }, (err, s) => { - expect(err).toBeFalsy(); - s.atime = 2; - s.mtime = 3; - s.ctime = 4; - s.birthtime = 5; - - expect(s.atime).toBe(2); - expect(s.mtime).toBe(3); - expect(s.ctime).toBe(4); - expect(s.birthtime).toBe(5); - done(); - }); -}); - -test("BigIntStats Date properties can be set after reading them", done => { - fs.stat(__filename, { bigint: true }, (err, s) => { - expect(err).toBeFalsy(); - // eslint-disable-next-line no-unused-expressions - s.atime, s.mtime, s.ctime, s.birthtime; - - s.atime = 2; - s.mtime = 3; - s.ctime = 4; - s.birthtime = 5; - - expect(s.atime).toBe(2); - expect(s.mtime).toBe(3); - expect(s.ctime).toBe(4); - expect(s.birthtime).toBe(5); - done(); - }); -}); - -//<#END_FILE: test-fs-stat-bigint.js diff --git a/test/js/node/test/parallel/fs-stream-double-close.test.js b/test/js/node/test/parallel/fs-stream-double-close.test.js deleted file mode 100644 index 0e89c7bf26..0000000000 --- a/test/js/node/test/parallel/fs-stream-double-close.test.js +++ /dev/null @@ -1,84 +0,0 @@ -//#FILE: test-fs-stream-double-close.js -//#SHA1: 25fa219f7ee462e67611751f996393afc1869490 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const tmpdir = path.join(os.tmpdir(), "node-test-fs-stream-double-close"); -beforeAll(() => { - if (!fs.existsSync(tmpdir)) { - fs.mkdirSync(tmpdir, { recursive: true }); - } -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test("test1 with ReadStream", () => { - test1(fs.createReadStream(__filename)); -}); - -test("test2 with ReadStream", () => { - test2(fs.createReadStream(__filename)); -}); - -test("test3 with ReadStream", () => { - test3(fs.createReadStream(__filename)); -}); - -test("test1 with WriteStream", () => { - test1(fs.createWriteStream(path.join(tmpdir, "dummy1"))); -}); - -test("test2 with WriteStream", () => { - test2(fs.createWriteStream(path.join(tmpdir, "dummy2"))); -}); - -test("test3 with WriteStream", () => { - test3(fs.createWriteStream(path.join(tmpdir, "dummy3"))); -}); - -function test1(stream) { - stream.destroy(); - stream.destroy(); -} - -function test2(stream) { - stream.destroy(); - stream.on("open", jest.fn()); -} - -function test3(stream) { - const openHandler = jest.fn(); - stream.on("open", openHandler); - stream.emit("open"); - expect(openHandler).toHaveBeenCalledTimes(1); - stream.destroy(); - stream.destroy(); -} - -//<#END_FILE: test-fs-stream-double-close.js diff --git a/test/js/node/test/parallel/fs-symlink-buffer-path.test.js b/test/js/node/test/parallel/fs-symlink-buffer-path.test.js deleted file mode 100644 index 17bde4dccb..0000000000 --- a/test/js/node/test/parallel/fs-symlink-buffer-path.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-fs-symlink-buffer-path.js -//#SHA1: 73fa7d9b492bd23730f1a8763caac92a9f4a1896 -//----------------- -'use strict'; -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const canCreateSymLink = () => { - try { - fs.symlinkSync('test-file', 'test-symlink'); - fs.unlinkSync('test-symlink'); - return true; - } catch (err) { - return false; - } -}; - -if (!canCreateSymLink()) { - test.skip('insufficient privileges', () => {}); -} else { - const tmpdir = path.join(os.tmpdir(), 'test-fs-symlink-buffer-path'); - const fixturesPath = path.join(__dirname, '..', 'fixtures'); - - beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); - }); - - afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); - }); - - test('creating and reading symbolic link', async () => { - const linkData = path.join(fixturesPath, 'cycles', 'root.js'); - const linkPath = path.join(tmpdir, 'symlink1.js'); - - fs.symlinkSync(Buffer.from(linkData), linkPath); - - const linkStats = await fs.promises.lstat(linkPath); - const linkTime = linkStats.mtime.getTime(); - - const fileStats = await fs.promises.stat(linkPath); - const fileTime = fileStats.mtime.getTime(); - - const destination = await fs.promises.readlink(linkPath); - expect(destination).toBe(linkData); - - expect(linkTime).not.toBe(fileTime); - }); -} - -//<#END_FILE: test-fs-symlink-buffer-path.js diff --git a/test/js/node/test/parallel/fs-symlink.test.js b/test/js/node/test/parallel/fs-symlink.test.js deleted file mode 100644 index c4e68336ba..0000000000 --- a/test/js/node/test/parallel/fs-symlink.test.js +++ /dev/null @@ -1,148 +0,0 @@ -//#FILE: test-fs-symlink.js -//#SHA1: 4861a453e314d789a1b933d7179da96b7a35378c -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const canCreateSymLink = () => { - try { - fs.symlinkSync("", ""); - fs.unlinkSync(""); - return true; - } catch (e) { - return false; - } -}; - -if (!canCreateSymLink()) { - it.skip("insufficient privileges", () => {}); -} else { - let linkTime; - let fileTime; - const tmpdir = os.tmpdir(); - - beforeEach(() => { - jest.spyOn(fs, "symlink"); - jest.spyOn(fs, "lstat"); - jest.spyOn(fs, "stat"); - jest.spyOn(fs, "readlink"); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - test("Test creating and reading symbolic link", async () => { - const linkData = path.resolve(__dirname, "../fixtures/cycles/root.js"); - const linkPath = path.resolve(tmpdir, "symlink1.js"); - - await new Promise(resolve => { - fs.symlink(linkData, linkPath, resolve); - }); - - expect(fs.symlink).toHaveBeenCalled(); - - await new Promise(resolve => { - fs.lstat(linkPath, (err, stats) => { - expect(err).toBeNull(); - linkTime = stats.mtime.getTime(); - resolve(); - }); - }); - - await new Promise(resolve => { - fs.stat(linkPath, (err, stats) => { - expect(err).toBeNull(); - fileTime = stats.mtime.getTime(); - resolve(); - }); - }); - - await new Promise(resolve => { - fs.readlink(linkPath, (err, destination) => { - expect(err).toBeNull(); - expect(destination).toBe(linkData); - resolve(); - }); - }); - }); - - test("Test invalid symlink", async () => { - const linkData = path.resolve(__dirname, "../fixtures/not/exists/file"); - const linkPath = path.resolve(tmpdir, "symlink2.js"); - - await new Promise(resolve => { - fs.symlink(linkData, linkPath, resolve); - }); - - expect(fs.existsSync(linkPath)).toBe(false); - }); - - test("Test invalid inputs", () => { - const invalidInputs = [false, 1, {}, [], null, undefined]; - const errObj = expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringMatching(/target|path/), - }); - - invalidInputs.forEach(input => { - expect(() => fs.symlink(input, "", () => {})).toThrow(errObj); - expect(() => fs.symlinkSync(input, "")).toThrow(errObj); - - expect(() => fs.symlink("", input, () => {})).toThrow(errObj); - expect(() => fs.symlinkSync("", input)).toThrow(errObj); - }); - }); - - test("Test invalid type inputs", () => { - const errObj = expect.objectContaining({ - code: "ERR_INVALID_ARG_VALUE", - name: "TypeError", - }); - - expect(() => fs.symlink("", "", "🍏", () => {})).toThrow(errObj); - expect(() => fs.symlinkSync("", "", "🍏")).toThrow(errObj); - - expect(() => fs.symlink("", "", "nonExistentType", () => {})).toThrow(errObj); - expect(() => fs.symlinkSync("", "", "nonExistentType")).toThrow(errObj); - expect(fs.promises.symlink("", "", "nonExistentType")).rejects.toMatchObject(errObj); - - expect(() => fs.symlink("", "", false, () => {})).toThrow(errObj); - expect(() => fs.symlinkSync("", "", false)).toThrow(errObj); - expect(fs.promises.symlink("", "", false)).rejects.toMatchObject(errObj); - - expect(() => fs.symlink("", "", {}, () => {})).toThrow(errObj); - expect(() => fs.symlinkSync("", "", {})).toThrow(errObj); - expect(fs.promises.symlink("", "", {})).rejects.toMatchObject(errObj); - }); - - test("Link time should not be equal to file time", () => { - expect(linkTime).not.toBe(fileTime); - }); -} - -//<#END_FILE: test-fs-symlink.js diff --git a/test/js/node/test/parallel/fs-truncate-clear-file-zero.test.js b/test/js/node/test/parallel/fs-truncate-clear-file-zero.test.js deleted file mode 100644 index 8dc9566b1f..0000000000 --- a/test/js/node/test/parallel/fs-truncate-clear-file-zero.test.js +++ /dev/null @@ -1,67 +0,0 @@ -//#FILE: test-fs-truncate-clear-file-zero.js -//#SHA1: 28aa057c9903ea2436c340ccecfec093c647714c -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -// This test ensures that `fs.truncate` opens the file with `r+` and not `w`, -// which had earlier resulted in the target file's content getting zeroed out. -// https://github.com/nodejs/node-v0.x-archive/issues/6233 - -const filename = path.join(os.tmpdir(), "truncate-file.txt"); - -beforeEach(() => { - // Clean up any existing test file - try { - fs.unlinkSync(filename); - } catch (err) { - if (err.code !== "ENOENT") throw err; - } -}); - -test("fs.truncateSync", () => { - fs.writeFileSync(filename, "0123456789"); - expect(fs.readFileSync(filename, "utf8")).toBe("0123456789"); - fs.truncateSync(filename, 5); - expect(fs.readFileSync(filename, "utf8")).toBe("01234"); -}); - -test("fs.truncate", async () => { - fs.writeFileSync(filename, "0123456789"); - expect(fs.readFileSync(filename, "utf8")).toBe("0123456789"); - - await new Promise((resolve, reject) => { - fs.truncate(filename, 5, err => { - if (err) reject(err); - else resolve(); - }); - }); - - expect(fs.readFileSync(filename, "utf8")).toBe("01234"); -}); - -//<#END_FILE: test-fs-truncate-clear-file-zero.js diff --git a/test/js/node/test/parallel/fs-truncate-sync.test.js b/test/js/node/test/parallel/fs-truncate-sync.test.js deleted file mode 100644 index cdf9a5f739..0000000000 --- a/test/js/node/test/parallel/fs-truncate-sync.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-fs-truncate-sync.js -//#SHA1: 6b4ccbf9b9fab199c6b258374cf0a1665b1c21fe -//----------------- -"use strict"; - -const path = require("path"); -const fs = require("fs"); -const tmpdir = require("../common/tmpdir"); -const tmp = tmpdir.path; - -describe("fs.truncateSync", () => { - beforeEach(() => { - tmpdir.refresh(); - }); - - test("truncates file correctly", () => { - const filename = path.resolve(tmp, "truncate-sync-file.txt"); - - fs.writeFileSync(filename, "hello world", "utf8"); - - const fd = fs.openSync(filename, "r+"); - - fs.truncateSync(fd, 5); - expect(fs.readFileSync(fd)).toEqual(Buffer.from("hello")); - - fs.closeSync(fd); - fs.unlinkSync(filename); - }); -}); - -//<#END_FILE: test-fs-truncate-sync.js diff --git a/test/js/node/test/parallel/fs-unlink-type-check.test.js b/test/js/node/test/parallel/fs-unlink-type-check.test.js deleted file mode 100644 index 4f819feecd..0000000000 --- a/test/js/node/test/parallel/fs-unlink-type-check.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-fs-unlink-type-check.js -//#SHA1: 337e42f3b15589a7652c32c0a1c92292abf098d0 -//----------------- -"use strict"; - -const fs = require("fs"); - -test("fs.unlink and fs.unlinkSync with invalid types", () => { - const invalidTypes = [false, 1, {}, [], null, undefined]; - - invalidTypes.forEach(invalidType => { - expect(() => fs.unlink(invalidType, jest.fn())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => fs.unlinkSync(invalidType)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-fs-unlink-type-check.js diff --git a/test/js/node/test/parallel/fs-util-validateoffsetlength.test.js b/test/js/node/test/parallel/fs-util-validateoffsetlength.test.js deleted file mode 100644 index 1cc49c3be9..0000000000 --- a/test/js/node/test/parallel/fs-util-validateoffsetlength.test.js +++ /dev/null @@ -1,85 +0,0 @@ -//#FILE: test-fs-util-validateoffsetlength.js -//#SHA1: d5c952d2e87072352a6a60351ede415d1925cf21 -//----------------- -'use strict'; - -// Implement the functions we want to test -function validateOffsetLengthRead(offset, length, byteLength) { - if (offset < 0) { - throw new RangeError('The value of "offset" is out of range. ' + - `It must be >= 0. Received ${offset}`); - } - if (length < 0) { - throw new RangeError('The value of "length" is out of range. ' + - `It must be >= 0. Received ${length}`); - } - if (offset + length > byteLength) { - throw new RangeError('The value of "length" is out of range. ' + - `It must be <= ${byteLength - offset}. Received ${length}`); - } -} - -function validateOffsetLengthWrite(offset, length, byteLength) { - if (offset > byteLength) { - throw new RangeError('The value of "offset" is out of range. ' + - `It must be <= ${byteLength}. Received ${offset}`); - } - if (length > byteLength - offset) { - throw new RangeError('The value of "length" is out of range. ' + - `It must be <= ${byteLength - offset}. Received ${length}`); - } -} - -describe('validateOffsetLengthRead', () => { - test('throws RangeError when offset is negative', () => { - const offset = -1; - expect(() => validateOffsetLengthRead(offset, 0, 0)).toThrow(expect.objectContaining({ - name: 'RangeError', - message: expect.stringContaining(`It must be >= 0. Received ${offset}`) - })); - }); - - test('throws RangeError when length is negative', () => { - const length = -1; - expect(() => validateOffsetLengthRead(0, length, 0)).toThrow(expect.objectContaining({ - name: 'RangeError', - message: expect.stringContaining(`It must be >= 0. Received ${length}`) - })); - }); - - test('throws RangeError when length is out of range', () => { - const offset = 1; - const length = 1; - const byteLength = offset + length - 1; - expect(() => validateOffsetLengthRead(offset, length, byteLength)).toThrow(expect.objectContaining({ - name: 'RangeError', - message: expect.stringContaining(`It must be <= ${byteLength - offset}. Received ${length}`) - })); - }); -}); - -describe('validateOffsetLengthWrite', () => { - const kIoMaxLength = 2 ** 31 - 1; - - test('throws RangeError when offset > byteLength', () => { - const offset = 100; - const length = 100; - const byteLength = 50; - expect(() => validateOffsetLengthWrite(offset, length, byteLength)).toThrow(expect.objectContaining({ - name: 'RangeError', - message: expect.stringContaining(`It must be <= ${byteLength}. Received ${offset}`) - })); - }); - - test('throws RangeError when byteLength < kIoMaxLength and length > byteLength - offset', () => { - const offset = kIoMaxLength - 150; - const length = 200; - const byteLength = kIoMaxLength - 100; - expect(() => validateOffsetLengthWrite(offset, length, byteLength)).toThrow(expect.objectContaining({ - name: 'RangeError', - message: expect.stringContaining(`It must be <= ${byteLength - offset}. Received ${length}`) - })); - }); -}); - -//<#END_FILE: test-fs-util-validateoffsetlength.js diff --git a/test/js/node/test/parallel/fs-watch-abort-signal.test.js b/test/js/node/test/parallel/fs-watch-abort-signal.test.js deleted file mode 100644 index 030e33814e..0000000000 --- a/test/js/node/test/parallel/fs-watch-abort-signal.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-fs-watch-abort-signal.js -//#SHA1: 6f0b7fcc2f597faa8e1353559d5d007cd744614a -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const isIBMi = process.platform === 'os400'; - -if (isIBMi) { - test.skip('IBMi does not support `fs.watch()`', () => {}); -} else { - const tmpdir = path.join(os.tmpdir(), 'test-fs-watch-abort-signal'); - const emptyFile = path.join(tmpdir, 'empty.js'); - - beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); - fs.writeFileSync(emptyFile, ''); - }); - - afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); - }); - - test('Signal aborted after creating the watcher', (done) => { - const ac = new AbortController(); - const { signal } = ac; - const watcher = fs.watch(emptyFile, { signal }); - watcher.once('close', () => { - done(); - }); - setImmediate(() => ac.abort()); - }); - - test('Signal aborted before creating the watcher', (done) => { - const signal = AbortSignal.abort(); - const watcher = fs.watch(emptyFile, { signal }); - watcher.once('close', () => { - done(); - }); - }); -} - -//<#END_FILE: test-fs-watch-abort-signal.js diff --git a/test/js/node/test/parallel/fs-watch-close-when-destroyed.test.js b/test/js/node/test/parallel/fs-watch-close-when-destroyed.test.js deleted file mode 100644 index 5749125642..0000000000 --- a/test/js/node/test/parallel/fs-watch-close-when-destroyed.test.js +++ /dev/null @@ -1,63 +0,0 @@ -//#FILE: test-fs-watch-close-when-destroyed.js -//#SHA1: f062b7243d0c42722a289a6228d4c2c1a503be1b -//----------------- -"use strict"; - -// This tests that closing a watcher when the underlying handle is -// already destroyed will result in a noop instead of a crash. - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -// fs-watch on folders have limited capability in AIX. -// The testcase makes use of folder watching, and causes -// hang. This behavior is documented. Skip this for AIX. - -if (process.platform === "aix") { - it.skip("folder watch capability is limited in AIX."); -} else if (process.platform === "os400") { - it.skip("IBMi does not support `fs.watch()`"); -} else { - let root; - - beforeEach(() => { - root = path.join(os.tmpdir(), "watched-directory-" + Math.random().toString(36).slice(2)); - fs.mkdirSync(root); - }); - - afterEach(() => { - try { - fs.rmdirSync(root); - } catch (error) { - // Ignore errors, directory might already be removed - } - }); - - it("should not crash when closing watcher after handle is destroyed", done => { - const watcher = fs.watch(root, { persistent: false, recursive: false }); - - // The following listeners may or may not be invoked. - - watcher.addListener("error", () => { - setTimeout( - () => { - watcher.close(); - }, // Should not crash if it's invoked - 10, - ); - }); - - watcher.addListener("change", () => { - setTimeout(() => { - watcher.close(); - }, 10); - }); - - fs.rmdirSync(root); - // Wait for the listener to hit - setTimeout(done, 100); - }); -} - -//<#END_FILE: test-fs-watch-close-when-destroyed.js diff --git a/test/js/node/test/parallel/fs-watch-encoding.test.js b/test/js/node/test/parallel/fs-watch-encoding.test.js deleted file mode 100644 index 472a77d57a..0000000000 --- a/test/js/node/test/parallel/fs-watch-encoding.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-fs-watch-encoding.js -//#SHA1: 63f7e4008743417c7ee5995bbf16a28ade764e48 -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const tmpdir = path.join(os.tmpdir(), 'test-fs-watch-encoding'); -const fn = '新建文夹件.txt'; -const a = path.join(tmpdir, fn); - -let interval; - -beforeAll(() => { - if (process.platform === 'aix') { - return test.skip('folder watch capability is limited in AIX.'); - } - if (process.platform === 'os400') { - return test.skip('IBMi does not support `fs.watch()`'); - } - - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); -}); - -afterAll(() => { - clearInterval(interval); - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -const watcherTests = [ - { - name: 'with hex encoding', - options: { encoding: 'hex' }, - expectedFilenames: ['e696b0e5bbbae69687e5a4b9e4bbb62e747874', null], - }, - { - name: 'without encoding option', - options: {}, - expectedFilenames: [fn, null], - }, - { - name: 'with buffer encoding', - options: { encoding: 'buffer' }, - expectedFilenames: [Buffer.from(fn), null], - }, -]; - -watcherTests.forEach(({ name, options, expectedFilenames }) => { - test(`fs.watch ${name}`, (done) => { - const watcher = fs.watch(tmpdir, options, (event, filename) => { - if (expectedFilenames.some(expected => - expected instanceof Buffer - ? expected.equals(filename) - : expected === filename)) { - watcher.close(); - done(); - } - }); - - // Start the interval after setting up the watcher - if (!interval) { - interval = setInterval(() => { - const fd = fs.openSync(a, 'w+'); - fs.closeSync(fd); - fs.unlinkSync(a); - }, 100); - } - }, 10000); // Increased timeout to allow for file operations -}); - -//<#END_FILE: test-fs-watch-encoding.js diff --git a/test/js/node/test/parallel/fs-watch-file-enoent-after-deletion.test.js b/test/js/node/test/parallel/fs-watch-file-enoent-after-deletion.test.js deleted file mode 100644 index ee439c8272..0000000000 --- a/test/js/node/test/parallel/fs-watch-file-enoent-after-deletion.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-fs-watch-file-enoent-after-deletion.js -//#SHA1: d6c93db608d119bd35fcab0e1e9307bfd6558b68 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -// Make sure the deletion event gets reported in the following scenario: -// 1. Watch a file. -// 2. The initial stat() goes okay. -// 3. Something deletes the watched file. -// 4. The second stat() fails with ENOENT. - -// The second stat() translates into the first 'change' event but a logic error -// stopped it from getting emitted. -// https://github.com/nodejs/node-v0.x-archive/issues/4027 - -test("fs.watchFile reports deletion", done => { - const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "test-")); - const filename = path.join(tmpdir, "watched"); - fs.writeFileSync(filename, "quis custodiet ipsos custodes"); - - const watcher = jest.fn(); - fs.watchFile(filename, { interval: 50 }, watcher); - - setTimeout(() => { - fs.unlinkSync(filename); - - setTimeout(() => { - expect(watcher).toHaveBeenCalledTimes(1); - const [curr, prev] = watcher.mock.calls[0]; - expect(curr.nlink).toBe(0); - expect(prev.nlink).toBe(1); - - fs.unwatchFile(filename); - fs.rmdirSync(tmpdir); - done(); - }, 100); - }, 100); -}); - -//<#END_FILE: test-fs-watch-file-enoent-after-deletion.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-add-file-to-existing-subfolder.test.js b/test/js/node/test/parallel/fs-watch-recursive-add-file-to-existing-subfolder.test.js deleted file mode 100644 index 3e2c067792..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-add-file-to-existing-subfolder.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-fs-watch-recursive-add-file-to-existing-subfolder.js -//#SHA1: 7d4414be8a9ba35f2ebdb685037951133137b6ef -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const testDir = path.join(os.tmpdir(), 'test-fs-watch-recursive-add-file-to-existing-subfolder'); - -beforeAll(() => { - if (fs.existsSync(testDir)) { - fs.rmSync(testDir, { recursive: true, force: true }); - } - fs.mkdirSync(testDir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(testDir, { recursive: true, force: true }); -}); - -test('fs.watch detects file added to existing subfolder', (done) => { - const rootDirectory = fs.mkdtempSync(path.join(testDir, 'test-')); - const testDirectory = path.join(rootDirectory, 'test-4'); - fs.mkdirSync(testDirectory); - - const file = 'folder-5'; - const filePath = path.join(testDirectory, file); - fs.mkdirSync(filePath); - - const subfolderPath = path.join(filePath, 'subfolder-6'); - fs.mkdirSync(subfolderPath); - - const childrenFile = 'file-7.txt'; - const childrenAbsolutePath = path.join(subfolderPath, childrenFile); - const relativePath = path.join(file, path.basename(subfolderPath), childrenFile); - - const watcher = fs.watch(testDirectory, { recursive: true }); - let watcherClosed = false; - - watcher.on('change', (event, filename) => { - expect(event).toBe('rename'); - - if (filename === relativePath) { - watcher.close(); - watcherClosed = true; - expect(watcherClosed).toBe(true); - done(); - } - }); - - // Do the write with a delay to ensure that the OS is ready to notify us. - setTimeout(() => { - fs.writeFileSync(childrenAbsolutePath, 'world'); - }, 200); -}, 10000); // Increased timeout to 10 seconds - -//<#END_FILE: test-fs-watch-recursive-add-file-to-existing-subfolder.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-add-file-with-url.test.js b/test/js/node/test/parallel/fs-watch-recursive-add-file-with-url.test.js deleted file mode 100644 index 2131b1dbe5..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-add-file-with-url.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-fs-watch-recursive-add-file-with-url.js -//#SHA1: e6498ea80abdf69cb66d888bbd7d631931970c0a -//----------------- -"use strict"; - -const { setTimeout } = require("timers/promises"); -const path = require("path"); -const fs = require("fs"); -const { pathToFileURL } = require("url"); -const os = require("os"); - -const isIBMi = process.platform === "os400"; -const isAIX = process.platform === "aix"; - -if (isIBMi) { - it.skip("IBMi does not support `fs.watch()`", () => {}); -} else if (isAIX) { - it.skip("folder watch capability is limited in AIX.", () => {}); -} else { - it("should watch for file changes using URL as path", async () => { - const testDir = fs.mkdtempSync(path.join(os.tmpdir(), "test-")); - - // Add a file to already watching folder, and use URL as the path - const rootDirectory = fs.mkdtempSync(path.join(testDir, path.sep)); - const testDirectory = path.join(rootDirectory, "test-5"); - fs.mkdirSync(testDirectory); - - const filePath = path.join(testDirectory, "file-8.txt"); - const url = pathToFileURL(testDirectory); - - const watcher = fs.watch(url, { recursive: true }); - let watcherClosed = false; - - const watchPromise = new Promise(resolve => { - watcher.on("change", function (event, filename) { - expect(event).toBe("rename"); - - if (filename === path.basename(filePath)) { - watcher.close(); - watcherClosed = true; - resolve(); - } - }); - }); - - await setTimeout(100); - fs.writeFileSync(filePath, "world"); - - await watchPromise; - - expect(watcherClosed).toBe(true); - }, 10000); // Increase timeout to 10 seconds -} - -//<#END_FILE: test-fs-watch-recursive-add-file-with-url.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-add-file.test.js b/test/js/node/test/parallel/fs-watch-recursive-add-file.test.js deleted file mode 100644 index 00c5fcf7e6..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-add-file.test.js +++ /dev/null @@ -1,63 +0,0 @@ -//#FILE: test-fs-watch-recursive-add-file.js -//#SHA1: e87d2c9f4789a6e6a83fbdca56e39683625bd0af -//----------------- -"use strict"; - -const path = require("path"); -const fs = require("fs"); -const os = require("os"); - -const isIBMi = os.platform() === "os400"; -const isAIX = os.platform() === "aix"; - -if (isIBMi) { - it.skip("IBMi does not support `fs.watch()`", () => {}); -} else if (isAIX) { - it.skip("folder watch capability is limited in AIX.", () => {}); -} else { - const tmpdir = { - path: path.join(os.tmpdir(), "jest-test-fs-watch-recursive-add-file"), - refresh: () => { - if (fs.existsSync(tmpdir.path)) { - fs.rmSync(tmpdir.path, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir.path, { recursive: true }); - }, - }; - - beforeEach(() => { - tmpdir.refresh(); - }); - - it("should detect file added to already watching folder", done => { - const rootDirectory = fs.mkdtempSync(tmpdir.path + path.sep); - const testDirectory = path.join(rootDirectory, "test-1"); - fs.mkdirSync(testDirectory); - - const testFile = path.join(testDirectory, "file-1.txt"); - - const watcher = fs.watch(testDirectory, { recursive: true }); - let watcherClosed = false; - - watcher.on("change", function (event, filename) { - expect(event).toBe("rename"); - - if (filename === path.basename(testFile)) { - watcher.close(); - watcherClosed = true; - expect(watcherClosed).toBe(true); - done(); - } - }); - - // Do the write with a delay to ensure that the OS is ready to notify us. - setTimeout( - () => { - fs.writeFileSync(testFile, "world"); - }, - process.platform === "win32" ? 200 : 100, - ); - }); -} - -//<#END_FILE: test-fs-watch-recursive-add-file.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-add-folder.test.js b/test/js/node/test/parallel/fs-watch-recursive-add-folder.test.js deleted file mode 100644 index dbe75002f5..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-add-folder.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-fs-watch-recursive-add-folder.js -//#SHA1: 4c2908ccc8502f5f760963a9b9a6db6ddadd4c1c -//----------------- -"use strict"; - -const { setTimeout } = require("timers/promises"); -const assert = require("assert"); -const path = require("path"); -const fs = require("fs"); -const os = require("os"); - -const isIBMi = os.platform() === "os400"; -const isAIX = os.platform() === "aix"; - -if (isIBMi) { - it.skip("IBMi does not support `fs.watch()`", () => {}); -} else if (isAIX) { - it.skip("folder watch capability is limited in AIX.", () => {}); -} else { - const testDir = fs.mkdtempSync(path.join(os.tmpdir(), "test-")); - - afterAll(() => { - fs.rmSync(testDir, { recursive: true, force: true }); - }); - - test("Add a folder to already watching folder", async () => { - // Add a folder to already watching folder - - const rootDirectory = fs.mkdtempSync(path.join(testDir, "root-")); - const testDirectory = path.join(rootDirectory, "test-2"); - fs.mkdirSync(testDirectory); - - const testFile = path.join(testDirectory, "folder-2"); - - const watcher = fs.watch(testDirectory, { recursive: true }); - let watcherClosed = false; - - const watchPromise = new Promise(resolve => { - watcher.on("change", function (event, filename) { - expect(event).toBe("rename"); - - if (filename === path.basename(testFile)) { - watcher.close(); - watcherClosed = true; - resolve(); - } - }); - }); - - await setTimeout(100); - fs.mkdirSync(testFile); - - await watchPromise; - - expect(watcherClosed).toBe(true); - }); -} - -//<#END_FILE: test-fs-watch-recursive-add-folder.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-assert-leaks.test.js b/test/js/node/test/parallel/fs-watch-recursive-assert-leaks.test.js deleted file mode 100644 index 69ec1c857c..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-assert-leaks.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-fs-watch-recursive-assert-leaks.js -//#SHA1: 316f1b184840ce5a8f2f92f4ab205038f4acaf9d -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); -const { setTimeout } = require('timers/promises'); - -const testDir = path.join(os.tmpdir(), 'test-fs-watch-recursive-assert-leaks'); - -beforeAll(() => { - if (fs.existsSync(testDir)) { - fs.rmSync(testDir, { recursive: true, force: true }); - } - fs.mkdirSync(testDir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(testDir, { recursive: true, force: true }); -}); - -// Skip test for IBMi and AIX -const isIBMi = process.platform === 'os400'; -const isAIX = process.platform === 'aix'; - -if (isIBMi) { - test.skip('IBMi does not support `fs.watch()`', () => {}); -} else if (isAIX) { - test.skip('folder watch capability is limited in AIX', () => {}); -} else { - test('recursive watch does not leak handles', async () => { - const rootDirectory = fs.mkdtempSync(path.join(testDir, 'root-')); - const testDirectory = path.join(rootDirectory, 'test-7'); - const filePath = path.join(testDirectory, 'only-file.txt'); - fs.mkdirSync(testDirectory); - - let watcherClosed = false; - const watcher = fs.watch(testDirectory, { recursive: true }); - - const watchPromise = new Promise((resolve) => { - watcher.on('change', async (event, filename) => { - await setTimeout(100); - if (filename === path.basename(filePath)) { - watcher.close(); - watcherClosed = true; - resolve(); - } - await setTimeout(100); - expect(process._getActiveHandles().some((handle) => handle.constructor.name === 'StatWatcher')).toBe(false); - }); - }); - - // Do the write with a delay to ensure that the OS is ready to notify us. - await setTimeout(200); - fs.writeFileSync(filePath, 'content'); - - await watchPromise; - expect(watcherClosed).toBe(true); - }, 10000); // Increased timeout to 10 seconds -} - -//<#END_FILE: test-fs-watch-recursive-assert-leaks.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-linux-parallel-remove.test.js b/test/js/node/test/parallel/fs-watch-recursive-linux-parallel-remove.test.js deleted file mode 100644 index b109622f57..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-linux-parallel-remove.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-fs-watch-recursive-linux-parallel-remove.js -//#SHA1: ed10536d8d54febe24a3dcf494a26eab06bc4f66 -//----------------- -"use strict"; - -const path = require("node:path"); -const fs = require("node:fs"); -const { spawn } = require("node:child_process"); -const os = require("node:os"); - -// Skip test if not running on Linux -if (os.platform() !== "linux") { - test.skip("This test can run only on Linux", () => {}); -} else { - // Test that the watcher do not crash if the file "disappears" while - // watch is being set up. - - let testDir; - let watcher; - - beforeEach(() => { - testDir = fs.mkdtempSync(path.join(os.tmpdir(), "test-")); - }); - - afterEach(() => { - if (watcher) { - watcher.close(); - } - fs.rmSync(testDir, { recursive: true, force: true }); - }); - - test("fs.watch does not crash on parallel file removal", done => { - watcher = fs.watch(testDir, { recursive: true }); - watcher.on("change", function (event, filename) { - // This console.log makes the error happen - // do not remove - console.log(filename, event); - }); - - const testFile = path.join(testDir, "a"); - const child = spawn( - process.argv[0], - [ - "-e", - `const fs = require('node:fs'); for (let i = 0; i < 10000; i++) { const fd = fs.openSync('${testFile}', 'w'); fs.writeSync(fd, Buffer.from('hello')); fs.rmSync('${testFile}') }`, - ], - { - stdio: "inherit", - }, - ); - - child.on("exit", function () { - watcher.close(); - done(); - }); - }); -} - -//<#END_FILE: test-fs-watch-recursive-linux-parallel-remove.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-sync-write.test.js b/test/js/node/test/parallel/fs-watch-recursive-sync-write.test.js deleted file mode 100644 index fa80c9a8e3..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-sync-write.test.js +++ /dev/null @@ -1,63 +0,0 @@ -//#FILE: test-fs-watch-recursive-sync-write.js -//#SHA1: 436087aa83502744252800b9a93dbe88a4ca3822 -//----------------- -'use strict'; - -const fs = require('node:fs'); -const path = require('node:path'); -const os = require('os'); - -const tmpDir = path.join(os.tmpdir(), 'test-fs-watch-recursive-sync-write'); -const filename = path.join(tmpDir, 'test.file'); - -beforeAll(() => { - if (fs.existsSync(tmpDir)) { - fs.rmSync(tmpDir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpDir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(tmpDir, { recursive: true, force: true }); -}); - -// Skip test for IBMi and AIX -const isIBMi = process.platform === 'os400'; -const isAIX = process.platform === 'aix'; - -if (isIBMi) { - test.skip('IBMi does not support `fs.watch()`', () => {}); -} else if (isAIX) { - test.skip('folder watch capability is limited in AIX', () => {}); -} else { - test('fs.watch detects file creation with recursive option', (done) => { - const timeout = setTimeout(() => { - done(new Error('timed out')); - }, 30000); - - function doWatch() { - const watcher = fs.watch(tmpDir, { recursive: true }, (eventType, _filename) => { - clearTimeout(timeout); - watcher.close(); - expect(eventType).toBe('rename'); - expect(path.join(tmpDir, _filename)).toBe(filename); - done(); - }); - - // Do the write with a delay to ensure that the OS is ready to notify us. - setTimeout(() => { - fs.writeFileSync(filename, 'foobar2'); - }, 200); - } - - if (process.platform === 'darwin') { - // On macOS delay watcher start to avoid leaking previous events. - // Refs: https://github.com/libuv/libuv/pull/4503 - setTimeout(doWatch, 100); - } else { - doWatch(); - } - }, 35000); // Increase timeout to account for the 30 second timeout in the test -} - -//<#END_FILE: test-fs-watch-recursive-sync-write.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-update-file.test.js b/test/js/node/test/parallel/fs-watch-recursive-update-file.test.js deleted file mode 100644 index 331df95da8..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-update-file.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-fs-watch-recursive-update-file.js -//#SHA1: d197449fc5f430b9ce49e7f75b57a44dd4f2259a -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const testDir = path.join(os.tmpdir(), 'test-fs-watch-recursive-update-file'); - -beforeAll(() => { - if (fs.existsSync(testDir)) { - fs.rmSync(testDir, { recursive: true, force: true }); - } - fs.mkdirSync(testDir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(testDir, { recursive: true, force: true }); -}); - -test('Watch a folder and update an already existing file in it', (done) => { - const rootDirectory = fs.mkdtempSync(path.join(testDir, 'test-')); - const testDirectory = path.join(rootDirectory, 'test-0'); - fs.mkdirSync(testDirectory); - - const testFile = path.join(testDirectory, 'file-1.txt'); - fs.writeFileSync(testFile, 'hello'); - - const watcher = fs.watch(testDirectory, { recursive: true }); - - watcher.on('change', (event, filename) => { - expect(event === 'change' || event === 'rename').toBe(true); - - if (filename === path.basename(testFile)) { - watcher.close(); - done(); - } - }); - - // Do the write with a delay to ensure that the OS is ready to notify us. - setTimeout(() => { - fs.writeFileSync(testFile, 'hello'); - }, 200); -}, 10000); // Increased timeout to allow for file system operations - -//<#END_FILE: test-fs-watch-recursive-update-file.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-validate.test.js b/test/js/node/test/parallel/fs-watch-recursive-validate.test.js deleted file mode 100644 index aeed19b67d..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-validate.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-fs-watch-recursive-validate.js -//#SHA1: eb5d9ff1caac7f9d4acf694c43e4f634f538befb -//----------------- -"use strict"; - -const path = require("path"); -const fs = require("fs"); -const os = require("os"); - -const isIBMi = process.platform === "os400"; -const isAIX = process.platform === "aix"; -const isWindows = process.platform === "win32"; -const isOSX = process.platform === "darwin"; - -if (isIBMi) { - test.skip("IBMi does not support `fs.watch()`", () => {}); -} else if (isAIX) { - test.skip("folder watch capability is limited in AIX.", () => {}); -} else { - const tmpdir = { - path: path.join(os.tmpdir(), "jest-fs-watch-recursive-validate"), - refresh: () => { - if (fs.existsSync(tmpdir.path)) { - fs.rmSync(tmpdir.path, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir.path, { recursive: true }); - }, - }; - - beforeEach(() => { - tmpdir.refresh(); - }); - - test("Handle non-boolean values for options.recursive", async () => { - if (!isWindows && !isOSX) { - expect(() => { - const testsubdir = fs.mkdtempSync(tmpdir.path + path.sep); - fs.watch(testsubdir, { recursive: "1" }); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - } - }); -} - -//<#END_FILE: test-fs-watch-recursive-validate.js diff --git a/test/js/node/test/parallel/fs-watch-recursive-watch-file.test.js b/test/js/node/test/parallel/fs-watch-recursive-watch-file.test.js deleted file mode 100644 index bf7b6e0212..0000000000 --- a/test/js/node/test/parallel/fs-watch-recursive-watch-file.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-fs-watch-recursive-watch-file.js -//#SHA1: 1f06958f6f645cb5c80b424a24b046f107ab83ae -//----------------- -"use strict"; - -const path = require("path"); -const fs = require("fs"); -const os = require("os"); - -const isIBMi = os.platform() === "os400"; -const isAIX = os.platform() === "aix"; - -if (isIBMi) { - test.skip("IBMi does not support `fs.watch()`"); -} - -// fs-watch on folders have limited capability in AIX. -// The testcase makes use of folder watching, and causes -// hang. This behavior is documented. Skip this for AIX. - -if (isAIX) { - test.skip("folder watch capability is limited in AIX."); -} - -const platformTimeout = ms => ms * (process.platform === "win32" ? 2 : 1); - -test("Watch a file (not a folder) using fs.watch", async () => { - // Create a temporary directory for testing - const testDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "test-")); - const rootDirectory = await fs.promises.mkdtemp(path.join(testDir, path.sep)); - const testDirectory = path.join(rootDirectory, "test-6"); - await fs.promises.mkdir(testDirectory); - - const filePath = path.join(testDirectory, "only-file.txt"); - await fs.promises.writeFile(filePath, "hello"); - - let watcherClosed = false; - let interval; - - const watcher = fs.watch(filePath, { recursive: true }); - - const watchPromise = new Promise(resolve => { - watcher.on("change", function (event, filename) { - expect(event).toBe("change"); - - if (filename === path.basename(filePath)) { - clearInterval(interval); - interval = null; - watcher.close(); - watcherClosed = true; - resolve(); - } - }); - }); - - interval = setInterval(() => { - fs.writeFileSync(filePath, "world"); - }, platformTimeout(10)); - - await watchPromise; - - expect(watcherClosed).toBe(true); - expect(interval).toBeNull(); -}); - -//<#END_FILE: test-fs-watch-recursive-watch-file.js diff --git a/test/js/node/test/parallel/fs-watch-ref-unref.test.js b/test/js/node/test/parallel/fs-watch-ref-unref.test.js deleted file mode 100644 index c8d92a6eaa..0000000000 --- a/test/js/node/test/parallel/fs-watch-ref-unref.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-fs-watch-ref-unref.js -//#SHA1: ffceabfd7f8fef655b05735b8bba7fb059609980 -//----------------- -"use strict"; - -const fs = require("fs"); - -if (process.platform === "os400") { - test.skip("IBMi does not support `fs.watch()`"); -} - -test("fs.watch() can be unref()ed and ref()ed", () => { - const watcher = fs.watch(__filename, () => { - // This callback should not be called - expect(true).toBe(false); - }); - - watcher.unref(); - - return new Promise(resolve => { - setTimeout( - () => { - watcher.ref(); - watcher.unref(); - resolve(); - }, - process.platform === "win32" ? 100 : 50, - ); - }); -}); - -//<#END_FILE: test-fs-watch-ref-unref.js diff --git a/test/js/node/test/parallel/fs-watch-stop-sync.test.js b/test/js/node/test/parallel/fs-watch-stop-sync.test.js deleted file mode 100644 index fe1eae0f1c..0000000000 --- a/test/js/node/test/parallel/fs-watch-stop-sync.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-fs-watch-stop-sync.js -//#SHA1: 8285d2bd43d2f9be7be525417cf51f9336b2f379 -//----------------- -"use strict"; - -// This test checks that the `stop` event is emitted asynchronously. -// -// If it isn't asynchronous, then the listener will be called during the -// execution of `watch.stop()`. That would be a bug. -// -// If it is asynchronous, then the listener will be removed before the event is -// emitted. - -const fs = require("fs"); - -test("stop event is emitted asynchronously", () => { - const listener = jest.fn(); - - const watch = fs.watchFile(__filename, jest.fn()); - watch.once("stop", listener); - watch.stop(); - watch.removeListener("stop", listener); - - expect(listener).not.toHaveBeenCalled(); -}); - -//<#END_FILE: test-fs-watch-stop-sync.js diff --git a/test/js/node/test/parallel/fs-watch.test.js b/test/js/node/test/parallel/fs-watch.test.js deleted file mode 100644 index 117b2611dc..0000000000 --- a/test/js/node/test/parallel/fs-watch.test.js +++ /dev/null @@ -1,122 +0,0 @@ -//#FILE: test-fs-watch.js -//#SHA1: 07373db00b057e796555cac6f75973e9e4358284 -//----------------- -'use strict'; -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const isIBMi = process.platform === 'os400'; -const isLinux = process.platform === 'linux'; -const isMacOS = process.platform === 'darwin'; -const isWindows = process.platform === 'win32'; -const isAIX = process.platform === 'aix'; - -if (isIBMi) { - test.skip('IBMi does not support `fs.watch()`', () => {}); -} else { - const tmpdir = path.join(os.tmpdir(), 'test-fs-watch'); - - class WatchTestCase { - constructor(shouldInclude, dirName, fileName, field) { - this.dirName = dirName; - this.fileName = fileName; - this.field = field; - this.shouldSkip = !shouldInclude; - } - get dirPath() { return path.join(tmpdir, this.dirName); } - get filePath() { return path.join(this.dirPath, this.fileName); } - } - - const cases = [ - new WatchTestCase( - isLinux || isMacOS || isWindows || isAIX, - 'watch1', - 'foo', - 'filePath' - ), - new WatchTestCase( - isLinux || isMacOS || isWindows, - 'watch2', - 'bar', - 'dirPath' - ), - ]; - - beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); - }); - - afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); - }); - - function doWatchTest(testCase) { - return new Promise((resolve, reject) => { - let interval; - const pathToWatch = testCase[testCase.field]; - const watcher = fs.watch(pathToWatch); - - watcher.on('error', (err) => { - if (interval) { - clearInterval(interval); - interval = null; - } - reject(err); - }); - - watcher.on('change', (eventType, argFilename) => { - if (interval) { - clearInterval(interval); - interval = null; - } - if (isMacOS) - expect(['rename', 'change'].includes(eventType)).toBe(true); - else - expect(eventType).toBe('change'); - expect(argFilename).toBe(testCase.fileName); - - watcher.close(); - watcher.close(); // Closing a closed watcher should be a noop - resolve(); - }); - - const content2 = Date.now() + testCase.fileName.toUpperCase().repeat(1e4); - interval = setInterval(() => { - fs.writeFileSync(testCase.filePath, ''); - fs.writeFileSync(testCase.filePath, content2); - }, 100); - }); - } - - test.each(cases.filter(testCase => !testCase.shouldSkip))( - 'Watch test for $dirName', - async (testCase) => { - fs.mkdirSync(testCase.dirPath, { recursive: true }); - const content1 = Date.now() + testCase.fileName.toLowerCase().repeat(1e4); - fs.writeFileSync(testCase.filePath, content1); - - if (isMacOS) { - await new Promise(resolve => setTimeout(resolve, 100)); - } - - await doWatchTest(testCase); - }, - 30000 // Increase timeout to 30 seconds - ); - - test('fs.watch throws for invalid inputs', () => { - [false, 1, {}, [], null, undefined].forEach((input) => { - expect(() => fs.watch(input, () => {})).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_TYPE', - name: 'TypeError', - message: expect.any(String) - })); - }); - }); -} - -//<#END_FILE: test-fs-watch.js diff --git a/test/js/node/test/parallel/fs-watchfile-bigint.test.js b/test/js/node/test/parallel/fs-watchfile-bigint.test.js deleted file mode 100644 index bf4c61d5b9..0000000000 --- a/test/js/node/test/parallel/fs-watchfile-bigint.test.js +++ /dev/null @@ -1,60 +0,0 @@ -//#FILE: test-fs-watchfile-bigint.js -//#SHA1: 3b2e1f656e95137ca75dedd42e71ba49e6405441 -//----------------- -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const tmpdir = path.join(os.tmpdir(), 'test-fs-watchfile-bigint'); -const enoentFile = path.join(tmpdir, 'non-existent-file'); - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test('fs.watchFile with bigint option', (done) => { - let fileExists = false; - const options = { interval: 0, bigint: true }; - - const watcher = fs.watchFile(enoentFile, options, (curr, prev) => { - if (!fileExists) { - // If the file does not exist, all the fields should be zero and the date - // fields should be UNIX EPOCH time - expect(curr.ino).toBe(0n); - expect(prev.ino).toBe(0n); - // Create the file now, so that the callback will be called back once the - // event loop notices it. - fs.closeSync(fs.openSync(enoentFile, 'w')); - fileExists = true; - } else { - // If the ino (inode) value is greater than zero, it means that the file - // is present in the filesystem and it has a valid inode number. - expect(curr.ino).toBeGreaterThan(0n); - // As the file just got created, previous ino value should be lesser than - // or equal to zero (non-existent file). - expect(prev.ino).toBeLessThanOrEqual(0n); - // Stop watching the file - fs.unwatchFile(enoentFile); - watcher.stop(); // Stopping a stopped watcher should be a noop - done(); - } - }); - - // 'stop' should only be emitted once - stopping a stopped watcher should - // not trigger a 'stop' event. - watcher.on('stop', jest.fn()); - - // Ensure the test times out if the callback is not called twice - jest.setTimeout(10000); -}); - -//<#END_FILE: test-fs-watchfile-bigint.js diff --git a/test/js/node/test/parallel/fs-write-file-buffer.test.js b/test/js/node/test/parallel/fs-write-file-buffer.test.js deleted file mode 100644 index 774f93cfb8..0000000000 --- a/test/js/node/test/parallel/fs-write-file-buffer.test.js +++ /dev/null @@ -1,71 +0,0 @@ -//#FILE: test-fs-write-file-buffer.js -//#SHA1: f721ad0f6969d6cf1ba78f96ccf9600a7f93458d -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const fs = require("fs"); -const path = require("path"); - -let data = [ - "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcH", - "Bw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/", - "2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e", - "Hh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCAAQABADASIAAhEBAxEB/8QA", - "HwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUF", - "BAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK", - "FhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1", - "dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG", - "x8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEB", - "AQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAEC", - "AxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRom", - "JygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE", - "hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU", - "1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDhfBUFl/wk", - "OmPqKJJZw3aiZFBw4z93jnkkc9u9dj8XLfSI/EBt7DTo7ea2Ox5YXVo5FC7g", - "Tjq24nJPXNVtO0KATRvNHCIg3zoWJWQHqp+o4pun+EtJ0zxBq8mnLJa2d1L5", - "0NvnKRjJBUE5PAx3NYxxUY0pRtvYHSc5Ka2X9d7H/9k=", -]; - -data = data.join("\n"); - -let tmpdir; - -beforeEach(() => { - tmpdir = fs.mkdtempSync(path.join(process.env.TEST_TMPDIR || "/tmp", "test-fs-write-file-buffer-")); -}); - -afterEach(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test("writeFileSync with Buffer", () => { - const buf = Buffer.from(data, "base64"); - const testFile = path.join(tmpdir, "test.jpg"); - - fs.writeFileSync(testFile, buf); - - expect(fs.existsSync(testFile)).toBe(true); - expect(fs.readFileSync(testFile)).toEqual(buf); -}); - -//<#END_FILE: test-fs-write-file-buffer.js diff --git a/test/js/node/test/parallel/fs-write-no-fd.test.js b/test/js/node/test/parallel/fs-write-no-fd.test.js deleted file mode 100644 index 6750c80c46..0000000000 --- a/test/js/node/test/parallel/fs-write-no-fd.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-fs-write-no-fd.js -//#SHA1: eade06241743a0d7e72b5239633e1ddd947f3a28 -//----------------- -"use strict"; -const fs = require("fs"); - -test("fs.write with null fd and Buffer throws TypeError", () => { - expect(() => { - fs.write(null, Buffer.allocUnsafe(1), 0, 1, () => {}); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -test("fs.write with null fd and string throws TypeError", () => { - expect(() => { - fs.write(null, "1", 0, 1, () => {}); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-fs-write-no-fd.js diff --git a/test/js/node/test/parallel/fs-write-stream-close-without-callback.test.js b/test/js/node/test/parallel/fs-write-stream-close-without-callback.test.js deleted file mode 100644 index 969a581bb5..0000000000 --- a/test/js/node/test/parallel/fs-write-stream-close-without-callback.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-fs-write-stream-close-without-callback.js -//#SHA1: 63e0c345b440c8cfb157aa84340f387cf314e20f -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const tmpdir = path.join(os.tmpdir(), "test-fs-write-stream-close-without-callback"); - -beforeEach(() => { - // Create a fresh temporary directory before each test - if (!fs.existsSync(tmpdir)) { - fs.mkdirSync(tmpdir, { recursive: true }); - } -}); - -afterEach(() => { - // Clean up the temporary directory after each test - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } -}); - -test("fs.WriteStream can be closed without a callback", () => { - const filePath = path.join(tmpdir, "nocallback"); - const s = fs.createWriteStream(filePath); - - s.end("hello world"); - s.close(); - - // We don't need to assert anything here as the test is checking - // that the above operations don't throw an error - expect(true).toBe(true); -}); - -//<#END_FILE: test-fs-write-stream-close-without-callback.js diff --git a/test/js/node/test/parallel/fs-write-stream-encoding.test.js b/test/js/node/test/parallel/fs-write-stream-encoding.test.js deleted file mode 100644 index ee0135bd36..0000000000 --- a/test/js/node/test/parallel/fs-write-stream-encoding.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-fs-write-stream-encoding.js -//#SHA1: a2f61bd26151411263b933d254ec75a7ca4056fc -//----------------- -'use strict'; -const fs = require('fs'); -const stream = require('stream'); -const path = require('path'); -const os = require('os'); - -const fixturesPath = path.join(__dirname, '..', 'fixtures'); -const tmpdir = path.join(os.tmpdir(), 'test-fs-write-stream-encoding'); - -const firstEncoding = 'base64'; -const secondEncoding = 'latin1'; - -const examplePath = path.join(fixturesPath, 'x.txt'); -const dummyPath = path.join(tmpdir, 'x.txt'); - -beforeAll(() => { - if (fs.existsSync(tmpdir)) { - fs.rmSync(tmpdir, { recursive: true, force: true }); - } - fs.mkdirSync(tmpdir, { recursive: true }); -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test('write stream encoding', (done) => { - const exampleReadStream = fs.createReadStream(examplePath, { - encoding: firstEncoding - }); - - const dummyWriteStream = fs.createWriteStream(dummyPath, { - encoding: firstEncoding - }); - - exampleReadStream.pipe(dummyWriteStream).on('finish', () => { - const assertWriteStream = new stream.Writable({ - write: function(chunk, enc, next) { - const expected = Buffer.from('xyz\n'); - expect(chunk).toEqual(expected); - next(); - } - }); - assertWriteStream.setDefaultEncoding(secondEncoding); - - fs.createReadStream(dummyPath, { - encoding: secondEncoding - }).pipe(assertWriteStream).on('finish', done); - }); -}); - -//<#END_FILE: test-fs-write-stream-encoding.js diff --git a/test/js/node/test/parallel/fs-write-stream-end.test.js b/test/js/node/test/parallel/fs-write-stream-end.test.js deleted file mode 100644 index a73488cb3b..0000000000 --- a/test/js/node/test/parallel/fs-write-stream-end.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-fs-write-stream-end.js -//#SHA1: a4194cfb1f416f5fddd5edc55b7d867db14a5320 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const fs = require("fs"); -const path = require("path"); - -const tmpdir = path.join(__dirname, "tmp"); - -beforeAll(() => { - if (!fs.existsSync(tmpdir)) { - fs.mkdirSync(tmpdir, { recursive: true }); - } -}); - -afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test("end without data", done => { - const file = path.join(tmpdir, "write-end-test0.txt"); - const stream = fs.createWriteStream(file); - stream.end(); - stream.on("close", () => { - done(); - }); -}); - -test("end with data", done => { - const file = path.join(tmpdir, "write-end-test1.txt"); - const stream = fs.createWriteStream(file); - stream.end("a\n", "utf8"); - stream.on("close", () => { - const content = fs.readFileSync(file, "utf8"); - expect(content).toBe("a\n"); - done(); - }); -}); - -test("end triggers open and finish events", done => { - const file = path.join(tmpdir, "write-end-test2.txt"); - const stream = fs.createWriteStream(file); - stream.end(); - - let calledOpen = false; - stream.on("open", () => { - calledOpen = true; - }); - stream.on("finish", () => { - expect(calledOpen).toBe(true); - done(); - }); -}); - -//<#END_FILE: test-fs-write-stream-end.js diff --git a/test/js/node/test/parallel/fs-write-sync.test.js b/test/js/node/test/parallel/fs-write-sync.test.js deleted file mode 100644 index 5e277e75e5..0000000000 --- a/test/js/node/test/parallel/fs-write-sync.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-fs-write-sync.js -//#SHA1: 4ae5fa7550eefe258b9c1de798f4a4092e9d15d1 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const filename = path.join(os.tmpdir(), "write.txt"); - -beforeEach(() => { - try { - fs.unlinkSync(filename); - } catch (err) { - // Ignore errors if file doesn't exist - } -}); - -test("fs.writeSync with various parameter combinations", () => { - const parameters = [Buffer.from("bár"), 0, Buffer.byteLength("bár")]; - - // The first time fs.writeSync is called with all parameters provided. - // After that, each pop in the cycle removes the final parameter. So: - // - The 2nd time fs.writeSync with a buffer, without the length parameter. - // - The 3rd time fs.writeSync with a buffer, without the offset and length - // parameters. - while (parameters.length > 0) { - const fd = fs.openSync(filename, "w"); - - let written = fs.writeSync(fd, ""); - expect(written).toBe(0); - - fs.writeSync(fd, "foo"); - - written = fs.writeSync(fd, ...parameters); - expect(written).toBeGreaterThan(3); - fs.closeSync(fd); - - expect(fs.readFileSync(filename, "utf-8")).toBe("foobár"); - - parameters.pop(); - } -}); - -//<#END_FILE: test-fs-write-sync.js diff --git a/test/js/node/test/parallel/fs-writestream-open-write.test.js b/test/js/node/test/parallel/fs-writestream-open-write.test.js deleted file mode 100644 index c641c31ce8..0000000000 --- a/test/js/node/test/parallel/fs-writestream-open-write.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-fs-writestream-open-write.js -//#SHA1: a4cb8508ae1f366c94442a43312a817f00b68de6 -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -// Regression test for https://github.com/nodejs/node/issues/51993 - -let tmpdir; - -beforeEach(() => { - tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "test-fs-writestream-open-write-")); -}); - -afterEach(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test("fs.createWriteStream opens and writes correctly", done => { - const file = path.join(tmpdir, "test-fs-writestream-open-write.txt"); - - const w = fs.createWriteStream(file); - - w.on("open", () => { - w.write("hello"); - - process.nextTick(() => { - w.write("world"); - w.end(); - }); - }); - - w.on("close", () => { - expect(fs.readFileSync(file, "utf8")).toBe("helloworld"); - fs.unlinkSync(file); - done(); - }); -}); - -//<#END_FILE: test-fs-writestream-open-write.js diff --git a/test/js/node/test/parallel/global-customevent.test.js b/test/js/node/test/parallel/global-customevent.test.js deleted file mode 100644 index 988e0cd447..0000000000 --- a/test/js/node/test/parallel/global-customevent.test.js +++ /dev/null @@ -1,19 +0,0 @@ -//#FILE: test-global-customevent.js -//#SHA1: 754c1b6babd0e73fa3206c9c9179ff3a034eba9b -//----------------- -"use strict"; - -// Global -test("CustomEvent is defined globally", () => { - expect(CustomEvent).toBeDefined(); -}); - -test("CustomEvent is the same as internal CustomEvent", () => { - // We can't use internal modules in Jest, so we'll skip this test - // and add a comment explaining why. - console.log("Skipping test for internal CustomEvent comparison"); - // The original test was: - // strictEqual(CustomEvent, internalCustomEvent); -}); - -//<#END_FILE: test-global-customevent.js diff --git a/test/js/node/test/parallel/global-domexception.test.js b/test/js/node/test/parallel/global-domexception.test.js deleted file mode 100644 index 2ebf63c2fd..0000000000 --- a/test/js/node/test/parallel/global-domexception.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-global-domexception.js -//#SHA1: 9a8d5eacea5ae98814fa6312b5f10089034c1ef4 -//----------------- -"use strict"; - -// This test checks the global availability and behavior of DOMException - -test("DOMException is a global function", () => { - expect(typeof DOMException).toBe("function"); -}); - -test("atob throws a DOMException for invalid input", () => { - expect(() => { - atob("我要抛错!"); - }).toThrow(DOMException); -}); - -//<#END_FILE: test-global-domexception.js diff --git a/test/js/node/test/parallel/global-encoder.test.js b/test/js/node/test/parallel/global-encoder.test.js deleted file mode 100644 index a9a928fb17..0000000000 --- a/test/js/node/test/parallel/global-encoder.test.js +++ /dev/null @@ -1,13 +0,0 @@ -//#FILE: test-global-encoder.js -//#SHA1: 7397937d3493488fc47e8ba6ba8fcb4f5bdd97fa -//----------------- -"use strict"; - -test("TextDecoder and TextEncoder are globally available", () => { - const util = require("util"); - - expect(TextDecoder).toBe(util.TextDecoder); - expect(TextEncoder).toBe(util.TextEncoder); -}); - -//<#END_FILE: test-global-encoder.js diff --git a/test/js/node/test/parallel/global-webcrypto.test.js b/test/js/node/test/parallel/global-webcrypto.test.js deleted file mode 100644 index a936c6b593..0000000000 --- a/test/js/node/test/parallel/global-webcrypto.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-global-webcrypto.js -//#SHA1: 3ae34178f201f6dfeb3ca5ec6e0914e6de63d64b -//----------------- -"use strict"; - -const crypto = require("crypto"); - -// Skip the test if crypto is not available -if (!crypto) { - test.skip("missing crypto", () => {}); -} else { - describe("Global WebCrypto", () => { - test("globalThis.crypto is crypto.webcrypto", () => { - expect(globalThis.crypto).toBe(crypto.webcrypto); - }); - - test("Crypto is the constructor of crypto.webcrypto", () => { - expect(Crypto).toBe(crypto.webcrypto.constructor); - }); - - test("SubtleCrypto is the constructor of crypto.webcrypto.subtle", () => { - expect(SubtleCrypto).toBe(crypto.webcrypto.subtle.constructor); - }); - }); -} - -//<#END_FILE: test-global-webcrypto.js diff --git a/test/js/node/test/parallel/heap-prof-exec-argv.test.js b/test/js/node/test/parallel/heap-prof-exec-argv.test.js deleted file mode 100644 index aa64858dbb..0000000000 --- a/test/js/node/test/parallel/heap-prof-exec-argv.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-heap-prof-exec-argv.js -//#SHA1: 77c3a447116b06f03f52fd56efe928699ba6d60d -//----------------- -"use strict"; - -// Tests --heap-prof generates a heap profile from worker -// when execArgv is set. - -const fixtures = require("../common/fixtures"); -const assert = require("assert"); -const { spawnSync } = require("child_process"); -const tmpdir = require("../common/tmpdir"); -const { getHeapProfiles, verifyFrames } = require("../common/prof"); - -// Skip the test if inspector is disabled -const isInspectorEnabled = process.execArgv.some(arg => arg.startsWith("--inspect")); -if (!isInspectorEnabled) { - test.skip("Inspector is disabled", () => {}); -} else { - test("--heap-prof generates a heap profile from worker when execArgv is set", () => { - tmpdir.refresh(); - const output = spawnSync(process.execPath, [fixtures.path("workload", "allocation-worker-argv.js")], { - cwd: tmpdir.path, - env: { - ...process.env, - HEAP_PROF_INTERVAL: "128", - }, - }); - - if (output.status !== 0) { - console.log(output.stderr.toString()); - } - - expect(output.status).toBe(0); - - const profiles = getHeapProfiles(tmpdir.path); - expect(profiles.length).toBe(1); - - verifyFrames(output, profiles[0], "runAllocation"); - }); -} - -//<#END_FILE: test-heap-prof-exec-argv.js diff --git a/test/js/node/test/parallel/http-abort-before-end.test.js b/test/js/node/test/parallel/http-abort-before-end.test.js deleted file mode 100644 index 0df59e1fcf..0000000000 --- a/test/js/node/test/parallel/http-abort-before-end.test.js +++ /dev/null @@ -1,60 +0,0 @@ -//#FILE: test-http-abort-before-end.js -//#SHA1: ccb82c66677f07f3ee815846261393edb8bfe5d4 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -test("HTTP request abort before end", async () => { - const server = http.createServer(jest.fn()); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const req = http.request({ - method: "GET", - host: "127.0.0.1", - port: server.address().port, - }); - - const abortPromise = new Promise(resolve => { - req.on("abort", resolve); - }); - - req.on("error", jest.fn()); - - req.abort(); - req.end(); - - await abortPromise; - - expect(server.listeners("request")[0]).not.toHaveBeenCalled(); - expect(req.listeners("error")[0]).not.toHaveBeenCalled(); - - await new Promise(resolve => { - server.close(resolve); - }); -}); - -//<#END_FILE: test-http-abort-before-end.js diff --git a/test/js/node/test/parallel/http-abort-queued.test.js b/test/js/node/test/parallel/http-abort-queued.test.js deleted file mode 100644 index d5c7fea669..0000000000 --- a/test/js/node/test/parallel/http-abort-queued.test.js +++ /dev/null @@ -1,102 +0,0 @@ -//#FILE: test-http-abort-queued.js -//#SHA1: e0fcd4a5eb0466a1e218147e8eb53714311a6f42 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -let complete; - -test("http abort queued request", async () => { - const server = http.createServer((req, res) => { - // We should not see the queued /thatotherone request within the server - // as it should be aborted before it is sent. - expect(req.url).toBe("/"); - - res.writeHead(200); - res.write("foo"); - - complete = - complete || - function () { - res.end(); - }; - }); - - await new Promise(resolve => server.listen(0, resolve)); - - const agent = new http.Agent({ maxSockets: 1 }); - expect(Object.keys(agent.sockets)).toHaveLength(0); - - const options = { - hostname: "localhost", - port: server.address().port, - method: "GET", - path: "/", - agent: agent, - }; - - const req1 = http.request(options); - req1.on("response", res1 => { - expect(Object.keys(agent.sockets)).toHaveLength(1); - expect(Object.keys(agent.requests)).toHaveLength(0); - - const req2 = http.request({ - method: "GET", - host: "localhost", - port: server.address().port, - path: "/thatotherone", - agent: agent, - }); - expect(Object.keys(agent.sockets)).toHaveLength(1); - expect(Object.keys(agent.requests)).toHaveLength(1); - - // TODO(jasnell): This event does not appear to currently be triggered. - // is this handler actually required? - req2.on("error", err => { - // This is expected in response to our explicit abort call - expect(err.code).toBe("ECONNRESET"); - }); - - req2.end(); - req2.abort(); - - expect(Object.keys(agent.sockets)).toHaveLength(1); - expect(Object.keys(agent.requests)).toHaveLength(1); - - res1.on("data", chunk => complete()); - - res1.on("end", () => { - setTimeout(() => { - expect(Object.keys(agent.sockets)).toHaveLength(0); - expect(Object.keys(agent.requests)).toHaveLength(0); - - server.close(); - }, 100); - }); - }); - - req1.end(); -}); - -//<#END_FILE: test-http-abort-queued.js diff --git a/test/js/node/test/parallel/http-agent-false.test.js b/test/js/node/test/parallel/http-agent-false.test.js deleted file mode 100644 index c99f236785..0000000000 --- a/test/js/node/test/parallel/http-agent-false.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-http-agent-false.js -//#SHA1: ba987050ea069a591615a69e341cf6f9c7298e5a -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -test("http.request with agent: false and port: null", async () => { - // Sending `agent: false` when `port: null` is also passed in (i.e. the result - // of a `url.parse()` call with the default port used, 80 or 443), should not - // result in an assertion error... - const opts = { - host: "127.0.0.1", - port: null, - path: "/", - method: "GET", - agent: false, - }; - - // We just want an "error" (no local HTTP server on port 80) or "response" - // to happen (user happens ot have HTTP server running on port 80). - // As long as the process doesn't crash from a C++ assertion then we're good. - const req = http.request(opts); - - // Will be called by either the response event or error event, not both - const oneResponse = jest.fn(); - req.on("response", oneResponse); - req.on("error", oneResponse); - req.end(); - - // Wait for the request to complete - await new Promise(resolve => { - req.on("response", resolve); - req.on("error", resolve); - }); - - // Check that oneResponse was called exactly once - expect(oneResponse).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-http-agent-false.js diff --git a/test/js/node/test/parallel/http-agent-no-protocol.test.js b/test/js/node/test/parallel/http-agent-no-protocol.test.js deleted file mode 100644 index 99d0acc26f..0000000000 --- a/test/js/node/test/parallel/http-agent-no-protocol.test.js +++ /dev/null @@ -1,63 +0,0 @@ -//#FILE: test-http-agent-no-protocol.js -//#SHA1: f1b40623163271a500c87971bf996466e006130e -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); - -test("http agent with no protocol", async () => { - const serverCallback = jest.fn((req, res) => { - res.end(); - }); - - const server = http.createServer(serverCallback); - - await new Promise(resolve => { - server.listen(0, "127.0.0.1", resolve); - }); - - const opts = url.parse(`http://127.0.0.1:${server.address().port}/`); - - // Remove the `protocol` field… the `http` module should fall back - // to "http:", as defined by the global, default `http.Agent` instance. - opts.agent = new http.Agent(); - opts.agent.protocol = null; - - const responseCallback = jest.fn(res => { - res.resume(); - server.close(); - }); - - await new Promise(resolve => { - http.get(opts, res => { - responseCallback(res); - resolve(); - }); - }); - - expect(serverCallback).toHaveBeenCalledTimes(1); - expect(responseCallback).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-http-agent-no-protocol.js diff --git a/test/js/node/test/parallel/http-agent-null.test.js b/test/js/node/test/parallel/http-agent-null.test.js deleted file mode 100644 index c44c03f788..0000000000 --- a/test/js/node/test/parallel/http-agent-null.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-http-agent-null.js -//#SHA1: 65fb22d32bae2a7eecc4242b5b2d2d693849641c -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -test("http.get with null agent", async () => { - const server = http.createServer((req, res) => { - res.end(); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const options = { - agent: null, - port: server.address().port, - }; - - const responsePromise = new Promise(resolve => { - http.get(options, res => { - res.resume(); - resolve(res); - }); - }); - - await expect(responsePromise).resolves.toBeDefined(); - - await new Promise(resolve => { - server.close(resolve); - }); -}); - -//<#END_FILE: test-http-agent-null.js diff --git a/test/js/node/test/parallel/http-agent-uninitialized-with-handle.test.js b/test/js/node/test/parallel/http-agent-uninitialized-with-handle.test.js deleted file mode 100644 index b78aacf5b6..0000000000 --- a/test/js/node/test/parallel/http-agent-uninitialized-with-handle.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-http-agent-uninitialized-with-handle.js -//#SHA1: 828942acbc68f8fd92425ecdf0e754ab13b4baff -//----------------- -"use strict"; - -const http = require("http"); -const net = require("net"); - -test("http agent with uninitialized socket handle", done => { - const agent = new http.Agent({ - keepAlive: true, - }); - const socket = new net.Socket(); - // If _handle exists then internals assume a couple methods exist. - socket._handle = { - ref() {}, - readStart() {}, - }; - - const server = http.createServer((req, res) => { - res.end(); - }); - - server.listen(0, () => { - const req = new http.ClientRequest(`http://localhost:${server.address().port}/`); - - // Manually add the socket without a _handle. - agent.freeSockets[agent.getName(req)] = [socket]; - // Now force the agent to use the socket and check that _handle exists before - // calling asyncReset(). - agent.addRequest(req, {}); - req.on("response", () => { - server.close(); - done(); - }); - req.end(); - }); - - expect(server).toHaveProperty("listen"); - expect(server).toHaveProperty("close"); -}); - -//<#END_FILE: test-http-agent-uninitialized-with-handle.js diff --git a/test/js/node/test/parallel/http-agent-uninitialized.test.js b/test/js/node/test/parallel/http-agent-uninitialized.test.js deleted file mode 100644 index 43e447a063..0000000000 --- a/test/js/node/test/parallel/http-agent-uninitialized.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-http-agent-uninitialized.js -//#SHA1: 00034f4963a5620af8a58e68c262c92ea9ec982b -//----------------- -"use strict"; - -const http = require("http"); -const net = require("net"); - -test("http agent handles uninitialized socket", done => { - const agent = new http.Agent({ - keepAlive: true, - }); - const socket = new net.Socket(); - - const server = http - .createServer((req, res) => { - res.end(); - }) - .listen(0, () => { - const req = new http.ClientRequest(`http://localhost:${server.address().port}/`); - - // Manually add the socket without a _handle. - agent.freeSockets[agent.getName(req)] = [socket]; - // Now force the agent to use the socket and check that _handle exists before - // calling asyncReset(). - agent.addRequest(req, {}); - req.on("response", () => { - server.close(); - done(); - }); - req.end(); - }); - - expect(server).toBeDefined(); -}); - -//<#END_FILE: test-http-agent-uninitialized.js diff --git a/test/js/node/test/parallel/http-agent.test.js b/test/js/node/test/parallel/http-agent.test.js deleted file mode 100644 index 900ad21d17..0000000000 --- a/test/js/node/test/parallel/http-agent.test.js +++ /dev/null @@ -1,97 +0,0 @@ -//#FILE: test-http-agent.js -//#SHA1: c5bb5b1b47100659ac17ae6c4ba084c6974ddaa7 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -const N = 4; -const M = 4; - -let server; - -beforeEach(() => { - server = http.Server((req, res) => { - res.writeHead(200); - res.end("hello world\n"); - }); -}); - -afterEach(() => { - server.close(); -}); - -function makeRequests(outCount, inCount, shouldFail) { - return new Promise(resolve => { - const totalRequests = outCount * inCount; - let completedRequests = 0; - - const onRequest = jest.fn(res => { - completedRequests++; - if (completedRequests === totalRequests) { - resolve(); - } - - if (!shouldFail) { - res.resume(); - } - }); - - server.listen(0, () => { - const port = server.address().port; - for (let i = 0; i < outCount; i++) { - setTimeout(() => { - for (let j = 0; j < inCount; j++) { - const req = http.get({ port: port, path: "/" }, onRequest); - if (shouldFail) { - req.on("error", onRequest); - } else { - req.on("error", e => { - throw e; - }); - } - } - }, i); - } - }); - }); -} - -test("makeRequests successful", async () => { - await makeRequests(N, M); - expect(server.listenerCount("request")).toBe(1); -}); - -test("makeRequests with failing requests", async () => { - const originalCreateConnection = http.Agent.prototype.createConnection; - - http.Agent.prototype.createConnection = function createConnection(_, cb) { - process.nextTick(cb, new Error("nothing")); - }; - - await makeRequests(N, M, true); - - http.Agent.prototype.createConnection = originalCreateConnection; -}); - -//<#END_FILE: test-http-agent.js diff --git a/test/js/node/test/parallel/http-bind-twice.test.js b/test/js/node/test/parallel/http-bind-twice.test.js deleted file mode 100644 index 99917ffc27..0000000000 --- a/test/js/node/test/parallel/http-bind-twice.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-http-bind-twice.js -//#SHA1: 71319f0a5445d1ea7644e952ec4048d8996be1bc -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -test("HTTP server bind twice", async () => { - const server1 = http.createServer(jest.fn()); - const server1ListenPromise = new Promise(resolve => { - server1.listen(0, "127.0.0.1", resolve); - }); - - await server1ListenPromise; - - const server2 = http.createServer(jest.fn()); - const server2ErrorPromise = new Promise(resolve => { - server2.on("error", resolve); - }); - - server2.listen(server1.address().port, "127.0.0.1"); - - const error = await server2ErrorPromise; - - expect(error).toEqual( - expect.objectContaining({ - code: "EADDRINUSE", - message: expect.any(String), - }), - ); - - await new Promise(resolve => server1.close(resolve)); -}); - -//<#END_FILE: test-http-bind-twice.js diff --git a/test/js/node/test/parallel/http-chunked-smuggling.test.js b/test/js/node/test/parallel/http-chunked-smuggling.test.js deleted file mode 100644 index e1ab305b10..0000000000 --- a/test/js/node/test/parallel/http-chunked-smuggling.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-http-chunked-smuggling.js -//#SHA1: c146d9dc37a522ac07d943b4c40b3301923659fa -//----------------- -"use strict"; - -const http = require("http"); -const net = require("net"); - -// Verify that invalid chunk extensions cannot be used to perform HTTP request -// smuggling attacks. - -describe("HTTP Chunked Smuggling", () => { - let server; - let serverPort; - - beforeAll(done => { - server = http.createServer((request, response) => { - expect(request.url).not.toBe("/admin"); - response.end("hello world"); - }); - - server.listen(0, () => { - serverPort = server.address().port; - done(); - }); - }); - - afterAll(done => { - server.close(done); - }); - - test("invalid chunk extensions", done => { - const sock = net.connect(serverPort); - - sock.write( - "" + - "GET / HTTP/1.1\r\n" + - "Host: localhost:8080\r\n" + - "Transfer-Encoding: chunked\r\n" + - "\r\n" + - "2;\n" + - "xx\r\n" + - "4c\r\n" + - "0\r\n" + - "\r\n" + - "GET /admin HTTP/1.1\r\n" + - "Host: localhost:8080\r\n" + - "Transfer-Encoding: chunked\r\n" + - "\r\n" + - "0\r\n" + - "\r\n", - ); - - sock.resume(); - sock.on("end", () => { - done(); - }); - }); -}); - -//<#END_FILE: test-http-chunked-smuggling.js diff --git a/test/js/node/test/parallel/http-client-abort2.test.js b/test/js/node/test/parallel/http-client-abort2.test.js deleted file mode 100644 index 416be11173..0000000000 --- a/test/js/node/test/parallel/http-client-abort2.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-http-client-abort2.js -//#SHA1: 9accf5214e90cab96d06a59931e65718616b85f3 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -test("http client abort", async () => { - const serverHandler = jest.fn((req, res) => { - res.end("Hello"); - }); - - const server = http.createServer(serverHandler); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const options = { port: server.address().port }; - - const req = http.get(options, res => { - res.on("data", data => { - req.abort(); - server.close(); - }); - }); - - await new Promise(resolve => { - server.on("close", resolve); - }); - - expect(serverHandler).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-http-client-abort2.js diff --git a/test/js/node/test/parallel/http-client-defaults.test.js b/test/js/node/test/parallel/http-client-defaults.test.js deleted file mode 100644 index f527d6ade8..0000000000 --- a/test/js/node/test/parallel/http-client-defaults.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-http-client-defaults.js -//#SHA1: 7209a7752de52cc378c3b29eda88c82d71e6839d -//----------------- -"use strict"; - -const http = require("http"); - -describe("ClientRequest defaults", () => { - test("default path and method", () => { - const req = new http.ClientRequest({ createConnection: () => {} }); - expect(req.path).toBe("/"); - expect(req.method).toBe("GET"); - }); - - test("empty method defaults to GET", () => { - const req = new http.ClientRequest({ method: "", createConnection: () => {} }); - expect(req.path).toBe("/"); - expect(req.method).toBe("GET"); - }); - - test("empty path defaults to /", () => { - const req = new http.ClientRequest({ path: "", createConnection: () => {} }); - expect(req.path).toBe("/"); - expect(req.method).toBe("GET"); - }); -}); - -//<#END_FILE: test-http-client-defaults.js diff --git a/test/js/node/test/parallel/http-client-encoding.test.js b/test/js/node/test/parallel/http-client-encoding.test.js deleted file mode 100644 index b0455d81bb..0000000000 --- a/test/js/node/test/parallel/http-client-encoding.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-http-client-encoding.js -//#SHA1: a3e1a0cc1bf9352602068f323d90071d3c2d5f7d -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -test("HTTP client encoding", async () => { - const server = http.createServer((req, res) => { - res.end("ok"); - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const port = server.address().port; - const req = http.request( - { - port: port, - encoding: "utf8", - }, - res => { - let data = ""; - res.on("data", chunk => (data += chunk)); - res.on("end", () => { - expect(data).toBe("ok"); - resolve(); - }); - }, - ); - req.end(); - }); - }); -}); - -//<#END_FILE: test-http-client-encoding.js diff --git a/test/js/node/test/parallel/http-client-get-url.test.js b/test/js/node/test/parallel/http-client-get-url.test.js deleted file mode 100644 index e13f87f7d4..0000000000 --- a/test/js/node/test/parallel/http-client-get-url.test.js +++ /dev/null @@ -1,78 +0,0 @@ -//#FILE: test-http-client-get-url.js -//#SHA1: 0329da4beb5be5da0ab6652b246dd912935e56af -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); -const testPath = "/foo?bar"; - -let server; -let serverAddress; - -beforeAll(async () => { - server = http.createServer((req, res) => { - expect(req.method).toBe("GET"); - expect(req.url).toBe(testPath); - res.writeHead(200, { "Content-Type": "text/plain" }); - res.write("hello\n"); - res.end(); - }); - - await new Promise(resolve => { - server.listen(0, "127.0.0.1", () => { - serverAddress = `http://127.0.0.1:${server.address().port}${testPath}`; - resolve(); - }); - }); -}); - -afterAll(() => { - server.close(); -}); - -test("http.get with string URL", async () => { - await new Promise(resolve => { - http.get(serverAddress, () => { - resolve(); - }); - }); -}); - -test("http.get with parsed URL", async () => { - await new Promise(resolve => { - http.get(url.parse(serverAddress), () => { - resolve(); - }); - }); -}); - -test("http.get with URL object", async () => { - await new Promise(resolve => { - http.get(new URL(serverAddress), () => { - resolve(); - }); - }); -}); - -//<#END_FILE: test-http-client-get-url.js diff --git a/test/js/node/test/parallel/http-client-input-function.test.js b/test/js/node/test/parallel/http-client-input-function.test.js deleted file mode 100644 index e34ee9a0d5..0000000000 --- a/test/js/node/test/parallel/http-client-input-function.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-http-client-input-function.js -//#SHA1: 2ca0147b992331ea69803031f33076c685bce264 -//----------------- -"use strict"; - -const http = require("http"); - -test("http.ClientRequest with server response", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200); - res.end("hello world"); - }); - - await new Promise(resolve => { - server.listen(0, "127.0.0.1", resolve); - }); - - const serverAddress = server.address(); - - const responsePromise = new Promise(resolve => { - const req = new http.ClientRequest(serverAddress, response => { - let body = ""; - response.setEncoding("utf8"); - response.on("data", chunk => { - body += chunk; - }); - - response.on("end", () => { - resolve(body); - }); - }); - - req.end(); - }); - - const body = await responsePromise; - expect(body).toBe("hello world"); - - await new Promise(resolve => server.close(resolve)); -}); - -//<#END_FILE: test-http-client-input-function.js diff --git a/test/js/node/test/parallel/http-client-keep-alive-release-before-finish.test.js b/test/js/node/test/parallel/http-client-keep-alive-release-before-finish.test.js deleted file mode 100644 index 5e7ca84113..0000000000 --- a/test/js/node/test/parallel/http-client-keep-alive-release-before-finish.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-http-client-keep-alive-release-before-finish.js -//#SHA1: 198cd4a6c28c8a7dda45f003305e8fa80f05469d -//----------------- -"use strict"; - -const http = require("http"); - -test("HTTP client keep-alive release before finish", done => { - const server = http.createServer((req, res) => { - res.end(); - }); - - server.listen(0, () => { - const agent = new http.Agent({ - maxSockets: 1, - keepAlive: true, - }); - - const port = server.address().port; - - const post = http.request( - { - agent, - method: "POST", - port, - }, - res => { - res.resume(); - }, - ); - - // What happens here is that the server `end`s the response before we send - // `something`, and the client thought that this is a green light for sending - // next GET request - post.write(Buffer.alloc(16 * 1024, "X")); - setTimeout(() => { - post.end("something"); - }, 100); - - http - .request( - { - agent, - method: "GET", - port, - }, - res => { - server.close(); - res.connection.end(); - done(); - }, - ) - .end(); - }); -}); - -//<#END_FILE: test-http-client-keep-alive-release-before-finish.js diff --git a/test/js/node/test/parallel/http-client-race-2.test.js b/test/js/node/test/parallel/http-client-race-2.test.js deleted file mode 100644 index bc4c83b4f4..0000000000 --- a/test/js/node/test/parallel/http-client-race-2.test.js +++ /dev/null @@ -1,136 +0,0 @@ -//#FILE: test-http-client-race-2.js -//#SHA1: f1e2a4ecdd401cb9fcf615496d1376ce0a94ad73 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); - -// -// Slight variation on test-http-client-race to test for another race -// condition involving the parsers FreeList used internally by http.Client. -// - -const body1_s = "1111111111111111"; -const body2_s = "22222"; -const body3_s = "3333333333333333333"; - -let server; -let port; - -beforeAll(() => { - return new Promise(resolve => { - server = http.createServer(function (req, res) { - const pathname = url.parse(req.url).pathname; - - let body; - switch (pathname) { - case "/1": - body = body1_s; - break; - case "/2": - body = body2_s; - break; - default: - body = body3_s; - } - - res.writeHead(200, { - "Content-Type": "text/plain", - "Content-Length": body.length, - }); - res.end(body); - }); - - server.listen(0, () => { - port = server.address().port; - resolve(); - }); - }); -}); - -afterAll(() => { - return new Promise(resolve => { - server.close(resolve); - }); -}); - -test("HTTP client race condition", async () => { - let body1 = ""; - let body2 = ""; - let body3 = ""; - - // Client #1 is assigned Parser #1 - const req1 = http.get({ port, path: "/1" }); - await new Promise(resolve => { - req1.on("response", function (res1) { - res1.setEncoding("utf8"); - - res1.on("data", function (chunk) { - body1 += chunk; - }); - - res1.on("end", function () { - // Delay execution a little to allow the 'close' event to be processed - // (required to trigger this bug!) - setTimeout(resolve, 500); - }); - }); - }); - - // The bug would introduce itself here: Client #2 would be allocated the - // parser that previously belonged to Client #1. But we're not finished - // with Client #1 yet! - // - // At this point, the bug would manifest itself and crash because the - // internal state of the parser was no longer valid for use by Client #1 - const req2 = http.get({ port, path: "/2" }); - await new Promise(resolve => { - req2.on("response", function (res2) { - res2.setEncoding("utf8"); - res2.on("data", function (chunk) { - body2 += chunk; - }); - res2.on("end", resolve); - }); - }); - - // Just to be really sure we've covered all our bases, execute a - // request using client2. - const req3 = http.get({ port, path: "/3" }); - await new Promise(resolve => { - req3.on("response", function (res3) { - res3.setEncoding("utf8"); - res3.on("data", function (chunk) { - body3 += chunk; - }); - res3.on("end", resolve); - }); - }); - - expect(body1).toBe(body1_s); - expect(body2).toBe(body2_s); - expect(body3).toBe(body3_s); -}); - -//<#END_FILE: test-http-client-race-2.js diff --git a/test/js/node/test/parallel/http-client-race.test.js b/test/js/node/test/parallel/http-client-race.test.js deleted file mode 100644 index ab780f28d8..0000000000 --- a/test/js/node/test/parallel/http-client-race.test.js +++ /dev/null @@ -1,72 +0,0 @@ -//#FILE: test-http-client-race.js -//#SHA1: 0ad515567d91a194670069b476e166d398543cc0 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); - -const body1_s = "1111111111111111"; -const body2_s = "22222"; - -test("http client race condition", async () => { - const server = http.createServer((req, res) => { - const body = url.parse(req.url).pathname === "/1" ? body1_s : body2_s; - res.writeHead(200, { - "Content-Type": "text/plain", - "Content-Length": body.length, - }); - res.end(body); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - let body1 = ""; - let body2 = ""; - - const makeRequest = path => { - return new Promise((resolve, reject) => { - const req = http.request({ port: server.address().port, path }); - req.end(); - req.on("response", res => { - res.setEncoding("utf8"); - let body = ""; - res.on("data", chunk => { - body += chunk; - }); - res.on("end", () => resolve(body)); - }); - req.on("error", reject); - }); - }; - - body1 = await makeRequest("/1"); - body2 = await makeRequest("/2"); - - await new Promise(resolve => server.close(resolve)); - - expect(body1).toBe(body1_s); - expect(body2).toBe(body2_s); -}); - -//<#END_FILE: test-http-client-race.js diff --git a/test/js/node/test/parallel/http-client-read-in-error.test.js b/test/js/node/test/parallel/http-client-read-in-error.test.js deleted file mode 100644 index 2dd33c52de..0000000000 --- a/test/js/node/test/parallel/http-client-read-in-error.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-http-client-read-in-error.js -//#SHA1: a7bd75283f46ff8f1246208c72bf0773a27f0fb0 -//----------------- -"use strict"; - -const net = require("net"); -const http = require("http"); - -class Agent extends http.Agent { - createConnection() { - const socket = new net.Socket(); - - socket.on("error", function () { - socket.push("HTTP/1.1 200\r\n\r\n"); - }); - - let onNewListener; - socket.on( - "newListener", - (onNewListener = name => { - if (name !== "error") return; - socket.removeListener("newListener", onNewListener); - - // Let other listeners to be set up too - process.nextTick(() => { - this.breakSocket(socket); - }); - }), - ); - - return socket; - } - - breakSocket(socket) { - socket.emit("error", new Error("Intentional error")); - } -} - -test("http client read in error", () => { - const agent = new Agent(); - const dataHandler = jest.fn(); - - const request = http.request({ agent }); - - request.once("error", function () { - console.log("ignore"); - this.on("data", dataHandler); - }); - - return new Promise(resolve => { - // Give some time for the 'data' event to potentially be called - setTimeout(() => { - expect(dataHandler).not.toHaveBeenCalled(); - resolve(); - }, 100); - }); -}); - -//<#END_FILE: test-http-client-read-in-error.js diff --git a/test/js/node/test/parallel/http-client-res-destroyed.test.js b/test/js/node/test/parallel/http-client-res-destroyed.test.js deleted file mode 100644 index 7b7662ea52..0000000000 --- a/test/js/node/test/parallel/http-client-res-destroyed.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-http-client-res-destroyed.js -//#SHA1: 9a7e890355cecb3eb88b6963b0c37df3f01bc8d7 -//----------------- -"use strict"; - -const http = require("http"); - -describe("HTTP Client Response Destroyed", () => { - test("Response destruction after manually calling destroy()", async () => { - const server = http.createServer((req, res) => { - res.end("asd"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get( - { - port: server.address().port, - }, - res => { - expect(res.destroyed).toBe(false); - res.destroy(); - expect(res.destroyed).toBe(true); - res.on("close", () => { - server.close(resolve); - }); - }, - ); - }); - }); - }); - - test("Response destruction after end of response", async () => { - const server = http.createServer((req, res) => { - res.end("asd"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get( - { - port: server.address().port, - }, - res => { - expect(res.destroyed).toBe(false); - res - .on("close", () => { - expect(res.destroyed).toBe(true); - server.close(resolve); - }) - .resume(); - }, - ); - }); - }); - }); -}); - -//<#END_FILE: test-http-client-res-destroyed.js diff --git a/test/js/node/test/parallel/http-client-timeout-connect-listener.test.js b/test/js/node/test/parallel/http-client-timeout-connect-listener.test.js deleted file mode 100644 index a70f33512b..0000000000 --- a/test/js/node/test/parallel/http-client-timeout-connect-listener.test.js +++ /dev/null @@ -1,73 +0,0 @@ -//#FILE: test-http-client-timeout-connect-listener.js -//#SHA1: 4311732db4ce9958ec0ed01be68786e522ed6ca8 -//----------------- -"use strict"; - -// This test ensures that `ClientRequest.prototype.setTimeout()` does -// not add a listener for the `'connect'` event to the socket if the -// socket is already connected. - -const http = require("http"); - -// Maximum allowed value for timeouts. -const timeout = 2 ** 31 - 1; - -let server; -let agent; - -beforeAll(() => { - return new Promise(resolve => { - server = http.createServer((req, res) => { - res.end(); - }); - - server.listen(0, () => { - agent = new http.Agent({ keepAlive: true, maxSockets: 1 }); - resolve(); - }); - }); -}); - -afterAll(() => { - return new Promise(resolve => { - agent.destroy(); - server.close(resolve); - }); -}); - -function doRequest(options) { - return new Promise(resolve => { - const req = http.get(options, res => { - res.on("end", resolve); - res.resume(); - }); - - req.setTimeout(timeout); - return req; - }); -} - -test("ClientRequest.prototype.setTimeout() does not add connect listener to connected socket", async () => { - const options = { port: server.address().port, agent: agent }; - - await doRequest(options); - - const req = http.get(options); - req.setTimeout(timeout); - - await new Promise(resolve => { - req.on("socket", socket => { - expect(socket.listenerCount("connect")).toBe(0); - resolve(); - }); - }); - - await new Promise(resolve => { - req.on("response", res => { - res.on("end", resolve); - res.resume(); - }); - }); -}); - -//<#END_FILE: test-http-client-timeout-connect-listener.js diff --git a/test/js/node/test/parallel/http-client-timeout-event.test.js b/test/js/node/test/parallel/http-client-timeout-event.test.js deleted file mode 100644 index bfff317e47..0000000000 --- a/test/js/node/test/parallel/http-client-timeout-event.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-http-client-timeout-event.js -//#SHA1: b4aeb9d5d97b5ffa46c8c281fbc04d052857b08f -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -const options = { - method: "GET", - port: undefined, - host: "127.0.0.1", - path: "/", -}; - -test("http client timeout event", async () => { - const server = http.createServer(); - - await new Promise(resolve => { - server.listen(0, options.host, () => { - options.port = server.address().port; - const req = http.request(options); - - req.on("error", () => { - // This space is intentionally left blank - }); - - req.on("close", () => { - expect(req.destroyed).toBe(true); - server.close(); - resolve(); - }); - - req.setTimeout(1); - req.on("timeout", () => { - req.end(() => { - setTimeout(() => { - req.destroy(); - }, 100); - }); - }); - }); - }); -}); - -//<#END_FILE: test-http-client-timeout-event.js diff --git a/test/js/node/test/parallel/http-client-timeout.test.js b/test/js/node/test/parallel/http-client-timeout.test.js deleted file mode 100644 index 557b469490..0000000000 --- a/test/js/node/test/parallel/http-client-timeout.test.js +++ /dev/null @@ -1,60 +0,0 @@ -//#FILE: test-http-client-timeout.js -//#SHA1: f99a3189acb9c566e378f9fa48c66dd503034d0d -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -const options = { - method: "GET", - port: undefined, - host: "127.0.0.1", - path: "/", -}; - -test("http client timeout", done => { - const server = http.createServer((req, res) => { - // This space intentionally left blank - }); - - server.listen(0, options.host, () => { - options.port = server.address().port; - const req = http.request(options, res => { - // This space intentionally left blank - }); - req.on("close", () => { - expect(req.destroyed).toBe(true); - server.close(); - done(); - }); - function destroy() { - req.destroy(); - } - const s = req.setTimeout(1, destroy); - expect(s).toBeInstanceOf(http.ClientRequest); - req.on("error", destroy); - req.end(); - }); -}); - -//<#END_FILE: test-http-client-timeout.js diff --git a/test/js/node/test/parallel/http-client-upload-buf.test.js b/test/js/node/test/parallel/http-client-upload-buf.test.js deleted file mode 100644 index 9891a442dc..0000000000 --- a/test/js/node/test/parallel/http-client-upload-buf.test.js +++ /dev/null @@ -1,77 +0,0 @@ -//#FILE: test-http-client-upload-buf.js -//#SHA1: bbfd7c52e710f53683f5f9a4578f34e451db4eb0 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const assert = require("assert"); -const http = require("http"); - -const N = 1024; - -test("HTTP client upload buffer", async () => { - const server = http.createServer((req, res) => { - expect(req.method).toBe("POST"); - let bytesReceived = 0; - - req.on("data", chunk => { - bytesReceived += chunk.length; - }); - - req.on("end", () => { - expect(bytesReceived).toBe(N); - console.log("request complete from server"); - res.writeHead(200, { "Content-Type": "text/plain" }); - res.write("hello\n"); - res.end(); - }); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - const { port } = server.address(); - - const responsePromise = new Promise(resolve => { - const req = http.request( - { - port, - method: "POST", - path: "/", - }, - res => { - res.setEncoding("utf8"); - res.on("data", chunk => { - console.log(chunk); - }); - res.on("end", resolve); - }, - ); - - req.write(Buffer.allocUnsafe(N)); - req.end(); - }); - - await responsePromise; - await new Promise(resolve => server.close(resolve)); -}); - -//<#END_FILE: test-http-client-upload-buf.js diff --git a/test/js/node/test/parallel/http-client-upload.test.js b/test/js/node/test/parallel/http-client-upload.test.js deleted file mode 100644 index 6adc480232..0000000000 --- a/test/js/node/test/parallel/http-client-upload.test.js +++ /dev/null @@ -1,84 +0,0 @@ -//#FILE: test-http-client-upload.js -//#SHA1: 328a7e9989cc28daa5996c83fe9e3cfcb0893e01 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -test("HTTP client upload", async () => { - const serverHandler = jest.fn((req, res) => { - expect(req.method).toBe("POST"); - req.setEncoding("utf8"); - - let sent_body = ""; - - req.on("data", chunk => { - console.log(`server got: ${JSON.stringify(chunk)}`); - sent_body += chunk; - }); - - req.on("end", () => { - expect(sent_body).toBe("1\n2\n3\n"); - console.log("request complete from server"); - res.writeHead(200, { "Content-Type": "text/plain" }); - res.write("hello\n"); - res.end(); - }); - }); - - const server = http.createServer(serverHandler); - await new Promise(resolve => server.listen(0, resolve)); - - const { port } = server.address(); - - const clientHandler = jest.fn(res => { - res.setEncoding("utf8"); - res.on("data", chunk => { - console.log(chunk); - }); - res.on("end", () => { - server.close(); - }); - }); - - const req = http.request( - { - port, - method: "POST", - path: "/", - }, - clientHandler, - ); - - req.write("1\n"); - req.write("2\n"); - req.write("3\n"); - req.end(); - - await new Promise(resolve => server.on("close", resolve)); - - expect(serverHandler).toHaveBeenCalledTimes(1); - expect(clientHandler).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-http-client-upload.js diff --git a/test/js/node/test/parallel/http-contentlength0.test.js b/test/js/node/test/parallel/http-contentlength0.test.js deleted file mode 100644 index 4a6a7fec72..0000000000 --- a/test/js/node/test/parallel/http-contentlength0.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-http-contentLength0.js -//#SHA1: d85b0cc3dcfcff522ffbeddacf89111897b80c02 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -// Simple test of Node's HTTP Client choking on a response -// with a 'Content-Length: 0 ' response header. -// I.E. a space character after the 'Content-Length' throws an `error` event. - -test("HTTP Client handles Content-Length: 0 with space", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200, { "Content-Length": "0 " }); - res.end(); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const { port } = server.address(); - - await new Promise(resolve => { - const request = http.request({ port }, response => { - expect(response.statusCode).toBe(200); - server.close(); - response.resume(); - resolve(); - }); - - request.end(); - }); -}); - -//<#END_FILE: test-http-contentLength0.js diff --git a/test/js/node/test/parallel/http-date-header.test.js b/test/js/node/test/parallel/http-date-header.test.js deleted file mode 100644 index b47a7163d9..0000000000 --- a/test/js/node/test/parallel/http-date-header.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-http-date-header.js -//#SHA1: e4d2a00dad7c6483d9ed328731bb04f5f431afb4 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -const testResBody = "other stuff!\n"; - -test("HTTP Date header", async () => { - const server = http.createServer((req, res) => { - expect(req.headers).not.toHaveProperty("date"); - res.writeHead(200, { - "Content-Type": "text/plain", - }); - res.end(testResBody); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const { port } = server.address(); - - const options = { - port, - path: "/", - method: "GET", - }; - - const responsePromise = new Promise((resolve, reject) => { - const req = http.request(options, res => { - expect(res.headers).toHaveProperty("date"); - res.resume(); - res.on("end", resolve); - }); - req.on("error", reject); - req.end(); - }); - - await responsePromise; - await new Promise(resolve => server.close(resolve)); -}); - -//<#END_FILE: test-http-date-header.js diff --git a/test/js/node/test/parallel/http-decoded-auth.test.js b/test/js/node/test/parallel/http-decoded-auth.test.js deleted file mode 100644 index ed117d1954..0000000000 --- a/test/js/node/test/parallel/http-decoded-auth.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-http-decoded-auth.js -//#SHA1: 70ba85653c7479ce80cf528a07aa85f598f85ef8 -//----------------- -"use strict"; - -const http = require("http"); - -const testCases = [ - { - username: 'test@test"', - password: "123456^", - expected: "dGVzdEB0ZXN0IjoxMjM0NTZe", - }, - { - username: "test%40test", - password: "123456", - expected: "dGVzdEB0ZXN0OjEyMzQ1Ng==", - }, - { - username: "not%3Agood", - password: "god", - expected: "bm90Omdvb2Q6Z29k", - }, - { - username: "not%22good", - password: "g%5Eod", - expected: "bm90Imdvb2Q6Z15vZA==", - }, - { - username: "test1234::::", - password: "mypass", - expected: "dGVzdDEyMzQ6Ojo6Om15cGFzcw==", - }, -]; - -testCases.forEach((testCase, index) => { - test(`HTTP decoded auth - case ${index + 1}`, async () => { - const server = http.createServer((request, response) => { - // The correct authorization header is be passed - expect(request.headers.authorization).toBe(`Basic ${testCase.expected}`); - response.writeHead(200, {}); - response.end("ok"); - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - // make the request - const url = new URL(`http://${testCase.username}:${testCase.password}@localhost:${server.address().port}`); - http.request(url).end(); - resolve(); - }); - }); - }); -}); - -//<#END_FILE: test-http-decoded-auth.js diff --git a/test/js/node/test/parallel/http-default-encoding.test.js b/test/js/node/test/parallel/http-default-encoding.test.js deleted file mode 100644 index 30fc083b29..0000000000 --- a/test/js/node/test/parallel/http-default-encoding.test.js +++ /dev/null @@ -1,73 +0,0 @@ -//#FILE: test-http-default-encoding.js -//#SHA1: f5dfdba00ec21efec894e5edf97583c77334a2c3 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -const expected = "This is a unicode text: سلام"; - -test("HTTP server with default encoding", async () => { - let result = ""; - - const server = http.Server((req, res) => { - req.setEncoding("utf8"); - req - .on("data", chunk => { - result += chunk; - }) - .on("end", () => { - res.writeHead(200); - res.end("hello world\n"); - server.close(); - }); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const req = http.request( - { - port: server.address().port, - path: "/", - method: "POST", - }, - res => { - expect(res.statusCode).toBe(200); - res.resume(); - resolve(); - }, - ); - - req.on("error", e => { - console.log(e.message); - process.exit(1); - }); - - req.end(expected); - }); - }); - - expect(result).toBe(expected); -}); - -//<#END_FILE: test-http-default-encoding.js diff --git a/test/js/node/test/parallel/http-eof-on-connect.test.js b/test/js/node/test/parallel/http-eof-on-connect.test.js deleted file mode 100644 index 1161c1f40c..0000000000 --- a/test/js/node/test/parallel/http-eof-on-connect.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-http-eof-on-connect.js -//#SHA1: c243d7ad215d84d88b20dfeea40976155f00a2bb -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const net = require("net"); -const http = require("http"); - -// This is a regression test for https://github.com/joyent/node/issues/44 -// It is separate from test-http-malformed-request.js because it is only -// reproducible on the first packet on the first connection to a server. - -test("EOF on connect", async () => { - const server = http.createServer(jest.fn()); - server.listen(0); - - await new Promise(resolve => { - server.on("listening", () => { - const client = net.createConnection(server.address().port, "127.0.0.1"); - - client.on("connect", () => { - client.destroy(); - }); - - client.on("close", () => { - server.close(resolve); - }); - }); - }); - - expect(server.listeners("request")[0]).not.toHaveBeenCalled(); -}); - -//<#END_FILE: test-http-eof-on-connect.js diff --git a/test/js/node/test/parallel/http-extra-response.test.js b/test/js/node/test/parallel/http-extra-response.test.js deleted file mode 100644 index b3e73a341a..0000000000 --- a/test/js/node/test/parallel/http-extra-response.test.js +++ /dev/null @@ -1,98 +0,0 @@ -//#FILE: test-http-extra-response.js -//#SHA1: 0d2dfa2459e54fa9f1b90a609c328afd478f2793 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const net = require("net"); - -// If an HTTP server is broken and sends data after the end of the response, -// node should ignore it and drop the connection. -// Demos this bug: https://github.com/joyent/node/issues/680 - -const body = "hello world\r\n"; -const fullResponse = - "HTTP/1.1 500 Internal Server Error\r\n" + - `Content-Length: ${body.length}\r\n` + - "Content-Type: text/plain\r\n" + - "Date: Fri + 18 Feb 2011 06:22:45 GMT\r\n" + - "Host: 10.20.149.2\r\n" + - "Access-Control-Allow-Credentials: true\r\n" + - "Server: badly broken/0.1 (OS NAME)\r\n" + - "\r\n" + - body; - -test("HTTP server sending data after response end", async () => { - const server = net.createServer(socket => { - let postBody = ""; - - socket.setEncoding("utf8"); - - socket.on("data", chunk => { - postBody += chunk; - - if (postBody.includes("\r\n")) { - socket.write(fullResponse); - socket.end(fullResponse); - } - }); - - socket.on("error", err => { - expect(err.code).toBe("ECONNRESET"); - }); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const { port } = server.address(); - - const consoleLogSpy = jest.spyOn(console, "log").mockImplementation(); - - await new Promise(resolve => { - http.get({ port }, res => { - let buffer = ""; - console.log(`Got res code: ${res.statusCode}`); - - res.setEncoding("utf8"); - res.on("data", chunk => { - buffer += chunk; - }); - - res.on("end", () => { - console.log(`Response ended, read ${buffer.length} bytes`); - expect(buffer).toBe(body); - resolve(); - }); - }); - }); - - expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringMatching(/Got res code: \d+/)); - expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringMatching(/Response ended, read \d+ bytes/)); - - consoleLogSpy.mockRestore(); - await new Promise(resolve => server.close(resolve)); -}); - -//<#END_FILE: test-http-extra-response.js diff --git a/test/js/node/test/parallel/http-full-response.test.js b/test/js/node/test/parallel/http-full-response.test.js deleted file mode 100644 index 8e0a8fcc3b..0000000000 --- a/test/js/node/test/parallel/http-full-response.test.js +++ /dev/null @@ -1,104 +0,0 @@ -//#FILE: test-http-full-response.js -//#SHA1: 3494c79026bf858a01bb497a50a8f2fd3166e62d -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// This test requires the program 'ab' -const http = require("http"); -const { exec } = require("child_process"); - -const bodyLength = 12345; - -const body = "c".repeat(bodyLength); - -let server; - -beforeAll(() => { - return new Promise(resolve => { - server = http.createServer((req, res) => { - res.writeHead(200, { - "Content-Length": bodyLength, - "Content-Type": "text/plain", - }); - res.end(body); - }); - - server.listen(0, () => { - resolve(); - }); - }); -}); - -afterAll(() => { - return new Promise(resolve => { - server.close(() => { - resolve(); - }); - }); -}); - -function runAb(opts) { - return new Promise((resolve, reject) => { - const command = `ab ${opts} http://127.0.0.1:${server.address().port}/`; - exec(command, (err, stdout, stderr) => { - if (err) { - if (/ab|apr/i.test(stderr)) { - console.log(`Skipping: problem spawning \`ab\`.\n${stderr}`); - return resolve(); - } - return reject(err); - } - - let m = /Document Length:\s*(\d+) bytes/i.exec(stdout); - const documentLength = parseInt(m[1]); - - m = /Complete requests:\s*(\d+)/i.exec(stdout); - const completeRequests = parseInt(m[1]); - - m = /HTML transferred:\s*(\d+) bytes/i.exec(stdout); - const htmlTransferred = parseInt(m[1]); - - expect(documentLength).toBe(bodyLength); - expect(htmlTransferred).toBe(completeRequests * documentLength); - - resolve(); - }); - }); -} - -test("-c 1 -n 10", async () => { - await runAb("-c 1 -n 10"); - console.log("-c 1 -n 10 okay"); -}); - -test("-c 1 -n 100", async () => { - await runAb("-c 1 -n 100"); - console.log("-c 1 -n 100 okay"); -}); - -test("-c 1 -n 1000", async () => { - await runAb("-c 1 -n 1000"); - console.log("-c 1 -n 1000 okay"); -}); - -//<#END_FILE: test-http-full-response.js diff --git a/test/js/node/test/parallel/http-get-pipeline-problem.test.js b/test/js/node/test/parallel/http-get-pipeline-problem.test.js deleted file mode 100644 index ec0750f018..0000000000 --- a/test/js/node/test/parallel/http-get-pipeline-problem.test.js +++ /dev/null @@ -1,98 +0,0 @@ -//#FILE: test-http-get-pipeline-problem.js -//#SHA1: 422a6c8ae8350bb70627efee734652421322b1bd -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// In previous versions of Node.js (e.g., 0.6.0), this sort of thing would halt -// after http.globalAgent.maxSockets number of files. -// See https://groups.google.com/forum/#!topic/nodejs-dev/V5fB69hFa9o -const fixtures = require("../common/fixtures"); -const http = require("http"); -const fs = require("fs"); -const tmpdir = require("../common/tmpdir"); - -http.globalAgent.maxSockets = 1; - -tmpdir.refresh(); - -const image = fixtures.readSync("/person.jpg"); - -console.log(`image.length = ${image.length}`); - -const total = 10; - -test("HTTP GET pipeline problem", async () => { - const server = http.createServer((req, res) => { - setTimeout(() => { - res.writeHead(200, { - "content-type": "image/jpeg", - connection: "close", - "content-length": image.length, - }); - res.end(image); - }, 1); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - const serverAddress = server.address(); - - const requests = Array.from({ length: total }, (_, i) => { - return new Promise((resolve, reject) => { - const opts = { - port: serverAddress.port, - headers: { connection: "close" }, - }; - - http - .get(opts, res => { - console.error(`recv ${i}`); - const s = fs.createWriteStream(`${tmpdir.path}/${i}.jpg`); - res.pipe(s); - - s.on("finish", () => { - console.error(`done ${i}`); - resolve(); - }); - }) - .on("error", reject); - }); - }); - - await Promise.all(requests); - - // Check files - const files = fs.readdirSync(tmpdir.path); - expect(files.length).toBeGreaterThanOrEqual(total); - - for (let i = 0; i < total; i++) { - const fn = `${i}.jpg`; - expect(files).toContain(fn); - const stat = fs.statSync(`${tmpdir.path}/${fn}`); - expect(stat.size).toBe(image.length); - } - - server.close(); -}); - -//<#END_FILE: test-http-get-pipeline-problem.js diff --git a/test/js/node/test/parallel/http-head-request.test.js b/test/js/node/test/parallel/http-head-request.test.js deleted file mode 100644 index b2f86c5ac2..0000000000 --- a/test/js/node/test/parallel/http-head-request.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-http-head-request.js -//#SHA1: ab54d1748aa92e4fa61cf4994e83ddf5e00bf874 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -const body = "hello world\n"; - -async function runTest(headers) { - return new Promise((resolve, reject) => { - const server = http.createServer((req, res) => { - console.error("req: %s headers: %j", req.method, headers); - res.writeHead(200, headers); - res.end(); - server.close(); - }); - - server.listen(0, () => { - const request = http.request( - { - port: server.address().port, - method: "HEAD", - path: "/", - }, - response => { - console.error("response start"); - response.on("end", () => { - console.error("response end"); - resolve(); - }); - response.resume(); - }, - ); - request.end(); - }); - }); -} - -test("HEAD request with Transfer-Encoding: chunked", async () => { - await expect( - runTest({ - "Transfer-Encoding": "chunked", - }), - ).resolves.toBeUndefined(); -}); - -test("HEAD request with Content-Length", async () => { - await expect( - runTest({ - "Content-Length": body.length, - }), - ).resolves.toBeUndefined(); -}); - -//<#END_FILE: test-http-head-request.js diff --git a/test/js/node/test/parallel/http-head-response-has-no-body-end-implicit-headers.test.js b/test/js/node/test/parallel/http-head-response-has-no-body-end-implicit-headers.test.js deleted file mode 100644 index 46f4f2269d..0000000000 --- a/test/js/node/test/parallel/http-head-response-has-no-body-end-implicit-headers.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-http-head-response-has-no-body-end-implicit-headers.js -//#SHA1: e2f884b0a99ba30e0e8065596d00af1ed99b4791 -//----------------- -"use strict"; -const http = require("http"); - -// This test is to make sure that when the HTTP server -// responds to a HEAD request with data to res.end, -// it does not send any body but the response is sent -// anyway. - -test("HTTP HEAD response has no body, end implicit headers", done => { - const server = http.createServer((req, res) => { - res.end("FAIL"); // broken: sends FAIL from hot path. - }); - - server.listen(0, () => { - const req = http.request( - { - port: server.address().port, - method: "HEAD", - path: "/", - }, - res => { - res.on("end", () => { - server.close(); - done(); - }); - res.resume(); - }, - ); - req.end(); - }); -}); - -//<#END_FILE: test-http-head-response-has-no-body-end-implicit-headers.js diff --git a/test/js/node/test/parallel/http-head-response-has-no-body-end.test.js b/test/js/node/test/parallel/http-head-response-has-no-body-end.test.js deleted file mode 100644 index 5d18311eb5..0000000000 --- a/test/js/node/test/parallel/http-head-response-has-no-body-end.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-http-head-response-has-no-body-end.js -//#SHA1: 64091937f68588f23597f106fa906d27380be005 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -// This test is to make sure that when the HTTP server -// responds to a HEAD request with data to res.end, -// it does not send any body. - -test("HTTP server responds to HEAD request without sending body", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200); - res.end("FAIL"); // broken: sends FAIL from hot path. - }); - - await new Promise(resolve => { - server.listen(0, () => { - const req = http.request( - { - port: server.address().port, - method: "HEAD", - path: "/", - }, - res => { - const onEnd = jest.fn(); - res.on("end", onEnd); - res.resume(); - - res.on("end", () => { - expect(onEnd).toHaveBeenCalledTimes(1); - server.close(resolve); - }); - }, - ); - req.end(); - }); - }); -}); - -//<#END_FILE: test-http-head-response-has-no-body-end.js diff --git a/test/js/node/test/parallel/http-head-response-has-no-body.test.js b/test/js/node/test/parallel/http-head-response-has-no-body.test.js deleted file mode 100644 index f87fece95a..0000000000 --- a/test/js/node/test/parallel/http-head-response-has-no-body.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-http-head-response-has-no-body.js -//#SHA1: f7df6559885b0465d43994e773c961b525b195a9 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -// This test is to make sure that when the HTTP server -// responds to a HEAD request, it does not send any body. -// In this case it was sending '0\r\n\r\n' - -test("HTTP server responds to HEAD request without body", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200); // broken: defaults to TE chunked - res.end(); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const { port } = server.address(); - - await new Promise((resolve, reject) => { - const req = http.request( - { - port, - method: "HEAD", - path: "/", - }, - res => { - res.on("end", () => { - server.close(() => { - resolve(); - }); - }); - res.resume(); - }, - ); - req.on("error", reject); - req.end(); - }); -}); - -//<#END_FILE: test-http-head-response-has-no-body.js diff --git a/test/js/node/test/parallel/http-header-obstext.test.js b/test/js/node/test/parallel/http-header-obstext.test.js deleted file mode 100644 index 55248d91c4..0000000000 --- a/test/js/node/test/parallel/http-header-obstext.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-http-header-obstext.js -//#SHA1: 031a5230bc91c831407772f2b8cbeba3559ed1d2 -//----------------- -"use strict"; - -// This test ensures that the http-parser can handle UTF-8 characters -// in the http header. - -const http = require("http"); - -test("http-parser can handle UTF-8 characters in http header", async () => { - const server = http.createServer((req, res) => { - res.end("ok"); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - const { port } = server.address(); - - const response = await new Promise(resolve => { - http.get( - { - port, - headers: { Test: "Düsseldorf" }, - }, - resolve, - ); - }); - - expect(response.statusCode).toBe(200); - - await new Promise(resolve => server.close(resolve)); -}); - -//<#END_FILE: test-http-header-obstext.js diff --git a/test/js/node/test/parallel/http-header-owstext.test.js b/test/js/node/test/parallel/http-header-owstext.test.js deleted file mode 100644 index cfab935f17..0000000000 --- a/test/js/node/test/parallel/http-header-owstext.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-http-header-owstext.js -//#SHA1: 339bfcf13a4cc9caa39940de3854eeda01b4500c -//----------------- -"use strict"; - -const http = require("http"); -const net = require("net"); - -// This test ensures that the http-parser strips leading and trailing OWS from -// header values. It sends the header values in chunks to force the parser to -// build the string up through multiple calls to on_header_value(). - -function check(hdr, snd, rcv) { - return new Promise(resolve => { - const server = http.createServer((req, res) => { - expect(req.headers[hdr]).toBe(rcv); - req.pipe(res); - }); - - server.listen(0, function () { - const client = net.connect(this.address().port, start); - function start() { - client.write("GET / HTTP/1.1\r\n" + hdr + ":", drain); - } - - function drain() { - if (snd.length === 0) { - return client.write("\r\nConnection: close\r\n\r\n"); - } - client.write(snd.shift(), drain); - } - - const bufs = []; - client.on("data", function (chunk) { - bufs.push(chunk); - }); - client.on("end", function () { - const head = Buffer.concat(bufs).toString("latin1").split("\r\n")[0]; - expect(head).toBe("HTTP/1.1 200 OK"); - server.close(); - resolve(); - }); - }); - }); -} - -test("http header OWS text parsing", async () => { - await check("host", [" \t foo.com\t"], "foo.com"); - await check("host", [" \t foo\tcom\t"], "foo\tcom"); - await check("host", [" \t", " ", " foo.com\t", "\t "], "foo.com"); - await check("host", [" \t", " \t".repeat(100), "\t "], ""); - await check("host", [" \t", " - - - - ", "\t "], "- - - -"); -}); - -//<#END_FILE: test-http-header-owstext.js diff --git a/test/js/node/test/parallel/http-host-headers.test.js b/test/js/node/test/parallel/http-host-headers.test.js deleted file mode 100644 index cb50ca9b04..0000000000 --- a/test/js/node/test/parallel/http-host-headers.test.js +++ /dev/null @@ -1,77 +0,0 @@ -//#FILE: test-http-host-headers.js -//#SHA1: 256e8b55e2c545a9f9df89607600f18a93c1c67a -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -function reqHandler(req, res) { - if (req.url === "/setHostFalse5") { - expect(req.headers.host).toBeUndefined(); - } else { - expect(req.headers.host).toBe(`localhost:${this.address().port}`); - } - res.writeHead(200, {}); - res.end("ok"); -} - -const httpServer = http.createServer(reqHandler); - -test("HTTP host headers", async () => { - await new Promise(resolve => { - httpServer.listen(0, async () => { - const port = httpServer.address().port; - const makeRequest = (method, path) => { - return new Promise(resolve => { - const req = http.request( - { - method, - path, - host: "localhost", - port, - rejectUnauthorized: false, - }, - res => { - res.resume(); - resolve(); - }, - ); - req.on("error", () => { - throw new Error("Request should not fail"); - }); - req.end(); - }); - }; - - await makeRequest("GET", "/0"); - await makeRequest("GET", "/1"); - await makeRequest("POST", "/2"); - await makeRequest("PUT", "/3"); - await makeRequest("DELETE", "/4"); - - httpServer.close(resolve); - }); - }); -}); - -//<#END_FILE: test-http-host-headers.js diff --git a/test/js/node/test/parallel/http-keep-alive-timeout-custom.test.js b/test/js/node/test/parallel/http-keep-alive-timeout-custom.test.js deleted file mode 100644 index 9d0e874e81..0000000000 --- a/test/js/node/test/parallel/http-keep-alive-timeout-custom.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-http-keep-alive-timeout-custom.js -//#SHA1: 4f7c5a20da7b46bea9198b3854aed7c2042a8691 -//----------------- -"use strict"; - -const http = require("http"); - -test("HTTP Keep-Alive timeout custom", async () => { - const server = http.createServer((req, res) => { - const body = "hello world\n"; - - res.writeHead(200, { - "Content-Length": body.length, - "Keep-Alive": "timeout=50", - }); - res.write(body); - res.end(); - }); - server.keepAliveTimeout = 12010; - - const agent = new http.Agent({ maxSockets: 1, keepAlive: true }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get( - { - path: "/", - port: server.address().port, - agent: agent, - }, - response => { - response.resume(); - expect(response.headers["keep-alive"]).toBe("timeout=50"); - server.close(); - agent.destroy(); - resolve(); - }, - ); - }); - }); -}); - -//<#END_FILE: test-http-keep-alive-timeout-custom.js diff --git a/test/js/node/test/parallel/http-many-ended-pipelines.test.js b/test/js/node/test/parallel/http-many-ended-pipelines.test.js deleted file mode 100644 index c142011e0d..0000000000 --- a/test/js/node/test/parallel/http-many-ended-pipelines.test.js +++ /dev/null @@ -1,82 +0,0 @@ -//#FILE: test-http-many-ended-pipelines.js -//#SHA1: 930bb6dc614c68f965c7b31e9a1223386234e389 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const net = require("net"); - -const numRequests = 20; -let first = false; - -test("HTTP server handles many ended pipelines", async () => { - const server = http.createServer((req, res) => { - if (!first) { - first = true; - req.socket.on("close", () => { - server.close(); - }); - } - - res.end("ok"); - // Oh no! The connection died! - req.socket.destroy(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const client = net.connect({ - port: server.address().port, - allowHalfOpen: true, - }); - - client.on("error", err => { - // The socket might be destroyed by the other peer while data is still - // being written. The `'EPIPE'` and `'ECONNABORTED'` codes might also be - // valid but they have not been seen yet. - expect(err.code).toBe("ECONNRESET"); - }); - - for (let i = 0; i < numRequests; i++) { - client.write("GET / HTTP/1.1\r\n" + "Host: some.host.name\r\n" + "\r\n\r\n"); - } - client.end(); - client.pipe(process.stdout); - - resolve(); - }); - }); -}); - -const mockWarning = jest.spyOn(process, "emit"); -mockWarning.mockImplementation((event, ...args) => { - if (event === "warning") return; - return process.emit.apply(process, [event, ...args]); -}); - -afterAll(() => { - expect(mockWarning).not.toHaveBeenCalledWith("warning", expect.anything()); - mockWarning.mockRestore(); -}); - -//<#END_FILE: test-http-many-ended-pipelines.js diff --git a/test/js/node/test/parallel/http-missing-header-separator-cr.test.js b/test/js/node/test/parallel/http-missing-header-separator-cr.test.js deleted file mode 100644 index 952d726eed..0000000000 --- a/test/js/node/test/parallel/http-missing-header-separator-cr.test.js +++ /dev/null @@ -1,89 +0,0 @@ -//#FILE: test-http-missing-header-separator-cr.js -//#SHA1: 6e213764778e9edddd0fc6a43c9a3183507054c6 -//----------------- -"use strict"; - -const http = require("http"); -const net = require("net"); - -function serverHandler(server, msg) { - const client = net.connect(server.address().port, "localhost"); - - let response = ""; - - client.on("data", chunk => { - response += chunk; - }); - - client.setEncoding("utf8"); - client.on("error", () => { - throw new Error("Client error should not occur"); - }); - client.on("end", () => { - expect(response).toBe("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n"); - server.close(); - }); - client.write(msg); - client.resume(); -} - -test("GET request with invalid header", async () => { - const msg = [ - "GET / HTTP/1.1", - "Host: localhost", - "Dummy: x\nContent-Length: 23", - "", - "GET / HTTP/1.1", - "Dummy: GET /admin HTTP/1.1", - "Host: localhost", - "", - "", - ].join("\r\n"); - - const server = http.createServer(() => { - throw new Error("Server should not be called"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - serverHandler(server, msg); - resolve(); - }); - }); -}); - -test("POST request with invalid Transfer-Encoding header", async () => { - const msg = ["POST / HTTP/1.1", "Host: localhost", "x:x\nTransfer-Encoding: chunked", "", "1", "A", "0", "", ""].join( - "\r\n", - ); - - const server = http.createServer(() => { - throw new Error("Server should not be called"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - serverHandler(server, msg); - resolve(); - }); - }); -}); - -test("POST request with invalid header and Transfer-Encoding", async () => { - const msg = ["POST / HTTP/1.1", "Host: localhost", "x:\nTransfer-Encoding: chunked", "", "1", "A", "0", "", ""].join( - "\r\n", - ); - - const server = http.createServer(() => { - throw new Error("Server should not be called"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - serverHandler(server, msg); - resolve(); - }); - }); -}); - -//<#END_FILE: test-http-missing-header-separator-cr.js diff --git a/test/js/node/test/parallel/http-no-read-no-dump.test.js b/test/js/node/test/parallel/http-no-read-no-dump.test.js deleted file mode 100644 index 2085ea1981..0000000000 --- a/test/js/node/test/parallel/http-no-read-no-dump.test.js +++ /dev/null @@ -1,86 +0,0 @@ -//#FILE: test-http-no-read-no-dump.js -//#SHA1: 8548eb47a6eb8ec151b9c60e74b026d983145d26 -//----------------- -"use strict"; - -const http = require("http"); - -let onPause = null; - -describe("HTTP no read no dump", () => { - let server; - let port; - - beforeAll(done => { - server = http - .createServer((req, res) => { - if (req.method === "GET") return res.end(); - - res.writeHead(200); - res.flushHeaders(); - - req.on("close", () => { - expect(() => { - req.on("end", () => {}); - }).not.toThrow(); - }); - - req.connection.on("pause", () => { - res.end(); - onPause(); - }); - }) - .listen(0, () => { - port = server.address().port; - done(); - }); - }); - - afterAll(done => { - server.close(done); - }); - - test("should handle POST and GET requests correctly", done => { - const agent = new http.Agent({ - maxSockets: 1, - keepAlive: true, - }); - - const post = http.request( - { - agent, - method: "POST", - port, - }, - res => { - res.resume(); - - post.write(Buffer.alloc(64 * 1024).fill("X")); - onPause = () => { - post.end("something"); - }; - }, - ); - - // What happens here is that the server `end`s the response before we send - // `something`, and the client thought that this is a green light for sending - // next GET request - post.write("initial"); - - http - .request( - { - agent, - method: "GET", - port, - }, - res => { - res.connection.end(); - done(); - }, - ) - .end(); - }); -}); - -//<#END_FILE: test-http-no-read-no-dump.js diff --git a/test/js/node/test/parallel/http-outgoing-finish.test.js b/test/js/node/test/parallel/http-outgoing-finish.test.js deleted file mode 100644 index fa7aab9a23..0000000000 --- a/test/js/node/test/parallel/http-outgoing-finish.test.js +++ /dev/null @@ -1,86 +0,0 @@ -//#FILE: test-http-outgoing-finish.js -//#SHA1: cd9dbce2b1b26369349c30bcd94979b354316128 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const http = require("http"); - -test("http outgoing finish", async () => { - const server = http.createServer((req, res) => { - req.resume(); - req.on("end", () => { - write(res); - }); - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const req = http.request({ - port: server.address().port, - method: "PUT", - }); - write(req); - req.on("response", res => { - res.resume(); - }); - resolve(); - }); - }); -}); - -const buf = Buffer.alloc(1024 * 16, "x"); -function write(out) { - const name = out.constructor.name; - let finishEvent = false; - let endCb = false; - - // First, write until it gets some backpressure - while (out.write(buf)); - - // Now end, and make sure that we don't get the 'finish' event - // before the tick where the cb gets called. We give it until - // nextTick because this is added as a listener before the endcb - // is registered. The order is not what we're testing here, just - // that 'finish' isn't emitted until the stream is fully flushed. - out.on("finish", () => { - finishEvent = true; - console.error(`${name} finish event`); - process.nextTick(() => { - expect(endCb).toBe(true); - console.log(`ok - ${name} finishEvent`); - }); - }); - - out.end(buf, () => { - endCb = true; - console.error(`${name} endCb`); - process.nextTick(() => { - expect(finishEvent).toBe(true); - console.log(`ok - ${name} endCb`); - }); - }); -} - -//<#END_FILE: test-http-outgoing-finish.js diff --git a/test/js/node/test/parallel/http-outgoing-finished.test.js b/test/js/node/test/parallel/http-outgoing-finished.test.js deleted file mode 100644 index 96363b2382..0000000000 --- a/test/js/node/test/parallel/http-outgoing-finished.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-http-outgoing-finished.js -//#SHA1: 9c1ce8205b113dbb5b4ddfd06c0c90017b344e15 -//----------------- -"use strict"; - -const http = require("http"); -const { finished } = require("stream"); - -let server; - -beforeAll(() => { - return new Promise(resolve => { - server = http - .createServer((req, res) => { - let closed = false; - res - .on("close", () => { - closed = true; - finished(res, () => { - server.close(); - }); - }) - .end(); - finished(res, () => { - expect(closed).toBe(true); - }); - }) - .listen(0, () => { - resolve(); - }); - }); -}); - -afterAll(() => { - return new Promise(resolve => { - server.close(() => { - resolve(); - }); - }); -}); - -test("HTTP outgoing finished", done => { - const closeHandler = jest.fn(); - const finishedHandler = jest.fn(); - - server.on("request", (req, res) => { - res.on("close", closeHandler); - finished(res, finishedHandler); - }); - - http - .request({ - port: server.address().port, - method: "GET", - }) - .on("response", res => { - res.resume(); - }) - .end(); - - setTimeout(() => { - expect(closeHandler).toHaveBeenCalledTimes(1); - expect(finishedHandler).toHaveBeenCalledTimes(1); - done(); - }, 1000); -}); - -//<#END_FILE: test-http-outgoing-finished.js diff --git a/test/js/node/test/parallel/http-outgoing-writablefinished.test.js b/test/js/node/test/parallel/http-outgoing-writablefinished.test.js deleted file mode 100644 index 2788589d87..0000000000 --- a/test/js/node/test/parallel/http-outgoing-writablefinished.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-http-outgoing-writableFinished.js -//#SHA1: f3fbc0d89cd03168f3ee92ed586b62dd5e3b8edb -//----------------- -"use strict"; - -const http = require("http"); - -test("HTTP server response writableFinished", async () => { - const server = http.createServer((req, res) => { - expect(res.writableFinished).toBe(false); - res.on("finish", () => { - expect(res.writableFinished).toBe(true); - server.close(); - }); - res.end(); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const port = server.address().port; - - const clientRequest = http.request({ - port, - method: "GET", - path: "/", - }); - - expect(clientRequest.writableFinished).toBe(false); - - await new Promise(resolve => { - clientRequest.on("finish", () => { - expect(clientRequest.writableFinished).toBe(true); - resolve(); - }); - clientRequest.end(); - expect(clientRequest.writableFinished).toBe(false); - }); -}); - -//<#END_FILE: test-http-outgoing-writableFinished.js diff --git a/test/js/node/test/parallel/http-outgoing-write-types.test.js b/test/js/node/test/parallel/http-outgoing-write-types.test.js deleted file mode 100644 index 6870939a90..0000000000 --- a/test/js/node/test/parallel/http-outgoing-write-types.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-http-outgoing-write-types.js -//#SHA1: bdeac2ab8008bea1c7e0b22f8744176dea0410e2 -//----------------- -"use strict"; - -const http = require("http"); - -test("HTTP outgoing write types", async () => { - const httpServer = http.createServer((req, res) => { - httpServer.close(); - - expect(() => { - res.write(["Throws."]); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); - - // should not throw - expect(() => res.write("1a2b3c")).not.toThrow(); - - // should not throw - expect(() => res.write(new Uint8Array(1024))).not.toThrow(); - - // should not throw - expect(() => res.write(Buffer.from("1".repeat(1024)))).not.toThrow(); - - res.end(); - }); - - await new Promise(resolve => { - httpServer.listen(0, () => { - http.get({ port: httpServer.address().port }, resolve); - }); - }); -}); - -//<#END_FILE: test-http-outgoing-write-types.js diff --git a/test/js/node/test/parallel/http-pause-no-dump.test.js b/test/js/node/test/parallel/http-pause-no-dump.test.js deleted file mode 100644 index 1f32f2b0a6..0000000000 --- a/test/js/node/test/parallel/http-pause-no-dump.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-http-pause-no-dump.js -//#SHA1: 30c3bd27f5edd0ba060a0d6833061d1ce6379cd5 -//----------------- -"use strict"; - -const http = require("http"); - -test("HTTP pause should not dump", done => { - const server = http.createServer((req, res) => { - req.once("data", () => { - req.pause(); - res.writeHead(200); - res.end(); - res.on("finish", () => { - expect(req._dumped).toBeFalsy(); - }); - }); - }); - - server.listen(0, () => { - const req = http.request( - { - port: server.address().port, - method: "POST", - path: "/", - }, - res => { - expect(res.statusCode).toBe(200); - res.resume(); - res.on("end", () => { - server.close(); - done(); - }); - }, - ); - - req.end(Buffer.allocUnsafe(1024)); - }); -}); - -//<#END_FILE: test-http-pause-no-dump.js diff --git a/test/js/node/test/parallel/http-pause-resume-one-end.test.js b/test/js/node/test/parallel/http-pause-resume-one-end.test.js deleted file mode 100644 index 52198ee8a3..0000000000 --- a/test/js/node/test/parallel/http-pause-resume-one-end.test.js +++ /dev/null @@ -1,70 +0,0 @@ -//#FILE: test-http-pause-resume-one-end.js -//#SHA1: 69f25ca624d470d640d6366b6df27eba31668e96 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -test("HTTP server pause and resume", async () => { - const server = http.Server(function (req, res) { - res.writeHead(200, { "Content-Type": "text/plain" }); - res.end("Hello World\n"); - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const opts = { - port: server.address().port, - headers: { connection: "close" }, - }; - - await new Promise(resolve => { - http.get(opts, res => { - res.on( - "data", - jest.fn().mockImplementation(() => { - res.pause(); - setImmediate(() => { - res.resume(); - }); - }), - ); - - res.on("end", () => { - expect(res.destroyed).toBe(false); - }); - - expect(res.destroyed).toBe(false); - - res.on("close", () => { - expect(res.destroyed).toBe(true); - resolve(); - }); - }); - }); -}); - -//<#END_FILE: test-http-pause-resume-one-end.js diff --git a/test/js/node/test/parallel/http-pause.test.js b/test/js/node/test/parallel/http-pause.test.js deleted file mode 100644 index 74bca8bc34..0000000000 --- a/test/js/node/test/parallel/http-pause.test.js +++ /dev/null @@ -1,87 +0,0 @@ -//#FILE: test-http-pause.js -//#SHA1: d7712077ebe0493c27ffd7180e73fdd409041bf7 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -const expectedServer = "Request Body from Client"; -let resultServer = ""; -const expectedClient = "Response Body from Server"; -let resultClient = ""; - -test("HTTP pause and resume", async () => { - const server = http.createServer((req, res) => { - console.error("pause server request"); - req.pause(); - setTimeout(() => { - console.error("resume server request"); - req.resume(); - req.setEncoding("utf8"); - req.on("data", chunk => { - resultServer += chunk; - }); - req.on("end", () => { - console.error(resultServer); - res.writeHead(200); - res.end(expectedClient); - }); - }, 100); - }); - - await new Promise(resolve => { - server.listen(0, function () { - // Anonymous function rather than arrow function to test `this` value. - expect(this).toBe(server); - const req = http.request( - { - port: this.address().port, - path: "/", - method: "POST", - }, - res => { - console.error("pause client response"); - res.pause(); - setTimeout(() => { - console.error("resume client response"); - res.resume(); - res.on("data", chunk => { - resultClient += chunk; - }); - res.on("end", () => { - console.error(resultClient); - server.close(); - resolve(); - }); - }, 100); - }, - ); - req.end(expectedServer); - }); - }); - - expect(resultServer).toBe(expectedServer); - expect(resultClient).toBe(expectedClient); -}); - -//<#END_FILE: test-http-pause.js diff --git a/test/js/node/test/parallel/http-pipe-fs.test.js b/test/js/node/test/parallel/http-pipe-fs.test.js deleted file mode 100644 index 8db6bebdd3..0000000000 --- a/test/js/node/test/parallel/http-pipe-fs.test.js +++ /dev/null @@ -1,90 +0,0 @@ -//#FILE: test-http-pipe-fs.js -//#SHA1: eb13abd37a9e18b0b28077247a7d336b92b79fbc -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -const NUMBER_OF_STREAMS = 2; - -const tmpdir = path.join(os.tmpdir(), "node-test-http-pipe-fs"); -fs.mkdirSync(tmpdir, { recursive: true }); - -const file = path.join(tmpdir, "http-pipe-fs-test.txt"); - -describe("HTTP pipe to fs", () => { - let server; - - beforeAll(() => { - server = http.createServer((req, res) => { - const stream = fs.createWriteStream(file); - req.pipe(stream); - stream.on("close", () => { - res.writeHead(200); - res.end(); - }); - }); - }); - - afterAll(() => { - return new Promise(resolve => server.close(resolve)); - }); - - it("should handle multiple concurrent requests", async () => { - await new Promise(resolve => server.listen(0, resolve)); - - const port = server.address().port; - http.globalAgent.maxSockets = 1; - - const makeRequest = () => { - return new Promise(resolve => { - const req = http.request( - { - port: port, - method: "POST", - headers: { - "Content-Length": 5, - }, - }, - res => { - res.on("end", resolve); - res.resume(); - }, - ); - - req.end("12345"); - }); - }; - - const requests = Array(NUMBER_OF_STREAMS).fill().map(makeRequest); - await Promise.all(requests); - - expect.assertions(1); - expect(true).toBe(true); // Dummy assertion to ensure the test ran - }); -}); - -//<#END_FILE: test-http-pipe-fs.js diff --git a/test/js/node/test/parallel/http-proxy.test.js b/test/js/node/test/parallel/http-proxy.test.js deleted file mode 100644 index dbb116434c..0000000000 --- a/test/js/node/test/parallel/http-proxy.test.js +++ /dev/null @@ -1,118 +0,0 @@ -//#FILE: test-http-proxy.js -//#SHA1: 7b000418a5941e059d64a57b2f0b4fdfb43eb71d -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); - -const cookies = [ - "session_token=; path=/; expires=Sun, 15-Sep-2030 13:48:52 GMT", - "prefers_open_id=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT", -]; - -const headers = { "content-type": "text/plain", "set-cookie": cookies, hello: "world" }; - -test("HTTP proxy server", async () => { - const backend = http.createServer((req, res) => { - console.error("backend request"); - res.writeHead(200, headers); - res.write("hello world\n"); - res.end(); - }); - - const proxy = http.createServer((req, res) => { - console.error(`proxy req headers: ${JSON.stringify(req.headers)}`); - http.get( - { - port: backend.address().port, - path: url.parse(req.url).pathname, - }, - proxy_res => { - console.error(`proxy res headers: ${JSON.stringify(proxy_res.headers)}`); - - expect(proxy_res.headers.hello).toBe("world"); - expect(proxy_res.headers["content-type"]).toBe("text/plain"); - expect(proxy_res.headers["set-cookie"]).toEqual(cookies); - - res.writeHead(proxy_res.statusCode, proxy_res.headers); - - proxy_res.on("data", chunk => { - res.write(chunk); - }); - - proxy_res.on("end", () => { - res.end(); - console.error("proxy res"); - }); - }, - ); - }); - - let body = ""; - - await new Promise(resolve => { - let nlistening = 0; - function startReq() { - nlistening++; - if (nlistening < 2) return; - - http.get( - { - port: proxy.address().port, - path: "/test", - }, - res => { - console.error("got res"); - expect(res.statusCode).toBe(200); - - expect(res.headers.hello).toBe("world"); - expect(res.headers["content-type"]).toBe("text/plain"); - expect(res.headers["set-cookie"]).toEqual(cookies); - - res.setEncoding("utf8"); - res.on("data", chunk => { - body += chunk; - }); - res.on("end", () => { - proxy.close(); - backend.close(); - console.error("closed both"); - resolve(); - }); - }, - ); - console.error("client req"); - } - - console.error("listen proxy"); - proxy.listen(0, startReq); - - console.error("listen backend"); - backend.listen(0, startReq); - }); - - expect(body).toBe("hello world\n"); -}); - -//<#END_FILE: test-http-proxy.js diff --git a/test/js/node/test/parallel/http-request-arguments.test.js b/test/js/node/test/parallel/http-request-arguments.test.js deleted file mode 100644 index d202ccc5af..0000000000 --- a/test/js/node/test/parallel/http-request-arguments.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-http-request-arguments.js -//#SHA1: c02b492e2dbf5fa6ffcda8a80c3e4ad41bb0c9e5 -//----------------- -"use strict"; - -const http = require("http"); - -// Test providing both a url and options, with the options partially -// replacing address and port portions of the URL provided. -test("http.get with url and options", done => { - const server = http.createServer((req, res) => { - expect(req.url).toBe("/testpath"); - res.end(); - server.close(); - }); - - server.listen(0, () => { - const port = server.address().port; - http.get("http://example.com/testpath", { hostname: "localhost", port }, res => { - res.resume(); - done(); - }); - }); -}); - -//<#END_FILE: test-http-request-arguments.js diff --git a/test/js/node/test/parallel/http-request-end-twice.test.js b/test/js/node/test/parallel/http-request-end-twice.test.js deleted file mode 100644 index b1d2c8a209..0000000000 --- a/test/js/node/test/parallel/http-request-end-twice.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-http-request-end-twice.js -//#SHA1: c8c502b3bf8a681a7acb9afa603a13cebaf1d00e -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -test("http request end twice", async () => { - const server = http.Server((req, res) => { - res.writeHead(200, { "Content-Type": "text/plain" }); - res.end("hello world\n"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const req = http.get({ port: server.address().port }, res => { - res.on("end", () => { - expect(req.end()).toBe(req); - server.close(resolve); - }); - res.resume(); - }); - }); - }); -}); - -//<#END_FILE: test-http-request-end-twice.js diff --git a/test/js/node/test/parallel/http-request-end.test.js b/test/js/node/test/parallel/http-request-end.test.js deleted file mode 100644 index 9ed911d1ad..0000000000 --- a/test/js/node/test/parallel/http-request-end.test.js +++ /dev/null @@ -1,74 +0,0 @@ -//#FILE: test-http-request-end.js -//#SHA1: e86769441d8d182d32d60d4631e32fbcc2675ed9 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -const expected = "Post Body For Test"; - -test("http request end", async () => { - const server = http.Server((req, res) => { - let result = ""; - - req.setEncoding("utf8"); - req.on("data", chunk => { - result += chunk; - }); - - req.on("end", () => { - expect(result).toBe(expected); - res.writeHead(200); - res.end("hello world\n"); - server.close(); - }); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const req = http - .request( - { - port: server.address().port, - path: "/", - method: "POST", - }, - res => { - expect(res.statusCode).toBe(200); - res.resume(); - resolve(); - }, - ) - .on("error", e => { - console.error(e.message); - process.exit(1); - }); - - const result = req.end(expected); - - expect(req).toBe(result); - }); - }); -}); - -//<#END_FILE: test-http-request-end.js diff --git a/test/js/node/test/parallel/http-request-large-payload.test.js b/test/js/node/test/parallel/http-request-large-payload.test.js deleted file mode 100644 index ea69bdc95a..0000000000 --- a/test/js/node/test/parallel/http-request-large-payload.test.js +++ /dev/null @@ -1,33 +0,0 @@ -//#FILE: test-http-request-large-payload.js -//#SHA1: 236870617a867c47c0767e351433c5deb7c87120 -//----------------- -"use strict"; - -// This test ensures Node.js doesn't throw an error when making requests with -// the payload 16kb or more in size. -// https://github.com/nodejs/node/issues/2821 - -const http = require("http"); - -test("HTTP request with large payload", done => { - const server = http.createServer((req, res) => { - res.writeHead(200); - res.end(); - - server.close(); - done(); - }); - - server.listen(0, function () { - const req = http.request({ - method: "POST", - port: this.address().port, - }); - - const payload = Buffer.alloc(16390, "Й"); - req.write(payload); - req.end(); - }); -}); - -//<#END_FILE: test-http-request-large-payload.js diff --git a/test/js/node/test/parallel/http-res-write-after-end.test.js b/test/js/node/test/parallel/http-res-write-after-end.test.js deleted file mode 100644 index 45a17ed5e7..0000000000 --- a/test/js/node/test/parallel/http-res-write-after-end.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-http-res-write-after-end.js -//#SHA1: e579896871bf375190397b4f7e99f37cc156e7c9 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -test("HTTP response write after end", async () => { - const server = http.Server((req, res) => { - res.on("error", error => { - expect(error).toMatchObject({ - code: "ERR_STREAM_WRITE_AFTER_END", - name: "Error", - message: expect.any(String), - }); - }); - - res.write("This should write."); - res.end(); - - const r = res.write("This should raise an error."); - // Write after end should return false - expect(r).toBe(false); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get({ port: server.address().port }, res => { - server.close(resolve); - }); - }); - }); -}); - -//<#END_FILE: test-http-res-write-after-end.js diff --git a/test/js/node/test/parallel/http-response-readable.test.js b/test/js/node/test/parallel/http-response-readable.test.js deleted file mode 100644 index 4af51a42e5..0000000000 --- a/test/js/node/test/parallel/http-response-readable.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-http-response-readable.js -//#SHA1: bfdd12475c68879668c3019c685001244559fb20 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -test("HTTP response readable state", async () => { - const testServer = new http.Server((req, res) => { - res.writeHead(200); - res.end("Hello world"); - }); - - await new Promise(resolve => { - testServer.listen(0, () => { - const port = testServer.address().port; - http.get({ port }, res => { - expect(res.readable).toBe(true); - res.on("end", () => { - expect(res.readable).toBe(false); - testServer.close(resolve); - }); - res.resume(); - }); - }); - }); -}); - -//<#END_FILE: test-http-response-readable.js diff --git a/test/js/node/test/parallel/http-response-writehead-returns-this.test.js b/test/js/node/test/parallel/http-response-writehead-returns-this.test.js deleted file mode 100644 index 9418c9e6b0..0000000000 --- a/test/js/node/test/parallel/http-response-writehead-returns-this.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-http-response-writehead-returns-this.js -//#SHA1: 8a079a3635356290e98a1e7c4eb89b97680b3889 -//----------------- -"use strict"; - -const http = require("http"); - -test("http.ServerResponse.writeHead() returns this", done => { - const server = http.createServer((req, res) => { - res.writeHead(200, { "a-header": "a-header-value" }).end("abc"); - }); - - server.listen(0, () => { - http.get({ port: server.address().port }, res => { - expect(res.headers["a-header"]).toBe("a-header-value"); - - const chunks = []; - - res.on("data", chunk => chunks.push(chunk)); - res.on("end", () => { - expect(Buffer.concat(chunks).toString()).toBe("abc"); - server.close(); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-http-response-writehead-returns-this.js diff --git a/test/js/node/test/parallel/http-server-close-idle-wait-response.test.js b/test/js/node/test/parallel/http-server-close-idle-wait-response.test.js deleted file mode 100644 index 415d32e729..0000000000 --- a/test/js/node/test/parallel/http-server-close-idle-wait-response.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-http-server-close-idle-wait-response.js -//#SHA1: 04c4c10103faabfd084635c9280824668eb0ba18 -//----------------- -"use strict"; - -const { createServer, get } = require("http"); - -test("HTTP server close idle connections after response", async () => { - const server = createServer( - jest.fn((req, res) => { - req.resume(); - - setTimeout(() => { - res.writeHead(204, { Connection: "keep-alive", "Keep-Alive": "timeout=1" }); - res.end(); - }, 1000); - }), - ); - - await new Promise(resolve => { - server.listen(0, () => { - const port = server.address().port; - - get(`http://localhost:${port}`, res => { - server.close(); - }).on("finish", () => { - setTimeout(() => { - server.closeIdleConnections(); - resolve(); - }, 500); - }); - }); - }); - - expect(server.listeners("request")[0]).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-http-server-close-idle-wait-response.js diff --git a/test/js/node/test/parallel/http-server-delete-parser.test.js b/test/js/node/test/parallel/http-server-delete-parser.test.js deleted file mode 100644 index f16d12c848..0000000000 --- a/test/js/node/test/parallel/http-server-delete-parser.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-http-server-delete-parser.js -//#SHA1: 49465ae50d9dac34e834dcb19c02e75b284acdc2 -//----------------- -"use strict"; - -const http = require("http"); - -test("HTTP server deletes parser after write", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200, { "Content-Type": "text/plain" }); - res.write("okay", () => { - delete res.socket.parser; - }); - res.end(); - }); - - await new Promise(resolve => { - server.listen(0, "127.0.0.1", resolve); - }); - - const { port } = server.address(); - - const req = http.request({ - port, - host: "127.0.0.1", - method: "GET", - }); - - await new Promise(resolve => { - req.end(resolve); - }); - - server.close(); -}); - -//<#END_FILE: test-http-server-delete-parser.js diff --git a/test/js/node/test/parallel/http-server-multiheaders.test.js b/test/js/node/test/parallel/http-server-multiheaders.test.js deleted file mode 100644 index f0ba4b4d61..0000000000 --- a/test/js/node/test/parallel/http-server-multiheaders.test.js +++ /dev/null @@ -1,87 +0,0 @@ -//#FILE: test-http-server-multiheaders.js -//#SHA1: 48e657fa74fb8aeeb2d04661ac760ff0cf5bf12a -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Verify that the HTTP server implementation handles multiple instances -// of the same header as per RFC2616: joining the handful of fields by ', ' -// that support it, and dropping duplicates for other fields. - -const http = require("http"); - -test("HTTP server handles multiple instances of the same header correctly", async () => { - const server = http.createServer((req, res) => { - expect(req.headers.accept).toBe("abc, def, ghijklmnopqrst"); - expect(req.headers.host).toBe("foo"); - expect(req.headers["www-authenticate"]).toBe("foo, bar, baz"); - expect(req.headers["proxy-authenticate"]).toBe("foo, bar, baz"); - expect(req.headers["x-foo"]).toBe("bingo"); - expect(req.headers["x-bar"]).toBe("banjo, bango"); - expect(req.headers["sec-websocket-protocol"]).toBe("chat, share"); - expect(req.headers["sec-websocket-extensions"]).toBe("foo; 1, bar; 2, baz"); - expect(req.headers.constructor).toBe("foo, bar, baz"); - - res.writeHead(200, { "Content-Type": "text/plain" }); - res.end("EOF"); - - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get({ - host: "localhost", - port: server.address().port, - path: "/", - headers: [ - ["accept", "abc"], - ["accept", "def"], - ["Accept", "ghijklmnopqrst"], - ["host", "foo"], - ["Host", "bar"], - ["hOst", "baz"], - ["www-authenticate", "foo"], - ["WWW-Authenticate", "bar"], - ["WWW-AUTHENTICATE", "baz"], - ["proxy-authenticate", "foo"], - ["Proxy-Authenticate", "bar"], - ["PROXY-AUTHENTICATE", "baz"], - ["x-foo", "bingo"], - ["x-bar", "banjo"], - ["x-bar", "bango"], - ["sec-websocket-protocol", "chat"], - ["sec-websocket-protocol", "share"], - ["sec-websocket-extensions", "foo; 1"], - ["sec-websocket-extensions", "bar; 2"], - ["sec-websocket-extensions", "baz"], - ["constructor", "foo"], - ["constructor", "bar"], - ["constructor", "baz"], - ], - }); - resolve(); - }); - }); -}); - -//<#END_FILE: test-http-server-multiheaders.js diff --git a/test/js/node/test/parallel/http-server-non-utf8-header.test.js b/test/js/node/test/parallel/http-server-non-utf8-header.test.js deleted file mode 100644 index de10907234..0000000000 --- a/test/js/node/test/parallel/http-server-non-utf8-header.test.js +++ /dev/null @@ -1,67 +0,0 @@ -//#FILE: test-http-server-non-utf8-header.js -//#SHA1: bc84accb29cf80323d0fb55455a596f36a7933b2 -//----------------- -"use strict"; -const http = require("http"); - -const nonUtf8Header = "bår"; -const nonUtf8ToLatin1 = Buffer.from(nonUtf8Header).toString("latin1"); - -test("HTTP server with non-UTF8 header", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200, ["content-disposition", Buffer.from(nonUtf8Header).toString("binary")]); - res.end("hello"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get({ port: server.address().port }, res => { - expect(res.statusCode).toBe(200); - expect(res.headers["content-disposition"]).toBe(nonUtf8ToLatin1); - res.resume().on("end", () => { - server.close(resolve); - }); - }); - }); - }); -}); - -test("HTTP server with multi-value non-UTF8 header", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200, ["content-disposition", [Buffer.from(nonUtf8Header).toString("binary")]]); - res.end("hello"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get({ port: server.address().port }, res => { - expect(res.statusCode).toBe(200); - expect(res.headers["content-disposition"]).toBe(nonUtf8ToLatin1); - res.resume().on("end", () => { - server.close(resolve); - }); - }); - }); - }); -}); - -test("HTTP server with non-UTF8 header and Content-Length", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200, ["Content-Length", "5", "content-disposition", Buffer.from(nonUtf8Header).toString("binary")]); - res.end("hello"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get({ port: server.address().port }, res => { - expect(res.statusCode).toBe(200); - expect(res.headers["content-disposition"]).toBe(nonUtf8ToLatin1); - res.resume().on("end", () => { - server.close(resolve); - }); - }); - }); - }); -}); - -//<#END_FILE: test-http-server-non-utf8-header.js diff --git a/test/js/node/test/parallel/http-server-options-incoming-message.test.js b/test/js/node/test/parallel/http-server-options-incoming-message.test.js deleted file mode 100644 index e8c595a1aa..0000000000 --- a/test/js/node/test/parallel/http-server-options-incoming-message.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-http-server-options-incoming-message.js -//#SHA1: 5d553fff4a2a29f67836269914e5f33b7e91b64e -//----------------- -"use strict"; - -/** - * This test covers http.Server({ IncomingMessage }) option: - * With IncomingMessage option the server should use - * the new class for creating req Object instead of the default - * http.IncomingMessage. - */ -const http = require("http"); - -class MyIncomingMessage extends http.IncomingMessage { - getUserAgent() { - return this.headers["user-agent"] || "unknown"; - } -} - -test("http.Server with custom IncomingMessage", done => { - const server = http.createServer( - { - IncomingMessage: MyIncomingMessage, - }, - (req, res) => { - expect(req.getUserAgent()).toBe("node-test"); - res.statusCode = 200; - res.end(); - }, - ); - - server.listen(() => { - const { port } = server.address(); - - http.get( - { - port, - headers: { - "User-Agent": "node-test", - }, - }, - res => { - expect(res.statusCode).toBe(200); - res.on("end", () => { - server.close(); - done(); - }); - res.resume(); - }, - ); - }); -}); - -//<#END_FILE: test-http-server-options-incoming-message.js diff --git a/test/js/node/test/parallel/http-server-options-server-response.test.js b/test/js/node/test/parallel/http-server-options-server-response.test.js deleted file mode 100644 index 67155a18ac..0000000000 --- a/test/js/node/test/parallel/http-server-options-server-response.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-http-server-options-server-response.js -//#SHA1: ae3128a67e671596c2470bb973747640620b807a -//----------------- -"use strict"; - -/** - * This test covers http.Server({ ServerResponse }) option: - * With ServerResponse option the server should use - * the new class for creating res Object instead of the default - * http.ServerResponse. - */ -const http = require("http"); - -class MyServerResponse extends http.ServerResponse { - status(code) { - return this.writeHead(code, { "Content-Type": "text/plain" }); - } -} - -test("http.Server with custom ServerResponse", done => { - const server = http.Server( - { - ServerResponse: MyServerResponse, - }, - jest.fn((req, res) => { - res.status(200); - res.end(); - }), - ); - - server.listen(() => { - const port = server.address().port; - - http.get({ port }, res => { - expect(res.statusCode).toBe(200); - res.on("end", () => { - server.close(); - done(); - }); - res.resume(); - }); - }); - - server.on("close", () => { - expect(server.listeners("request")[0]).toHaveBeenCalledTimes(1); - }); -}); - -//<#END_FILE: test-http-server-options-server-response.js diff --git a/test/js/node/test/parallel/http-server-reject-chunked-with-content-length.test.js b/test/js/node/test/parallel/http-server-reject-chunked-with-content-length.test.js deleted file mode 100644 index 9bd2f8b82b..0000000000 --- a/test/js/node/test/parallel/http-server-reject-chunked-with-content-length.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-http-server-reject-chunked-with-content-length.js -//#SHA1: e94d6c381c99ba72c2cc2bcbc4c6474a7c63819a -//----------------- -"use strict"; - -const http = require("http"); -const net = require("net"); - -const reqstr = "POST / HTTP/1.1\r\n" + "Content-Length: 1\r\n" + "Transfer-Encoding: chunked\r\n\r\n"; - -test("HTTP server rejects chunked with content length", done => { - const server = http.createServer(expect.any(Function)); - - server.on("clientError", err => { - expect(err.message).toMatch(/^Parse Error/); - expect(err.code).toBe("HPE_INVALID_TRANSFER_ENCODING"); - server.close(); - }); - - server.listen(0, () => { - const client = net.connect({ port: server.address().port }, () => { - client.write(reqstr); - client.end(); - }); - - client.on("data", () => { - // Should not get to this point because the server should simply - // close the connection without returning any data. - throw new Error("no data should be returned by the server"); - }); - - client.on("end", () => { - done(); - }); - }); -}); - -//<#END_FILE: test-http-server-reject-chunked-with-content-length.js diff --git a/test/js/node/test/parallel/http-server-stale-close.test.js b/test/js/node/test/parallel/http-server-stale-close.test.js deleted file mode 100644 index ea0f99dcf0..0000000000 --- a/test/js/node/test/parallel/http-server-stale-close.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-http-server-stale-close.js -//#SHA1: 5c246ffb442bd9ff61779bc300db12d2f3394be4 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const fork = require("child_process").fork; - -if (process.env.NODE_TEST_FORK_PORT) { - const req = http.request( - { - headers: { "Content-Length": "42" }, - method: "POST", - host: "127.0.0.1", - port: +process.env.NODE_TEST_FORK_PORT, - }, - process.exit, - ); - req.write("BAM"); - req.end(); -} else { - test("HTTP server stale close", async () => { - const server = http.createServer((req, res) => { - res.writeHead(200, { "Content-Length": "42" }); - req.pipe(res); - expect(req.destroyed).toBe(false); - req.on("close", () => { - expect(req.destroyed).toBe(true); - server.close(); - res.end(); - }); - }); - - await new Promise(resolve => { - server.listen(0, function () { - fork(__filename, { - env: { ...process.env, NODE_TEST_FORK_PORT: this.address().port }, - }); - resolve(); - }); - }); - }); -} - -//<#END_FILE: test-http-server-stale-close.js diff --git a/test/js/node/test/parallel/http-server-write-after-end.test.js b/test/js/node/test/parallel/http-server-write-after-end.test.js deleted file mode 100644 index c842524c4b..0000000000 --- a/test/js/node/test/parallel/http-server-write-after-end.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-http-server-write-after-end.js -//#SHA1: cacf983393f707ddefc829a25ce16a5bf6f41c19 -//----------------- -"use strict"; - -const http = require("http"); - -// Fix for https://github.com/nodejs/node/issues/14368 - -test("HTTP server write after end", done => { - const server = http.createServer(handle); - - function handle(req, res) { - res.on("error", jest.fn()); - - res.write("hello"); - res.end(); - - setImmediate(() => { - res.write("world", err => { - expect(err).toEqual( - expect.objectContaining({ - code: "ERR_STREAM_WRITE_AFTER_END", - name: "Error", - message: expect.any(String), - }), - ); - server.close(); - done(); - }); - }); - } - - server.listen(0, () => { - http.get(`http://localhost:${server.address().port}`); - }); -}); - -//<#END_FILE: test-http-server-write-after-end.js diff --git a/test/js/node/test/parallel/http-server-write-end-after-end.test.js b/test/js/node/test/parallel/http-server-write-end-after-end.test.js deleted file mode 100644 index 836b835e1f..0000000000 --- a/test/js/node/test/parallel/http-server-write-end-after-end.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-http-server-write-end-after-end.js -//#SHA1: 5b7550b3241cd6b99e607419c3b81d2df519b641 -//----------------- -"use strict"; - -const http = require("http"); - -let server; - -beforeAll(() => { - server = http.createServer(handle); -}); - -afterAll(() => { - server.close(); -}); - -function handle(req, res) { - res.on("error", jest.fn()); - - res.write("hello"); - res.end(); - - setImmediate(() => { - res.end("world"); - process.nextTick(() => { - server.close(); - }); - res.write("world", err => { - expect(err).toMatchObject({ - code: "ERR_STREAM_WRITE_AFTER_END", - name: "Error", - message: expect.any(String), - }); - server.close(); - }); - }); -} - -test("http server write end after end", done => { - server.listen(0, () => { - http.get(`http://localhost:${server.address().port}`); - done(); - }); -}); - -//<#END_FILE: test-http-server-write-end-after-end.js diff --git a/test/js/node/test/parallel/http-set-header-chain.test.js b/test/js/node/test/parallel/http-set-header-chain.test.js deleted file mode 100644 index 85aac8f9e1..0000000000 --- a/test/js/node/test/parallel/http-set-header-chain.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-http-set-header-chain.js -//#SHA1: e009f5ffdce12a659bd2d3402449cd0095d79aa2 -//----------------- -"use strict"; - -const http = require("http"); - -const expected = { - __proto__: null, - testheader1: "foo", - testheader2: "bar", - testheader3: "xyz", -}; - -test("HTTP setHeader chaining", async () => { - const server = http.createServer((req, res) => { - let retval = res.setHeader("testheader1", "foo"); - - // Test that the setHeader returns the same response object. - expect(retval).toBe(res); - - retval = res.setHeader("testheader2", "bar").setHeader("testheader3", "xyz"); - // Test that chaining works for setHeader. - expect(res.getHeaders()).toEqual(expected); - res.end("ok"); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get({ port: server.address().port }, res => { - res.on("data", () => {}); - res.on("end", () => { - server.close(resolve); - }); - }); - }); - }); -}); - -//<#END_FILE: test-http-set-header-chain.js diff --git a/test/js/node/test/parallel/http-upgrade-reconsume-stream.test.js b/test/js/node/test/parallel/http-upgrade-reconsume-stream.test.js deleted file mode 100644 index 57d72bf41d..0000000000 --- a/test/js/node/test/parallel/http-upgrade-reconsume-stream.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-http-upgrade-reconsume-stream.js -//#SHA1: 4117d0b2212d192173b5bd6bf2ef7fe82f627079 -//----------------- -"use strict"; - -const tls = require("tls"); -const http = require("http"); - -// Tests that, after the HTTP parser stopped owning a socket that emits an -// 'upgrade' event, another C++ stream can start owning it (e.g. a TLSSocket). - -test("HTTP upgrade and TLSSocket creation", done => { - const server = http.createServer(expect.any(Function)); - - server.on("upgrade", (request, socket, head) => { - // This should not crash. - new tls.TLSSocket(socket); - server.close(); - socket.destroy(); - done(); - }); - - server.listen(0, () => { - http - .get({ - port: server.address().port, - headers: { - "Connection": "Upgrade", - "Upgrade": "websocket", - }, - }) - .on("error", () => {}); - }); -}); - -//<#END_FILE: test-http-upgrade-reconsume-stream.js diff --git a/test/js/node/test/parallel/http-url.parse-auth-with-header-in-request.test.js b/test/js/node/test/parallel/http-url.parse-auth-with-header-in-request.test.js deleted file mode 100644 index a87bfbd3ac..0000000000 --- a/test/js/node/test/parallel/http-url.parse-auth-with-header-in-request.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-http-url.parse-auth-with-header-in-request.js -//#SHA1: 396adc5e441a57d24b11a42513c834b6b11ea7ff -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); - -test("HTTP request with authorization header in request", async () => { - function check(request) { - // The correct authorization header is be passed - expect(request.headers.authorization).toBe("NoAuthForYOU"); - } - - const server = http.createServer((request, response) => { - // Run the check function - check(request); - response.writeHead(200, {}); - response.end("ok"); - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const testURL = url.parse(`http://asdf:qwer@localhost:${server.address().port}`); - // The test here is if you set a specific authorization header in the - // request we should not override that with basic auth - testURL.headers = { - Authorization: "NoAuthForYOU", - }; - - // make the request - http.request(testURL).end(); - resolve(); - }); - }); -}); - -//<#END_FILE: test-http-url.parse-auth-with-header-in-request.js diff --git a/test/js/node/test/parallel/http-url.parse-auth.test.js b/test/js/node/test/parallel/http-url.parse-auth.test.js deleted file mode 100644 index 439d699393..0000000000 --- a/test/js/node/test/parallel/http-url.parse-auth.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-http-url.parse-auth.js -//#SHA1: 97f9b1c737c705489b2d6402750034291a9f6f63 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); - -test("http url parse auth", async () => { - function check(request) { - // The correct authorization header is be passed - expect(request.headers.authorization).toBe("Basic dXNlcjpwYXNzOg=="); - } - - const server = http.createServer((request, response) => { - // Run the check function - check(request); - response.writeHead(200, {}); - response.end("ok"); - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const port = server.address().port; - // username = "user", password = "pass:" - const testURL = url.parse(`http://user:pass%3A@localhost:${port}`); - - // make the request - http.request(testURL).end(); - resolve(); - }); - }); -}); - -//<#END_FILE: test-http-url.parse-auth.js diff --git a/test/js/node/test/parallel/http-url.parse-basic.test.js b/test/js/node/test/parallel/http-url.parse-basic.test.js deleted file mode 100644 index 9d2d329d3f..0000000000 --- a/test/js/node/test/parallel/http-url.parse-basic.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-http-url.parse-basic.js -//#SHA1: f2f2841de1c82e38067e73196926090f350d89c6 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); - -let testURL; - -// Make sure the basics work -function check(request) { - // Default method should still be 'GET' - expect(request.method).toBe("GET"); - // There are no URL params, so you should not see any - expect(request.url).toBe("/"); - // The host header should use the url.parse.hostname - expect(request.headers.host).toBe(`${testURL.hostname}:${testURL.port}`); -} - -test("HTTP URL parsing basics", async () => { - const server = http.createServer((request, response) => { - // Run the check function - check(request); - response.writeHead(200, {}); - response.end("ok"); - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - testURL = url.parse(`http://localhost:${server.address().port}`); - - // make the request - const clientRequest = http.request(testURL); - // Since there is a little magic with the agent - // make sure that an http request uses the http.Agent - expect(clientRequest.agent).toBeInstanceOf(http.Agent); - clientRequest.end(); - resolve(); - }); - }); -}); - -//<#END_FILE: test-http-url.parse-basic.js diff --git a/test/js/node/test/parallel/http-url.parse-https.request.test.js b/test/js/node/test/parallel/http-url.parse-https.request.test.js deleted file mode 100644 index 6b8a992569..0000000000 --- a/test/js/node/test/parallel/http-url.parse-https.request.test.js +++ /dev/null @@ -1,81 +0,0 @@ -//#FILE: test-http-url.parse-https.request.js -//#SHA1: e9b9e39f28d5d2633f9444150977b748bc8995cb -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const https = require("https"); -const url = require("url"); -const { readKey } = require("../common/fixtures"); - -let common; -try { - common = require("../common"); -} catch (e) { - // For Bun compatibility - common = { - hasCrypto: true, - skip: console.log, - }; -} - -if (!common.hasCrypto) { - common.skip("missing crypto"); - process.exit(0); -} - -// https options -const httpsOptions = { - key: readKey("agent1-key.pem"), - cert: readKey("agent1-cert.pem"), -}; - -function check(request) { - // Assert that I'm https - expect(request.socket._secureEstablished).toBeTruthy(); -} - -test("HTTPS request with URL object", done => { - const server = https.createServer(httpsOptions, function (request, response) { - // Run the check function - check(request); - response.writeHead(200, {}); - response.end("ok"); - server.close(); - }); - - server.listen(0, function () { - const testURL = url.parse(`https://localhost:${this.address().port}`); - testURL.rejectUnauthorized = false; - - // make the request - const clientRequest = https.request(testURL); - // Since there is a little magic with the agent - // make sure that the request uses the https.Agent - expect(clientRequest.agent).toBeInstanceOf(https.Agent); - clientRequest.end(); - done(); - }); -}); - -//<#END_FILE: test-http-url.parse-https.request.js diff --git a/test/js/node/test/parallel/http-url.parse-only-support-http-https-protocol.test.js b/test/js/node/test/parallel/http-url.parse-only-support-http-https-protocol.test.js deleted file mode 100644 index 4f3c5dd20a..0000000000 --- a/test/js/node/test/parallel/http-url.parse-only-support-http-https-protocol.test.js +++ /dev/null @@ -1,52 +0,0 @@ -//#FILE: test-http-url.parse-only-support-http-https-protocol.js -//#SHA1: 924c029f73164388b765c128401affa763af7b56 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); - -const invalidUrls = [ - "file:///whatever", - "mailto:asdf@asdf.com", - "ftp://www.example.com", - "javascript:alert('hello');", - "xmpp:foo@bar.com", - "f://some.host/path", -]; - -describe("http.request with invalid protocols", () => { - test.each(invalidUrls)("throws for invalid URL: %s", invalid => { - expect(() => { - http.request(url.parse(invalid)); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_PROTOCOL", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-http-url.parse-only-support-http-https-protocol.js diff --git a/test/js/node/test/parallel/http-url.parse-path.test.js b/test/js/node/test/parallel/http-url.parse-path.test.js deleted file mode 100644 index 21b456b66f..0000000000 --- a/test/js/node/test/parallel/http-url.parse-path.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-http-url.parse-path.js -//#SHA1: 9eb246a6c09b70b76260a83bec4bb25452d38b7d -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); - -test("http request url parsing", async () => { - function check(request) { - // A path should come over - expect(request.url).toBe("/asdf"); - } - - const server = http.createServer((request, response) => { - // Run the check function - check(request); - response.writeHead(200, {}); - response.end("ok"); - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const testURL = url.parse(`http://localhost:${server.address().port}/asdf`); - - // make the request - http.request(testURL).end(); - resolve(); - }); - }); -}); - -//<#END_FILE: test-http-url.parse-path.js diff --git a/test/js/node/test/parallel/http-url.parse-post.test.js b/test/js/node/test/parallel/http-url.parse-post.test.js deleted file mode 100644 index 800cde5e39..0000000000 --- a/test/js/node/test/parallel/http-url.parse-post.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-http-url.parse-post.js -//#SHA1: e0e7f97c725fb9eaa6058365bef5021e9710e857 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); - -let testURL; - -function check(request) { - // url.parse should not mess with the method - expect(request.method).toBe("POST"); - // Everything else should be right - expect(request.url).toBe("/asdf?qwer=zxcv"); - // The host header should use the url.parse.hostname - expect(request.headers.host).toBe(`${testURL.hostname}:${testURL.port}`); -} - -test("http url parse post", async () => { - const server = http.createServer((request, response) => { - // Run the check function - check(request); - response.writeHead(200, {}); - response.end("ok"); - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - testURL = url.parse(`http://localhost:${server.address().port}/asdf?qwer=zxcv`); - testURL.method = "POST"; - - // make the request - http.request(testURL).end(); - resolve(); - }); - }); -}); - -//<#END_FILE: test-http-url.parse-post.js diff --git a/test/js/node/test/parallel/http-url.parse-search.test.js b/test/js/node/test/parallel/http-url.parse-search.test.js deleted file mode 100644 index fe07df2f63..0000000000 --- a/test/js/node/test/parallel/http-url.parse-search.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-http-url.parse-search.js -//#SHA1: 11d08b9c62625b7b554d5fb46d63c4aaa77c1a7c -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); -const url = require("url"); - -test("HTTP request URL parsing with search params", async () => { - function check(request) { - // A path should come over with params - expect(request.url).toBe("/asdf?qwer=zxcv"); - } - - const server = http.createServer((request, response) => { - // Run the check function - check(request); - response.writeHead(200, {}); - response.end("ok"); - server.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const port = server.address().port; - const testURL = url.parse(`http://localhost:${port}/asdf?qwer=zxcv`); - - // make the request - http.request(testURL).end(); - resolve(); - }); - }); -}); - -//<#END_FILE: test-http-url.parse-search.js diff --git a/test/js/node/test/parallel/http-write-empty-string.test.js b/test/js/node/test/parallel/http-write-empty-string.test.js deleted file mode 100644 index 16e1b0def3..0000000000 --- a/test/js/node/test/parallel/http-write-empty-string.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-http-write-empty-string.js -//#SHA1: 779199784d3142e353324041eeb30924c7e4d5b1 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const http = require("http"); - -test("http write empty string", async () => { - const server = http.createServer(function (request, response) { - console.log(`responding to ${request.url}`); - - response.writeHead(200, { "Content-Type": "text/plain" }); - response.write("1\n"); - response.write(""); - response.write("2\n"); - response.write(""); - response.end("3\n"); - - this.close(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - http.get({ port: server.address().port }, res => { - let response = ""; - - expect(res.statusCode).toBe(200); - res.setEncoding("ascii"); - res.on("data", chunk => { - response += chunk; - }); - res.on("end", () => { - expect(response).toBe("1\n2\n3\n"); - resolve(); - }); - }); - }); - }); -}); - -//<#END_FILE: test-http-write-empty-string.js diff --git a/test/js/node/test/parallel/http-zerolengthbuffer.test.js b/test/js/node/test/parallel/http-zerolengthbuffer.test.js deleted file mode 100644 index 8c82c7a82f..0000000000 --- a/test/js/node/test/parallel/http-zerolengthbuffer.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-http-zerolengthbuffer.js -//#SHA1: 28fff143238744f829f63936c8902047ad2c2fc5 -//----------------- -"use strict"; -// Serving up a zero-length buffer should work. - -const http = require("http"); - -test("Serve zero-length buffer", done => { - const server = http.createServer((req, res) => { - const buffer = Buffer.alloc(0); - res.writeHead(200, { "Content-Type": "text/html", "Content-Length": buffer.length }); - res.end(buffer); - }); - - server.listen(0, () => { - http.get({ port: server.address().port }, res => { - const dataHandler = jest.fn(); - res.on("data", dataHandler); - - res.on("end", () => { - expect(dataHandler).not.toHaveBeenCalled(); - server.close(); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-http-zerolengthbuffer.js diff --git a/test/js/node/test/parallel/http2-binding.test.js b/test/js/node/test/parallel/http2-binding.test.js deleted file mode 100644 index a7ea7fb939..0000000000 --- a/test/js/node/test/parallel/http2-binding.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-http2-binding.js -//#SHA1: 73c6e6b3c2f9b4c9c06183713dbf28454185a1a0 -//----------------- -const http2 = require('http2'); - -describe('HTTP/2 Binding', () => { - beforeAll(() => { - // Skip all tests in this file - jest.spyOn(console, 'log').mockImplementation(() => {}); - console.log('Skipping HTTP/2 binding tests - internal bindings not available'); - }); - - test('SKIP: HTTP/2 binding tests', () => { - expect(true).toBe(true); - }); -}); - -//<#END_FILE: test-http2-binding.test.js diff --git a/test/js/node/test/parallel/http2-client-priority-before-connect.test.js b/test/js/node/test/parallel/http2-client-priority-before-connect.test.js deleted file mode 100644 index 273aa7bf44..0000000000 --- a/test/js/node/test/parallel/http2-client-priority-before-connect.test.js +++ /dev/null @@ -1,58 +0,0 @@ -//#FILE: test-http2-client-priority-before-connect.js -//#SHA1: bc94924856dc82c18ccf699d467d63c28fed0d13 -//----------------- -'use strict'; - -const h2 = require('http2'); - -let server; -let port; - -beforeAll(async () => { - // Check if crypto is available - try { - require('crypto'); - } catch (err) { - return test.skip('missing crypto'); - } -}); - -afterAll(() => { - if (server) { - server.close(); - } -}); - -test('HTTP2 client priority before connect', (done) => { - server = h2.createServer(); - - // We use the lower-level API here - server.on('stream', (stream) => { - stream.respond(); - stream.end('ok'); - }); - - server.listen(0, () => { - port = server.address().port; - const client = h2.connect(`http://localhost:${port}`); - const req = client.request(); - req.priority({}); - - req.on('response', () => { - // Response received - }); - - req.resume(); - - req.on('end', () => { - // Request ended - }); - - req.on('close', () => { - client.close(); - done(); - }); - }); -}); - -//<#END_FILE: test-http2-client-priority-before-connect.js diff --git a/test/js/node/test/parallel/http2-client-request-listeners-warning.test.js b/test/js/node/test/parallel/http2-client-request-listeners-warning.test.js deleted file mode 100644 index a560ec53ad..0000000000 --- a/test/js/node/test/parallel/http2-client-request-listeners-warning.test.js +++ /dev/null @@ -1,70 +0,0 @@ -//#FILE: test-http2-client-request-listeners-warning.js -//#SHA1: cb4f9a71d1f670a78f989caed948e88fa5dbd681 -//----------------- -"use strict"; -const http2 = require("http2"); -const EventEmitter = require("events"); - -// Skip the test if crypto is not available -let hasCrypto; -try { - require("crypto"); - hasCrypto = true; -} catch (err) { - hasCrypto = false; -} - -(hasCrypto ? describe : describe.skip)("HTTP2 client request listeners warning", () => { - let server; - let port; - - beforeAll(done => { - server = http2.createServer(); - server.on("stream", stream => { - stream.respond(); - stream.end(); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("should not emit MaxListenersExceededWarning", done => { - const warningListener = jest.fn(); - process.on("warning", warningListener); - - const client = http2.connect(`http://localhost:${port}`); - - function request() { - return new Promise((resolve, reject) => { - const stream = client.request(); - stream.on("error", reject); - stream.on("response", resolve); - stream.end(); - }); - } - - const requests = []; - for (let i = 0; i < EventEmitter.defaultMaxListeners + 1; i++) { - requests.push(request()); - } - - Promise.all(requests) - .then(() => { - expect(warningListener).not.toHaveBeenCalled(); - }) - .finally(() => { - process.removeListener("warning", warningListener); - client.close(); - done(); - }); - }); -}); - -//<#END_FILE: test-http2-client-request-listeners-warning.js diff --git a/test/js/node/test/parallel/http2-client-shutdown-before-connect.test.js b/test/js/node/test/parallel/http2-client-shutdown-before-connect.test.js deleted file mode 100644 index 18091d3a31..0000000000 --- a/test/js/node/test/parallel/http2-client-shutdown-before-connect.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-http2-client-shutdown-before-connect.js -//#SHA1: 75a343e9d8b577911242f867708310346fe9ddce -//----------------- -'use strict'; - -const h2 = require('http2'); - -// Skip test if crypto is not available -const hasCrypto = (() => { - try { - require('crypto'); - return true; - } catch (err) { - return false; - } -})(); - -if (!hasCrypto) { - test.skip('missing crypto', () => {}); -} else { - test('HTTP/2 client shutdown before connect', (done) => { - const server = h2.createServer(); - - // We use the lower-level API here - server.on('stream', () => { - throw new Error('Stream should not be created'); - }); - - server.listen(0, () => { - const client = h2.connect(`http://localhost:${server.address().port}`); - client.close(() => { - server.close(() => { - done(); - }); - }); - }); - }); -} - -//<#END_FILE: test-http2-client-shutdown-before-connect.js diff --git a/test/js/node/test/parallel/http2-client-write-before-connect.test.js b/test/js/node/test/parallel/http2-client-write-before-connect.test.js deleted file mode 100644 index b245680da9..0000000000 --- a/test/js/node/test/parallel/http2-client-write-before-connect.test.js +++ /dev/null @@ -1,58 +0,0 @@ -//#FILE: test-http2-client-write-before-connect.js -//#SHA1: f38213aa6b5fb615d5b80f0213022ea06e2705cc -//----------------- -'use strict'; - -const h2 = require('http2'); - -let server; -let client; - -beforeAll(() => { - if (!process.versions.openssl) { - test.skip('missing crypto'); - return; - } -}); - -afterEach(() => { - if (client) { - client.close(); - } - if (server) { - server.close(); - } -}); - -test('HTTP/2 client write before connect', (done) => { - server = h2.createServer(); - - server.on('stream', (stream, headers, flags) => { - let data = ''; - stream.setEncoding('utf8'); - stream.on('data', (chunk) => data += chunk); - stream.on('end', () => { - expect(data).toBe('some data more data'); - }); - stream.respond(); - stream.end('ok'); - }); - - server.listen(0, () => { - const port = server.address().port; - client = h2.connect(`http://localhost:${port}`); - - const req = client.request({ ':method': 'POST' }); - req.write('some data '); - req.end('more data'); - - req.on('response', () => {}); - req.resume(); - req.on('end', () => {}); - req.on('close', () => { - done(); - }); - }); -}); - -//<#END_FILE: test-http2-client-write-before-connect.js diff --git a/test/js/node/test/parallel/http2-client-write-empty-string.test.js b/test/js/node/test/parallel/http2-client-write-empty-string.test.js deleted file mode 100644 index daf8182df6..0000000000 --- a/test/js/node/test/parallel/http2-client-write-empty-string.test.js +++ /dev/null @@ -1,74 +0,0 @@ -//#FILE: test-http2-client-write-empty-string.js -//#SHA1: d4371ceba660942fe3c398bbb3144ce691054cec -//----------------- -'use strict'; - -const http2 = require('http2'); - -const runTest = async (chunkSequence) => { - return new Promise((resolve, reject) => { - const server = http2.createServer(); - server.on('stream', (stream, headers, flags) => { - stream.respond({ 'content-type': 'text/html' }); - - let data = ''; - stream.on('data', (chunk) => { - data += chunk.toString(); - }); - stream.on('end', () => { - stream.end(`"${data}"`); - }); - }); - - server.listen(0, async () => { - const port = server.address().port; - const client = http2.connect(`http://localhost:${port}`); - - const req = client.request({ - ':method': 'POST', - ':path': '/' - }); - - req.on('response', (headers) => { - expect(headers[':status']).toBe(200); - expect(headers['content-type']).toBe('text/html'); - }); - - let data = ''; - req.setEncoding('utf8'); - req.on('data', (d) => data += d); - req.on('end', () => { - expect(data).toBe('""'); - server.close(); - client.close(); - resolve(); - }); - - for (const chunk of chunkSequence) { - req.write(chunk); - } - req.end(); - }); - }); -}; - -const testCases = [ - [''], - ['', ''] -]; - -describe('http2 client write empty string', () => { - beforeAll(() => { - if (typeof http2 === 'undefined') { - return test.skip('http2 module not available'); - } - }); - - testCases.forEach((chunkSequence, index) => { - it(`should handle chunk sequence ${index + 1}`, async () => { - await runTest(chunkSequence); - }); - }); -}); - -//<#END_FILE: test-http2-client-write-empty-string.js diff --git a/test/js/node/test/parallel/http2-compat-aborted.test.js b/test/js/node/test/parallel/http2-compat-aborted.test.js deleted file mode 100644 index b304d69e16..0000000000 --- a/test/js/node/test/parallel/http2-compat-aborted.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-http2-compat-aborted.js -//#SHA1: 2aaf11840d98c2b8f4387473180ec86626ac48d1 -//----------------- -"use strict"; - -const h2 = require("http2"); - -let server; -let port; - -beforeAll(done => { - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } - server = h2.createServer((req, res) => { - req.on("aborted", () => { - expect(req.aborted).toBe(true); - expect(req.complete).toBe(true); - }); - expect(req.aborted).toBe(false); - expect(req.complete).toBe(false); - res.write("hello"); - server.close(); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterAll(() => { - if (server) { - server.close(); - } -}); - -test("HTTP/2 compat aborted", done => { - const url = `http://localhost:${port}`; - const client = h2.connect(url, () => { - const request = client.request(); - request.on("data", chunk => { - client.destroy(); - }); - request.on("end", () => { - done(); - }); - }); - - client.on("error", err => { - // Ignore client errors as we're forcibly destroying the connection - }); -}); - -//<#END_FILE: test-http2-compat-aborted.js diff --git a/test/js/node/test/parallel/http2-compat-client-upload-reject.test.js b/test/js/node/test/parallel/http2-compat-client-upload-reject.test.js deleted file mode 100644 index a9e085022b..0000000000 --- a/test/js/node/test/parallel/http2-compat-client-upload-reject.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-http2-compat-client-upload-reject.js -//#SHA1: 4dff98612ac613af951070f79f07f5c1750045da -//----------------- -'use strict'; - -const http2 = require('http2'); -const fs = require('fs'); -const path = require('path'); - -const fixturesPath = path.resolve(__dirname, '..', 'fixtures'); -const loc = path.join(fixturesPath, 'person-large.jpg'); - -let server; -let client; - -beforeAll(() => { - if (!process.versions.openssl) { - return test.skip('missing crypto'); - } -}); - -afterEach(() => { - if (server) server.close(); - if (client) client.close(); -}); - -test('HTTP/2 client upload reject', (done) => { - expect(fs.existsSync(loc)).toBe(true); - - fs.readFile(loc, (err, data) => { - expect(err).toBeNull(); - - server = http2.createServer((req, res) => { - setImmediate(() => { - res.writeHead(400); - res.end(); - }); - }); - - server.listen(0, () => { - const port = server.address().port; - client = http2.connect(`http://localhost:${port}`); - - const req = client.request({ ':method': 'POST' }); - req.on('response', (headers) => { - expect(headers[':status']).toBe(400); - }); - - req.resume(); - req.on('end', () => { - server.close(); - client.close(); - done(); - }); - - const str = fs.createReadStream(loc); - str.pipe(req); - }); - }); -}); - -//<#END_FILE: test-http2-compat-client-upload-reject.js diff --git a/test/js/node/test/parallel/http2-compat-errors.test.js b/test/js/node/test/parallel/http2-compat-errors.test.js deleted file mode 100644 index e326447865..0000000000 --- a/test/js/node/test/parallel/http2-compat-errors.test.js +++ /dev/null @@ -1,67 +0,0 @@ -//#FILE: test-http2-compat-errors.js -//#SHA1: 3a958d2216c02d05272fbc89bd09a532419876a4 -//----------------- -'use strict'; - -const h2 = require('http2'); - -// Simulate crypto check -const hasCrypto = true; -if (!hasCrypto) { - test.skip('missing crypto', () => {}); -} else { - let expected = null; - - describe('http2 compat errors', () => { - let server; - let url; - - beforeAll((done) => { - server = h2.createServer((req, res) => { - const resStreamErrorHandler = jest.fn(); - const reqErrorHandler = jest.fn(); - const resErrorHandler = jest.fn(); - const reqAbortedHandler = jest.fn(); - const resAbortedHandler = jest.fn(); - - res.stream.on('error', resStreamErrorHandler); - req.on('error', reqErrorHandler); - res.on('error', resErrorHandler); - req.on('aborted', reqAbortedHandler); - res.on('aborted', resAbortedHandler); - - res.write('hello'); - - expected = new Error('kaboom'); - res.stream.destroy(expected); - - // Use setImmediate to allow event handlers to be called - setImmediate(() => { - expect(resStreamErrorHandler).toHaveBeenCalled(); - expect(reqErrorHandler).not.toHaveBeenCalled(); - expect(resErrorHandler).not.toHaveBeenCalled(); - expect(reqAbortedHandler).toHaveBeenCalled(); - expect(resAbortedHandler).not.toHaveBeenCalled(); - server.close(done); - }); - }); - - server.listen(0, () => { - url = `http://localhost:${server.address().port}`; - done(); - }); - }); - - test('should handle errors correctly', (done) => { - const client = h2.connect(url, () => { - const request = client.request(); - request.on('data', (chunk) => { - client.destroy(); - done(); - }); - }); - }); - }); -} - -//<#END_FILE: test-http2-compat-errors.js diff --git a/test/js/node/test/parallel/http2-compat-expect-continue-check.test.js b/test/js/node/test/parallel/http2-compat-expect-continue-check.test.js deleted file mode 100644 index 8ee10f45fd..0000000000 --- a/test/js/node/test/parallel/http2-compat-expect-continue-check.test.js +++ /dev/null @@ -1,77 +0,0 @@ -//#FILE: test-http2-compat-expect-continue-check.js -//#SHA1: cfaba2929ccb61aa085572010d7730ceef07859e -//----------------- -'use strict'; - -const http2 = require('http2'); - -const testResBody = 'other stuff!\n'; - -describe('HTTP/2 100-continue flow', () => { - let server; - - beforeAll(() => { - if (!process.versions.openssl) { - return test.skip('missing crypto'); - } - }); - - afterEach(() => { - if (server) { - server.close(); - } - }); - - test('Full 100-continue flow', (done) => { - server = http2.createServer(); - const fullRequestHandler = jest.fn(); - server.on('request', fullRequestHandler); - - server.on('checkContinue', (req, res) => { - res.writeContinue(); - res.writeHead(200, {}); - res.end(testResBody); - - expect(res.writeContinue()).toBe(false); - - res.on('finish', () => { - process.nextTick(() => { - expect(res.writeContinue()).toBe(false); - }); - }); - }); - - server.listen(0, () => { - let body = ''; - - const client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request({ - ':method': 'POST', - 'expect': '100-continue' - }); - - let gotContinue = false; - req.on('continue', () => { - gotContinue = true; - }); - - req.on('response', (headers) => { - expect(gotContinue).toBe(true); - expect(headers[':status']).toBe(200); - req.end(); - }); - - req.setEncoding('utf-8'); - req.on('data', (chunk) => { body += chunk; }); - - req.on('end', () => { - expect(body).toBe(testResBody); - expect(fullRequestHandler).not.toHaveBeenCalled(); - client.close(); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-http2-compat-expect-continue-check.js diff --git a/test/js/node/test/parallel/http2-compat-expect-continue.test.js b/test/js/node/test/parallel/http2-compat-expect-continue.test.js deleted file mode 100644 index b2e98efb5d..0000000000 --- a/test/js/node/test/parallel/http2-compat-expect-continue.test.js +++ /dev/null @@ -1,98 +0,0 @@ -//#FILE: test-http2-compat-expect-continue.js -//#SHA1: 3c95de1bb9a0bf620945ec5fc39ba3a515dfe5fd -//----------------- -'use strict'; - -const http2 = require('http2'); - -// Skip the test if crypto is not available -const hasCrypto = (() => { - try { - require('crypto'); - return true; - } catch (err) { - return false; - } -})(); - -if (!hasCrypto) { - test.skip('missing crypto', () => {}); -} else { - describe('HTTP/2 100-continue flow', () => { - test('full 100-continue flow with response', (done) => { - const testResBody = 'other stuff!\n'; - const server = http2.createServer(); - let sentResponse = false; - - server.on('request', (req, res) => { - res.end(testResBody); - sentResponse = true; - }); - - server.listen(0, () => { - let body = ''; - const client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request({ - ':method': 'POST', - 'expect': '100-continue' - }); - - let gotContinue = false; - req.on('continue', () => { - gotContinue = true; - }); - - req.on('response', (headers) => { - expect(gotContinue).toBe(true); - expect(sentResponse).toBe(true); - expect(headers[':status']).toBe(200); - req.end(); - }); - - req.setEncoding('utf8'); - req.on('data', (chunk) => { body += chunk; }); - req.on('end', () => { - expect(body).toBe(testResBody); - client.close(); - server.close(done); - }); - }); - }); - - test('100-continue flow with immediate response', (done) => { - const server = http2.createServer(); - - server.on('request', (req, res) => { - res.end(); - }); - - server.listen(0, () => { - const client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request({ - ':path': '/', - 'expect': '100-continue' - }); - - let gotContinue = false; - req.on('continue', () => { - gotContinue = true; - }); - - let gotResponse = false; - req.on('response', () => { - gotResponse = true; - }); - - req.setEncoding('utf8'); - req.on('end', () => { - expect(gotContinue).toBe(true); - expect(gotResponse).toBe(true); - client.close(); - server.close(done); - }); - }); - }); - }); -} - -//<#END_FILE: test-http2-compat-expect-continue.js diff --git a/test/js/node/test/parallel/http2-compat-expect-handling.test.js b/test/js/node/test/parallel/http2-compat-expect-handling.test.js deleted file mode 100644 index 2a1940ae23..0000000000 --- a/test/js/node/test/parallel/http2-compat-expect-handling.test.js +++ /dev/null @@ -1,96 +0,0 @@ -//#FILE: test-http2-compat-expect-handling.js -//#SHA1: 015a7b40547c969f4d631e7e743f5293d9e8f843 -//----------------- -"use strict"; - -const http2 = require("http2"); - -const hasCrypto = (() => { - try { - require("crypto"); - return true; - } catch (err) { - return false; - } -})(); - -const expectValue = "meoww"; - -describe("HTTP/2 Expect Header Handling", () => { - let server; - let port; - - beforeAll(done => { - server = http2.createServer(); - server.listen(0, () => { - port = server.address().port; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("server should not call request handler", () => { - const requestHandler = jest.fn(); - server.on("request", requestHandler); - - return new Promise(resolve => { - server.once("checkExpectation", (req, res) => { - expect(req.headers.expect).toBe(expectValue); - res.statusCode = 417; - res.end(); - expect(requestHandler).not.toHaveBeenCalled(); - resolve(); - }); - - const client = http2.connect(`http://localhost:${port}`); - const req = client.request({ - ":path": "/", - ":method": "GET", - ":scheme": "http", - ":authority": `localhost:${port}`, - "expect": expectValue, - }); - - req.on("response", headers => { - expect(headers[":status"]).toBe(417); - req.resume(); - }); - - req.on("end", () => { - client.close(); - }); - }); - }); - - test("client should receive 417 status", () => { - return new Promise(resolve => { - const client = http2.connect(`http://localhost:${port}`); - const req = client.request({ - ":path": "/", - ":method": "GET", - ":scheme": "http", - ":authority": `localhost:${port}`, - "expect": expectValue, - }); - - req.on("response", headers => { - expect(headers[":status"]).toBe(417); - req.resume(); - }); - - req.on("end", () => { - client.close(); - resolve(); - }); - }); - }); -}); - -if (!hasCrypto) { - test.skip("skipping HTTP/2 tests due to missing crypto support", () => {}); -} - -//<#END_FILE: test-http2-compat-expect-handling.js diff --git a/test/js/node/test/parallel/http2-compat-serverrequest-pause.test.js b/test/js/node/test/parallel/http2-compat-serverrequest-pause.test.js deleted file mode 100644 index a42d021210..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverrequest-pause.test.js +++ /dev/null @@ -1,75 +0,0 @@ -//#FILE: test-http2-compat-serverrequest-pause.js -//#SHA1: 3f3eff95f840e6321b0d25211ef5116304049dc7 -//----------------- -'use strict'; - -const h2 = require('http2'); - -const hasCrypto = (() => { - try { - require('crypto'); - return true; - } catch (err) { - return false; - } -})(); - -if (!hasCrypto) { - test.skip('missing crypto', () => {}); -} else { - const testStr = 'Request Body from Client'; - let server; - let client; - - beforeAll(() => { - server = h2.createServer(); - }); - - afterAll(() => { - if (client) client.close(); - if (server) server.close(); - }); - - test('pause & resume work as expected with Http2ServerRequest', (done) => { - const requestHandler = jest.fn((req, res) => { - let data = ''; - req.pause(); - req.setEncoding('utf8'); - req.on('data', jest.fn((chunk) => (data += chunk))); - setTimeout(() => { - expect(data).toBe(''); - req.resume(); - }, 100); - req.on('end', () => { - expect(data).toBe(testStr); - res.end(); - }); - - res.on('finish', () => process.nextTick(() => { - req.pause(); - req.resume(); - })); - }); - - server.on('request', requestHandler); - - server.listen(0, () => { - const port = server.address().port; - - client = h2.connect(`http://localhost:${port}`); - const request = client.request({ - ':path': '/foobar', - ':method': 'POST', - ':scheme': 'http', - ':authority': `localhost:${port}` - }); - request.resume(); - request.end(testStr); - request.on('end', () => { - expect(requestHandler).toHaveBeenCalled(); - done(); - }); - }); - }); -} -//<#END_FILE: test-http2-compat-serverrequest-pause.js diff --git a/test/js/node/test/parallel/http2-compat-serverrequest-pipe.test.js b/test/js/node/test/parallel/http2-compat-serverrequest-pipe.test.js deleted file mode 100644 index 47ed561685..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverrequest-pipe.test.js +++ /dev/null @@ -1,69 +0,0 @@ -//#FILE: test-http2-compat-serverrequest-pipe.js -//#SHA1: c4254ac88df3334dccc8adb4b60856193a6e644e -//----------------- -"use strict"; - -const http2 = require("http2"); -const fs = require("fs"); -const path = require("path"); -const os = require("os"); -const { isWindows } = require("harness"); - -const fixtures = path.join(__dirname, "..", "fixtures"); -const tmpdir = os.tmpdir(); - -let server; -let client; -let port; - -beforeAll(async () => { - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } - - await fs.promises.mkdir(tmpdir, { recursive: true }); -}); - -afterAll(async () => { - if (server) server.close(); - if (client) client.close(); -}); - -test.todoIf(isWindows)("HTTP/2 server request pipe", done => { - const loc = path.join(fixtures, "person-large.jpg"); - const fn = path.join(tmpdir, "http2-url-tests.js"); - - server = http2.createServer(); - - server.on("request", (req, res) => { - const dest = req.pipe(fs.createWriteStream(fn)); - dest.on("finish", () => { - expect(req.complete).toBe(true); - expect(fs.readFileSync(loc).length).toBe(fs.readFileSync(fn).length); - fs.unlinkSync(fn); - res.end(); - }); - }); - - server.listen(0, () => { - port = server.address().port; - client = http2.connect(`http://localhost:${port}`); - - let remaining = 2; - function maybeClose() { - if (--remaining === 0) { - done(); - } - } - - const req = client.request({ ":method": "POST" }); - req.on("response", () => {}); - req.resume(); - req.on("end", maybeClose); - const str = fs.createReadStream(loc); - str.on("end", maybeClose); - str.pipe(req); - }); -}); - -//<#END_FILE: test-http2-compat-serverrequest-pipe.js diff --git a/test/js/node/test/parallel/http2-compat-serverrequest.test.js b/test/js/node/test/parallel/http2-compat-serverrequest.test.js deleted file mode 100644 index 2349965420..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverrequest.test.js +++ /dev/null @@ -1,69 +0,0 @@ -//#FILE: test-http2-compat-serverrequest.js -//#SHA1: f661c6c9249c0cdc770439f7498943fc5edbf86b -//----------------- -"use strict"; - -const h2 = require("http2"); -const net = require("net"); - -let server; -let port; - -beforeAll(done => { - server = h2.createServer(); - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterAll(done => { - server.close(done); -}); - -// today we deatch the socket earlier -test.todo("Http2ServerRequest should expose convenience properties", done => { - expect.assertions(7); - - server.once("request", (request, response) => { - const expected = { - version: "2.0", - httpVersionMajor: 2, - httpVersionMinor: 0, - }; - - expect(request.httpVersion).toBe(expected.version); - expect(request.httpVersionMajor).toBe(expected.httpVersionMajor); - expect(request.httpVersionMinor).toBe(expected.httpVersionMinor); - - expect(request.socket).toBeInstanceOf(net.Socket); - expect(request.connection).toBeInstanceOf(net.Socket); - expect(request.socket).toBe(request.connection); - - response.on("finish", () => { - process.nextTick(() => { - expect(request.socket).toBeTruthy(); - done(); - }); - }); - response.end(); - }); - - const url = `http://localhost:${port}`; - const client = h2.connect(url, () => { - const headers = { - ":path": "/foobar", - ":method": "GET", - ":scheme": "http", - ":authority": `localhost:${port}`, - }; - const request = client.request(headers); - request.on("end", () => { - client.close(); - }); - request.end(); - request.resume(); - }); -}); - -//<#END_FILE: test-http2-compat-serverrequest.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-close.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-close.test.js deleted file mode 100644 index 6ae966fc55..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-close.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-close.js -//#SHA1: 6b61a9cea948447ae33843472678ffbed0b47c9a -//----------------- -"use strict"; - -const h2 = require("http2"); - -// Skip the test if crypto is not available -let hasCrypto; -try { - require("crypto"); - hasCrypto = true; -} catch (err) { - hasCrypto = false; -} - -(hasCrypto ? describe : describe.skip)("HTTP/2 server response close", () => { - let server; - let url; - - beforeAll(done => { - server = h2.createServer((req, res) => { - res.writeHead(200); - res.write("a"); - - const reqCloseMock = jest.fn(); - const resCloseMock = jest.fn(); - const reqErrorMock = jest.fn(); - - req.on("close", reqCloseMock); - res.on("close", resCloseMock); - req.on("error", reqErrorMock); - - // Use Jest's fake timers to ensure the test doesn't hang - setTimeout(() => { - expect(reqCloseMock).toHaveBeenCalled(); - expect(resCloseMock).toHaveBeenCalled(); - expect(reqErrorMock).not.toHaveBeenCalled(); - done(); - }, 1000); - }); - - server.listen(0, () => { - url = `http://localhost:${server.address().port}`; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("Server request and response should receive close event if connection terminated before response.end", done => { - const client = h2.connect(url, () => { - const request = client.request(); - request.on("data", chunk => { - client.destroy(); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-http2-compat-serverresponse-close.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-drain.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-drain.test.js deleted file mode 100644 index 4976ad2284..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-drain.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-drain.js -//#SHA1: 4ec55745f622a31b4729fcb9daf9bfd707a3bdb3 -//----------------- -'use strict'; - -const h2 = require('http2'); - -const hasCrypto = (() => { - try { - require('crypto'); - return true; - } catch (err) { - return false; - } -})(); - -const testString = 'tests'; - -test('HTTP/2 server response drain event', async () => { - if (!hasCrypto) { - test.skip('missing crypto'); - return; - } - - const server = h2.createServer(); - - const requestHandler = jest.fn((req, res) => { - res.stream._writableState.highWaterMark = testString.length; - expect(res.write(testString)).toBe(false); - res.on('drain', jest.fn(() => res.end(testString))); - }); - - server.on('request', requestHandler); - - await new Promise(resolve => server.listen(0, resolve)); - const port = server.address().port; - - const client = h2.connect(`http://localhost:${port}`); - const request = client.request({ - ':path': '/foobar', - ':method': 'POST', - ':scheme': 'http', - ':authority': `localhost:${port}` - }); - request.resume(); - request.end(); - - let data = ''; - request.setEncoding('utf8'); - request.on('data', (chunk) => (data += chunk)); - - await new Promise(resolve => request.on('end', resolve)); - - expect(data).toBe(testString.repeat(2)); - expect(requestHandler).toHaveBeenCalled(); - - client.close(); - await new Promise(resolve => server.close(resolve)); -}); - -//<#END_FILE: test-http2-compat-serverresponse-drain.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-end-after-statuses-without-body.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-end-after-statuses-without-body.test.js deleted file mode 100644 index 2dd0f00dd3..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-end-after-statuses-without-body.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-end-after-statuses-without-body.js -//#SHA1: c4a4b76e1b04b7e6779f80f7077758dfab0e8b80 -//----------------- -"use strict"; - -const h2 = require("http2"); - -const { HTTP_STATUS_NO_CONTENT, HTTP_STATUS_RESET_CONTENT, HTTP_STATUS_NOT_MODIFIED } = h2.constants; - -const statusWithoutBody = [HTTP_STATUS_NO_CONTENT, HTTP_STATUS_RESET_CONTENT, HTTP_STATUS_NOT_MODIFIED]; -const STATUS_CODES_COUNT = statusWithoutBody.length; - -describe("HTTP/2 server response end after statuses without body", () => { - let server; - let url; - - beforeAll(done => { - server = h2.createServer((req, res) => { - res.writeHead(statusWithoutBody.pop()); - res.end(); - }); - - server.listen(0, () => { - url = `http://localhost:${server.address().port}`; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - it("should handle end() after sending statuses without body", done => { - const client = h2.connect(url, () => { - let responseCount = 0; - const closeAfterResponse = () => { - if (STATUS_CODES_COUNT === ++responseCount) { - client.destroy(); - done(); - } - }; - - for (let i = 0; i < STATUS_CODES_COUNT; i++) { - const request = client.request(); - request.on("response", closeAfterResponse); - } - }); - }); -}); - -//<#END_FILE: test-http2-compat-serverresponse-end-after-statuses-without-body.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-end.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-end.test.js deleted file mode 100644 index 27b1f393db..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-end.test.js +++ /dev/null @@ -1,80 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-end.js -//#SHA1: 672da69abcb0b86d5234556e692949ac36ef6395 -//----------------- -'use strict'; - -const http2 = require('http2'); -const { promisify } = require('util'); - -// Mock the common module functions -const mustCall = (fn) => jest.fn(fn); -const mustNotCall = () => jest.fn().mockImplementation(() => { - throw new Error('This function should not have been called'); -}); - -const { - HTTP2_HEADER_STATUS, - HTTP_STATUS_OK -} = http2.constants; - -// Helper function to create a server and get its port -const createServerAndGetPort = async (requestListener) => { - const server = http2.createServer(requestListener); - await promisify(server.listen.bind(server))(0); - const { port } = server.address(); - return { server, port }; -}; - -// Helper function to create a client -const createClient = (port) => { - const url = `http://localhost:${port}`; - return http2.connect(url); -}; - -describe('Http2ServerResponse.end', () => { - test('accepts chunk, encoding, cb as args and can be called multiple times', async () => { - const { server, port } = await createServerAndGetPort((request, response) => { - const endCallback = jest.fn(() => { - response.end(jest.fn()); - process.nextTick(() => { - response.end(jest.fn()); - server.close(); - }); - }); - - response.end('end', 'utf8', endCallback); - response.on('finish', () => { - response.end(jest.fn()); - }); - response.end(jest.fn()); - }); - - const client = createClient(port); - const headers = { - ':path': '/', - ':method': 'GET', - ':scheme': 'http', - ':authority': `localhost:${port}` - }; - - let data = ''; - const request = client.request(headers); - request.setEncoding('utf8'); - request.on('data', (chunk) => (data += chunk)); - await new Promise(resolve => { - request.on('end', () => { - expect(data).toBe('end'); - client.close(); - resolve(); - }); - request.end(); - request.resume(); - }); - }); - - // Add more tests here... -}); - -// More test blocks for other scenarios... - -//<#END_FILE: test-http2-compat-serverresponse-end.test.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-finished.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-finished.test.js deleted file mode 100644 index fb6f9c2b52..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-finished.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-finished.js -//#SHA1: 6ef7a05f30923975d7a267cee54aafae1bfdbc7d -//----------------- -'use strict'; - -const h2 = require('http2'); -const net = require('net'); - -let server; - -beforeAll(() => { - // Skip the test if crypto is not available - if (!process.versions.openssl) { - return test.skip('missing crypto'); - } -}); - -afterEach(() => { - if (server) { - server.close(); - } -}); - -test('Http2ServerResponse.finished', (done) => { - server = h2.createServer(); - server.listen(0, () => { - const port = server.address().port; - - server.once('request', (request, response) => { - expect(response.socket).toBeInstanceOf(net.Socket); - expect(response.connection).toBeInstanceOf(net.Socket); - expect(response.socket).toBe(response.connection); - - response.on('finish', () => { - expect(response.socket).toBeUndefined(); - expect(response.connection).toBeUndefined(); - process.nextTick(() => { - expect(response.stream).toBeDefined(); - done(); - }); - }); - - expect(response.finished).toBe(false); - expect(response.writableEnded).toBe(false); - response.end(); - expect(response.finished).toBe(true); - expect(response.writableEnded).toBe(true); - }); - - const url = `http://localhost:${port}`; - const client = h2.connect(url, () => { - const headers = { - ':path': '/', - ':method': 'GET', - ':scheme': 'http', - ':authority': `localhost:${port}` - }; - const request = client.request(headers); - request.on('end', () => { - client.close(); - }); - request.end(); - request.resume(); - }); - }); -}); - -//<#END_FILE: test-http2-compat-serverresponse-finished.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-flushheaders.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-flushheaders.test.js deleted file mode 100644 index 6d0864b507..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-flushheaders.test.js +++ /dev/null @@ -1,71 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-flushheaders.js -//#SHA1: ea772e05a29f43bd7b61e4d70f24b94c1e1e201c -//----------------- -"use strict"; - -const h2 = require("http2"); - -let server; -let serverResponse; - -beforeAll(done => { - server = h2.createServer(); - server.listen(0, () => { - done(); - }); -}); - -afterAll(() => { - server.close(); -}); - -test("Http2ServerResponse.flushHeaders", done => { - const port = server.address().port; - - server.once("request", (request, response) => { - expect(response.headersSent).toBe(false); - expect(response._header).toBe(false); // Alias for headersSent - response.flushHeaders(); - expect(response.headersSent).toBe(true); - expect(response._header).toBe(true); - response.flushHeaders(); // Idempotent - - expect(() => { - response.writeHead(400, { "foo-bar": "abc123" }); - }).toThrow( - expect.objectContaining({ - code: "ERR_HTTP2_HEADERS_SENT", - }), - ); - response.on("finish", () => { - process.nextTick(() => { - response.flushHeaders(); // Idempotent - done(); - }); - }); - serverResponse = response; - }); - - const url = `http://localhost:${port}`; - const client = h2.connect(url, () => { - const headers = { - ":path": "/", - ":method": "GET", - ":scheme": "http", - ":authority": `localhost:${port}`, - }; - const request = client.request(headers); - request.on("response", (headers, flags) => { - expect(headers["foo-bar"]).toBeUndefined(); - expect(headers[":status"]).toBe(200); - serverResponse.end(); - }); - request.on("end", () => { - client.close(); - }); - request.end(); - request.resume(); - }); -}); - -//<#END_FILE: test-http2-compat-serverresponse-flushheaders.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-headers-send-date.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-headers-send-date.test.js deleted file mode 100644 index 6f410d12f1..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-headers-send-date.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-headers-send-date.js -//#SHA1: 1ed6319986a3bb9bf58709d9577d03407fdde3f2 -//----------------- -"use strict"; -const http2 = require("http2"); - -let server; -let port; - -beforeAll(done => { - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } - - server = http2.createServer((request, response) => { - response.sendDate = false; - response.writeHead(200); - response.end(); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterAll(() => { - server.close(); -}); - -test("HTTP/2 server response should not send Date header when sendDate is false", done => { - const session = http2.connect(`http://localhost:${port}`); - const req = session.request(); - - req.on("response", (headers, flags) => { - expect(headers).not.toHaveProperty("Date"); - expect(headers).not.toHaveProperty("date"); - }); - - req.on("end", () => { - session.close(); - done(); - }); - - req.end(); -}); - -//<#END_FILE: test-http2-compat-serverresponse-headers-send-date.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-settimeout.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-settimeout.test.js deleted file mode 100644 index 305f398176..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-settimeout.test.js +++ /dev/null @@ -1,78 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-settimeout.js -//#SHA1: fe2e0371e885463968a268362464724494b758a6 -//----------------- -"use strict"; - -const http2 = require("http2"); - -const msecs = 1000; // Assuming a reasonable timeout for all platforms - -let server; -let client; - -beforeAll(done => { - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } - server = http2.createServer(); - server.listen(0, () => { - done(); - }); -}); - -afterAll(() => { - if (client) { - client.close(); - } - if (server) { - server.close(); - } -}); - -test("HTTP2 ServerResponse setTimeout", done => { - const timeoutCallback = jest.fn(); - const onTimeout = jest.fn(); - const onFinish = jest.fn(); - - server.on("request", (req, res) => { - res.setTimeout(msecs, timeoutCallback); - res.on("timeout", onTimeout); - res.on("finish", () => { - onFinish(); - res.setTimeout(msecs, jest.fn()); - process.nextTick(() => { - res.setTimeout(msecs, jest.fn()); - }); - }); - - // Explicitly end the response after a short delay - setTimeout(() => { - res.end(); - }, 100); - }); - - const port = server.address().port; - client = http2.connect(`http://localhost:${port}`); - const req = client.request({ - ":path": "/", - ":method": "GET", - ":scheme": "http", - ":authority": `localhost:${port}`, - }); - - req.on("end", () => { - client.close(); - - // Move assertions here to ensure they run after the response has finished - expect(timeoutCallback).not.toHaveBeenCalled(); - expect(onTimeout).not.toHaveBeenCalled(); - expect(onFinish).toHaveBeenCalledTimes(1); - - done(); - }); - - req.resume(); - req.end(); -}, 10000); // Increase the timeout to 10 seconds - -//<#END_FILE: test-http2-compat-serverresponse-settimeout.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-statuscode.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-statuscode.test.js deleted file mode 100644 index 8845f6c532..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-statuscode.test.js +++ /dev/null @@ -1,95 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-statuscode.js -//#SHA1: 10cb487c1fd9e256f807319b84c426b356be443f -//----------------- -"use strict"; - -const h2 = require("http2"); - -let server; -let port; - -beforeAll(async () => { - server = h2.createServer(); - await new Promise(resolve => server.listen(0, resolve)); - port = server.address().port; -}); - -afterAll(async () => { - server.close(); -}); - -test("Http2ServerResponse should have a statusCode property", async () => { - const responsePromise = new Promise(resolve => { - server.once("request", (request, response) => { - const expectedDefaultStatusCode = 200; - const realStatusCodes = { - continue: 100, - ok: 200, - multipleChoices: 300, - badRequest: 400, - internalServerError: 500, - }; - const fakeStatusCodes = { - tooLow: 99, - tooHigh: 600, - }; - - expect(response.statusCode).toBe(expectedDefaultStatusCode); - - // Setting the response.statusCode should not throw. - response.statusCode = realStatusCodes.ok; - response.statusCode = realStatusCodes.multipleChoices; - response.statusCode = realStatusCodes.badRequest; - response.statusCode = realStatusCodes.internalServerError; - - expect(() => { - response.statusCode = realStatusCodes.continue; - }).toThrow( - expect.objectContaining({ - code: "ERR_HTTP2_INFO_STATUS_NOT_ALLOWED", - name: "RangeError", - }), - ); - - expect(() => { - response.statusCode = fakeStatusCodes.tooLow; - }).toThrow( - expect.objectContaining({ - code: "ERR_HTTP2_STATUS_INVALID", - name: "RangeError", - }), - ); - - expect(() => { - response.statusCode = fakeStatusCodes.tooHigh; - }).toThrow( - expect.objectContaining({ - code: "ERR_HTTP2_STATUS_INVALID", - name: "RangeError", - }), - ); - - response.on("finish", resolve); - response.end(); - }); - }); - - const url = `http://localhost:${port}`; - const client = h2.connect(url); - - const headers = { - ":path": "/", - ":method": "GET", - ":scheme": "http", - ":authority": `localhost:${port}`, - }; - - const request = client.request(headers); - request.end(); - await new Promise(resolve => request.resume().on("end", resolve)); - - await responsePromise; - client.close(); -}); - -//<#END_FILE: test-http2-compat-serverresponse-statuscode.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-writehead-array.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-writehead-array.test.js deleted file mode 100644 index 2b1ca358a9..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-writehead-array.test.js +++ /dev/null @@ -1,114 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-writehead-array.js -//#SHA1: e43a5a9f99ddad68b313e15fbb69839cca6d0775 -//----------------- -'use strict'; - -const http2 = require('http2'); - -// Skip the test if crypto is not available -const hasCrypto = (() => { - try { - require('crypto'); - return true; - } catch (err) { - return false; - } -})(); - -if (!hasCrypto) { - test.skip('missing crypto', () => {}); -} else { - describe('Http2ServerResponse.writeHead with arrays', () => { - test('should support nested arrays', (done) => { - const server = http2.createServer(); - server.listen(0, () => { - const port = server.address().port; - - server.once('request', (request, response) => { - const returnVal = response.writeHead(200, [ - ['foo', 'bar'], - ['foo', 'baz'], - ['ABC', 123], - ]); - expect(returnVal).toBe(response); - response.end(() => { server.close(); }); - }); - - const client = http2.connect(`http://localhost:${port}`, () => { - const request = client.request(); - - request.on('response', (headers) => { - expect(headers.foo).toBe('bar, baz'); - expect(headers.abc).toBe('123'); - expect(headers[':status']).toBe(200); - }); - request.on('end', () => { - client.close(); - done(); - }); - request.end(); - request.resume(); - }); - }); - }); - - test('should support flat arrays', (done) => { - const server = http2.createServer(); - server.listen(0, () => { - const port = server.address().port; - - server.once('request', (request, response) => { - const returnVal = response.writeHead(200, ['foo', 'bar', 'foo', 'baz', 'ABC', 123]); - expect(returnVal).toBe(response); - response.end(() => { server.close(); }); - }); - - const client = http2.connect(`http://localhost:${port}`, () => { - const request = client.request(); - - request.on('response', (headers) => { - expect(headers.foo).toBe('bar, baz'); - expect(headers.abc).toBe('123'); - expect(headers[':status']).toBe(200); - }); - request.on('end', () => { - client.close(); - done(); - }); - request.end(); - request.resume(); - }); - }); - }); - - test('should throw ERR_INVALID_ARG_VALUE for invalid array', (done) => { - const server = http2.createServer(); - server.listen(0, () => { - const port = server.address().port; - - server.once('request', (request, response) => { - expect(() => { - response.writeHead(200, ['foo', 'bar', 'ABC', 123, 'extra']); - }).toThrow(expect.objectContaining({ - code: 'ERR_INVALID_ARG_VALUE' - })); - - response.end(() => { server.close(); }); - }); - - const client = http2.connect(`http://localhost:${port}`, () => { - const request = client.request(); - - request.on('end', () => { - client.close(); - done(); - }); - request.end(); - request.resume(); - }); - }); - }); - }); -} - -//<#END_FILE: test-http2-compat-serverresponse-writehead-array.js diff --git a/test/js/node/test/parallel/http2-compat-serverresponse-writehead.test.js b/test/js/node/test/parallel/http2-compat-serverresponse-writehead.test.js deleted file mode 100644 index 296a1e1a73..0000000000 --- a/test/js/node/test/parallel/http2-compat-serverresponse-writehead.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-http2-compat-serverresponse-writehead.js -//#SHA1: fa267d5108f95ba69583bc709a82185ee9d18e76 -//----------------- -'use strict'; - -const h2 = require('http2'); - -// Http2ServerResponse.writeHead should override previous headers - -test('Http2ServerResponse.writeHead overrides previous headers', (done) => { - const server = h2.createServer(); - server.listen(0, () => { - const port = server.address().port; - server.once('request', (request, response) => { - response.setHeader('foo-bar', 'def456'); - - // Override - const returnVal = response.writeHead(418, { 'foo-bar': 'abc123' }); - - expect(returnVal).toBe(response); - - expect(() => { response.writeHead(300); }).toThrow(expect.objectContaining({ - code: 'ERR_HTTP2_HEADERS_SENT' - })); - - response.on('finish', () => { - server.close(); - process.nextTick(() => { - // The stream is invalid at this point, - // and this line verifies this does not throw. - response.writeHead(300); - done(); - }); - }); - response.end(); - }); - - const url = `http://localhost:${port}`; - const client = h2.connect(url, () => { - const headers = { - ':path': '/', - ':method': 'GET', - ':scheme': 'http', - ':authority': `localhost:${port}` - }; - const request = client.request(headers); - request.on('response', (headers) => { - expect(headers['foo-bar']).toBe('abc123'); - expect(headers[':status']).toBe(418); - }); - request.on('end', () => { - client.close(); - }); - request.end(); - request.resume(); - }); - }); -}); - -// Skip the test if crypto is not available -if (!process.versions.openssl) { - test.skip('missing crypto', () => {}); -} - -//<#END_FILE: test-http2-compat-serverresponse-writehead.js diff --git a/test/js/node/test/parallel/http2-compat-socket-destroy-delayed.test.js b/test/js/node/test/parallel/http2-compat-socket-destroy-delayed.test.js deleted file mode 100644 index 10e6afe2bc..0000000000 --- a/test/js/node/test/parallel/http2-compat-socket-destroy-delayed.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-http2-compat-socket-destroy-delayed.js -//#SHA1: c7b5b8b5de4667a89e0e261e36098f617d411ed2 -//----------------- -"use strict"; - -const http2 = require("http2"); - -const { HTTP2_HEADER_PATH, HTTP2_HEADER_METHOD } = http2.constants; - -// Skip the test if crypto is not available -if (!process.versions.openssl) { - test.skip("missing crypto", () => {}); -} else { - test("HTTP/2 socket destroy delayed", done => { - const app = http2.createServer((req, res) => { - res.end("hello"); - setImmediate(() => req.socket?.destroy()); - }); - - app.listen(0, () => { - const session = http2.connect(`http://localhost:${app.address().port}`); - const request = session.request({ - [HTTP2_HEADER_PATH]: "/", - [HTTP2_HEADER_METHOD]: "get", - }); - request.once("response", (headers, flags) => { - let data = ""; - request.on("data", chunk => { - data += chunk; - }); - request.on("end", () => { - expect(data).toBe("hello"); - session.close(); - app.close(); - done(); - }); - }); - request.end(); - }); - }); -} - -// This tests verifies that calling `req.socket.destroy()` via -// setImmediate does not crash. -// Fixes https://github.com/nodejs/node/issues/22855. - -//<#END_FILE: test-http2-compat-socket-destroy-delayed.js diff --git a/test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js b/test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js deleted file mode 100644 index 0ab3a588a3..0000000000 --- a/test/js/node/test/parallel/http2-compat-write-early-hints-invalid-argument-type.test.js +++ /dev/null @@ -1,72 +0,0 @@ -//#FILE: test-http2-compat-write-early-hints-invalid-argument-type.js -//#SHA1: 8ae2eba59668a38b039a100d3ad26f88e54be806 -//----------------- -"use strict"; - -const http2 = require("node:http2"); -const util = require("node:util"); -const debug = util.debuglog("test"); - -const testResBody = "response content"; - -// Check if crypto is available -let hasCrypto = false; -try { - require("crypto"); - hasCrypto = true; -} catch (err) { - // crypto not available -} - -(hasCrypto ? describe : describe.skip)("HTTP2 compat writeEarlyHints invalid argument type", () => { - let server; - let client; - - beforeAll(done => { - server = http2.createServer(); - server.listen(0, () => { - done(); - }); - }); - - afterAll(() => { - if (client) { - client.close(); - } - server.close(); - }); - - test("should throw ERR_INVALID_ARG_TYPE for invalid object value", done => { - server.on("request", (req, res) => { - debug("Server sending early hints..."); - expect(() => { - res.writeEarlyHints("this should not be here"); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - debug("Server sending full response..."); - res.end(testResBody); - }); - - client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request(); - - debug("Client sending request..."); - - req.on("headers", () => { - done(new Error("Should not receive headers")); - }); - - req.on("response", () => { - done(); - }); - - req.end(); - }); -}); - -//<#END_FILE: test-http2-compat-write-early-hints-invalid-argument-type.js diff --git a/test/js/node/test/parallel/http2-compat-write-early-hints.test.js b/test/js/node/test/parallel/http2-compat-write-early-hints.test.js deleted file mode 100644 index c3d8fb4e15..0000000000 --- a/test/js/node/test/parallel/http2-compat-write-early-hints.test.js +++ /dev/null @@ -1,146 +0,0 @@ -//#FILE: test-http2-compat-write-early-hints.js -//#SHA1: 0ed18263958421cde07c37b8ec353005b7477499 -//----------------- -'use strict'; - -const http2 = require('node:http2'); -const util = require('node:util'); -const debug = util.debuglog('test'); - -const testResBody = 'response content'; - -describe('HTTP/2 Early Hints', () => { - test('Happy flow - string argument', async () => { - const server = http2.createServer(); - - server.on('request', (req, res) => { - debug('Server sending early hints...'); - res.writeEarlyHints({ - link: '; rel=preload; as=style' - }); - - debug('Server sending full response...'); - res.end(testResBody); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - const client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request(); - - debug('Client sending request...'); - - await new Promise(resolve => { - req.on('headers', (headers) => { - expect(headers).toBeDefined(); - expect(headers[':status']).toBe(103); - expect(headers.link).toBe('; rel=preload; as=style'); - }); - - req.on('response', (headers) => { - expect(headers[':status']).toBe(200); - }); - - let data = ''; - req.on('data', (d) => data += d); - - req.on('end', () => { - debug('Got full response.'); - expect(data).toBe(testResBody); - client.close(); - server.close(resolve); - }); - }); - }); - - test('Happy flow - array argument', async () => { - const server = http2.createServer(); - - server.on('request', (req, res) => { - debug('Server sending early hints...'); - res.writeEarlyHints({ - link: [ - '; rel=preload; as=style', - '; rel=preload; as=script', - ] - }); - - debug('Server sending full response...'); - res.end(testResBody); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - const client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request(); - - debug('Client sending request...'); - - await new Promise(resolve => { - req.on('headers', (headers) => { - expect(headers).toBeDefined(); - expect(headers[':status']).toBe(103); - expect(headers.link).toBe( - '; rel=preload; as=style, ; rel=preload; as=script' - ); - }); - - req.on('response', (headers) => { - expect(headers[':status']).toBe(200); - }); - - let data = ''; - req.on('data', (d) => data += d); - - req.on('end', () => { - debug('Got full response.'); - expect(data).toBe(testResBody); - client.close(); - server.close(resolve); - }); - }); - }); - - test('Happy flow - empty array', async () => { - const server = http2.createServer(); - - server.on('request', (req, res) => { - debug('Server sending early hints...'); - res.writeEarlyHints({ - link: [] - }); - - debug('Server sending full response...'); - res.end(testResBody); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - const client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request(); - - debug('Client sending request...'); - - await new Promise(resolve => { - const headersListener = jest.fn(); - req.on('headers', headersListener); - - req.on('response', (headers) => { - expect(headers[':status']).toBe(200); - expect(headersListener).not.toHaveBeenCalled(); - }); - - let data = ''; - req.on('data', (d) => data += d); - - req.on('end', () => { - debug('Got full response.'); - expect(data).toBe(testResBody); - client.close(); - server.close(resolve); - }); - }); - }); -}); - -//<#END_FILE: test-http2-compat-write-early-hints.js diff --git a/test/js/node/test/parallel/http2-compat-write-head-destroyed.test.js b/test/js/node/test/parallel/http2-compat-write-head-destroyed.test.js deleted file mode 100644 index 601f47928e..0000000000 --- a/test/js/node/test/parallel/http2-compat-write-head-destroyed.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-http2-compat-write-head-destroyed.js -//#SHA1: 29f693f49912d4621c1a19ab7412b1b318d55d8e -//----------------- -"use strict"; - -const http2 = require("http2"); - -let server; -let port; - -beforeAll(done => { - if (!process.versions.openssl) { - done(); - return; - } - - server = http2.createServer((req, res) => { - // Destroy the stream first - req.stream.destroy(); - - res.writeHead(200); - res.write("hello "); - res.end("world"); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterAll(() => { - if (server) { - server.close(); - } -}); - -test("writeHead, write and end do not crash in compatibility mode", done => { - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } - - const client = http2.connect(`http://localhost:${port}`); - - const req = client.request(); - - req.on("response", () => { - done.fail("Should not receive response"); - }); - - req.on("close", () => { - client.close(); - done(); - }); - - req.resume(); -}); - -//<#END_FILE: test-http2-compat-write-head-destroyed.js diff --git a/test/js/node/test/parallel/http2-connect-tls-with-delay.test.js b/test/js/node/test/parallel/http2-connect-tls-with-delay.test.js deleted file mode 100644 index 1161272cab..0000000000 --- a/test/js/node/test/parallel/http2-connect-tls-with-delay.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-http2-connect-tls-with-delay.js -//#SHA1: 8c5489e025ec14c2cc53788b27fde11a11990e42 -//----------------- -"use strict"; - -const http2 = require("http2"); -const tls = require("tls"); -const fs = require("fs"); -const path = require("path"); - -const serverOptions = { - key: fs.readFileSync(path.join(__dirname, "..", "fixtures", "keys", "agent1-key.pem")), - cert: fs.readFileSync(path.join(__dirname, "..", "fixtures", "keys", "agent1-cert.pem")), -}; - -let server; - -beforeAll(done => { - server = http2.createSecureServer(serverOptions, (req, res) => { - res.end(); - }); - - server.listen(0, "127.0.0.1", done); -}); - -afterAll(() => { - server.close(); -}); - -test("HTTP/2 connect with TLS and delay", done => { - const options = { - ALPNProtocols: ["h2"], - host: "127.0.0.1", - servername: "localhost", - port: server.address().port, - rejectUnauthorized: false, - }; - - const socket = tls.connect(options, async () => { - socket.once("readable", () => { - const client = http2.connect("https://localhost:" + server.address().port, { - ...options, - createConnection: () => socket, - }); - - client.once("remoteSettings", () => { - const req = client.request({ - ":path": "/", - }); - req.on("data", () => req.resume()); - req.on("end", () => { - client.close(); - req.close(); - done(); - }); - req.end(); - }); - }); - }); -}); - -//<#END_FILE: test-http2-connect-tls-with-delay.js diff --git a/test/js/node/test/parallel/http2-cookies.test.js b/test/js/node/test/parallel/http2-cookies.test.js deleted file mode 100644 index c906992d71..0000000000 --- a/test/js/node/test/parallel/http2-cookies.test.js +++ /dev/null @@ -1,71 +0,0 @@ -//#FILE: test-http2-cookies.js -//#SHA1: 91bdbacba9eb8ebd9dddd43327aa2271dc00c271 -//----------------- -'use strict'; - -const h2 = require('http2'); - -const hasCrypto = (() => { - try { - require('crypto'); - return true; - } catch (err) { - return false; - } -})(); - -if (!hasCrypto) { - test.skip('missing crypto', () => {}); -} else { - test('HTTP/2 cookies', async () => { - const server = h2.createServer(); - - const setCookie = [ - 'a=b', - 'c=d; Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly', - 'e=f', - ]; - - server.on('stream', (stream, headers) => { - expect(typeof headers.abc).toBe('string'); - expect(headers.abc).toBe('1, 2, 3'); - expect(typeof headers.cookie).toBe('string'); - expect(headers.cookie).toBe('a=b; c=d; e=f'); - - stream.respond({ - 'content-type': 'text/html', - ':status': 200, - 'set-cookie': setCookie - }); - - stream.end('hello world'); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - const client = h2.connect(`http://localhost:${server.address().port}`); - - const req = client.request({ - ':path': '/', - 'abc': [1, 2, 3], - 'cookie': ['a=b', 'c=d', 'e=f'], - }); - - await new Promise((resolve, reject) => { - req.on('response', (headers) => { - expect(Array.isArray(headers['set-cookie'])).toBe(true); - expect(headers['set-cookie']).toEqual(setCookie); - }); - - req.on('end', resolve); - req.on('error', reject); - req.end(); - req.resume(); - }); - - server.close(); - client.close(); - }); -} - -//<#END_FILE: test-http2-cookies.js diff --git a/test/js/node/test/parallel/http2-createwritereq.test.js b/test/js/node/test/parallel/http2-createwritereq.test.js deleted file mode 100644 index 2c768f880a..0000000000 --- a/test/js/node/test/parallel/http2-createwritereq.test.js +++ /dev/null @@ -1,88 +0,0 @@ -//#FILE: test-http2-createwritereq.js -//#SHA1: 8b0d2399fb8a26ce6cc76b9f338be37a7ff08ca5 -//----------------- -"use strict"; - -const http2 = require("http2"); - -// Mock the gc function -global.gc = jest.fn(); - -const testString = "a\u00A1\u0100\uD83D\uDE00"; - -const encodings = { - // "buffer": "utf8", - "ascii": "ascii", - // "latin1": "latin1", - // "binary": "latin1", - // "utf8": "utf8", - // "utf-8": "utf8", - // "ucs2": "ucs2", - // "ucs-2": "ucs2", - // "utf16le": "ucs2", - // "utf-16le": "ucs2", - // "UTF8": "utf8", -}; - -describe("http2 createWriteReq", () => { - let server; - let serverAddress; - - beforeAll(done => { - server = http2.createServer((req, res) => { - const testEncoding = encodings[req.url.slice(1)]; - - req.on("data", chunk => { - // console.error(testEncoding, chunk, Buffer.from(testString, testEncoding)); - expect(Buffer.from(testString, testEncoding).equals(chunk)).toBe(true); - }); - - req.on("end", () => res.end()); - }); - - server.listen(0, () => { - serverAddress = `http://localhost:${server.address().port}`; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - Object.keys(encodings).forEach(writeEncoding => { - test(`should handle ${writeEncoding} encoding`, done => { - const client = http2.connect(serverAddress); - const req = client.request({ - ":path": `/${writeEncoding}`, - ":method": "POST", - }); - - expect(req._writableState.decodeStrings).toBe(false); - - req.write( - writeEncoding !== "buffer" ? testString : Buffer.from(testString), - writeEncoding !== "buffer" ? writeEncoding : undefined, - ); - req.resume(); - - req.on("end", () => { - client.close(); - done(); - }); - - // Ref: https://github.com/nodejs/node/issues/17840 - const origDestroy = req.destroy; - req.destroy = function (...args) { - // Schedule a garbage collection event at the end of the current - // MakeCallback() run. - process.nextTick(global.gc); - return origDestroy.call(this, ...args); - }; - - req.end(); - }); - }); -}); - -//<#END_FILE: test-http2-createwritereq.test.js diff --git a/test/js/node/test/parallel/http2-destroy-after-write.test.js b/test/js/node/test/parallel/http2-destroy-after-write.test.js deleted file mode 100644 index c3303887ac..0000000000 --- a/test/js/node/test/parallel/http2-destroy-after-write.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-http2-destroy-after-write.js -//#SHA1: 193688397df0b891b9286ff825ca873935d30e04 -//----------------- -"use strict"; - -const http2 = require("http2"); - -let server; -let port; - -beforeAll(done => { - server = http2.createServer(); - - server.on("session", session => { - session.on("stream", stream => { - stream.on("end", function () { - this.respond({ - ":status": 200, - }); - this.write("foo"); - this.destroy(); - }); - stream.resume(); - }); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterAll(() => { - server.close(); -}); - -test("http2 destroy after write", done => { - const client = http2.connect(`http://localhost:${port}`); - const stream = client.request({ ":method": "POST" }); - - stream.on("response", headers => { - expect(headers[":status"]).toBe(200); - }); - - stream.on("close", () => { - client.close(); - done(); - }); - - stream.resume(); - stream.end(); -}); - -//<#END_FILE: test-http2-destroy-after-write.js diff --git a/test/js/node/test/parallel/http2-dont-override.test.js b/test/js/node/test/parallel/http2-dont-override.test.js deleted file mode 100644 index ea465da5a3..0000000000 --- a/test/js/node/test/parallel/http2-dont-override.test.js +++ /dev/null @@ -1,58 +0,0 @@ -//#FILE: test-http2-dont-override.js -//#SHA1: d295b8c4823cc34c03773eb08bf0393fca541694 -//----------------- -'use strict'; - -const http2 = require('http2'); - -// Skip test if crypto is not available -if (!process.versions.openssl) { - test.skip('missing crypto', () => {}); -} else { - test('http2 should not override options', (done) => { - const options = {}; - - const server = http2.createServer(options); - - // Options are defaulted but the options are not modified - expect(Object.keys(options)).toEqual([]); - - server.on('stream', (stream) => { - const headers = {}; - const options = {}; - stream.respond(headers, options); - - // The headers are defaulted but the original object is not modified - expect(Object.keys(headers)).toEqual([]); - - // Options are defaulted but the original object is not modified - expect(Object.keys(options)).toEqual([]); - - stream.end(); - }); - - server.listen(0, () => { - const client = http2.connect(`http://localhost:${server.address().port}`); - - const headers = {}; - const options = {}; - - const req = client.request(headers, options); - - // The headers are defaulted but the original object is not modified - expect(Object.keys(headers)).toEqual([]); - - // Options are defaulted but the original object is not modified - expect(Object.keys(options)).toEqual([]); - - req.resume(); - req.on('end', () => { - server.close(); - client.close(); - done(); - }); - }); - }); -} - -//<#END_FILE: test-http2-dont-override.js diff --git a/test/js/node/test/parallel/http2-forget-closed-streams.test.js b/test/js/node/test/parallel/http2-forget-closed-streams.test.js deleted file mode 100644 index b21280b343..0000000000 --- a/test/js/node/test/parallel/http2-forget-closed-streams.test.js +++ /dev/null @@ -1,85 +0,0 @@ -//#FILE: test-http2-forget-closed-streams.js -//#SHA1: 2f917924c763cc220e68ce2b829c63dc03a836ab -//----------------- -"use strict"; -const http2 = require("http2"); - -// Skip test if crypto is not available -const hasCrypto = (() => { - try { - require("crypto"); - return true; - } catch (err) { - return false; - } -})(); - -(hasCrypto ? describe : describe.skip)("http2 forget closed streams", () => { - let server; - - beforeAll(() => { - server = http2.createServer({ maxSessionMemory: 1 }); - - server.on("session", session => { - session.on("stream", stream => { - stream.on("end", () => { - stream.respond( - { - ":status": 200, - }, - { - endStream: true, - }, - ); - }); - stream.resume(); - }); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("should handle 10000 requests without memory issues", done => { - const listenPromise = new Promise(resolve => { - server.listen(0, () => { - resolve(server.address().port); - }); - }); - - listenPromise.then(port => { - const client = http2.connect(`http://localhost:${port}`); - - function makeRequest(i) { - return new Promise(resolve => { - const stream = client.request({ ":method": "POST" }); - stream.on("response", headers => { - expect(headers[":status"]).toBe(200); - stream.on("close", resolve); - }); - stream.end(); - }); - } - - async function runRequests() { - for (let i = 0; i < 10000; i++) { - await makeRequest(i); - } - client.close(); - } - - runRequests() - .then(() => { - // If we've reached here without errors, the test has passed - expect(true).toBe(true); - done(); - }) - .catch(err => { - done(err); - }); - }); - }, 30000); // Increase timeout to 30 seconds -}); - -//<#END_FILE: test-http2-forget-closed-streams.js diff --git a/test/js/node/test/parallel/http2-goaway-opaquedata.test.js b/test/js/node/test/parallel/http2-goaway-opaquedata.test.js deleted file mode 100644 index 7de3263266..0000000000 --- a/test/js/node/test/parallel/http2-goaway-opaquedata.test.js +++ /dev/null @@ -1,58 +0,0 @@ -//#FILE: test-http2-goaway-opaquedata.js -//#SHA1: 5ad5b6a64cb0e7419753dcd88d59692eb97973ed -//----------------- -'use strict'; - -const http2 = require('http2'); - -let server; -let serverPort; - -beforeAll((done) => { - server = http2.createServer(); - server.listen(0, () => { - serverPort = server.address().port; - done(); - }); -}); - -afterAll((done) => { - server.close(done); -}); - -test('HTTP/2 GOAWAY with opaque data', (done) => { - const data = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]); - let session; - - server.once('stream', (stream) => { - session = stream.session; - session.on('close', () => { - expect(true).toBe(true); // Session closed - }); - session.goaway(0, 0, data); - stream.respond(); - stream.end(); - }); - - const client = http2.connect(`http://localhost:${serverPort}`); - client.once('goaway', (code, lastStreamID, buf) => { - expect(code).toBe(0); - expect(lastStreamID).toBe(1); - expect(buf).toEqual(data); - session.close(); - client.close(); - done(); - }); - - const req = client.request(); - req.resume(); - req.on('end', () => { - expect(true).toBe(true); // Request ended - }); - req.on('close', () => { - expect(true).toBe(true); // Request closed - }); - req.end(); -}); - -//<#END_FILE: test-http2-goaway-opaquedata.js diff --git a/test/js/node/test/parallel/http2-large-write-close.test.js b/test/js/node/test/parallel/http2-large-write-close.test.js deleted file mode 100644 index f50a3b581f..0000000000 --- a/test/js/node/test/parallel/http2-large-write-close.test.js +++ /dev/null @@ -1,70 +0,0 @@ -//#FILE: test-http2-large-write-close.js -//#SHA1: 66ad4345c0888700887c23af455fdd9ff49721d9 -//----------------- -"use strict"; -const fixtures = require("../common/fixtures"); -const http2 = require("http2"); - -const { beforeEach, afterEach, test, expect } = require("bun:test"); -const { isWindows } = require("harness"); -const content = Buffer.alloc(1e5, 0x44); - -let server; -let port; - -beforeEach(done => { - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } - - server = http2.createSecureServer({ - key: fixtures.readKey("agent1-key.pem"), - cert: fixtures.readKey("agent1-cert.pem"), - }); - - server.on("stream", stream => { - stream.respond({ - "Content-Type": "application/octet-stream", - "Content-Length": content.byteLength.toString() * 2, - "Vary": "Accept-Encoding", - }); - - stream.write(content); - stream.write(content); - stream.end(); - stream.close(); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterEach(() => { - server.close(); -}); - -test.todoIf(isWindows)( - "HTTP/2 large write and close", - done => { - const client = http2.connect(`https://localhost:${port}`, { rejectUnauthorized: false }); - - const req = client.request({ ":path": "/" }); - req.end(); - - let receivedBufferLength = 0; - req.on("data", buf => { - receivedBufferLength += buf.byteLength; - }); - - req.on("close", () => { - expect(receivedBufferLength).toBe(content.byteLength * 2); - client.close(); - done(); - }); - }, - 5000, -); - -//<#END_FILE: test-http2-large-write-close.js diff --git a/test/js/node/test/parallel/http2-large-write-destroy.test.js b/test/js/node/test/parallel/http2-large-write-destroy.test.js deleted file mode 100644 index b9d7679961..0000000000 --- a/test/js/node/test/parallel/http2-large-write-destroy.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-http2-large-write-destroy.js -//#SHA1: 0c76344570b21b6ed78f12185ddefde59a9b2914 -//----------------- -'use strict'; - -const http2 = require('http2'); - -const content = Buffer.alloc(60000, 0x44); - -let server; - -afterEach(() => { - if (server) { - server.close(); - } -}); - -test('HTTP/2 large write and destroy', (done) => { - server = http2.createServer(); - - server.on('stream', (stream) => { - stream.respond({ - 'Content-Type': 'application/octet-stream', - 'Content-Length': (content.length.toString() * 2), - 'Vary': 'Accept-Encoding' - }, { waitForTrailers: true }); - - stream.write(content); - stream.destroy(); - }); - - server.listen(0, () => { - const client = http2.connect(`http://localhost:${server.address().port}`); - - const req = client.request({ ':path': '/' }); - req.end(); - req.resume(); // Otherwise close won't be emitted if there's pending data. - - req.on('close', () => { - client.close(); - done(); - }); - - req.on('error', (err) => { - // We expect an error due to the stream being destroyed - expect(err.code).toBe('ECONNRESET'); - client.close(); - done(); - }); - }); -}); - -//<#END_FILE: test-http2-large-write-destroy.js diff --git a/test/js/node/test/parallel/http2-large-writes-session-memory-leak.test.js b/test/js/node/test/parallel/http2-large-writes-session-memory-leak.test.js deleted file mode 100644 index e718f292ff..0000000000 --- a/test/js/node/test/parallel/http2-large-writes-session-memory-leak.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-http2-large-writes-session-memory-leak.js -//#SHA1: 8f39b92e38fac58143b4d50534b2f6a171fb1a1b -//----------------- -'use strict'; - -const http2 = require('http2'); - -test.skip('HTTP/2 large writes should not cause session memory leak', () => { - console.log('This test is skipped because it requires specific Node.js internals and fixtures.'); - console.log('Original test description:'); - console.log('Regression test for https://github.com/nodejs/node/issues/29223.'); - console.log('There was a "leak" in the accounting of session memory leading'); - console.log('to streams eventually failing with NGHTTP2_ENHANCE_YOUR_CALM.'); - - // Original test logic preserved for reference: - /* - const server = http2.createSecureServer({ - key: fixtures.readKey('agent2-key.pem'), - cert: fixtures.readKey('agent2-cert.pem'), - }); - - const data200k = 'a'.repeat(200 * 1024); - server.on('stream', (stream) => { - stream.write(data200k); - stream.end(); - }); - - server.listen(0, () => { - const client = http2.connect(`https://localhost:${server.address().port}`, { - ca: fixtures.readKey('agent2-cert.pem'), - servername: 'agent2', - maxSessionMemory: 1 - }); - - let streamsLeft = 50; - function newStream() { - const stream = client.request({ ':path': '/' }); - stream.on('data', () => { }); - stream.on('close', () => { - if (streamsLeft-- > 0) { - newStream(); - } else { - client.destroy(); - server.close(); - } - }); - } - newStream(); - }); - */ -}); - -//<#END_FILE: test-http2-large-writes-session-memory-leak.js diff --git a/test/js/node/test/parallel/http2-many-writes-and-destroy.test.js b/test/js/node/test/parallel/http2-many-writes-and-destroy.test.js deleted file mode 100644 index 503419d879..0000000000 --- a/test/js/node/test/parallel/http2-many-writes-and-destroy.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-http2-many-writes-and-destroy.js -//#SHA1: b4a66fa27d761038f79e0eb3562f521724887db4 -//----------------- -"use strict"; - -const http2 = require("http2"); - -// Skip the test if crypto is not available -let hasCrypto; -try { - require("crypto"); - hasCrypto = true; -} catch (err) { - hasCrypto = false; -} - -(hasCrypto ? describe : describe.skip)("HTTP/2 many writes and destroy", () => { - let server; - let url; - - beforeAll(done => { - server = http2.createServer((req, res) => { - req.pipe(res); - }); - - server.listen(0, () => { - url = `http://localhost:${server.address().port}`; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("should handle many writes and destroy", done => { - const client = http2.connect(url); - const req = client.request({ ":method": "POST" }); - - for (let i = 0; i < 4000; i++) { - req.write(Buffer.alloc(6)); - } - - req.on("close", () => { - console.log("(req onclose)"); - client.close(); - done(); - }); - - req.once("data", () => { - req.destroy(); - }); - }); -}); - -//<#END_FILE: test-http2-many-writes-and-destroy.js diff --git a/test/js/node/test/parallel/http2-misc-util.test.js b/test/js/node/test/parallel/http2-misc-util.test.js deleted file mode 100644 index 0af25ec564..0000000000 --- a/test/js/node/test/parallel/http2-misc-util.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-http2-misc-util.js -//#SHA1: 0fa21e185faeff6ee5b1d703d9a998bf98d6b229 -//----------------- -const http2 = require("http2"); - -describe("HTTP/2 Misc Util", () => { - test("HTTP2 constants are defined", () => { - expect(http2.constants).toBeDefined(); - expect(http2.constants.NGHTTP2_SESSION_SERVER).toBe(0); - expect(http2.constants.NGHTTP2_SESSION_CLIENT).toBe(1); - }); - // make it not fail after re-enabling push - test.todo("HTTP2 default settings are within valid ranges", () => { - const defaultSettings = http2.getDefaultSettings(); - expect(defaultSettings).toBeDefined(); - expect(defaultSettings.headerTableSize).toBeGreaterThanOrEqual(0); - expect(defaultSettings.enablePush).toBe(true); // push is disabled because is not implemented yet - expect(defaultSettings.initialWindowSize).toBeGreaterThanOrEqual(0); - expect(defaultSettings.maxFrameSize).toBeGreaterThanOrEqual(16384); - expect(defaultSettings.maxConcurrentStreams).toBeGreaterThanOrEqual(0); - expect(defaultSettings.maxHeaderListSize).toBeGreaterThanOrEqual(0); - }); - - test("HTTP2 getPackedSettings and getUnpackedSettings", () => { - const settings = { - headerTableSize: 4096, - enablePush: true, - initialWindowSize: 65535, - maxFrameSize: 16384, - }; - const packed = http2.getPackedSettings(settings); - expect(packed).toBeInstanceOf(Buffer); - - const unpacked = http2.getUnpackedSettings(packed); - expect(unpacked).toEqual(expect.objectContaining(settings)); - }); -}); - -//<#END_FILE: test-http2-misc-util.js diff --git a/test/js/node/test/parallel/http2-multistream-destroy-on-read-tls.test.js b/test/js/node/test/parallel/http2-multistream-destroy-on-read-tls.test.js deleted file mode 100644 index 5e27b6472c..0000000000 --- a/test/js/node/test/parallel/http2-multistream-destroy-on-read-tls.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-http2-multistream-destroy-on-read-tls.js -//#SHA1: bf3869a9f8884210710d41c0fb1f54d2112e9af5 -//----------------- -"use strict"; -const http2 = require("http2"); - -describe("HTTP2 multistream destroy on read", () => { - let server; - const filenames = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]; - - beforeAll(done => { - server = http2.createServer(); - - server.on("stream", stream => { - function write() { - stream.write("a".repeat(10240)); - stream.once("drain", write); - } - write(); - }); - - server.listen(0, done); - }); - - afterAll(() => { - if (server) { - server.close(); - } else { - done(); - } - }); - - test("should handle multiple stream destructions", done => { - const client = http2.connect(`http://localhost:${server.address().port}`); - - let destroyed = 0; - for (const entry of filenames) { - const stream = client.request({ - ":path": `/${entry}`, - }); - stream.once("data", () => { - stream.destroy(); - - if (++destroyed === filenames.length) { - client.close(); - done(); - } - }); - } - }); -}); - -//<#END_FILE: test-http2-multistream-destroy-on-read-tls.js diff --git a/test/js/node/test/parallel/http2-no-wanttrailers-listener.test.js b/test/js/node/test/parallel/http2-no-wanttrailers-listener.test.js deleted file mode 100644 index b7aa239af9..0000000000 --- a/test/js/node/test/parallel/http2-no-wanttrailers-listener.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-http2-no-wanttrailers-listener.js -//#SHA1: a5297c0a1ed58f7d2d0a13bc4eaaa198a7ab160e -//----------------- -"use strict"; - -const h2 = require("http2"); - -let server; -let client; - -beforeAll(() => { - // Check if crypto is available - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } -}); - -afterEach(() => { - if (client) { - client.close(); - } - if (server) { - server.close(); - } -}); - -test("HTTP/2 server should not hang without wantTrailers listener", done => { - server = h2.createServer(); - - server.on("stream", (stream, headers, flags) => { - stream.respond(undefined, { waitForTrailers: true }); - stream.end("ok"); - }); - - server.listen(0, () => { - const port = server.address().port; - client = h2.connect(`http://localhost:${port}`); - const req = client.request(); - req.resume(); - - req.on("trailers", () => { - throw new Error("Unexpected trailers event"); - }); - - req.on("close", () => { - done(); - }); - }); -}); - -//<#END_FILE: test-http2-no-wanttrailers-listener.js diff --git a/test/js/node/test/parallel/http2-options-server-response.test.js b/test/js/node/test/parallel/http2-options-server-response.test.js deleted file mode 100644 index 4ad8e33898..0000000000 --- a/test/js/node/test/parallel/http2-options-server-response.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-http2-options-server-response.js -//#SHA1: 66736f340efdbdf2e20a79a3dffe75f499e65d89 -//----------------- -'use strict'; - -const h2 = require('http2'); - -class MyServerResponse extends h2.Http2ServerResponse { - status(code) { - return this.writeHead(code, { 'Content-Type': 'text/plain' }); - } -} - -let server; -let client; - -beforeAll(() => { - if (!process.versions.openssl) { - return test.skip('missing crypto'); - } -}); - -afterAll(() => { - if (server) server.close(); - if (client) client.destroy(); -}); - -test('http2 server with custom ServerResponse', (done) => { - server = h2.createServer({ - Http2ServerResponse: MyServerResponse - }, (req, res) => { - res.status(200); - res.end(); - }); - - server.listen(0, () => { - const port = server.address().port; - client = h2.connect(`http://localhost:${port}`); - const req = client.request({ ':path': '/' }); - - const responseHandler = jest.fn(); - req.on('response', responseHandler); - - const endHandler = jest.fn(() => { - expect(responseHandler).toHaveBeenCalled(); - done(); - }); - - req.resume(); - req.on('end', endHandler); - }); -}); - -//<#END_FILE: test-http2-options-server-response.js diff --git a/test/js/node/test/parallel/http2-perf_hooks.test.js b/test/js/node/test/parallel/http2-perf_hooks.test.js deleted file mode 100644 index b45b8d48c7..0000000000 --- a/test/js/node/test/parallel/http2-perf_hooks.test.js +++ /dev/null @@ -1,124 +0,0 @@ -//#FILE: test-http2-perf_hooks.js -//#SHA1: a759a55527c8587bdf272da00c6597d93aa36da0 -//----------------- -'use strict'; - -const h2 = require('http2'); -const { PerformanceObserver } = require('perf_hooks'); - -let server; -let client; - -beforeAll(() => { - if (!process.versions.openssl) { - return test.skip('missing crypto'); - } -}); - -afterEach(() => { - if (client) client.close(); - if (server) server.close(); -}); - -test('HTTP/2 performance hooks', (done) => { - const obs = new PerformanceObserver((items) => { - const entry = items.getEntries()[0]; - expect(entry.entryType).toBe('http2'); - expect(typeof entry.startTime).toBe('number'); - expect(typeof entry.duration).toBe('number'); - - switch (entry.name) { - case 'Http2Session': - expect(typeof entry.pingRTT).toBe('number'); - expect(typeof entry.streamAverageDuration).toBe('number'); - expect(typeof entry.streamCount).toBe('number'); - expect(typeof entry.framesReceived).toBe('number'); - expect(typeof entry.framesSent).toBe('number'); - expect(typeof entry.bytesWritten).toBe('number'); - expect(typeof entry.bytesRead).toBe('number'); - expect(typeof entry.maxConcurrentStreams).toBe('number'); - expect(typeof entry.detail.pingRTT).toBe('number'); - expect(typeof entry.detail.streamAverageDuration).toBe('number'); - expect(typeof entry.detail.streamCount).toBe('number'); - expect(typeof entry.detail.framesReceived).toBe('number'); - expect(typeof entry.detail.framesSent).toBe('number'); - expect(typeof entry.detail.bytesWritten).toBe('number'); - expect(typeof entry.detail.bytesRead).toBe('number'); - expect(typeof entry.detail.maxConcurrentStreams).toBe('number'); - switch (entry.type) { - case 'server': - expect(entry.detail.streamCount).toBe(1); - expect(entry.detail.framesReceived).toBeGreaterThanOrEqual(3); - break; - case 'client': - expect(entry.detail.streamCount).toBe(1); - expect(entry.detail.framesReceived).toBe(7); - break; - default: - fail('invalid Http2Session type'); - } - break; - case 'Http2Stream': - expect(typeof entry.timeToFirstByte).toBe('number'); - expect(typeof entry.timeToFirstByteSent).toBe('number'); - expect(typeof entry.timeToFirstHeader).toBe('number'); - expect(typeof entry.bytesWritten).toBe('number'); - expect(typeof entry.bytesRead).toBe('number'); - expect(typeof entry.detail.timeToFirstByte).toBe('number'); - expect(typeof entry.detail.timeToFirstByteSent).toBe('number'); - expect(typeof entry.detail.timeToFirstHeader).toBe('number'); - expect(typeof entry.detail.bytesWritten).toBe('number'); - expect(typeof entry.detail.bytesRead).toBe('number'); - break; - default: - fail('invalid entry name'); - } - }); - - obs.observe({ type: 'http2' }); - - const body = '

this is some data

'; - - server = h2.createServer(); - - server.on('stream', (stream, headers, flags) => { - expect(headers[':scheme']).toBe('http'); - expect(headers[':authority']).toBeTruthy(); - expect(headers[':method']).toBe('GET'); - expect(flags).toBe(5); - stream.respond({ - 'content-type': 'text/html', - ':status': 200 - }); - stream.write(body.slice(0, 20)); - stream.end(body.slice(20)); - }); - - server.on('session', (session) => { - session.ping(jest.fn()); - }); - - server.listen(0, () => { - client = h2.connect(`http://localhost:${server.address().port}`); - - client.on('connect', () => { - client.ping(jest.fn()); - }); - - const req = client.request(); - - req.on('response', jest.fn()); - - let data = ''; - req.setEncoding('utf8'); - req.on('data', (d) => data += d); - req.on('end', () => { - expect(body).toBe(data); - }); - req.on('close', () => { - obs.disconnect(); - done(); - }); - }); -}); -//<#END_FILE: test-http2-perf_hooks.js diff --git a/test/js/node/test/parallel/http2-pipe.test.js b/test/js/node/test/parallel/http2-pipe.test.js deleted file mode 100644 index 0f852cef61..0000000000 --- a/test/js/node/test/parallel/http2-pipe.test.js +++ /dev/null @@ -1,83 +0,0 @@ -//#FILE: test-http2-pipe.js -//#SHA1: bb970b612d495580b8c216a1b202037e5eb0721e -//----------------- -"use strict"; - -import { afterEach, beforeEach, test, expect, describe, mock } from "bun:test"; - -const http2 = require("http2"); -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -// Skip the test if crypto is not available -let hasCrypto; -try { - require("crypto"); - hasCrypto = true; -} catch (err) { - hasCrypto = false; -} - -const testIfCrypto = hasCrypto ? test : test.skip; - -describe("HTTP2 Pipe", () => { - let server; - let serverPort; - let tmpdir; - const fixturesDir = path.join(__dirname, "..", "fixtures"); - const loc = path.join(fixturesDir, "person-large.jpg"); - let fn; - - beforeEach(() => { - tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "http2-test-")); - fn = path.join(tmpdir, "http2-url-tests.js"); - }); - - afterEach(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); - }); - - testIfCrypto("Piping should work as expected with createWriteStream", done => { - server = http2.createServer(); - - server.on("stream", stream => { - const dest = stream.pipe(fs.createWriteStream(fn)); - - dest.on("finish", () => { - expect(fs.readFileSync(loc).length).toBe(fs.readFileSync(fn).length); - }); - stream.respond(); - stream.end(); - }); - - server.listen(0, () => { - serverPort = server.address().port; - const client = http2.connect(`http://localhost:${serverPort}`); - - const req = client.request({ ":method": "POST" }); - - const responseHandler = mock(() => {}); - req.on("response", responseHandler); - req.resume(); - - req.on("close", () => { - expect(responseHandler).toHaveBeenCalled(); - server.close(); - client.close(); - done(); - }); - - const str = fs.createReadStream(loc); - const strEndHandler = mock(() => {}); - str.on("end", strEndHandler); - str.pipe(req); - - req.on("finish", () => { - expect(strEndHandler).toHaveBeenCalled(); - }); - }); - }); -}); - -//<#END_FILE: test-http2-pipe.js diff --git a/test/js/node/test/parallel/http2-priority-cycle-.test.js b/test/js/node/test/parallel/http2-priority-cycle-.test.js deleted file mode 100644 index 61bab1f9cd..0000000000 --- a/test/js/node/test/parallel/http2-priority-cycle-.test.js +++ /dev/null @@ -1,84 +0,0 @@ -//#FILE: test-http2-priority-cycle-.js -//#SHA1: 32c70d0d1e4be42834f071fa3d9bb529aa4ea1c1 -//----------------- -'use strict'; - -const http2 = require('http2'); - -const largeBuffer = Buffer.alloc(1e4); - -class Countdown { - constructor(count, done) { - this.count = count; - this.done = done; - } - - dec() { - this.count--; - if (this.count === 0) this.done(); - } -} - -test('HTTP/2 priority cycle', (done) => { - const server = http2.createServer(); - - server.on('stream', (stream) => { - stream.respond(); - setImmediate(() => { - stream.end(largeBuffer); - }); - }); - - server.on('session', (session) => { - session.on('priority', (id, parent, weight, exclusive) => { - expect(weight).toBe(16); - expect(exclusive).toBe(false); - switch (id) { - case 1: - expect(parent).toBe(5); - break; - case 3: - expect(parent).toBe(1); - break; - case 5: - expect(parent).toBe(3); - break; - default: - fail('should not happen'); - } - }); - }); - - server.listen(0, () => { - const client = http2.connect(`http://localhost:${server.address().port}`); - - const countdown = new Countdown(3, () => { - client.close(); - server.close(); - done(); - }); - - { - const req = client.request(); - req.priority({ parent: 5 }); - req.resume(); - req.on('close', () => countdown.dec()); - } - - { - const req = client.request(); - req.priority({ parent: 1 }); - req.resume(); - req.on('close', () => countdown.dec()); - } - - { - const req = client.request(); - req.priority({ parent: 3 }); - req.resume(); - req.on('close', () => countdown.dec()); - } - }); -}); - -//<#END_FILE: test-http2-priority-cycle-.js diff --git a/test/js/node/test/parallel/http2-removed-header-stays-removed.test.js b/test/js/node/test/parallel/http2-removed-header-stays-removed.test.js deleted file mode 100644 index a996aabc1c..0000000000 --- a/test/js/node/test/parallel/http2-removed-header-stays-removed.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-http2-removed-header-stays-removed.js -//#SHA1: f8bc3d1be9927b83a02492d9cb44c803c337e3c1 -//----------------- -"use strict"; -const http2 = require("http2"); - -let server; -let port; - -beforeAll(done => { - server = http2.createServer((request, response) => { - response.setHeader("date", "snacks o clock"); - response.end(); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterAll(() => { - server.close(); -}); - -test("HTTP/2 removed header stays removed", done => { - const session = http2.connect(`http://localhost:${port}`); - const req = session.request(); - - req.on("response", (headers, flags) => { - expect(headers.date).toBe("snacks o clock"); - }); - - req.on("end", () => { - session.close(); - done(); - }); -}); - -// Conditional skip if crypto is not available -try { - require("crypto"); -} catch (err) { - test.skip("missing crypto", () => {}); -} - -//<#END_FILE: test-http2-removed-header-stays-removed.js diff --git a/test/js/node/test/parallel/http2-request-remove-connect-listener.test.js b/test/js/node/test/parallel/http2-request-remove-connect-listener.test.js deleted file mode 100644 index 85bcbf502c..0000000000 --- a/test/js/node/test/parallel/http2-request-remove-connect-listener.test.js +++ /dev/null @@ -1,50 +0,0 @@ -//#FILE: test-http2-request-remove-connect-listener.js -//#SHA1: 28cbc334f4429a878522e1e78eac56d13fb0c916 -//----------------- -'use strict'; - -const http2 = require('http2'); - -// Skip the test if crypto is not available -let cryptoAvailable = true; -try { - require('crypto'); -} catch (err) { - cryptoAvailable = false; -} - -test('HTTP/2 request removes connect listener', (done) => { - if (!cryptoAvailable) { - console.log('Skipping test: missing crypto'); - return done(); - } - - const server = http2.createServer(); - const streamHandler = jest.fn((stream) => { - stream.respond(); - stream.end(); - }); - server.on('stream', streamHandler); - - server.listen(0, () => { - const client = http2.connect(`http://localhost:${server.address().port}`); - const connectHandler = jest.fn(); - client.once('connect', connectHandler); - - const req = client.request(); - - req.on('response', () => { - expect(client.listenerCount('connect')).toBe(0); - expect(streamHandler).toHaveBeenCalled(); - expect(connectHandler).toHaveBeenCalled(); - }); - - req.on('close', () => { - server.close(); - client.close(); - done(); - }); - }); -}); - -//<#END_FILE: test-http2-request-remove-connect-listener.js diff --git a/test/js/node/test/parallel/http2-request-response-proto.test.js b/test/js/node/test/parallel/http2-request-response-proto.test.js deleted file mode 100644 index 5ed889e51a..0000000000 --- a/test/js/node/test/parallel/http2-request-response-proto.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-http2-request-response-proto.js -//#SHA1: ffffac0d4d11b6a77ddbfce366c206de8db99446 -//----------------- -'use strict'; - -const hasCrypto = (() => { - try { - require('crypto'); - return true; - } catch (err) { - return false; - } -})(); - -let http2; - -if (!hasCrypto) { - test.skip('missing crypto', () => {}); -} else { - http2 = require('http2'); - - const { - Http2ServerRequest, - Http2ServerResponse, - } = http2; - - describe('Http2ServerRequest and Http2ServerResponse prototypes', () => { - test('protoRequest should be instance of Http2ServerRequest', () => { - const protoRequest = { __proto__: Http2ServerRequest.prototype }; - expect(protoRequest instanceof Http2ServerRequest).toBe(true); - }); - - test('protoResponse should be instance of Http2ServerResponse', () => { - const protoResponse = { __proto__: Http2ServerResponse.prototype }; - expect(protoResponse instanceof Http2ServerResponse).toBe(true); - }); - }); -} - -//<#END_FILE: test-http2-request-response-proto.js diff --git a/test/js/node/test/parallel/http2-res-corked.test.js b/test/js/node/test/parallel/http2-res-corked.test.js deleted file mode 100644 index 0da21d6cc4..0000000000 --- a/test/js/node/test/parallel/http2-res-corked.test.js +++ /dev/null @@ -1,79 +0,0 @@ -//#FILE: test-http2-res-corked.js -//#SHA1: a6c5da9f22eae611c043c6d177d63c0eaca6e02e -//----------------- -"use strict"; -const http2 = require("http2"); - -// Skip the test if crypto is not available -let hasCrypto = false; -try { - require("crypto"); - hasCrypto = true; -} catch (err) { - // crypto not available -} - -(hasCrypto ? describe : describe.skip)("Http2ServerResponse#[writableCorked,cork,uncork]", () => { - let server; - let client; - let corksLeft = 0; - - beforeAll(done => { - server = http2.createServer((req, res) => { - expect(res.writableCorked).toBe(corksLeft); - res.write(Buffer.from("1".repeat(1024))); - res.cork(); - corksLeft++; - expect(res.writableCorked).toBe(corksLeft); - res.write(Buffer.from("1".repeat(1024))); - res.cork(); - corksLeft++; - expect(res.writableCorked).toBe(corksLeft); - res.write(Buffer.from("1".repeat(1024))); - res.cork(); - corksLeft++; - expect(res.writableCorked).toBe(corksLeft); - res.write(Buffer.from("1".repeat(1024))); - res.cork(); - corksLeft++; - expect(res.writableCorked).toBe(corksLeft); - res.uncork(); - corksLeft--; - expect(res.writableCorked).toBe(corksLeft); - res.uncork(); - corksLeft--; - expect(res.writableCorked).toBe(corksLeft); - res.uncork(); - corksLeft--; - expect(res.writableCorked).toBe(corksLeft); - res.uncork(); - corksLeft--; - expect(res.writableCorked).toBe(corksLeft); - res.end(); - }); - - server.listen(0, () => { - const port = server.address().port; - client = http2.connect(`http://localhost:${port}`); - done(); - }); - }); - - afterAll(() => { - client.close(); - server.close(); - }); - - test("cork and uncork operations", done => { - const req = client.request(); - let dataCallCount = 0; - req.on("data", () => { - dataCallCount++; - }); - req.on("end", () => { - expect(dataCallCount).toBe(2); - done(); - }); - }); -}); -//<#END_FILE: test-http2-res-corked.js diff --git a/test/js/node/test/parallel/http2-respond-file-compat.test.js b/test/js/node/test/parallel/http2-respond-file-compat.test.js deleted file mode 100644 index 7d05c6e8f0..0000000000 --- a/test/js/node/test/parallel/http2-respond-file-compat.test.js +++ /dev/null @@ -1,73 +0,0 @@ -//#FILE: test-http2-respond-file-compat.js -//#SHA1: fac1eb9c2e4f7a75e9c7605abc64fc9c6e6f7f14 -//----------------- -'use strict'; - -const http2 = require('http2'); -const fs = require('fs'); -const path = require('path'); - -const hasCrypto = (() => { - try { - require('crypto'); - return true; - } catch (err) { - return false; - } -})(); - -const fname = path.join(__dirname, '..', 'fixtures', 'elipses.txt'); - -describe('HTTP/2 respondWithFile', () => { - let server; - - beforeAll(() => { - if (!hasCrypto) { - return; - } - // Ensure the file exists - if (!fs.existsSync(fname)) { - fs.writeFileSync(fname, '...'); - } - }); - - afterAll(() => { - if (server) { - server.close(); - } - }); - - test('should respond with file', (done) => { - if (!hasCrypto) { - done(); - return; - } - - const requestHandler = jest.fn((request, response) => { - response.stream.respondWithFile(fname); - }); - - server = http2.createServer(requestHandler); - server.listen(0, () => { - const client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request(); - - const responseHandler = jest.fn(); - req.on('response', responseHandler); - - req.on('end', () => { - expect(requestHandler).toHaveBeenCalled(); - expect(responseHandler).toHaveBeenCalled(); - client.close(); - server.close(() => { - done(); - }); - }); - - req.end(); - req.resume(); - }); - }); -}); - -//<#END_FILE: test-http2-respond-file-compat.js diff --git a/test/js/node/test/parallel/http2-respond-file-error-dir.test.js b/test/js/node/test/parallel/http2-respond-file-error-dir.test.js deleted file mode 100644 index b3b9e7a592..0000000000 --- a/test/js/node/test/parallel/http2-respond-file-error-dir.test.js +++ /dev/null @@ -1,70 +0,0 @@ -//#FILE: test-http2-respond-file-error-dir.js -//#SHA1: 61f98e2ad2c69302fe84383e1dec1118edaa70e1 -//----------------- -'use strict'; - -const http2 = require('http2'); -const path = require('path'); - -let server; -let client; - -beforeAll(() => { - if (!process.versions.openssl) { - test.skip('missing crypto'); - } -}); - -afterEach(() => { - if (client) { - client.close(); - } - if (server) { - server.close(); - } -}); - -test('http2 respondWithFile with directory should fail', (done) => { - server = http2.createServer(); - server.on('stream', (stream) => { - stream.respondWithFile(process.cwd(), { - 'content-type': 'text/plain' - }, { - onError(err) { - expect(err).toMatchObject({ - code: 'ERR_HTTP2_SEND_FILE', - name: 'Error', - message: 'Directories cannot be sent' - }); - - stream.respond({ ':status': 404 }); - stream.end(); - }, - statCheck: jest.fn() - }); - }); - - server.listen(0, () => { - const port = server.address().port; - client = http2.connect(`http://localhost:${port}`); - const req = client.request(); - - const responseHandler = jest.fn((headers) => { - expect(headers[':status']).toBe(404); - }); - - const dataHandler = jest.fn(); - const endHandler = jest.fn(() => { - expect(responseHandler).toHaveBeenCalled(); - expect(dataHandler).not.toHaveBeenCalled(); - done(); - }); - - req.on('response', responseHandler); - req.on('data', dataHandler); - req.on('end', endHandler); - req.end(); - }); -}); - -//<#END_FILE: test-http2-respond-file-error-dir.js diff --git a/test/js/node/test/parallel/http2-respond-file-filehandle.test.js b/test/js/node/test/parallel/http2-respond-file-filehandle.test.js deleted file mode 100644 index 297f53d852..0000000000 --- a/test/js/node/test/parallel/http2-respond-file-filehandle.test.js +++ /dev/null @@ -1,73 +0,0 @@ -//#FILE: test-http2-respond-file-filehandle.js -//#SHA1: c80cf9e1a4a879a73d275616e0604e56ac7756bb -//----------------- -'use strict'; - -const http2 = require('http2'); -const fs = require('fs'); -const path = require('path'); - -const { - HTTP2_HEADER_CONTENT_TYPE, - HTTP2_HEADER_CONTENT_LENGTH -} = http2.constants; - -const fixturesPath = path.join(__dirname, '..', 'fixtures'); -const fname = path.join(fixturesPath, 'elipses.txt'); - -test('http2 respond with file handle', async () => { - // Skip test if running in Bun - if (process.versions.bun) { - return; - } - - const data = await fs.promises.readFile(fname); - const stat = await fs.promises.stat(fname); - - const fileHandle = await fs.promises.open(fname, 'r'); - - const server = http2.createServer(); - server.on('stream', (stream) => { - stream.respondWithFD(fileHandle, { - [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain', - [HTTP2_HEADER_CONTENT_LENGTH]: stat.size, - }); - }); - - const serverCloseHandler = jest.fn(); - server.on('close', serverCloseHandler); - - await new Promise(resolve => server.listen(0, resolve)); - - const client = http2.connect(`http://localhost:${server.address().port}`); - const req = client.request(); - - const responseHandler = jest.fn((headers) => { - expect(headers[HTTP2_HEADER_CONTENT_TYPE]).toBe('text/plain'); - expect(Number(headers[HTTP2_HEADER_CONTENT_LENGTH])).toBe(data.length); - }); - req.on('response', responseHandler); - - req.setEncoding('utf8'); - let check = ''; - req.on('data', (chunk) => check += chunk); - - await new Promise(resolve => { - req.on('end', () => { - expect(check).toBe(data.toString('utf8')); - client.close(); - server.close(); - resolve(); - }); - req.end(); - }); - - await new Promise(resolve => server.on('close', resolve)); - - expect(responseHandler).toHaveBeenCalled(); - expect(serverCloseHandler).toHaveBeenCalled(); - - await fileHandle.close(); -}); - -//<#END_FILE: test-http2-respond-file-filehandle.js diff --git a/test/js/node/test/parallel/http2-sent-headers.test.js b/test/js/node/test/parallel/http2-sent-headers.test.js deleted file mode 100644 index 21a5c36ad1..0000000000 --- a/test/js/node/test/parallel/http2-sent-headers.test.js +++ /dev/null @@ -1,74 +0,0 @@ -//#FILE: test-http2-sent-headers.js -//#SHA1: cbc2db06925ef62397fd91d70872b787363cd96c -//----------------- -"use strict"; - -const h2 = require("http2"); - -const hasCrypto = (() => { - try { - require("crypto"); - return true; - } catch (err) { - return false; - } -})(); - -(hasCrypto ? describe : describe.skip)("http2 sent headers", () => { - let server; - let client; - let port; - - beforeAll(done => { - server = h2.createServer(); - - server.on("stream", stream => { - stream.additionalHeaders({ ":status": 102 }); - expect(stream.sentInfoHeaders[0][":status"]).toBe(102); - - stream.respond({ abc: "xyz" }, { waitForTrailers: true }); - stream.on("wantTrailers", () => { - stream.sendTrailers({ xyz: "abc" }); - }); - expect(stream.sentHeaders.abc).toBe("xyz"); - expect(stream.sentHeaders[":status"]).toBe(200); - expect(stream.sentHeaders.date).toBeDefined(); - stream.end(); - stream.on("close", () => { - expect(stream.sentTrailers.xyz).toBe("abc"); - }); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("client request headers", done => { - client = h2.connect(`http://localhost:${port}`); - const req = client.request(); - - req.on("headers", (headers, flags) => { - expect(headers[":status"]).toBe(102); - expect(typeof flags).toBe("number"); - }); - - expect(req.sentHeaders[":method"]).toBe("GET"); - expect(req.sentHeaders[":authority"]).toBe(`localhost:${port}`); - expect(req.sentHeaders[":scheme"]).toBe("http"); - expect(req.sentHeaders[":path"]).toBe("/"); - - req.resume(); - req.on("close", () => { - client.close(); - done(); - }); - }); -}); - -//<#END_FILE: test-http2-sent-headers.js diff --git a/test/js/node/test/parallel/http2-server-async-dispose.test.js b/test/js/node/test/parallel/http2-server-async-dispose.test.js deleted file mode 100644 index bdf5282129..0000000000 --- a/test/js/node/test/parallel/http2-server-async-dispose.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-http2-server-async-dispose.js -//#SHA1: 3f26a183d15534b5f04c61836e718ede1726834f -//----------------- -'use strict'; - -const http2 = require('http2'); - -// Check if crypto is available -let hasCrypto = false; -try { - require('crypto'); - hasCrypto = true; -} catch (err) { - // crypto is not available -} - -(hasCrypto ? test : test.skip)('http2 server async close', (done) => { - const server = http2.createServer(); - - const closeHandler = jest.fn(); - server.on('close', closeHandler); - - server.listen(0, () => { - // Use the close method instead of Symbol.asyncDispose - server.close(() => { - expect(closeHandler).toHaveBeenCalled(); - done(); - }); - }); -}, 10000); // Increase timeout to 10 seconds - -//<#END_FILE: test-http2-server-async-dispose.js diff --git a/test/js/node/test/parallel/http2-server-push-stream-errors.test.js b/test/js/node/test/parallel/http2-server-push-stream-errors.test.js deleted file mode 100644 index 7c9aa34bcc..0000000000 --- a/test/js/node/test/parallel/http2-server-push-stream-errors.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-http2-server-push-stream-errors.js -//#SHA1: e0c43917d2cc3edee06a7d89fb1cbeff9c81fb08 -//----------------- -'use strict'; - -test.skip('HTTP/2 server push stream errors', () => { - console.log('This test is skipped because it relies on Node.js internals that are not accessible in Jest.'); -}); - -// Original test code (commented out for reference) -/* -const http2 = require('http2'); -const { internalBinding } = require('internal/test/binding'); -const { - constants, - Http2Stream, - nghttp2ErrorString -} = internalBinding('http2'); -const { NghttpError } = require('internal/http2/util'); - -// ... rest of the original test code ... -*/ - -//<#END_FILE: test-http2-server-push-stream-errors.test.js diff --git a/test/js/node/test/parallel/http2-server-rst-before-respond.test.js b/test/js/node/test/parallel/http2-server-rst-before-respond.test.js deleted file mode 100644 index 9280ea17eb..0000000000 --- a/test/js/node/test/parallel/http2-server-rst-before-respond.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-http2-server-rst-before-respond.js -//#SHA1: 67d0d7c2fdd32d5eb050bf8473a767dbf24d158a -//----------------- -'use strict'; - -const h2 = require('http2'); - -let server; -let client; - -beforeEach(() => { - server = h2.createServer(); -}); - -afterEach(() => { - if (server) server.close(); - if (client) client.close(); -}); - -test('HTTP/2 server reset stream before respond', (done) => { - if (!process.versions.openssl) { - test.skip('missing crypto'); - return; - } - - const onStream = jest.fn((stream, headers, flags) => { - stream.close(); - - expect(() => { - stream.additionalHeaders({ - ':status': 123, - 'abc': 123 - }); - }).toThrow(expect.objectContaining({ - code: 'ERR_HTTP2_INVALID_STREAM' - })); - }); - - server.on('stream', onStream); - - server.listen(0, () => { - const port = server.address().port; - client = h2.connect(`http://localhost:${port}`); - const req = client.request(); - - const onHeaders = jest.fn(); - req.on('headers', onHeaders); - - const onResponse = jest.fn(); - req.on('response', onResponse); - - req.on('close', () => { - expect(req.rstCode).toBe(h2.constants.NGHTTP2_NO_ERROR); - expect(onStream).toHaveBeenCalledTimes(1); - expect(onHeaders).not.toHaveBeenCalled(); - expect(onResponse).not.toHaveBeenCalled(); - done(); - }); - }); -}); - -//<#END_FILE: test-http2-server-rst-before-respond.js diff --git a/test/js/node/test/parallel/http2-server-set-header.test.js b/test/js/node/test/parallel/http2-server-set-header.test.js deleted file mode 100644 index 8f63781248..0000000000 --- a/test/js/node/test/parallel/http2-server-set-header.test.js +++ /dev/null @@ -1,77 +0,0 @@ -//#FILE: test-http2-server-set-header.js -//#SHA1: d4ba0042eab7b4ef4927f3aa3e344f4b5e04f935 -//----------------- -'use strict'; - -const http2 = require('http2'); - -const body = '

this is some data

'; - -let server; -let port; - -beforeAll((done) => { - server = http2.createServer((req, res) => { - res.setHeader('foobar', 'baz'); - res.setHeader('X-POWERED-BY', 'node-test'); - res.setHeader('connection', 'connection-test'); - res.end(body); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterAll((done) => { - server.close(done); -}); - -test('HTTP/2 server set header', (done) => { - const client = http2.connect(`http://localhost:${port}`); - const headers = { ':path': '/' }; - const req = client.request(headers); - req.setEncoding('utf8'); - - req.on('response', (headers) => { - expect(headers.foobar).toBe('baz'); - expect(headers['x-powered-by']).toBe('node-test'); - // The 'connection' header should not be present in HTTP/2 - expect(headers.connection).toBeUndefined(); - }); - - let data = ''; - req.on('data', (d) => data += d); - req.on('end', () => { - expect(data).toBe(body); - client.close(); - done(); - }); - req.end(); -}); - -test('Setting connection header should not throw', () => { - const res = { - setHeader: jest.fn() - }; - - expect(() => { - res.setHeader('connection', 'test'); - }).not.toThrow(); - - expect(res.setHeader).toHaveBeenCalledWith('connection', 'test'); -}); - -test('Server should not emit error', (done) => { - const errorListener = jest.fn(); - server.on('error', errorListener); - - setTimeout(() => { - expect(errorListener).not.toHaveBeenCalled(); - server.removeListener('error', errorListener); - done(); - }, 100); -}); - -//<#END_FILE: test-http2-server-set-header.js diff --git a/test/js/node/test/parallel/http2-session-timeout.test.js b/test/js/node/test/parallel/http2-session-timeout.test.js deleted file mode 100644 index 08b4a07c34..0000000000 --- a/test/js/node/test/parallel/http2-session-timeout.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-http2-session-timeout.js -//#SHA1: 8a03d5dc642f9d07faac7b4a44caa0e02b625339 -//----------------- -'use strict'; - -const http2 = require('http2'); -const { hrtime } = process; -const NS_PER_MS = 1_000_000n; - -let requests = 0; - -test('HTTP/2 session timeout', (done) => { - const server = http2.createServer(); - server.timeout = 0n; - - server.on('request', (req, res) => res.end()); - server.on('timeout', () => { - throw new Error(`Timeout after ${requests} request(s)`); - }); - - server.listen(0, () => { - const port = server.address().port; - const url = `http://localhost:${port}`; - const client = http2.connect(url); - let startTime = hrtime.bigint(); - - function makeReq() { - const request = client.request({ - ':path': '/foobar', - ':method': 'GET', - ':scheme': 'http', - ':authority': `localhost:${port}`, - }); - request.resume(); - request.end(); - - requests += 1; - - request.on('end', () => { - const diff = hrtime.bigint() - startTime; - const milliseconds = diff / NS_PER_MS; - if (server.timeout === 0n) { - server.timeout = milliseconds * 2n; - startTime = hrtime.bigint(); - makeReq(); - } else if (milliseconds < server.timeout * 2n) { - makeReq(); - } else { - server.close(); - client.close(); - expect(requests).toBeGreaterThan(1); - done(); - } - }); - } - - makeReq(); - }); -}); - -//<#END_FILE: test-http2-session-timeout.js diff --git a/test/js/node/test/parallel/http2-socket-proxy.test.js b/test/js/node/test/parallel/http2-socket-proxy.test.js deleted file mode 100644 index 3e6122df11..0000000000 --- a/test/js/node/test/parallel/http2-socket-proxy.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-http2-socket-proxy.js -//#SHA1: c5158fe06db7a7572dc5f7a52c23f019d16fb8ce -//----------------- -'use strict'; - -const h2 = require('http2'); -const net = require('net'); - -let server; -let port; - -beforeAll(async () => { - server = h2.createServer(); - await new Promise(resolve => server.listen(0, () => { - port = server.address().port; - resolve(); - })); -}); - -afterAll(async () => { - await new Promise(resolve => server.close(resolve)); -}); - -describe('HTTP/2 Socket Proxy', () => { - test('Socket behavior on Http2Session', async () => { - expect.assertions(5); - - server.once('stream', (stream, headers) => { - const socket = stream.session.socket; - const session = stream.session; - - expect(socket).toBeInstanceOf(net.Socket); - expect(socket.writable).toBe(true); - expect(socket.readable).toBe(true); - expect(typeof socket.address()).toBe('object'); - - // Test that setting a property on socket affects the session - const fn = jest.fn(); - socket.setTimeout = fn; - expect(session.setTimeout).toBe(fn); - - stream.respond({ ':status': 200 }); - stream.end('OK'); - }); - - const client = h2.connect(`http://localhost:${port}`); - const req = client.request({ ':path': '/' }); - - await new Promise(resolve => { - req.on('response', () => { - req.on('data', () => {}); - req.on('end', () => { - client.close(); - resolve(); - }); - }); - }); - }, 10000); // Increase timeout to 10 seconds -}); - -//<#END_FILE: test-http2-socket-proxy.js diff --git a/test/js/node/test/parallel/http2-status-code.test.js b/test/js/node/test/parallel/http2-status-code.test.js deleted file mode 100644 index ec02531975..0000000000 --- a/test/js/node/test/parallel/http2-status-code.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-http2-status-code.js -//#SHA1: 53911ac66c46f57bca1d56cdaf76e46d61c957d8 -//----------------- -"use strict"; - -const http2 = require("http2"); - -const codes = [200, 202, 300, 400, 404, 451, 500]; -let server; -let client; - -beforeAll(done => { - server = http2.createServer(); - - let testIndex = 0; - server.on("stream", stream => { - const status = codes[testIndex++]; - stream.respond({ ":status": status }, { endStream: true }); - }); - - server.listen(0, () => { - done(); - }); -}); - -afterAll(() => { - client.close(); - server.close(); -}); - -test("HTTP/2 status codes", done => { - const port = server.address().port; - client = http2.connect(`http://localhost:${port}`); - - let remaining = codes.length; - function maybeClose() { - if (--remaining === 0) { - done(); - } - } - - function doTest(expected) { - return new Promise(resolve => { - const req = client.request(); - req.on("response", headers => { - expect(headers[":status"]).toBe(expected); - }); - req.resume(); - req.on("end", () => { - maybeClose(); - resolve(); - }); - }); - } - - Promise.all(codes.map(doTest)).then(() => { - // All tests completed - }); -}); - -//<#END_FILE: test-http2-status-code.js diff --git a/test/js/node/test/parallel/http2-tls-disconnect.test.js b/test/js/node/test/parallel/http2-tls-disconnect.test.js deleted file mode 100644 index f26b325906..0000000000 --- a/test/js/node/test/parallel/http2-tls-disconnect.test.js +++ /dev/null @@ -1,17 +0,0 @@ -//#FILE: test-http2-tls-disconnect.js -//#SHA1: 0673265638d2f040031cb9fbc7a1fefda23ba0e1 -//----------------- -'use strict'; - -test.skip('http2 TLS disconnect', () => { - console.log('This test is skipped because:'); - console.log('1. It requires specific SSL certificate files (agent8-key.pem and agent8-cert.pem) which are not available in the current test environment.'); - console.log('2. It relies on an external tool (h2load) which may not be installed on all systems.'); - console.log('3. The test involves creating a real HTTPS server and spawning a child process, which is not ideal for unit testing.'); - console.log('To properly test this functionality, consider:'); - console.log('- Mocking the SSL certificates and http2 server creation'); - console.log('- Replacing the h2load functionality with a simulated load using pure JavaScript'); - console.log('- Focusing on testing the specific behavior (TLS disconnect handling) without relying on external tools'); -}); - -//<#END_FILE: test-http2-tls-disconnect.js diff --git a/test/js/node/test/parallel/http2-trailers.test.js b/test/js/node/test/parallel/http2-trailers.test.js deleted file mode 100644 index 63666b1966..0000000000 --- a/test/js/node/test/parallel/http2-trailers.test.js +++ /dev/null @@ -1,71 +0,0 @@ -//#FILE: test-http2-trailers.js -//#SHA1: 1e3d42d5008cf87fa8bf557b38f4fd00b4dbd712 -//----------------- -'use strict'; - -const h2 = require('http2'); - -const body = - '

this is some data

'; -const trailerKey = 'test-trailer'; -const trailerValue = 'testing'; - -let server; - -beforeAll(() => { - server = h2.createServer(); - server.on('stream', onStream); -}); - -afterAll(() => { - server.close(); -}); - -function onStream(stream, headers, flags) { - stream.on('trailers', (headers) => { - expect(headers[trailerKey]).toBe(trailerValue); - stream.end(body); - }); - stream.respond({ - 'content-type': 'text/html', - ':status': 200 - }, { waitForTrailers: true }); - stream.on('wantTrailers', () => { - stream.sendTrailers({ [trailerKey]: trailerValue }); - expect(() => stream.sendTrailers({})).toThrow(expect.objectContaining({ - code: 'ERR_HTTP2_TRAILERS_ALREADY_SENT', - name: 'Error' - })); - }); - - expect(() => stream.sendTrailers({})).toThrow(expect.objectContaining({ - code: 'ERR_HTTP2_TRAILERS_NOT_READY', - name: 'Error' - })); -} - -test('HTTP/2 trailers', (done) => { - server.listen(0, () => { - const client = h2.connect(`http://localhost:${server.address().port}`); - const req = client.request({ ':path': '/', ':method': 'POST' }, - { waitForTrailers: true }); - req.on('wantTrailers', () => { - req.sendTrailers({ [trailerKey]: trailerValue }); - }); - req.on('data', () => {}); - req.on('trailers', (headers) => { - expect(headers[trailerKey]).toBe(trailerValue); - }); - req.on('close', () => { - expect(() => req.sendTrailers({})).toThrow(expect.objectContaining({ - code: 'ERR_HTTP2_INVALID_STREAM', - name: 'Error' - })); - client.close(); - done(); - }); - req.end('data'); - }); -}); - -//<#END_FILE: test-http2-trailers.js diff --git a/test/js/node/test/parallel/http2-unbound-socket-proxy.test.js b/test/js/node/test/parallel/http2-unbound-socket-proxy.test.js deleted file mode 100644 index c4c0635240..0000000000 --- a/test/js/node/test/parallel/http2-unbound-socket-proxy.test.js +++ /dev/null @@ -1,73 +0,0 @@ -//#FILE: test-http2-unbound-socket-proxy.js -//#SHA1: bcb8a31b2f29926a8e8d9a3bb5f23d09bfa5e805 -//----------------- -'use strict'; - -const http2 = require('http2'); -const net = require('net'); - -let server; -let client; - -beforeAll(() => { - if (!process.versions.openssl) { - return test.skip('missing crypto'); - } -}); - -afterEach(() => { - if (client) { - client.close(); - } - if (server) { - server.close(); - } -}); - -test('http2 unbound socket proxy', (done) => { - server = http2.createServer(); - const streamHandler = jest.fn((stream) => { - stream.respond(); - stream.end('ok'); - }); - server.on('stream', streamHandler); - - server.listen(0, () => { - client = http2.connect(`http://localhost:${server.address().port}`); - const socket = client.socket; - const req = client.request(); - req.resume(); - req.on('close', () => { - client.close(); - server.close(); - - // Tests to make sure accessing the socket proxy fails with an - // informative error. - setImmediate(() => { - expect(() => { - socket.example; // eslint-disable-line no-unused-expressions - }).toThrow(expect.objectContaining({ - code: 'ERR_HTTP2_SOCKET_UNBOUND' - })); - - expect(() => { - socket.example = 1; - }).toThrow(expect.objectContaining({ - code: 'ERR_HTTP2_SOCKET_UNBOUND' - })); - - expect(() => { - // eslint-disable-next-line no-unused-expressions - socket instanceof net.Socket; - }).toThrow(expect.objectContaining({ - code: 'ERR_HTTP2_SOCKET_UNBOUND' - })); - - expect(streamHandler).toHaveBeenCalled(); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-http2-unbound-socket-proxy.js diff --git a/test/js/node/test/parallel/http2-util-assert-valid-pseudoheader.test.js b/test/js/node/test/parallel/http2-util-assert-valid-pseudoheader.test.js deleted file mode 100644 index 42f0ccf3c2..0000000000 --- a/test/js/node/test/parallel/http2-util-assert-valid-pseudoheader.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-http2-util-assert-valid-pseudoheader.js -//#SHA1: 765cdbf9a64c432ef1706fb7b24ab35d926cda3b -//----------------- -'use strict'; - -let mapToHeaders; - -beforeAll(() => { - try { - // Try to require the internal module - ({ mapToHeaders } = require('internal/http2/util')); - } catch (error) { - // If the internal module is not available, mock it - mapToHeaders = jest.fn((headers) => { - const validPseudoHeaders = [':status', ':path', ':authority', ':scheme', ':method']; - for (const key in headers) { - if (key.startsWith(':') && !validPseudoHeaders.includes(key)) { - throw new TypeError(`"${key}" is an invalid pseudoheader or is used incorrectly`); - } - } - }); - } -}); - -describe('HTTP/2 Util - assertValidPseudoHeader', () => { - test('should not throw for valid pseudo-headers', () => { - expect(() => mapToHeaders({ ':status': 'a' })).not.toThrow(); - expect(() => mapToHeaders({ ':path': 'a' })).not.toThrow(); - expect(() => mapToHeaders({ ':authority': 'a' })).not.toThrow(); - expect(() => mapToHeaders({ ':scheme': 'a' })).not.toThrow(); - expect(() => mapToHeaders({ ':method': 'a' })).not.toThrow(); - }); - - test('should throw for invalid pseudo-headers', () => { - expect(() => mapToHeaders({ ':foo': 'a' })).toThrow(expect.objectContaining({ - name: 'TypeError', - message: expect.stringContaining('is an invalid pseudoheader or is used incorrectly') - })); - }); -}); - -//<#END_FILE: test-http2-util-assert-valid-pseudoheader.js diff --git a/test/js/node/test/parallel/http2-util-update-options-buffer.test.js b/test/js/node/test/parallel/http2-util-update-options-buffer.test.js deleted file mode 100644 index d83855aa28..0000000000 --- a/test/js/node/test/parallel/http2-util-update-options-buffer.test.js +++ /dev/null @@ -1,11 +0,0 @@ -//#FILE: test-http2-util-update-options-buffer.js -//#SHA1: f1d75eaca8be74152cd7eafc114815b5d59d7f0c -//----------------- -'use strict'; - -test('Skip: HTTP/2 util update options buffer test', () => { - console.log('This test is skipped because it relies on Node.js internals that are not easily accessible in a Jest environment.'); - expect(true).toBe(true); -}); - -//<#END_FILE: test-http2-util-update-options-buffer.js diff --git a/test/js/node/test/parallel/http2-write-callbacks.test.js b/test/js/node/test/parallel/http2-write-callbacks.test.js deleted file mode 100644 index 2aa826a373..0000000000 --- a/test/js/node/test/parallel/http2-write-callbacks.test.js +++ /dev/null @@ -1,72 +0,0 @@ -//#FILE: test-http2-write-callbacks.js -//#SHA1: 4ad84acd162dcde6c2fbe344e6da2a3ec225edc1 -//----------------- -"use strict"; - -const http2 = require("http2"); - -// Mock for common.mustCall -const mustCall = fn => { - const wrappedFn = jest.fn(fn); - return wrappedFn; -}; - -describe("HTTP/2 write callbacks", () => { - let server; - let client; - let port; - - beforeAll(done => { - server = http2.createServer(); - server.listen(0, () => { - port = server.address().port; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("write callbacks are called", done => { - const serverWriteCallback = mustCall(() => {}); - const clientWriteCallback = mustCall(() => {}); - - server.once("stream", stream => { - stream.write("abc", serverWriteCallback); - stream.end("xyz"); - - let actual = ""; - stream.setEncoding("utf8"); - stream.on("data", chunk => (actual += chunk)); - stream.on("end", () => { - expect(actual).toBe("abcxyz"); - }); - }); - - client = http2.connect(`http://localhost:${port}`); - const req = client.request({ ":method": "POST" }); - - req.write("abc", clientWriteCallback); - req.end("xyz"); - - let actual = ""; - req.setEncoding("utf8"); - req.on("data", chunk => (actual += chunk)); - req.on("end", () => { - expect(actual).toBe("abcxyz"); - }); - - req.on("close", () => { - client.close(); - - // Check if callbacks were called - expect(serverWriteCallback).toHaveBeenCalled(); - expect(clientWriteCallback).toHaveBeenCalled(); - - done(); - }); - }); -}); - -//<#END_FILE: test-http2-write-callbacks.js diff --git a/test/js/node/test/parallel/http2-write-empty-string.test.js b/test/js/node/test/parallel/http2-write-empty-string.test.js deleted file mode 100644 index ca1e65b234..0000000000 --- a/test/js/node/test/parallel/http2-write-empty-string.test.js +++ /dev/null @@ -1,69 +0,0 @@ -//#FILE: test-http2-write-empty-string.js -//#SHA1: 59ba4a8a3c63aad827770d96f668922107ed2f2f -//----------------- -'use strict'; - -const http2 = require('http2'); - -// Skip the test if crypto is not available -let http2Server; -beforeAll(() => { - if (!process.versions.openssl) { - test.skip('missing crypto'); - } -}); - -afterAll(() => { - if (http2Server) { - http2Server.close(); - } -}); - -test('HTTP/2 server writes empty strings correctly', async () => { - http2Server = http2.createServer((request, response) => { - response.writeHead(200, { 'Content-Type': 'text/plain' }); - response.write('1\n'); - response.write(''); - response.write('2\n'); - response.write(''); - response.end('3\n'); - }); - - await new Promise(resolve => { - http2Server.listen(0, resolve); - }); - - const port = http2Server.address().port; - const client = http2.connect(`http://localhost:${port}`); - const headers = { ':path': '/' }; - - const responsePromise = new Promise((resolve, reject) => { - const req = client.request(headers); - - let res = ''; - req.setEncoding('ascii'); - - req.on('response', (headers) => { - expect(headers[':status']).toBe(200); - }); - - req.on('data', (chunk) => { - res += chunk; - }); - - req.on('end', () => { - resolve(res); - }); - - req.on('error', reject); - - req.end(); - }); - - const response = await responsePromise; - expect(response).toBe('1\n2\n3\n'); - - await new Promise(resolve => client.close(resolve)); -}); - -//<#END_FILE: test-http2-write-empty-string.js diff --git a/test/js/node/test/parallel/http2-zero-length-header.test.js b/test/js/node/test/parallel/http2-zero-length-header.test.js deleted file mode 100644 index aef1d62dbf..0000000000 --- a/test/js/node/test/parallel/http2-zero-length-header.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-http2-zero-length-header.js -//#SHA1: 65bd4ca954be7761c2876b26c6ac5d3f0e5c98e4 -//----------------- -"use strict"; -const http2 = require("http2"); - -// Skip test if crypto is not available -const hasCrypto = (() => { - try { - require("crypto"); - return true; - } catch (err) { - return false; - } -})(); - -(hasCrypto ? describe : describe.skip)("http2 zero length header", () => { - let server; - let port; - - beforeAll(async () => { - server = http2.createServer(); - await new Promise(resolve => server.listen(0, resolve)); - port = server.address().port; - }); - - afterAll(() => { - server.close(); - }); - - test("server receives correct headers", async () => { - const serverPromise = new Promise(resolve => { - server.once("stream", (stream, headers) => { - expect(headers).toEqual({ - ":scheme": "http", - ":authority": `localhost:${port}`, - ":method": "GET", - ":path": "/", - "bar": "", - "__proto__": null, - [http2.sensitiveHeaders]: [], - }); - stream.session.destroy(); - resolve(); - }); - }); - - const client = http2.connect(`http://localhost:${port}/`); - client.request({ ":path": "/", "": "foo", "bar": "" }).end(); - - await serverPromise; - client.close(); - }); -}); - -//<#END_FILE: test-http2-zero-length-header.js diff --git a/test/js/node/test/parallel/http2-zero-length-write.test.js b/test/js/node/test/parallel/http2-zero-length-write.test.js deleted file mode 100644 index dbd25616c5..0000000000 --- a/test/js/node/test/parallel/http2-zero-length-write.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-http2-zero-length-write.js -//#SHA1: a948a83af3675490313ff7b33a36d2c12cdd2837 -//----------------- -"use strict"; - -const http2 = require("http2"); -const { Readable } = require("stream"); - -function getSrc() { - const chunks = ["", "asdf", "", "foo", "", "bar", ""]; - return new Readable({ - read() { - const chunk = chunks.shift(); - if (chunk !== undefined) this.push(chunk); - else this.push(null); - }, - }); -} - -const expectedOutput = "asdffoobar"; - -let server; -let client; - -beforeAll(() => { - if (!process.versions.openssl) { - test.skip("missing crypto"); - } -}); - -afterEach(() => { - if (client) client.close(); - if (server) server.close(); -}); - -test("HTTP/2 zero length write", async () => { - return new Promise((resolve, reject) => { - server = http2.createServer(); - server.on("stream", stream => { - let actual = ""; - stream.respond(); - stream.resume(); - stream.setEncoding("utf8"); - stream.on("data", chunk => (actual += chunk)); - stream.on("end", () => { - getSrc().pipe(stream); - expect(actual).toBe(expectedOutput); - }); - }); - - server.listen(0, () => { - client = http2.connect(`http://localhost:${server.address().port}`); - let actual = ""; - const req = client.request({ ":method": "POST" }); - req.on("response", () => {}); - req.setEncoding("utf8"); - req.on("data", chunk => (actual += chunk)); - - req.on("end", () => { - expect(actual).toBe(expectedOutput); - resolve(); - }); - getSrc().pipe(req); - }); - }); -}, 10000); // Increase timeout to 10 seconds - -//<#END_FILE: test-http2-zero-length-write.js diff --git a/test/js/node/test/parallel/https-agent-constructor.test.js b/test/js/node/test/parallel/https-agent-constructor.test.js deleted file mode 100644 index dc3079eddd..0000000000 --- a/test/js/node/test/parallel/https-agent-constructor.test.js +++ /dev/null @@ -1,17 +0,0 @@ -//#FILE: test-https-agent-constructor.js -//#SHA1: 6b63dcb4d1a1a60f19fbb26cb555013821af5791 -//----------------- -"use strict"; - -if (!process.versions.openssl) { - test.skip("missing crypto"); -} - -const https = require("https"); - -test("https.Agent constructor", () => { - expect(new https.Agent()).toBeInstanceOf(https.Agent); - expect(https.Agent()).toBeInstanceOf(https.Agent); -}); - -//<#END_FILE: test-https-agent-constructor.js diff --git a/test/js/node/test/parallel/https-agent.test.js b/test/js/node/test/parallel/https-agent.test.js deleted file mode 100644 index 31b1e0ee25..0000000000 --- a/test/js/node/test/parallel/https-agent.test.js +++ /dev/null @@ -1,106 +0,0 @@ -//#FILE: test-https-agent.js -//#SHA1: 1348abc863ae99725dd893838c95b42c5120a052 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const https = require("https"); -const { readKey } = require("../common/fixtures"); - -const options = { - key: readKey("agent1-key.pem"), - cert: readKey("agent1-cert.pem"), -}; - -const N = 4; -const M = 4; - -let server; -let responses = 0; - -beforeAll(() => { - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } -}); - -beforeEach(() => { - return new Promise(resolve => { - server = https.createServer(options, (req, res) => { - res.writeHead(200); - res.end("hello world\n"); - }); - - server.listen(0, () => { - resolve(); - }); - }); -}); - -afterEach(() => { - return new Promise(resolve => { - server.close(() => { - resolve(); - }); - }); -}); - -test("HTTPS Agent handles multiple concurrent requests", async () => { - const makeRequests = i => { - return new Promise(resolve => { - setTimeout(() => { - const requests = Array.from( - { length: M }, - () => - new Promise(innerResolve => { - https - .get( - { - path: "/", - port: server.address().port, - rejectUnauthorized: false, - }, - function (res) { - res.resume(); - expect(res.statusCode).toBe(200); - responses++; - innerResolve(); - }, - ) - .on("error", e => { - throw e; - }); - }), - ); - Promise.all(requests).then(resolve); - }, i); - }); - }; - - const allRequests = Array.from({ length: N }, (_, i) => makeRequests(i)); - await Promise.all(allRequests); - - expect(responses).toBe(N * M); -}); - -//<#END_FILE: test-https-agent.js diff --git a/test/js/node/test/parallel/https-byteswritten.test.js b/test/js/node/test/parallel/https-byteswritten.test.js deleted file mode 100644 index c695fa7027..0000000000 --- a/test/js/node/test/parallel/https-byteswritten.test.js +++ /dev/null @@ -1,58 +0,0 @@ -//#FILE: test-https-byteswritten.js -//#SHA1: 8b808da3e55de553190426095aee298ec7d5df36 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const fixtures = require("../common/fixtures"); -const https = require("https"); - -const options = { - key: fixtures.readKey("agent1-key.pem"), - cert: fixtures.readKey("agent1-cert.pem"), -}; - -const body = "hello world\n"; - -test("HTTPS server bytesWritten", async () => { - const httpsServer = https.createServer(options, (req, res) => { - res.on("finish", () => { - expect(typeof req.connection.bytesWritten).toBe("number"); - expect(req.connection.bytesWritten).toBeGreaterThan(0); - httpsServer.close(); - }); - res.writeHead(200, { "Content-Type": "text/plain" }); - res.end(body); - }); - - await new Promise(resolve => { - httpsServer.listen(0, () => { - https.get({ - port: httpsServer.address().port, - rejectUnauthorized: false, - }); - resolve(); - }); - }); -}); - -//<#END_FILE: test-https-byteswritten.js diff --git a/test/js/node/test/parallel/https-foafssl.test.js b/test/js/node/test/parallel/https-foafssl.test.js deleted file mode 100644 index aac467af04..0000000000 --- a/test/js/node/test/parallel/https-foafssl.test.js +++ /dev/null @@ -1,114 +0,0 @@ -//#FILE: test-https-foafssl.js -//#SHA1: 07ac711f5948207540af7366d06803f2675f04c7 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const { skip } = require("../common"); -const fixtures = require("../common/fixtures"); -const https = require("https"); -const { spawn } = require("child_process"); - -if (!process.versions.openssl) { - skip("missing crypto"); -} - -if (!process.env.NODE_OPENSSL_CERT) { - skip("node compiled without OpenSSL CLI."); -} - -const options = { - key: fixtures.readKey("rsa_private.pem"), - cert: fixtures.readKey("rsa_cert.crt"), - requestCert: true, - rejectUnauthorized: false, -}; - -const webIdUrl = "URI:http://example.com/#me"; -const modulus = fixtures.readKey("rsa_cert_foafssl_b.modulus", "ascii").replace(/\n/g, ""); -const exponent = fixtures.readKey("rsa_cert_foafssl_b.exponent", "ascii").replace(/\n/g, ""); - -const CRLF = "\r\n"; -const body = "hello world\n"; -let cert; - -test("HTTPS FOAFSSL", async () => { - const serverHandler = jest.fn((req, res) => { - console.log("got request"); - - cert = req.connection.getPeerCertificate(); - - expect(cert.subjectaltname).toBe(webIdUrl); - expect(cert.exponent).toBe(exponent); - expect(cert.modulus).toBe(modulus); - res.writeHead(200, { "content-type": "text/plain" }); - res.end(body, () => { - console.log("stream finished"); - }); - console.log("sent response"); - }); - - const server = https.createServer(options, serverHandler); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const { port } = server.address(); - - const args = [ - "s_client", - "-quiet", - "-connect", - `127.0.0.1:${port}`, - "-cert", - fixtures.path("keys/rsa_cert_foafssl_b.crt"), - "-key", - fixtures.path("keys/rsa_private_b.pem"), - ]; - - const client = spawn(process.env.NODE_OPENSSL_CERT, args); - - client.stdout.on("data", data => { - console.log("response received"); - const message = data.toString(); - const contents = message.split(CRLF + CRLF).pop(); - expect(contents).toBe(body); - server.close(e => { - expect(e).toBeFalsy(); - console.log("server closed"); - }); - console.log("server.close() called"); - }); - - client.stdin.write("GET /\r\n\r\n"); - - await new Promise((resolve, reject) => { - client.on("error", reject); - client.on("close", resolve); - }); - - expect(serverHandler).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-https-foafssl.js diff --git a/test/js/node/test/parallel/https-socket-options.test.js b/test/js/node/test/parallel/https-socket-options.test.js deleted file mode 100644 index 5e325acc5d..0000000000 --- a/test/js/node/test/parallel/https-socket-options.test.js +++ /dev/null @@ -1,102 +0,0 @@ -//#FILE: test-https-socket-options.js -//#SHA1: 8f63b3c65f69e8b766b159d148e681984c134477 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const fixtures = require("../common/fixtures"); -const https = require("https"); -const http = require("http"); - -const options = { - key: fixtures.readKey("agent1-key.pem"), - cert: fixtures.readKey("agent1-cert.pem"), -}; - -const body = "hello world\n"; - -test("HTTP server socket options", async () => { - const server_http = http.createServer((req, res) => { - console.log("got HTTP request"); - res.writeHead(200, { "content-type": "text/plain" }); - res.end(body); - }); - - await new Promise(resolve => { - server_http.listen(0, () => { - const req = http.request( - { - port: server_http.address().port, - rejectUnauthorized: false, - }, - res => { - server_http.close(); - res.resume(); - resolve(); - }, - ); - // These methods should exist on the request and get passed down to the socket - expect(req.setNoDelay).toBeDefined(); - expect(req.setTimeout).toBeDefined(); - expect(req.setSocketKeepAlive).toBeDefined(); - req.setNoDelay(true); - req.setTimeout(1000, () => {}); - req.setSocketKeepAlive(true, 1000); - req.end(); - }); - }); -}); - -test("HTTPS server socket options", async () => { - const server_https = https.createServer(options, (req, res) => { - console.log("got HTTPS request"); - res.writeHead(200, { "content-type": "text/plain" }); - res.end(body); - }); - - await new Promise(resolve => { - server_https.listen(0, () => { - const req = https.request( - { - port: server_https.address().port, - rejectUnauthorized: false, - }, - res => { - server_https.close(); - res.resume(); - resolve(); - }, - ); - // These methods should exist on the request and get passed down to the socket - expect(req.setNoDelay).toBeDefined(); - expect(req.setTimeout).toBeDefined(); - expect(req.setSocketKeepAlive).toBeDefined(); - req.setNoDelay(true); - req.setTimeout(1000, () => {}); - req.setSocketKeepAlive(true, 1000); - req.end(); - }); - }); -}); - -//<#END_FILE: test-https-socket-options.js diff --git a/test/js/node/test/parallel/inspect-publish-uid.test.js b/test/js/node/test/parallel/inspect-publish-uid.test.js deleted file mode 100644 index 09ec36dcd3..0000000000 --- a/test/js/node/test/parallel/inspect-publish-uid.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-inspect-publish-uid.js -//#SHA1: cd6577ea81261e5e89b5ec6272e27f5a0614ffcf -//----------------- -"use strict"; - -const { spawnSync } = require("child_process"); -const inspector = require("inspector"); -const http = require("http"); -const url = require("url"); - -// Skip the test if inspector is disabled -if (!inspector.url()) { - test.skip("Inspector is disabled", () => {}); -} else { - test("Checks stderr", async () => { - await testArg("stderr"); - }); - - test("Checks http", async () => { - await testArg("http"); - }); - - test("Checks http,stderr", async () => { - await testArg("http,stderr"); - }); -} - -async function testArg(argValue) { - console.log("Checks " + argValue + ".."); - const hasHttp = argValue.split(",").includes("http"); - const hasStderr = argValue.split(",").includes("stderr"); - - const nodeProcess = spawnSync(process.execPath, [ - "--inspect=0", - `--inspect-publish-uid=${argValue}`, - "-e", - `(${scriptMain.toString()})(${hasHttp ? 200 : 404})`, - ]); - const hasWebSocketInStderr = checkStdError(nodeProcess.stderr.toString("utf8")); - expect(hasWebSocketInStderr).toBe(hasStderr); -} - -function checkStdError(data) { - const matches = data.toString("utf8").match(/ws:\/\/.+:(\d+)\/.+/); - return !!matches; -} - -function scriptMain(code) { - const inspectorUrl = inspector.url(); - const { host } = url.parse(inspectorUrl); - http.get("http://" + host + "/json/list", response => { - expect(response.statusCode).toBe(code); - response.destroy(); - }); -} - -//<#END_FILE: test-inspect-publish-uid.js diff --git a/test/js/node/test/parallel/inspect-support-for-node_options.test.js b/test/js/node/test/parallel/inspect-support-for-node_options.test.js deleted file mode 100644 index 77187f7aa6..0000000000 --- a/test/js/node/test/parallel/inspect-support-for-node_options.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-inspect-support-for-node_options.js -//#SHA1: 622a8cb07922833373b0d88c42c2a7bbfdc7d58e -//----------------- -"use strict"; - -const cluster = require("cluster"); - -// Skip if inspector is disabled -if (process.config.variables.v8_enable_inspector === 0) { - test.skip("Inspector is disabled", () => {}); -} else { - checkForInspectSupport("--inspect"); -} - -function checkForInspectSupport(flag) { - const nodeOptions = JSON.stringify(flag); - const numWorkers = 2; - process.env.NODE_OPTIONS = flag; - - test(`Cluster support for NODE_OPTIONS ${nodeOptions}`, () => { - if (cluster.isPrimary) { - const workerExitPromises = []; - - for (let i = 0; i < numWorkers; i++) { - const worker = cluster.fork(); - - worker.on("online", () => { - worker.disconnect(); - }); - - workerExitPromises.push( - new Promise(resolve => { - worker.on("exit", (code, signal) => { - expect(worker.exitedAfterDisconnect).toBe(true); - resolve(); - }); - }), - ); - } - - return Promise.all(workerExitPromises); - } - }); -} - -//<#END_FILE: test-inspect-support-for-node_options.js diff --git a/test/js/node/test/parallel/internal-fs.test.js b/test/js/node/test/parallel/internal-fs.test.js deleted file mode 100644 index 7b84ec5156..0000000000 --- a/test/js/node/test/parallel/internal-fs.test.js +++ /dev/null @@ -1,71 +0,0 @@ -//#FILE: test-internal-fs.js -//#SHA1: 47b2f898d6c0cdfba71a1f82b7617f466eb475c9 -//----------------- -'use strict'; - -const assert = require('assert'); -const fs = require('fs'); -const path = require('path'); - -// We can't use internal modules in this test, so we'll mock the necessary functions -const mockFs = { - assertEncoding: (encoding) => { - if (encoding && !['utf8', 'utf-8', 'ascii', 'utf16le', 'ucs2', 'ucs-2', 'base64', 'base64url', 'latin1', 'binary', 'hex'].includes(encoding.toLowerCase())) { - throw new TypeError('ERR_INVALID_ARG_VALUE'); - } - }, - preprocessSymlinkDestination: (pathString, type, linkPathString) => { - if (process.platform === 'win32' && type === 'junction') { - return path.join('\\\\?\\', pathString); - } - return pathString; - } -}; - -test('assertEncoding should not throw for valid encodings', () => { - expect(() => mockFs.assertEncoding()).not.toThrow(); - expect(() => mockFs.assertEncoding('utf8')).not.toThrow(); -}); - -test('assertEncoding should throw for invalid encodings', () => { - expect(() => mockFs.assertEncoding('foo')).toThrow(expect.objectContaining({ - name: 'TypeError', - message: expect.stringContaining('ERR_INVALID_ARG_VALUE') - })); -}); - -test('preprocessSymlinkDestination for junction symlinks', () => { - const pathString = 'c:\\test1'; - const linkPathString = '\\test2'; - - const preprocessSymlinkDestination = mockFs.preprocessSymlinkDestination( - pathString, - 'junction', - linkPathString - ); - - if (process.platform === 'win32') { - expect(preprocessSymlinkDestination).toMatch(/^\\\\\?\\/); - } else { - expect(preprocessSymlinkDestination).toBe(pathString); - } -}); - -test('preprocessSymlinkDestination for non-junction symlinks', () => { - const pathString = 'c:\\test1'; - const linkPathString = '\\test2'; - - const preprocessSymlinkDestination = mockFs.preprocessSymlinkDestination( - pathString, - undefined, - linkPathString - ); - - if (process.platform === 'win32') { - expect(preprocessSymlinkDestination).not.toMatch(/\//); - } else { - expect(preprocessSymlinkDestination).toBe(pathString); - } -}); - -//<#END_FILE: test-internal-fs.js diff --git a/test/js/node/test/parallel/internal-process-binding.test.js b/test/js/node/test/parallel/internal-process-binding.test.js deleted file mode 100644 index e3b90d28a4..0000000000 --- a/test/js/node/test/parallel/internal-process-binding.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-internal-process-binding.js -//#SHA1: e14c48cb6cd21ab499bd5d72cf8c8d0cddccf767 -//----------------- -"use strict"; - -test("process internal binding", () => { - expect(process._internalBinding).toBeUndefined(); - expect(process.internalBinding).toBeUndefined(); - expect(() => { - process.binding("module_wrap"); - }).toThrow( - expect.objectContaining({ - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-internal-process-binding.js diff --git a/test/js/node/test/parallel/intl-v8breakiterator.test.js b/test/js/node/test/parallel/intl-v8breakiterator.test.js deleted file mode 100644 index 7bf86915d2..0000000000 --- a/test/js/node/test/parallel/intl-v8breakiterator.test.js +++ /dev/null @@ -1,19 +0,0 @@ -//#FILE: test-intl-v8BreakIterator.js -//#SHA1: c1592e4a1a3d4971e70d4b2bc30e31bb157f8646 -//----------------- -"use strict"; - -if (!globalThis.Intl) { - test.skip("missing Intl"); -} - -test("v8BreakIterator is not in Intl", () => { - expect("v8BreakIterator" in Intl).toBe(false); -}); - -test("v8BreakIterator is not in Intl in a new context", () => { - const vm = require("vm"); - expect(vm.runInNewContext('"v8BreakIterator" in Intl')).toBe(false); -}); - -//<#END_FILE: test-intl-v8BreakIterator.js diff --git a/test/js/node/test/parallel/jest.config.js b/test/js/node/test/parallel/jest.config.js deleted file mode 100644 index 7f2c94ff64..0000000000 --- a/test/js/node/test/parallel/jest.config.js +++ /dev/null @@ -1,2 +0,0 @@ -// So jest doesn't try to look up. -module.exports = {}; diff --git a/test/js/node/test/parallel/js-stream-call-properties.test.js b/test/js/node/test/parallel/js-stream-call-properties.test.js deleted file mode 100644 index 070bd47e99..0000000000 --- a/test/js/node/test/parallel/js-stream-call-properties.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-js-stream-call-properties.js -//#SHA1: 491c3447495fadda8e713d00b9621ea31d8fb27d -//----------------- -"use strict"; - -test("JSStream properties can be inspected", () => { - // We can't use internal bindings in Jest, so we'll mock the JSStream - class MockJSStream { - constructor() { - // Add some properties that might be inspected - this.readableFlowing = null; - this.writableFinished = false; - // Add more properties as needed - } - } - - // Mock util.inspect to ensure it's called - const mockInspect = jest.fn(); - jest.spyOn(console, "log").mockImplementation(mockInspect); - - // Create an instance of our mock JSStream - const jsStream = new MockJSStream(); - - // Call console.log, which will internally call util.inspect - console.log(jsStream); - - // Verify that inspect was called - expect(mockInspect).toHaveBeenCalledTimes(1); - expect(mockInspect).toHaveBeenCalledWith(expect.any(MockJSStream)); - - // Clean up - console.log.mockRestore(); -}); - -//<#END_FILE: test-js-stream-call-properties.js diff --git a/test/js/node/test/parallel/kill-segfault-freebsd.test.js b/test/js/node/test/parallel/kill-segfault-freebsd.test.js deleted file mode 100644 index 786d3f4dbf..0000000000 --- a/test/js/node/test/parallel/kill-segfault-freebsd.test.js +++ /dev/null @@ -1,22 +0,0 @@ -//#FILE: test-kill-segfault-freebsd.js -//#SHA1: ac3b65d8e5e92ffb9714307d2249b4c3b9240ed9 -//----------------- -"use strict"; - -// This test ensures Node.js doesn't crash on hitting Ctrl+C in order to -// terminate the currently running process (especially on FreeBSD). -// https://github.com/nodejs/node-v0.x-archive/issues/9326 - -const child_process = require("child_process"); - -test("Node.js doesn't crash on SIGINT (FreeBSD issue)", done => { - // NOTE: Was crashing on FreeBSD - const cp = child_process.spawn(process.execPath, ["-e", 'process.kill(process.pid, "SIGINT")']); - - cp.on("exit", function (code) { - expect(code).not.toBe(0); - done(); - }); -}); - -//<#END_FILE: test-kill-segfault-freebsd.js diff --git a/test/js/node/test/parallel/listen-fd-detached-inherit.test.js b/test/js/node/test/parallel/listen-fd-detached-inherit.test.js deleted file mode 100644 index ba5323fbdc..0000000000 --- a/test/js/node/test/parallel/listen-fd-detached-inherit.test.js +++ /dev/null @@ -1,137 +0,0 @@ -//#FILE: test-listen-fd-detached-inherit.js -//#SHA1: 4aab69e9262c853cc790bd9de1fc3cf9529baa01 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const http = require("http"); -const net = require("net"); -const { spawn } = require("child_process"); - -if (process.platform === "win32") { - test.skip("This test is disabled on windows."); -} - -switch (process.argv[2]) { - case "child": - child(); - break; - case "parent": - parent(); - break; - default: - runTest(); -} - -// Spawn the parent, and listen for it to tell us the pid of the child. -// WARNING: This is an example of listening on some arbitrary FD number -// that has already been bound elsewhere in advance. However, binding -// server handles to stdio fd's is NOT a good or reliable way to do -// concurrency in HTTP servers! Use the cluster module, or if you want -// a more low-level approach, use child process IPC manually. -async function runTest() { - const parent = spawn(process.execPath, [__filename, "parent"], { - stdio: [0, "pipe", 2], - }); - - let json = ""; - for await (const chunk of parent.stdout) { - json += chunk.toString(); - if (json.includes("\n")) break; - } - - console.error("output from parent = %s", json); - const child = JSON.parse(json); - - // Now make sure that we can request to the subprocess, then kill it. - const response = await new Promise(resolve => { - http.get( - { - hostname: "localhost", - port: child.port, - path: "/", - }, - resolve, - ); - }); - - let responseBody = ""; - for await (const chunk of response) { - responseBody += chunk.toString(); - } - - // Kill the subprocess before we start doing asserts. - // It's really annoying when tests leave orphans! - process.kill(child.pid, "SIGKILL"); - try { - parent.kill(); - } catch { - // Continue regardless of error. - } - - expect(responseBody).toBe("hello from child\n"); - expect(response.statusCode).toBe(200); -} - -// Listen on port, and then pass the handle to the detached child. -// Then output the child's pid, and immediately exit. -function parent() { - const server = net - .createServer(conn => { - conn.end("HTTP/1.1 403 Forbidden\r\n\r\nI got problems.\r\n"); - throw new Error("Should not see connections on parent"); - }) - .listen(0, function () { - console.error("server listening on %d", this.address().port); - - const child = spawn(process.execPath, [__filename, "child"], { - stdio: [0, 1, 2, server._handle], - detached: true, - }); - - console.log("%j\n", { pid: child.pid, port: this.address().port }); - - // Now close the parent, so that the child is the only thing - // referencing that handle. Note that connections will still - // be accepted, because the child has the fd open, but the parent - // will exit gracefully. - server.close(); - child.unref(); - }); -} - -// Run as a child of the parent() mode. -function child() { - // Start a server on fd=3 - http - .createServer((req, res) => { - console.error("request on child"); - console.error("%s %s", req.method, req.url, req.headers); - res.end("hello from child\n"); - }) - .listen({ fd: 3 }, () => { - console.error("child listening on fd=3"); - }); -} - -//<#END_FILE: test-listen-fd-detached-inherit.js diff --git a/test/js/node/test/parallel/memory-usage-emfile.test.js b/test/js/node/test/parallel/memory-usage-emfile.test.js deleted file mode 100644 index 6e8cb22c8d..0000000000 --- a/test/js/node/test/parallel/memory-usage-emfile.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-memory-usage-emfile.js -//#SHA1: 062c0483d1da90d9e08bab6a7d006d2da5861bd9 -//----------------- -"use strict"; - -// On IBMi, the rss memory always returns zero -if (process.platform === "os400") { - test.skip("On IBMi, the rss memory always returns zero", () => {}); -} else { - const fs = require("fs"); - - test("memory usage with many open files", () => { - const files = []; - - while (files.length < 256) files.push(fs.openSync(__filename, "r")); - - const r = process.memoryUsage.rss(); - expect(r).toBeGreaterThan(0); - - // Clean up opened files - files.forEach(fd => fs.closeSync(fd)); - }); -} - -//<#END_FILE: test-memory-usage-emfile.js diff --git a/test/js/node/test/parallel/memory-usage.test.js b/test/js/node/test/parallel/memory-usage.test.js deleted file mode 100644 index 74c73896df..0000000000 --- a/test/js/node/test/parallel/memory-usage.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-memory-usage.js -//#SHA1: fffba1b4ff9ad7092d9a8f51b2799a0606d769eb -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// Flags: --predictable-gc-schedule -"use strict"; - -const isIBMi = process.platform === "aix" && process.execPath.includes("powerpc"); - -test("memory usage", () => { - const r = process.memoryUsage(); - // On IBMi, the rss memory always returns zero - if (!isIBMi) { - expect(r.rss).toBeGreaterThan(0); - expect(process.memoryUsage.rss()).toBeGreaterThan(0); - } - - expect(r.heapTotal).toBeGreaterThan(0); - expect(r.heapUsed).toBeGreaterThan(0); - expect(r.external).toBeGreaterThan(0); - - expect(typeof r.arrayBuffers).toBe("number"); - if (r.arrayBuffers > 0) { - const size = 10 * 1024 * 1024; - // eslint-disable-next-line no-unused-vars - const ab = new ArrayBuffer(size); - - const after = process.memoryUsage(); - expect(after.external - r.external).toBeGreaterThanOrEqual(size); - expect(after.arrayBuffers - r.arrayBuffers).toBe(size); - } -}); - -//<#END_FILE: test-memory-usage.js diff --git a/test/js/node/test/parallel/messageevent-brandcheck.test.js b/test/js/node/test/parallel/messageevent-brandcheck.test.js deleted file mode 100644 index ba2dd9c11a..0000000000 --- a/test/js/node/test/parallel/messageevent-brandcheck.test.js +++ /dev/null @@ -1,17 +0,0 @@ -//#FILE: test-messageevent-brandcheck.js -//#SHA1: 1b04c8b7c45fe0f2fe12018ca10137eefa892b4c -//----------------- -"use strict"; - -test("MessageEvent brand checks", () => { - ["data", "origin", "lastEventId", "source", "ports"].forEach(prop => { - expect(() => Reflect.get(MessageEvent.prototype, prop, {})).toThrow( - expect.objectContaining({ - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-messageevent-brandcheck.js diff --git a/test/js/node/test/parallel/microtask-queue-integration.test.js b/test/js/node/test/parallel/microtask-queue-integration.test.js deleted file mode 100644 index 7d0dc16d64..0000000000 --- a/test/js/node/test/parallel/microtask-queue-integration.test.js +++ /dev/null @@ -1,81 +0,0 @@ -//#FILE: test-microtask-queue-integration.js -//#SHA1: bc144f7d64d2ed682489718745be5883122cf323 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const implementations = [ - function (fn) { - Promise.resolve().then(fn); - }, -]; - -let expected = 0; -let done = 0; - -afterAll(() => { - expect(done).toBe(expected); -}); - -function testMicrotask(scheduleMicrotask) { - return new Promise(resolve => { - let nextTickCalled = false; - expected++; - - scheduleMicrotask(() => { - process.nextTick(() => { - nextTickCalled = true; - }); - - setTimeout(() => { - expect(nextTickCalled).toBe(true); - done++; - resolve(); - }, 0); - }); - }); -} - -test("first tick case", async () => { - await Promise.all(implementations.map(testMicrotask)); -}); - -test("tick callback case", async () => { - await new Promise(resolve => { - setTimeout(async () => { - await Promise.all( - implementations.map( - impl => - new Promise(resolve => { - process.nextTick(() => { - testMicrotask(impl).then(resolve); - }); - }), - ), - ); - resolve(); - }, 0); - }); -}); - -//<#END_FILE: test-microtask-queue-integration.js diff --git a/test/js/node/test/parallel/microtask-queue-run-immediate.test.js b/test/js/node/test/parallel/microtask-queue-run-immediate.test.js deleted file mode 100644 index e64e7022b7..0000000000 --- a/test/js/node/test/parallel/microtask-queue-run-immediate.test.js +++ /dev/null @@ -1,60 +0,0 @@ -//#FILE: test-microtask-queue-run-immediate.js -//#SHA1: 49e5d82cc3467e4e12d0e93629607cd48b3548e4 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -function enqueueMicrotask(fn) { - Promise.resolve().then(fn); -} - -test("microtask queue runs in setImmediate", done => { - let microtaskExecuted = false; - - setImmediate(() => { - enqueueMicrotask(() => { - microtaskExecuted = true; - expect(microtaskExecuted).toBe(true); - done(); - }); - }); -}); - -test("microtask with nextTick runs before next setImmediate", done => { - let nextTickCalled = false; - - setImmediate(() => { - enqueueMicrotask(() => { - process.nextTick(() => { - nextTickCalled = true; - }); - }); - - setImmediate(() => { - expect(nextTickCalled).toBe(true); - done(); - }); - }); -}); - -//<#END_FILE: test-microtask-queue-run-immediate.js diff --git a/test/js/node/test/parallel/microtask-queue-run.test.js b/test/js/node/test/parallel/microtask-queue-run.test.js deleted file mode 100644 index 8e25e318d2..0000000000 --- a/test/js/node/test/parallel/microtask-queue-run.test.js +++ /dev/null @@ -1,67 +0,0 @@ -//#FILE: test-microtask-queue-run.js -//#SHA1: caf14b2c15bbc84816eb2ce6b9bdb073c009b447 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -function enqueueMicrotask(fn) { - Promise.resolve().then(fn); -} - -test("microtask queue runs correctly", async () => { - let done = 0; - - // No nextTick, microtask - await new Promise(resolve => { - setTimeout(() => { - enqueueMicrotask(() => { - done++; - resolve(); - }); - }, 0); - }); - - // No nextTick, microtask with nextTick - await new Promise(resolve => { - setTimeout(() => { - let called = false; - - enqueueMicrotask(() => { - process.nextTick(() => { - called = true; - }); - }); - - setTimeout(() => { - if (called) { - done++; - } - resolve(); - }, 0); - }, 0); - }); - - expect(done).toBe(2); -}); - -//<#END_FILE: test-microtask-queue-run.js diff --git a/test/js/node/test/parallel/module-builtin.test.js b/test/js/node/test/parallel/module-builtin.test.js deleted file mode 100644 index cc9f208d38..0000000000 --- a/test/js/node/test/parallel/module-builtin.test.js +++ /dev/null @@ -1,17 +0,0 @@ -//#FILE: test-module-builtin.js -//#SHA1: 18114886f66eccc937942a815feca25d9b324a37 -//----------------- -"use strict"; - -test("builtinModules", () => { - const { builtinModules } = require("module"); - - // Includes modules in lib/ (even deprecated ones) - expect(builtinModules).toContain("http"); - expect(builtinModules).toContain("sys"); - - // Does not include internal modules - expect(builtinModules.filter(mod => mod.startsWith("internal/"))).toEqual([]); -}); - -//<#END_FILE: test-module-builtin.js diff --git a/test/js/node/test/parallel/module-cache.test.js b/test/js/node/test/parallel/module-cache.test.js deleted file mode 100644 index 605b768808..0000000000 --- a/test/js/node/test/parallel/module-cache.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-module-cache.js -//#SHA1: ff0f4c6ca37e23c009f98bba966e9daee2dcaef6 -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -let tmpdir; - -beforeEach(() => { - tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "test-module-cache-")); -}); - -afterEach(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); -}); - -test("throws MODULE_NOT_FOUND when file does not exist", () => { - const filePath = path.join(tmpdir, "test-module-cache.json"); - expect(() => require(filePath)).toThrow( - expect.objectContaining({ - code: "MODULE_NOT_FOUND", - message: expect.any(String), - }), - ); -}); - -test("requires JSON file successfully after creation", () => { - const filePath = path.join(tmpdir, "test-module-cache.json"); - fs.writeFileSync(filePath, "[]"); - - const content = require(filePath); - expect(Array.isArray(content)).toBe(true); - expect(content.length).toBe(0); -}); - -//<#END_FILE: test-module-cache.js diff --git a/test/js/node/test/parallel/module-main-extension-lookup.test.js b/test/js/node/test/parallel/module-main-extension-lookup.test.js deleted file mode 100644 index 741f721bc6..0000000000 --- a/test/js/node/test/parallel/module-main-extension-lookup.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-module-main-extension-lookup.js -//#SHA1: d50be34ba21e1e14de5225ac5d93b6fe20505014 -//----------------- -"use strict"; - -const path = require("path"); -const { execFileSync } = require("child_process"); - -const node = process.argv[0]; - -test("ES modules extension lookup", () => { - const fixturesPath = path.resolve(__dirname, "..", "fixtures"); - - expect(() => { - execFileSync(node, [path.join(fixturesPath, "es-modules", "test-esm-ok.mjs")]); - }).not.toThrow(); - - expect(() => { - execFileSync(node, [path.join(fixturesPath, "es-modules", "noext")]); - }).not.toThrow(); -}); - -//<#END_FILE: test-module-main-extension-lookup.js diff --git a/test/js/node/test/parallel/net-after-close.test.js b/test/js/node/test/parallel/net-after-close.test.js deleted file mode 100644 index 5d2248cc5e..0000000000 --- a/test/js/node/test/parallel/net-after-close.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-net-after-close.js -//#SHA1: 5b16857d2580262739b7c74c87a520ee6fc974c9 -//----------------- -"use strict"; -const net = require("net"); - -let server; -let serverPort; - -beforeAll(done => { - server = net.createServer(s => { - s.end(); - }); - - server.listen(0, () => { - serverPort = server.address().port; - done(); - }); -}); - -afterAll(done => { - server.close(done); -}); - -test("net socket behavior after close", done => { - const c = net.createConnection(serverPort); - - c.on("close", () => { - expect(c._handle).toBeNull(); - - // Calling functions / accessing properties of a closed socket should not throw. - expect(() => { - c.setNoDelay(); - c.setKeepAlive(); - c.bufferSize; - c.pause(); - c.resume(); - c.address(); - c.remoteAddress; - c.remotePort; - }).not.toThrow(); - - done(); - }); -}); - -//<#END_FILE: test-net-after-close.js diff --git a/test/js/node/test/parallel/net-allow-half-open.test.js b/test/js/node/test/parallel/net-allow-half-open.test.js deleted file mode 100644 index 0b05942eeb..0000000000 --- a/test/js/node/test/parallel/net-allow-half-open.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-net-allow-half-open.js -//#SHA1: 713191e6681104ac9709a51cbe5dc881f7a7fa89 -//----------------- -'use strict'; - -const net = require('net'); - -describe('Net allow half open', () => { - test('Socket not destroyed immediately after end', (done) => { - const server = net.createServer((socket) => { - socket.end(Buffer.alloc(1024)); - }); - - server.listen(0, () => { - const socket = net.connect(server.address().port); - expect(socket.allowHalfOpen).toBe(false); - socket.resume(); - - socket.on('end', () => { - process.nextTick(() => { - // Ensure socket is not destroyed straight away - // without proper shutdown. - expect(socket.destroyed).toBe(false); - server.close(); - done(); - }); - }); - - socket.on('finish', () => { - expect(socket.destroyed).toBe(false); - }); - - socket.on('close', () => {}); - }); - }); - - test('Socket not destroyed after end and write', (done) => { - const server = net.createServer((socket) => { - socket.end(Buffer.alloc(1024)); - }); - - server.listen(0, () => { - const socket = net.connect(server.address().port); - expect(socket.allowHalfOpen).toBe(false); - socket.resume(); - - socket.on('end', () => { - expect(socket.destroyed).toBe(false); - }); - - socket.end('asd'); - - socket.on('finish', () => { - expect(socket.destroyed).toBe(false); - }); - - socket.on('close', () => { - server.close(); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-net-allow-half-open.js diff --git a/test/js/node/test/parallel/net-autoselectfamily-attempt-timeout-default-value.test.js b/test/js/node/test/parallel/net-autoselectfamily-attempt-timeout-default-value.test.js deleted file mode 100644 index 5b33636b4d..0000000000 --- a/test/js/node/test/parallel/net-autoselectfamily-attempt-timeout-default-value.test.js +++ /dev/null @@ -1,13 +0,0 @@ -//#FILE: test-net-autoselectfamily-attempt-timeout-default-value.js -//#SHA1: 028b16515c47d987e68ca138e753ed4d255f179c -//----------------- -"use strict"; - -const { platformTimeout } = require("../common"); -const { getDefaultAutoSelectFamilyAttemptTimeout } = require("net"); - -test("getDefaultAutoSelectFamilyAttemptTimeout returns the correct default value", () => { - expect(getDefaultAutoSelectFamilyAttemptTimeout()).toBe(platformTimeout(2500)); -}); - -//<#END_FILE: test-net-autoselectfamily-attempt-timeout-default-value.js diff --git a/test/js/node/test/parallel/net-bind-twice.test.js b/test/js/node/test/parallel/net-bind-twice.test.js deleted file mode 100644 index de2b9428ca..0000000000 --- a/test/js/node/test/parallel/net-bind-twice.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-net-bind-twice.js -//#SHA1: 432eb9529d0affc39c8af9ebc1147528d96305c9 -//----------------- -"use strict"; -const net = require("net"); - -test("net.Server should not allow binding to the same port twice", done => { - const server1 = net.createServer(() => { - throw new Error("Server1 should not receive connections"); - }); - - server1.listen(0, "127.0.0.1", () => { - const server2 = net.createServer(() => { - throw new Error("Server2 should not receive connections"); - }); - - const port = server1.address().port; - server2.listen(port, "127.0.0.1", () => { - throw new Error("Server2 should not be able to listen"); - }); - - server2.on("error", e => { - expect(e.code).toBe("EADDRINUSE"); - server1.close(() => { - done(); - }); - }); - }); -}, 100000); - -//<#END_FILE: test-net-bind-twice.js diff --git a/test/js/node/test/parallel/net-buffersize.test.js b/test/js/node/test/parallel/net-buffersize.test.js deleted file mode 100644 index 5701356648..0000000000 --- a/test/js/node/test/parallel/net-buffersize.test.js +++ /dev/null @@ -1,60 +0,0 @@ -//#FILE: test-net-buffersize.js -//#SHA1: b6b1298dc9f836252e5fcdcee680116d50da7651 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const net = require("net"); - -const iter = 10; - -test("net buffer size", async () => { - const server = net.createServer(socket => { - socket.on("readable", () => { - socket.read(); - }); - - socket.on("end", () => { - server.close(); - }); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const client = net.connect(server.address().port); - - client.on("finish", () => { - expect(client.bufferSize).toBe(0); - resolve(); - }); - - for (let i = 1; i < iter; i++) { - client.write("a"); - expect(client.bufferSize).toBe(i); - } - - client.end(); - }); - }); -}); - -//<#END_FILE: test-net-buffersize.js diff --git a/test/js/node/test/parallel/net-bytes-written-large.test.js b/test/js/node/test/parallel/net-bytes-written-large.test.js deleted file mode 100644 index 715af6ecc7..0000000000 --- a/test/js/node/test/parallel/net-bytes-written-large.test.js +++ /dev/null @@ -1,73 +0,0 @@ -//#FILE: test-net-bytes-written-large.js -//#SHA1: 9005801147f80a8058f1b2126d772e52abd1f237 -//----------------- -"use strict"; -const net = require("net"); - -const N = 10000000; - -describe("Net bytes written large", () => { - test("Write a Buffer", done => { - const server = net - .createServer(socket => { - socket.end(Buffer.alloc(N), () => { - expect(socket.bytesWritten).toBe(N); - }); - expect(socket.bytesWritten).toBe(N); - }) - .listen(0, () => { - const client = net.connect(server.address().port); - client.resume(); - client.on("close", () => { - expect(client.bytesRead).toBe(N); - server.close(); - done(); - }); - }); - }); - - test("Write a string", done => { - const server = net - .createServer(socket => { - socket.end("a".repeat(N), () => { - expect(socket.bytesWritten).toBe(N); - }); - expect(socket.bytesWritten).toBe(N); - }) - .listen(0, () => { - const client = net.connect(server.address().port); - client.resume(); - client.on("close", () => { - expect(client.bytesRead).toBe(N); - server.close(); - done(); - }); - }); - }); - - test("writev() with mixed data", done => { - const server = net - .createServer(socket => { - socket.cork(); - socket.write("a".repeat(N)); - expect(socket.bytesWritten).toBe(N); - socket.write(Buffer.alloc(N)); - expect(socket.bytesWritten).toBe(2 * N); - socket.end("", () => { - expect(socket.bytesWritten).toBe(2 * N); - }); - socket.uncork(); - }) - .listen(0, () => { - const client = net.connect(server.address().port); - client.resume(); - client.on("close", () => { - expect(client.bytesRead).toBe(2 * N); - server.close(); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-net-bytes-written-large.js diff --git a/test/js/node/test/parallel/net-can-reset-timeout.test.js b/test/js/node/test/parallel/net-can-reset-timeout.test.js deleted file mode 100644 index 1bb7e8e6a8..0000000000 --- a/test/js/node/test/parallel/net-can-reset-timeout.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-net-can-reset-timeout.js -//#SHA1: 871319149db929419e14ba7f08e5d0c878222a93 -//----------------- -'use strict'; - -const net = require('net'); - -describe('Net can reset timeout', () => { - let server; - let port; - - beforeAll((done) => { - server = net.createServer((stream) => { - stream.setTimeout(100); - - stream.resume(); - - stream.once('timeout', () => { - console.log('timeout'); - // Try to reset the timeout. - stream.write('WHAT.'); - }); - - stream.on('end', () => { - console.log('server side end'); - stream.end(); - }); - }); - - server.listen(0, () => { - port = server.address().port; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test('should handle timeout and reset', (done) => { - const c = net.createConnection(port, "127.0.0.1"); - - c.on('data', () => { - c.end(); - }); - - c.on('end', () => { - console.log('client side end'); - done(); - }); - }); -}); - -//<#END_FILE: test-net-can-reset-timeout.js diff --git a/test/js/node/test/parallel/net-connect-after-destroy.test.js b/test/js/node/test/parallel/net-connect-after-destroy.test.js deleted file mode 100644 index 013f7cd0da..0000000000 --- a/test/js/node/test/parallel/net-connect-after-destroy.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-net-connect-after-destroy.js -//#SHA1: 9341bea710601b5a3a8e823f4847396b210a855c -//----------------- -'use strict'; - -const net = require('net'); - -test('net.createConnection after destroy', () => { - // Connect to something that we need to DNS resolve - const c = net.createConnection(80, 'google.com'); - - // The test passes if this doesn't throw an error - expect(() => { - c.destroy(); - }).not.toThrow(); -}); - -//<#END_FILE: test-net-connect-after-destroy.js diff --git a/test/js/node/test/parallel/net-connect-call-socket-connect.test.js b/test/js/node/test/parallel/net-connect-call-socket-connect.test.js deleted file mode 100644 index 8a74641b74..0000000000 --- a/test/js/node/test/parallel/net-connect-call-socket-connect.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-net-connect-call-socket-connect.js -//#SHA1: 953a427c411633a029dcf978ea07fb701ae5ed9a -//----------------- -"use strict"; - -const net = require("net"); -const Socket = net.Socket; - -// This test checks that calling `net.connect` internally calls -// `Socket.prototype.connect`. -// -// This is important for people who monkey-patch `Socket.prototype.connect` -// since it's not possible to monkey-patch `net.connect` directly (as the core -// `connect` function is called internally in Node instead of calling the -// `exports.connect` function). -// -// Monkey-patching of `Socket.prototype.connect` is done by - among others - -// most APM vendors, the async-listener module and the -// continuation-local-storage module. -// -// Related: -// - https://github.com/nodejs/node/pull/12342 -// - https://github.com/nodejs/node/pull/12852 - -test("net.connect calls Socket.prototype.connect", async () => { - // Monkey patch Socket.prototype.connect to check that it's called. - const orig = Socket.prototype.connect; - const connectMock = jest.fn(function () { - return orig.apply(this, arguments); - }); - Socket.prototype.connect = connectMock; - - const server = net.createServer(); - - await new Promise(resolve => { - server.listen(() => { - const port = server.address().port; - const client = net.connect({ port }, () => { - client.end(); - }); - client.on("end", () => { - server.close(resolve); - }); - }); - }); - - expect(connectMock).toHaveBeenCalledTimes(1); - - // Restore original Socket.prototype.connect - Socket.prototype.connect = orig; -}); - -//<#END_FILE: test-net-connect-call-socket-connect.js diff --git a/test/js/node/test/parallel/net-connect-destroy.test.js b/test/js/node/test/parallel/net-connect-destroy.test.js deleted file mode 100644 index 358d9495a9..0000000000 --- a/test/js/node/test/parallel/net-connect-destroy.test.js +++ /dev/null @@ -1,19 +0,0 @@ -//#FILE: test-net-connect-destroy.js -//#SHA1: a185f5169d7b2988a09b74d9524743beda08dcff -//----------------- -'use strict'; -const net = require('net'); - -test('Socket is destroyed and emits close event', (done) => { - const socket = new net.Socket(); - - socket.on('close', () => { - // The close event was emitted - expect(true).toBe(true); - done(); - }); - - socket.destroy(); -}); - -//<#END_FILE: test-net-connect-destroy.js diff --git a/test/js/node/test/parallel/net-connect-immediate-destroy.test.js b/test/js/node/test/parallel/net-connect-immediate-destroy.test.js deleted file mode 100644 index 055b18bae4..0000000000 --- a/test/js/node/test/parallel/net-connect-immediate-destroy.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-net-connect-immediate-destroy.js -//#SHA1: 28ba78fafba37cb07a2ec4b18e3e35bbb78ef699 -//----------------- -"use strict"; -const net = require("net"); - -test("net.connect immediate destroy", done => { - const server = net.createServer(); - server.listen(0, () => { - const port = server.address().port; - const socket = net.connect(port, "127.0.0.1"); - - socket.on("connect", () => { - throw new Error("Socket should not connect"); - }); - - socket.on("error", () => { - throw new Error("Socket should not emit error"); - }); - - server.close(() => { - socket.destroy(); - done(); - }); - }); -}); - -//<#END_FILE: test-net-connect-immediate-destroy.js diff --git a/test/js/node/test/parallel/net-connect-options-allowhalfopen.test.js b/test/js/node/test/parallel/net-connect-options-allowhalfopen.test.js deleted file mode 100644 index e0cdeb1803..0000000000 --- a/test/js/node/test/parallel/net-connect-options-allowhalfopen.test.js +++ /dev/null @@ -1,112 +0,0 @@ -//#FILE: test-net-connect-options-allowhalfopen.js -//#SHA1: 9ba18563d747b3ebfa63f8f54468b62526224ec6 -//----------------- -"use strict"; -const net = require("net"); - -describe("Net connect options allowHalfOpen", () => { - let server; - let clientReceivedFIN = 0; - let serverConnections = 0; - let clientSentFIN = 0; - let serverReceivedFIN = 0; - const host = "127.0.0.1"; - const CLIENT_VARIANTS = 6; - - function serverOnConnection(socket) { - console.log(`'connection' ${++serverConnections} emitted on server`); - const srvConn = serverConnections; - socket.resume(); - socket.on("data", data => { - socket.clientId = data.toString(); - console.log(`server connection ${srvConn} is started by client ${socket.clientId}`); - }); - - socket.on("end", () => { - console.log(`Server received FIN sent by client ${socket.clientId}`); - if (++serverReceivedFIN < CLIENT_VARIANTS) return; - setTimeout(() => { - server.close(); - console.log( - `connection ${socket.clientId} is closing the server: - FIN ${serverReceivedFIN} received by server, - FIN ${clientReceivedFIN} received by client - FIN ${clientSentFIN} sent by client, - FIN ${serverConnections} sent by server`.replace(/ {3,}/g, ""), - ); - }, 50); - }); - socket.end(); - console.log(`Server has sent ${serverConnections} FIN`); - } - - function serverOnClose() { - console.log( - `Server has been closed: - FIN ${serverReceivedFIN} received by server - FIN ${clientReceivedFIN} received by client - FIN ${clientSentFIN} sent by client - FIN ${serverConnections} sent by server`.replace(/ {3,}/g, ""), - ); - } - - beforeAll(done => { - server = net - .createServer({ allowHalfOpen: true }) - .on("connection", serverOnConnection) - .on("close", serverOnClose) - .listen(0, host, () => { - console.log(`Server started listening at ${host}:${server.address().port}`); - done(); - }); - }); - - afterAll(() => { - if (server) { - server.close(); - } else { - done(); - } - }); - - test("should handle allowHalfOpen connections correctly", done => { - function clientOnConnect(index) { - return function clientOnConnectInner() { - const client = this; - console.log(`'connect' emitted on Client ${index}`); - client.resume(); - client.on("end", () => { - setTimeout(() => { - console.log(`client ${index} received FIN`); - expect(client.readable).toBe(false); - expect(client.writable).toBe(true); - expect(client.write(String(index))).toBeTruthy(); - client.end(); - clientSentFIN++; - console.log(`client ${index} sent FIN, ${clientSentFIN} have been sent`); - }, 50); - }); - client.on("close", () => { - clientReceivedFIN++; - console.log( - `connection ${index} has been closed by both sides,` + ` ${clientReceivedFIN} clients have closed`, - ); - if (clientReceivedFIN === CLIENT_VARIANTS) { - done(); - } - }); - }; - } - - const port = server.address().port; - const opts = { allowHalfOpen: true, host, port }; - net.connect(opts, clientOnConnect(1)); - net.connect(opts).on("connect", clientOnConnect(2)); - net.createConnection(opts, clientOnConnect(3)); - net.createConnection(opts).on("connect", clientOnConnect(4)); - new net.Socket(opts).connect(opts, clientOnConnect(5)); - new net.Socket(opts).connect(opts).on("connect", clientOnConnect(6)); - }); -}); - -//<#END_FILE: test-net-connect-options-allowhalfopen.js diff --git a/test/js/node/test/parallel/net-connect-options-fd.test.js b/test/js/node/test/parallel/net-connect-options-fd.test.js deleted file mode 100644 index a685b4a0e6..0000000000 --- a/test/js/node/test/parallel/net-connect-options-fd.test.js +++ /dev/null @@ -1,12 +0,0 @@ -//#FILE: test-net-connect-options-fd.js -//#SHA1: 3933f2a09469bfaad999b5ba483bde9c6255cb35 -//----------------- -'use strict'; - -// This test requires internal Node.js modules and cannot be run in a standard Jest environment -test('net connect options fd', () => { - console.log('This test requires internal Node.js modules and cannot be run in a standard Jest environment'); - expect(true).toBe(true); -}); - -//<#END_FILE: test-net-connect-options-fd.js diff --git a/test/js/node/test/parallel/net-connect-options-path.test.js b/test/js/node/test/parallel/net-connect-options-path.test.js deleted file mode 100644 index 446200036b..0000000000 --- a/test/js/node/test/parallel/net-connect-options-path.test.js +++ /dev/null @@ -1,70 +0,0 @@ -//#FILE: test-net-connect-options-path.js -//#SHA1: 03b1a7de04f689c6429298b553a49478321b4adb -//----------------- -'use strict'; -const net = require('net'); -const fs = require('fs'); -const path = require('path'); -const os = require('os'); - -const CLIENT_VARIANTS = 12; - -describe('net.connect options path', () => { - let serverPath; - let server; - - beforeAll(() => { - const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'net-connect-options-path-')); - serverPath = path.join(tmpdir, 'server'); - }); - - afterAll(() => { - fs.rmdirSync(path.dirname(serverPath), { recursive: true }); - }); - - test('connect with various options', (done) => { - let connectionsCount = 0; - - server = net.createServer((socket) => { - socket.end('ok'); - }); - - server.listen(serverPath, () => { - const connectAndTest = (connectFn) => { - return new Promise((resolve) => { - const socket = connectFn(); - socket.on('data', (data) => { - expect(data.toString()).toBe('ok'); - socket.end(); - }); - socket.on('end', () => { - connectionsCount++; - resolve(); - }); - }); - }; - - const connectPromises = [ - () => net.connect(serverPath), - () => net.createConnection(serverPath), - () => new net.Socket().connect(serverPath), - () => net.connect({ path: serverPath }), - () => net.createConnection({ path: serverPath }), - () => new net.Socket().connect({ path: serverPath }) - ]; - - Promise.all(connectPromises.map(connectAndTest)) - .then(() => { - expect(connectionsCount).toBe(CLIENT_VARIANTS / 2); // We're testing 6 variants instead of 12 - server.close(() => { - done(); - }); - }) - .catch((err) => { - done(err); - }); - }); - }); -}); - -//<#END_FILE: test-net-connect-options-path.js diff --git a/test/js/node/test/parallel/net-connect-paused-connection.test.js b/test/js/node/test/parallel/net-connect-paused-connection.test.js deleted file mode 100644 index 6bfc5f554a..0000000000 --- a/test/js/node/test/parallel/net-connect-paused-connection.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-net-connect-paused-connection.js -//#SHA1: ab2fae629f3abb5fc4d5e59dd7d1dd2e09b9eb48 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const net = require("net"); - -test("net connect with paused connection", done => { - const server = net.createServer(conn => { - conn.unref(); - }); - - server.listen(0, () => { - const connection = net.connect(server.address().port, "localhost"); - connection.pause(); - - const timeoutId = setTimeout(() => { - done.fail("Should not have called timeout"); - }, 1000); - - // Unref the timeout to allow the process to exit - timeoutId.unref(); - - // Allow some time for the test to potentially fail - setTimeout(() => { - server.close(); - done(); - }, 500); - }); - - server.unref(); -}); - -//<#END_FILE: test-net-connect-paused-connection.js diff --git a/test/js/node/test/parallel/net-connect-reset-until-connected.test.js b/test/js/node/test/parallel/net-connect-reset-until-connected.test.js deleted file mode 100644 index 7e2e77698b..0000000000 --- a/test/js/node/test/parallel/net-connect-reset-until-connected.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-net-connect-reset-until-connected.js -//#SHA1: b0170103868d4f693e9afda7923e021758393a39 -//----------------- -"use strict"; - -const net = require("net"); - -function barrier(count, cb) { - return function () { - if (--count === 0) cb(); - }; -} - -test("net connection reset until connected", done => { - const server = net.createServer(); - server.listen(0, () => { - const port = server.address().port; - const conn = net.createConnection(port); - const connok = barrier(2, () => conn.resetAndDestroy()); - - conn.on("close", () => { - expect(true).toBe(true); // Ensure 'close' event is called - }); - - server.on("connection", socket => { - connok(); - socket.on("error", err => { - expect(err).toEqual( - expect.objectContaining({ - code: "ECONNRESET", - name: "Error", - message: expect.any(String), - }), - ); - }); - server.close(); - }); - - conn.on("connect", connok); - }); - - // Ensure the test completes - setTimeout(() => { - done(); - }, 1000); -}); - -//<#END_FILE: test-net-connect-reset-until-connected.js diff --git a/test/js/node/test/parallel/net-dns-lookup-skip.test.js b/test/js/node/test/parallel/net-dns-lookup-skip.test.js deleted file mode 100644 index b75771a6cf..0000000000 --- a/test/js/node/test/parallel/net-dns-lookup-skip.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-net-dns-lookup-skip.js -//#SHA1: 023bfbaa998480ab732d83d4bf8efb68ad4fe5db -//----------------- -'use strict'; -const net = require('net'); - -async function checkDnsLookupSkip(addressType) { - return new Promise((resolve, reject) => { - const server = net.createServer((client) => { - client.end(); - server.close(); - }); - - const address = addressType === 4 ? '127.0.0.1' : '::1'; - const lookupSpy = jest.fn(); - - server.listen(0, address, () => { - net.connect(server.address().port, address) - .on('lookup', lookupSpy) - .on('connect', () => { - expect(lookupSpy).not.toHaveBeenCalled(); - resolve(); - }) - .on('error', reject); - }); - }); -} - -test('DNS lookup should be skipped for IPv4', async () => { - await checkDnsLookupSkip(4); -}); - -// Check if the environment supports IPv6 -const hasIPv6 = (() => { - try { - net.createServer().listen(0, '::1').close(); - return true; - } catch { - return false; - } -})(); - -(hasIPv6 ? test : test.skip)('DNS lookup should be skipped for IPv6', async () => { - await checkDnsLookupSkip(6); -}); - -//<#END_FILE: test-net-dns-lookup-skip.js diff --git a/test/js/node/test/parallel/net-during-close.test.js b/test/js/node/test/parallel/net-during-close.test.js deleted file mode 100644 index 9edb7efc92..0000000000 --- a/test/js/node/test/parallel/net-during-close.test.js +++ /dev/null @@ -1,52 +0,0 @@ -//#FILE: test-net-during-close.js -//#SHA1: c5fc5c85760b2c68679f7041ebf737e8204ca8c5 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const net = require("net"); - -test("accessing client properties during server close", done => { - const server = net.createServer(socket => { - socket.end(); - }); - - server.listen(0, () => { - const client = net.createConnection(server.address().port); - server.close(); - - // Server connection event has not yet fired client is still attempting to - // connect. Accessing properties should not throw in this case. - expect(() => { - /* eslint-disable no-unused-expressions */ - client.remoteAddress; - client.remoteFamily; - client.remotePort; - /* eslint-enable no-unused-expressions */ - }).not.toThrow(); - - // Exit now, do not wait for the client error event. - done(); - }); -}); - -//<#END_FILE: test-net-during-close.js diff --git a/test/js/node/test/parallel/net-end-close.test.js b/test/js/node/test/parallel/net-end-close.test.js deleted file mode 100644 index 10d17c8c07..0000000000 --- a/test/js/node/test/parallel/net-end-close.test.js +++ /dev/null @@ -1,12 +0,0 @@ -//#FILE: test-net-end-close.js -//#SHA1: 01ac4a26e7cb4d477e547f9e6bd2f52a3b0d9277 -//----------------- -"use strict"; - -test.skip("net Socket end and close events", () => { - console.log( - "This test relies on internal Node.js APIs and cannot be accurately replicated in a cross-platform manner.", - ); -}); - -//<#END_FILE: test-net-end-close.js diff --git a/test/js/node/test/parallel/net-end-destroyed.test.js b/test/js/node/test/parallel/net-end-destroyed.test.js deleted file mode 100644 index 0ad62bbf17..0000000000 --- a/test/js/node/test/parallel/net-end-destroyed.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-net-end-destroyed.js -//#SHA1: cf219496c5dd2cb11f3d01750692b61791c7e2f9 -//----------------- -"use strict"; - -const net = require("net"); - -test('socket is not destroyed when the "end" event is emitted', done => { - const server = net.createServer(); - - server.on("connection", () => { - // Connection event handler - }); - - // Ensure that the socket is not destroyed when the 'end' event is emitted. - - server.listen(() => { - const socket = net.createConnection({ - port: server.address().port, - }); - - socket.on("connect", () => { - socket.on("end", () => { - expect(socket.destroyed).toBe(false); - server.close(); - done(); - }); - - socket.end(); - }); - }); -}); - -//<#END_FILE: test-net-end-destroyed.js diff --git a/test/js/node/test/parallel/net-end-without-connect.test.js b/test/js/node/test/parallel/net-end-without-connect.test.js deleted file mode 100644 index ffdd987eb0..0000000000 --- a/test/js/node/test/parallel/net-end-without-connect.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-net-end-without-connect.js -//#SHA1: d13d4a7117c5625fec0c619acc024e705dfb4212 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const net = require("net"); - -test("Socket.end() without connect", done => { - const sock = new net.Socket(); - sock.end(() => { - expect(sock.writable).toBe(false); - done(); - }); -}); - -//<#END_FILE: test-net-end-without-connect.js diff --git a/test/js/node/test/parallel/net-isip.test.js b/test/js/node/test/parallel/net-isip.test.js deleted file mode 100644 index d30b722c22..0000000000 --- a/test/js/node/test/parallel/net-isip.test.js +++ /dev/null @@ -1,107 +0,0 @@ -//#FILE: test-net-isip.js -//#SHA1: 5fb15ec330f4e7489c3e7eb2a74547c44aa5a4dc -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const net = require("net"); - -test("net.isIP", () => { - expect(net.isIP("127.0.0.1")).toBe(4); - expect(net.isIP("x127.0.0.1")).toBe(0); - expect(net.isIP("example.com")).toBe(0); - expect(net.isIP("0000:0000:0000:0000:0000:0000:0000:0000")).toBe(6); - expect(net.isIP("0000:0000:0000:0000:0000:0000:0000:0000::0000")).toBe(0); - expect(net.isIP("1050:0:0:0:5:600:300c:326b")).toBe(6); - expect(net.isIP("2001:252:0:1::2008:6")).toBe(6); - expect(net.isIP("2001:dead:beef:1::2008:6")).toBe(6); - expect(net.isIP("2001::")).toBe(6); - expect(net.isIP("2001:dead::")).toBe(6); - expect(net.isIP("2001:dead:beef::")).toBe(6); - expect(net.isIP("2001:dead:beef:1::")).toBe(6); - expect(net.isIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")).toBe(6); - expect(net.isIP(":2001:252:0:1::2008:6:")).toBe(0); - expect(net.isIP(":2001:252:0:1::2008:6")).toBe(0); - expect(net.isIP("2001:252:0:1::2008:6:")).toBe(0); - expect(net.isIP("2001:252::1::2008:6")).toBe(0); - expect(net.isIP("::2001:252:1:2008:6")).toBe(6); - expect(net.isIP("::2001:252:1:1.1.1.1")).toBe(6); - expect(net.isIP("::2001:252:1:255.255.255.255")).toBe(6); - expect(net.isIP("::2001:252:1:255.255.255.255.76")).toBe(0); - expect(net.isIP("fe80::2008%eth0")).toBe(6); - expect(net.isIP("fe80::2008%eth0.0")).toBe(6); - expect(net.isIP("fe80::2008%eth0@1")).toBe(0); - expect(net.isIP("::anything")).toBe(0); - expect(net.isIP("::1")).toBe(6); - expect(net.isIP("::")).toBe(6); - expect(net.isIP("0000:0000:0000:0000:0000:0000:12345:0000")).toBe(0); - expect(net.isIP("0")).toBe(0); - expect(net.isIP()).toBe(0); - expect(net.isIP("")).toBe(0); - expect(net.isIP(null)).toBe(0); - expect(net.isIP(123)).toBe(0); - expect(net.isIP(true)).toBe(0); - expect(net.isIP({})).toBe(0); - expect(net.isIP({ toString: () => "::2001:252:1:255.255.255.255" })).toBe(6); - expect(net.isIP({ toString: () => "127.0.0.1" })).toBe(4); - expect(net.isIP({ toString: () => "bla" })).toBe(0); -}); - -test("net.isIPv4", () => { - expect(net.isIPv4("127.0.0.1")).toBe(true); - expect(net.isIPv4("example.com")).toBe(false); - expect(net.isIPv4("2001:252:0:1::2008:6")).toBe(false); - expect(net.isIPv4()).toBe(false); - expect(net.isIPv4("")).toBe(false); - expect(net.isIPv4(null)).toBe(false); - expect(net.isIPv4(123)).toBe(false); - expect(net.isIPv4(true)).toBe(false); - expect(net.isIPv4({})).toBe(false); - expect( - net.isIPv4({ - toString: () => "::2001:252:1:255.255.255.255", - }), - ).toBe(false); - expect(net.isIPv4({ toString: () => "127.0.0.1" })).toBe(true); - expect(net.isIPv4({ toString: () => "bla" })).toBe(false); -}); - -test("net.isIPv6", () => { - expect(net.isIPv6("127.0.0.1")).toBe(false); - expect(net.isIPv6("example.com")).toBe(false); - expect(net.isIPv6("2001:252:0:1::2008:6")).toBe(true); - expect(net.isIPv6()).toBe(false); - expect(net.isIPv6("")).toBe(false); - expect(net.isIPv6(null)).toBe(false); - expect(net.isIPv6(123)).toBe(false); - expect(net.isIPv6(true)).toBe(false); - expect(net.isIPv6({})).toBe(false); - expect( - net.isIPv6({ - toString: () => "::2001:252:1:255.255.255.255", - }), - ).toBe(true); - expect(net.isIPv6({ toString: () => "127.0.0.1" })).toBe(false); - expect(net.isIPv6({ toString: () => "bla" })).toBe(false); -}); - -//<#END_FILE: test-net-isip.js diff --git a/test/js/node/test/parallel/net-isipv4.test.js b/test/js/node/test/parallel/net-isipv4.test.js deleted file mode 100644 index d76f97e740..0000000000 --- a/test/js/node/test/parallel/net-isipv4.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-net-isipv4.js -//#SHA1: 2f26a4e5d952b01d6fe2143c1e8f65a19997c974 -//----------------- -"use strict"; - -const net = require("net"); - -const v4 = [ - "0.0.0.0", - "8.8.8.8", - "127.0.0.1", - "100.100.100.100", - "192.168.0.1", - "18.101.25.153", - "123.23.34.2", - "172.26.168.134", - "212.58.241.131", - "128.0.0.0", - "23.71.254.72", - "223.255.255.255", - "192.0.2.235", - "99.198.122.146", - "46.51.197.88", - "173.194.34.134", -]; - -const v4not = [ - ".100.100.100.100", - "100..100.100.100.", - "100.100.100.100.", - "999.999.999.999", - "256.256.256.256", - "256.100.100.100.100", - "123.123.123", - "http://123.123.123", - "1000.2.3.4", - "999.2.3.4", - "0000000192.168.0.200", - "192.168.0.2000000000", -]; - -test("net.isIPv4 correctly identifies valid IPv4 addresses", () => { - for (const ip of v4) { - expect(net.isIPv4(ip)).toBe(true); - } -}); - -test("net.isIPv4 correctly identifies invalid IPv4 addresses", () => { - for (const ip of v4not) { - expect(net.isIPv4(ip)).toBe(false); - } -}); - -//<#END_FILE: test-net-isipv4.js diff --git a/test/js/node/test/parallel/net-isipv6.test.js b/test/js/node/test/parallel/net-isipv6.test.js deleted file mode 100644 index 62d06daf6b..0000000000 --- a/test/js/node/test/parallel/net-isipv6.test.js +++ /dev/null @@ -1,254 +0,0 @@ -//#FILE: test-net-isipv6.js -//#SHA1: 4c6eea8203876ea65c4496732ee169a8fe0eb574 -//----------------- -"use strict"; - -const net = require("net"); - -const v6 = [ - "::", - "1::", - "::1", - "1::8", - "1::7:8", - "1:2:3:4:5:6:7:8", - "1:2:3:4:5:6::8", - "1:2:3:4:5:6:7::", - "1:2:3:4:5::7:8", - "1:2:3:4:5::8", - "1:2:3::8", - "1::4:5:6:7:8", - "1::6:7:8", - "1::3:4:5:6:7:8", - "1:2:3:4::6:7:8", - "1:2::4:5:6:7:8", - "::2:3:4:5:6:7:8", - "1:2::8", - "2001:0000:1234:0000:0000:C1C0:ABCD:0876", - "3ffe:0b00:0000:0000:0001:0000:0000:000a", - "FF02:0000:0000:0000:0000:0000:0000:0001", - "0000:0000:0000:0000:0000:0000:0000:0001", - "0000:0000:0000:0000:0000:0000:0000:0000", - "::ffff:192.168.1.26", - "2::10", - "ff02::1", - "fe80::", - "2002::", - "2001:db8::", - "2001:0db8:1234::", - "::ffff:0:0", - "::ffff:192.168.1.1", - "1:2:3:4::8", - "1::2:3:4:5:6:7", - "1::2:3:4:5:6", - "1::2:3:4:5", - "1::2:3:4", - "1::2:3", - "::2:3:4:5:6:7", - "::2:3:4:5:6", - "::2:3:4:5", - "::2:3:4", - "::2:3", - "::8", - "1:2:3:4:5:6::", - "1:2:3:4:5::", - "1:2:3:4::", - "1:2:3::", - "1:2::", - "1:2:3:4::7:8", - "1:2:3::7:8", - "1:2::7:8", - "1:2:3:4:5:6:1.2.3.4", - "1:2:3:4:5::1.2.3.4", - "1:2:3:4::1.2.3.4", - "1:2:3::1.2.3.4", - "1:2::1.2.3.4", - "1::1.2.3.4", - "1:2:3:4::5:1.2.3.4", - "1:2:3::5:1.2.3.4", - "1:2::5:1.2.3.4", - "1::5:1.2.3.4", - "1::5:11.22.33.44", - "fe80::217:f2ff:254.7.237.98", - "fe80::217:f2ff:fe07:ed62", - "2001:DB8:0:0:8:800:200C:417A", - "FF01:0:0:0:0:0:0:101", - "0:0:0:0:0:0:0:1", - "0:0:0:0:0:0:0:0", - "2001:DB8::8:800:200C:417A", - "FF01::101", - "0:0:0:0:0:0:13.1.68.3", - "0:0:0:0:0:FFFF:129.144.52.38", - "::13.1.68.3", - "::FFFF:129.144.52.38", - "fe80:0000:0000:0000:0204:61ff:fe9d:f156", - "fe80:0:0:0:204:61ff:fe9d:f156", - "fe80::204:61ff:fe9d:f156", - "fe80:0:0:0:204:61ff:254.157.241.86", - "fe80::204:61ff:254.157.241.86", - "fe80::1", - "2001:0db8:85a3:0000:0000:8a2e:0370:7334", - "2001:db8:85a3:0:0:8a2e:370:7334", - "2001:db8:85a3::8a2e:370:7334", - "2001:0db8:0000:0000:0000:0000:1428:57ab", - "2001:0db8:0000:0000:0000::1428:57ab", - "2001:0db8:0:0:0:0:1428:57ab", - "2001:0db8:0:0::1428:57ab", - "2001:0db8::1428:57ab", - "2001:db8::1428:57ab", - "::ffff:12.34.56.78", - "::ffff:0c22:384e", - "2001:0db8:1234:0000:0000:0000:0000:0000", - "2001:0db8:1234:ffff:ffff:ffff:ffff:ffff", - "2001:db8:a::123", - "::ffff:192.0.2.128", - "::ffff:c000:280", - "a:b:c:d:e:f:f1:f2", - "a:b:c::d:e:f:f1", - "a:b:c::d:e:f", - "a:b:c::d:e", - "a:b:c::d", - "::a", - "::a:b:c", - "::a:b:c:d:e:f:f1", - "a::", - "a:b:c::", - "a:b:c:d:e:f:f1::", - "a:bb:ccc:dddd:000e:00f:0f::", - "0:a:0:a:0:0:0:a", - "0:a:0:0:a:0:0:a", - "2001:db8:1:1:1:1:0:0", - "2001:db8:1:1:1:0:0:0", - "2001:db8:1:1:0:0:0:0", - "2001:db8:1:0:0:0:0:0", - "2001:db8:0:0:0:0:0:0", - "2001:0:0:0:0:0:0:0", - "A:BB:CCC:DDDD:000E:00F:0F::", - "0:0:0:0:0:0:0:a", - "0:0:0:0:a:0:0:0", - "0:0:0:a:0:0:0:0", - "a:0:0:a:0:0:a:a", - "a:0:0:a:0:0:0:a", - "a:0:0:0:a:0:0:a", - "a:0:0:0:a:0:0:0", - "a:0:0:0:0:0:0:0", - "fe80::7:8%eth0", - "fe80::7:8%1", -]; - -const v6not = [ - "", - "1:", - ":1", - "11:36:12", - "02001:0000:1234:0000:0000:C1C0:ABCD:0876", - "2001:0000:1234:0000:00001:C1C0:ABCD:0876", - "2001:0000:1234: 0000:0000:C1C0:ABCD:0876", - "2001:1:1:1:1:1:255Z255X255Y255", - "3ffe:0b00:0000:0001:0000:0000:000a", - "FF02:0000:0000:0000:0000:0000:0000:0000:0001", - "3ffe:b00::1::a", - "::1111:2222:3333:4444:5555:6666::", - "1:2:3::4:5::7:8", - "12345::6:7:8", - "1::5:400.2.3.4", - "1::5:260.2.3.4", - "1::5:256.2.3.4", - "1::5:1.256.3.4", - "1::5:1.2.256.4", - "1::5:1.2.3.256", - "1::5:300.2.3.4", - "1::5:1.300.3.4", - "1::5:1.2.300.4", - "1::5:1.2.3.300", - "1::5:900.2.3.4", - "1::5:1.900.3.4", - "1::5:1.2.900.4", - "1::5:1.2.3.900", - "1::5:300.300.300.300", - "1::5:3000.30.30.30", - "1::400.2.3.4", - "1::260.2.3.4", - "1::256.2.3.4", - "1::1.256.3.4", - "1::1.2.256.4", - "1::1.2.3.256", - "1::300.2.3.4", - "1::1.300.3.4", - "1::1.2.300.4", - "1::1.2.3.300", - "1::900.2.3.4", - "1::1.900.3.4", - "1::1.2.900.4", - "1::1.2.3.900", - "1::300.300.300.300", - "1::3000.30.30.30", - "::400.2.3.4", - "::260.2.3.4", - "::256.2.3.4", - "::1.256.3.4", - "::1.2.256.4", - "::1.2.3.256", - "::300.2.3.4", - "::1.300.3.4", - "::1.2.300.4", - "::1.2.3.300", - "::900.2.3.4", - "::1.900.3.4", - "::1.2.900.4", - "::1.2.3.900", - "::300.300.300.300", - "::3000.30.30.30", - "2001:DB8:0:0:8:800:200C:417A:221", - "FF01::101::2", - "1111:2222:3333:4444::5555:", - "1111:2222:3333::5555:", - "1111:2222::5555:", - "1111::5555:", - "::5555:", - ":::", - "1111:", - ":", - ":1111:2222:3333:4444::5555", - ":1111:2222:3333::5555", - ":1111:2222::5555", - ":1111::5555", - ":::5555", - "1.2.3.4:1111:2222:3333:4444::5555", - "1.2.3.4:1111:2222:3333::5555", - "1.2.3.4:1111:2222::5555", - "1.2.3.4:1111::5555", - "1.2.3.4::5555", - "1.2.3.4::", - "fe80:0000:0000:0000:0204:61ff:254.157.241.086", - "123", - "ldkfj", - "2001::FFD3::57ab", - "2001:db8:85a3::8a2e:37023:7334", - "2001:db8:85a3::8a2e:370k:7334", - "1:2:3:4:5:6:7:8:9", - "1::2::3", - "1:::3:4:5", - "1:2:3::4:5:6:7:8:9", - "::ffff:2.3.4", - "::ffff:257.1.2.3", - "::ffff:12345678901234567890.1.26", - "2001:0000:1234:0000:0000:C1C0:ABCD:0876 0", - "02001:0000:1234:0000:0000:C1C0:ABCD:0876", -]; - -describe("net.isIPv6", () => { - test("valid IPv6 addresses", () => { - for (const ip of v6) { - expect(net.isIPv6(ip)).toBe(true); - } - }); - - test("invalid IPv6 addresses", () => { - for (const ip of v6not) { - expect(net.isIPv6(ip)).toBe(false); - } - }); -}); - -//<#END_FILE: test-net-isipv6.js diff --git a/test/js/node/test/parallel/net-keepalive.test.js b/test/js/node/test/parallel/net-keepalive.test.js deleted file mode 100644 index 2b875ceb20..0000000000 --- a/test/js/node/test/parallel/net-keepalive.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-net-keepalive.js -//#SHA1: 822f2eb57a17abc64e2664803a4ac69430e5b035 -//----------------- -"use strict"; - -const net = require("net"); - -describe("net keepalive", () => { - test("should maintain connection", async () => { - let serverConnection; - let clientConnection; - - const { promise, resolve, reject } = Promise.withResolvers(); - function done(err) { - clientConnection.destroy(); - echoServer.close(); - if (err) reject(err); - else resolve(); - } - - const echoServer = net.createServer(connection => { - serverConnection = connection; - connection.setTimeout(0); - try { - expect(connection.setKeepAlive).toBeDefined(); - } catch (err) { - done(err); - return; - } - connection.setKeepAlive(true, 50); - connection.on("end", () => { - connection.end(); - }); - }); - - echoServer.listen(0, () => { - clientConnection = net.createConnection(echoServer.address().port, "127.0.0.1"); - clientConnection.setTimeout(0); - clientConnection.on("connect", () => { - setTimeout(() => { - try { - expect(serverConnection.readyState).toBe("open"); - expect(clientConnection.readyState).toBe("open"); - done(); - } catch (err) { - done(err); - } - }, 100); - }); - }); - - await promise; - }); -}); - -//<#END_FILE: test-net-keepalive.js diff --git a/test/js/node/test/parallel/net-large-string.test.js b/test/js/node/test/parallel/net-large-string.test.js deleted file mode 100644 index e69dd073d4..0000000000 --- a/test/js/node/test/parallel/net-large-string.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-net-large-string.js -//#SHA1: d823932009345f5d651ca02b7ddbba67057a423b -//----------------- -"use strict"; -const net = require("net"); - -const kPoolSize = 40 * 1024; -const data = "あ".repeat(kPoolSize); -const encoding = "UTF-8"; - -test("net large string", done => { - const server = net.createServer(socket => { - let receivedSize = 0; - socket.setEncoding(encoding); - socket.on("data", chunk => { - receivedSize += chunk.length; - }); - socket.on("end", () => { - expect(receivedSize).toBe(kPoolSize); - socket.end(); - }); - }); - - server.listen(0, () => { - // we connect to the server using 127.0.0.1 to avoid happy eyeballs - const client = net.createConnection(server.address().port, "127.0.0.1"); - client.on("end", () => { - server.close(); - done(); - }); - client.write(data, encoding); - client.end(); - }); -}); - -//<#END_FILE: test-net-large-string.js diff --git a/test/js/node/test/parallel/net-listen-after-destroying-stdin.test.js b/test/js/node/test/parallel/net-listen-after-destroying-stdin.test.js deleted file mode 100644 index 3084568eda..0000000000 --- a/test/js/node/test/parallel/net-listen-after-destroying-stdin.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-net-listen-after-destroying-stdin.js -//#SHA1: 933b6b80e7babac7189e16f85967b75836efea74 -//----------------- -"use strict"; - -// Just test that destroying stdin doesn't mess up listening on a server. -// This is a regression test for -// https://github.com/nodejs/node-v0.x-archive/issues/746. - -const net = require("net"); - -test("destroying stdin does not affect server listening", done => { - process.stdin.destroy(); - - const server = net.createServer(socket => { - console.log("accepted..."); - socket.end(() => { - console.log("finished..."); - expect(true).toBe(true); // Ensure this callback is called - }); - server.close(() => { - console.log("closed"); - expect(true).toBe(true); // Ensure this callback is called - done(); - }); - }); - - server.listen(0, () => { - console.log("listening..."); - expect(server.address().port).toBeGreaterThan(0); - - net.createConnection(server.address().port); - }); -}); - -//<#END_FILE: test-net-listen-after-destroying-stdin.js diff --git a/test/js/node/test/parallel/net-listen-close-server.test.js b/test/js/node/test/parallel/net-listen-close-server.test.js deleted file mode 100644 index 519ae0a2cb..0000000000 --- a/test/js/node/test/parallel/net-listen-close-server.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-net-listen-close-server.js -//#SHA1: e39989e43d691f9cc91a02156112ba681366601b -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const net = require("net"); - -test("server listen and close without calling callbacks", () => { - const server = net.createServer(() => {}); - const listenSpy = jest.fn(); - const errorSpy = jest.fn(); - - server.listen(0, listenSpy); - server.on("error", errorSpy); - server.close(); - - expect(listenSpy).not.toHaveBeenCalled(); - expect(errorSpy).not.toHaveBeenCalled(); -}); - -//<#END_FILE: test-net-listen-close-server.js diff --git a/test/js/node/test/parallel/net-listen-exclusive-random-ports.test.js b/test/js/node/test/parallel/net-listen-exclusive-random-ports.test.js deleted file mode 100644 index 01f8e25506..0000000000 --- a/test/js/node/test/parallel/net-listen-exclusive-random-ports.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-net-listen-exclusive-random-ports.js -//#SHA1: d125e8ff5fd688b5638099581c08c78d91460c59 -//----------------- -'use strict'; - -const net = require('net'); - -describe('Net listen exclusive random ports', () => { - test('should listen on different ports for different servers', async () => { - const createServer = () => { - return new Promise((resolve, reject) => { - const server = net.createServer(() => {}); - server.listen({ - port: 0, - exclusive: true - }, () => { - const port = server.address().port; - resolve({ server, port }); - }); - server.on('error', reject); - }); - }; - - const { server: server1, port: port1 } = await createServer(); - const { server: server2, port: port2 } = await createServer(); - - expect(port1).toBe(port1 | 0); - expect(port2).toBe(port2 | 0); - expect(port1).not.toBe(port2); - - server1.close(); - server2.close(); - }); -}); - -//<#END_FILE: test-net-listen-exclusive-random-ports.js diff --git a/test/js/node/test/parallel/net-listen-handle-in-cluster-2.test.js b/test/js/node/test/parallel/net-listen-handle-in-cluster-2.test.js deleted file mode 100644 index ac5017b087..0000000000 --- a/test/js/node/test/parallel/net-listen-handle-in-cluster-2.test.js +++ /dev/null @@ -1,10 +0,0 @@ -//#FILE: test-net-listen-handle-in-cluster-2.js -//#SHA1: 1902a830aa4f12e7049fc0383e9a919b46aa79dc -//----------------- -'use strict'; - -test.skip('net.listen with handle in cluster (worker)', () => { - console.log('This test is skipped because it relies on Node.js internals and cluster functionality that cannot be accurately replicated in a Jest environment.'); -}); - -//<#END_FILE: test-net-listen-handle-in-cluster-2.js diff --git a/test/js/node/test/parallel/net-listening.test.js b/test/js/node/test/parallel/net-listening.test.js deleted file mode 100644 index ea0b14ab53..0000000000 --- a/test/js/node/test/parallel/net-listening.test.js +++ /dev/null @@ -1,22 +0,0 @@ -//#FILE: test-net-listening.js -//#SHA1: 3c2824f7bd90fec69702e096faba0b4f07353a20 -//----------------- -"use strict"; -const net = require("net"); - -test("Server listening state", done => { - const server = net.createServer(); - - expect(server.listening).toBe(false); - - server.listen(0, () => { - expect(server.listening).toBe(true); - - server.close(() => { - expect(server.listening).toBe(false); - done(); - }); - }); -}); - -//<#END_FILE: test-net-listening.js diff --git a/test/js/node/test/parallel/net-local-address-port.test.js b/test/js/node/test/parallel/net-local-address-port.test.js deleted file mode 100644 index a41661e52b..0000000000 --- a/test/js/node/test/parallel/net-local-address-port.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-net-local-address-port.js -//#SHA1: 9fdb2786eb87ca722138e027be5ee72f04b9909c -//----------------- -"use strict"; -const net = require("net"); - -const localhostIPv4 = "127.0.0.1"; - -describe("Net local address and port", () => { - let server; - let client; - - afterEach(() => { - if (client) { - client.destroy(); - } - if (server && server.listening) { - server.close(); - } - }); - - test("should have correct local address, port, and family", done => { - server = net.createServer(socket => { - expect(socket.localAddress).toBe(localhostIPv4); - expect(socket.localPort).toBe(server.address().port); - expect(socket.localFamily).toBe(server.address().family); - - socket.resume(); - }); - - server.listen(0, localhostIPv4, () => { - client = net.createConnection(server.address().port, localhostIPv4); - client.on("connect", () => { - client.end(); - // We'll end the test here instead of waiting for the server to close - done(); - }); - }); - }); -}); - -//<#END_FILE: test-net-local-address-port.js diff --git a/test/js/node/test/parallel/net-persistent-keepalive.test.js b/test/js/node/test/parallel/net-persistent-keepalive.test.js deleted file mode 100644 index 86b5fbc054..0000000000 --- a/test/js/node/test/parallel/net-persistent-keepalive.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-net-persistent-keepalive.js -//#SHA1: 1428cedddea85130590caec6c04b1939c1f614d4 -//----------------- -"use strict"; -const net = require("net"); - -let serverConnection; -let clientConnection; -let echoServer; -let serverPort; - -beforeAll((done) => { - echoServer = net.createServer((connection) => { - serverConnection = connection; - connection.setTimeout(0); - expect(typeof connection.setKeepAlive).toBe("function"); - connection.on("end", () => { - connection.end(); - }); - }); - - echoServer.listen(0, () => { - serverPort = echoServer.address().port; - done(); - }); -}); - -afterAll((done) => { - if (echoServer) { - echoServer.close(done); - } else { - done(); - } -}); - -test("persistent keepalive", (done) => { - clientConnection = new net.Socket(); - // Send a keepalive packet after 400 ms and make sure it persists - const s = clientConnection.setKeepAlive(true, 400); - expect(s).toBeInstanceOf(net.Socket); - - clientConnection.connect(serverPort, "127.0.0.1"); - clientConnection.setTimeout(0); - - setTimeout(() => { - // Make sure both connections are still open - expect(serverConnection.readyState).toBe("open"); - expect(clientConnection.readyState).toBe("open"); - - serverConnection.end(); - clientConnection.end(); - done(); - }, 600); -}); - -//<#END_FILE: test-net-persistent-keepalive.js diff --git a/test/js/node/test/parallel/net-persistent-nodelay.test.js b/test/js/node/test/parallel/net-persistent-nodelay.test.js deleted file mode 100644 index 87e9db3e82..0000000000 --- a/test/js/node/test/parallel/net-persistent-nodelay.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-net-persistent-nodelay.js -//#SHA1: 48ab228711df09f547aa2ef4abaac2735ef4625b -//----------------- -"use strict"; - -const net = require("net"); - -describe("TCP setNoDelay persistence", () => { - let echoServer; - let originalSetNoDelay; - let callCount; - - beforeAll(() => { - echoServer = net.createServer(connection => { - connection.end(); - }); - }); - - afterAll(() => { - echoServer.close(); - }); - - beforeEach(() => { - callCount = 0; - originalSetNoDelay = net.Socket.prototype.setNoDelay; - net.Socket.prototype.setNoDelay = function (enable) { - const result = originalSetNoDelay.call(this, enable); - callCount++; - return result; - }; - }); - - afterEach(() => { - net.Socket.prototype.setNoDelay = originalSetNoDelay; - }); - - test("setNoDelay is called once when connecting", done => { - echoServer.listen(0, () => { - const sock1 = new net.Socket(); - - // setNoDelay before the handle is created - const s = sock1.setNoDelay(); - expect(s).toBeInstanceOf(net.Socket); - - sock1.connect(echoServer.address().port); - sock1.on("end", () => { - expect(callCount).toBe(1); - done(); - }); - }); - }); -}); - -//<#END_FILE: test-net-persistent-nodelay.js diff --git a/test/js/node/test/parallel/net-persistent-ref-unref.test.js b/test/js/node/test/parallel/net-persistent-ref-unref.test.js deleted file mode 100644 index 58c2a799bc..0000000000 --- a/test/js/node/test/parallel/net-persistent-ref-unref.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-net-persistent-ref-unref.js -//#SHA1: 630ad893713b3c13100743b5e5ae46453adc523e -//----------------- -'use strict'; -const net = require('net'); - -// Mock TCPWrap -const TCPWrap = { - prototype: { - ref: jest.fn(), - unref: jest.fn(), - }, -}; - -let refCount = 0; - -describe('Net persistent ref/unref', () => { - let echoServer; - - beforeAll((done) => { - echoServer = net.createServer((conn) => { - conn.end(); - }); - - TCPWrap.prototype.ref = jest.fn().mockImplementation(function() { - TCPWrap.prototype.ref.mockOriginal.call(this); - refCount++; - expect(refCount).toBe(0); - }); - - TCPWrap.prototype.unref = jest.fn().mockImplementation(function() { - TCPWrap.prototype.unref.mockOriginal.call(this); - refCount--; - expect(refCount).toBe(-1); - }); - - echoServer.listen(0, done); - }); - - afterAll((done) => { - echoServer.close(done); - }); - - test('should maintain correct ref count', (done) => { - const sock = new net.Socket(); - sock.unref(); - sock.ref(); - sock.connect(echoServer.address().port); - sock.on('end', () => { - expect(refCount).toBe(0); - done(); - }); - }); -}); - -//<#END_FILE: test-net-persistent-ref-unref.js diff --git a/test/js/node/test/parallel/net-remote-address.test.js b/test/js/node/test/parallel/net-remote-address.test.js deleted file mode 100644 index ea145b43d3..0000000000 --- a/test/js/node/test/parallel/net-remote-address.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-net-remote-address.js -//#SHA1: a4c7e915b2d6465060b3d283c3cc3906ad629531 -//----------------- -"use strict"; - -const net = require("net"); - -test("remote address behavior", done => { - const server = net.createServer(); - - server.listen(() => { - const socket = net.connect({ port: server.address().port }); - - expect(socket.connecting).toBe(true); - expect(socket.remoteAddress).toBeUndefined(); - - socket.on("connect", () => { - expect(socket.remoteAddress).toBeDefined(); - socket.end(); - }); - - socket.on("end", () => { - server.close(); - done(); - }); - }); -}); - -//<#END_FILE: test-net-remote-address.js diff --git a/test/js/node/test/parallel/net-server-close-before-calling-lookup-callback.test.js b/test/js/node/test/parallel/net-server-close-before-calling-lookup-callback.test.js deleted file mode 100644 index dd3c3b91d0..0000000000 --- a/test/js/node/test/parallel/net-server-close-before-calling-lookup-callback.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-net-server-close-before-calling-lookup-callback.js -//#SHA1: 42a269f691f19c53994f939bacf7e0451f065107 -//----------------- -"use strict"; - -const net = require("net"); - -test("server closes before calling lookup callback", () => { - // Process should exit because it does not create a real TCP server. - // Pass localhost to ensure create TCP handle asynchronously because It causes DNS resolution. - const server = net.createServer(); - - expect(() => { - server.listen(0, "localhost", () => { - throw new Error("This callback should not be called"); - }); - server.close(); - }).not.toThrow(); -}); - -//<#END_FILE: test-net-server-close-before-calling-lookup-callback.js diff --git a/test/js/node/test/parallel/net-server-close-before-ipc-response.test.js b/test/js/node/test/parallel/net-server-close-before-ipc-response.test.js deleted file mode 100644 index 95bba271d2..0000000000 --- a/test/js/node/test/parallel/net-server-close-before-ipc-response.test.js +++ /dev/null @@ -1,16 +0,0 @@ -//#FILE: test-net-server-close-before-ipc-response.js -//#SHA1: 540c9049f49219e9dbcbbd053be54cc2cbd332a0 -//----------------- -'use strict'; - -const net = require('net'); - -describe('Net server close before IPC response', () => { - test.skip('Process should exit', () => { - console.log('This test is skipped because it requires a complex cluster and IPC setup that is difficult to simulate in a Jest environment.'); - console.log('The original test verified that the process exits correctly when a server is closed before an IPC response is received.'); - console.log('To properly test this, we would need to set up a real cluster environment or use a more sophisticated mocking approach.'); - }); -}); - -//<#END_FILE: test-net-server-close-before-ipc-response.js diff --git a/test/js/node/test/parallel/net-server-listen-remove-callback.test.js b/test/js/node/test/parallel/net-server-listen-remove-callback.test.js deleted file mode 100644 index 0aaff47a52..0000000000 --- a/test/js/node/test/parallel/net-server-listen-remove-callback.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-net-server-listen-remove-callback.js -//#SHA1: 031a06bd108815e34b9ebbc3019044daeb8cf8c8 -//----------------- -'use strict'; - -const net = require('net'); - -let server; - -beforeEach(() => { - server = net.createServer(); -}); - -afterEach((done) => { - if (server.listening) { - server.close(done); - } else { - done(); - } -}); - -test('Server should only fire listen callback once', (done) => { - server.on('close', () => { - const listeners = server.listeners('listening'); - console.log('Closed, listeners:', listeners.length); - expect(listeners.length).toBe(0); - }); - - server.listen(0, () => { - server.close(); - }); - - server.once('close', () => { - server.listen(0, () => { - server.close(done); - }); - }); -}); - -//<#END_FILE: test-net-server-listen-remove-callback.js diff --git a/test/js/node/test/parallel/net-server-unref-persistent.test.js b/test/js/node/test/parallel/net-server-unref-persistent.test.js deleted file mode 100644 index add3449f2b..0000000000 --- a/test/js/node/test/parallel/net-server-unref-persistent.test.js +++ /dev/null @@ -1,13 +0,0 @@ -//#FILE: test-net-server-unref-persistent.js -//#SHA1: 4b518c58827ac05dd5c3746c8a0811181184b945 -//----------------- -'use strict'; -const net = require('net'); - -test.skip('net server unref should be persistent', () => { - // This test is skipped in Jest because it relies on Node.js-specific event loop behavior - // that can't be accurately simulated in a Jest environment. - // The original test should be kept in Node.js's test suite. -}); - -//<#END_FILE: test-net-server-unref-persistent.js diff --git a/test/js/node/test/parallel/net-server-unref.test.js b/test/js/node/test/parallel/net-server-unref.test.js deleted file mode 100644 index 2067d6cbb9..0000000000 --- a/test/js/node/test/parallel/net-server-unref.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-net-server-unref.js -//#SHA1: bb2f989bf01182d804d6a8a0d0f33950f357c617 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const net = require("net"); - -test("net server unref", () => { - const s = net.createServer(); - s.listen(0); - s.unref(); - - const mockCallback = jest.fn(); - setTimeout(mockCallback, 1000).unref(); - - expect(mockCallback).not.toHaveBeenCalled(); -}); - -//<#END_FILE: test-net-server-unref.js diff --git a/test/js/node/test/parallel/net-settimeout.test.js b/test/js/node/test/parallel/net-settimeout.test.js deleted file mode 100644 index b766196ac8..0000000000 --- a/test/js/node/test/parallel/net-settimeout.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-net-settimeout.js -//#SHA1: 24fde10dfba0d555d2a61853374866b370e40edf -//----------------- -'use strict'; - -const net = require('net'); - -const T = 100; - -let server; -let serverPort; - -beforeAll((done) => { - server = net.createServer((c) => { - c.write('hello'); - }); - - server.listen(0, () => { - serverPort = server.address().port; - done(); - }); -}); - -afterAll((done) => { - server.close(done); -}); - -test('setTimeout and immediate clearTimeout', (done) => { - const socket = net.createConnection(serverPort, 'localhost'); - - const timeoutCallback = jest.fn(); - const s = socket.setTimeout(T, timeoutCallback); - expect(s).toBeInstanceOf(net.Socket); - - socket.on('data', () => { - setTimeout(() => { - socket.destroy(); - expect(timeoutCallback).not.toHaveBeenCalled(); - done(); - }, T * 2); - }); - - socket.setTimeout(0); -}); - -//<#END_FILE: test-net-settimeout.js diff --git a/test/js/node/test/parallel/net-socket-close-after-end.test.js b/test/js/node/test/parallel/net-socket-close-after-end.test.js deleted file mode 100644 index d0d744a6e3..0000000000 --- a/test/js/node/test/parallel/net-socket-close-after-end.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-net-socket-close-after-end.js -//#SHA1: d3abfad3599a4245fb35f5589c55bb56a43ca3f7 -//----------------- -"use strict"; - -const net = require("net"); - -test('socket emits "end" before "close"', done => { - const server = net.createServer(); - - server.on("connection", socket => { - let endEmitted = false; - - socket.once("readable", () => { - setTimeout(() => { - socket.read(); - }, 100); - }); - - socket.on("end", () => { - endEmitted = true; - }); - - socket.on("close", () => { - expect(endEmitted).toBe(true); - server.close(); - done(); - }); - - socket.end("foo"); - }); - - server.listen(() => { - const socket = net.createConnection(server.address().port, () => { - socket.end("foo"); - }); - }); -}); - -//<#END_FILE: test-net-socket-close-after-end.js diff --git a/test/js/node/test/parallel/net-socket-connect-without-cb.test.js b/test/js/node/test/parallel/net-socket-connect-without-cb.test.js deleted file mode 100644 index 3f47475afc..0000000000 --- a/test/js/node/test/parallel/net-socket-connect-without-cb.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-net-socket-connect-without-cb.js -//#SHA1: 2441c4dfe4351f2e9a02cd08df36e4703096864a -//----------------- -"use strict"; - -const net = require("net"); - -// This test ensures that socket.connect can be called without callback -// which is optional. - -test("socket.connect without callback", done => { - const server = net - .createServer(conn => { - conn.end(); - server.close(); - }) - .listen(0, () => { - const client = new net.Socket(); - - client.on("connect", () => { - client.end(); - done(); - }); - - const address = server.address(); - if (process.version.startsWith("v") && !process.versions.bun && address.family === "IPv6") { - // Necessary to pass CI running inside containers. - client.connect(address.port); - } else { - client.connect(address); - } - }); - - expect(server).toBeDefined(); -}); - -//<#END_FILE: test-net-socket-connect-without-cb.js diff --git a/test/js/node/test/parallel/net-socket-destroy-twice.test.js b/test/js/node/test/parallel/net-socket-destroy-twice.test.js deleted file mode 100644 index cc8a7ecaf2..0000000000 --- a/test/js/node/test/parallel/net-socket-destroy-twice.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-net-socket-destroy-twice.js -//#SHA1: b9066749198a610e24f0b75c017f00abb3c70bfc -//----------------- -"use strict"; - -const net = require("net"); - -describe("Net socket destroy twice", () => { - let server; - let port; - - beforeAll((done) => { - server = net.createServer(); - server.listen(0, () => { - port = server.address().port; - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("should handle destroying a socket twice", (done) => { - const conn = net.createConnection(port, "127.0.0.1"); - - let errorCalled = 0; - conn.on("error", () => { - errorCalled++; - conn.destroy(); - }); - - conn.on("close", () => { - expect(errorCalled).toBe(1); - done(); - }); - - // Trigger an error by closing the server - server.close(); - }); -}); - -//<#END_FILE: test-net-socket-destroy-twice.js diff --git a/test/js/node/test/parallel/net-socket-end-before-connect.test.js b/test/js/node/test/parallel/net-socket-end-before-connect.test.js deleted file mode 100644 index d27dfd7d46..0000000000 --- a/test/js/node/test/parallel/net-socket-end-before-connect.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-net-socket-end-before-connect.js -//#SHA1: e09a7492b07dfa5467171563408395f653e9b032 -//----------------- -'use strict'; - -const net = require('net'); - -test('Socket ends before connect', (done) => { - const server = net.createServer(); - - server.listen(() => { - const socket = net.createConnection(server.address().port, "127.0.0.1"); - - const closeHandler = function() { - server.close(); - done(); - } - socket.on('close', closeHandler); - socket.end(); - }); -}); - -//<#END_FILE: test-net-socket-end-before-connect.js diff --git a/test/js/node/test/parallel/net-socket-ready-without-cb.test.js b/test/js/node/test/parallel/net-socket-ready-without-cb.test.js deleted file mode 100644 index d22eac4d22..0000000000 --- a/test/js/node/test/parallel/net-socket-ready-without-cb.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-net-socket-ready-without-cb.js -//#SHA1: 2f6be9472163372bcd602f547bd709b27a2baad6 -//----------------- -'use strict'; - -const net = require('net'); - -test('socket.connect can be called without callback', (done) => { - const server = net.createServer((conn) => { - conn.end(); - server.close(); - }); - - server.listen(0, 'localhost', () => { - const client = new net.Socket(); - - client.on('ready', () => { - client.end(); - done(); - }); - - client.connect(server.address()); - }); -}); - -//<#END_FILE: test-net-socket-ready-without-cb.js diff --git a/test/js/node/test/parallel/net-socket-reset-twice.test.js b/test/js/node/test/parallel/net-socket-reset-twice.test.js deleted file mode 100644 index 10adfdc49d..0000000000 --- a/test/js/node/test/parallel/net-socket-reset-twice.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-net-socket-reset-twice.js -//#SHA1: 70cb2037a6385ada696f8b9f8fa66a0b111275c4 -//----------------- -"use strict"; -const net = require("net"); - -let server; -let port; - -beforeAll((done) => { - server = net.createServer(); - server.listen(0, () => { - port = server.address().port; - done(); - }); -}); - -afterAll(() => { - server.close(); -}); - -test("net socket reset twice", (done) => { - const conn = net.createConnection(port, "127.0.0.1"); - - const errorHandler = jest.fn(() => { - conn.resetAndDestroy(); - }); - - conn.on("error", errorHandler); - - const closeHandler = jest.fn(() => { - expect(errorHandler).toHaveBeenCalled(); - expect(closeHandler).toHaveBeenCalled(); - done(); - }); - - conn.on("close", closeHandler); - - // Trigger the error event - server.close(); -}); - -//<#END_FILE: test-net-socket-reset-twice.js diff --git a/test/js/node/test/parallel/net-socket-timeout-unref.test.js b/test/js/node/test/parallel/net-socket-timeout-unref.test.js deleted file mode 100644 index 5e05c5cf1c..0000000000 --- a/test/js/node/test/parallel/net-socket-timeout-unref.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-net-socket-timeout-unref.js -//#SHA1: 1583fd33473989bba11fead2493c70a79d9ff48e -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -// Test that unref'ed sockets with timeouts do not prevent exit. - -const net = require("net"); - -test("unref'ed sockets with timeouts do not prevent exit", () => { - const server = net.createServer(c => { - c.write("hello"); - c.unref(); - }); - server.listen(0); - server.unref(); - - let connections = 0; - const sockets = []; - const delays = [8, 5, 3, 6, 2, 4]; - - delays.forEach(T => { - const socket = net.createConnection(server.address().port, "localhost"); - socket.on("connect", () => { - if (++connections === delays.length) { - sockets.forEach(s => { - s.socket.setTimeout(s.timeout, () => { - s.socket.destroy(); - throw new Error("socket timed out unexpectedly"); - }); - - s.socket.unref(); - }); - } - }); - - sockets.push({ socket: socket, timeout: T * 1000 }); - }); - - // We don't need to explicitly assert anything here. - // The test will pass if the process exits without throwing an error. -}); - -//<#END_FILE: test-net-socket-timeout-unref.js diff --git a/test/js/node/test/parallel/net-socket-write-error.test.js b/test/js/node/test/parallel/net-socket-write-error.test.js deleted file mode 100644 index 56b8b5634f..0000000000 --- a/test/js/node/test/parallel/net-socket-write-error.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-net-socket-write-error.js -//#SHA1: a69bb02fc98fc265ad23ff03e7ae16e9c984202d -//----------------- -"use strict"; - -const net = require("net"); - -describe("Net Socket Write Error", () => { - let server; - - beforeAll(done => { - server = net.createServer().listen(0, () => { - done(); - }); - }); - - afterAll(() => { - server.close(); - }); - - test("should throw TypeError when writing non-string/buffer", done => { - const client = net.createConnection(server.address().port, () => { - client.on("error", () => { - done.fail("Client should not emit error"); - }); - - expect(() => { - client.write(1337); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - - client.destroy(); - done(); - }); - - client.on("close", () => { - // This ensures the server closes after the client disconnects - server.close(); - }); - }); -}); - -//<#END_FILE: test-net-socket-write-error.js diff --git a/test/js/node/test/parallel/net-stream.test.js b/test/js/node/test/parallel/net-stream.test.js deleted file mode 100644 index bbfca1ad3e..0000000000 --- a/test/js/node/test/parallel/net-stream.test.js +++ /dev/null @@ -1,58 +0,0 @@ -//#FILE: test-net-stream.js -//#SHA1: 3682dee1fcd1fea4f59bbad200ab1476e0f49bda -//----------------- -"use strict"; - -const net = require("net"); -const { once } = require("events"); -const SIZE = 2e6; -const N = 10; -const buf = Buffer.alloc(SIZE, "a"); -//TODO: need to check how to handle error on close events properly -test.skip("net stream behavior", async () => { - let server; - try { - const { promise, resolve: done } = Promise.withResolvers(); - - server = net.createServer(socket => { - socket.setNoDelay(); - - let onErrorCalls = 0; - let onCloseCalls = 0; - socket - .on("error", () => { - onErrorCalls++; - socket.destroy(); - }) - .on("close", () => { - onCloseCalls++; - done({ onErrorCalls, onCloseCalls }); - }); - - for (let i = 0; i < N; ++i) { - socket.write(buf, () => {}); - } - - socket.end(); - }); - await once(server.listen(0), "listening"); - - const conn = net.connect(server.address().port, "127.0.0.1"); - const { promise: dataPromise, resolve: dataResolve } = Promise.withResolvers(); - conn.on("data", buf => { - dataResolve(conn.pause()); - setTimeout(() => { - conn.destroy(); - }, 20); - }); - expect(await dataPromise).toBe(conn); - - const { onCloseCalls, onErrorCalls } = await promise; - expect(onErrorCalls).toBeGreaterThan(0); - expect(onCloseCalls).toBeGreaterThan(0); - } finally { - server.close(); - } -}); - -//<#END_FILE: test-net-stream.js diff --git a/test/js/node/test/parallel/net-sync-cork.test.js b/test/js/node/test/parallel/net-sync-cork.test.js deleted file mode 100644 index bc0c4524fd..0000000000 --- a/test/js/node/test/parallel/net-sync-cork.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-net-sync-cork.js -//#SHA1: baf95df782bcb1c53ea0118e8e47e93d63cf4262 -//----------------- -"use strict"; - -const net = require("net"); - -const N = 100; -const buf = Buffer.alloc(2, "a"); - -let server; - -beforeAll(done => { - server = net.createServer(handle); - server.listen(0, done); -}); - -afterAll(() => { - server.close(); -}); - -test("net sync cork", done => { - const conn = net.connect(server.address().port); - - conn.on("connect", () => { - let res = true; - let i = 0; - for (; i < N && res; i++) { - conn.cork(); - conn.write(buf); - res = conn.write(buf); - conn.uncork(); - } - expect(i).toBe(N); - conn.end(); - }); - - conn.on("close", done); -}); - -function handle(socket) { - socket.resume(); - socket.on("error", () => { - throw new Error("Socket error should not occur"); - }); - socket.on("close", () => { - // This is called when the connection is closed - }); -} - -//<#END_FILE: test-net-sync-cork.js diff --git a/test/js/node/test/parallel/net-throttle.test.js b/test/js/node/test/parallel/net-throttle.test.js deleted file mode 100644 index b33fc01bea..0000000000 --- a/test/js/node/test/parallel/net-throttle.test.js +++ /dev/null @@ -1,78 +0,0 @@ -//#FILE: test-net-throttle.js -//#SHA1: 5c09d0b1c174ba1f88acae8d731c039ae7c3fc99 -//----------------- -"use strict"; - -const net = require("net"); -const { debuglog } = require("util"); - -const debug = debuglog("test"); - -let chars_recved = 0; -let npauses = 0; -let totalLength = 0; -let server; - -beforeAll(done => { - server = net.createServer(connection => { - const body = "C".repeat(1024); - let n = 1; - debug("starting write loop"); - while (connection.write(body)) { - n++; - } - debug("ended write loop"); - // Now that we're throttled, do some more writes to make sure the data isn't - // lost. - connection.write(body); - connection.write(body); - n += 2; - totalLength = n * body.length; - expect(connection.bufferSize).toBeGreaterThanOrEqual(0); - expect(connection.writableLength).toBeLessThanOrEqual(totalLength); - connection.end(); - }); - - server.listen(0, () => { - debug(`server started on port ${server.address().port}`); - done(); - }); -}); - -afterAll(done => { - server.close(done); -}); - -test("net throttle", done => { - const port = server.address().port; - let paused = false; - const client = net.createConnection(port, "127.0.0.1"); - client.setEncoding("ascii"); - - client.on("data", d => { - chars_recved += d.length; - debug(`got ${chars_recved}`); - if (!paused) { - client.pause(); - npauses += 1; - paused = true; - debug("pause"); - const x = chars_recved; - setTimeout(() => { - expect(chars_recved).toBe(x); - client.resume(); - debug("resume"); - paused = false; - }, 100); - } - }); - - client.on("end", () => { - client.end(); - expect(chars_recved).toBe(totalLength); - expect(npauses).toBeGreaterThan(1); - done(); - }); -}); - -//<#END_FILE: test-net-throttle.js diff --git a/test/js/node/test/parallel/net-writable.test.js b/test/js/node/test/parallel/net-writable.test.js deleted file mode 100644 index 60f10d743b..0000000000 --- a/test/js/node/test/parallel/net-writable.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-net-writable.js -//#SHA1: dfbbbc883e83311b16b93fc9e06d214552cb6448 -//----------------- -"use strict"; - -const net = require("net"); - -test("net writable after end event", done => { - const server = net.createServer(s => { - server.close(); - s.end(); - }); - - server.listen(0, "127.0.0.1", () => { - const socket = net.connect(server.address().port, "127.0.0.1"); - socket.on("end", () => { - expect(socket.writable).toBe(true); - socket.write("hello world"); - done(); - }); - }); - - expect.assertions(1); -}); - -//<#END_FILE: test-net-writable.js diff --git a/test/js/node/test/parallel/net-write-after-close.test.js b/test/js/node/test/parallel/net-write-after-close.test.js deleted file mode 100644 index 8aacf621b9..0000000000 --- a/test/js/node/test/parallel/net-write-after-close.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-net-write-after-close.js -//#SHA1: fe97d63608f4e6651247e83071c81800a6de2ee6 -//----------------- -"use strict"; - -const net = require("net"); - -test("write after close", async () => { - const { promise, resolve } = Promise.withResolvers(); - const { promise: writePromise, resolve: writeResolve } = Promise.withResolvers(); - let server; - try { - server = net.createServer(socket => { - socket.on("end", () => resolve(socket)); - socket.resume(); - socket.on("error", error => { - throw new Error("Server socket should not emit error"); - }); - }); - - server.listen(0, () => { - const client = net.connect(server.address().port, "127.0.0.1", () => { - client.end(); - }); - }); - (await promise).write("test", writeResolve); - const err = await writePromise; - expect(err).toBeTruthy(); - } finally { - server.close(); - } -}); - -//<#END_FILE: test-net-write-after-close.js diff --git a/test/js/node/test/parallel/net-write-after-end-nt.test.js b/test/js/node/test/parallel/net-write-after-end-nt.test.js deleted file mode 100644 index b3f2e81936..0000000000 --- a/test/js/node/test/parallel/net-write-after-end-nt.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-net-write-after-end-nt.js -//#SHA1: 086a5699d5eff4953af4e9f19757b8489e915579 -//----------------- -"use strict"; -const net = require("net"); - -describe("net.Socket.write() after end", () => { - let server; - let port; - - beforeAll(done => { - server = net - .createServer(socket => { - socket.end(); - }) - .listen(0, () => { - port = server.address().port; - done(); - }); - }); - - afterAll(done => { - server.close(done); - }); - - test("error is emitted in the next tick", done => { - const client = net.connect(port, "127.0.0.1", () => { - let hasError = false; - - client.on("error", err => { - hasError = true; - expect(err).toEqual( - expect.objectContaining({ - code: "EPIPE", - message: "This socket has been ended by the other party", - name: "Error", - }), - ); - done(); - }); - - client.on("end", () => { - const ret = client.write("hello"); - expect(ret).toBe(false); - expect(hasError).toBe(false); - process.nextTick(() => { - expect(hasError).toBe(true); - }); - }); - - client.end(); - }); - }); -}); - -//<#END_FILE: test-net-write-after-end-nt.js diff --git a/test/js/node/test/parallel/net-write-arguments.test.js b/test/js/node/test/parallel/net-write-arguments.test.js deleted file mode 100644 index 347230744d..0000000000 --- a/test/js/node/test/parallel/net-write-arguments.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-net-write-arguments.js -//#SHA1: 2a9ed0086e2675e0e31ef15c1e86b15c47c10c5b -//----------------- -"use strict"; -const net = require("net"); - -test("net.Stream write arguments", () => { - const socket = net.Stream({ highWaterMark: 0 }); - - // Make sure that anything besides a buffer or a string throws. - socket.on("error", jest.fn()); - expect(() => { - socket.write(null); - }).toThrow( - expect.objectContaining({ - code: "ERR_STREAM_NULL_VALUES", - name: "TypeError", - message: expect.any(String), - }), - ); - - [true, false, undefined, 1, 1.0, +Infinity, -Infinity, [], {}].forEach(value => { - const socket = net.Stream({ highWaterMark: 0 }); - // We need to check the callback since 'error' will only - // be emitted once per instance. - expect(() => { - socket.write(value); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-net-write-arguments.js diff --git a/test/js/node/test/parallel/net-write-cb-on-destroy-before-connect.test.js b/test/js/node/test/parallel/net-write-cb-on-destroy-before-connect.test.js deleted file mode 100644 index 5a6b245ff6..0000000000 --- a/test/js/node/test/parallel/net-write-cb-on-destroy-before-connect.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-net-write-cb-on-destroy-before-connect.js -//#SHA1: 49dc0c1780402ca7bc3648f52f821b0ba89eff32 -//----------------- -'use strict'; - -const net = require('net'); - -let server; - -beforeAll((done) => { - server = net.createServer(); - server.listen(0, () => { - done(); - }); -}); - -afterAll((done) => { - server.close(done); -}); - -test('write callback on destroy before connect', (done) => { - const socket = new net.Socket(); - - socket.on('connect', () => { - done('Socket should not connect'); - }); - - socket.connect({ - port: server.address().port, - }, "127.0.0.1"); - - expect(socket.connecting).toBe(true); - - socket.write('foo', (err) => { - expect(err).toEqual(expect.objectContaining({ - code: 'ERR_SOCKET_CLOSED_BEFORE_CONNECTION', - name: 'Error' - })); - done(); - }); - - socket.destroy(); -}); - -//<#END_FILE: test-net-write-cb-on-destroy-before-connect.js diff --git a/test/js/node/test/parallel/net-write-connect-write.test.js b/test/js/node/test/parallel/net-write-connect-write.test.js deleted file mode 100644 index bc959534eb..0000000000 --- a/test/js/node/test/parallel/net-write-connect-write.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-net-write-connect-write.js -//#SHA1: 8d6e9a30cc58bee105db15dc48c8a13c451629be -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const net = require("net"); - -test("net write connect write", async () => { - const server = net.createServer(socket => { - socket.pipe(socket); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const conn = net.connect(server.address().port); - let received = ""; - - conn.setEncoding("utf8"); - conn.write("before"); - - await new Promise(resolve => { - conn.on("connect", () => { - conn.write(" after"); - resolve(); - }); - }); - - await new Promise(resolve => { - conn.on("data", buf => { - received += buf; - conn.end(); - }); - - conn.on("end", () => { - server.close(); - expect(received).toBe("before after"); - resolve(); - }); - }); -}); - -//<#END_FILE: test-net-write-connect-write.js diff --git a/test/js/node/test/parallel/net-write-fully-async-buffer.test.js b/test/js/node/test/parallel/net-write-fully-async-buffer.test.js deleted file mode 100644 index acd0eeb23c..0000000000 --- a/test/js/node/test/parallel/net-write-fully-async-buffer.test.js +++ /dev/null @@ -1,55 +0,0 @@ -//#FILE: test-net-write-fully-async-buffer.js -//#SHA1: b26773ed4c8c5bafaaa8a4513b25d1806a72ae5f -//----------------- -"use strict"; - -const net = require("net"); - -// Note: This test assumes that the --expose-gc flag is available. -// In a Jest environment, you might need to configure this separately. - -const data = Buffer.alloc(1000000); - -let server; - -beforeAll(done => { - server = net - .createServer(conn => { - conn.resume(); - }) - .listen(0, () => { - done(); - }); -}); - -afterAll(() => { - server.close(); -}); - -test("net write fully async buffer", done => { - const conn = net.createConnection(server.address().port, () => { - let count = 0; - - function writeLoop() { - if (count++ === 200) { - conn.destroy(); - done(); - return; - } - - while (conn.write(Buffer.from(data))); - - // Note: global.gc() is not available in standard Jest environments. - // You might need to configure Jest to run with the --expose-gc flag. - // For this test, we'll comment it out, but in a real scenario, you'd need to ensure it's available. - // global.gc({ type: 'minor' }); - // The buffer allocated above should still be alive. - } - - conn.on("drain", writeLoop); - - writeLoop(); - }); -}); - -//<#END_FILE: test-net-write-fully-async-buffer.js diff --git a/test/js/node/test/parallel/net-write-fully-async-hex-string.test.js b/test/js/node/test/parallel/net-write-fully-async-hex-string.test.js deleted file mode 100644 index 64b79e17ed..0000000000 --- a/test/js/node/test/parallel/net-write-fully-async-hex-string.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-net-write-fully-async-hex-string.js -//#SHA1: e5b365bb794f38e7153fc41ebfaf991031f85423 -//----------------- -"use strict"; - -const net = require("net"); - -let server; - -afterAll(() => { - if (server) { - server.close(); - } -}); - -test("net write fully async hex string", done => { - const data = Buffer.alloc(1000000).toString("hex"); - - server = net.createServer(conn => { - conn.resume(); - }); - - server.listen(0, () => { - const conn = net.createConnection(server.address().port, () => { - let count = 0; - - function writeLoop() { - if (count++ === 20) { - conn.destroy(); - done(); - return; - } - while (conn.write(data, "hex")); - // Note: We can't use global.gc in Jest, so we'll skip this part - // global.gc({ type: 'minor' }); - // The buffer allocated inside the .write() call should still be alive. - - // Use setImmediate to allow other operations to occur - setImmediate(writeLoop); - } - - conn.on("drain", writeLoop); - - writeLoop(); - }); - }); -}); - -//<#END_FILE: test-net-write-fully-async-hex-string.js diff --git a/test/js/node/test/parallel/net-write-slow.test.js b/test/js/node/test/parallel/net-write-slow.test.js deleted file mode 100644 index 9ce97d8d39..0000000000 --- a/test/js/node/test/parallel/net-write-slow.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-net-write-slow.js -//#SHA1: ef646d024e2dfcfb07b99fcdfb9ccf2bfbcb6487 -//----------------- -'use strict'; -const net = require('net'); - -const SIZE = 2E5; -const N = 10; -let flushed = 0; -let received = 0; -const buf = Buffer.alloc(SIZE, 'a'); - -let server; - -beforeAll(() => { - return new Promise((resolve) => { - server = net.createServer((socket) => { - socket.setNoDelay(); - socket.setTimeout(9999); - socket.on('timeout', () => { - throw new Error(`flushed: ${flushed}, received: ${received}/${SIZE * N}`); - }); - - for (let i = 0; i < N; ++i) { - socket.write(buf, () => { - ++flushed; - if (flushed === N) { - socket.setTimeout(0); - } - }); - } - socket.end(); - }).listen(0, () => { - resolve(); - }); - }); -}); - -afterAll(() => { - return new Promise((resolve) => { - server.close(resolve); - }); -}); - -test('net write slow', (done) => { - const conn = net.connect(server.address().port); - - conn.on('data', (buf) => { - received += buf.length; - conn.pause(); - setTimeout(() => { - conn.resume(); - }, 20); - }); - - conn.on('end', () => { - expect(received).toBe(SIZE * N); - done(); - }); -}); - -//<#END_FILE: test-net-write-slow.js diff --git a/test/js/node/test/parallel/next-tick-doesnt-hang.test.js b/test/js/node/test/parallel/next-tick-doesnt-hang.test.js deleted file mode 100644 index 23fa936de0..0000000000 --- a/test/js/node/test/parallel/next-tick-doesnt-hang.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-next-tick-doesnt-hang.js -//#SHA1: 6812bb4cd77cd15dd04c4409e34e0c5b605bbb88 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -// This test verifies that having a single nextTick statement and nothing else -// does not hang the event loop. If this test times out it has failed. - -test("nextTick does not hang", done => { - process.nextTick(() => { - // Nothing - done(); - }); -}); - -//<#END_FILE: test-next-tick-doesnt-hang.js diff --git a/test/js/node/test/parallel/next-tick-domain.test.js b/test/js/node/test/parallel/next-tick-domain.test.js deleted file mode 100644 index d975718da7..0000000000 --- a/test/js/node/test/parallel/next-tick-domain.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-next-tick-domain.js -//#SHA1: 75db0e440cbc2eb2fc89b4486eb10e398042a88b -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -test("requiring domain should not change process.nextTick", () => { - const origNextTick = process.nextTick; - - require("domain"); - - // Requiring domain should not change nextTick. - expect(process.nextTick).toBe(origNextTick); -}); - -//<#END_FILE: test-next-tick-domain.js diff --git a/test/js/node/test/parallel/next-tick-ordering.test.js b/test/js/node/test/parallel/next-tick-ordering.test.js deleted file mode 100644 index 3790be15ab..0000000000 --- a/test/js/node/test/parallel/next-tick-ordering.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-next-tick-ordering.js -//#SHA1: 224023554e0ccd7644d70c5acef5bf3a094409ac -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -test("next tick ordering", async () => { - const N = 30; - const done = []; - - function get_printer(timeout) { - return function () { - console.log(`Running from setTimeout ${timeout}`); - done.push(timeout); - }; - } - - process.nextTick(function () { - console.log("Running from nextTick"); - done.push("nextTick"); - }); - - for (let i = 0; i < N; i += 1) { - setTimeout(get_printer(i), i); - } - - console.log("Running from main."); - - // Wait for all timeouts to complete - await new Promise(resolve => setTimeout(resolve, N + 100)); - - expect(done[0]).toBe("nextTick"); - // Disabling this test. I don't think we can ensure the order - // for (let i = 0; i < N; i += 1) { - // expect(done[i + 1]).toBe(i); - // } -}); - -//<#END_FILE: test-next-tick-ordering.js diff --git a/test/js/node/test/parallel/next-tick-ordering2.test.js b/test/js/node/test/parallel/next-tick-ordering2.test.js deleted file mode 100644 index 233df7aed3..0000000000 --- a/test/js/node/test/parallel/next-tick-ordering2.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-next-tick-ordering2.js -//#SHA1: 6aa2e4c01b6ff008c2c2c844cb50a75d45af481d -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -test("nextTick and setTimeout ordering", async () => { - const order = []; - - await new Promise(resolve => { - process.nextTick(() => { - setTimeout(() => { - order.push("setTimeout"); - resolve(); - }, 0); - - process.nextTick(() => { - order.push("nextTick"); - }); - }); - }); - - expect(order).toEqual(["nextTick", "setTimeout"]); -}); - -//<#END_FILE: test-next-tick-ordering2.js diff --git a/test/js/node/test/parallel/next-tick-when-exiting.test.js b/test/js/node/test/parallel/next-tick-when-exiting.test.js deleted file mode 100644 index 1684eebc47..0000000000 --- a/test/js/node/test/parallel/next-tick-when-exiting.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-next-tick-when-exiting.js -//#SHA1: fcca0c205805ee8c1d9e994013d5e3738e3ef6e4 -//----------------- -"use strict"; - -test("process.nextTick should not be called when exiting", () => { - const exitHandler = jest.fn(() => { - expect(process._exiting).toBe(true); - - process.nextTick(jest.fn().mockName("process is exiting, should not be called")); - }); - - process.on("exit", exitHandler); - - process.exit(); - - expect(exitHandler).toHaveBeenCalledTimes(1); - expect(exitHandler.mock.calls[0][0]).toBe(undefined); -}); - -//<#END_FILE: test-next-tick-when-exiting.js diff --git a/test/js/node/test/parallel/no-node-snapshot.test.js b/test/js/node/test/parallel/no-node-snapshot.test.js deleted file mode 100644 index bebaabb1c5..0000000000 --- a/test/js/node/test/parallel/no-node-snapshot.test.js +++ /dev/null @@ -1,14 +0,0 @@ -//#FILE: test-no-node-snapshot.js -//#SHA1: e8e4ecdb2aa4c064a55e1c7b076424d0b0d0a007 -//----------------- -"use strict"; - -// Flags: --no-node-snapshot - -test("--no-node-snapshot flag", () => { - // This test doesn't actually assert anything. - // It's merely checking if the script runs without errors when the flag is set. - expect(true).toBe(true); -}); - -//<#END_FILE: test-no-node-snapshot.js diff --git a/test/js/node/test/parallel/outgoing-message-destroy.test.js b/test/js/node/test/parallel/outgoing-message-destroy.test.js deleted file mode 100644 index 6f61032083..0000000000 --- a/test/js/node/test/parallel/outgoing-message-destroy.test.js +++ /dev/null @@ -1,19 +0,0 @@ -//#FILE: test-outgoing-message-destroy.js -//#SHA1: 64f5438a6e8b8315e79f25d8f9e40b7dde6e3c19 -//----------------- -"use strict"; - -// Test that http.OutgoingMessage,prototype.destroy() returns `this`. - -const http = require("http"); - -test("http.OutgoingMessage.prototype.destroy() returns `this`", () => { - const outgoingMessage = new http.OutgoingMessage(); - - expect(outgoingMessage.destroyed).toBe(false); - expect(outgoingMessage.destroy()).toBe(outgoingMessage); - expect(outgoingMessage.destroyed).toBe(true); - expect(outgoingMessage.destroy()).toBe(outgoingMessage); -}); - -//<#END_FILE: test-outgoing-message-destroy.js diff --git a/test/js/node/test/parallel/path-extname.test.js b/test/js/node/test/parallel/path-extname.test.js deleted file mode 100644 index e8a74c6b2e..0000000000 --- a/test/js/node/test/parallel/path-extname.test.js +++ /dev/null @@ -1,115 +0,0 @@ -//#FILE: test-path-extname.js -//#SHA1: 29d676d507ef80d7e5795db0f2a0265dbc7baf1e -//----------------- -"use strict"; -const path = require("path"); - -const slashRE = /\//g; - -const testPaths = [ - [__filename, ".js"], - ["", ""], - ["/path/to/file", ""], - ["/path/to/file.ext", ".ext"], - ["/path.to/file.ext", ".ext"], - ["/path.to/file", ""], - ["/path.to/.file", ""], - ["/path.to/.file.ext", ".ext"], - ["/path/to/f.ext", ".ext"], - ["/path/to/..ext", ".ext"], - ["/path/to/..", ""], - ["file", ""], - ["file.ext", ".ext"], - [".file", ""], - [".file.ext", ".ext"], - ["/file", ""], - ["/file.ext", ".ext"], - ["/.file", ""], - ["/.file.ext", ".ext"], - [".path/file.ext", ".ext"], - ["file.ext.ext", ".ext"], - ["file.", "."], - [".", ""], - ["./", ""], - [".file.ext", ".ext"], - [".file", ""], - [".file.", "."], - [".file..", "."], - ["..", ""], - ["../", ""], - ["..file.ext", ".ext"], - ["..file", ".file"], - ["..file.", "."], - ["..file..", "."], - ["...", "."], - ["...ext", ".ext"], - ["....", "."], - ["file.ext/", ".ext"], - ["file.ext//", ".ext"], - ["file/", ""], - ["file//", ""], - ["file./", "."], - ["file.//", "."], -]; - -describe("path.extname", () => { - test("should return correct extensions for various paths", () => { - const failures = []; - - for (const testPath of testPaths) { - const expected = testPath[1]; - const extNames = [path.posix.extname, path.win32.extname]; - for (const extname of extNames) { - let input = testPath[0]; - let os; - if (extname === path.win32.extname) { - input = input.replace(slashRE, "\\"); - os = "win32"; - } else { - os = "posix"; - } - const actual = extname(input); - const message = `path.${os}.extname(${JSON.stringify(input)})\n expect=${JSON.stringify( - expected, - )}\n actual=${JSON.stringify(actual)}`; - if (actual !== expected) failures.push(`\n${message}`); - } - const input = `C:${testPath[0].replace(slashRE, "\\")}`; - const actual = path.win32.extname(input); - const message = `path.win32.extname(${JSON.stringify(input)})\n expect=${JSON.stringify( - expected, - )}\n actual=${JSON.stringify(actual)}`; - if (actual !== expected) failures.push(`\n${message}`); - } - - expect(failures).toHaveLength(0); - }); - - describe("Windows-specific behavior", () => { - test("backslash is a path separator", () => { - expect(path.win32.extname(".\\").toString()).toBe(""); - expect(path.win32.extname("..\\").toString()).toBe(""); - expect(path.win32.extname("file.ext\\").toString()).toBe(".ext"); - expect(path.win32.extname("file.ext\\\\").toString()).toBe(".ext"); - expect(path.win32.extname("file\\").toString()).toBe(""); - expect(path.win32.extname("file\\\\").toString()).toBe(""); - expect(path.win32.extname("file.\\").toString()).toBe("."); - expect(path.win32.extname("file.\\\\").toString()).toBe("."); - }); - }); - - describe("POSIX-specific behavior", () => { - test("backslash is a valid name component", () => { - expect(path.posix.extname(".\\").toString()).toBe(""); - expect(path.posix.extname("..\\").toString()).toBe(".\\"); - expect(path.posix.extname("file.ext\\").toString()).toBe(".ext\\"); - expect(path.posix.extname("file.ext\\\\").toString()).toBe(".ext\\\\"); - expect(path.posix.extname("file\\").toString()).toBe(""); - expect(path.posix.extname("file\\\\").toString()).toBe(""); - expect(path.posix.extname("file.\\").toString()).toBe(".\\"); - expect(path.posix.extname("file.\\\\").toString()).toBe(".\\\\"); - }); - }); -}); - -//<#END_FILE: test-path-extname.js diff --git a/test/js/node/test/parallel/path-isabsolute.test.js b/test/js/node/test/parallel/path-isabsolute.test.js deleted file mode 100644 index 53604b0d51..0000000000 --- a/test/js/node/test/parallel/path-isabsolute.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-path-isabsolute.js -//#SHA1: d0ff051a7934f18aed9c435a823ff688e5f782c1 -//----------------- -"use strict"; - -const path = require("path"); - -test("path.win32.isAbsolute", () => { - expect(path.win32.isAbsolute("/")).toBe(true); - expect(path.win32.isAbsolute("//")).toBe(true); - expect(path.win32.isAbsolute("//server")).toBe(true); - expect(path.win32.isAbsolute("//server/file")).toBe(true); - expect(path.win32.isAbsolute("\\\\server\\file")).toBe(true); - expect(path.win32.isAbsolute("\\\\server")).toBe(true); - expect(path.win32.isAbsolute("\\\\")).toBe(true); - expect(path.win32.isAbsolute("c")).toBe(false); - expect(path.win32.isAbsolute("c:")).toBe(false); - expect(path.win32.isAbsolute("c:\\")).toBe(true); - expect(path.win32.isAbsolute("c:/")).toBe(true); - expect(path.win32.isAbsolute("c://")).toBe(true); - expect(path.win32.isAbsolute("C:/Users/")).toBe(true); - expect(path.win32.isAbsolute("C:\\Users\\")).toBe(true); - expect(path.win32.isAbsolute("C:cwd/another")).toBe(false); - expect(path.win32.isAbsolute("C:cwd\\another")).toBe(false); - expect(path.win32.isAbsolute("directory/directory")).toBe(false); - expect(path.win32.isAbsolute("directory\\directory")).toBe(false); -}); - -test("path.posix.isAbsolute", () => { - expect(path.posix.isAbsolute("/home/foo")).toBe(true); - expect(path.posix.isAbsolute("/home/foo/..")).toBe(true); - expect(path.posix.isAbsolute("bar/")).toBe(false); - expect(path.posix.isAbsolute("./baz")).toBe(false); -}); - -//<#END_FILE: test-path-isabsolute.js diff --git a/test/js/node/test/parallel/path-normalize.test.js b/test/js/node/test/parallel/path-normalize.test.js deleted file mode 100644 index c1a4bee899..0000000000 --- a/test/js/node/test/parallel/path-normalize.test.js +++ /dev/null @@ -1,63 +0,0 @@ -//#FILE: test-path-normalize.js -//#SHA1: 94c9aec4a962fc0737d7a88610d3c3e17a3b96b5 -//----------------- -"use strict"; - -const path = require("path"); - -describe("path.normalize", () => { - describe("win32", () => { - test("normalizes various paths correctly", () => { - expect(path.win32.normalize("./fixtures///b/../b/c.js")).toBe("fixtures\\b\\c.js"); - expect(path.win32.normalize("/foo/../../../bar")).toBe("\\bar"); - expect(path.win32.normalize("a//b//../b")).toBe("a\\b"); - expect(path.win32.normalize("a//b//./c")).toBe("a\\b\\c"); - expect(path.win32.normalize("a//b//.")).toBe("a\\b"); - expect(path.win32.normalize("//server/share/dir/file.ext")).toBe("\\\\server\\share\\dir\\file.ext"); - expect(path.win32.normalize("/a/b/c/../../../x/y/z")).toBe("\\x\\y\\z"); - expect(path.win32.normalize("C:")).toBe("C:."); - expect(path.win32.normalize("C:..\\abc")).toBe("C:..\\abc"); - expect(path.win32.normalize("C:..\\..\\abc\\..\\def")).toBe("C:..\\..\\def"); - expect(path.win32.normalize("C:\\.")).toBe("C:\\"); - expect(path.win32.normalize("file:stream")).toBe("file:stream"); - expect(path.win32.normalize("bar\\foo..\\..\\")).toBe("bar\\"); - expect(path.win32.normalize("bar\\foo..\\..")).toBe("bar"); - expect(path.win32.normalize("bar\\foo..\\..\\baz")).toBe("bar\\baz"); - expect(path.win32.normalize("bar\\foo..\\")).toBe("bar\\foo..\\"); - expect(path.win32.normalize("bar\\foo..")).toBe("bar\\foo.."); - expect(path.win32.normalize("..\\foo..\\..\\..\\bar")).toBe("..\\..\\bar"); - expect(path.win32.normalize("..\\...\\..\\.\\...\\..\\..\\bar")).toBe("..\\..\\bar"); - expect(path.win32.normalize("../../../foo/../../../bar")).toBe("..\\..\\..\\..\\..\\bar"); - expect(path.win32.normalize("../../../foo/../../../bar/../../")).toBe("..\\..\\..\\..\\..\\..\\"); - expect(path.win32.normalize("../foobar/barfoo/foo/../../../bar/../../")).toBe("..\\..\\"); - expect(path.win32.normalize("../.../../foobar/../../../bar/../../baz")).toBe("..\\..\\..\\..\\baz"); - expect(path.win32.normalize("foo/bar\\baz")).toBe("foo\\bar\\baz"); - }); - }); - - describe("posix", () => { - test("normalizes various paths correctly", () => { - expect(path.posix.normalize("./fixtures///b/../b/c.js")).toBe("fixtures/b/c.js"); - expect(path.posix.normalize("/foo/../../../bar")).toBe("/bar"); - expect(path.posix.normalize("a//b//../b")).toBe("a/b"); - expect(path.posix.normalize("a//b//./c")).toBe("a/b/c"); - expect(path.posix.normalize("a//b//.")).toBe("a/b"); - expect(path.posix.normalize("/a/b/c/../../../x/y/z")).toBe("/x/y/z"); - expect(path.posix.normalize("///..//./foo/.//bar")).toBe("/foo/bar"); - expect(path.posix.normalize("bar/foo../../")).toBe("bar/"); - expect(path.posix.normalize("bar/foo../..")).toBe("bar"); - expect(path.posix.normalize("bar/foo../../baz")).toBe("bar/baz"); - expect(path.posix.normalize("bar/foo../")).toBe("bar/foo../"); - expect(path.posix.normalize("bar/foo..")).toBe("bar/foo.."); - expect(path.posix.normalize("../foo../../../bar")).toBe("../../bar"); - expect(path.posix.normalize("../.../.././.../../../bar")).toBe("../../bar"); - expect(path.posix.normalize("../../../foo/../../../bar")).toBe("../../../../../bar"); - expect(path.posix.normalize("../../../foo/../../../bar/../../")).toBe("../../../../../../"); - expect(path.posix.normalize("../foobar/barfoo/foo/../../../bar/../../")).toBe("../../"); - expect(path.posix.normalize("../.../../foobar/../../../bar/../../baz")).toBe("../../../../baz"); - expect(path.posix.normalize("foo/bar\\baz")).toBe("foo/bar\\baz"); - }); - }); -}); - -//<#END_FILE: test-path-normalize.js diff --git a/test/js/node/test/parallel/path-posix-exists.test.js b/test/js/node/test/parallel/path-posix-exists.test.js deleted file mode 100644 index 8dfb86ea81..0000000000 --- a/test/js/node/test/parallel/path-posix-exists.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-path-posix-exists.js -//#SHA1: 4bd4c9ef3ffd03623fefbdedd28732c21fd10956 -//----------------- -"use strict"; - -// The original test file used Node.js specific modules and assertions. -// We'll convert this to use Jest's testing framework while maintaining -// the same behavior and allowing it to run in both Node.js and Bun. - -test("path/posix module exists and is identical to path.posix", () => { - // In Jest, we don't need to explicitly require assert - // We'll use Jest's expect API instead - - // We still need to require the path module - const path = require("path"); - - // Check if the path/posix module is the same as path.posix - expect(require("path/posix")).toBe(path.posix); -}); - -//<#END_FILE: test-path-posix-exists.js diff --git a/test/js/node/test/parallel/path-posix-relative-on-windows.test.js b/test/js/node/test/parallel/path-posix-relative-on-windows.test.js deleted file mode 100644 index c1a4e9ea2c..0000000000 --- a/test/js/node/test/parallel/path-posix-relative-on-windows.test.js +++ /dev/null @@ -1,15 +0,0 @@ -//#FILE: test-path-posix-relative-on-windows.js -//#SHA1: 2fb8d86d02eea6a077fbca45828aa2433d6a49e6 -//----------------- -"use strict"; - -const path = require("path"); - -// Refs: https://github.com/nodejs/node/issues/13683 - -test("path.posix.relative on Windows", () => { - const relativePath = path.posix.relative("a/b/c", "../../x"); - expect(relativePath).toMatch(/^(\.\.\/){3,5}x$/); -}); - -//<#END_FILE: test-path-posix-relative-on-windows.js diff --git a/test/js/node/test/parallel/path-relative.test.js b/test/js/node/test/parallel/path-relative.test.js deleted file mode 100644 index 86c50968de..0000000000 --- a/test/js/node/test/parallel/path-relative.test.js +++ /dev/null @@ -1,75 +0,0 @@ -//#FILE: test-path-relative.js -//#SHA1: 9f0d03bf451853a369a3b31b94b902ee4f607e51 -//----------------- -"use strict"; - -const path = require("path"); - -describe("path.relative", () => { - const relativeTests = [ - [ - path.win32.relative, - // Arguments result - [ - ["c:/blah\\blah", "d:/games", "d:\\games"], - ["c:/aaaa/bbbb", "c:/aaaa", ".."], - ["c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"], - ["c:/aaaa/bbbb", "c:/aaaa/bbbb", ""], - ["c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"], - ["c:/aaaa/", "c:/aaaa/cccc", "cccc"], - ["c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"], - ["c:/aaaa/bbbb", "d:\\", "d:\\"], - ["c:/AaAa/bbbb", "c:/aaaa/bbbb", ""], - ["c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"], - ["C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."], - ["C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json"], - ["C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"], - ["C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"], - ["\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"], - ["\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."], - ["\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"], - ["\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"], - ["C:\\baz-quux", "C:\\baz", "..\\baz"], - ["C:\\baz", "C:\\baz-quux", "..\\baz-quux"], - ["\\\\foo\\baz-quux", "\\\\foo\\baz", "..\\baz"], - ["\\\\foo\\baz", "\\\\foo\\baz-quux", "..\\baz-quux"], - ["C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"], - ["\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"], - ], - ], - [ - path.posix.relative, - // Arguments result - [ - ["/var/lib", "/var", ".."], - ["/var/lib", "/bin", "../../bin"], - ["/var/lib", "/var/lib", ""], - ["/var/lib", "/var/apache", "../apache"], - ["/var/", "/var/lib", "lib"], - ["/", "/var/lib", "var/lib"], - ["/foo/test", "/foo/test/bar/package.json", "bar/package.json"], - ["/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."], - ["/foo/bar/baz-quux", "/foo/bar/baz", "../baz"], - ["/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"], - ["/baz-quux", "/baz", "../baz"], - ["/baz", "/baz-quux", "../baz-quux"], - ["/page1/page2/foo", "/", "../../.."], - ], - ], - ]; - - relativeTests.forEach(test => { - const relative = test[0]; - const os = relative === path.win32.relative ? "win32" : "posix"; - - test[1].forEach(testCase => { - it(`path.${os}.relative(${JSON.stringify(testCase[0])}, ${JSON.stringify(testCase[1])})`, () => { - const actual = relative(testCase[0], testCase[1]); - const expected = testCase[2]; - expect(actual).toBe(expected); - }); - }); - }); -}); - -//<#END_FILE: test-path-relative.js diff --git a/test/js/node/test/parallel/path-win32-exists.test.js b/test/js/node/test/parallel/path-win32-exists.test.js deleted file mode 100644 index b3aae0de87..0000000000 --- a/test/js/node/test/parallel/path-win32-exists.test.js +++ /dev/null @@ -1,10 +0,0 @@ -//#FILE: test-path-win32-exists.js -//#SHA1: 7d5cfa1f0fc5a13f9878eddd3b119b9c488fecc5 -//----------------- -"use strict"; - -test("path/win32 exists and is the same as path.win32", () => { - expect(require("path/win32")).toBe(require("path").win32); -}); - -//<#END_FILE: test-path-win32-exists.js diff --git a/test/js/node/test/parallel/path-zero-length-strings.test.js b/test/js/node/test/parallel/path-zero-length-strings.test.js deleted file mode 100644 index 0c4766ddd6..0000000000 --- a/test/js/node/test/parallel/path-zero-length-strings.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-path-zero-length-strings.js -//#SHA1: 2f55f68499f5dcd0b2cbb43e7793c0f45175402f -//----------------- -"use strict"; - -// These testcases are specific to one uncommon behavior in path module. Few -// of the functions in path module, treat '' strings as current working -// directory. This test makes sure that the behavior is intact between commits. -// See: https://github.com/nodejs/node/pull/2106 - -const path = require("path"); -const pwd = process.cwd(); - -describe("Path module zero-length strings behavior", () => { - test("Join with zero-length strings", () => { - expect(path.posix.join("")).toBe("."); - expect(path.posix.join("", "")).toBe("."); - expect(path.win32.join("")).toBe("."); - expect(path.win32.join("", "")).toBe("."); - expect(path.join(pwd)).toBe(pwd); - expect(path.join(pwd, "")).toBe(pwd); - }); - - test("Normalize with zero-length strings", () => { - expect(path.posix.normalize("")).toBe("."); - expect(path.win32.normalize("")).toBe("."); - expect(path.normalize(pwd)).toBe(pwd); - }); - - test("isAbsolute with zero-length strings", () => { - expect(path.posix.isAbsolute("")).toBe(false); - expect(path.win32.isAbsolute("")).toBe(false); - }); - - test("Resolve with zero-length strings", () => { - expect(path.resolve("")).toBe(pwd); - expect(path.resolve("", "")).toBe(pwd); - }); - - test("Relative with zero-length strings", () => { - expect(path.relative("", pwd)).toBe(""); - expect(path.relative(pwd, "")).toBe(""); - expect(path.relative(pwd, pwd)).toBe(""); - }); -}); - -//<#END_FILE: test-path-zero-length-strings.js diff --git a/test/js/node/test/parallel/path.test.js b/test/js/node/test/parallel/path.test.js deleted file mode 100644 index f358ae1a27..0000000000 --- a/test/js/node/test/parallel/path.test.js +++ /dev/null @@ -1,88 +0,0 @@ -//#FILE: test-path.js -//#SHA1: da9d0113e57d9da3983b555973f7835c17657326 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const path = require("path"); - -// Test thrown TypeErrors -const typeErrorTests = [true, false, 7, null, {}, undefined, [], NaN]; - -function fail(fn) { - const args = Array.from(arguments).slice(1); - - expect(() => { - fn.apply(null, args); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); -} - -test("Invalid argument types", () => { - for (const test of typeErrorTests) { - for (const namespace of [path.posix, path.win32]) { - fail(namespace.join, test); - fail(namespace.resolve, test); - fail(namespace.normalize, test); - fail(namespace.isAbsolute, test); - fail(namespace.relative, test, "foo"); - fail(namespace.relative, "foo", test); - fail(namespace.parse, test); - fail(namespace.dirname, test); - fail(namespace.basename, test); - fail(namespace.extname, test); - - // Undefined is a valid value as the second argument to basename - if (test !== undefined) { - fail(namespace.basename, "foo", test); - } - } - } -}); - -test("path.sep tests", () => { - // windows - expect(path.win32.sep).toBe("\\"); - // posix - expect(path.posix.sep).toBe("/"); -}); - -test("path.delimiter tests", () => { - // windows - expect(path.win32.delimiter).toBe(";"); - // posix - expect(path.posix.delimiter).toBe(":"); -}); - -test("path module", () => { - if (process.platform === "win32") { - expect(path).toBe(path.win32); - } else { - expect(path).toBe(path.posix); - } -}); - -//<#END_FILE: test-path.js diff --git a/test/js/node/test/parallel/perf-gc-crash.test.js b/test/js/node/test/parallel/perf-gc-crash.test.js deleted file mode 100644 index 059bff9b3c..0000000000 --- a/test/js/node/test/parallel/perf-gc-crash.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-perf-gc-crash.js -//#SHA1: 376f73db023a0e22edee3e715b8b5999213b5c93 -//----------------- -"use strict"; - -// Refers to https://github.com/nodejs/node/issues/39548 - -// The test fails if this crashes. If it closes normally, -// then all is good. - -const { PerformanceObserver } = require("perf_hooks"); - -test("PerformanceObserver does not crash on multiple observe and disconnect calls", () => { - // We don't actually care if the observer callback is called here. - const gcObserver = new PerformanceObserver(() => {}); - - expect(() => { - gcObserver.observe({ entryTypes: ["gc"] }); - gcObserver.disconnect(); - }).not.toThrow(); - - const gcObserver2 = new PerformanceObserver(() => {}); - - expect(() => { - gcObserver2.observe({ entryTypes: ["gc"] }); - gcObserver2.disconnect(); - }).not.toThrow(); -}); - -//<#END_FILE: test-perf-gc-crash.js diff --git a/test/js/node/test/parallel/performance-measure.test.js b/test/js/node/test/parallel/performance-measure.test.js deleted file mode 100644 index eff0e0d818..0000000000 --- a/test/js/node/test/parallel/performance-measure.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-performance-measure.js -//#SHA1: f049b29e11ba7864ddf502609caf424eccee7ca5 -//----------------- -"use strict"; - -const { PerformanceObserver, performance } = require("perf_hooks"); - -const DELAY = 1000; -const ALLOWED_MARGIN = 10; - -test("performance measures", done => { - const expected = ["Start to Now", "A to Now", "A to B"]; - const obs = new PerformanceObserver(items => { - items.getEntries().forEach(({ name, duration }) => { - expect(duration).toBeGreaterThan(DELAY - ALLOWED_MARGIN); - expect(expected.shift()).toBe(name); - }); - if (expected.length === 0) { - done(); - } - }); - obs.observe({ entryTypes: ["measure"] }); - - performance.mark("A"); - setTimeout(() => { - performance.measure("Start to Now"); - performance.measure("A to Now", "A"); - - performance.mark("B"); - performance.measure("A to B", "A", "B"); - }, DELAY); -}); - -//<#END_FILE: test-performance-measure.js diff --git a/test/js/node/test/parallel/performance-nodetiming.test.js b/test/js/node/test/parallel/performance-nodetiming.test.js deleted file mode 100644 index 9146bc5b05..0000000000 --- a/test/js/node/test/parallel/performance-nodetiming.test.js +++ /dev/null @@ -1,63 +0,0 @@ -//#FILE: test-performance-nodetiming.js -//#SHA1: 7b861bd4f2035688c2cb23ff54525d8e039c2d23 -//----------------- -"use strict"; - -const { performance } = require("perf_hooks"); -const { isMainThread } = require("worker_threads"); - -describe("performance.nodeTiming", () => { - const { nodeTiming } = performance; - - test("basic properties", () => { - expect(nodeTiming.name).toBe("node"); - expect(nodeTiming.entryType).toBe("node"); - }); - - test("timing values", () => { - expect(nodeTiming.startTime).toBe(0); - const now = performance.now(); - expect(nodeTiming.duration).toBeGreaterThanOrEqual(now); - }); - - test("milestone values order", () => { - const keys = ["nodeStart", "v8Start", "environment", "bootstrapComplete"]; - for (let idx = 0; idx < keys.length; idx++) { - if (idx === 0) { - expect(nodeTiming[keys[idx]]).toBeGreaterThanOrEqual(0); - continue; - } - expect(nodeTiming[keys[idx]]).toBeGreaterThan(nodeTiming[keys[idx - 1]]); - } - }); - - test("loop milestones", () => { - expect(nodeTiming.idleTime).toBe(0); - if (isMainThread) { - expect(nodeTiming.loopStart).toBe(-1); - } else { - expect(nodeTiming.loopStart).toBeGreaterThanOrEqual(nodeTiming.bootstrapComplete); - } - expect(nodeTiming.loopExit).toBe(-1); - }); - - test("idle time and loop exit", done => { - setTimeout(() => { - expect(nodeTiming.idleTime).toBeGreaterThanOrEqual(0); - expect(nodeTiming.idleTime + nodeTiming.loopExit).toBeLessThanOrEqual(nodeTiming.duration); - expect(nodeTiming.loopStart).toBeGreaterThanOrEqual(nodeTiming.bootstrapComplete); - done(); - }, 1); - }); - - test("loop exit on process exit", done => { - process.on("exit", () => { - expect(nodeTiming.loopExit).toBeGreaterThan(0); - done(); - }); - // Trigger process exit - process.exit(); - }); -}); - -//<#END_FILE: test-performance-nodetiming.js diff --git a/test/js/node/test/parallel/performanceobserver-gc.test.js b/test/js/node/test/parallel/performanceobserver-gc.test.js deleted file mode 100644 index b5e6c913d0..0000000000 --- a/test/js/node/test/parallel/performanceobserver-gc.test.js +++ /dev/null @@ -1,22 +0,0 @@ -//#FILE: test-performanceobserver-gc.js -//#SHA1: 2a18df2fa465d96ec5c9ee5d42052c732c777e2b -//----------------- -"use strict"; - -// Verifies that setting up two observers to listen -// to gc performance does not crash. - -const { PerformanceObserver } = require("perf_hooks"); - -test("Setting up two observers to listen to gc performance does not crash", () => { - // We don't actually care if the callback is ever invoked in this test - const obs = new PerformanceObserver(() => {}); - const obs2 = new PerformanceObserver(() => {}); - - expect(() => { - obs.observe({ type: "gc" }); - obs2.observe({ type: "gc" }); - }).not.toThrow(); -}); - -//<#END_FILE: test-performanceobserver-gc.js diff --git a/test/js/node/test/parallel/pipe-abstract-socket.test.js b/test/js/node/test/parallel/pipe-abstract-socket.test.js deleted file mode 100644 index 934108f4c5..0000000000 --- a/test/js/node/test/parallel/pipe-abstract-socket.test.js +++ /dev/null @@ -1,71 +0,0 @@ -//#FILE: test-pipe-abstract-socket.js -//#SHA1: 085e51018c26c846b8ea9a2b23da4f45f37fe82f -//----------------- -"use strict"; - -const net = require("net"); - -const isLinux = process.platform === "linux"; - -if (!isLinux) { - it.skip("Skipping test on non-Linux platforms", () => {}); -} else { - describe("Abstract Unix socket tests", () => { - const path = "\0abstract"; - const expectedErrorMessage = "The argument 'options' can not set readableAll or writableAll to true when path is abstract unix socket. Received"; - - test("throws when setting readableAll to true", () => { - const options = { - path, - readableAll: true, - }; - - expect(() => { - const server = net.createServer(jest.fn()); - server.listen(options); - }).toThrow( - expect.objectContaining({ - message: `${expectedErrorMessage} ${JSON.stringify(options)}`, - code: "ERR_INVALID_ARG_VALUE", - }), - ); - }); - - test("throws when setting writableAll to true", () => { - const options = { - path, - writableAll: true, - } ; - - expect(() => { - const server = net.createServer(jest.fn()); - server.listen(options); - }).toThrow( - expect.objectContaining({ - message: `${expectedErrorMessage} ${JSON.stringify(options)}`, - code: "ERR_INVALID_ARG_VALUE", - }), - ); - }); - - test("throws when setting both readableAll and writableAll to true", () => { - const options = { - path, - readableAll: true, - writableAll: true, - }; - - expect(() => { - const server = net.createServer(jest.fn()); - server.listen(options); - }).toThrow( - expect.objectContaining({ - message: `${expectedErrorMessage} ${JSON.stringify(options)}`, - code: "ERR_INVALID_ARG_VALUE", - }), - ); - }); - }); -} - -//<#END_FILE: test-pipe-abstract-socket.js diff --git a/test/js/node/test/parallel/pipe-return-val.test.js b/test/js/node/test/parallel/pipe-return-val.test.js deleted file mode 100644 index 5027349c9c..0000000000 --- a/test/js/node/test/parallel/pipe-return-val.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-pipe-return-val.js -//#SHA1: 63e55c62d40a6ea0c652d66b609d89f9f5324ea1 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// This test ensures SourceStream.pipe(DestStream) returns DestStream - -const Stream = require("stream").Stream; - -test("SourceStream.pipe(DestStream) returns DestStream", () => { - const sourceStream = new Stream(); - const destStream = new Stream(); - const result = sourceStream.pipe(destStream); - - expect(result).toBe(destStream); -}); - -//<#END_FILE: test-pipe-return-val.js diff --git a/test/js/node/test/parallel/preload-print-process-argv.test.js b/test/js/node/test/parallel/preload-print-process-argv.test.js deleted file mode 100644 index 9f26ef5044..0000000000 --- a/test/js/node/test/parallel/preload-print-process-argv.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-preload-print-process-argv.js -//#SHA1: 071fd007af8342d4f1924da004a6dcc7d419cf9f -//----------------- -"use strict"; - -// This tests that process.argv is the same in the preloaded module -// and the user module. - -const tmpdir = require("../common/tmpdir"); -const { spawnSync } = require("child_process"); -const fs = require("fs"); -const path = require("path"); - -beforeAll(() => { - tmpdir.refresh(); -}); - -test("process.argv is the same in preloaded and user module", () => { - const preloadPath = path.join(tmpdir.path, "preload.js"); - const mainPath = path.join(tmpdir.path, "main.js"); - - fs.writeFileSync(preloadPath, "console.log(JSON.stringify(process.argv));", "utf-8"); - - fs.writeFileSync(mainPath, "console.log(JSON.stringify(process.argv));", "utf-8"); - - const child = spawnSync(process.execPath, ["-r", "./preload.js", "main.js"], { cwd: tmpdir.path }); - - expect(child.status).toBe(0); - - if (child.status !== 0) { - console.log(child.stderr.toString()); - } - - const lines = child.stdout.toString().trim().split("\n"); - expect(JSON.parse(lines[0])).toEqual(JSON.parse(lines[1])); -}); - -//<#END_FILE: test-preload-print-process-argv.js diff --git a/test/js/node/test/parallel/preload-self-referential.test.js b/test/js/node/test/parallel/preload-self-referential.test.js deleted file mode 100644 index b050248bd6..0000000000 --- a/test/js/node/test/parallel/preload-self-referential.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-preload-self-referential.js -//#SHA1: 24bb3def0bae68082aee5280abc5116ebd81c972 -//----------------- -"use strict"; - -const path = require("path"); -const { exec } = require("child_process"); - -const nodeBinary = process.argv[0]; - -// Skip test if not in main thread -if (typeof Worker !== "undefined") { - test.skip("process.chdir is not available in Workers", () => {}); -} else { - test("self-referential module preload", done => { - const selfRefModule = path.join(__dirname, "..", "fixtures", "self_ref_module"); - const fixtureA = path.join(__dirname, "..", "fixtures", "printA.js"); - - exec(`"${nodeBinary}" -r self_ref "${fixtureA}"`, { cwd: selfRefModule }, (err, stdout, stderr) => { - expect(err).toBeFalsy(); - expect(stdout).toBe("A\n"); - done(); - }); - }); -} - -//<#END_FILE: test-preload-self-referential.js diff --git a/test/js/node/test/parallel/process-abort.test.js b/test/js/node/test/parallel/process-abort.test.js deleted file mode 100644 index 24731b9cc3..0000000000 --- a/test/js/node/test/parallel/process-abort.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-process-abort.js -//#SHA1: ca6e85cb79ad3e78182547bd6be24625268aced4 -//----------------- -"use strict"; - -// Skip this test in Workers as process.abort() is not available -if (typeof Worker !== "undefined") { - test.skip("process.abort() is not available in Workers", () => {}); -} else { - describe("process.abort", () => { - test("should not have a prototype", () => { - expect(process.abort.prototype).toBeUndefined(); - }); - - test("should throw TypeError when instantiated", () => { - expect(() => new process.abort()).toThrow(TypeError); - }); - }); -} - -//<#END_FILE: test-process-abort.js diff --git a/test/js/node/test/parallel/process-argv-0.test.js b/test/js/node/test/parallel/process-argv-0.test.js deleted file mode 100644 index 7dee3674eb..0000000000 --- a/test/js/node/test/parallel/process-argv-0.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-process-argv-0.js -//#SHA1: 849d017b5c8496f1927b0618a55a688f4a7aa982 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const path = require("path"); -const { spawn } = require("child_process"); - -if (process.argv[2] !== "child") { - test("process.argv[0] in child process", async () => { - const child = spawn(process.execPath, [__filename, "child"], { - cwd: path.dirname(process.execPath), - }); - - let childArgv0 = ""; - for await (const chunk of child.stdout) { - childArgv0 += chunk; - } - - expect(childArgv0).toBe(process.execPath); - }); -} else { - process.stdout.write(process.argv[0]); -} - -//<#END_FILE: test-process-argv-0.js diff --git a/test/js/node/test/parallel/process-chdir-errormessage.test.js b/test/js/node/test/parallel/process-chdir-errormessage.test.js deleted file mode 100644 index 3703d4e1b9..0000000000 --- a/test/js/node/test/parallel/process-chdir-errormessage.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-process-chdir-errormessage.js -//#SHA1: d0eee0a43892b20221d341b892fa425fe207c506 -//----------------- -"use strict"; - -// Skip test in workers where process.chdir is not available -if (typeof Worker !== "undefined") { - test.skip("process.chdir is not available in Workers"); -} else { - test("process.chdir throws correct error for non-existent directory", () => { - expect(() => { - process.chdir("does-not-exist"); - }).toThrow( - expect.objectContaining({ - name: "Error", - code: "ENOENT", - message: expect.stringMatching(/ENOENT: no such file or directory, chdir .+ -> 'does-not-exist'/), - path: process.cwd(), - syscall: "chdir", - dest: "does-not-exist", - }), - ); - }); -} - -//<#END_FILE: test-process-chdir-errormessage.js diff --git a/test/js/node/test/parallel/process-constants-noatime.test.js b/test/js/node/test/parallel/process-constants-noatime.test.js deleted file mode 100644 index 319d5ff97a..0000000000 --- a/test/js/node/test/parallel/process-constants-noatime.test.js +++ /dev/null @@ -1,17 +0,0 @@ -//#FILE: test-process-constants-noatime.js -//#SHA1: cc1fb622e4cb1e217a3e7a0662db5050dc2562c2 -//----------------- -"use strict"; - -const fs = require("fs"); - -test("O_NOATIME constant", () => { - if (process.platform === "linux") { - expect(fs.constants).toHaveProperty("O_NOATIME"); - expect(fs.constants.O_NOATIME).toBe(0x40000); - } else { - expect(fs.constants).not.toHaveProperty("O_NOATIME"); - } -}); - -//<#END_FILE: test-process-constants-noatime.js diff --git a/test/js/node/test/parallel/process-emit.test.js b/test/js/node/test/parallel/process-emit.test.js deleted file mode 100644 index ca5b2c353e..0000000000 --- a/test/js/node/test/parallel/process-emit.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-process-emit.js -//#SHA1: a019bda4bcc14ef2e20bad3cc89cf8676ed5bc49 -//----------------- -"use strict"; - -const sym = Symbol(); - -test("process.emit for normal event", () => { - const listener = jest.fn(); - process.on("normal", listener); - - process.emit("normal", "normalData"); - - expect(listener).toHaveBeenCalledTimes(1); - expect(listener).toHaveBeenCalledWith("normalData"); - - process.removeListener("normal", listener); -}); - -test("process.emit for symbol event", () => { - const listener = jest.fn(); - process.on(sym, listener); - - process.emit(sym, "symbolData"); - - expect(listener).toHaveBeenCalledTimes(1); - expect(listener).toHaveBeenCalledWith("symbolData"); - - process.removeListener(sym, listener); -}); - -test("process.emit for SIGPIPE signal", () => { - const listener = jest.fn(); - process.on("SIGPIPE", listener); - - process.emit("SIGPIPE", "signalData"); - - expect(listener).toHaveBeenCalledTimes(1); - expect(listener).toHaveBeenCalledWith("signalData"); - - process.removeListener("SIGPIPE", listener); -}); - -test("process._eventsCount is not NaN", () => { - expect(Number.isNaN(process._eventsCount)).toBe(false); -}); - -//<#END_FILE: test-process-emit.js diff --git a/test/js/node/test/parallel/process-env-windows-error-reset.test.js b/test/js/node/test/parallel/process-env-windows-error-reset.test.js deleted file mode 100644 index 57c703c828..0000000000 --- a/test/js/node/test/parallel/process-env-windows-error-reset.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-process-env-windows-error-reset.js -//#SHA1: f7e32cc8da8c33ecfa3cddeb13d3dd8689d6af64 -//----------------- -"use strict"; - -// This checks that after accessing a missing env var, a subsequent -// env read will succeed even for empty variables. - -test("empty env var after accessing missing env var", () => { - process.env.FOO = ""; - process.env.NONEXISTENT_ENV_VAR; // eslint-disable-line no-unused-expressions - const foo = process.env.FOO; - - expect(foo).toBe(""); -}); - -test("env var existence after accessing missing env var", () => { - process.env.FOO = ""; - process.env.NONEXISTENT_ENV_VAR; // eslint-disable-line no-unused-expressions - const hasFoo = "FOO" in process.env; - - expect(hasFoo).toBe(true); -}); - -//<#END_FILE: test-process-env-windows-error-reset.js diff --git a/test/js/node/test/parallel/process-exit.test.js b/test/js/node/test/parallel/process-exit.test.js deleted file mode 100644 index 53ad68de05..0000000000 --- a/test/js/node/test/parallel/process-exit.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-process-exit.js -//#SHA1: 6b66cdd7fd70fedb0fab288294724b6ffe7df8c9 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -test('Calling .exit() from within "exit" should not overflow the call stack', () => { - let nexits = 0; - - const exitHandler = jest.fn(code => { - expect(nexits++).toBe(0); - expect(code).toBe(0); - process.exit(); - }); - - process.on("exit", exitHandler); - - // Simulate process exit - process.emit("exit", 0); - - expect(exitHandler).toHaveBeenCalledTimes(1); -}); - -// "exit" should be emitted unprovoked - -//<#END_FILE: test-process-exit.js diff --git a/test/js/node/test/parallel/process-features.test.js b/test/js/node/test/parallel/process-features.test.js deleted file mode 100644 index ee32335bf1..0000000000 --- a/test/js/node/test/parallel/process-features.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-process-features.js -//#SHA1: 18e2385d3d69890cd826120906ce495a0bc4ba85 -//----------------- -"use strict"; - -test("process.features", () => { - const keys = new Set(Object.keys(process.features)); - - expect(keys).toEqual( - new Set(["inspector", "debug", "uv", "ipv6", "tls_alpn", "tls_sni", "tls_ocsp", "tls", "cached_builtins"]), - ); - - for (const key of keys) { - expect(typeof process.features[key]).toBe("boolean"); - } -}); - -//<#END_FILE: test-process-features.js diff --git a/test/js/node/test/parallel/process-hrtime-bigint.test.js b/test/js/node/test/parallel/process-hrtime-bigint.test.js deleted file mode 100644 index 3e8aa3d6fb..0000000000 --- a/test/js/node/test/parallel/process-hrtime-bigint.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-process-hrtime-bigint.js -//#SHA1: 2fbd7286e22ca2f6d3155f829032524692e4d77c -//----------------- -"use strict"; - -// Tests that process.hrtime.bigint() works. - -test("process.hrtime.bigint() works", () => { - const start = process.hrtime.bigint(); - expect(typeof start).toBe("bigint"); - - const end = process.hrtime.bigint(); - expect(typeof end).toBe("bigint"); - - expect(end - start).toBeGreaterThanOrEqual(0n); -}); - -//<#END_FILE: test-process-hrtime-bigint.js diff --git a/test/js/node/test/parallel/process-initgroups.test.js b/test/js/node/test/parallel/process-initgroups.test.js deleted file mode 100644 index 8dac517f1e..0000000000 --- a/test/js/node/test/parallel/process-initgroups.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-process-initgroups.js -//#SHA1: e7321b3005c066a0b2edbe457e695622b9f2b8e9 -//----------------- -"use strict"; - -if (process.platform === "win32") { - test("process.initgroups is undefined on Windows", () => { - expect(process.initgroups).toBeUndefined(); - }); -} else if (typeof process.initgroups !== "undefined") { - describe("process.initgroups", () => { - test("throws TypeError for invalid user argument", () => { - [undefined, null, true, {}, [], () => {}].forEach(val => { - expect(() => { - process.initgroups(val); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringContaining('The "user" argument must be one of type number or string.'), - }), - ); - }); - }); - - test("throws TypeError for invalid extraGroup argument", () => { - [undefined, null, true, {}, [], () => {}].forEach(val => { - expect(() => { - process.initgroups("foo", val); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringContaining('The "extraGroup" argument must be one of type number or string.'), - }), - ); - }); - }); - - test("throws ERR_UNKNOWN_CREDENTIAL for non-existent group", () => { - expect(() => { - process.initgroups("fhqwhgadshgnsdhjsdbkhsdabkfabkveyb", "fhqwhgadshgnsdhjsdbkhsdabkfabkveyb"); - }).toThrow( - expect.objectContaining({ - code: "ERR_UNKNOWN_CREDENTIAL", - message: expect.stringContaining("Group identifier does not exist"), - }), - ); - }); - }); -} - -//<#END_FILE: test-process-initgroups.js diff --git a/test/js/node/test/parallel/process-uptime.test.js b/test/js/node/test/parallel/process-uptime.test.js deleted file mode 100644 index f776e4d2a0..0000000000 --- a/test/js/node/test/parallel/process-uptime.test.js +++ /dev/null @@ -1,44 +0,0 @@ -//#FILE: test-process-uptime.js -//#SHA1: 98140b3c8b495ef62c519ca900eeb15f1ef5b5aa -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -test("process.uptime() returns a reasonable value", () => { - console.error(process.uptime()); - // Add some wiggle room for different platforms. - // Verify that the returned value is in seconds - - // 15 seconds should be a good estimate. - expect(process.uptime()).toBeLessThanOrEqual(15); -}); - -test("process.uptime() increases over time", async () => { - const original = process.uptime(); - - await new Promise(resolve => setTimeout(resolve, 10)); - - const uptime = process.uptime(); - expect(uptime).toBeGreaterThan(original); -}); - -//<#END_FILE: test-process-uptime.js diff --git a/test/js/node/test/parallel/promise-unhandled-issue-43655.test.js b/test/js/node/test/parallel/promise-unhandled-issue-43655.test.js deleted file mode 100644 index cdaa2e24a9..0000000000 --- a/test/js/node/test/parallel/promise-unhandled-issue-43655.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-promise-unhandled-issue-43655.js -//#SHA1: d0726f3e05d7aba39fc84013c63399d915956e30 -//----------------- -"use strict"; - -function delay(time) { - return new Promise(resolve => { - setTimeout(resolve, time); - }); -} - -test("Promise unhandled rejection performance", async () => { - for (let i = 0; i < 100000; i++) { - await new Promise((resolve, reject) => { - reject("value"); - }).then( - () => {}, - () => {}, - ); - } - - const time0 = Date.now(); - await delay(0); - - const diff = Date.now() - time0; - expect(diff).toBeLessThan(500); -}, 10000); // Increased timeout to 10 seconds to ensure enough time for the test - -//<#END_FILE: test-promise-unhandled-issue-43655.js diff --git a/test/js/node/test/parallel/querystring-maxkeys-non-finite.test.js b/test/js/node/test/parallel/querystring-maxkeys-non-finite.test.js deleted file mode 100644 index 10116e91b3..0000000000 --- a/test/js/node/test/parallel/querystring-maxkeys-non-finite.test.js +++ /dev/null @@ -1,71 +0,0 @@ -//#FILE: test-querystring-maxKeys-non-finite.js -//#SHA1: a1b76b45daad6e46e5504d52e5931d9e4a6d745d -//----------------- -"use strict"; - -// This test was originally written to test a regression -// that was introduced by -// https://github.com/nodejs/node/pull/2288#issuecomment-179543894 - -const querystring = require("querystring"); - -// Taken from express-js/body-parser -// https://github.com/expressjs/body-parser/blob/ed25264fb494cf0c8bc992b8257092cd4f694d5e/test/urlencoded.js#L636-L651 -function createManyParams(count) { - let str = ""; - - if (count === 0) { - return str; - } - - str += "0=0"; - - for (let i = 1; i < count; i++) { - const n = i.toString(36); - str += `&${n}=${n}`; - } - - return str; -} - -const count = 10000; -const originalMaxLength = 1000; -const params = createManyParams(count); - -// thealphanerd -// 27def4f introduced a change to parse that would cause Infinity -// to be passed to String.prototype.split as an argument for limit -// In this instance split will always return an empty array -// this test confirms that the output of parse is the expected length -// when passed Infinity as the argument for maxKeys -describe("querystring.parse with non-finite maxKeys", () => { - test("Infinity maxKeys should return the length of input", () => { - const resultInfinity = querystring.parse(params, undefined, undefined, { - maxKeys: Infinity, - }); - expect(Object.keys(resultInfinity)).toHaveLength(count); - }); - - test("NaN maxKeys should return the length of input", () => { - const resultNaN = querystring.parse(params, undefined, undefined, { - maxKeys: NaN, - }); - expect(Object.keys(resultNaN)).toHaveLength(count); - }); - - test('String "Infinity" maxKeys should return the maxLength defined by parse internals', () => { - const resultInfinityString = querystring.parse(params, undefined, undefined, { - maxKeys: "Infinity", - }); - expect(Object.keys(resultInfinityString)).toHaveLength(originalMaxLength); - }); - - test('String "NaN" maxKeys should return the maxLength defined by parse internals', () => { - const resultNaNString = querystring.parse(params, undefined, undefined, { - maxKeys: "NaN", - }); - expect(Object.keys(resultNaNString)).toHaveLength(originalMaxLength); - }); -}); - -//<#END_FILE: test-querystring-maxKeys-non-finite.js diff --git a/test/js/node/test/parallel/querystring-multichar-separator.test.js b/test/js/node/test/parallel/querystring-multichar-separator.test.js deleted file mode 100644 index cba18fc69a..0000000000 --- a/test/js/node/test/parallel/querystring-multichar-separator.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-querystring-multichar-separator.js -//#SHA1: 22b484432502e6f32fc4517ea91060b983c7be25 -//----------------- -"use strict"; - -const qs = require("querystring"); - -function check(actual, expected) { - expect(actual).not.toBeInstanceOf(Object); - expect(Object.keys(actual).sort()).toEqual(Object.keys(expected).sort()); - Object.keys(expected).forEach(function (key) { - expect(actual[key]).toEqual(expected[key]); - }); -} - -test("qs.parse with multi-character separator", () => { - check(qs.parse("foo=>bar&&bar=>baz", "&&", "=>"), { foo: "bar", bar: "baz" }); -}); - -test("qs.stringify with multi-character separator", () => { - check(qs.stringify({ foo: "bar", bar: "baz" }, "&&", "=>"), "foo=>bar&&bar=>baz"); -}); - -test("qs.parse with different multi-character separators", () => { - check(qs.parse("foo==>bar, bar==>baz", ", ", "==>"), { foo: "bar", bar: "baz" }); -}); - -test("qs.stringify with different multi-character separators", () => { - check(qs.stringify({ foo: "bar", bar: "baz" }, ", ", "==>"), "foo==>bar, bar==>baz"); -}); - -//<#END_FILE: test-querystring-multichar-separator.js diff --git a/test/js/node/test/parallel/quic-internal-endpoint-options.test.js b/test/js/node/test/parallel/quic-internal-endpoint-options.test.js deleted file mode 100644 index 9a5694dcfd..0000000000 --- a/test/js/node/test/parallel/quic-internal-endpoint-options.test.js +++ /dev/null @@ -1,192 +0,0 @@ -//#FILE: test-quic-internal-endpoint-options.js -//#SHA1: 089ba4358a2a9ed3c5463e59d205ee7854f26f30 -//----------------- -// Flags: --expose-internals -"use strict"; - -const common = require("../common"); -if (!common.hasQuic) common.skip("missing quic"); - -const { internalBinding } = require("internal/test/binding"); -const quic = internalBinding("quic"); - -quic.setCallbacks({ - onEndpointClose() {}, - onSessionNew() {}, - onSessionClose() {}, - onSessionDatagram() {}, - onSessionDatagramStatus() {}, - onSessionHandshake() {}, - onSessionPathValidation() {}, - onSessionTicket() {}, - onSessionVersionNegotiation() {}, - onStreamCreated() {}, - onStreamBlocked() {}, - onStreamClose() {}, - onStreamReset() {}, - onStreamHeaders() {}, - onStreamTrailers() {}, -}); - -test("Invalid Endpoint constructor arguments", () => { - expect(() => new quic.Endpoint()).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - - expect(() => new quic.Endpoint("a")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - - expect(() => new quic.Endpoint(null)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - - expect(() => new quic.Endpoint(false)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); -}); - -test("Default options work", () => { - expect(() => new quic.Endpoint({})).not.toThrow(); -}); - -const cases = [ - { - key: "retryTokenExpiration", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "tokenExpiration", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "maxConnectionsPerHost", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "maxConnectionsTotal", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "maxStatelessResetsPerHost", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "addressLRUSize", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "maxRetries", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "maxPayloadSize", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "unacknowledgedPacketThreshold", - valid: [1, 10, 100, 1000, 10000, 10000n], - invalid: [-1, -1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "validateAddress", - valid: [true, false, 0, 1, "a"], - invalid: [], - }, - { - key: "disableStatelessReset", - valid: [true, false, 0, 1, "a"], - invalid: [], - }, - { - key: "ipv6Only", - valid: [true, false, 0, 1, "a"], - invalid: [], - }, - { - key: "cc", - valid: [ - quic.CC_ALGO_RENO, - quic.CC_ALGO_CUBIC, - quic.CC_ALGO_BBR, - quic.CC_ALGO_BBR2, - quic.CC_ALGO_RENO_STR, - quic.CC_ALGO_CUBIC_STR, - quic.CC_ALGO_BBR_STR, - quic.CC_ALGO_BBR2_STR, - ], - invalid: [-1, 4, 1n, "a", null, false, true, {}, [], () => {}], - }, - { - key: "udpReceiveBufferSize", - valid: [0, 1, 2, 3, 4, 1000], - invalid: [-1, "a", null, false, true, {}, [], () => {}], - }, - { - key: "udpSendBufferSize", - valid: [0, 1, 2, 3, 4, 1000], - invalid: [-1, "a", null, false, true, {}, [], () => {}], - }, - { - key: "udpTTL", - valid: [0, 1, 2, 3, 4, 255], - invalid: [-1, 256, "a", null, false, true, {}, [], () => {}], - }, - { - key: "resetTokenSecret", - valid: [new Uint8Array(16), new Uint16Array(8), new Uint32Array(4)], - invalid: ["a", null, false, true, {}, [], () => {}, new Uint8Array(15), new Uint8Array(17), new ArrayBuffer(16)], - }, - { - key: "tokenSecret", - valid: [new Uint8Array(16), new Uint16Array(8), new Uint32Array(4)], - invalid: ["a", null, false, true, {}, [], () => {}, new Uint8Array(15), new Uint8Array(17), new ArrayBuffer(16)], - }, - { - // Unknown options are ignored entirely for any value type - key: "ignored", - valid: ["a", null, false, true, {}, [], () => {}], - invalid: [], - }, -]; - -for (const { key, valid, invalid } of cases) { - describe(`Endpoint option: ${key}`, () => { - test.each(valid)("valid value: %p", value => { - const options = { [key]: value }; - expect(() => new quic.Endpoint(options)).not.toThrow(); - }); - - test.each(invalid)("invalid value: %p", value => { - const options = { [key]: value }; - expect(() => new quic.Endpoint(options)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_VALUE", - message: expect.any(String), - }), - ); - }); - }); -} - -//<#END_FILE: test-quic-internal-endpoint-options.js diff --git a/test/js/node/test/parallel/readable-large-hwm.test.js b/test/js/node/test/parallel/readable-large-hwm.test.js deleted file mode 100644 index 2fc8b24615..0000000000 --- a/test/js/node/test/parallel/readable-large-hwm.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-readable-large-hwm.js -//#SHA1: 1f1184c10e91262eb541830677abcd3e759d304e -//----------------- -"use strict"; -const { Readable } = require("stream"); - -// Make sure that readable completes -// even when reading larger buffer. -test("readable completes when reading larger buffer", done => { - const bufferSize = 10 * 1024 * 1024; - let n = 0; - const r = new Readable({ - read() { - // Try to fill readable buffer piece by piece. - r.push(Buffer.alloc(bufferSize / 10)); - - if (n++ > 10) { - r.push(null); - } - }, - }); - - r.on("readable", () => { - while (true) { - const ret = r.read(bufferSize); - if (ret === null) break; - } - }); - - r.on("end", () => { - done(); - }); -}); - -//<#END_FILE: test-readable-large-hwm.js diff --git a/test/js/node/test/parallel/readable-single-end.test.js b/test/js/node/test/parallel/readable-single-end.test.js deleted file mode 100644 index f8d6813997..0000000000 --- a/test/js/node/test/parallel/readable-single-end.test.js +++ /dev/null @@ -1,33 +0,0 @@ -//#FILE: test-readable-single-end.js -//#SHA1: eb85fac7020fa4b5bc4a0f17ba287e8ab3c9dd2f -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -// This test ensures that there will not be an additional empty 'readable' -// event when stream has ended (only 1 event signalling about end) - -test("Readable stream emits only one event when ended", () => { - const r = new Readable({ - read: () => {}, - }); - - r.push(null); - - const readableSpy = jest.fn(); - const endSpy = jest.fn(); - - r.on("readable", readableSpy); - r.on("end", endSpy); - - return new Promise(resolve => { - setTimeout(() => { - expect(readableSpy).toHaveBeenCalledTimes(1); - expect(endSpy).toHaveBeenCalledTimes(1); - resolve(); - }, 0); - }); -}); - -//<#END_FILE: test-readable-single-end.js diff --git a/test/js/node/test/parallel/readline-async-iterators-destroy.test.js b/test/js/node/test/parallel/readline-async-iterators-destroy.test.js deleted file mode 100644 index 1680fec8d8..0000000000 --- a/test/js/node/test/parallel/readline-async-iterators-destroy.test.js +++ /dev/null @@ -1,99 +0,0 @@ -//#FILE: test-readline-async-iterators-destroy.js -//#SHA1: c082e44d93a4bc199683c7cad216da7bc3f0dc7b -//----------------- -"use strict"; - -const fs = require("fs"); -const { once } = require("events"); -const readline = require("readline"); -const path = require("path"); -const os = require("os"); - -const tmpdir = os.tmpdir(); - -const filename = path.join(tmpdir, "test.txt"); - -const testContents = [ - "", - "\n", - "line 1", - "line 1\nline 2 南越国是前203年至前111年存在于岭南地区的一个国家\nline 3\ntrailing", - "line 1\nline 2\nline 3 ends with newline\n", -]; - -async function testSimpleDestroy() { - for (const fileContent of testContents) { - fs.writeFileSync(filename, fileContent); - - const readable = fs.createReadStream(filename); - const rli = readline.createInterface({ - input: readable, - crlfDelay: Infinity, - }); - - const iteratedLines = []; - for await (const k of rli) { - iteratedLines.push(k); - break; - } - - const expectedLines = fileContent.split("\n"); - if (expectedLines[expectedLines.length - 1] === "") { - expectedLines.pop(); - } - expectedLines.splice(1); - - expect(iteratedLines).toEqual(expectedLines); - - rli.close(); - readable.destroy(); - - await once(readable, "close"); - } -} - -async function testMutualDestroy() { - for (const fileContent of testContents) { - fs.writeFileSync(filename, fileContent); - - const readable = fs.createReadStream(filename); - const rli = readline.createInterface({ - input: readable, - crlfDelay: Infinity, - }); - - const expectedLines = fileContent.split("\n"); - if (expectedLines[expectedLines.length - 1] === "") { - expectedLines.pop(); - } - expectedLines.splice(2); - - const iteratedLines = []; - for await (const k of rli) { - iteratedLines.push(k); - for await (const l of rli) { - iteratedLines.push(l); - break; - } - expect(iteratedLines).toEqual(expectedLines); - break; - } - - expect(iteratedLines).toEqual(expectedLines); - - rli.close(); - readable.destroy(); - - await once(readable, "close"); - } -} - -test("Simple destroy", async () => { - await testSimpleDestroy(); -}); - -test("Mutual destroy", async () => { - await testMutualDestroy(); -}); - -//<#END_FILE: test-readline-async-iterators-destroy.js diff --git a/test/js/node/test/parallel/readline-csi.test.js b/test/js/node/test/parallel/readline-csi.test.js deleted file mode 100644 index e22cdf3b53..0000000000 --- a/test/js/node/test/parallel/readline-csi.test.js +++ /dev/null @@ -1,211 +0,0 @@ -//#FILE: test-readline-csi.js -//#SHA1: 6c80ba1b15c53086d80064d93c6f4cf56d1056d6 -//----------------- -"use strict"; - -const readline = require("readline"); -const { Writable } = require("stream"); - -// Mock the CSI object -const CSI = { - kClearToLineBeginning: "\x1b[1K", - kClearToLineEnd: "\x1b[0K", - kClearLine: "\x1b[2K", - kClearScreenDown: "\x1b[0J", -}; - -test("CSI constants", () => { - expect(CSI).toBeDefined(); - expect(CSI.kClearToLineBeginning).toBe("\x1b[1K"); - expect(CSI.kClearToLineEnd).toBe("\x1b[0K"); - expect(CSI.kClearLine).toBe("\x1b[2K"); - expect(CSI.kClearScreenDown).toBe("\x1b[0J"); -}); - -class TestWritable extends Writable { - constructor() { - super(); - this.data = ""; - } - _write(chunk, encoding, callback) { - this.data += chunk.toString(); - callback(); - } -} - -let writable; - -beforeEach(() => { - writable = new TestWritable(); -}); - -test("clearScreenDown", () => { - expect(readline.clearScreenDown(writable)).toBe(true); - expect(writable.data).toBe(CSI.kClearScreenDown); - - writable.data = ""; - expect(readline.clearScreenDown(writable, jest.fn())).toBe(true); - - expect(() => { - readline.clearScreenDown(writable, null); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_TYPE", - }), - ); - - expect(readline.clearScreenDown(null, jest.fn())).toBe(true); - expect(readline.clearScreenDown(undefined, jest.fn())).toBe(true); -}); - -test("clearLine", () => { - expect(readline.clearLine(writable, -1)).toBe(true); - expect(writable.data).toBe(CSI.kClearToLineBeginning); - - writable.data = ""; - expect(readline.clearLine(writable, 1)).toBe(true); - expect(writable.data).toBe(CSI.kClearToLineEnd); - - writable.data = ""; - expect(readline.clearLine(writable, 0)).toBe(true); - expect(writable.data).toBe(CSI.kClearLine); - - writable.data = ""; - expect(readline.clearLine(writable, -1, jest.fn())).toBe(true); - expect(writable.data).toBe(CSI.kClearToLineBeginning); - - expect(() => { - readline.clearLine(writable, 0, null); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_TYPE", - }), - ); - - expect(readline.clearLine(null, 0)).toBe(true); - expect(readline.clearLine(undefined, 0)).toBe(true); - expect(readline.clearLine(null, 0, jest.fn())).toBe(true); - expect(readline.clearLine(undefined, 0, jest.fn())).toBe(true); -}); - -test("moveCursor", () => { - const testCases = [ - [0, 0, ""], - [1, 0, "\x1b[1C"], - [-1, 0, "\x1b[1D"], - [0, 1, "\x1b[1B"], - [0, -1, "\x1b[1A"], - [1, 1, "\x1b[1C\x1b[1B"], - [-1, 1, "\x1b[1D\x1b[1B"], - [-1, -1, "\x1b[1D\x1b[1A"], - [1, -1, "\x1b[1C\x1b[1A"], - ]; - - testCases.forEach(([dx, dy, expected]) => { - writable.data = ""; - expect(readline.moveCursor(writable, dx, dy)).toBe(true); - expect(writable.data).toBe(expected); - - writable.data = ""; - expect(readline.moveCursor(writable, dx, dy, jest.fn())).toBe(true); - expect(writable.data).toBe(expected); - }); - - expect(() => { - readline.moveCursor(writable, 1, 1, null); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_TYPE", - }), - ); - - expect(readline.moveCursor(null, 1, 1)).toBe(true); - expect(readline.moveCursor(undefined, 1, 1)).toBe(true); - expect(readline.moveCursor(null, 1, 1, jest.fn())).toBe(true); - expect(readline.moveCursor(undefined, 1, 1, jest.fn())).toBe(true); -}); - -test("cursorTo", () => { - expect(readline.cursorTo(null)).toBe(true); - expect(readline.cursorTo()).toBe(true); - expect(readline.cursorTo(null, 1, 1, jest.fn())).toBe(true); - expect(readline.cursorTo(undefined, 1, 1, jest.fn())).toBe(true); - - expect(readline.cursorTo(writable, "a")).toBe(true); - expect(writable.data).toBe(""); - - writable.data = ""; - expect(readline.cursorTo(writable, "a", "b")).toBe(true); - expect(writable.data).toBe(""); - - writable.data = ""; - expect(() => readline.cursorTo(writable, "a", 1)).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_CURSOR_POS", - message: "Cannot set cursor row without setting its column", - }), - ); - expect(writable.data).toBe(""); - - writable.data = ""; - expect(readline.cursorTo(writable, 1, "a")).toBe(true); - expect(writable.data).toBe("\x1b[2G"); - - writable.data = ""; - expect(readline.cursorTo(writable, 1)).toBe(true); - expect(writable.data).toBe("\x1b[2G"); - - writable.data = ""; - expect(readline.cursorTo(writable, 1, 2)).toBe(true); - expect(writable.data).toBe("\x1b[3;2H"); - - writable.data = ""; - expect(readline.cursorTo(writable, 1, 2, jest.fn())).toBe(true); - expect(writable.data).toBe("\x1b[3;2H"); - - writable.data = ""; - expect(readline.cursorTo(writable, 1, jest.fn())).toBe(true); - expect(writable.data).toBe("\x1b[2G"); - - expect(() => { - readline.cursorTo(writable, 1, 1, null); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_TYPE", - }), - ); - - expect(() => { - readline.cursorTo(writable, NaN); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_VALUE", - }), - ); - - expect(() => { - readline.cursorTo(writable, 1, NaN); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_VALUE", - }), - ); - - expect(() => { - readline.cursorTo(writable, NaN, NaN); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_VALUE", - }), - ); -}); - -//<#END_FILE: test-readline-csi.js diff --git a/test/js/node/test/parallel/readline-emit-keypress-events.test.js b/test/js/node/test/parallel/readline-emit-keypress-events.test.js deleted file mode 100644 index 34eaeaa63b..0000000000 --- a/test/js/node/test/parallel/readline-emit-keypress-events.test.js +++ /dev/null @@ -1,75 +0,0 @@ -//#FILE: test-readline-emit-keypress-events.js -//#SHA1: 79b97832d1108222b690320e06d4028f73910125 -//----------------- -"use strict"; -// emitKeypressEvents is thoroughly tested in test-readline-keys.js. -// However, that test calls it implicitly. This is just a quick sanity check -// to verify that it works when called explicitly. - -const readline = require("readline"); -const { PassThrough } = require("stream"); - -const expectedSequence = ["f", "o", "o"]; -const expectedKeys = [ - { sequence: "f", name: "f", ctrl: false, meta: false, shift: false }, - { sequence: "o", name: "o", ctrl: false, meta: false, shift: false }, - { sequence: "o", name: "o", ctrl: false, meta: false, shift: false }, -]; - -test("emitKeypressEvents with stream", () => { - const stream = new PassThrough(); - const sequence = []; - const keys = []; - - readline.emitKeypressEvents(stream); - stream.on("keypress", (s, k) => { - sequence.push(s); - keys.push(k); - }); - stream.write("foo"); - - expect(sequence).toEqual(expectedSequence); - expect(keys).toEqual(expectedKeys); -}); - -test("emitKeypressEvents after attaching listener", () => { - const stream = new PassThrough(); - const sequence = []; - const keys = []; - - stream.on("keypress", (s, k) => { - sequence.push(s); - keys.push(k); - }); - readline.emitKeypressEvents(stream); - stream.write("foo"); - - expect(sequence).toEqual(expectedSequence); - expect(keys).toEqual(expectedKeys); -}); - -test("emitKeypressEvents with listener removal", () => { - const stream = new PassThrough(); - const sequence = []; - const keys = []; - const keypressListener = (s, k) => { - sequence.push(s); - keys.push(k); - }; - - stream.on("keypress", keypressListener); - readline.emitKeypressEvents(stream); - stream.removeListener("keypress", keypressListener); - stream.write("foo"); - - expect(sequence).toEqual([]); - expect(keys).toEqual([]); - - stream.on("keypress", keypressListener); - stream.write("foo"); - - expect(sequence).toEqual(expectedSequence); - expect(keys).toEqual(expectedKeys); -}); - -//<#END_FILE: test-readline-emit-keypress-events.js diff --git a/test/js/node/test/parallel/readline-interface-escapecodetimeout.test.js b/test/js/node/test/parallel/readline-interface-escapecodetimeout.test.js deleted file mode 100644 index c11f399b5a..0000000000 --- a/test/js/node/test/parallel/readline-interface-escapecodetimeout.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-readline-interface-escapecodetimeout.js -//#SHA1: 6d32b42ce02228999a37e3f7017ddd747b346d5c -//----------------- -"use strict"; - -const readline = require("readline"); -const EventEmitter = require("events").EventEmitter; - -// This test ensures that the escapeCodeTimeout option set correctly - -class FakeInput extends EventEmitter { - resume() {} - pause() {} - write() {} - end() {} -} - -test("escapeCodeTimeout option is set correctly", () => { - const fi = new FakeInput(); - const rli = new readline.Interface({ - input: fi, - output: fi, - escapeCodeTimeout: 50, - }); - expect(rli.escapeCodeTimeout).toBe(50); - rli.close(); -}); - -test.each([null, {}, NaN, "50"])("invalid escapeCodeTimeout input throws TypeError", invalidInput => { - const fi = new FakeInput(); - expect(() => { - const rli = new readline.Interface({ - input: fi, - output: fi, - escapeCodeTimeout: invalidInput, - }); - rli.close(); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_VALUE", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-readline-interface-escapecodetimeout.js diff --git a/test/js/node/test/parallel/readline-position.test.js b/test/js/node/test/parallel/readline-position.test.js deleted file mode 100644 index 967f5ba830..0000000000 --- a/test/js/node/test/parallel/readline-position.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-readline-position.js -//#SHA1: 652d50a766a0728968f155ca650ff2ba9f5c32a8 -//----------------- -"use strict"; -const { PassThrough } = require("stream"); -const readline = require("readline"); - -const ctrlU = { ctrl: true, name: "u" }; - -// Skip test if running in a dumb terminal -const isDumbTerminal = process.env.TERM === "dumb"; -if (isDumbTerminal) { - test.skip("Skipping test in dumb terminal", () => {}); -} else { - describe("readline position", () => { - let input; - let rl; - - beforeEach(() => { - input = new PassThrough(); - rl = readline.createInterface({ - terminal: true, - input: input, - prompt: "", - }); - }); - - afterEach(() => { - rl.close(); - }); - - test.each([ - [1, "a"], - [2, "ab"], - [2, "丁"], - [0, "\u0301"], // COMBINING ACUTE ACCENT - [1, "a\u0301"], // á - [0, "\u20DD"], // COMBINING ENCLOSING CIRCLE - [2, "a\u20DDb"], // a⃝b - [0, "\u200E"], // LEFT-TO-RIGHT MARK - ])('cursor position for "%s" should be %i', (expectedCursor, string) => { - rl.write(string); - expect(rl.getCursorPos().cols).toBe(expectedCursor); - rl.write(null, ctrlU); - }); - }); -} - -//<#END_FILE: test-readline-position.js diff --git a/test/js/node/test/parallel/readline-reopen.test.js b/test/js/node/test/parallel/readline-reopen.test.js deleted file mode 100644 index 490034547d..0000000000 --- a/test/js/node/test/parallel/readline-reopen.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-readline-reopen.js -//#SHA1: 39894fe94eb8e03222c86db2daa35a7b449447eb -//----------------- -"use strict"; - -// Regression test for https://github.com/nodejs/node/issues/13557 -// Tests that multiple subsequent readline instances can re-use an input stream. - -const readline = require("readline"); -const { PassThrough } = require("stream"); - -test("multiple readline instances can re-use an input stream", async () => { - const input = new PassThrough(); - const output = new PassThrough(); - - const rl1 = readline.createInterface({ - input, - output, - terminal: true, - }); - - const rl1LinePromise = new Promise(resolve => { - rl1.once("line", line => { - expect(line).toBe("foo"); - resolve(); - }); - }); - - // Write a line plus the first byte of a UTF-8 multibyte character to make sure - // that it doesn't get lost when closing the readline instance. - input.write( - Buffer.concat([ - Buffer.from("foo\n"), - Buffer.from([0xe2]), // Exactly one third of a ☃ snowman. - ]), - ); - - await rl1LinePromise; - rl1.close(); - - const rl2 = readline.createInterface({ - input, - output, - terminal: true, - }); - - const rl2LinePromise = new Promise(resolve => { - rl2.once("line", line => { - expect(line).toBe("☃bar"); - resolve(); - }); - }); - - input.write(Buffer.from([0x98, 0x83])); // The rest of the ☃ snowman. - input.write("bar\n"); - - await rl2LinePromise; - rl2.close(); -}); - -//<#END_FILE: test-readline-reopen.js diff --git a/test/js/node/test/parallel/ref-unref-return.test.js b/test/js/node/test/parallel/ref-unref-return.test.js deleted file mode 100644 index c17af7034a..0000000000 --- a/test/js/node/test/parallel/ref-unref-return.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-ref-unref-return.js -//#SHA1: c7275fa0ca17f1cee96244c48f1044ce4d2e67c1 -//----------------- -"use strict"; - -const net = require("net"); -const dgram = require("dgram"); - -test("ref and unref methods return the same instance", () => { - expect(new net.Server().ref()).toBeInstanceOf(net.Server); - expect(new net.Server().unref()).toBeInstanceOf(net.Server); - expect(new net.Socket().ref()).toBeInstanceOf(net.Socket); - expect(new net.Socket().unref()).toBeInstanceOf(net.Socket); - expect(new dgram.Socket("udp4").ref()).toBeInstanceOf(dgram.Socket); - expect(new dgram.Socket("udp6").unref()).toBeInstanceOf(dgram.Socket); -}); - -//<#END_FILE: test-ref-unref-return.js diff --git a/test/js/node/test/parallel/regression-object-prototype.test.js b/test/js/node/test/parallel/regression-object-prototype.test.js deleted file mode 100644 index aae31cdf28..0000000000 --- a/test/js/node/test/parallel/regression-object-prototype.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-regression-object-prototype.js -//#SHA1: 198fbbc217f8034fb1b81ce137b6b09a19acd0de -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -/* eslint-disable node-core/require-common-first, node-core/required-modules */ -"use strict"; - -test("Object prototype modification regression", () => { - const consoleSpy = jest.spyOn(console, "log"); - - Object.prototype.xadsadsdasasdxx = function () {}; - - expect(consoleSpy).not.toHaveBeenCalled(); - - console.log("puts after"); - - expect(consoleSpy).toHaveBeenCalledWith("puts after"); - expect(consoleSpy).toHaveBeenCalledTimes(1); - - consoleSpy.mockRestore(); -}); - -//<#END_FILE: test-regression-object-prototype.js diff --git a/test/js/node/test/parallel/require-empty-main.test.js b/test/js/node/test/parallel/require-empty-main.test.js deleted file mode 100644 index 93e53d4aed..0000000000 --- a/test/js/node/test/parallel/require-empty-main.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-require-empty-main.js -//#SHA1: 1c03cef0482df2bd119e42f418f54123675b532d -//----------------- -"use strict"; - -const path = require("path"); -const fixtures = require("../common/fixtures"); - -const where = fixtures.path("require-empty-main"); -const expected = path.join(where, "index.js"); - -const testRequireResolve = () => { - expect(require.resolve(where)).toBe(expected); - expect(require(where)).toBe(42); - expect(require.resolve(where)).toBe(expected); -}; - -test('A package.json with an empty "main" property should use index.js if present', testRequireResolve); - -test("require.resolve() should resolve to index.js for the same reason", testRequireResolve); - -test('Any "main" property that doesn\'t resolve to a file should result in index.js being used', testRequireResolve); - -test("Asynchronous test execution", done => { - setImmediate(() => { - testRequireResolve(); - done(); - }); -}); - -//<#END_FILE: test-require-empty-main.js diff --git a/test/js/node/test/parallel/require-extensions-main.test.js b/test/js/node/test/parallel/require-extensions-main.test.js deleted file mode 100644 index 47dee39446..0000000000 --- a/test/js/node/test/parallel/require-extensions-main.test.js +++ /dev/null @@ -1,15 +0,0 @@ -//#FILE: test-require-extensions-main.js -//#SHA1: c3dd50393bbc3eb542e40c67611fc48707ad3cba -//----------------- -"use strict"; - -const path = require("path"); - -test("require extensions main", () => { - const fixturesPath = path.join(__dirname, "..", "fixtures"); - const fixturesRequire = require(path.join(fixturesPath, "require-bin", "bin", "req.js")); - - expect(fixturesRequire).toBe(""); -}); - -//<#END_FILE: test-require-extensions-main.js diff --git a/test/js/node/test/parallel/require-process.test.js b/test/js/node/test/parallel/require-process.test.js deleted file mode 100644 index b6efc34c07..0000000000 --- a/test/js/node/test/parallel/require-process.test.js +++ /dev/null @@ -1,11 +0,0 @@ -//#FILE: test-require-process.js -//#SHA1: 699b499b3f906d140de0d550c310085aa0791c95 -//----------------- -"use strict"; - -test('require("process") should return global process reference', () => { - const nativeProcess = require("process"); - expect(nativeProcess).toBe(process); -}); - -//<#END_FILE: test-require-process.js diff --git a/test/js/node/test/parallel/require-unicode.test.js b/test/js/node/test/parallel/require-unicode.test.js deleted file mode 100644 index b4fa1f1f01..0000000000 --- a/test/js/node/test/parallel/require-unicode.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-require-unicode.js -//#SHA1: 3101d8c9e69745baeed9e6f09f32ca9ab31684a8 -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); - -const tmpdir = require("../common/tmpdir"); - -test("require with unicode path", () => { - tmpdir.refresh(); - - const dirname = tmpdir.resolve("\u4e2d\u6587\u76ee\u5f55"); - fs.mkdirSync(dirname); - fs.writeFileSync(path.join(dirname, "file.js"), "module.exports = 42;"); - fs.writeFileSync(path.join(dirname, "package.json"), JSON.stringify({ name: "test", main: "file.js" })); - - expect(require(dirname)).toBe(42); - expect(require(path.join(dirname, "file.js"))).toBe(42); -}); - -//<#END_FILE: test-require-unicode.js diff --git a/test/js/node/test/parallel/runner-filter-warning.test.js b/test/js/node/test/parallel/runner-filter-warning.test.js deleted file mode 100644 index 56e4817871..0000000000 --- a/test/js/node/test/parallel/runner-filter-warning.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-runner-filter-warning.js -//#SHA1: c0887965f213c569d83684255054bf9e4bc27c29 -//----------------- -// Flags: --test-only -"use strict"; - -const { defaultMaxListeners } = require("node:events"); - -// Remove the process.on('warning') listener as it's not needed in Jest - -for (let i = 0; i < defaultMaxListeners + 1; ++i) { - test(`test ${i + 1}`, () => { - // Empty test body, just to create the specified number of tests - }); -} - -// Add a test to ensure no warnings are emitted -test("no warnings should be emitted", () => { - const warningListener = jest.fn(); - process.on("warning", warningListener); - - // Run all tests - return new Promise(resolve => { - setTimeout(() => { - expect(warningListener).not.toHaveBeenCalled(); - process.removeListener("warning", warningListener); - resolve(); - }, 100); // Wait a short time to ensure all tests have run - }); -}); - -//<#END_FILE: test-runner-filter-warning.js diff --git a/test/js/node/test/parallel/runner-root-after-with-refed-handles.test.js b/test/js/node/test/parallel/runner-root-after-with-refed-handles.test.js deleted file mode 100644 index 7656ca6ca0..0000000000 --- a/test/js/node/test/parallel/runner-root-after-with-refed-handles.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-runner-root-after-with-refed-handles.js -//#SHA1: cdfe0c601f7139167bfdc5fb5ab39ecf7b403a10 -//----------------- -"use strict"; - -const { createServer } = require("node:http"); - -let server; - -beforeAll(() => { - return new Promise((resolve, reject) => { - server = createServer(); - server.listen(0, err => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); -}); - -afterAll(() => { - return new Promise(resolve => { - server.close(() => { - resolve(); - }); - }); -}); - -test("placeholder test", () => { - // This is a placeholder test to ensure the test suite runs - expect(true).toBe(true); -}); - -//<#END_FILE: test-runner-root-after-with-refed-handles.js diff --git a/test/js/node/test/parallel/safe-get-env.test.js b/test/js/node/test/parallel/safe-get-env.test.js deleted file mode 100644 index 118585eeab..0000000000 --- a/test/js/node/test/parallel/safe-get-env.test.js +++ /dev/null @@ -1,44 +0,0 @@ -//#FILE: test-safe-get-env.js -//#SHA1: 4d2553ac6bc24242b872b748a62aca05ca6fbcc8 -//----------------- -"use strict"; - -// This test has been converted to use Jest's API and removed dependencies on internal bindings. -// The original functionality of testing safeGetenv is preserved by mocking process.env. - -describe("safeGetenv", () => { - let originalEnv; - - beforeEach(() => { - originalEnv = { ...process.env }; - }); - - afterEach(() => { - process.env = originalEnv; - }); - - test("should return the same values as process.env", () => { - // Mock some environment variables - process.env = { - TEST_VAR1: "value1", - TEST_VAR2: "value2", - TEST_VAR3: "value3", - }; - - // In a real scenario, we would use the actual safeGetenv function. - // For this test, we'll simulate its behavior by directly accessing process.env - const safeGetenv = key => process.env[key]; - - for (const oneEnv in process.env) { - expect(safeGetenv(oneEnv)).toBe(process.env[oneEnv]); - } - }); - - // Note: The following comment is preserved from the original test file - // FIXME(joyeecheung): this test is not entirely useful. To properly - // test this we could create a mismatch between the effective/real - // group/user id of a Node.js process and see if the environment variables - // are no longer available - but that might be tricky to set up reliably. -}); - -//<#END_FILE: test-safe-get-env.js diff --git a/test/js/node/test/parallel/sigint-infinite-loop.test.js b/test/js/node/test/parallel/sigint-infinite-loop.test.js deleted file mode 100644 index 8c511f99d9..0000000000 --- a/test/js/node/test/parallel/sigint-infinite-loop.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-sigint-infinite-loop.js -//#SHA1: 9c59ace1c99605bce14768e06d62a29e254e6116 -//----------------- -"use strict"; -// This test is to assert that we can SIGINT a script which loops forever. -// Ref(http): -// groups.google.com/group/nodejs-dev/browse_thread/thread/e20f2f8df0296d3f -const { spawn } = require("child_process"); - -test("SIGINT can kill an infinite loop", async () => { - console.log("start"); - - const c = spawn(process.execPath, ["-e", 'while(true) { console.log("hi"); }']); - - let sentKill = false; - - c.stdout.on("data", function (s) { - // Prevent race condition: - // Wait for the first bit of output from the child process - // so that we're sure that it's in the V8 event loop and not - // just in the startup phase of execution. - if (!sentKill) { - c.kill("SIGINT"); - console.log("SIGINT infinite-loop.js"); - sentKill = true; - } - }); - - await new Promise(resolve => { - c.on("exit", code => { - expect(code).not.toBe(0); - console.log("killed infinite-loop.js"); - resolve(); - }); - }); - - expect(sentKill).toBe(true); -}); - -//<#END_FILE: test-sigint-infinite-loop.js diff --git a/test/js/node/test/parallel/signal-safety.test.js b/test/js/node/test/parallel/signal-safety.test.js deleted file mode 100644 index 51f6c19fe8..0000000000 --- a/test/js/node/test/parallel/signal-safety.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-signal-safety.js -//#SHA1: 49090f0605b0ba01c323138ac8e94d423925cbf2 -//----------------- -"use strict"; - -test("Signal `this` safety", () => { - // We cannot use internal bindings in Jest, so we'll mock the Signal class - class Signal { - start() { - // This method should be called with the correct 'this' context - if (!(this instanceof Signal)) { - throw new TypeError("Illegal invocation"); - } - } - } - - const s = new Signal(); - const nots = { start: s.start }; - - expect(() => { - nots.start(9); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-signal-safety.js diff --git a/test/js/node/test/parallel/signal-unregister.test.js b/test/js/node/test/parallel/signal-unregister.test.js deleted file mode 100644 index 0f7b55ce66..0000000000 --- a/test/js/node/test/parallel/signal-unregister.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-signal-unregister.js -//#SHA1: 44f7b12e1cb3d5e2bc94cb244f52d5a6a591d8f4 -//----------------- -"use strict"; - -const { spawn } = require("child_process"); -const path = require("path"); - -const fixturesPath = path.resolve(__dirname, "..", "fixtures"); - -test("Child process exits on SIGINT", () => { - const child = spawn(process.argv[0], [path.join(fixturesPath, "should_exit.js")]); - - return new Promise(resolve => { - child.stdout.once("data", () => { - child.kill("SIGINT"); - }); - - child.on("exit", (exitCode, signalCode) => { - expect(exitCode).toBeNull(); - expect(signalCode).toBe("SIGINT"); - resolve(); - }); - }); -}); - -//<#END_FILE: test-signal-unregister.js diff --git a/test/js/node/test/parallel/stdin-from-file-spawn.test.js b/test/js/node/test/parallel/stdin-from-file-spawn.test.js deleted file mode 100644 index aad7445891..0000000000 --- a/test/js/node/test/parallel/stdin-from-file-spawn.test.js +++ /dev/null @@ -1,60 +0,0 @@ -//#FILE: test-stdin-from-file-spawn.js -//#SHA1: 1f8f432985d08b841ebb2c8142b865ae417737a1 -//----------------- -"use strict"; - -const process = require("process"); -const { execSync } = require("child_process"); -const fs = require("fs"); -const path = require("path"); -const os = require("os"); - -let defaultShell; -if (process.platform === "linux" || process.platform === "darwin") { - defaultShell = "/bin/sh"; -} else if (process.platform === "win32") { - defaultShell = "cmd.exe"; -} else { - it.skip("This test exists only on Linux/Win32/OSX", () => {}); -} - -if (defaultShell) { - test("stdin from file spawn", () => { - const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "test-")); - const tmpCmdFile = path.join(tmpDir, "test-stdin-from-file-spawn-cmd"); - const tmpJsFile = path.join(tmpDir, "test-stdin-from-file-spawn.js"); - - fs.writeFileSync(tmpCmdFile, "echo hello"); - fs.writeFileSync( - tmpJsFile, - ` - 'use strict'; - const { spawn } = require('child_process'); - // Reference the object to invoke the getter - process.stdin; - setTimeout(() => { - let ok = false; - const child = spawn(process.env.SHELL || '${defaultShell}', - [], { stdio: ['inherit', 'pipe'] }); - child.stdout.on('data', () => { - ok = true; - }); - child.on('close', () => { - process.exit(ok ? 0 : -1); - }); - }, 100); - `, - ); - - expect(() => { - execSync(`${process.argv[0]} ${tmpJsFile} < ${tmpCmdFile}`); - }).not.toThrow(); - - // Clean up - fs.unlinkSync(tmpCmdFile); - fs.unlinkSync(tmpJsFile); - fs.rmdirSync(tmpDir); - }); -} - -//<#END_FILE: test-stdin-from-file-spawn.js diff --git a/test/js/node/test/parallel/stdin-hang.test.js b/test/js/node/test/parallel/stdin-hang.test.js deleted file mode 100644 index b9f9dd71a9..0000000000 --- a/test/js/node/test/parallel/stdin-hang.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-stdin-hang.js -//#SHA1: f28512893c8d9cd6500247aa87881347638eb616 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -// This test *only* verifies that invoking the stdin getter does not -// cause node to hang indefinitely. -// If it does, then the test-runner will nuke it. - -test("process.stdin getter does not cause indefinite hang", () => { - // invoke the getter. - process.stdin; // eslint-disable-line no-unused-expressions - - // If we reach this point, it means the process didn't hang - expect(true).toBe(true); -}); - -test("Console output", () => { - const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {}); - - console.error("Should exit normally now."); - - expect(consoleErrorSpy).toHaveBeenCalledWith("Should exit normally now."); - - consoleErrorSpy.mockRestore(); -}); - -//<#END_FILE: test-stdin-hang.js diff --git a/test/js/node/test/parallel/stdin-pause-resume-sync.test.js b/test/js/node/test/parallel/stdin-pause-resume-sync.test.js deleted file mode 100644 index ed69ac4afe..0000000000 --- a/test/js/node/test/parallel/stdin-pause-resume-sync.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-stdin-pause-resume-sync.js -//#SHA1: 2256a91336e221a1770b59ebebb4afb406a770dd -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -test("stdin pause and resume", () => { - const consoleSpy = jest.spyOn(console, "error"); - const stdinResumeSpy = jest.spyOn(process.stdin, "resume"); - const stdinPauseSpy = jest.spyOn(process.stdin, "pause"); - - console.error("before opening stdin"); - process.stdin.resume(); - console.error("stdin opened"); - console.error("pausing stdin"); - process.stdin.pause(); - console.error("opening again"); - process.stdin.resume(); - console.error("pausing again"); - process.stdin.pause(); - console.error("should exit now"); - - expect(consoleSpy).toHaveBeenCalledTimes(6); - expect(consoleSpy).toHaveBeenNthCalledWith(1, "before opening stdin"); - expect(consoleSpy).toHaveBeenNthCalledWith(2, "stdin opened"); - expect(consoleSpy).toHaveBeenNthCalledWith(3, "pausing stdin"); - expect(consoleSpy).toHaveBeenNthCalledWith(4, "opening again"); - expect(consoleSpy).toHaveBeenNthCalledWith(5, "pausing again"); - expect(consoleSpy).toHaveBeenNthCalledWith(6, "should exit now"); - - expect(stdinResumeSpy).toHaveBeenCalledTimes(2); - expect(stdinPauseSpy).toHaveBeenCalledTimes(2); - - consoleSpy.mockRestore(); - stdinResumeSpy.mockRestore(); - stdinPauseSpy.mockRestore(); -}); - -//<#END_FILE: test-stdin-pause-resume-sync.js diff --git a/test/js/node/test/parallel/stdin-pause-resume.test.js b/test/js/node/test/parallel/stdin-pause-resume.test.js deleted file mode 100644 index f172615922..0000000000 --- a/test/js/node/test/parallel/stdin-pause-resume.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-stdin-pause-resume.js -//#SHA1: 941cff7d9e52f178538b4fdd09458bb2fc6a12b7 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -test("stdin pause and resume", async () => { - const consoleSpy = jest.spyOn(console, "error"); - const stdinResumeSpy = jest.spyOn(process.stdin, "resume"); - const stdinPauseSpy = jest.spyOn(process.stdin, "pause"); - - console.error("before opening stdin"); - process.stdin.resume(); - console.error("stdin opened"); - - await new Promise(resolve => setTimeout(resolve, 1)); - - console.error("pausing stdin"); - process.stdin.pause(); - - await new Promise(resolve => setTimeout(resolve, 1)); - - console.error("opening again"); - process.stdin.resume(); - - await new Promise(resolve => setTimeout(resolve, 1)); - - console.error("pausing again"); - process.stdin.pause(); - console.error("should exit now"); - - expect(consoleSpy).toHaveBeenCalledTimes(6); - expect(consoleSpy).toHaveBeenNthCalledWith(1, "before opening stdin"); - expect(consoleSpy).toHaveBeenNthCalledWith(2, "stdin opened"); - expect(consoleSpy).toHaveBeenNthCalledWith(3, "pausing stdin"); - expect(consoleSpy).toHaveBeenNthCalledWith(4, "opening again"); - expect(consoleSpy).toHaveBeenNthCalledWith(5, "pausing again"); - expect(consoleSpy).toHaveBeenNthCalledWith(6, "should exit now"); - - expect(stdinResumeSpy).toHaveBeenCalledTimes(2); - expect(stdinPauseSpy).toHaveBeenCalledTimes(2); - - consoleSpy.mockRestore(); - stdinResumeSpy.mockRestore(); - stdinPauseSpy.mockRestore(); -}); - -//<#END_FILE: test-stdin-pause-resume.js diff --git a/test/js/node/test/parallel/stdin-pipe-resume.test.js b/test/js/node/test/parallel/stdin-pipe-resume.test.js deleted file mode 100644 index 7a38ca23c3..0000000000 --- a/test/js/node/test/parallel/stdin-pipe-resume.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-stdin-pipe-resume.js -//#SHA1: 6775f16e6a971590e3a5308d4e3678029be47411 -//----------------- -"use strict"; - -// This tests that piping stdin will cause it to resume() as well. - -const { spawn } = require("child_process"); - -if (process.argv[2] === "child") { - process.stdin.pipe(process.stdout); -} else { - test("piping stdin causes it to resume", done => { - const buffers = []; - const child = spawn(process.execPath, [__filename, "child"]); - - child.stdout.on("data", c => { - buffers.push(c); - }); - - child.stdout.on("close", () => { - const b = Buffer.concat(buffers).toString(); - expect(b).toBe("Hello, world\n"); - done(); - }); - - child.stdin.write("Hel"); - child.stdin.write("lo,"); - child.stdin.write(" wo"); - - setTimeout(() => { - child.stdin.write("rld\n"); - child.stdin.end(); - }, 10); - }); -} - -//<#END_FILE: test-stdin-pipe-resume.js diff --git a/test/js/node/test/parallel/stdin-resume-pause.test.js b/test/js/node/test/parallel/stdin-resume-pause.test.js deleted file mode 100644 index b455110d35..0000000000 --- a/test/js/node/test/parallel/stdin-resume-pause.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-stdin-resume-pause.js -//#SHA1: ef96cec1b4e29ec6b96da37fd44e3bbdb50e1393 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -test("process.stdin can be resumed and paused", () => { - const originalResume = process.stdin.resume; - const originalPause = process.stdin.pause; - - const mockResume = jest.fn(); - const mockPause = jest.fn(); - - process.stdin.resume = mockResume; - process.stdin.pause = mockPause; - - process.stdin.resume(); - process.stdin.pause(); - - expect(mockResume).toHaveBeenCalledTimes(1); - expect(mockPause).toHaveBeenCalledTimes(1); - - // Restore original methods - process.stdin.resume = originalResume; - process.stdin.pause = originalPause; -}); - -//<#END_FILE: test-stdin-resume-pause.js diff --git a/test/js/node/test/parallel/stdin-script-child-option.test.js b/test/js/node/test/parallel/stdin-script-child-option.test.js deleted file mode 100644 index 3dc4f26ce6..0000000000 --- a/test/js/node/test/parallel/stdin-script-child-option.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-stdin-script-child-option.js -//#SHA1: 80d38c88249e1ceb4ef9b029e6910c91dd08ccc5 -//----------------- -"use strict"; - -const { spawn } = require("child_process"); - -test("child process receives option from command line", done => { - const expected = "--option-to-be-seen-on-child"; - const child = spawn(process.execPath, ["-", expected], { stdio: "pipe" }); - - child.stdin.end("console.log(process.argv[2])"); - - let actual = ""; - child.stdout.setEncoding("utf8"); - child.stdout.on("data", chunk => (actual += chunk)); - child.stdout.on("end", () => { - expect(actual.trim()).toBe(expected); - done(); - }); -}); - -//<#END_FILE: test-stdin-script-child-option.js diff --git a/test/js/node/test/parallel/stdio-pipe-access.test.js b/test/js/node/test/parallel/stdio-pipe-access.test.js deleted file mode 100644 index 026d7ae891..0000000000 --- a/test/js/node/test/parallel/stdio-pipe-access.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-stdio-pipe-access.js -//#SHA1: a1f6e6c04c96ad54cbbd4270958bcb2934a3ce6b -//----------------- -"use strict"; - -// Test if Node handles accessing process.stdin if it is a redirected -// pipe without deadlocking -const { spawn, spawnSync } = require("child_process"); - -const numTries = 5; -const who = process.argv.length <= 2 ? "runner" : process.argv[2]; - -// Skip test for Workers as they don't have process-like stdio -if (typeof Worker !== "undefined") { - test.skip("Workers don't have process-like stdio", () => {}); -} else { - test("stdio pipe access", () => { - switch (who) { - case "runner": - for (let num = 0; num < numTries; ++num) { - const result = spawnSync(process.argv0, [process.argv[1], "parent"], { stdio: "inherit" }); - expect(result.status).toBe(0); - } - break; - case "parent": { - const middle = spawn(process.argv0, [process.argv[1], "middle"], { stdio: "pipe" }); - middle.stdout.on("data", () => {}); - break; - } - case "middle": - spawn(process.argv0, [process.argv[1], "bottom"], { stdio: [process.stdin, process.stdout, process.stderr] }); - break; - case "bottom": - process.stdin; // eslint-disable-line no-unused-expressions - break; - } - }); -} - -//<#END_FILE: test-stdio-pipe-access.js diff --git a/test/js/node/test/parallel/stdio-pipe-stderr.test.js b/test/js/node/test/parallel/stdio-pipe-stderr.test.js deleted file mode 100644 index eaae7ef20c..0000000000 --- a/test/js/node/test/parallel/stdio-pipe-stderr.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-stdio-pipe-stderr.js -//#SHA1: 5a30748a31ac72c12cd7438b96a8e09c7c8f07f7 -//----------------- -"use strict"; - -const fs = require("fs"); -const path = require("path"); -const { spawnSync } = require("child_process"); - -// Test that invoking node with require, and piping stderr to file, -// does not result in exception, -// see: https://github.com/nodejs/node/issues/11257 - -describe("stdio pipe stderr", () => { - const tmpdir = path.join(__dirname, "tmp"); - const fakeModulePath = path.join(tmpdir, "batman.js"); - const stderrOutputPath = path.join(tmpdir, "stderr-output.txt"); - - beforeAll(() => { - if (!fs.existsSync(tmpdir)) { - fs.mkdirSync(tmpdir, { recursive: true }); - } - }); - - afterAll(() => { - fs.rmSync(tmpdir, { recursive: true, force: true }); - }); - - test("piping stderr to file should not result in exception", done => { - // We need to redirect stderr to a file to produce #11257 - const stream = fs.createWriteStream(stderrOutputPath); - - // The error described in #11257 only happens when we require a - // non-built-in module. - fs.writeFileSync(fakeModulePath, "", "utf8"); - - stream.on("open", () => { - spawnSync(process.execPath, { - input: `require(${JSON.stringify(fakeModulePath)})`, - stdio: ["pipe", "pipe", stream], - }); - - const stderr = fs.readFileSync(stderrOutputPath, "utf8").trim(); - expect(stderr).toBe(""); - - stream.end(); - fs.unlinkSync(stderrOutputPath); - fs.unlinkSync(fakeModulePath); - done(); - }); - }); -}); - -//<#END_FILE: test-stdio-pipe-stderr.js diff --git a/test/js/node/test/parallel/stdout-cannot-be-closed-child-process-pipe.test.js b/test/js/node/test/parallel/stdout-cannot-be-closed-child-process-pipe.test.js deleted file mode 100644 index 1626f01a67..0000000000 --- a/test/js/node/test/parallel/stdout-cannot-be-closed-child-process-pipe.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-stdout-cannot-be-closed-child-process-pipe.js -//#SHA1: 405380c20ca8313c3f58109a4928d90eae9b79b9 -//----------------- -"use strict"; - -const { spawn } = require("child_process"); - -if (process.argv[2] === "child") { - process.stdout.end("foo"); -} else { - test("stdout cannot be closed in child process pipe", done => { - const child = spawn(process.execPath, [__filename, "child"]); - let out = ""; - let err = ""; - - child.stdout.setEncoding("utf8"); - child.stderr.setEncoding("utf8"); - - child.stdout.on("data", c => { - out += c; - }); - child.stderr.on("data", c => { - err += c; - }); - - child.on("close", (code, signal) => { - expect(code).toBe(0); - expect(err).toBe(""); - expect(out).toBe("foo"); - console.log("ok"); - done(); - }); - }); -} - -//<#END_FILE: test-stdout-cannot-be-closed-child-process-pipe.js diff --git a/test/js/node/test/parallel/stdout-stderr-write.test.js b/test/js/node/test/parallel/stdout-stderr-write.test.js deleted file mode 100644 index 3ee9dba103..0000000000 --- a/test/js/node/test/parallel/stdout-stderr-write.test.js +++ /dev/null @@ -1,13 +0,0 @@ -//#FILE: test-stdout-stderr-write.js -//#SHA1: 8811b3e776c91c3300ee6eb32b7d1ccb4d3d5d6a -//----------------- -"use strict"; - -// This test checks if process.stderr.write() and process.stdout.write() return true - -test("process.stderr.write() and process.stdout.write() return true", () => { - expect(process.stderr.write("asd")).toBe(true); - expect(process.stdout.write("asd")).toBe(true); -}); - -//<#END_FILE: test-stdout-stderr-write.js diff --git a/test/js/node/test/parallel/stream-add-abort-signal.test.js b/test/js/node/test/parallel/stream-add-abort-signal.test.js deleted file mode 100644 index 1a704c5d5b..0000000000 --- a/test/js/node/test/parallel/stream-add-abort-signal.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-stream-add-abort-signal.js -//#SHA1: 8caf14dd370aac5a01fad14026a78e1994dd3e4e -//----------------- -"use strict"; - -const { addAbortSignal, Readable } = require("stream"); - -describe("addAbortSignal", () => { - test("throws error for invalid signal", () => { - expect(() => { - addAbortSignal("INVALID_SIGNAL"); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - }); - - test("throws error for invalid stream", () => { - const ac = new AbortController(); - expect(() => { - addAbortSignal(ac.signal, "INVALID_STREAM"); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - }); -}); - -describe("addAbortSignalNoValidate", () => { - test("returns the same readable stream", () => { - const r = new Readable({ - read: () => {}, - }); - - // Since addAbortSignalNoValidate is an internal function, - // we'll skip this test in the Jest version. - // In a real-world scenario, we'd need to mock or implement this function. - expect(true).toBe(true); - }); -}); - -//<#END_FILE: test-stream-add-abort-signal.js diff --git a/test/js/node/test/parallel/stream-base-prototype-accessors-enumerability.test.js b/test/js/node/test/parallel/stream-base-prototype-accessors-enumerability.test.js deleted file mode 100644 index d1776dd952..0000000000 --- a/test/js/node/test/parallel/stream-base-prototype-accessors-enumerability.test.js +++ /dev/null @@ -1,20 +0,0 @@ -//#FILE: test-stream-base-prototype-accessors-enumerability.js -//#SHA1: a5423c2b42bae0fbdd1530553de4d40143c010cf -//----------------- -"use strict"; - -// This tests that the prototype accessors added by StreamBase::AddMethods -// are not enumerable. They could be enumerated when inspecting the prototype -// with util.inspect or the inspector protocol. - -// Or anything that calls StreamBase::AddMethods when setting up its prototype -const TTY = process.binding("tty_wrap").TTY; - -test("StreamBase prototype accessors are not enumerable", () => { - const ttyIsEnumerable = Object.prototype.propertyIsEnumerable.bind(TTY); - expect(ttyIsEnumerable("bytesRead")).toBe(false); - expect(ttyIsEnumerable("fd")).toBe(false); - expect(ttyIsEnumerable("_externalStream")).toBe(false); -}); - -//<#END_FILE: test-stream-base-prototype-accessors-enumerability.js diff --git a/test/js/node/test/parallel/stream-big-packet.test.js b/test/js/node/test/parallel/stream-big-packet.test.js deleted file mode 100644 index 01b34975c9..0000000000 --- a/test/js/node/test/parallel/stream-big-packet.test.js +++ /dev/null @@ -1,73 +0,0 @@ -//#FILE: test-stream-big-packet.js -//#SHA1: ce0f56afc4946041321028a7128eca47765fd53d -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const stream = require("stream"); - -let passed = false; - -class TestStream extends stream.Transform { - _transform(chunk, encoding, done) { - if (!passed) { - // Char 'a' only exists in the last write - passed = chunk.toString().includes("a"); - } - done(); - } -} - -test("Large buffer is handled properly by Writable Stream", done => { - const s1 = new stream.Transform({ - transform(chunk, encoding, cb) { - process.nextTick(cb, null, chunk); - }, - }); - const s2 = new stream.PassThrough(); - const s3 = new TestStream(); - s1.pipe(s3); - // Don't let s2 auto close which may close s3 - s2.pipe(s3, { end: false }); - - // We must write a buffer larger than highWaterMark - const big = Buffer.alloc(s1.writableHighWaterMark + 1, "x"); - - // Since big is larger than highWaterMark, it will be buffered internally. - expect(s1.write(big)).toBe(false); - // 'tiny' is small enough to pass through internal buffer. - expect(s2.write("tiny")).toBe(true); - - // Write some small data in next IO loop, which will never be written to s3 - // Because 'drain' event is not emitted from s1 and s1 is still paused - setImmediate(s1.write.bind(s1), "later"); - - // Assert after two IO loops when all operations have been done. - setImmediate(() => { - setImmediate(() => { - expect(passed).toBe(true); - done(); - }); - }); -}); - -//<#END_FILE: test-stream-big-packet.js diff --git a/test/js/node/test/parallel/stream-big-push.test.js b/test/js/node/test/parallel/stream-big-push.test.js deleted file mode 100644 index 664dc59bea..0000000000 --- a/test/js/node/test/parallel/stream-big-push.test.js +++ /dev/null @@ -1,87 +0,0 @@ -//#FILE: test-stream-big-push.js -//#SHA1: 833718bae7463fa469ed5acc9a1c69aa321785b7 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const stream = require("stream"); -const str = "asdfasdfasdfasdfasdf"; - -test("stream big push", async () => { - const r = new stream.Readable({ - highWaterMark: 5, - encoding: "utf8", - }); - - let reads = 0; - - function _read() { - if (reads === 0) { - setTimeout(() => { - r.push(str); - }, 1); - reads++; - } else if (reads === 1) { - const ret = r.push(str); - expect(ret).toBe(false); - reads++; - } else { - r.push(null); - } - } - - r._read = jest.fn(_read); - - const endPromise = new Promise(resolve => { - r.on("end", resolve); - }); - - // Push some data in to start. - // We've never gotten any read event at this point. - const ret = r.push(str); - // Should be false. > hwm - expect(ret).toBe(false); - let chunk = r.read(); - expect(chunk).toBe(str); - chunk = r.read(); - expect(chunk).toBeNull(); - - await new Promise(resolve => { - r.once("readable", () => { - // This time, we'll get *all* the remaining data, because - // it's been added synchronously, as the read WOULD take - // us below the hwm, and so it triggered a _read() again, - // which synchronously added more, which we then return. - chunk = r.read(); - expect(chunk).toBe(str + str); - - chunk = r.read(); - expect(chunk).toBeNull(); - resolve(); - }); - }); - - await endPromise; - expect(r._read).toHaveBeenCalledTimes(3); -}); - -//<#END_FILE: test-stream-big-push.js diff --git a/test/js/node/test/parallel/stream-decoder-objectmode.test.js b/test/js/node/test/parallel/stream-decoder-objectmode.test.js deleted file mode 100644 index a78775aa5d..0000000000 --- a/test/js/node/test/parallel/stream-decoder-objectmode.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-stream-decoder-objectmode.js -//#SHA1: 373c0c494e625b8264fae296b46f16a5cd5a9ef8 -//----------------- -"use strict"; - -const stream = require("stream"); - -test("stream.Readable with objectMode and utf16le encoding", () => { - const readable = new stream.Readable({ - read: () => {}, - encoding: "utf16le", - objectMode: true, - }); - - readable.push(Buffer.from("abc", "utf16le")); - readable.push(Buffer.from("def", "utf16le")); - readable.push(null); - - // Without object mode, these would be concatenated into a single chunk. - expect(readable.read()).toBe("abc"); - expect(readable.read()).toBe("def"); - expect(readable.read()).toBeNull(); -}); - -//<#END_FILE: test-stream-decoder-objectmode.js diff --git a/test/js/node/test/parallel/stream-destroy-event-order.test.js b/test/js/node/test/parallel/stream-destroy-event-order.test.js deleted file mode 100644 index 480b80267d..0000000000 --- a/test/js/node/test/parallel/stream-destroy-event-order.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-stream-destroy-event-order.js -//#SHA1: 0d5e12d85e093a1d7c118a2e15cf0c38c1ab96f6 -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -test("Readable stream destroy event order", () => { - const rs = new Readable({ - read() {}, - }); - - let closed = false; - let errored = false; - - rs.on("close", () => { - closed = true; - expect(errored).toBe(true); - }); - - rs.on("error", () => { - errored = true; - expect(closed).toBe(false); - }); - - rs.destroy(new Error("kaboom")); - - return new Promise(resolve => { - rs.on("close", () => { - expect(closed).toBe(true); - expect(errored).toBe(true); - resolve(); - }); - }); -}); - -//<#END_FILE: test-stream-destroy-event-order.js diff --git a/test/js/node/test/parallel/stream-duplex-props.test.js b/test/js/node/test/parallel/stream-duplex-props.test.js deleted file mode 100644 index 8fbc7f3cff..0000000000 --- a/test/js/node/test/parallel/stream-duplex-props.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-stream-duplex-props.js -//#SHA1: ae1e09a8b6631f457ad8587544a2a245f3c2ef04 -//----------------- -"use strict"; - -const { Duplex } = require("stream"); - -test("Duplex stream with same object mode and high water mark for readable and writable", () => { - const d = new Duplex({ - objectMode: true, - highWaterMark: 100, - }); - - expect(d.writableObjectMode).toBe(true); - expect(d.writableHighWaterMark).toBe(100); - expect(d.readableObjectMode).toBe(true); - expect(d.readableHighWaterMark).toBe(100); -}); - -test("Duplex stream with different object mode and high water mark for readable and writable", () => { - const d = new Duplex({ - readableObjectMode: false, - readableHighWaterMark: 10, - writableObjectMode: true, - writableHighWaterMark: 100, - }); - - expect(d.writableObjectMode).toBe(true); - expect(d.writableHighWaterMark).toBe(100); - expect(d.readableObjectMode).toBe(false); - expect(d.readableHighWaterMark).toBe(10); -}); - -//<#END_FILE: test-stream-duplex-props.js diff --git a/test/js/node/test/parallel/stream-duplex-readable-end.test.js b/test/js/node/test/parallel/stream-duplex-readable-end.test.js deleted file mode 100644 index 7ccfe779b6..0000000000 --- a/test/js/node/test/parallel/stream-duplex-readable-end.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-stream-duplex-readable-end.js -//#SHA1: 8cecff6703e081aeb94abe2b37332bc7aef28b1d -//----------------- -"use strict"; - -// https://github.com/nodejs/node/issues/35926 -const stream = require("stream"); - -test("stream duplex readable end", done => { - let loops = 5; - - const src = new stream.Readable({ - highWaterMark: 16 * 1024, - read() { - if (loops--) this.push(Buffer.alloc(20000)); - }, - }); - - const dst = new stream.Transform({ - highWaterMark: 16 * 1024, - transform(chunk, output, fn) { - this.push(null); - fn(); - }, - }); - - src.pipe(dst); - - dst.on("data", () => {}); - dst.on("end", () => { - expect(loops).toBe(3); - expect(src.isPaused()).toBe(true); - done(); - }); -}); - -//<#END_FILE: test-stream-duplex-readable-end.js diff --git a/test/js/node/test/parallel/stream-duplex-readable-writable.test.js b/test/js/node/test/parallel/stream-duplex-readable-writable.test.js deleted file mode 100644 index c37a46d6dd..0000000000 --- a/test/js/node/test/parallel/stream-duplex-readable-writable.test.js +++ /dev/null @@ -1,95 +0,0 @@ -//#FILE: test-stream-duplex-readable-writable.js -//#SHA1: d56d29f5fbb8adc3d61708839985f0ea7ffb9b5c -//----------------- -"use strict"; - -const { Duplex } = require("stream"); - -test("Duplex with readable false", () => { - const duplex = new Duplex({ - readable: false, - }); - expect(duplex.readable).toBe(false); - duplex.push("asd"); - - const errorHandler = jest.fn(); - duplex.on("error", errorHandler); - - const dataHandler = jest.fn(); - duplex.on("data", dataHandler); - - const endHandler = jest.fn(); - duplex.on("end", endHandler); - - return new Promise(resolve => { - setImmediate(() => { - expect(errorHandler).toHaveBeenCalledTimes(1); - expect(errorHandler).toHaveBeenCalledWith( - expect.objectContaining({ - code: "ERR_STREAM_PUSH_AFTER_EOF", - message: expect.any(String), - }), - ); - expect(dataHandler).not.toHaveBeenCalled(); - expect(endHandler).not.toHaveBeenCalled(); - resolve(); - }); - }); -}); - -test("Duplex with writable false", () => { - const writeSpy = jest.fn(); - const duplex = new Duplex({ - writable: false, - write: writeSpy, - }); - expect(duplex.writable).toBe(false); - duplex.write("asd"); - - const errorHandler = jest.fn(); - duplex.on("error", errorHandler); - - const finishHandler = jest.fn(); - duplex.on("finish", finishHandler); - - return new Promise(resolve => { - setImmediate(() => { - expect(errorHandler).toHaveBeenCalledTimes(1); - expect(errorHandler).toHaveBeenCalledWith( - expect.objectContaining({ - code: "ERR_STREAM_WRITE_AFTER_END", - message: expect.any(String), - }), - ); - expect(writeSpy).not.toHaveBeenCalled(); - expect(finishHandler).not.toHaveBeenCalled(); - resolve(); - }); - }); -}); - -test("Duplex with readable false and async iteration", async () => { - const duplex = new Duplex({ - readable: false, - }); - expect(duplex.readable).toBe(false); - - const dataHandler = jest.fn(); - duplex.on("data", dataHandler); - - const endHandler = jest.fn(); - duplex.on("end", endHandler); - - async function run() { - for await (const chunk of duplex) { - expect(chunk).toBeFalsy(); // This should never be reached - } - } - - await run(); - - expect(dataHandler).not.toHaveBeenCalled(); - expect(endHandler).not.toHaveBeenCalled(); -}); - -//<#END_FILE: test-stream-duplex-readable-writable.js diff --git a/test/js/node/test/parallel/stream-duplex-writable-finished.test.js b/test/js/node/test/parallel/stream-duplex-writable-finished.test.js deleted file mode 100644 index 0a2400bb78..0000000000 --- a/test/js/node/test/parallel/stream-duplex-writable-finished.test.js +++ /dev/null @@ -1,33 +0,0 @@ -//#FILE: test-stream-duplex-writable-finished.js -//#SHA1: ba8c61c576c3a900076baa134c8a0d6876e84db5 -//----------------- -"use strict"; - -const { Duplex } = require("stream"); - -// basic -test("Duplex.prototype has writableFinished", () => { - expect(Object.hasOwn(Duplex.prototype, "writableFinished")).toBe(true); -}); - -// event -test("writableFinished state changes correctly", done => { - const duplex = new Duplex(); - - duplex._write = (chunk, encoding, cb) => { - // The state finished should start in false. - expect(duplex.writableFinished).toBe(false); - cb(); - }; - - duplex.on("finish", () => { - expect(duplex.writableFinished).toBe(true); - }); - - duplex.end("testing finished state", () => { - expect(duplex.writableFinished).toBe(true); - done(); - }); -}); - -//<#END_FILE: test-stream-duplex-writable-finished.js diff --git a/test/js/node/test/parallel/stream-end-of-streams.test.js b/test/js/node/test/parallel/stream-end-of-streams.test.js deleted file mode 100644 index af768bc70e..0000000000 --- a/test/js/node/test/parallel/stream-end-of-streams.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-stream-end-of-streams.js -//#SHA1: 3fa13e31cef06059026b0fcf90c151a8a975752c -//----------------- -"use strict"; - -const { Duplex, finished } = require("stream"); - -test("finished function with invalid stream", () => { - // Passing empty object to mock invalid stream - // should throw error - expect(() => { - finished({}, () => {}); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); -}); - -test("finished function with valid stream", () => { - const streamObj = new Duplex(); - streamObj.end(); - // Below code should not throw any errors as the - // streamObj is `Stream` - expect(() => { - finished(streamObj, () => {}); - }).not.toThrow(); -}); - -//<#END_FILE: test-stream-end-of-streams.js diff --git a/test/js/node/test/parallel/stream-end-paused.test.js b/test/js/node/test/parallel/stream-end-paused.test.js deleted file mode 100644 index 1022f980ef..0000000000 --- a/test/js/node/test/parallel/stream-end-paused.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-stream-end-paused.js -//#SHA1: 4ebe901f30ba0469bb75c7f9f7ba5316ceb271a5 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const { Readable } = require("stream"); - -test("end event for paused 0-length streams", done => { - // Make sure we don't miss the end event for paused 0-length streams - - const stream = new Readable(); - let calledRead = false; - stream._read = function () { - expect(calledRead).toBe(false); - calledRead = true; - this.push(null); - }; - - stream.on("data", () => { - throw new Error("should not ever get data"); - }); - stream.pause(); - - setTimeout(() => { - stream.on("end", () => { - expect(calledRead).toBe(true); - done(); - }); - stream.resume(); - }, 1); -}); - -//<#END_FILE: test-stream-end-paused.js diff --git a/test/js/node/test/parallel/stream-events-prepend.test.js b/test/js/node/test/parallel/stream-events-prepend.test.js deleted file mode 100644 index 590421f79d..0000000000 --- a/test/js/node/test/parallel/stream-events-prepend.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-stream-events-prepend.js -//#SHA1: db830318b8c5a2e990a75320319c8fcd96fa760a -//----------------- -"use strict"; -const stream = require("stream"); - -class Writable extends stream.Writable { - constructor() { - super(); - this.prependListener = undefined; - } - - _write(chunk, end, cb) { - cb(); - } -} - -class Readable extends stream.Readable { - _read() { - this.push(null); - } -} - -test("pipe event is emitted even when prependListener is undefined", () => { - const w = new Writable(); - const pipeSpy = jest.fn(); - w.on("pipe", pipeSpy); - - const r = new Readable(); - r.pipe(w); - - expect(pipeSpy).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-stream-events-prepend.js diff --git a/test/js/node/test/parallel/stream-once-readable-pipe.test.js b/test/js/node/test/parallel/stream-once-readable-pipe.test.js deleted file mode 100644 index 71b0c32698..0000000000 --- a/test/js/node/test/parallel/stream-once-readable-pipe.test.js +++ /dev/null @@ -1,75 +0,0 @@ -//#FILE: test-stream-once-readable-pipe.js -//#SHA1: 4f12e7a8a1c06ba3cef54605469eb67d23306aa3 -//----------------- -"use strict"; - -const { Readable, Writable } = require("stream"); - -// This test ensures that if have 'readable' listener -// on Readable instance it will not disrupt the pipe. - -test("readable listener before pipe", () => { - let receivedData = ""; - const w = new Writable({ - write: (chunk, env, callback) => { - receivedData += chunk; - callback(); - }, - }); - - const data = ["foo", "bar", "baz"]; - const r = new Readable({ - read: () => {}, - }); - - const readableSpy = jest.fn(); - r.once("readable", readableSpy); - - r.pipe(w); - r.push(data[0]); - r.push(data[1]); - r.push(data[2]); - r.push(null); - - return new Promise(resolve => { - w.on("finish", () => { - expect(receivedData).toBe(data.join("")); - expect(readableSpy).toHaveBeenCalledTimes(1); - resolve(); - }); - }); -}); - -test("readable listener after pipe", () => { - let receivedData = ""; - const w = new Writable({ - write: (chunk, env, callback) => { - receivedData += chunk; - callback(); - }, - }); - - const data = ["foo", "bar", "baz"]; - const r = new Readable({ - read: () => {}, - }); - - r.pipe(w); - r.push(data[0]); - r.push(data[1]); - r.push(data[2]); - r.push(null); - - const readableSpy = jest.fn(); - r.once("readable", readableSpy); - - return new Promise(resolve => { - w.on("finish", () => { - expect(receivedData).toBe(data.join("")); - expect(readableSpy).toHaveBeenCalledTimes(1); - resolve(); - }); - }); -}); - -//<#END_FILE: test-stream-once-readable-pipe.js diff --git a/test/js/node/test/parallel/stream-passthrough-drain.test.js b/test/js/node/test/parallel/stream-passthrough-drain.test.js deleted file mode 100644 index b77fa6daf3..0000000000 --- a/test/js/node/test/parallel/stream-passthrough-drain.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-stream-passthrough-drain.js -//#SHA1: c17561a8fc9a14d7abc05af3528d0ead32502a57 -//----------------- -"use strict"; -const { PassThrough } = require("stream"); - -test("PassThrough stream emits drain event when buffer is emptied", () => { - const pt = new PassThrough({ highWaterMark: 0 }); - - const drainHandler = jest.fn(); - pt.on("drain", drainHandler); - - expect(pt.write("hello1")).toBe(false); - - pt.read(); - pt.read(); - - // Use process.nextTick to ensure the drain event has a chance to fire - return new Promise(resolve => { - process.nextTick(() => { - expect(drainHandler).toHaveBeenCalledTimes(1); - resolve(); - }); - }); -}); - -//<#END_FILE: test-stream-passthrough-drain.js diff --git a/test/js/node/test/parallel/stream-pipe-event.test.js b/test/js/node/test/parallel/stream-pipe-event.test.js deleted file mode 100644 index 5c7c0626f9..0000000000 --- a/test/js/node/test/parallel/stream-pipe-event.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-stream-pipe-event.js -//#SHA1: 63887b8cce85a4c7cfa27c8111edd14330a2078f -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const stream = require("stream"); - -function Writable() { - this.writable = true; - stream.Stream.call(this); -} -Object.setPrototypeOf(Writable.prototype, stream.Stream.prototype); -Object.setPrototypeOf(Writable, stream.Stream); - -function Readable() { - this.readable = true; - stream.Stream.call(this); -} -Object.setPrototypeOf(Readable.prototype, stream.Stream.prototype); -Object.setPrototypeOf(Readable, stream.Stream); - -test("pipe event is emitted", () => { - let passed = false; - - const w = new Writable(); - w.on("pipe", function (src) { - passed = true; - }); - - const r = new Readable(); - r.pipe(w); - - expect(passed).toBe(true); -}); - -//<#END_FILE: test-stream-pipe-event.js diff --git a/test/js/node/test/parallel/stream-pipe-needdrain.test.js b/test/js/node/test/parallel/stream-pipe-needdrain.test.js deleted file mode 100644 index 42e4b2e9f3..0000000000 --- a/test/js/node/test/parallel/stream-pipe-needdrain.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-stream-pipe-needDrain.js -//#SHA1: 5c524a253a770fcbb95365aea011a38a665c61da -//----------------- -"use strict"; - -const { Readable, Writable } = require("stream"); - -// Pipe should pause temporarily if writable needs drain. -test("Pipe pauses when writable needs drain", done => { - const w = new Writable({ - write(buf, encoding, callback) { - process.nextTick(callback); - }, - highWaterMark: 1, - }); - - while (w.write("asd")); - - expect(w.writableNeedDrain).toBe(true); - - const r = new Readable({ - read() { - this.push("asd"); - this.push(null); - }, - }); - - const pauseSpy = jest.fn(); - r.on("pause", pauseSpy); - - const endSpy = jest.fn().mockImplementation(() => { - expect(pauseSpy).toHaveBeenCalledTimes(2); - done(); - }); - r.on("end", endSpy); - - r.pipe(w); -}); - -//<#END_FILE: test-stream-pipe-needDrain.js diff --git a/test/js/node/test/parallel/stream-preprocess.test.js b/test/js/node/test/parallel/stream-preprocess.test.js deleted file mode 100644 index 8c5dd3cc8a..0000000000 --- a/test/js/node/test/parallel/stream-preprocess.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-stream-preprocess.js -//#SHA1: 4061428f95671f257c4a57a92d77c0dc63a1394a -//----------------- -"use strict"; - -const fs = require("fs"); -const rl = require("readline"); -const fixtures = require("../common/fixtures"); - -const BOM = "\uFEFF"; - -// Get the data using a non-stream way to compare with the streamed data. -const modelData = fixtures.readSync("file-to-read-without-bom.txt", "utf8"); -const modelDataFirstCharacter = modelData[0]; - -// Detect the number of forthcoming 'line' events for mustCall() 'expected' arg. -const lineCount = modelData.match(/\n/g).length; - -test("Ensure both without-bom and with-bom test files are textwise equal", () => { - expect(fixtures.readSync("file-to-read-with-bom.txt", "utf8")).toBe(`${BOM}${modelData}`); -}); - -test("An unjustified BOM stripping with a non-BOM character unshifted to a stream", done => { - const inputWithoutBOM = fs.createReadStream(fixtures.path("file-to-read-without-bom.txt"), "utf8"); - - inputWithoutBOM.once("readable", () => { - const maybeBOM = inputWithoutBOM.read(1); - expect(maybeBOM).toBe(modelDataFirstCharacter); - expect(maybeBOM).not.toBe(BOM); - - inputWithoutBOM.unshift(maybeBOM); - - let streamedData = ""; - rl.createInterface({ - input: inputWithoutBOM, - }) - .on("line", line => { - streamedData += `${line}\n`; - }) - .on("close", () => { - expect(streamedData).toBe(modelData); - done(); - }); - }); -}); - -test("A justified BOM stripping", done => { - const inputWithBOM = fs.createReadStream(fixtures.path("file-to-read-with-bom.txt"), "utf8"); - - inputWithBOM.once("readable", () => { - const maybeBOM = inputWithBOM.read(1); - expect(maybeBOM).toBe(BOM); - - let streamedData = ""; - rl.createInterface({ - input: inputWithBOM, - }) - .on("line", line => { - streamedData += `${line}\n`; - }) - .on("close", () => { - expect(streamedData).toBe(modelData); - done(); - }); - }); -}); - -//<#END_FILE: test-stream-preprocess.js diff --git a/test/js/node/test/parallel/stream-push-strings.test.js b/test/js/node/test/parallel/stream-push-strings.test.js deleted file mode 100644 index fecd87ceaa..0000000000 --- a/test/js/node/test/parallel/stream-push-strings.test.js +++ /dev/null @@ -1,72 +0,0 @@ -//#FILE: test-stream-push-strings.js -//#SHA1: d2da34fc74795ea8cc46460ce443d0b30b0e98d8 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const { Readable } = require("stream"); - -class MyStream extends Readable { - constructor(options) { - super(options); - this._chunks = 3; - } - - _read(n) { - switch (this._chunks--) { - case 0: - return this.push(null); - case 1: - return setTimeout(() => { - this.push("last chunk"); - }, 100); - case 2: - return this.push("second to last chunk"); - case 3: - return process.nextTick(() => { - this.push("first chunk"); - }); - default: - throw new Error("?"); - } - } -} - -test("MyStream pushes strings correctly", done => { - const ms = new MyStream(); - const results = []; - ms.on("readable", function () { - let chunk; - while (null !== (chunk = ms.read())) results.push(String(chunk)); - }); - - const expected = ["first chunksecond to last chunk", "last chunk"]; - - ms.on("end", () => { - expect(ms._chunks).toBe(-1); - expect(results).toEqual(expected); - done(); - }); -}); - -//<#END_FILE: test-stream-push-strings.js diff --git a/test/js/node/test/parallel/stream-readable-aborted.test.js b/test/js/node/test/parallel/stream-readable-aborted.test.js deleted file mode 100644 index 3d005e5901..0000000000 --- a/test/js/node/test/parallel/stream-readable-aborted.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-stream-readable-aborted.js -//#SHA1: b4d59c7cd8eda084bae2d2ff603dd153aff79f98 -//----------------- -"use strict"; - -const { Readable, Duplex } = require("stream"); - -test("Readable stream aborted state", () => { - const readable = new Readable({ - read() {}, - }); - expect(readable.readableAborted).toBe(false); - readable.destroy(); - expect(readable.readableAborted).toBe(true); -}); - -test("Readable stream aborted state after push(null)", () => { - const readable = new Readable({ - read() {}, - }); - expect(readable.readableAborted).toBe(false); - readable.push(null); - readable.destroy(); - expect(readable.readableAborted).toBe(true); -}); - -test("Readable stream aborted state after push(data)", () => { - const readable = new Readable({ - read() {}, - }); - expect(readable.readableAborted).toBe(false); - readable.push("asd"); - readable.destroy(); - expect(readable.readableAborted).toBe(true); -}); - -test("Readable stream aborted state after end event", done => { - const readable = new Readable({ - read() {}, - }); - expect(readable.readableAborted).toBe(false); - readable.push("asd"); - readable.push(null); - expect(readable.readableAborted).toBe(false); - readable.on("end", () => { - expect(readable.readableAborted).toBe(false); - readable.destroy(); - expect(readable.readableAborted).toBe(false); - queueMicrotask(() => { - expect(readable.readableAborted).toBe(false); - done(); - }); - }); - readable.resume(); -}); - -test("Duplex stream with readable false", () => { - const duplex = new Duplex({ - readable: false, - write() {}, - }); - duplex.destroy(); - expect(duplex.readableAborted).toBe(false); -}); - -//<#END_FILE: test-stream-readable-aborted.js diff --git a/test/js/node/test/parallel/stream-readable-add-chunk-during-data.test.js b/test/js/node/test/parallel/stream-readable-add-chunk-during-data.test.js deleted file mode 100644 index 2a75e47cbf..0000000000 --- a/test/js/node/test/parallel/stream-readable-add-chunk-during-data.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-stream-readable-add-chunk-during-data.js -//#SHA1: f34525f5ea022c837ba1e40e98b1b41da5df50b2 -//----------------- -"use strict"; -const { Readable } = require("stream"); - -// Verify that .push() and .unshift() can be called from 'data' listeners. - -["push", "unshift"].forEach(method => { - test(`Readable ${method} can be called from 'data' listeners`, done => { - const r = new Readable({ read() {} }); - - r.once("data", chunk => { - expect(r.readableLength).toBe(0); - r[method](chunk); - expect(r.readableLength).toBe(chunk.length); - - r.on("data", newChunk => { - expect(newChunk.toString()).toBe("Hello, world"); - done(); - }); - }); - - r.push("Hello, world"); - }); -}); - -//<#END_FILE: test-stream-readable-add-chunk-during-data.js diff --git a/test/js/node/test/parallel/stream-readable-data.test.js b/test/js/node/test/parallel/stream-readable-data.test.js deleted file mode 100644 index b654147c26..0000000000 --- a/test/js/node/test/parallel/stream-readable-data.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-stream-readable-data.js -//#SHA1: c679a0f31d84cd65d9e85ab0617abfd65a053afc -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -test("Readable stream emits data event after removing readable listener", done => { - const readable = new Readable({ - read() {}, - }); - - function read() {} - - readable.setEncoding("utf8"); - readable.on("readable", read); - readable.removeListener("readable", read); - - process.nextTick(() => { - const dataHandler = jest.fn(); - readable.on("data", dataHandler); - readable.push("hello"); - - // Use setImmediate to ensure the data event has time to be emitted - setImmediate(() => { - expect(dataHandler).toHaveBeenCalledTimes(1); - done(); - }); - }); -}); - -//<#END_FILE: test-stream-readable-data.js diff --git a/test/js/node/test/parallel/stream-readable-emittedreadable.test.js b/test/js/node/test/parallel/stream-readable-emittedreadable.test.js deleted file mode 100644 index 744dba984c..0000000000 --- a/test/js/node/test/parallel/stream-readable-emittedreadable.test.js +++ /dev/null @@ -1,111 +0,0 @@ -//#FILE: test-stream-readable-emittedReadable.js -//#SHA1: 1c9463d9bb0e7927d8d18949aae849a04cc034fe -//----------------- -"use strict"; -const { Readable } = require("stream"); - -describe("Readable Stream emittedReadable", () => { - test("emittedReadable state changes correctly", () => { - const readable = new Readable({ - read: () => {}, - }); - - // Initialized to false. - expect(readable._readableState.emittedReadable).toBe(false); - - const expected = [Buffer.from("foobar"), Buffer.from("quo"), null]; - const readableSpy = jest.fn(() => { - // emittedReadable should be true when the readable event is emitted - expect(readable._readableState.emittedReadable).toBe(true); - expect(readable.read()).toEqual(expected.shift()); - // emittedReadable is reset to false during read() - expect(readable._readableState.emittedReadable).toBe(false); - }); - - readable.on("readable", readableSpy); - - // When the first readable listener is just attached, - // emittedReadable should be false - expect(readable._readableState.emittedReadable).toBe(false); - - // These trigger a single 'readable', as things are batched up - process.nextTick(() => { - readable.push("foo"); - }); - process.nextTick(() => { - readable.push("bar"); - }); - - // These triggers two readable events - setImmediate(() => { - readable.push("quo"); - process.nextTick(() => { - readable.push(null); - }); - }); - - return new Promise(resolve => { - setTimeout(() => { - expect(readableSpy).toHaveBeenCalledTimes(3); - resolve(); - }, 100); - }); - }); - - test("emittedReadable with read(0)", () => { - const noRead = new Readable({ - read: () => {}, - }); - - const readableSpy = jest.fn(() => { - // emittedReadable should be true when the readable event is emitted - expect(noRead._readableState.emittedReadable).toBe(true); - noRead.read(0); - // emittedReadable is not reset during read(0) - expect(noRead._readableState.emittedReadable).toBe(true); - }); - - noRead.on("readable", readableSpy); - - noRead.push("foo"); - noRead.push(null); - - return new Promise(resolve => { - setTimeout(() => { - expect(readableSpy).toHaveBeenCalledTimes(1); - resolve(); - }, 100); - }); - }); - - test("emittedReadable in flowing mode", () => { - const flowing = new Readable({ - read: () => {}, - }); - - const dataSpy = jest.fn(() => { - // When in flowing mode, emittedReadable is always false. - expect(flowing._readableState.emittedReadable).toBe(false); - flowing.read(); - expect(flowing._readableState.emittedReadable).toBe(false); - }); - - flowing.on("data", dataSpy); - - flowing.push("foooo"); - flowing.push("bar"); - flowing.push("quo"); - process.nextTick(() => { - flowing.push(null); - }); - - return new Promise(resolve => { - setTimeout(() => { - expect(dataSpy).toHaveBeenCalledTimes(3); - resolve(); - }, 100); - }); - }); -}); - -//<#END_FILE: test-stream-readable-emittedReadable.js diff --git a/test/js/node/test/parallel/stream-readable-end-destroyed.test.js b/test/js/node/test/parallel/stream-readable-end-destroyed.test.js deleted file mode 100644 index c8b98db765..0000000000 --- a/test/js/node/test/parallel/stream-readable-end-destroyed.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-stream-readable-end-destroyed.js -//#SHA1: 20c8bb870db11018d2eaa1c9e4dece071917d03d -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -test("Don't emit 'end' after 'close'", () => { - const r = new Readable(); - - const endListener = jest.fn(); - r.on("end", endListener); - r.resume(); - r.destroy(); - - return new Promise(resolve => { - r.on("close", () => { - r.push(null); - expect(endListener).not.toHaveBeenCalled(); - resolve(); - }); - }); -}); - -//<#END_FILE: test-stream-readable-end-destroyed.js diff --git a/test/js/node/test/parallel/stream-readable-ended.test.js b/test/js/node/test/parallel/stream-readable-ended.test.js deleted file mode 100644 index fb6a3c3b64..0000000000 --- a/test/js/node/test/parallel/stream-readable-ended.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-stream-readable-ended.js -//#SHA1: 93aa267630bb32f94783c5bbdeb8e345c0acd94a -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -// basic -test("Readable.prototype has readableEnded property", () => { - expect(Object.hasOwn(Readable.prototype, "readableEnded")).toBe(true); -}); - -// event -test("readableEnded state changes correctly", done => { - const readable = new Readable(); - - readable._read = () => { - // The state ended should start in false. - expect(readable.readableEnded).toBe(false); - readable.push("asd"); - expect(readable.readableEnded).toBe(false); - readable.push(null); - expect(readable.readableEnded).toBe(false); - }; - - readable.on("end", () => { - expect(readable.readableEnded).toBe(true); - done(); - }); - - readable.on("data", () => { - expect(readable.readableEnded).toBe(false); - }); -}); - -// Verifies no `error` triggered on multiple .push(null) invocations -test("No error triggered on multiple .push(null) invocations", done => { - const readable = new Readable(); - - readable.on("readable", () => { - readable.read(); - }); - - const errorHandler = jest.fn(); - readable.on("error", errorHandler); - - readable.on("end", () => { - expect(errorHandler).not.toHaveBeenCalled(); - done(); - }); - - readable.push("a"); - readable.push(null); - readable.push(null); -}); - -//<#END_FILE: test-stream-readable-ended.js diff --git a/test/js/node/test/parallel/stream-readable-event.test.js b/test/js/node/test/parallel/stream-readable-event.test.js deleted file mode 100644 index 688f207d1b..0000000000 --- a/test/js/node/test/parallel/stream-readable-event.test.js +++ /dev/null @@ -1,146 +0,0 @@ -//#FILE: test-stream-readable-event.js -//#SHA1: 8a3da958252097730dcd22e82d325d106d5512a5 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const { Readable } = require("stream"); - -test("not reading when the readable is added", done => { - const r = new Readable({ - highWaterMark: 3, - }); - - r._read = jest.fn(); - - // This triggers a 'readable' event, which is lost. - r.push(Buffer.from("blerg")); - - setTimeout(() => { - // We're testing what we think we are - expect(r._readableState.reading).toBe(false); - const readableSpy = jest.fn(); - r.on("readable", readableSpy); - - // Allow time for the 'readable' event to potentially fire - setTimeout(() => { - expect(readableSpy).toHaveBeenCalled(); - expect(r._read).not.toHaveBeenCalled(); - done(); - }, 10); - }, 1); -}); - -test("readable is re-emitted if there's already a length, while it IS reading", done => { - const r = new Readable({ - highWaterMark: 3, - }); - - r._read = jest.fn(); - - // This triggers a 'readable' event, which is lost. - r.push(Buffer.from("bl")); - - setTimeout(() => { - // Assert we're testing what we think we are - expect(r._readableState.reading).toBe(true); - const readableSpy = jest.fn(); - r.on("readable", readableSpy); - - // Allow time for the 'readable' event to potentially fire - setTimeout(() => { - expect(readableSpy).toHaveBeenCalled(); - expect(r._read).toHaveBeenCalled(); - done(); - }, 10); - }, 1); -}); - -test("not reading when the stream has not passed the highWaterMark but has reached EOF", done => { - const r = new Readable({ - highWaterMark: 30, - }); - - r._read = jest.fn(); - - // This triggers a 'readable' event, which is lost. - r.push(Buffer.from("blerg")); - r.push(null); - - setTimeout(() => { - // Assert we're testing what we think we are - expect(r._readableState.reading).toBe(false); - const readableSpy = jest.fn(); - r.on("readable", readableSpy); - - // Allow time for the 'readable' event to potentially fire - setTimeout(() => { - expect(readableSpy).toHaveBeenCalled(); - expect(r._read).not.toHaveBeenCalled(); - done(); - }, 10); - }, 1); -}); - -test("Pushing an empty string in non-objectMode should trigger next `read()`", done => { - const underlyingData = ["", "x", "y", "", "z"]; - const expected = underlyingData.filter(data => data); - const result = []; - - const r = new Readable({ - encoding: "utf8", - }); - r._read = function () { - process.nextTick(() => { - if (!underlyingData.length) { - this.push(null); - } else { - this.push(underlyingData.shift()); - } - }); - }; - - r.on("readable", () => { - const data = r.read(); - if (data !== null) result.push(data); - }); - - r.on("end", () => { - expect(result).toEqual(expected); - done(); - }); -}); - -test("#20923 - removeAllListeners should clear all event listeners", () => { - const r = new Readable(); - r._read = function () { - // Actually doing thing here - }; - r.on("data", function () {}); - - r.removeAllListeners(); - - expect(r.eventNames().length).toBe(0); -}); - -//<#END_FILE: test-stream-readable-event.js diff --git a/test/js/node/test/parallel/stream-readable-hwm-0-async.test.js b/test/js/node/test/parallel/stream-readable-hwm-0-async.test.js deleted file mode 100644 index 0d2388c333..0000000000 --- a/test/js/node/test/parallel/stream-readable-hwm-0-async.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-stream-readable-hwm-0-async.js -//#SHA1: ddaa3718bf6d6ae9258293494ac3449800169768 -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -// This test ensures that Readable stream will continue to call _read -// for streams with highWaterMark === 0 once the stream returns data -// by calling push() asynchronously. - -test("Readable stream with highWaterMark 0 and async push", async () => { - let count = 5; - const readMock = jest.fn(() => { - process.nextTick(() => { - if (count--) { - r.push("a"); - } else { - r.push(null); - } - }); - }); - - const r = new Readable({ - read: readMock, - highWaterMark: 0, - }); - - const dataHandler = jest.fn(); - const endHandler = jest.fn(); - - r.on("data", dataHandler); - r.on("end", endHandler); - - // Consume the stream - for await (const chunk of r) { - // This loop will iterate 5 times - } - - // Called 6 times: First 5 return data, last one signals end of stream. - expect(readMock).toHaveBeenCalledTimes(6); - expect(dataHandler).toHaveBeenCalledTimes(5); - expect(endHandler).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-stream-readable-hwm-0-async.js diff --git a/test/js/node/test/parallel/stream-readable-hwm-0.test.js b/test/js/node/test/parallel/stream-readable-hwm-0.test.js deleted file mode 100644 index 3d8266581d..0000000000 --- a/test/js/node/test/parallel/stream-readable-hwm-0.test.js +++ /dev/null @@ -1,52 +0,0 @@ -//#FILE: test-stream-readable-hwm-0.js -//#SHA1: 986085294672eff1ba0a13f99633edd30c3fb54a -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -// This test ensures that Readable stream will call _read() for streams -// with highWaterMark === 0 upon .read(0) instead of just trying to -// emit 'readable' event. - -test("Readable stream with highWaterMark 0 calls _read()", () => { - const mockRead = jest.fn(); - - const r = new Readable({ - // Must be called only once upon setting 'readable' listener - read: mockRead, - highWaterMark: 0, - }); - - let pushedNull = false; - - // This will trigger read(0) but must only be called after push(null) - // because we haven't pushed any data - r.on( - "readable", - jest.fn(() => { - expect(r.read()).toBeNull(); - expect(pushedNull).toBe(true); - }), - ); - - const endHandler = jest.fn(); - r.on("end", endHandler); - - return new Promise(resolve => { - process.nextTick(() => { - expect(r.read()).toBeNull(); - pushedNull = true; - r.push(null); - - // Use setImmediate to ensure all events have been processed - setImmediate(() => { - expect(mockRead).toHaveBeenCalledTimes(1); - expect(endHandler).toHaveBeenCalledTimes(1); - resolve(); - }); - }); - }); -}); - -//<#END_FILE: test-stream-readable-hwm-0.js diff --git a/test/js/node/test/parallel/stream-readable-needreadable.test.js b/test/js/node/test/parallel/stream-readable-needreadable.test.js deleted file mode 100644 index 171eeee0df..0000000000 --- a/test/js/node/test/parallel/stream-readable-needreadable.test.js +++ /dev/null @@ -1,131 +0,0 @@ -//#FILE: test-stream-readable-needReadable.js -//#SHA1: 301ca49c86e59196821c0fcd419c71f5ffd4a94d -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -describe("Readable stream needReadable property", () => { - test("Initial state and readable event", () => { - const readable = new Readable({ - read: () => {}, - }); - - // Initialized to false. - expect(readable._readableState.needReadable).toBe(false); - - readable.on("readable", () => { - // When the readable event fires, needReadable is reset. - expect(readable._readableState.needReadable).toBe(false); - readable.read(); - }); - - // If a readable listener is attached, then a readable event is needed. - expect(readable._readableState.needReadable).toBe(true); - - readable.push("foo"); - readable.push(null); - - return new Promise(resolve => { - readable.on("end", () => { - // No need to emit readable anymore when the stream ends. - expect(readable._readableState.needReadable).toBe(false); - resolve(); - }); - }); - }); - - test("Async readable stream", done => { - const asyncReadable = new Readable({ - read: () => {}, - }); - - let readableCallCount = 0; - asyncReadable.on("readable", () => { - if (asyncReadable.read() !== null) { - // After each read(), the buffer is empty. - // If the stream doesn't end now, - // then we need to notify the reader on future changes. - expect(asyncReadable._readableState.needReadable).toBe(true); - } - readableCallCount++; - if (readableCallCount === 2) { - done(); - } - }); - - process.nextTick(() => { - asyncReadable.push("foooo"); - }); - process.nextTick(() => { - asyncReadable.push("bar"); - }); - setImmediate(() => { - asyncReadable.push(null); - expect(asyncReadable._readableState.needReadable).toBe(false); - }); - }); - - test("Flowing mode", done => { - const flowing = new Readable({ - read: () => {}, - }); - - // Notice this must be above the on('data') call. - flowing.push("foooo"); - flowing.push("bar"); - flowing.push("quo"); - process.nextTick(() => { - flowing.push(null); - }); - - let dataCallCount = 0; - // When the buffer already has enough data, and the stream is - // in flowing mode, there is no need for the readable event. - flowing.on("data", data => { - expect(flowing._readableState.needReadable).toBe(false); - dataCallCount++; - if (dataCallCount === 3) { - done(); - } - }); - }); - - test("Slow producer", done => { - const slowProducer = new Readable({ - read: () => {}, - }); - - let readableCallCount = 0; - slowProducer.on("readable", () => { - const chunk = slowProducer.read(8); - const state = slowProducer._readableState; - if (chunk === null) { - // The buffer doesn't have enough data, and the stream is not end, - // we need to notify the reader when data arrives. - expect(state.needReadable).toBe(true); - } else { - expect(state.needReadable).toBe(false); - } - readableCallCount++; - if (readableCallCount === 4) { - done(); - } - }); - - process.nextTick(() => { - slowProducer.push("foo"); - process.nextTick(() => { - slowProducer.push("foo"); - process.nextTick(() => { - slowProducer.push("foo"); - process.nextTick(() => { - slowProducer.push(null); - }); - }); - }); - }); - }); -}); - -//<#END_FILE: test-stream-readable-needReadable.js diff --git a/test/js/node/test/parallel/stream-readable-next-no-null.test.js b/test/js/node/test/parallel/stream-readable-next-no-null.test.js deleted file mode 100644 index c0129802e9..0000000000 --- a/test/js/node/test/parallel/stream-readable-next-no-null.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-stream-readable-next-no-null.js -//#SHA1: b3362d071d6f13ce317db2b897ff1146441d1bea -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -describe("Readable.from with null value", () => { - it("should throw ERR_STREAM_NULL_VALUES error", async () => { - async function* generate() { - yield null; - } - - const stream = Readable.from(generate()); - - const errorPromise = new Promise(resolve => { - stream.on("error", error => { - resolve(error); - }); - }); - - const dataPromise = new Promise(resolve => { - stream.on("data", () => { - resolve("data"); - }); - }); - - const endPromise = new Promise(resolve => { - stream.on("end", () => { - resolve("end"); - }); - }); - - await expect(errorPromise).resolves.toEqual( - expect.objectContaining({ - code: "ERR_STREAM_NULL_VALUES", - name: "TypeError", - message: expect.any(String), - }), - ); - - await expect(Promise.race([dataPromise, endPromise, errorPromise])).resolves.not.toBe("data"); - await expect(Promise.race([dataPromise, endPromise, errorPromise])).resolves.not.toBe("end"); - }); -}); - -//<#END_FILE: test-stream-readable-next-no-null.js diff --git a/test/js/node/test/parallel/stream-readable-readable-then-resume.test.js b/test/js/node/test/parallel/stream-readable-readable-then-resume.test.js deleted file mode 100644 index e542119c61..0000000000 --- a/test/js/node/test/parallel/stream-readable-readable-then-resume.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-stream-readable-readable-then-resume.js -//#SHA1: 79790a4cd766421a6891704bf157072eaef2d5b4 -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -// This test verifies that a stream could be resumed after -// removing the readable event in the same tick - -function check(s) { - const readableListener = jest.fn(); - s.on("readable", readableListener); - s.on("end", jest.fn()); - expect(s.removeListener).toBe(s.off); - s.removeListener("readable", readableListener); - s.resume(); - - expect(readableListener).not.toHaveBeenCalled(); -} - -test("Readable stream can be resumed after removing readable event", () => { - const s = new Readable({ - objectMode: true, - highWaterMark: 1, - read() { - if (!this.first) { - this.push("hello"); - this.first = true; - return; - } - - this.push(null); - }, - }); - - check(s); -}); - -//<#END_FILE: test-stream-readable-readable-then-resume.js diff --git a/test/js/node/test/parallel/stream-readable-readable.test.js b/test/js/node/test/parallel/stream-readable-readable.test.js deleted file mode 100644 index cb0b9c2dba..0000000000 --- a/test/js/node/test/parallel/stream-readable-readable.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-stream-readable-readable.js -//#SHA1: f2d897473803968b7ee8efb20a8f1f374987980b -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -describe("Readable.readable", () => { - test("readable property is set correctly", () => { - const r = new Readable({ - read() {}, - }); - expect(r.readable).toBe(true); - r.destroy(); - expect(r.readable).toBe(false); - }); - - test("readable property remains true until end event", () => { - const r = new Readable({ - read() {}, - }); - expect(r.readable).toBe(true); - - const endHandler = jest.fn(); - r.on("end", endHandler); - r.resume(); - r.push(null); - expect(r.readable).toBe(true); - r.off("end", endHandler); - - return new Promise(resolve => { - r.on("end", () => { - expect(r.readable).toBe(false); - resolve(); - }); - }); - }); - - test("readable property becomes false on error", () => { - const r = new Readable({ - read: jest.fn(() => { - process.nextTick(() => { - r.destroy(new Error()); - expect(r.readable).toBe(false); - }); - }), - }); - r.resume(); - - return new Promise(resolve => { - r.on("error", () => { - expect(r.readable).toBe(false); - resolve(); - }); - }); - }); -}); - -//<#END_FILE: test-stream-readable-readable.js diff --git a/test/js/node/test/parallel/stream-readable-resume-hwm.test.js b/test/js/node/test/parallel/stream-readable-resume-hwm.test.js deleted file mode 100644 index 7e18b7f9a0..0000000000 --- a/test/js/node/test/parallel/stream-readable-resume-hwm.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-stream-readable-resume-hwm.js -//#SHA1: 8149b27327258da89c087856f54e7e7584ddf1e5 -//----------------- -"use strict"; -const { Readable } = require("stream"); - -// readable.resume() should not lead to a ._read() call being scheduled -// when we exceed the high water mark already. - -test("readable.resume() should not call _read() when exceeding highWaterMark", () => { - const mockRead = jest.fn(); - const readable = new Readable({ - read: mockRead, - highWaterMark: 100, - }); - - // Fill up the internal buffer so that we definitely exceed the HWM: - for (let i = 0; i < 10; i++) readable.push("a".repeat(200)); - - // Call resume, and pause after one chunk. - // The .pause() is just so that we don't empty the buffer fully, which would - // be a valid reason to call ._read(). - readable.resume(); - - return new Promise(resolve => { - readable.once("data", () => { - readable.pause(); - expect(mockRead).not.toHaveBeenCalled(); - resolve(); - }); - }); -}); - -//<#END_FILE: test-stream-readable-resume-hwm.js diff --git a/test/js/node/test/parallel/stream-readable-resumescheduled.test.js b/test/js/node/test/parallel/stream-readable-resumescheduled.test.js deleted file mode 100644 index 499d82925f..0000000000 --- a/test/js/node/test/parallel/stream-readable-resumescheduled.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-stream-readable-resumeScheduled.js -//#SHA1: 3327b31acfd00e4df0bac4e89d7a764c4de6cb4b -//----------------- -"use strict"; - -const { Readable, Writable } = require("stream"); - -// Testing Readable Stream resumeScheduled state - -describe("Readable Stream resumeScheduled state", () => { - test("pipe() test case", done => { - const r = new Readable({ read() {} }); - const w = new Writable(); - - // resumeScheduled should start = `false`. - expect(r._readableState.resumeScheduled).toBe(false); - - // Calling pipe() should change the state value = true. - r.pipe(w); - expect(r._readableState.resumeScheduled).toBe(true); - - process.nextTick(() => { - expect(r._readableState.resumeScheduled).toBe(false); - done(); - }); - }); - - test("data listener test case", done => { - const r = new Readable({ read() {} }); - - // resumeScheduled should start = `false`. - expect(r._readableState.resumeScheduled).toBe(false); - - r.push(Buffer.from([1, 2, 3])); - - // Adding 'data' listener should change the state value - r.on( - "data", - jest.fn(() => { - expect(r._readableState.resumeScheduled).toBe(false); - }), - ); - expect(r._readableState.resumeScheduled).toBe(true); - - process.nextTick(() => { - expect(r._readableState.resumeScheduled).toBe(false); - done(); - }); - }); - - test("resume() test case", done => { - const r = new Readable({ read() {} }); - - // resumeScheduled should start = `false`. - expect(r._readableState.resumeScheduled).toBe(false); - - // Calling resume() should change the state value. - r.resume(); - expect(r._readableState.resumeScheduled).toBe(true); - - const resumeHandler = jest.fn(() => { - // The state value should be `false` again - expect(r._readableState.resumeScheduled).toBe(false); - }); - - r.on("resume", resumeHandler); - - process.nextTick(() => { - expect(r._readableState.resumeScheduled).toBe(false); - expect(resumeHandler).toHaveBeenCalledTimes(1); - done(); - }); - }); -}); - -//<#END_FILE: test-stream-readable-resumeScheduled.js diff --git a/test/js/node/test/parallel/stream-readable-setencoding-existing-buffers.test.js b/test/js/node/test/parallel/stream-readable-setencoding-existing-buffers.test.js deleted file mode 100644 index f79124ae7f..0000000000 --- a/test/js/node/test/parallel/stream-readable-setencoding-existing-buffers.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-stream-readable-setEncoding-existing-buffers.js -//#SHA1: 1b54f93d0be77b949ce81135243cc9ab3318db5b -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -test("Call .setEncoding() while there are bytes already in the buffer", done => { - const r = new Readable({ read() {} }); - - r.push(Buffer.from("a")); - r.push(Buffer.from("b")); - - r.setEncoding("utf8"); - const chunks = []; - r.on("data", chunk => chunks.push(chunk)); - - process.nextTick(() => { - expect(chunks).toEqual(["ab"]); - done(); - }); -}); - -test("Call .setEncoding() while the buffer contains a complete, but chunked character", done => { - const r = new Readable({ read() {} }); - - r.push(Buffer.from([0xf0])); - r.push(Buffer.from([0x9f])); - r.push(Buffer.from([0x8e])); - r.push(Buffer.from([0x89])); - - r.setEncoding("utf8"); - const chunks = []; - r.on("data", chunk => chunks.push(chunk)); - - process.nextTick(() => { - expect(chunks).toEqual(["🎉"]); - done(); - }); -}); - -test("Call .setEncoding() while the buffer contains an incomplete character, and finish the character later", done => { - const r = new Readable({ read() {} }); - - r.push(Buffer.from([0xf0])); - r.push(Buffer.from([0x9f])); - - r.setEncoding("utf8"); - - r.push(Buffer.from([0x8e])); - r.push(Buffer.from([0x89])); - - const chunks = []; - r.on("data", chunk => chunks.push(chunk)); - - process.nextTick(() => { - expect(chunks).toEqual(["🎉"]); - done(); - }); -}); - -//<#END_FILE: test-stream-readable-setEncoding-existing-buffers.js diff --git a/test/js/node/test/parallel/stream-readable-with-unimplemented-_read.test.js b/test/js/node/test/parallel/stream-readable-with-unimplemented-_read.test.js deleted file mode 100644 index 4160e39091..0000000000 --- a/test/js/node/test/parallel/stream-readable-with-unimplemented-_read.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-stream-readable-with-unimplemented-_read.js -//#SHA1: ba318da76b4c594580f62650bad861933a59c215 -//----------------- -"use strict"; -const { Readable } = require("stream"); - -test("Readable stream with unimplemented _read method", done => { - const readable = new Readable(); - - readable.read(); - readable.on("error", error => { - expect(error).toEqual( - expect.objectContaining({ - code: "ERR_METHOD_NOT_IMPLEMENTED", - name: "Error", - message: expect.any(String), - }), - ); - }); - - readable.on("close", () => { - done(); - }); -}); - -//<#END_FILE: test-stream-readable-with-unimplemented-_read.js diff --git a/test/js/node/test/parallel/stream-set-default-hwm.test.js b/test/js/node/test/parallel/stream-set-default-hwm.test.js deleted file mode 100644 index 6bd5bbee52..0000000000 --- a/test/js/node/test/parallel/stream-set-default-hwm.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-stream-set-default-hwm.js -//#SHA1: bc1189f9270a4b5463d8421ef234fc7baaad667f -//----------------- -"use strict"; - -const { setDefaultHighWaterMark, getDefaultHighWaterMark, Writable, Readable, Transform } = require("stream"); - -test("setDefaultHighWaterMark and getDefaultHighWaterMark for object mode", () => { - expect(getDefaultHighWaterMark(false)).not.toBe(32 * 1000); - setDefaultHighWaterMark(false, 32 * 1000); - expect(getDefaultHighWaterMark(false)).toBe(32 * 1000); -}); - -test("setDefaultHighWaterMark and getDefaultHighWaterMark for non-object mode", () => { - expect(getDefaultHighWaterMark(true)).not.toBe(32); - setDefaultHighWaterMark(true, 32); - expect(getDefaultHighWaterMark(true)).toBe(32); -}); - -test("Writable stream uses new default high water mark", () => { - const w = new Writable({ - write() {}, - }); - expect(w.writableHighWaterMark).toBe(32 * 1000); -}); - -test("Readable stream uses new default high water mark", () => { - const r = new Readable({ - read() {}, - }); - expect(r.readableHighWaterMark).toBe(32 * 1000); -}); - -test("Transform stream uses new default high water mark for both readable and writable", () => { - const t = new Transform({ - transform() {}, - }); - expect(t.writableHighWaterMark).toBe(32 * 1000); - expect(t.readableHighWaterMark).toBe(32 * 1000); -}); - -//<#END_FILE: test-stream-set-default-hwm.js diff --git a/test/js/node/test/parallel/stream-transform-callback-twice.test.js b/test/js/node/test/parallel/stream-transform-callback-twice.test.js deleted file mode 100644 index 3be1a53af3..0000000000 --- a/test/js/node/test/parallel/stream-transform-callback-twice.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-stream-transform-callback-twice.js -//#SHA1: f2ad2048f83461d93a84b8b5696230beb4dba9f2 -//----------------- -"use strict"; - -const { Transform } = require("stream"); - -test("Transform stream callback called twice", done => { - const stream = new Transform({ - transform(chunk, enc, cb) { - cb(); - cb(); - }, - }); - - stream.on("error", error => { - expect(error).toMatchObject({ - name: "Error", - code: "ERR_MULTIPLE_CALLBACK", - message: expect.any(String), - }); - done(); - }); - - stream.write("foo"); -}); - -//<#END_FILE: test-stream-transform-callback-twice.js diff --git a/test/js/node/test/parallel/stream-transform-constructor-set-methods.test.js b/test/js/node/test/parallel/stream-transform-constructor-set-methods.test.js deleted file mode 100644 index ec66e53654..0000000000 --- a/test/js/node/test/parallel/stream-transform-constructor-set-methods.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-stream-transform-constructor-set-methods.js -//#SHA1: a827edab0555cd9f8bd240738812b4d6a48b4e7d -//----------------- -"use strict"; - -const { Transform } = require("stream"); - -test("Transform constructor throws when _transform is not implemented", () => { - const t = new Transform(); - - expect(() => { - t.end(Buffer.from("blerg")); - }).toThrow( - expect.objectContaining({ - name: "Error", - code: "ERR_METHOD_NOT_IMPLEMENTED", - message: expect.any(String), - }), - ); -}); - -test("Transform constructor sets methods correctly", () => { - const _transform = jest.fn((chunk, _, next) => { - next(); - }); - - const _final = jest.fn(next => { - next(); - }); - - const _flush = jest.fn(next => { - next(); - }); - - const t2 = new Transform({ - transform: _transform, - flush: _flush, - final: _final, - }); - - expect(t2._transform).toBe(_transform); - expect(t2._flush).toBe(_flush); - expect(t2._final).toBe(_final); - - t2.end(Buffer.from("blerg")); - t2.resume(); - - expect(_transform).toHaveBeenCalled(); - expect(_final).toHaveBeenCalled(); - expect(_flush).toHaveBeenCalled(); -}); - -//<#END_FILE: test-stream-transform-constructor-set-methods.js diff --git a/test/js/node/test/parallel/stream-transform-flush-data.test.js b/test/js/node/test/parallel/stream-transform-flush-data.test.js deleted file mode 100644 index 6bd8e013c0..0000000000 --- a/test/js/node/test/parallel/stream-transform-flush-data.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-stream-transform-flush-data.js -//#SHA1: 04d0db2bec19ea79b0914db96fd2c996ab8d67bd -//----------------- -"use strict"; - -const { Transform } = require("stream"); - -test("Transform flush should emit expected data", done => { - const expected = "asdf"; - - function _transform(d, e, n) { - n(); - } - - function _flush(n) { - n(null, expected); - } - - const t = new Transform({ - transform: _transform, - flush: _flush, - }); - - t.end(Buffer.from("blerg")); - t.on("data", data => { - expect(data.toString()).toBe(expected); - done(); - }); -}); - -//<#END_FILE: test-stream-transform-flush-data.js diff --git a/test/js/node/test/parallel/stream-unpipe-event.test.js b/test/js/node/test/parallel/stream-unpipe-event.test.js deleted file mode 100644 index 7ad2afc486..0000000000 --- a/test/js/node/test/parallel/stream-unpipe-event.test.js +++ /dev/null @@ -1,140 +0,0 @@ -//#FILE: test-stream-unpipe-event.js -//#SHA1: 17303ffe85d8760f81f6294d9004c98638985bd6 -//----------------- -"use strict"; - -const { Writable, Readable } = require("stream"); - -class NullWriteable extends Writable { - _write(chunk, encoding, callback) { - return callback(); - } -} - -class QuickEndReadable extends Readable { - _read() { - this.push(null); - } -} - -class NeverEndReadable extends Readable { - _read() {} -} - -test("QuickEndReadable pipes and unpipes", done => { - const dest = new NullWriteable(); - const src = new QuickEndReadable(); - const pipeSpy = jest.fn(); - const unpipeSpy = jest.fn(); - - dest.on("pipe", pipeSpy); - dest.on("unpipe", unpipeSpy); - - src.pipe(dest); - - setImmediate(() => { - expect(pipeSpy).toHaveBeenCalledTimes(1); - expect(unpipeSpy).toHaveBeenCalledTimes(1); - expect(src._readableState.pipes.length).toBe(0); - done(); - }); -}); - -test("NeverEndReadable pipes but does not unpipe", done => { - const dest = new NullWriteable(); - const src = new NeverEndReadable(); - const pipeSpy = jest.fn(); - const unpipeSpy = jest.fn(); - - dest.on("pipe", pipeSpy); - dest.on("unpipe", unpipeSpy); - - src.pipe(dest); - - setImmediate(() => { - expect(pipeSpy).toHaveBeenCalledTimes(1); - expect(unpipeSpy).not.toHaveBeenCalled(); - expect(src._readableState.pipes.length).toBe(1); - done(); - }); -}); - -test("NeverEndReadable pipes and manually unpipes", done => { - const dest = new NullWriteable(); - const src = new NeverEndReadable(); - const pipeSpy = jest.fn(); - const unpipeSpy = jest.fn(); - - dest.on("pipe", pipeSpy); - dest.on("unpipe", unpipeSpy); - - src.pipe(dest); - src.unpipe(dest); - - setImmediate(() => { - expect(pipeSpy).toHaveBeenCalledTimes(1); - expect(unpipeSpy).toHaveBeenCalledTimes(1); - expect(src._readableState.pipes.length).toBe(0); - done(); - }); -}); - -test("QuickEndReadable pipes and unpipes with end: false", done => { - const dest = new NullWriteable(); - const src = new QuickEndReadable(); - const pipeSpy = jest.fn(); - const unpipeSpy = jest.fn(); - - dest.on("pipe", pipeSpy); - dest.on("unpipe", unpipeSpy); - - src.pipe(dest, { end: false }); - - setImmediate(() => { - expect(pipeSpy).toHaveBeenCalledTimes(1); - expect(unpipeSpy).toHaveBeenCalledTimes(1); - expect(src._readableState.pipes.length).toBe(0); - done(); - }); -}); - -test("NeverEndReadable pipes but does not unpipe with end: false", done => { - const dest = new NullWriteable(); - const src = new NeverEndReadable(); - const pipeSpy = jest.fn(); - const unpipeSpy = jest.fn(); - - dest.on("pipe", pipeSpy); - dest.on("unpipe", unpipeSpy); - - src.pipe(dest, { end: false }); - - setImmediate(() => { - expect(pipeSpy).toHaveBeenCalledTimes(1); - expect(unpipeSpy).not.toHaveBeenCalled(); - expect(src._readableState.pipes.length).toBe(1); - done(); - }); -}); - -test("NeverEndReadable pipes and manually unpipes with end: false", done => { - const dest = new NullWriteable(); - const src = new NeverEndReadable(); - const pipeSpy = jest.fn(); - const unpipeSpy = jest.fn(); - - dest.on("pipe", pipeSpy); - dest.on("unpipe", unpipeSpy); - - src.pipe(dest, { end: false }); - src.unpipe(dest); - - setImmediate(() => { - expect(pipeSpy).toHaveBeenCalledTimes(1); - expect(unpipeSpy).toHaveBeenCalledTimes(1); - expect(src._readableState.pipes.length).toBe(0); - done(); - }); -}); - -//<#END_FILE: test-stream-unpipe-event.js diff --git a/test/js/node/test/parallel/stream-unshift-read-race.test.js b/test/js/node/test/parallel/stream-unshift-read-race.test.js deleted file mode 100644 index 9a03dbb0f0..0000000000 --- a/test/js/node/test/parallel/stream-unshift-read-race.test.js +++ /dev/null @@ -1,141 +0,0 @@ -//#FILE: test-stream-unshift-read-race.js -//#SHA1: 3b6a1e1b0ae4b58251211a49cd8171569cfd86ed -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const stream = require("stream"); - -// This test verifies that: -// 1. unshift() does not cause colliding _read() calls. -// 2. unshift() after the 'end' event is an error, but after the EOF -// signalling null, it is ok, and just creates a new readable chunk. -// 3. push() after the EOF signaling null is an error. -// 4. _read() is not called after pushing the EOF null chunk. - -test("stream unshift read race", done => { - const hwm = 10; - const r = stream.Readable({ highWaterMark: hwm, autoDestroy: false }); - const chunks = 10; - - const data = Buffer.allocUnsafe(chunks * hwm + Math.ceil(hwm / 2)); - for (let i = 0; i < data.length; i++) { - const c = "asdf".charCodeAt(i % 4); - data[i] = c; - } - - let pos = 0; - let pushedNull = false; - r._read = function (n) { - expect(pushedNull).toBe(false); - - // Every third chunk is fast - push(!(chunks % 3)); - - function push(fast) { - expect(pushedNull).toBe(false); - const c = pos >= data.length ? null : data.slice(pos, pos + n); - pushedNull = c === null; - if (fast) { - pos += n; - r.push(c); - if (c === null) pushError(); - } else { - setTimeout(function () { - pos += n; - r.push(c); - if (c === null) pushError(); - }, 1); - } - } - }; - - function pushError() { - r.unshift(Buffer.allocUnsafe(1)); - w.end(); - - expect(() => { - r.push(Buffer.allocUnsafe(1)); - }).toThrow( - expect.objectContaining({ - code: "ERR_STREAM_PUSH_AFTER_EOF", - name: "Error", - message: expect.any(String), - }), - ); - } - - const w = stream.Writable(); - const written = []; - w._write = function (chunk, encoding, cb) { - written.push(chunk.toString()); - cb(); - }; - - r.on("end", () => { - throw new Error("end event should not be emitted"); - }); - - r.on("readable", function () { - let chunk; - while (null !== (chunk = r.read(10))) { - w.write(chunk); - if (chunk.length > 4) r.unshift(Buffer.from("1234")); - } - }); - - w.on("finish", () => { - // Each chunk should start with 1234, and then be asfdasdfasdf... - // The first got pulled out before the first unshift('1234'), so it's - // lacking that piece. - expect(written[0]).toBe("asdfasdfas"); - let asdf = "d"; - console.error(`0: ${written[0]}`); - for (let i = 1; i < written.length; i++) { - console.error(`${i.toString(32)}: ${written[i]}`); - expect(written[i].slice(0, 4)).toBe("1234"); - for (let j = 4; j < written[i].length; j++) { - const c = written[i].charAt(j); - expect(c).toBe(asdf); - switch (asdf) { - case "a": - asdf = "s"; - break; - case "s": - asdf = "d"; - break; - case "d": - asdf = "f"; - break; - case "f": - asdf = "a"; - break; - } - } - } - expect(written).toHaveLength(18); - console.log("ok"); - done(); - }); -}); - -//<#END_FILE: test-stream-unshift-read-race.js diff --git a/test/js/node/test/parallel/stream-wrap.test.js b/test/js/node/test/parallel/stream-wrap.test.js deleted file mode 100644 index ab020ef2b1..0000000000 --- a/test/js/node/test/parallel/stream-wrap.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-stream-wrap.js -//#SHA1: 675a22f043e5a5a38cdd7fff376f269f8d341aab -//----------------- -"use strict"; - -const { Duplex } = require("stream"); - -// Remove internal bindings and StreamWrap import as they're not accessible in a normal environment -// We'll mock the necessary functionality instead - -test("StreamWrap shutdown behavior", done => { - const stream = new Duplex({ - read: function () {}, - write: function () {}, - }); - - // Mock StreamWrap - class MockStreamWrap { - constructor(stream) { - this.stream = stream; - this._handle = { - shutdown: jest.fn(req => { - // Simulate async completion with error - process.nextTick(() => { - req.oncomplete(-1); - }); - }), - }; - } - - destroy() { - // Simulate handle closure - } - } - - // Mock ShutdownWrap - class MockShutdownWrap { - oncomplete = null; - } - - function testShutdown(callback) { - const wrap = new MockStreamWrap(stream); - - const req = new MockShutdownWrap(); - req.oncomplete = function (code) { - expect(code).toBeLessThan(0); - callback(); - }; - req.handle = wrap._handle; - - // Close the handle to simulate - wrap.destroy(); - req.handle.shutdown(req); - } - - testShutdown(done); -}); - -//<#END_FILE: test-stream-wrap.js diff --git a/test/js/node/test/parallel/stream-writable-aborted.test.js b/test/js/node/test/parallel/stream-writable-aborted.test.js deleted file mode 100644 index 82c4be4233..0000000000 --- a/test/js/node/test/parallel/stream-writable-aborted.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-stream-writable-aborted.js -//#SHA1: be315bbc27ad16f13bb6b3022e864c8902265391 -//----------------- -"use strict"; - -const { Writable } = require("stream"); - -describe("Writable stream aborted property", () => { - test("writableAborted is false initially and true after destroy", () => { - const writable = new Writable({ - write() {}, - }); - expect(writable.writableAborted).toBe(false); - writable.destroy(); - expect(writable.writableAborted).toBe(true); - }); - - test("writableAborted is false initially and true after end and destroy", () => { - const writable = new Writable({ - write() {}, - }); - expect(writable.writableAborted).toBe(false); - writable.end(); - writable.destroy(); - expect(writable.writableAborted).toBe(true); - }); -}); - -//<#END_FILE: test-stream-writable-aborted.js diff --git a/test/js/node/test/parallel/stream-writable-change-default-encoding.test.js b/test/js/node/test/parallel/stream-writable-change-default-encoding.test.js deleted file mode 100644 index 0ccbd29ac5..0000000000 --- a/test/js/node/test/parallel/stream-writable-change-default-encoding.test.js +++ /dev/null @@ -1,91 +0,0 @@ -//#FILE: test-stream-writable-change-default-encoding.js -//#SHA1: 7a4e7c22888d4901899fe5a0ed4604c319f73ff9 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const stream = require("stream"); - -class MyWritable extends stream.Writable { - constructor(fn, options) { - super(options); - this.fn = fn; - } - - _write(chunk, encoding, callback) { - this.fn(Buffer.isBuffer(chunk), typeof chunk, encoding); - callback(); - } -} - -test("default encoding is utf8", () => { - const m = new MyWritable( - (isBuffer, type, enc) => { - expect(enc).toBe("utf8"); - }, - { decodeStrings: false }, - ); - m.write("foo"); - m.end(); -}); - -test("change default encoding to ascii", () => { - const m = new MyWritable( - (isBuffer, type, enc) => { - expect(enc).toBe("ascii"); - }, - { decodeStrings: false }, - ); - m.setDefaultEncoding("ascii"); - m.write("bar"); - m.end(); -}); - -test("change default encoding to invalid value", () => { - const m = new MyWritable((isBuffer, type, enc) => {}, { decodeStrings: false }); - - expect(() => { - m.setDefaultEncoding({}); - m.write("bar"); - m.end(); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_UNKNOWN_ENCODING", - message: expect.any(String), - }), - ); -}); - -test("check variable case encoding", () => { - const m = new MyWritable( - (isBuffer, type, enc) => { - expect(enc).toBe("ascii"); - }, - { decodeStrings: false }, - ); - m.setDefaultEncoding("AsCii"); - m.write("bar"); - m.end(); -}); - -//<#END_FILE: test-stream-writable-change-default-encoding.js diff --git a/test/js/node/test/parallel/stream-writable-clear-buffer.test.js b/test/js/node/test/parallel/stream-writable-clear-buffer.test.js deleted file mode 100644 index 329e26d512..0000000000 --- a/test/js/node/test/parallel/stream-writable-clear-buffer.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-stream-writable-clear-buffer.js -//#SHA1: 0088292e626fb952c1777f191acee9a6d92d3f4d -//----------------- -"use strict"; - -const Stream = require("stream"); - -class StreamWritable extends Stream.Writable { - constructor() { - super({ objectMode: true }); - } - - // Refs: https://github.com/nodejs/node/issues/6758 - // We need a timer like on the original issue thread. - // Otherwise the code will never reach our test case. - _write(chunk, encoding, cb) { - setImmediate(cb); - } -} - -test("StreamWritable bufferedRequestCount matches actual buffered request count", done => { - const testStream = new StreamWritable(); - testStream.cork(); - - const writeOperations = 5; - let completedWrites = 0; - - for (let i = 1; i <= writeOperations; i++) { - testStream.write(i, () => { - expect(testStream._writableState.bufferedRequestCount).toBe(testStream._writableState.getBuffer().length); - completedWrites++; - - if (completedWrites === writeOperations) { - done(); - } - }); - } - - testStream.end(); -}); - -//<#END_FILE: test-stream-writable-clear-buffer.js diff --git a/test/js/node/test/parallel/stream-writable-constructor-set-methods.test.js b/test/js/node/test/parallel/stream-writable-constructor-set-methods.test.js deleted file mode 100644 index 932e14afb6..0000000000 --- a/test/js/node/test/parallel/stream-writable-constructor-set-methods.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-stream-writable-constructor-set-methods.js -//#SHA1: 5610c9523a02a55898e40f7c72a09973affd133f -//----------------- -"use strict"; - -const { Writable } = require("stream"); - -const bufferBlerg = Buffer.from("blerg"); - -test("Writable without _write method throws", () => { - const w = new Writable(); - - expect(() => { - w.end(bufferBlerg); - }).toThrow( - expect.objectContaining({ - name: "Error", - code: "ERR_METHOD_NOT_IMPLEMENTED", - message: expect.any(String), - }), - ); -}); - -test("Writable with custom write and writev methods", () => { - const _write = jest.fn((chunk, _, next) => { - next(); - }); - - const _writev = jest.fn((chunks, next) => { - expect(chunks.length).toBe(2); - next(); - }); - - const w2 = new Writable({ write: _write, writev: _writev }); - - expect(w2._write).toBe(_write); - expect(w2._writev).toBe(_writev); - - w2.write(bufferBlerg); - - w2.cork(); - w2.write(bufferBlerg); - w2.write(bufferBlerg); - - w2.end(); - - expect(_write).toHaveBeenCalledTimes(1); - expect(_writev).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-stream-writable-constructor-set-methods.js diff --git a/test/js/node/test/parallel/stream-writable-end-multiple.test.js b/test/js/node/test/parallel/stream-writable-end-multiple.test.js deleted file mode 100644 index 71b55c8cc7..0000000000 --- a/test/js/node/test/parallel/stream-writable-end-multiple.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-stream-writable-end-multiple.js -//#SHA1: db58e265a3eb8bdf7dba1cb959979059ba686f6d -//----------------- -"use strict"; - -const stream = require("stream"); - -test("stream writable end multiple", async () => { - const writable = new stream.Writable(); - writable._write = (chunk, encoding, cb) => { - setTimeout(() => cb(), 10); - }; - - const endCallback1 = jest.fn(); - const endCallback2 = jest.fn(); - const finishCallback = jest.fn(); - const endCallback3 = jest.fn(); - - writable.end("testing ended state", endCallback1); - writable.end(endCallback2); - - writable.on("finish", finishCallback); - - await new Promise(resolve => setTimeout(resolve, 20)); - - expect(endCallback1).toHaveBeenCalledTimes(1); - expect(endCallback2).toHaveBeenCalledTimes(1); - expect(finishCallback).toHaveBeenCalledTimes(1); - - let ticked = false; - writable.end(endCallback3); - ticked = true; - - await new Promise(resolve => setTimeout(resolve, 0)); - - expect(endCallback3).toHaveBeenCalledTimes(1); - expect(endCallback3).toHaveBeenCalledWith( - expect.objectContaining({ - code: "ERR_STREAM_ALREADY_FINISHED", - message: expect.any(String), - }), - ); - expect(ticked).toBe(true); -}); - -//<#END_FILE: test-stream-writable-end-multiple.js diff --git a/test/js/node/test/parallel/stream-writable-ended-state.test.js b/test/js/node/test/parallel/stream-writable-ended-state.test.js deleted file mode 100644 index fb6c3d10c6..0000000000 --- a/test/js/node/test/parallel/stream-writable-ended-state.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-stream-writable-ended-state.js -//#SHA1: e6a35fad059c742def91bd4cab4786faffa26f5b -//----------------- -"use strict"; - -const stream = require("stream"); - -describe("Stream Writable Ended State", () => { - let writable; - - beforeEach(() => { - writable = new stream.Writable(); - - writable._write = (chunk, encoding, cb) => { - expect(writable._writableState.ended).toBe(false); - expect(writable._writableState.writable).toBeUndefined(); - expect(writable.writableEnded).toBe(false); - cb(); - }; - }); - - test("initial state", () => { - expect(writable._writableState.ended).toBe(false); - expect(writable._writableState.writable).toBeUndefined(); - expect(writable.writable).toBe(true); - expect(writable.writableEnded).toBe(false); - }); - - test("ended state after end() call", done => { - writable.end("testing ended state", () => { - expect(writable._writableState.ended).toBe(true); - expect(writable._writableState.writable).toBeUndefined(); - expect(writable.writable).toBe(false); - expect(writable.writableEnded).toBe(true); - done(); - }); - - expect(writable._writableState.ended).toBe(true); - expect(writable._writableState.writable).toBeUndefined(); - expect(writable.writable).toBe(false); - expect(writable.writableEnded).toBe(true); - }); -}); - -//<#END_FILE: test-stream-writable-ended-state.js diff --git a/test/js/node/test/parallel/stream-writable-final-destroy.test.js b/test/js/node/test/parallel/stream-writable-final-destroy.test.js deleted file mode 100644 index 0d3b853b3b..0000000000 --- a/test/js/node/test/parallel/stream-writable-final-destroy.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-stream-writable-final-destroy.js -//#SHA1: 4213d1382f0e5b950211e183a94adc5f3e7a1468 -//----------------- -"use strict"; - -const { Writable } = require("stream"); - -test("Writable stream with final and destroy", () => { - const w = new Writable({ - write(chunk, encoding, callback) { - callback(null); - }, - final(callback) { - queueMicrotask(callback); - }, - }); - - w.end(); - w.destroy(); - - const prefinishSpy = jest.fn(); - const finishSpy = jest.fn(); - const closeSpy = jest.fn(); - - w.on("prefinish", prefinishSpy); - w.on("finish", finishSpy); - w.on("close", closeSpy); - - return new Promise(resolve => { - // Use setImmediate to ensure all microtasks have been processed - setImmediate(() => { - expect(prefinishSpy).not.toHaveBeenCalled(); - expect(finishSpy).not.toHaveBeenCalled(); - expect(closeSpy).toHaveBeenCalledTimes(1); - resolve(); - }); - }); -}); - -//<#END_FILE: test-stream-writable-final-destroy.js diff --git a/test/js/node/test/parallel/stream-writable-finished-state.test.js b/test/js/node/test/parallel/stream-writable-finished-state.test.js deleted file mode 100644 index c2d8e8ab49..0000000000 --- a/test/js/node/test/parallel/stream-writable-finished-state.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-stream-writable-finished-state.js -//#SHA1: e9ea6f7cc3e0262bf187b9cf08e9a054c93d7b5f -//----------------- -"use strict"; - -const stream = require("stream"); - -test("Writable stream finished state", done => { - const writable = new stream.Writable(); - - writable._write = (chunk, encoding, cb) => { - // The state finished should start in false. - expect(writable._writableState.finished).toBe(false); - cb(); - }; - - writable.on("finish", () => { - expect(writable._writableState.finished).toBe(true); - }); - - writable.end("testing finished state", () => { - expect(writable._writableState.finished).toBe(true); - done(); - }); -}); - -//<#END_FILE: test-stream-writable-finished-state.js diff --git a/test/js/node/test/parallel/stream-writable-finished.test.js b/test/js/node/test/parallel/stream-writable-finished.test.js deleted file mode 100644 index d7db2acf1d..0000000000 --- a/test/js/node/test/parallel/stream-writable-finished.test.js +++ /dev/null @@ -1,97 +0,0 @@ -//#FILE: test-stream-writable-finished.js -//#SHA1: 20d27885cc10a6787d3e6c6fb877c0aba2310f93 -//----------------- -"use strict"; - -const { Writable } = require("stream"); - -// basic -test("Writable.prototype has writableFinished", () => { - expect(Object.hasOwn(Writable.prototype, "writableFinished")).toBe(true); -}); - -// event -test("writableFinished state changes correctly", done => { - const writable = new Writable(); - - writable._write = (chunk, encoding, cb) => { - // The state finished should start in false. - expect(writable.writableFinished).toBe(false); - cb(); - }; - - writable.on("finish", () => { - expect(writable.writableFinished).toBe(true); - done(); - }); - - writable.end("testing finished state", () => { - expect(writable.writableFinished).toBe(true); - }); -}); - -test("Emit finish asynchronously", done => { - const w = new Writable({ - write(chunk, encoding, cb) { - cb(); - }, - }); - - w.end(); - w.on("finish", done); -}); - -test("Emit prefinish synchronously", () => { - const w = new Writable({ - write(chunk, encoding, cb) { - cb(); - }, - }); - - let sync = true; - w.on("prefinish", () => { - expect(sync).toBe(true); - }); - w.end(); - sync = false; -}); - -test("Emit prefinish synchronously w/ final", () => { - const w = new Writable({ - write(chunk, encoding, cb) { - cb(); - }, - final(cb) { - cb(); - }, - }); - - let sync = true; - w.on("prefinish", () => { - expect(sync).toBe(true); - }); - w.end(); - sync = false; -}); - -test("Call _final synchronously", () => { - let sync = true; - const finalMock = jest.fn(cb => { - expect(sync).toBe(true); - cb(); - }); - - const w = new Writable({ - write(chunk, encoding, cb) { - cb(); - }, - final: finalMock, - }); - - w.end(); - sync = false; - - expect(finalMock).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-stream-writable-finished.js diff --git a/test/js/node/test/parallel/stream-writable-invalid-chunk.test.js b/test/js/node/test/parallel/stream-writable-invalid-chunk.test.js deleted file mode 100644 index 259e993173..0000000000 --- a/test/js/node/test/parallel/stream-writable-invalid-chunk.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-stream-writable-invalid-chunk.js -//#SHA1: 8febb7872aa1c8bfb7ebcb33db7a5fcd2903b2bd -//----------------- -"use strict"; - -const stream = require("stream"); - -function testWriteType(val, objectMode, code) { - const writable = new stream.Writable({ - objectMode, - write: () => {}, - }); - - const writeOperation = () => writable.write(val); - - if (code) { - expect(writeOperation).toThrow( - expect.objectContaining({ - code, - message: expect.any(String), - }), - ); - } else { - expect(writeOperation).not.toThrow(); - } -} - -describe("Writable stream invalid chunk tests", () => { - test("non-object mode invalid types", () => { - testWriteType([], false, "ERR_INVALID_ARG_TYPE"); - testWriteType({}, false, "ERR_INVALID_ARG_TYPE"); - testWriteType(0, false, "ERR_INVALID_ARG_TYPE"); - testWriteType(true, false, "ERR_INVALID_ARG_TYPE"); - testWriteType(0.0, false, "ERR_INVALID_ARG_TYPE"); - testWriteType(undefined, false, "ERR_INVALID_ARG_TYPE"); - testWriteType(null, false, "ERR_STREAM_NULL_VALUES"); - }); - - test("object mode valid types", () => { - testWriteType([], true); - testWriteType({}, true); - testWriteType(0, true); - testWriteType(true, true); - testWriteType(0.0, true); - testWriteType(undefined, true); - }); - - test("object mode null value", () => { - testWriteType(null, true, "ERR_STREAM_NULL_VALUES"); - }); -}); - -//<#END_FILE: test-stream-writable-invalid-chunk.js diff --git a/test/js/node/test/parallel/stream-writable-needdrain-state.test.js b/test/js/node/test/parallel/stream-writable-needdrain-state.test.js deleted file mode 100644 index 3cac2829fa..0000000000 --- a/test/js/node/test/parallel/stream-writable-needdrain-state.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-stream-writable-needdrain-state.js -//#SHA1: c73d65b940e3ea2fe9c94d9c9d0d4ffe36c47397 -//----------------- -"use strict"; - -const stream = require("stream"); - -test("Transform stream needDrain state", done => { - const transform = new stream.Transform({ - transform: _transform, - highWaterMark: 1, - }); - - function _transform(chunk, encoding, cb) { - process.nextTick(() => { - expect(transform._writableState.needDrain).toBe(true); - cb(); - }); - } - - expect(transform._writableState.needDrain).toBe(false); - - transform.write("asdasd", () => { - expect(transform._writableState.needDrain).toBe(false); - done(); - }); - - expect(transform._writableState.needDrain).toBe(true); -}); - -//<#END_FILE: test-stream-writable-needdrain-state.js diff --git a/test/js/node/test/parallel/stream-writable-null.test.js b/test/js/node/test/parallel/stream-writable-null.test.js deleted file mode 100644 index 6f747df705..0000000000 --- a/test/js/node/test/parallel/stream-writable-null.test.js +++ /dev/null @@ -1,58 +0,0 @@ -//#FILE: test-stream-writable-null.js -//#SHA1: 5a080b117b05a98d0b7bf6895b554892c2690ed8 -//----------------- -"use strict"; - -const stream = require("stream"); - -class MyWritable extends stream.Writable { - constructor(options) { - super({ autoDestroy: false, ...options }); - } - _write(chunk, encoding, callback) { - expect(chunk).not.toBe(null); - callback(); - } -} - -test("MyWritable throws on null in object mode", () => { - const m = new MyWritable({ objectMode: true }); - expect(() => { - m.write(null); - }).toThrow( - expect.objectContaining({ - code: "ERR_STREAM_NULL_VALUES", - }), - ); -}); - -test("MyWritable throws on false in non-object mode", () => { - const m = new MyWritable(); - expect(() => { - m.write(false); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - }), - ); -}); - -test("MyWritable should not throw on false in object mode", done => { - const m = new MyWritable({ objectMode: true }); - m.write(false, err => { - expect(err).toBeFalsy(); - done(); - }); -}); - -test("MyWritable should not throw or emit error on false in object mode", done => { - const m = new MyWritable({ objectMode: true }).on("error", e => { - done(e || new Error("should not get here")); - }); - m.write(false, err => { - expect(err).toBeFalsy(); - done(); - }); -}); - -//<#END_FILE: test-stream-writable-null.js diff --git a/test/js/node/test/parallel/stream-writable-properties.test.js b/test/js/node/test/parallel/stream-writable-properties.test.js deleted file mode 100644 index 86bf40656b..0000000000 --- a/test/js/node/test/parallel/stream-writable-properties.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-stream-writable-properties.js -//#SHA1: 3af7ed348fc81b0a70901cb29735a8778d0a8875 -//----------------- -"use strict"; - -const { Writable } = require("stream"); - -describe("Writable stream properties", () => { - test("writableCorked property", () => { - const w = new Writable(); - - expect(w.writableCorked).toBe(0); - - w.uncork(); - expect(w.writableCorked).toBe(0); - - w.cork(); - expect(w.writableCorked).toBe(1); - - w.cork(); - expect(w.writableCorked).toBe(2); - - w.uncork(); - expect(w.writableCorked).toBe(1); - - w.uncork(); - expect(w.writableCorked).toBe(0); - - w.uncork(); - expect(w.writableCorked).toBe(0); - }); -}); - -//<#END_FILE: test-stream-writable-properties.js diff --git a/test/js/node/test/parallel/stream-writable-write-error.test.js b/test/js/node/test/parallel/stream-writable-write-error.test.js deleted file mode 100644 index 6c4a7d0b51..0000000000 --- a/test/js/node/test/parallel/stream-writable-write-error.test.js +++ /dev/null @@ -1,88 +0,0 @@ -//#FILE: test-stream-writable-write-error.js -//#SHA1: 16053b21f2a6c80ae69ae55424550e7213f1f868 -//----------------- -"use strict"; - -const { Writable } = require("stream"); - -function expectError(w, args, code, sync) { - if (sync) { - if (code) { - expect(() => w.write(...args)).toThrow( - expect.objectContaining({ - code, - message: expect.any(String), - }), - ); - } else { - w.write(...args); - } - } else { - let errorCalled = false; - let ticked = false; - w.write(...args, err => { - expect(ticked).toBe(true); - expect(errorCalled).toBe(false); - expect(err.code).toBe(code); - }); - ticked = true; - w.on("error", err => { - errorCalled = true; - expect(err.code).toBe(code); - }); - } -} - -function runTest(autoDestroy) { - test("write after end", () => { - const w = new Writable({ - autoDestroy, - write() {}, - }); - w.end(); - expectError(w, ["asd"], "ERR_STREAM_WRITE_AFTER_END"); - }); - - test("write after destroy", () => { - const w = new Writable({ - autoDestroy, - write() {}, - }); - w.destroy(); - }); - - test("write null values", () => { - const w = new Writable({ - autoDestroy, - write() {}, - }); - expectError(w, [null], "ERR_STREAM_NULL_VALUES", true); - }); - - test("write invalid arg type", () => { - const w = new Writable({ - autoDestroy, - write() {}, - }); - expectError(w, [{}], "ERR_INVALID_ARG_TYPE", true); - }); - - test("write with unknown encoding", () => { - const w = new Writable({ - decodeStrings: false, - autoDestroy, - write() {}, - }); - expectError(w, ["asd", "noencoding"], "ERR_UNKNOWN_ENCODING", true); - }); -} - -describe("Writable stream write errors (autoDestroy: false)", () => { - runTest(false); -}); - -describe("Writable stream write errors (autoDestroy: true)", () => { - runTest(true); -}); - -//<#END_FILE: test-stream-writable-write-error.js diff --git a/test/js/node/test/parallel/stream-writablestate-ending.test.js b/test/js/node/test/parallel/stream-writablestate-ending.test.js deleted file mode 100644 index 43063afed8..0000000000 --- a/test/js/node/test/parallel/stream-writablestate-ending.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-stream-writableState-ending.js -//#SHA1: 97f5685bff2d1c4507caed842006d83c7317e0c0 -//----------------- -"use strict"; - -const stream = require("stream"); - -describe("Writable Stream State", () => { - let writable; - - beforeEach(() => { - writable = new stream.Writable(); - }); - - function testStates(ending, finished, ended) { - expect(writable._writableState.ending).toBe(ending); - expect(writable._writableState.finished).toBe(finished); - expect(writable._writableState.ended).toBe(ended); - } - - test("Writable state transitions", done => { - writable._write = (chunk, encoding, cb) => { - // Ending, finished, ended start in false. - testStates(false, false, false); - cb(); - }; - - writable.on("finish", () => { - // Ending, finished, ended = true. - testStates(true, true, true); - done(); - }); - - const result = writable.end("testing function end()", () => { - // Ending, finished, ended = true. - testStates(true, true, true); - }); - - // End returns the writable instance - expect(result).toBe(writable); - - // Ending, ended = true. - // finished = false. - testStates(true, false, true); - }); -}); - -//<#END_FILE: test-stream-writableState-ending.js diff --git a/test/js/node/test/parallel/stream-writablestate-uncorked-bufferedrequestcount.test.js b/test/js/node/test/parallel/stream-writablestate-uncorked-bufferedrequestcount.test.js deleted file mode 100644 index 7a9e5b4db1..0000000000 --- a/test/js/node/test/parallel/stream-writablestate-uncorked-bufferedrequestcount.test.js +++ /dev/null @@ -1,71 +0,0 @@ -//#FILE: test-stream-writableState-uncorked-bufferedRequestCount.js -//#SHA1: 39a95157551d47517d4c7aa46d1806b5dbccebcf -//----------------- -"use strict"; - -const stream = require("stream"); - -describe("Writable stream corking and uncorking", () => { - let writable; - - beforeEach(() => { - writable = new stream.Writable(); - - writable._writev = jest.fn((chunks, cb) => { - expect(chunks.length).toBe(2); - cb(); - }); - - writable._write = jest.fn((chunk, encoding, cb) => { - cb(); - }); - }); - - test("corking and uncorking behavior", done => { - // first cork - writable.cork(); - expect(writable._writableState.corked).toBe(1); - expect(writable._writableState.bufferedRequestCount).toBe(0); - - // cork again - writable.cork(); - expect(writable._writableState.corked).toBe(2); - - // The first chunk is buffered - writable.write("first chunk"); - expect(writable._writableState.bufferedRequestCount).toBe(1); - - // First uncork does nothing - writable.uncork(); - expect(writable._writableState.corked).toBe(1); - expect(writable._writableState.bufferedRequestCount).toBe(1); - - process.nextTick(() => { - // The second chunk is buffered, because we uncork at the end of tick - writable.write("second chunk"); - expect(writable._writableState.corked).toBe(1); - expect(writable._writableState.bufferedRequestCount).toBe(2); - - // Second uncork flushes the buffer - writable.uncork(); - expect(writable._writableState.corked).toBe(0); - expect(writable._writableState.bufferedRequestCount).toBe(0); - - // Verify that end() uncorks correctly - writable.cork(); - writable.write("third chunk"); - writable.end(); - - // End causes an uncork() as well - expect(writable._writableState.corked).toBe(0); - expect(writable._writableState.bufferedRequestCount).toBe(0); - - expect(writable._writev).toHaveBeenCalledTimes(1); - expect(writable._write).toHaveBeenCalledTimes(1); - - done(); - }); - }); -}); - -//<#END_FILE: test-stream-writableState-uncorked-bufferedRequestCount.js diff --git a/test/js/node/test/parallel/stream-write-destroy.test.js b/test/js/node/test/parallel/stream-write-destroy.test.js deleted file mode 100644 index 4fd760ef04..0000000000 --- a/test/js/node/test/parallel/stream-write-destroy.test.js +++ /dev/null @@ -1,70 +0,0 @@ -//#FILE: test-stream-write-destroy.js -//#SHA1: d39354900702b56f19b37407f2e8459ca063fbd6 -//----------------- -"use strict"; - -const { Writable } = require("stream"); - -// Test interaction between calling .destroy() on a writable and pending -// writes. - -describe("Stream write and destroy interaction", () => { - for (const withPendingData of [false, true]) { - for (const useEnd of [false, true]) { - test(`withPendingData: ${withPendingData}, useEnd: ${useEnd}`, () => { - const callbacks = []; - - const w = new Writable({ - write(data, enc, cb) { - callbacks.push(cb); - }, - // Effectively disable the HWM to observe 'drain' events more easily. - highWaterMark: 1, - }); - - let chunksWritten = 0; - let drains = 0; - w.on("drain", () => drains++); - - function onWrite(err) { - if (err) { - expect(w.destroyed).toBe(true); - expect(err.code).toBe("ERR_STREAM_DESTROYED"); - } else { - chunksWritten++; - } - } - - w.write("abc", onWrite); - expect(chunksWritten).toBe(0); - expect(drains).toBe(0); - callbacks.shift()(); - expect(chunksWritten).toBe(1); - expect(drains).toBe(1); - - if (withPendingData) { - // Test 2 cases: There either is or is not data still in the write queue. - // (The second write will never actually get executed either way.) - w.write("def", onWrite); - } - if (useEnd) { - // Again, test 2 cases: Either we indicate that we want to end the - // writable or not. - w.end("ghi", onWrite); - } else { - w.write("ghi", onWrite); - } - - expect(chunksWritten).toBe(1); - w.destroy(); - expect(chunksWritten).toBe(1); - callbacks.shift()(); - expect(chunksWritten).toBe(useEnd && !withPendingData ? 1 : 2); - expect(callbacks.length).toBe(0); - expect(drains).toBe(1); - }); - } - } -}); - -//<#END_FILE: test-stream-write-destroy.js diff --git a/test/js/node/test/parallel/stream-write-drain.test.js b/test/js/node/test/parallel/stream-write-drain.test.js deleted file mode 100644 index d0c89e4302..0000000000 --- a/test/js/node/test/parallel/stream-write-drain.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-stream-write-drain.js -//#SHA1: 893708699284e105a409388953fae28a836370b2 -//----------------- -"use strict"; -const { Writable } = require("stream"); - -// Don't emit 'drain' if ended - -test("Writable stream should not emit 'drain' if ended", done => { - const w = new Writable({ - write(data, enc, cb) { - process.nextTick(cb); - }, - highWaterMark: 1, - }); - - const drainSpy = jest.fn(); - w.on("drain", drainSpy); - - w.write("asd"); - w.end(); - - // Use process.nextTick to ensure that any potential 'drain' event would have been emitted - process.nextTick(() => { - expect(drainSpy).not.toHaveBeenCalled(); - done(); - }); -}); - -//<#END_FILE: test-stream-write-drain.js diff --git a/test/js/node/test/parallel/stream2-base64-single-char-read-end.test.js b/test/js/node/test/parallel/stream2-base64-single-char-read-end.test.js deleted file mode 100644 index bcaae8e2bc..0000000000 --- a/test/js/node/test/parallel/stream2-base64-single-char-read-end.test.js +++ /dev/null @@ -1,65 +0,0 @@ -//#FILE: test-stream2-base64-single-char-read-end.js -//#SHA1: 7d3b8e9ad3f47e915bc265658c0b5639fa4a68cd -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const { Readable: R, Writable: W } = require("stream"); - -test("stream2 base64 single char read end", async () => { - const src = new R({ encoding: "base64" }); - const dst = new W(); - let hasRead = false; - const accum = []; - - src._read = function (n) { - if (!hasRead) { - hasRead = true; - process.nextTick(function () { - src.push(Buffer.from("1")); - src.push(null); - }); - } - }; - - dst._write = function (chunk, enc, cb) { - accum.push(chunk); - cb(); - }; - - const endPromise = new Promise(resolve => { - src.on("end", resolve); - }); - - src.pipe(dst); - - await Promise.race([ - endPromise, - new Promise((_, reject) => { - setTimeout(() => reject(new Error("timed out waiting for _write")), 100); - }), - ]); - - expect(String(Buffer.concat(accum))).toBe("MQ=="); -}); - -//<#END_FILE: test-stream2-base64-single-char-read-end.js diff --git a/test/js/node/test/parallel/stream2-compatibility.test.js b/test/js/node/test/parallel/stream2-compatibility.test.js deleted file mode 100644 index a06395efa9..0000000000 --- a/test/js/node/test/parallel/stream2-compatibility.test.js +++ /dev/null @@ -1,81 +0,0 @@ -//#FILE: test-stream2-compatibility.js -//#SHA1: 4767fa4655235101bee847c06850d6db3b71d1cd -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const { Readable: R, Writable: W } = require("stream"); - -let ondataCalled = 0; - -class TestReader extends R { - constructor() { - super(); - this._buffer = Buffer.alloc(100, "x"); - - this.on("data", () => { - ondataCalled++; - }); - } - - _read(n) { - this.push(this._buffer); - this._buffer = Buffer.alloc(0); - } -} - -class TestWriter extends W { - constructor() { - super(); - this.write("foo"); - this.end(); - } - - _write(chunk, enc, cb) { - cb(); - } -} - -test("TestReader emits data once", done => { - const reader = new TestReader(); - setImmediate(() => { - expect(ondataCalled).toBe(1); - reader.push(null); - done(); - }); -}); - -test("TestWriter ends correctly", () => { - const writer = new TestWriter(); - expect(writer.writable).toBe(false); -}); - -test("TestReader becomes unreadable", done => { - const reader = new TestReader(); - reader.on("end", () => { - expect(reader.readable).toBe(false); - done(); - }); - reader.push(null); -}); - -//<#END_FILE: test-stream2-compatibility.js diff --git a/test/js/node/test/parallel/stream2-decode-partial.test.js b/test/js/node/test/parallel/stream2-decode-partial.test.js deleted file mode 100644 index 47cbf45918..0000000000 --- a/test/js/node/test/parallel/stream2-decode-partial.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-stream2-decode-partial.js -//#SHA1: bc4bec1c0be7857c86b9cd75dbb76b939d9619ab -//----------------- -"use strict"; - -const { Readable } = require("stream"); - -let buf = ""; -const euro = Buffer.from([0xe2, 0x82, 0xac]); -const cent = Buffer.from([0xc2, 0xa2]); -const source = Buffer.concat([euro, cent]); - -test("Readable stream decodes partial UTF-8 characters correctly", done => { - const readable = Readable({ encoding: "utf8" }); - readable.push(source.slice(0, 2)); - readable.push(source.slice(2, 4)); - readable.push(source.slice(4, 6)); - readable.push(null); - - readable.on("data", function (data) { - buf += data; - }); - - readable.on("end", function () { - expect(buf).toBe("€¢"); - done(); - }); -}); - -//<#END_FILE: test-stream2-decode-partial.js diff --git a/test/js/node/test/parallel/stream2-finish-pipe.test.js b/test/js/node/test/parallel/stream2-finish-pipe.test.js deleted file mode 100644 index db9e55b8f6..0000000000 --- a/test/js/node/test/parallel/stream2-finish-pipe.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-stream2-finish-pipe.js -//#SHA1: dfe174476d312542a54b9574955bddf0ad35aa9e -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const stream = require("stream"); - -test("stream2 finish pipe", done => { - const r = new stream.Readable(); - r._read = function (size) { - r.push(Buffer.allocUnsafe(size)); - }; - - const w = new stream.Writable(); - w._write = function (data, encoding, cb) { - process.nextTick(cb, null); - }; - - r.pipe(w); - - // end() must be called in nextTick or a WRITE_AFTER_END error occurs. - process.nextTick(() => { - // This might sound unrealistic, but it happens in net.js. When - // socket.allowHalfOpen === false, EOF will cause .destroySoon() call which - // ends the writable side of net.Socket. - w.end(); - // We need to wait for the 'finish' event to ensure the test completes successfully - w.on("finish", () => { - done(); - }); - }); -}); - -//<#END_FILE: test-stream2-finish-pipe.js diff --git a/test/js/node/test/parallel/stream2-large-read-stall.test.js b/test/js/node/test/parallel/stream2-large-read-stall.test.js deleted file mode 100644 index 9754a67b0a..0000000000 --- a/test/js/node/test/parallel/stream2-large-read-stall.test.js +++ /dev/null @@ -1,78 +0,0 @@ -//#FILE: test-stream2-large-read-stall.js -//#SHA1: c83f78a1c91b12eedd6262c4691bb2e9118c90ae -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -// If everything aligns so that you do a read(n) of exactly the -// remaining buffer, then make sure that 'end' still emits. - -const READSIZE = 100; -const PUSHSIZE = 20; -const PUSHCOUNT = 1000; -const HWM = 50; - -const { Readable } = require("stream"); - -test("large read stall", async () => { - const r = new Readable({ - highWaterMark: HWM, - }); - const rs = r._readableState; - - r._read = push; - - let pushes = 0; - function push() { - if (pushes > PUSHCOUNT) return; - - if (pushes++ === PUSHCOUNT) { - console.error(" push(EOF)"); - return r.push(null); - } - - console.error(` push #${pushes}`); - if (r.push(Buffer.allocUnsafe(PUSHSIZE))) setTimeout(push, 1); - } - - r.on("readable", function () { - console.error(">> readable"); - let ret; - do { - console.error(` > read(${READSIZE})`); - ret = r.read(READSIZE); - console.error(` < ${ret && ret.length} (${rs.length} remain)`); - } while (ret && ret.length === READSIZE); - - console.error("<< after read()", ret && ret.length, rs.needReadable, rs.length); - }); - - await new Promise(resolve => { - r.on("end", () => { - expect(pushes).toBe(PUSHCOUNT + 1); - resolve(); - }); - }); -}); - -//<#END_FILE: test-stream2-large-read-stall.js diff --git a/test/js/node/test/parallel/stream2-objects.test.js b/test/js/node/test/parallel/stream2-objects.test.js deleted file mode 100644 index 847802f2db..0000000000 --- a/test/js/node/test/parallel/stream2-objects.test.js +++ /dev/null @@ -1,298 +0,0 @@ -//#FILE: test-stream2-objects.js -//#SHA1: e9aa308270bcb656e33df7deb63c8ce8739c0f35 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const { Readable, Writable } = require("stream"); - -function toArray(callback) { - const stream = new Writable({ objectMode: true }); - const list = []; - stream.write = function (chunk) { - list.push(chunk); - }; - - stream.end = function () { - callback(list); - }; - - return stream; -} - -function fromArray(list) { - const r = new Readable({ objectMode: true }); - r._read = jest.fn(); - list.forEach(function (chunk) { - r.push(chunk); - }); - r.push(null); - - return r; -} - -test("Verify that objects can be read from the stream", () => { - const r = fromArray([{ one: "1" }, { two: "2" }]); - - const v1 = r.read(); - const v2 = r.read(); - const v3 = r.read(); - - expect(v1).toEqual({ one: "1" }); - expect(v2).toEqual({ two: "2" }); - expect(v3).toBeNull(); -}); - -test("Verify that objects can be piped into the stream", done => { - const r = fromArray([{ one: "1" }, { two: "2" }]); - - r.pipe( - toArray(list => { - expect(list).toEqual([{ one: "1" }, { two: "2" }]); - done(); - }), - ); -}); - -test("Verify that read(n) is ignored", () => { - const r = fromArray([{ one: "1" }, { two: "2" }]); - const value = r.read(2); - - expect(value).toEqual({ one: "1" }); -}); - -test("Verify that objects can be synchronously read", done => { - const r = new Readable({ objectMode: true }); - const list = [{ one: "1" }, { two: "2" }]; - r._read = function (n) { - const item = list.shift(); - r.push(item || null); - }; - - r.pipe( - toArray(list => { - expect(list).toEqual([{ one: "1" }, { two: "2" }]); - done(); - }), - ); -}); - -test("Verify that objects can be asynchronously read", done => { - const r = new Readable({ objectMode: true }); - const list = [{ one: "1" }, { two: "2" }]; - r._read = function (n) { - const item = list.shift(); - process.nextTick(function () { - r.push(item || null); - }); - }; - - r.pipe( - toArray(list => { - expect(list).toEqual([{ one: "1" }, { two: "2" }]); - done(); - }), - ); -}); - -test("Verify that strings can be read as objects", done => { - const r = new Readable({ - objectMode: true, - }); - r._read = jest.fn(); - const list = ["one", "two", "three"]; - list.forEach(function (str) { - r.push(str); - }); - r.push(null); - - r.pipe( - toArray(array => { - expect(array).toEqual(list); - done(); - }), - ); -}); - -test("Verify read(0) behavior for object streams", done => { - const r = new Readable({ - objectMode: true, - }); - r._read = jest.fn(); - - r.push("foobar"); - r.push(null); - - r.pipe( - toArray(array => { - expect(array).toEqual(["foobar"]); - done(); - }), - ); -}); - -test("Verify the behavior of pushing falsey values", done => { - const r = new Readable({ - objectMode: true, - }); - r._read = jest.fn(); - - r.push(false); - r.push(0); - r.push(""); - r.push(null); - - r.pipe( - toArray(array => { - expect(array).toEqual([false, 0, ""]); - done(); - }), - ); -}); - -test("Verify high watermark _read() behavior", () => { - const r = new Readable({ - highWaterMark: 6, - objectMode: true, - }); - let calls = 0; - const list = ["1", "2", "3", "4", "5", "6", "7", "8"]; - - r._read = function (n) { - calls++; - }; - - list.forEach(function (c) { - r.push(c); - }); - - const v = r.read(); - - expect(calls).toBe(0); - expect(v).toBe("1"); - - const v2 = r.read(); - expect(v2).toBe("2"); - - const v3 = r.read(); - expect(v3).toBe("3"); - - expect(calls).toBe(1); -}); - -test("Verify high watermark push behavior", () => { - const r = new Readable({ - highWaterMark: 6, - objectMode: true, - }); - r._read = jest.fn(); - for (let i = 0; i < 6; i++) { - const bool = r.push(i); - expect(bool).toBe(i !== 5); - } -}); - -test("Verify that objects can be written to stream", done => { - const w = new Writable({ objectMode: true }); - - w._write = function (chunk, encoding, cb) { - expect(chunk).toEqual({ foo: "bar" }); - cb(); - }; - - w.on("finish", done); - w.write({ foo: "bar" }); - w.end(); -}); - -test("Verify that multiple objects can be written to stream", done => { - const w = new Writable({ objectMode: true }); - const list = []; - - w._write = function (chunk, encoding, cb) { - list.push(chunk); - cb(); - }; - - w.on("finish", () => { - expect(list).toEqual([0, 1, 2, 3, 4]); - done(); - }); - - w.write(0); - w.write(1); - w.write(2); - w.write(3); - w.write(4); - w.end(); -}); - -test("Verify that strings can be written as objects", done => { - const w = new Writable({ - objectMode: true, - }); - const list = []; - - w._write = function (chunk, encoding, cb) { - list.push(chunk); - process.nextTick(cb); - }; - - w.on("finish", () => { - expect(list).toEqual(["0", "1", "2", "3", "4"]); - done(); - }); - - w.write("0"); - w.write("1"); - w.write("2"); - w.write("3"); - w.write("4"); - w.end(); -}); - -test("Verify that stream buffers finish until callback is called", done => { - const w = new Writable({ - objectMode: true, - }); - let called = false; - - w._write = function (chunk, encoding, cb) { - expect(chunk).toBe("foo"); - - process.nextTick(function () { - called = true; - cb(); - }); - }; - - w.on("finish", () => { - expect(called).toBe(true); - done(); - }); - - w.write("foo"); - w.end(); -}); - -//<#END_FILE: test-stream2-objects.js diff --git a/test/js/node/test/parallel/stream2-pipe-error-handling.test.js b/test/js/node/test/parallel/stream2-pipe-error-handling.test.js deleted file mode 100644 index 4a482b234a..0000000000 --- a/test/js/node/test/parallel/stream2-pipe-error-handling.test.js +++ /dev/null @@ -1,110 +0,0 @@ -//#FILE: test-stream2-pipe-error-handling.js -//#SHA1: c5e7ad139c64f22b16e8fff8a62f6f91067087c8 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const stream = require("stream"); - -test("stream pipe error handling with autoDestroy true", () => { - let count = 1000; - - const source = new stream.Readable(); - source._read = function (n) { - n = Math.min(count, n); - count -= n; - source.push(Buffer.allocUnsafe(n)); - }; - - let unpipedDest; - source.unpipe = function (dest) { - unpipedDest = dest; - stream.Readable.prototype.unpipe.call(this, dest); - }; - - const dest = new stream.Writable(); - dest._write = function (chunk, encoding, cb) { - cb(); - }; - - source.pipe(dest); - - let gotErr = null; - dest.on("error", function (err) { - gotErr = err; - }); - - let unpipedSource; - dest.on("unpipe", function (src) { - unpipedSource = src; - }); - - const err = new Error("This stream turned into bacon."); - dest.emit("error", err); - expect(gotErr).toBe(err); - expect(unpipedSource).toBe(source); - expect(unpipedDest).toBe(dest); -}); - -test("stream pipe error handling with autoDestroy false", () => { - let count = 1000; - - const source = new stream.Readable(); - source._read = function (n) { - n = Math.min(count, n); - count -= n; - source.push(Buffer.allocUnsafe(n)); - }; - - let unpipedDest; - source.unpipe = function (dest) { - unpipedDest = dest; - stream.Readable.prototype.unpipe.call(this, dest); - }; - - const dest = new stream.Writable({ autoDestroy: false }); - dest._write = function (chunk, encoding, cb) { - cb(); - }; - - source.pipe(dest); - - let unpipedSource; - dest.on("unpipe", function (src) { - unpipedSource = src; - }); - - const err = new Error("This stream turned into bacon."); - - let gotErr = null; - expect(() => { - dest.emit("error", err); - }).toThrow( - expect.objectContaining({ - message: expect.any(String), - }), - ); - expect(unpipedSource).toBe(source); - expect(unpipedDest).toBe(dest); -}); - -//<#END_FILE: test-stream2-pipe-error-handling.js diff --git a/test/js/node/test/parallel/stream2-pipe-error-once-listener.test.js b/test/js/node/test/parallel/stream2-pipe-error-once-listener.test.js deleted file mode 100644 index 4879a847e0..0000000000 --- a/test/js/node/test/parallel/stream2-pipe-error-once-listener.test.js +++ /dev/null @@ -1,63 +0,0 @@ -//#FILE: test-stream2-pipe-error-once-listener.js -//#SHA1: a0bd981aa626f937edb6779bcf0e4dc49b82e69e -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const stream = require("stream"); - -class Read extends stream.Readable { - _read(size) { - this.push("x"); - this.push(null); - } -} - -class Write extends stream.Writable { - _write(buffer, encoding, cb) { - this.emit("error", new Error("boom")); - this.emit("alldone"); - } -} - -test("stream2 pipe error once listener", done => { - const read = new Read(); - const write = new Write(); - - write.once("error", () => {}); - write.once("alldone", () => { - console.log("ok"); - done(); - }); - - const exitSpy = jest.spyOn(process, "exit").mockImplementation(() => {}); - - read.pipe(write); - - process.nextTick(() => { - expect(exitSpy).not.toHaveBeenCalled(); - exitSpy.mockRestore(); - }); -}); - -//<#END_FILE: test-stream2-pipe-error-once-listener.js diff --git a/test/js/node/test/parallel/stream2-push.test.js b/test/js/node/test/parallel/stream2-push.test.js deleted file mode 100644 index d623f82a98..0000000000 --- a/test/js/node/test/parallel/stream2-push.test.js +++ /dev/null @@ -1,139 +0,0 @@ -//#FILE: test-stream2-push.js -//#SHA1: 9b6aded0c40321f8d2c15dceab98b26033f23adf -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const { Readable, Writable } = require("stream"); -const EE = require("events").EventEmitter; - -test("Stream2 push behavior", async () => { - const stream = new Readable({ - highWaterMark: 16, - encoding: "utf8", - }); - - const source = new EE(); - - stream._read = function () { - console.error("stream._read"); - readStart(); - }; - - let ended = false; - stream.on("end", function () { - ended = true; - }); - - source.on("data", function (chunk) { - const ret = stream.push(chunk); - console.error("data", stream.readableLength); - if (!ret) readStop(); - }); - - source.on("end", function () { - stream.push(null); - }); - - let reading = false; - - function readStart() { - console.error("readStart"); - reading = true; - } - - function readStop() { - console.error("readStop"); - reading = false; - process.nextTick(function () { - const r = stream.read(); - if (r !== null) writer.write(r); - }); - } - - const writer = new Writable({ - decodeStrings: false, - }); - - const written = []; - - const expectWritten = [ - "asdfgasdfgasdfgasdfg", - "asdfgasdfgasdfgasdfg", - "asdfgasdfgasdfgasdfg", - "asdfgasdfgasdfgasdfg", - "asdfgasdfgasdfgasdfg", - "asdfgasdfgasdfgasdfg", - ]; - - writer._write = function (chunk, encoding, cb) { - console.error(`WRITE ${chunk}`); - written.push(chunk); - process.nextTick(cb); - }; - - const finishPromise = new Promise(resolve => { - writer.on("finish", () => { - console.error("finish"); - expect(written).toEqual(expectWritten); - console.log("ok"); - resolve(); - }); - }); - - // Now emit some chunks. - const chunk = "asdfg"; - - let set = 0; - readStart(); - - function data() { - expect(reading).toBe(true); - source.emit("data", chunk); - expect(reading).toBe(true); - source.emit("data", chunk); - expect(reading).toBe(true); - source.emit("data", chunk); - expect(reading).toBe(true); - source.emit("data", chunk); - expect(reading).toBe(false); - if (set++ < 5) return new Promise(resolve => setTimeout(() => resolve(data()), 10)); - else return end(); - } - - function end() { - source.emit("end"); - expect(reading).toBe(false); - writer.end(stream.read()); - return new Promise(resolve => - setImmediate(() => { - expect(ended).toBe(true); - resolve(); - }), - ); - } - - await data(); - await finishPromise; -}); - -//<#END_FILE: test-stream2-push.js diff --git a/test/js/node/test/parallel/stream2-readable-empty-buffer-no-eof.test.js b/test/js/node/test/parallel/stream2-readable-empty-buffer-no-eof.test.js deleted file mode 100644 index 5aad62d568..0000000000 --- a/test/js/node/test/parallel/stream2-readable-empty-buffer-no-eof.test.js +++ /dev/null @@ -1,107 +0,0 @@ -//#FILE: test-stream2-readable-empty-buffer-no-eof.js -//#SHA1: 70ef48637116477747867b03a60462fb64331087 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const { Readable } = require("stream"); - -test("test1", done => { - const r = new Readable(); - - // Should not end when we get a Buffer.alloc(0) or '' as the _read - // result that just means that there is *temporarily* no data, but to - // go ahead and try again later. - // - // note that this is very unusual. it only works for crypto streams - // because the other side of the stream will call read(0) to cycle - // data through openssl. that's why setImmediate() is used to call - // r.read(0) again later, otherwise there is no more work being done - // and the process just exits. - - const buf = Buffer.alloc(5, "x"); - let reads = 5; - r._read = function (n) { - switch (reads--) { - case 5: - return setImmediate(() => { - return r.push(buf); - }); - case 4: - setImmediate(() => { - return r.push(Buffer.alloc(0)); - }); - return setImmediate(r.read.bind(r, 0)); - case 3: - setImmediate(r.read.bind(r, 0)); - return process.nextTick(() => { - return r.push(Buffer.alloc(0)); - }); - case 2: - setImmediate(r.read.bind(r, 0)); - return r.push(Buffer.alloc(0)); // Not-EOF! - case 1: - return r.push(buf); - case 0: - return r.push(null); // EOF - default: - throw new Error("unreachable"); - } - }; - - const results = []; - function flow() { - let chunk; - while (null !== (chunk = r.read())) results.push(String(chunk)); - } - r.on("readable", flow); - r.on("end", () => { - results.push("EOF"); - expect(results).toEqual(["xxxxx", "xxxxx", "EOF"]); - done(); - }); - flow(); -}); - -test("test2", done => { - const r = new Readable({ encoding: "base64" }); - let reads = 5; - r._read = function (n) { - if (!reads--) return r.push(null); // EOF - return r.push(Buffer.from("x")); - }; - - const results = []; - function flow() { - let chunk; - while (null !== (chunk = r.read())) results.push(String(chunk)); - } - r.on("readable", flow); - r.on("end", () => { - results.push("EOF"); - expect(results).toEqual(["eHh4", "eHg=", "EOF"]); - done(); - }); - flow(); -}); - -//<#END_FILE: test-stream2-readable-empty-buffer-no-eof.js diff --git a/test/js/node/test/parallel/stream2-readable-legacy-drain.test.js b/test/js/node/test/parallel/stream2-readable-legacy-drain.test.js deleted file mode 100644 index 137ee20ab6..0000000000 --- a/test/js/node/test/parallel/stream2-readable-legacy-drain.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-stream2-readable-legacy-drain.js -//#SHA1: 8182fd1e12ce8538106404d39102ea69eee2e467 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const Stream = require("stream"); -const Readable = Stream.Readable; - -test("Readable stream with legacy drain", done => { - const r = new Readable(); - const N = 256; - let reads = 0; - r._read = function (n) { - return r.push(++reads === N ? null : Buffer.allocUnsafe(1)); - }; - - const onEnd = jest.fn(); - r.on("end", onEnd); - - const w = new Stream(); - w.writable = true; - let buffered = 0; - w.write = function (c) { - buffered += c.length; - process.nextTick(drain); - return false; - }; - - function drain() { - expect(buffered).toBeLessThanOrEqual(3); - buffered = 0; - w.emit("drain"); - } - - const endSpy = jest.fn(); - w.end = endSpy; - - r.pipe(w); - - // Wait for the 'end' event to be emitted - r.on("end", () => { - expect(onEnd).toHaveBeenCalledTimes(1); - expect(endSpy).toHaveBeenCalledTimes(1); - done(); - }); -}); - -//<#END_FILE: test-stream2-readable-legacy-drain.js diff --git a/test/js/node/test/parallel/stream2-readable-wrap-destroy.test.js b/test/js/node/test/parallel/stream2-readable-wrap-destroy.test.js deleted file mode 100644 index b001ee2a9b..0000000000 --- a/test/js/node/test/parallel/stream2-readable-wrap-destroy.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-stream2-readable-wrap-destroy.js -//#SHA1: 632a198f6b4fc882942984df461383047f6b78a6 -//----------------- -"use strict"; - -const { Readable } = require("stream"); -const EventEmitter = require("events"); - -test('Readable.wrap should call destroy on "destroy" event', () => { - const oldStream = new EventEmitter(); - oldStream.pause = jest.fn(); - oldStream.resume = jest.fn(); - - const destroyMock = jest.fn(); - - const readable = new Readable({ - autoDestroy: false, - destroy: destroyMock, - }); - - readable.wrap(oldStream); - oldStream.emit("destroy"); - - expect(destroyMock).toHaveBeenCalledTimes(1); -}); - -test('Readable.wrap should call destroy on "close" event', () => { - const oldStream = new EventEmitter(); - oldStream.pause = jest.fn(); - oldStream.resume = jest.fn(); - - const destroyMock = jest.fn(); - - const readable = new Readable({ - autoDestroy: false, - destroy: destroyMock, - }); - - readable.wrap(oldStream); - oldStream.emit("close"); - - expect(destroyMock).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-stream2-readable-wrap-destroy.js diff --git a/test/js/node/test/parallel/stream2-readable-wrap-empty.test.js b/test/js/node/test/parallel/stream2-readable-wrap-empty.test.js deleted file mode 100644 index 9735960e5b..0000000000 --- a/test/js/node/test/parallel/stream2-readable-wrap-empty.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-stream2-readable-wrap-empty.js -//#SHA1: aaac82ec7df0743321f2aaacd9512ecf1b932ad6 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const { Readable } = require("stream"); -const EventEmitter = require("events"); - -test("Readable.wrap with empty stream", done => { - const oldStream = new EventEmitter(); - oldStream.pause = jest.fn(); - oldStream.resume = jest.fn(); - - const newStream = new Readable().wrap(oldStream); - - newStream - .on("readable", () => {}) - .on("end", () => { - done(); - }); - - oldStream.emit("end"); -}); - -//<#END_FILE: test-stream2-readable-wrap-empty.js diff --git a/test/js/node/test/parallel/stream2-unpipe-drain.test.js b/test/js/node/test/parallel/stream2-unpipe-drain.test.js deleted file mode 100644 index a2ea536ef6..0000000000 --- a/test/js/node/test/parallel/stream2-unpipe-drain.test.js +++ /dev/null @@ -1,75 +0,0 @@ -//#FILE: test-stream2-unpipe-drain.js -//#SHA1: b04d9c383281786f45989d8d7f85f6f1a620bde2 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const stream = require("stream"); - -class TestWriter extends stream.Writable { - _write(buffer, encoding, callback) { - console.log("write called"); - // Super slow write stream (callback never called) - } -} - -class TestReader extends stream.Readable { - constructor() { - super(); - this.reads = 0; - } - - _read(size) { - this.reads += 1; - this.push(Buffer.alloc(size)); - } -} - -test("stream2 unpipe drain", done => { - const dest = new TestWriter(); - const src1 = new TestReader(); - const src2 = new TestReader(); - - src1.pipe(dest); - - src1.once("readable", () => { - process.nextTick(() => { - src2.pipe(dest); - - src2.once("readable", () => { - process.nextTick(() => { - src1.unpipe(dest); - - // Use setImmediate to ensure all microtasks have been processed - setImmediate(() => { - expect(src1.reads).toBe(2); - expect(src2.reads).toBe(2); - done(); - }); - }); - }); - }); - }); -}); - -//<#END_FILE: test-stream2-unpipe-drain.js diff --git a/test/js/node/test/parallel/stream3-cork-end.test.js b/test/js/node/test/parallel/stream3-cork-end.test.js deleted file mode 100644 index 974e12ce1a..0000000000 --- a/test/js/node/test/parallel/stream3-cork-end.test.js +++ /dev/null @@ -1,98 +0,0 @@ -//#FILE: test-stream3-cork-end.js -//#SHA1: 1ac6a2589bee41bc1e9e08ef308bcae3cd999106 -//----------------- -"use strict"; - -const stream = require("stream"); -const Writable = stream.Writable; - -// Test the buffering behavior of Writable streams. -// -// The call to cork() triggers storing chunks which are flushed -// on calling end() and the stream subsequently ended. -// -// node version target: 0.12 - -test("Writable stream buffering behavior with cork() and end()", done => { - const expectedChunks = ["please", "buffer", "me", "kindly"]; - const inputChunks = expectedChunks.slice(0); - let seenChunks = []; - let seenEnd = false; - - const w = new Writable(); - // Let's arrange to store the chunks. - w._write = function (chunk, encoding, cb) { - // Stream end event is not seen before the last write. - expect(seenEnd).toBe(false); - // Default encoding given none was specified. - expect(encoding).toBe("buffer"); - - seenChunks.push(chunk); - cb(); - }; - // Let's record the stream end event. - w.on("finish", () => { - seenEnd = true; - }); - - function writeChunks(remainingChunks, callback) { - const writeChunk = remainingChunks.shift(); - let writeState; - - if (writeChunk) { - setImmediate(() => { - writeState = w.write(writeChunk); - // We were not told to stop writing. - expect(writeState).toBe(true); - - writeChunks(remainingChunks, callback); - }); - } else { - callback(); - } - } - - // Do an initial write. - w.write("stuff"); - // The write was immediate. - expect(seenChunks.length).toBe(1); - // Reset the seen chunks. - seenChunks = []; - - // Trigger stream buffering. - w.cork(); - - // Write the bufferedChunks. - writeChunks(inputChunks, () => { - // Should not have seen anything yet. - expect(seenChunks.length).toBe(0); - - // Trigger flush and ending the stream. - w.end(); - - // Stream should not ended in current tick. - expect(seenEnd).toBe(false); - - // Buffered bytes should be seen in current tick. - expect(seenChunks.length).toBe(4); - - // Did the chunks match. - for (let i = 0, l = expectedChunks.length; i < l; i++) { - const seen = seenChunks[i]; - // There was a chunk. - expect(seen).toBeTruthy(); - - const expected = Buffer.from(expectedChunks[i]); - // It was what we expected. - expect(seen.equals(expected)).toBe(true); - } - - setImmediate(() => { - // Stream should have ended in next tick. - expect(seenEnd).toBe(true); - done(); - }); - }); -}); - -//<#END_FILE: test-stream3-cork-end.js diff --git a/test/js/node/test/parallel/stream3-cork-uncork.test.js b/test/js/node/test/parallel/stream3-cork-uncork.test.js deleted file mode 100644 index 85aa626dec..0000000000 --- a/test/js/node/test/parallel/stream3-cork-uncork.test.js +++ /dev/null @@ -1,104 +0,0 @@ -//#FILE: test-stream3-cork-uncork.js -//#SHA1: d1cc0d9e9be4ae657ab2db8e02589ac485268c63 -//----------------- -"use strict"; - -const stream = require("stream"); -const Writable = stream.Writable; - -// Test the buffering behavior of Writable streams. -// -// The call to cork() triggers storing chunks which are flushed -// on calling uncork() in the same tick. -// -// node version target: 0.12 - -describe("Writable stream cork and uncork", () => { - const expectedChunks = ["please", "buffer", "me", "kindly"]; - let inputChunks; - let seenChunks; - let seenEnd; - let w; - - beforeEach(() => { - inputChunks = expectedChunks.slice(0); - seenChunks = []; - seenEnd = false; - - w = new Writable(); - // Let's arrange to store the chunks. - w._write = function (chunk, encoding, cb) { - // Default encoding given none was specified. - expect(encoding).toBe("buffer"); - - seenChunks.push(chunk); - cb(); - }; - // Let's record the stream end event. - w.on("finish", () => { - seenEnd = true; - }); - }); - - function writeChunks(remainingChunks) { - return new Promise(resolve => { - function write() { - const writeChunk = remainingChunks.shift(); - if (writeChunk) { - setImmediate(() => { - const writeState = w.write(writeChunk); - // We were not told to stop writing. - expect(writeState).toBe(true); - write(); - }); - } else { - resolve(); - } - } - write(); - }); - } - - test("initial write is immediate", () => { - w.write("stuff"); - // The write was immediate. - expect(seenChunks.length).toBe(1); - }); - - test("cork buffers writes and uncork flushes", async () => { - // Reset the chunks seen so far. - seenChunks = []; - - // Trigger stream buffering. - w.cork(); - - // Write the bufferedChunks. - await writeChunks(inputChunks); - - // Should not have seen anything yet. - expect(seenChunks.length).toBe(0); - - // Trigger writing out the buffer. - w.uncork(); - - // Buffered bytes should be seen in current tick. - expect(seenChunks.length).toBe(4); - - // Did the chunks match. - for (let i = 0, l = expectedChunks.length; i < l; i++) { - const seen = seenChunks[i]; - // There was a chunk. - expect(seen).toBeTruthy(); - - const expected = Buffer.from(expectedChunks[i]); - // It was what we expected. - expect(seen.equals(expected)).toBe(true); - } - - await new Promise(resolve => setImmediate(resolve)); - // The stream should not have been ended. - expect(seenEnd).toBe(false); - }); -}); - -//<#END_FILE: test-stream3-cork-uncork.js diff --git a/test/js/node/test/parallel/stream3-pause-then-read.test.js b/test/js/node/test/parallel/stream3-pause-then-read.test.js deleted file mode 100644 index f7df831524..0000000000 --- a/test/js/node/test/parallel/stream3-pause-then-read.test.js +++ /dev/null @@ -1,187 +0,0 @@ -//#FILE: test-stream3-pause-then-read.js -//#SHA1: bd44dc04c63140e4b65c0755eec67a55eaf48158 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const stream = require("stream"); -const Readable = stream.Readable; -const Writable = stream.Writable; - -const totalChunks = 100; -const chunkSize = 99; -const expectTotalData = totalChunks * chunkSize; -let expectEndingData = expectTotalData; - -let r, totalPushed; - -beforeEach(() => { - r = new Readable({ highWaterMark: 1000 }); - let chunks = totalChunks; - r._read = function (n) { - console.log("_read called", chunks); - if (!(chunks % 2)) setImmediate(push); - else if (!(chunks % 3)) process.nextTick(push); - else push(); - }; - - totalPushed = 0; - function push() { - const chunk = chunks-- > 0 ? Buffer.alloc(chunkSize, "x") : null; - if (chunk) { - totalPushed += chunk.length; - } - console.log("chunks", chunks); - r.push(chunk); - } -}); - -test("stream3 pause then read", async () => { - await read100(); - await new Promise(resolve => setImmediate(resolve)); - await pipeLittle(); - await read1234(); - await resumePause(); - await pipe(); -}); - -// First we read 100 bytes. -async function read100() { - await readn(100); -} - -async function readn(n) { - console.error(`read ${n}`); - expectEndingData -= n; - return new Promise(resolve => { - function read() { - const c = r.read(n); - console.error("c", c); - if (!c) r.once("readable", read); - else { - expect(c.length).toBe(n); - expect(r.readableFlowing).toBeFalsy(); - resolve(); - } - } - read(); - }); -} - -// Then we listen to some data events. -function onData() { - return new Promise(resolve => { - expectEndingData -= 100; - console.error("onData"); - let seen = 0; - r.on("data", function od(c) { - seen += c.length; - if (seen >= 100) { - // Seen enough - r.removeListener("data", od); - r.pause(); - if (seen > 100) { - // Oh no, seen too much! - // Put the extra back. - const diff = seen - 100; - r.unshift(c.slice(c.length - diff)); - console.error("seen too much", seen, diff); - } - resolve(); - } - }); - }); -} - -// Just pipe 200 bytes, then unshift the extra and unpipe. -async function pipeLittle() { - expectEndingData -= 200; - console.error("pipe a little"); - const w = new Writable(); - let written = 0; - await new Promise(resolve => { - w.on("finish", () => { - expect(written).toBe(200); - resolve(); - }); - w._write = function (chunk, encoding, cb) { - written += chunk.length; - if (written >= 200) { - r.unpipe(w); - w.end(); - cb(); - if (written > 200) { - const diff = written - 200; - written -= diff; - r.unshift(chunk.slice(chunk.length - diff)); - } - } else { - setImmediate(cb); - } - }; - r.pipe(w); - }); -} - -// Now read 1234 more bytes. -async function read1234() { - await readn(1234); -} - -function resumePause() { - console.error("resumePause"); - // Don't read anything, just resume and re-pause a whole bunch. - r.resume(); - r.pause(); - r.resume(); - r.pause(); - r.resume(); - r.pause(); - r.resume(); - r.pause(); - r.resume(); - r.pause(); - return new Promise(resolve => setImmediate(resolve)); -} - -function pipe() { - console.error("pipe the rest"); - const w = new Writable(); - let written = 0; - w._write = function (chunk, encoding, cb) { - written += chunk.length; - cb(); - }; - return new Promise(resolve => { - w.on("finish", () => { - console.error("written", written, totalPushed); - expect(written).toBe(expectEndingData); - expect(totalPushed).toBe(expectTotalData); - console.log("ok"); - resolve(); - }); - r.pipe(w); - }); -} - -//<#END_FILE: test-stream3-pause-then-read.js diff --git a/test/js/node/test/parallel/stream3-pipeline-async-iterator.test.js b/test/js/node/test/parallel/stream3-pipeline-async-iterator.test.js deleted file mode 100644 index d84b763fcc..0000000000 --- a/test/js/node/test/parallel/stream3-pipeline-async-iterator.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-stream3-pipeline-async-iterator.js -//#SHA1: db2d5b4cb6c502fdccdfa1ed9384d6baa70b1e0b -//----------------- -/* eslint-disable node-core/require-common-first, require-yield */ -"use strict"; -const { pipeline } = require("node:stream/promises"); - -test("async iterators can act as readable and writable streams", async () => { - // Ensure that async iterators can act as readable and writable streams - async function* myCustomReadable() { - yield "Hello"; - yield "World"; - } - - const messages = []; - async function* myCustomWritable(stream) { - for await (const chunk of stream) { - messages.push(chunk); - } - } - - await pipeline(myCustomReadable, myCustomWritable); - - expect(messages).toEqual(["Hello", "World"]); -}); - -//<#END_FILE: test-stream3-pipeline-async-iterator.js diff --git a/test/js/node/test/parallel/string-decoder-end.test.js b/test/js/node/test/parallel/string-decoder-end.test.js deleted file mode 100644 index fc12a567f7..0000000000 --- a/test/js/node/test/parallel/string-decoder-end.test.js +++ /dev/null @@ -1,124 +0,0 @@ -//#FILE: test-string-decoder-end.js -//#SHA1: 9b9cd65cf41dc419c54b8c47317aef7fcb251c5c -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Verify that the string decoder works getting 1 byte at a time, -// the whole buffer at once, and that both match the .toString(enc) -// result of the entire buffer. - -const assert = require("assert"); -const SD = require("string_decoder").StringDecoder; -const encodings = ["base64", "base64url", "hex", "utf8", "utf16le", "ucs2"]; - -const bufs = ["☃💩", "asdf"].map(b => Buffer.from(b)); - -// Also test just arbitrary bytes from 0-15. -for (let i = 1; i <= 16; i++) { - const bytes = "." - .repeat(i - 1) - .split(".") - .map((_, j) => j + 0x78); - bufs.push(Buffer.from(bytes)); -} - -encodings.forEach(testEncoding); - -function testEncoding(encoding) { - bufs.forEach(buf => { - testBuf(encoding, buf); - }); -} - -function testBuf(encoding, buf) { - test(`StringDecoder ${encoding} - ${buf.toString()}`, () => { - // Write one byte at a time. - let s = new SD(encoding); - let res1 = ""; - for (let i = 0; i < buf.length; i++) { - res1 += s.write(buf.slice(i, i + 1)); - } - res1 += s.end(); - - // Write the whole buffer at once. - let res2 = ""; - s = new SD(encoding); - res2 += s.write(buf); - res2 += s.end(); - - // .toString() on the buffer - const res3 = buf.toString(encoding); - - // One byte at a time should match toString - expect(res1).toBe(res3); - // All bytes at once should match toString - expect(res2).toBe(res3); - }); -} - -function testEnd(encoding, incomplete, next, expected) { - test(`StringDecoder ${encoding} end - ${incomplete.toString("hex")} + ${next.toString("hex")}`, () => { - let res = ""; - const s = new SD(encoding); - res += s.write(incomplete); - res += s.end(); - res += s.write(next); - res += s.end(); - - expect(res).toBe(expected); - }); -} - -testEnd("utf8", Buffer.of(0xe2), Buffer.of(0x61), "\uFFFDa"); -testEnd("utf8", Buffer.of(0xe2), Buffer.of(0x82), "\uFFFD\uFFFD"); -testEnd("utf8", Buffer.of(0xe2), Buffer.of(0xe2), "\uFFFD\uFFFD"); -testEnd("utf8", Buffer.of(0xe2, 0x82), Buffer.of(0x61), "\uFFFDa"); -testEnd("utf8", Buffer.of(0xe2, 0x82), Buffer.of(0xac), "\uFFFD\uFFFD"); -testEnd("utf8", Buffer.of(0xe2, 0x82), Buffer.of(0xe2), "\uFFFD\uFFFD"); -testEnd("utf8", Buffer.of(0xe2, 0x82, 0xac), Buffer.of(0x61), "€a"); - -testEnd("utf16le", Buffer.of(0x3d), Buffer.of(0x61, 0x00), "a"); -testEnd("utf16le", Buffer.of(0x3d), Buffer.of(0xd8, 0x4d, 0xdc), "\u4DD8"); -testEnd("utf16le", Buffer.of(0x3d, 0xd8), Buffer.of(), "\uD83D"); -testEnd("utf16le", Buffer.of(0x3d, 0xd8), Buffer.of(0x61, 0x00), "\uD83Da"); -testEnd("utf16le", Buffer.of(0x3d, 0xd8), Buffer.of(0x4d, 0xdc), "\uD83D\uDC4D"); -testEnd("utf16le", Buffer.of(0x3d, 0xd8, 0x4d), Buffer.of(), "\uD83D"); -testEnd("utf16le", Buffer.of(0x3d, 0xd8, 0x4d), Buffer.of(0x61, 0x00), "\uD83Da"); -testEnd("utf16le", Buffer.of(0x3d, 0xd8, 0x4d), Buffer.of(0xdc), "\uD83D"); -testEnd("utf16le", Buffer.of(0x3d, 0xd8, 0x4d, 0xdc), Buffer.of(0x61, 0x00), "👍a"); - -testEnd("base64", Buffer.of(0x61), Buffer.of(), "YQ=="); -testEnd("base64", Buffer.of(0x61), Buffer.of(0x61), "YQ==YQ=="); -testEnd("base64", Buffer.of(0x61, 0x61), Buffer.of(), "YWE="); -testEnd("base64", Buffer.of(0x61, 0x61), Buffer.of(0x61), "YWE=YQ=="); -testEnd("base64", Buffer.of(0x61, 0x61, 0x61), Buffer.of(), "YWFh"); -testEnd("base64", Buffer.of(0x61, 0x61, 0x61), Buffer.of(0x61), "YWFhYQ=="); - -testEnd("base64url", Buffer.of(0x61), Buffer.of(), "YQ"); -testEnd("base64url", Buffer.of(0x61), Buffer.of(0x61), "YQYQ"); -testEnd("base64url", Buffer.of(0x61, 0x61), Buffer.of(), "YWE"); -testEnd("base64url", Buffer.of(0x61, 0x61), Buffer.of(0x61), "YWEYQ"); -testEnd("base64url", Buffer.of(0x61, 0x61, 0x61), Buffer.of(), "YWFh"); -testEnd("base64url", Buffer.of(0x61, 0x61, 0x61), Buffer.of(0x61), "YWFhYQ"); - -//<#END_FILE: test-string-decoder-end.js diff --git a/test/js/node/test/parallel/sys.test.js b/test/js/node/test/parallel/sys.test.js deleted file mode 100644 index eb25bc0689..0000000000 --- a/test/js/node/test/parallel/sys.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-sys.js -//#SHA1: a7732f65863d5e2856179378dc44f09f2b315650 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const sys = require("sys"); // eslint-disable-line no-restricted-modules -const util = require("util"); - -test("sys module is identical to util module", () => { - expect(sys).toBe(util); -}); - -//<#END_FILE: test-sys.js diff --git a/test/js/node/test/parallel/tcp-wrap-connect.test.js b/test/js/node/test/parallel/tcp-wrap-connect.test.js deleted file mode 100644 index 819fd28e18..0000000000 --- a/test/js/node/test/parallel/tcp-wrap-connect.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-tcp-wrap-connect.js -//#SHA1: cc302b52d997beac187400587ce2dffc0978a7da -//----------------- -"use strict"; - -const net = require("net"); - -let connectCount = 0; -let endCount = 0; -let shutdownCount = 0; - -function makeConnection(server) { - return new Promise((resolve, reject) => { - const client = new net.Socket(); - - client.connect(server.address().port, "127.0.0.1", () => { - expect(client.readable).toBe(true); - expect(client.writable).toBe(true); - - client.end(() => { - shutdownCount++; - client.destroy(); - resolve(); - }); - }); - - client.on("error", reject); - }); -} - -test("TCP connection and shutdown", async () => { - const server = net.createServer(socket => { - connectCount++; - socket.resume(); - socket.on("end", () => { - endCount++; - socket.destroy(); - server.close(); - }); - }); - - await new Promise(resolve => server.listen(0, resolve)); - - await makeConnection(server); - - await new Promise(resolve => server.on("close", resolve)); - - expect(shutdownCount).toBe(1); - expect(connectCount).toBe(1); - expect(endCount).toBe(1); -}); - -//<#END_FILE: test-tcp-wrap-connect.js diff --git a/test/js/node/test/parallel/test-arm-math-illegal-instruction.js b/test/js/node/test/parallel/test-arm-math-illegal-instruction.js new file mode 100644 index 0000000000..4bf881d1b3 --- /dev/null +++ b/test/js/node/test/parallel/test-arm-math-illegal-instruction.js @@ -0,0 +1,15 @@ +'use strict'; +require('../common'); + +// This test ensures Math functions don't fail with an "illegal instruction" +// error on ARM devices (primarily on the Raspberry Pi 1) +// See https://github.com/nodejs/node/issues/1376 +// and https://code.google.com/p/v8/issues/detail?id=4019 + +// Iterate over all Math functions +Object.getOwnPropertyNames(Math).forEach((functionName) => { + if (!/[A-Z]/.test(functionName)) { + // The function names don't have capital letters. + Math[functionName](-0.5); + } +}); diff --git a/test/js/node/test/parallel/test-assert-builtins-not-read-from-filesystem.js b/test/js/node/test/parallel/test-assert-builtins-not-read-from-filesystem.js new file mode 100644 index 0000000000..7a713a2ea4 --- /dev/null +++ b/test/js/node/test/parallel/test-assert-builtins-not-read-from-filesystem.js @@ -0,0 +1,48 @@ +'use strict'; + +// Do not read filesystem when creating AssertionError messages for code in +// builtin modules. + +require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); +const e = new EventEmitter(); +e.on('hello', assert); + +if (process.argv[2] !== 'child') { + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + const { spawnSync } = require('child_process'); + + let threw = false; + try { + e.emit('hello', false); + } catch (err) { + const frames = err.stack.split('\n'); + const [, filename, line, column] = frames[1].match(/\((.+):(\d+):(\d+)\)/); + // Spawn a child process to avoid the error having been cached in the assert + // module's `errorCache` Map. + + const { output, status, error } = + spawnSync(process.execPath, + [process.argv[1], 'child', filename, line, column], + { cwd: tmpdir.path, env: process.env }); + assert.ifError(error); + assert.strictEqual(status, 0, `Exit code: ${status}\n${output}`); + threw = true; + } + assert.ok(threw); +} else { + const { writeFileSync } = require('fs'); + const [, , , filename, line, column] = process.argv; + const data = `${'\n'.repeat(line - 1)}${' '.repeat(column - 1)}` + + 'ok(failed(badly));'; + + writeFileSync(filename, data); + assert.throws( + () => e.emit('hello', false), + { + message: 'false == true' + } + ); +} diff --git a/test/js/node/test/parallel/test-assert-strict-exists.js b/test/js/node/test/parallel/test-assert-strict-exists.js new file mode 100644 index 0000000000..50cd8a49a7 --- /dev/null +++ b/test/js/node/test/parallel/test-assert-strict-exists.js @@ -0,0 +1,6 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(require('assert/strict'), assert.strict); diff --git a/test/js/node/test/parallel/test-async-hooks-recursive-stack-runInAsyncScope.js b/test/js/node/test/parallel/test-async-hooks-recursive-stack-runInAsyncScope.js new file mode 100644 index 0000000000..bc4ac86e7f --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-recursive-stack-runInAsyncScope.js @@ -0,0 +1,20 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const async_hooks = require('async_hooks'); + +// This test verifies that the async ID stack can grow indefinitely. + +function recurse(n) { + const a = new async_hooks.AsyncResource('foobar'); + a.runInAsyncScope(() => { + assert.strictEqual(a.asyncId(), async_hooks.executionAsyncId()); + assert.strictEqual(a.triggerAsyncId(), async_hooks.triggerAsyncId()); + if (n >= 0) + recurse(n - 1); + assert.strictEqual(a.asyncId(), async_hooks.executionAsyncId()); + assert.strictEqual(a.triggerAsyncId(), async_hooks.triggerAsyncId()); + }); +} + +recurse(1000); diff --git a/test/js/node/test/parallel/test-async-hooks-run-in-async-scope-caught-exception.js b/test/js/node/test/parallel/test-async-hooks-run-in-async-scope-caught-exception.js new file mode 100644 index 0000000000..e38cefd20e --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-run-in-async-scope-caught-exception.js @@ -0,0 +1,11 @@ +'use strict'; + +require('../common'); +const { AsyncResource } = require('async_hooks'); + +try { + new AsyncResource('foo').runInAsyncScope(() => { throw new Error('bar'); }); +} catch { + // Continue regardless of error. +} +// Should abort (fail the case) if async id is not matching. diff --git a/test/js/node/test/parallel/test-async-hooks-run-in-async-scope-this-arg.js b/test/js/node/test/parallel/test-async-hooks-run-in-async-scope-this-arg.js new file mode 100644 index 0000000000..a5016da9d5 --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-run-in-async-scope-this-arg.js @@ -0,0 +1,17 @@ +'use strict'; + +// Test that passing thisArg to runInAsyncScope() works. + +const common = require('../common'); +const assert = require('assert'); +const { AsyncResource } = require('async_hooks'); + +const thisArg = {}; + +const res = new AsyncResource('fhqwhgads'); + +function callback() { + assert.strictEqual(this, thisArg); +} + +res.runInAsyncScope(common.mustCall(callback), thisArg); diff --git a/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-1.js b/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-1.js new file mode 100644 index 0000000000..eb16645913 --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-1.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +const w = new Worker(` +const { createHook } = require('async_hooks'); + +setImmediate(async () => { + createHook({ init() {} }).enable(); + await 0; + process.exit(); +}); +`, { eval: true }); + +w.on('exit', common.mustCall()); diff --git a/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-2.js b/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-2.js new file mode 100644 index 0000000000..049264d3e8 --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-2.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +// Like test-async-hooks-worker-promise.js but with the `await` and `createHook` +// lines switched, because that resulted in different assertion failures +// (one a Node.js assertion and one a V8 DCHECK) and it seems prudent to +// cover both of those failures. + +const w = new Worker(` +const { createHook } = require('async_hooks'); + +setImmediate(async () => { + await 0; + createHook({ init() {} }).enable(); + process.exit(); +}); +`, { eval: true }); + +w.postMessage({}); +w.on('exit', common.mustCall()); diff --git a/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-3.js b/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-3.js new file mode 100644 index 0000000000..40c7d85835 --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-3.js @@ -0,0 +1,20 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +// Like test-async-hooks-worker-promise.js but with an additional statement +// after the `process.exit()` call, that shouldn’t really make a difference +// but apparently does. + +const w = new Worker(` +const { createHook } = require('async_hooks'); + +setImmediate(async () => { + createHook({ init() {} }).enable(); + await 0; + process.exit(); + process._rawDebug('THIS SHOULD NEVER BE REACHED'); +}); +`, { eval: true }); + +w.on('exit', common.mustCall()); diff --git a/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-4.js b/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-4.js new file mode 100644 index 0000000000..c522091006 --- /dev/null +++ b/test/js/node/test/parallel/test-async-hooks-worker-asyncfn-terminate-4.js @@ -0,0 +1,25 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// Like test-async-hooks-worker-promise.js but doing a trivial counter increase +// after process.exit(). This should not make a difference, but apparently it +// does. This is *also* different from test-async-hooks-worker-promise-3.js, +// in that the statement is an ArrayBuffer access rather than a full method, +// which *also* makes a difference even though it shouldn’t. + +const workerData = new Int32Array(new SharedArrayBuffer(4)); +const w = new Worker(` +const { createHook } = require('async_hooks'); +const { workerData } = require('worker_threads'); + +setImmediate(async () => { + createHook({ init() {} }).enable(); + await 0; + process.exit(); + workerData[0]++; +}); +`, { eval: true, workerData }); + +w.on('exit', common.mustCall(() => assert.strictEqual(workerData[0], 0))); diff --git a/test/js/node/test/parallel/test-async-local-storage-contexts.js b/test/js/node/test/parallel/test-async-local-storage-contexts.js new file mode 100644 index 0000000000..9a63271337 --- /dev/null +++ b/test/js/node/test/parallel/test-async-local-storage-contexts.js @@ -0,0 +1,35 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const vm = require('vm'); +const { AsyncLocalStorage } = require('async_hooks'); + +// Regression test for https://github.com/nodejs/node/issues/38781 + +const context = vm.createContext({ + AsyncLocalStorage, + assert +}); + +vm.runInContext(` + const storage = new AsyncLocalStorage() + async function test() { + return storage.run({ test: 'vm' }, async () => { + assert.strictEqual(storage.getStore().test, 'vm'); + await 42; + assert.strictEqual(storage.getStore().test, 'vm'); + }); + } + test() +`, context); + +const storage = new AsyncLocalStorage(); +async function test() { + return storage.run({ test: 'main context' }, async () => { + assert.strictEqual(storage.getStore().test, 'main context'); + await 42; + assert.strictEqual(storage.getStore().test, 'main context'); + }); +} +test(); diff --git a/test/js/node/test/parallel/test-async-local-storage-deep-stack.js b/test/js/node/test/parallel/test-async-local-storage-deep-stack.js new file mode 100644 index 0000000000..b5e1048d94 --- /dev/null +++ b/test/js/node/test/parallel/test-async-local-storage-deep-stack.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const { AsyncLocalStorage } = require('async_hooks'); + +// Regression test for: https://github.com/nodejs/node/issues/34556 + +const als = new AsyncLocalStorage(); + +const done = common.mustCall(); + +function run(count) { + if (count !== 0) return als.run({}, run, --count); + done(); +} +run(1000); diff --git a/test/js/node/test/parallel/test-async-local-storage-http-multiclients.js b/test/js/node/test/parallel/test-async-local-storage-http-multiclients.js new file mode 100644 index 0000000000..1903d5825d --- /dev/null +++ b/test/js/node/test/parallel/test-async-local-storage-http-multiclients.js @@ -0,0 +1,65 @@ +'use strict'; +const common = require('../common'); +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const { AsyncLocalStorage } = require('async_hooks'); +const http = require('http'); +const cls = new AsyncLocalStorage(); +const NUM_CLIENTS = 10; + +// Run multiple clients that receive data from a server +// in multiple chunks, in a single non-closure function. +// Use the AsyncLocalStorage (ALS) APIs to maintain the context +// and data download. Make sure that individual clients +// receive their respective data, with no conflicts. + +// Set up a server that sends large buffers of data, filled +// with cardinal numbers, increasing per request +let index = 0; +const server = http.createServer((q, r) => { + // Send a large chunk as response, otherwise the data + // may be sent in a single chunk, and the callback in the + // client may be called only once, defeating the purpose of test + r.end((index++ % 10).toString().repeat(1024 * 1024)); +}); + +const countdown = new Countdown(NUM_CLIENTS, () => { + server.close(); +}); + +server.listen(0, common.mustCall(() => { + for (let i = 0; i < NUM_CLIENTS; i++) { + cls.run(new Map(), common.mustCall(() => { + const options = { port: server.address().port }; + const req = http.get(options, common.mustCall((res) => { + const store = cls.getStore(); + store.set('data', ''); + + // Make ondata and onend non-closure + // functions and fully dependent on ALS + res.setEncoding('utf8'); + res.on('data', ondata); + res.on('end', common.mustCall(onend)); + })); + req.end(); + })); + } +})); + +// Accumulate the current data chunk with the store data +function ondata(d) { + const store = cls.getStore(); + assert.notStrictEqual(store, undefined); + let chunk = store.get('data'); + chunk += d; + store.set('data', chunk); +} + +// Retrieve the store data, and test for homogeneity +function onend() { + const store = cls.getStore(); + assert.notStrictEqual(store, undefined); + const data = store.get('data'); + assert.strictEqual(data, data[0].repeat(data.length)); + countdown.dec(); +} diff --git a/test/js/node/test/parallel/test-async-local-storage-snapshot.js b/test/js/node/test/parallel/test-async-local-storage-snapshot.js new file mode 100644 index 0000000000..63e47ba3ce --- /dev/null +++ b/test/js/node/test/parallel/test-async-local-storage-snapshot.js @@ -0,0 +1,16 @@ +'use strict'; + +const common = require('../common'); +const { strictEqual } = require('assert'); +const { AsyncLocalStorage } = require('async_hooks'); + +const asyncLocalStorage = new AsyncLocalStorage(); +const runInAsyncScope = + asyncLocalStorage.run(123, common.mustCall(() => AsyncLocalStorage.snapshot())); +const result = + asyncLocalStorage.run(321, common.mustCall(() => { + return runInAsyncScope(() => { + return asyncLocalStorage.getStore(); + }); + })); +strictEqual(result, 123); diff --git a/test/js/node/test/parallel/test-atomics-wake.js b/test/js/node/test/parallel/test-atomics-wake.js new file mode 100644 index 0000000000..0f38700176 --- /dev/null +++ b/test/js/node/test/parallel/test-atomics-wake.js @@ -0,0 +1,7 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +// https://github.com/nodejs/node/issues/21219 +assert.strictEqual(Atomics.wake, undefined); diff --git a/test/js/node/test/parallel/test-bad-unicode.js b/test/js/node/test/parallel/test-bad-unicode.js new file mode 100644 index 0000000000..b4fccc0644 --- /dev/null +++ b/test/js/node/test/parallel/test-bad-unicode.js @@ -0,0 +1,33 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +let exception = null; + +try { + eval('"\\uc/ef"'); +} catch (e) { + exception = e; +} + +assert(exception instanceof SyntaxError); diff --git a/test/js/node/test/parallel/test-beforeexit-event-exit.js b/test/js/node/test/parallel/test-beforeexit-event-exit.js new file mode 100644 index 0000000000..4210ad04b6 --- /dev/null +++ b/test/js/node/test/parallel/test-beforeexit-event-exit.js @@ -0,0 +1,27 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const { mustNotCall } = require('../common'); + +process.on('beforeExit', mustNotCall('exit should not allow this to occur')); + +process.exit(); diff --git a/test/js/node/test/parallel/test-blob-createobjecturl.js b/test/js/node/test/parallel/test-blob-createobjecturl.js new file mode 100644 index 0000000000..614b8ae4a6 --- /dev/null +++ b/test/js/node/test/parallel/test-blob-createobjecturl.js @@ -0,0 +1,54 @@ +// Flags: --no-warnings +'use strict'; + +const common = require('../common'); + +// Because registering a Blob URL requires generating a random +// UUID, it can only be done if crypto support is enabled. +if (!common.hasCrypto) + common.skip('missing crypto'); + +const { + URL, +} = require('url'); + +const { + Blob, + resolveObjectURL, +} = require('buffer'); + +const assert = require('assert'); + +(async () => { + const blob = new Blob(['hello']); + const id = URL.createObjectURL(blob); + assert.strictEqual(typeof id, 'string'); + const otherBlob = resolveObjectURL(id); + assert.ok(otherBlob instanceof Blob); + assert.strictEqual(otherBlob.constructor, Blob); + assert.strictEqual(otherBlob.size, 5); + assert.strictEqual( + Buffer.from(await otherBlob.arrayBuffer()).toString(), + 'hello'); + URL.revokeObjectURL(id); + + // should do nothing + URL.revokeObjectURL(id); + + assert.strictEqual(resolveObjectURL(id), undefined); + + // Leaving a Blob registered should not cause an assert + // when Node.js exists + URL.createObjectURL(new Blob()); + +})().then(common.mustCall()); + +['not a url', undefined, 1, 'blob:nodedata:1:wrong', {}].forEach((i) => { + assert.strictEqual(resolveObjectURL(i), undefined); +}); + +[undefined, 1, '', false, {}].forEach((i) => { + assert.throws(() => URL.createObjectURL(i), { + code: 'ERR_INVALID_ARG_TYPE', + }); +}); diff --git a/test/js/node/test/parallel/test-btoa-atob.js b/test/js/node/test/parallel/test-btoa-atob.js new file mode 100644 index 0000000000..abf05adeef --- /dev/null +++ b/test/js/node/test/parallel/test-btoa-atob.js @@ -0,0 +1,39 @@ +'use strict'; + +require('../common'); + +const { strictEqual, throws } = require('assert'); +const buffer = require('buffer'); + +// Exported on the global object +strictEqual(globalThis.atob, buffer.atob); +strictEqual(globalThis.btoa, buffer.btoa); + +// Throws type error on no argument passed +throws(() => buffer.atob(), /TypeError/); +throws(() => buffer.btoa(), /TypeError/); + +strictEqual(atob(' '), ''); +strictEqual(atob(' Y\fW\tJ\njZ A=\r= '), 'abcd'); + +strictEqual(atob(null), '\x9Eée'); +strictEqual(atob(NaN), '5£'); +strictEqual(atob(Infinity), '"wâ\x9E+r'); +strictEqual(atob(true), '¶»\x9E'); +strictEqual(atob(1234), '×mø'); +strictEqual(atob([]), ''); +strictEqual(atob({ toString: () => '' }), ''); +strictEqual(atob({ [Symbol.toPrimitive]: () => '' }), ''); + +throws(() => atob(Symbol()), /TypeError/); +[ + undefined, false, () => {}, {}, [1], + 0, 1, 0n, 1n, -Infinity, + 'a', 'a\n\n\n', '\ra\r\r', ' a ', '\t\t\ta', 'a\f\f\f', '\ta\r \n\f', +].forEach((value) => + // See #2 - https://html.spec.whatwg.org/multipage/webappapis.html#dom-atob + throws(() => atob(value), { + constructor: DOMException, + name: 'InvalidCharacterError', + code: 5, + })); diff --git a/test/js/node/test/parallel/test-buffer-ascii.js b/test/js/node/test/parallel/test-buffer-ascii.js new file mode 100644 index 0000000000..afedb7252c --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-ascii.js @@ -0,0 +1,46 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// ASCII conversion in node.js simply masks off the high bits, +// it doesn't do transliteration. +assert.strictEqual(Buffer.from('hérité').toString('ascii'), 'hC)ritC)'); + +// 71 characters, 78 bytes. The ’ character is a triple-byte sequence. +const input = 'C’est, graphiquement, la réunion d’un accent aigu ' + + 'et d’un accent grave.'; + +const expected = 'Cb\u0000\u0019est, graphiquement, la rC)union ' + + 'db\u0000\u0019un accent aigu et db\u0000\u0019un ' + + 'accent grave.'; + +const buf = Buffer.from(input); + +for (let i = 0; i < expected.length; ++i) { + assert.strictEqual(buf.slice(i).toString('ascii'), expected.slice(i)); + + // Skip remainder of multi-byte sequence. + if (input.charCodeAt(i) > 65535) ++i; + if (input.charCodeAt(i) > 127) ++i; +} diff --git a/test/js/node/test/parallel/test-buffer-compare-offset.js b/test/js/node/test/parallel/test-buffer-compare-offset.js new file mode 100644 index 0000000000..9f6f733547 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-compare-offset.js @@ -0,0 +1,94 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const a = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); +const b = Buffer.from([5, 6, 7, 8, 9, 0, 1, 2, 3, 4]); + +assert.strictEqual(a.compare(b), -1); + +// Equivalent to a.compare(b). +assert.strictEqual(a.compare(b, 0), -1); +assert.throws(() => a.compare(b, '0'), { code: 'ERR_INVALID_ARG_TYPE' }); +assert.strictEqual(a.compare(b, undefined), -1); + +// Equivalent to a.compare(b). +assert.strictEqual(a.compare(b, 0, undefined, 0), -1); + +// Zero-length target, return 1 +assert.strictEqual(a.compare(b, 0, 0, 0), 1); +assert.throws( + () => a.compare(b, 0, '0', '0'), + { code: 'ERR_INVALID_ARG_TYPE' } +); + +// Equivalent to Buffer.compare(a, b.slice(6, 10)) +assert.strictEqual(a.compare(b, 6, 10), 1); + +// Zero-length source, return -1 +assert.strictEqual(a.compare(b, 6, 10, 0, 0), -1); + +// Zero-length source and target, return 0 +assert.strictEqual(a.compare(b, 0, 0, 0, 0), 0); +assert.strictEqual(a.compare(b, 1, 1, 2, 2), 0); + +// Equivalent to Buffer.compare(a.slice(4), b.slice(0, 5)) +assert.strictEqual(a.compare(b, 0, 5, 4), 1); + +// Equivalent to Buffer.compare(a.slice(1), b.slice(5)) +assert.strictEqual(a.compare(b, 5, undefined, 1), 1); + +// Equivalent to Buffer.compare(a.slice(2), b.slice(2, 4)) +assert.strictEqual(a.compare(b, 2, 4, 2), -1); + +// Equivalent to Buffer.compare(a.slice(4), b.slice(0, 7)) +assert.strictEqual(a.compare(b, 0, 7, 4), -1); + +// Equivalent to Buffer.compare(a.slice(4, 6), b.slice(0, 7)); +assert.strictEqual(a.compare(b, 0, 7, 4, 6), -1); + +// Null is ambiguous. +assert.throws( + () => a.compare(b, 0, null), + { code: 'ERR_INVALID_ARG_TYPE' } +); + +// Values do not get coerced. +assert.throws( + () => a.compare(b, 0, { valueOf: () => 5 }), + { code: 'ERR_INVALID_ARG_TYPE' } +); + +// Infinity should not be coerced. +assert.throws( + () => a.compare(b, Infinity, -Infinity), + { code: 'ERR_OUT_OF_RANGE' } +); + +// Zero length target because default for targetEnd <= targetSource +assert.strictEqual(a.compare(b, 0xff), 1); + +assert.throws( + () => a.compare(b, '0xff'), + { code: 'ERR_INVALID_ARG_TYPE' } +); +assert.throws( + () => a.compare(b, 0, '0xff'), + { code: 'ERR_INVALID_ARG_TYPE' } +); + +const oor = { code: 'ERR_OUT_OF_RANGE' }; + +assert.throws(() => a.compare(b, 0, 100, 0), oor); +assert.throws(() => a.compare(b, 0, 1, 0, 100), oor); +assert.throws(() => a.compare(b, -1), oor); +assert.throws(() => a.compare(b, 0, Infinity), oor); +assert.throws(() => a.compare(b, 0, 1, -1), oor); +assert.throws(() => a.compare(b, -Infinity, Infinity), oor); +assert.throws(() => a.compare(), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "target" argument must be an instance of ' + + 'Buffer or Uint8Array. Received undefined' +}); diff --git a/test/js/node/test/parallel/test-buffer-failed-alloc-typed-arrays.js b/test/js/node/test/parallel/test-buffer-failed-alloc-typed-arrays.js new file mode 100644 index 0000000000..699475ad0a --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-failed-alloc-typed-arrays.js @@ -0,0 +1,33 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const SlowBuffer = require('buffer').SlowBuffer; + +// Test failed or zero-sized Buffer allocations not affecting typed arrays. +// This test exists because of a regression that occurred. Because Buffer +// instances are allocated with the same underlying allocator as TypedArrays, +// but Buffer's can optional be non-zero filled, there was a regression that +// occurred when a Buffer allocated failed, the internal flag specifying +// whether or not to zero-fill was not being reset, causing TypedArrays to +// allocate incorrectly. +const zeroArray = new Uint32Array(10).fill(0); +const sizes = [1e20, 0, 0.1, -1, 'a', undefined, null, NaN]; +const allocators = [ + Buffer, + SlowBuffer, + Buffer.alloc, + Buffer.allocUnsafe, + Buffer.allocUnsafeSlow, +]; +for (const allocator of allocators) { + for (const size of sizes) { + try { + // Some of these allocations are known to fail. If they do, + // Uint32Array should still produce a zeroed out result. + allocator(size); + } catch { + assert.deepStrictEqual(zeroArray, new Uint32Array(10)); + } + } +} diff --git a/test/js/node/test/parallel/test-buffer-fakes.js b/test/js/node/test/parallel/test-buffer-fakes.js new file mode 100644 index 0000000000..da78fe0895 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-fakes.js @@ -0,0 +1,54 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +function FakeBuffer() { } +Object.setPrototypeOf(FakeBuffer, Buffer); +Object.setPrototypeOf(FakeBuffer.prototype, Buffer.prototype); + +const fb = new FakeBuffer(); + +assert.throws(function() { + Buffer.from(fb); +}, TypeError); + +assert.throws(function() { + +Buffer.prototype; // eslint-disable-line no-unused-expressions +}, TypeError); + +assert.throws(function() { + Buffer.compare(fb, Buffer.alloc(0)); +}, TypeError); + +assert.throws(function() { + fb.write('foo'); +}, TypeError); + +assert.throws(function() { + Buffer.concat([fb, fb]); +}, TypeError); + +assert.throws(function() { + fb.toString(); +}, TypeError); + +assert.throws(function() { + fb.equals(Buffer.alloc(0)); +}, TypeError); + +assert.throws(function() { + fb.indexOf(5); +}, TypeError); + +assert.throws(function() { + fb.readFloatLE(0); +}, TypeError); + +assert.throws(function() { + fb.writeFloatLE(0); +}, TypeError); + +assert.throws(function() { + fb.fill(0); +}, TypeError); diff --git a/test/js/node/test/parallel/test-buffer-inheritance.js b/test/js/node/test/parallel/test-buffer-inheritance.js new file mode 100644 index 0000000000..4794f56717 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-inheritance.js @@ -0,0 +1,39 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + + +function T(n) { + const ui8 = new Uint8Array(n); + Object.setPrototypeOf(ui8, T.prototype); + return ui8; +} +Object.setPrototypeOf(T.prototype, Buffer.prototype); +Object.setPrototypeOf(T, Buffer); + +T.prototype.sum = function sum() { + let cntr = 0; + for (let i = 0; i < this.length; i++) + cntr += this[i]; + return cntr; +}; + + +const vals = [new T(4), T(4)]; + +vals.forEach(function(t) { + assert.strictEqual(t.constructor, T); + assert.strictEqual(Object.getPrototypeOf(t), T.prototype); + assert.strictEqual(Object.getPrototypeOf(Object.getPrototypeOf(t)), + Buffer.prototype); + + t.fill(5); + let cntr = 0; + for (let i = 0; i < t.length; i++) + cntr += t[i]; + assert.strictEqual(cntr, t.length * 5); + + // Check this does not throw + t.toString(); +}); diff --git a/test/js/node/test/parallel/test-buffer-isascii.js b/test/js/node/test/parallel/test-buffer-isascii.js new file mode 100644 index 0000000000..b9468ca133 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-isascii.js @@ -0,0 +1,42 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const { isAscii, Buffer } = require('buffer'); +const { TextEncoder } = require('util'); + +const encoder = new TextEncoder(); + +assert.strictEqual(isAscii(encoder.encode('hello')), true); +assert.strictEqual(isAscii(encoder.encode('ğ')), false); +assert.strictEqual(isAscii(Buffer.from([])), true); + +[ + undefined, + '', 'hello', + false, true, + 0, 1, + 0n, 1n, + Symbol(), + () => {}, + {}, [], null, +].forEach((input) => { + assert.throws( + () => { isAscii(input); }, + { + code: 'ERR_INVALID_ARG_TYPE', + }, + ); +}); + +{ + // Test with detached array buffers + const arrayBuffer = new ArrayBuffer(1024); + structuredClone(arrayBuffer, { transfer: [arrayBuffer] }); + assert.throws( + () => { isAscii(arrayBuffer); }, + { + code: 'ERR_INVALID_STATE' + } + ); +} diff --git a/test/js/node/test/parallel/test-buffer-isencoding.js b/test/js/node/test/parallel/test-buffer-isencoding.js new file mode 100644 index 0000000000..f9150055cc --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-isencoding.js @@ -0,0 +1,38 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +[ + 'hex', + 'utf8', + 'utf-8', + 'ascii', + 'latin1', + 'binary', + 'base64', + 'base64url', + 'ucs2', + 'ucs-2', + 'utf16le', + 'utf-16le', +].forEach((enc) => { + assert.strictEqual(Buffer.isEncoding(enc), true); +}); + +[ + 'utf9', + 'utf-7', + 'Unicode-FTW', + 'new gnu gun', + false, + NaN, + {}, + Infinity, + [], + 1, + 0, + -1, +].forEach((enc) => { + assert.strictEqual(Buffer.isEncoding(enc), false); +}); diff --git a/test/js/node/test/parallel/test-buffer-isutf8.js b/test/js/node/test/parallel/test-buffer-isutf8.js new file mode 100644 index 0000000000..204db3e6a5 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-isutf8.js @@ -0,0 +1,86 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const { isUtf8, Buffer } = require('buffer'); +const { TextEncoder } = require('util'); + +const encoder = new TextEncoder(); + +assert.strictEqual(isUtf8(encoder.encode('hello')), true); +assert.strictEqual(isUtf8(encoder.encode('ğ')), true); +assert.strictEqual(isUtf8(Buffer.from([])), true); + +// Taken from test/fixtures/wpt/encoding/textdecoder-fatal.any.js +[ + [0xFF], // 'invalid code' + [0xC0], // 'ends early' + [0xE0], // 'ends early 2' + [0xC0, 0x00], // 'invalid trail' + [0xC0, 0xC0], // 'invalid trail 2' + [0xE0, 0x00], // 'invalid trail 3' + [0xE0, 0xC0], // 'invalid trail 4' + [0xE0, 0x80, 0x00], // 'invalid trail 5' + [0xE0, 0x80, 0xC0], // 'invalid trail 6' + [0xFC, 0x80, 0x80, 0x80, 0x80, 0x80], // '> 0x10FFFF' + [0xFE, 0x80, 0x80, 0x80, 0x80, 0x80], // 'obsolete lead byte' + + // Overlong encodings + [0xC0, 0x80], // 'overlong U+0000 - 2 bytes' + [0xE0, 0x80, 0x80], // 'overlong U+0000 - 3 bytes' + [0xF0, 0x80, 0x80, 0x80], // 'overlong U+0000 - 4 bytes' + [0xF8, 0x80, 0x80, 0x80, 0x80], // 'overlong U+0000 - 5 bytes' + [0xFC, 0x80, 0x80, 0x80, 0x80, 0x80], // 'overlong U+0000 - 6 bytes' + + [0xC1, 0xBF], // 'overlong U+007F - 2 bytes' + [0xE0, 0x81, 0xBF], // 'overlong U+007F - 3 bytes' + [0xF0, 0x80, 0x81, 0xBF], // 'overlong U+007F - 4 bytes' + [0xF8, 0x80, 0x80, 0x81, 0xBF], // 'overlong U+007F - 5 bytes' + [0xFC, 0x80, 0x80, 0x80, 0x81, 0xBF], // 'overlong U+007F - 6 bytes' + + [0xE0, 0x9F, 0xBF], // 'overlong U+07FF - 3 bytes' + [0xF0, 0x80, 0x9F, 0xBF], // 'overlong U+07FF - 4 bytes' + [0xF8, 0x80, 0x80, 0x9F, 0xBF], // 'overlong U+07FF - 5 bytes' + [0xFC, 0x80, 0x80, 0x80, 0x9F, 0xBF], // 'overlong U+07FF - 6 bytes' + + [0xF0, 0x8F, 0xBF, 0xBF], // 'overlong U+FFFF - 4 bytes' + [0xF8, 0x80, 0x8F, 0xBF, 0xBF], // 'overlong U+FFFF - 5 bytes' + [0xFC, 0x80, 0x80, 0x8F, 0xBF, 0xBF], // 'overlong U+FFFF - 6 bytes' + + [0xF8, 0x84, 0x8F, 0xBF, 0xBF], // 'overlong U+10FFFF - 5 bytes' + [0xFC, 0x80, 0x84, 0x8F, 0xBF, 0xBF], // 'overlong U+10FFFF - 6 bytes' + + // UTF-16 surrogates encoded as code points in UTF-8 + [0xED, 0xA0, 0x80], // 'lead surrogate' + [0xED, 0xB0, 0x80], // 'trail surrogate' + [0xED, 0xA0, 0x80, 0xED, 0xB0, 0x80], // 'surrogate pair' +].forEach((input) => { + assert.strictEqual(isUtf8(Buffer.from(input)), false); +}); + +[ + null, + undefined, + 'hello', + true, + false, +].forEach((input) => { + assert.throws( + () => { isUtf8(input); }, + { + code: 'ERR_INVALID_ARG_TYPE', + }, + ); +}); + +{ + // Test with detached array buffers + const arrayBuffer = new ArrayBuffer(1024); + structuredClone(arrayBuffer, { transfer: [arrayBuffer] }); + assert.throws( + () => { isUtf8(arrayBuffer); }, + { + code: 'ERR_INVALID_STATE' + } + ); +} diff --git a/test/js/node/test/parallel/test-buffer-iterator.js b/test/js/node/test/parallel/test-buffer-iterator.js new file mode 100644 index 0000000000..6cf64712c0 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-iterator.js @@ -0,0 +1,62 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +const buffer = Buffer.from([1, 2, 3, 4, 5]); +let arr; +let b; + +// Buffers should be iterable + +arr = []; + +for (b of buffer) + arr.push(b); + +assert.deepStrictEqual(arr, [1, 2, 3, 4, 5]); + + +// Buffer iterators should be iterable + +arr = []; + +for (b of buffer[Symbol.iterator]()) + arr.push(b); + +assert.deepStrictEqual(arr, [1, 2, 3, 4, 5]); + + +// buffer#values() should return iterator for values + +arr = []; + +for (b of buffer.values()) + arr.push(b); + +assert.deepStrictEqual(arr, [1, 2, 3, 4, 5]); + + +// buffer#keys() should return iterator for keys + +arr = []; + +for (b of buffer.keys()) + arr.push(b); + +assert.deepStrictEqual(arr, [0, 1, 2, 3, 4]); + + +// buffer#entries() should return iterator for entries + +arr = []; + +for (b of buffer.entries()) + arr.push(b); + +assert.deepStrictEqual(arr, [ + [0, 1], + [1, 2], + [2, 3], + [3, 4], + [4, 5], +]); diff --git a/test/js/node/test/parallel/test-buffer-no-negative-allocation.js b/test/js/node/test/parallel/test-buffer-no-negative-allocation.js new file mode 100644 index 0000000000..055e2d5dc6 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-no-negative-allocation.js @@ -0,0 +1,37 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const { SlowBuffer } = require('buffer'); + +const msg = { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', +}; + +// Test that negative Buffer length inputs throw errors. + +assert.throws(() => Buffer(-Buffer.poolSize), msg); +assert.throws(() => Buffer(-100), msg); +assert.throws(() => Buffer(-1), msg); +assert.throws(() => Buffer(NaN), msg); + +assert.throws(() => Buffer.alloc(-Buffer.poolSize), msg); +assert.throws(() => Buffer.alloc(-100), msg); +assert.throws(() => Buffer.alloc(-1), msg); +assert.throws(() => Buffer.alloc(NaN), msg); + +assert.throws(() => Buffer.allocUnsafe(-Buffer.poolSize), msg); +assert.throws(() => Buffer.allocUnsafe(-100), msg); +assert.throws(() => Buffer.allocUnsafe(-1), msg); +assert.throws(() => Buffer.allocUnsafe(NaN), msg); + +assert.throws(() => Buffer.allocUnsafeSlow(-Buffer.poolSize), msg); +assert.throws(() => Buffer.allocUnsafeSlow(-100), msg); +assert.throws(() => Buffer.allocUnsafeSlow(-1), msg); +assert.throws(() => Buffer.allocUnsafeSlow(NaN), msg); + +assert.throws(() => SlowBuffer(-Buffer.poolSize), msg); +assert.throws(() => SlowBuffer(-100), msg); +assert.throws(() => SlowBuffer(-1), msg); +assert.throws(() => SlowBuffer(NaN), msg); diff --git a/test/js/node/test/parallel/test-buffer-nopendingdep-map.js b/test/js/node/test/parallel/test-buffer-nopendingdep-map.js new file mode 100644 index 0000000000..c85d184fbc --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-nopendingdep-map.js @@ -0,0 +1,13 @@ +// Flags: --no-warnings --pending-deprecation +'use strict'; + +const common = require('../common'); + +process.on('warning', common.mustNotCall('A warning should not be emitted')); + +// With the --pending-deprecation flag, the deprecation warning for +// new Buffer() should not be emitted when Uint8Array methods are called. + +Buffer.from('abc').map((i) => i); +Buffer.from('abc').filter((i) => i); +Buffer.from('abc').slice(1, 2); diff --git a/test/js/node/test/parallel/test-buffer-of-no-deprecation.js b/test/js/node/test/parallel/test-buffer-of-no-deprecation.js new file mode 100644 index 0000000000..d0ac75b4a3 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-of-no-deprecation.js @@ -0,0 +1,7 @@ +'use strict'; + +const common = require('../common'); + +process.on('warning', common.mustNotCall()); + +Buffer.of(0, 1); diff --git a/test/js/node/test/parallel/test-buffer-over-max-length.js b/test/js/node/test/parallel/test-buffer-over-max-length.js new file mode 100644 index 0000000000..f29d6b62d4 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-over-max-length.js @@ -0,0 +1,19 @@ +'use strict'; +require('../common'); + +const assert = require('assert'); + +const buffer = require('buffer'); +const SlowBuffer = buffer.SlowBuffer; + +const kMaxLength = buffer.kMaxLength; +const bufferMaxSizeMsg = { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', +}; + +assert.throws(() => Buffer(kMaxLength + 1), bufferMaxSizeMsg); +assert.throws(() => SlowBuffer(kMaxLength + 1), bufferMaxSizeMsg); +assert.throws(() => Buffer.alloc(kMaxLength + 1), bufferMaxSizeMsg); +assert.throws(() => Buffer.allocUnsafe(kMaxLength + 1), bufferMaxSizeMsg); +assert.throws(() => Buffer.allocUnsafeSlow(kMaxLength + 1), bufferMaxSizeMsg); diff --git a/test/js/node/test/parallel/test-buffer-parent-property.js b/test/js/node/test/parallel/test-buffer-parent-property.js new file mode 100644 index 0000000000..24cdaade43 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-parent-property.js @@ -0,0 +1,21 @@ +'use strict'; + +// Fix for https://github.com/nodejs/node/issues/8266 +// +// Zero length Buffer objects should expose the `buffer` property of the +// TypedArrays, via the `parent` property. +require('../common'); +const assert = require('assert'); + +// If the length of the buffer object is zero +assert((new Buffer(0)).parent instanceof ArrayBuffer); + +// If the length of the buffer object is equal to the underlying ArrayBuffer +assert((new Buffer(Buffer.poolSize)).parent instanceof ArrayBuffer); + +// Same as the previous test, but with user created buffer +const arrayBuffer = new ArrayBuffer(0); +assert.strictEqual(new Buffer(arrayBuffer).parent, arrayBuffer); +assert.strictEqual(new Buffer(arrayBuffer).buffer, arrayBuffer); +assert.strictEqual(Buffer.from(arrayBuffer).parent, arrayBuffer); +assert.strictEqual(Buffer.from(arrayBuffer).buffer, arrayBuffer); diff --git a/test/js/node/test/parallel/test-buffer-safe-unsafe.js b/test/js/node/test/parallel/test-buffer-safe-unsafe.js new file mode 100644 index 0000000000..9f8b6b7410 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-safe-unsafe.js @@ -0,0 +1,24 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const safe = Buffer.alloc(10); + +function isZeroFilled(buf) { + for (let n = 0; n < buf.length; n++) + if (buf[n] !== 0) return false; + return true; +} + +assert(isZeroFilled(safe)); + +// Test that unsafe allocations doesn't affect subsequent safe allocations +Buffer.allocUnsafe(10); +assert(isZeroFilled(new Float64Array(10))); + +new Buffer(10); +assert(isZeroFilled(new Float64Array(10))); + +Buffer.allocUnsafe(10); +assert(isZeroFilled(Buffer.alloc(10))); diff --git a/test/js/node/test/parallel/test-buffer-set-inspect-max-bytes.js b/test/js/node/test/parallel/test-buffer-set-inspect-max-bytes.js new file mode 100644 index 0000000000..975c828111 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-set-inspect-max-bytes.js @@ -0,0 +1,34 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const buffer = require('buffer'); + +const rangeErrorObjs = [NaN, -1]; +const typeErrorObj = 'and even this'; + +for (const obj of rangeErrorObjs) { + assert.throws( + () => buffer.INSPECT_MAX_BYTES = obj, + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + } + ); + + assert.throws( + () => buffer.INSPECT_MAX_BYTES = obj, + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + } + ); +} + +assert.throws( + () => buffer.INSPECT_MAX_BYTES = typeErrorObj, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + } +); diff --git a/test/js/node/test/parallel/test-buffer-slice.js b/test/js/node/test/parallel/test-buffer-slice.js new file mode 100644 index 0000000000..52720bb87b --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-slice.js @@ -0,0 +1,129 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(Buffer.from('hello', 'utf8').slice(0, 0).length, 0); +assert.strictEqual(Buffer('hello', 'utf8').slice(0, 0).length, 0); + +const buf = Buffer.from('0123456789', 'utf8'); +const expectedSameBufs = [ + [buf.slice(-10, 10), Buffer.from('0123456789', 'utf8')], + [buf.slice(-20, 10), Buffer.from('0123456789', 'utf8')], + [buf.slice(-20, -10), Buffer.from('', 'utf8')], + [buf.slice(), Buffer.from('0123456789', 'utf8')], + [buf.slice(0), Buffer.from('0123456789', 'utf8')], + [buf.slice(0, 0), Buffer.from('', 'utf8')], + [buf.slice(undefined), Buffer.from('0123456789', 'utf8')], + [buf.slice('foobar'), Buffer.from('0123456789', 'utf8')], + [buf.slice(undefined, undefined), Buffer.from('0123456789', 'utf8')], + [buf.slice(2), Buffer.from('23456789', 'utf8')], + [buf.slice(5), Buffer.from('56789', 'utf8')], + [buf.slice(10), Buffer.from('', 'utf8')], + [buf.slice(5, 8), Buffer.from('567', 'utf8')], + [buf.slice(8, -1), Buffer.from('8', 'utf8')], + [buf.slice(-10), Buffer.from('0123456789', 'utf8')], + [buf.slice(0, -9), Buffer.from('0', 'utf8')], + [buf.slice(0, -10), Buffer.from('', 'utf8')], + [buf.slice(0, -1), Buffer.from('012345678', 'utf8')], + [buf.slice(2, -2), Buffer.from('234567', 'utf8')], + [buf.slice(0, 65536), Buffer.from('0123456789', 'utf8')], + [buf.slice(65536, 0), Buffer.from('', 'utf8')], + [buf.slice(-5, -8), Buffer.from('', 'utf8')], + [buf.slice(-5, -3), Buffer.from('56', 'utf8')], + [buf.slice(-10, 10), Buffer.from('0123456789', 'utf8')], + [buf.slice('0', '1'), Buffer.from('0', 'utf8')], + [buf.slice('-5', '10'), Buffer.from('56789', 'utf8')], + [buf.slice('-10', '10'), Buffer.from('0123456789', 'utf8')], + [buf.slice('-10', '-5'), Buffer.from('01234', 'utf8')], + [buf.slice('-10', '-0'), Buffer.from('', 'utf8')], + [buf.slice('111'), Buffer.from('', 'utf8')], + [buf.slice('0', '-111'), Buffer.from('', 'utf8')], +]; + +for (let i = 0, s = buf.toString(); i < buf.length; ++i) { + expectedSameBufs.push( + [buf.slice(i), Buffer.from(s.slice(i))], + [buf.slice(0, i), Buffer.from(s.slice(0, i))], + [buf.slice(-i), Buffer.from(s.slice(-i))], + [buf.slice(0, -i), Buffer.from(s.slice(0, -i))] + ); +} + +for (const [buf1, buf2] of expectedSameBufs) { + assert.strictEqual(Buffer.compare(buf1, buf2), 0); +} + +const utf16Buf = Buffer.from('0123456789', 'utf16le'); +assert.deepStrictEqual(utf16Buf.slice(0, 6), Buffer.from('012', 'utf16le')); +// Try to slice a zero length Buffer. +// See https://github.com/joyent/node/issues/5881 +assert.strictEqual(Buffer.alloc(0).slice(0, 1).length, 0); + +{ + // Single argument slice + assert.strictEqual(Buffer.from('abcde', 'utf8').slice(1).toString('utf8'), + 'bcde'); +} + +// slice(0,0).length === 0 +assert.strictEqual(Buffer.from('hello', 'utf8').slice(0, 0).length, 0); + +{ + // Regression tests for https://github.com/nodejs/node/issues/9096 + const buf = Buffer.from('abcd', 'utf8'); + assert.strictEqual(buf.slice(buf.length / 3).toString('utf8'), 'bcd'); + assert.strictEqual( + buf.slice(buf.length / 3, buf.length).toString(), + 'bcd' + ); +} + +{ + const buf = Buffer.from('abcdefg', 'utf8'); + assert.strictEqual(buf.slice(-(-1 >>> 0) - 1).toString('utf8'), + buf.toString('utf8')); +} + +{ + const buf = Buffer.from('abc', 'utf8'); + assert.strictEqual(buf.slice(-0.5).toString('utf8'), buf.toString('utf8')); +} + +{ + const buf = Buffer.from([ + 1, 29, 0, 0, 1, 143, 216, 162, 92, 254, 248, 63, 0, + 0, 0, 18, 184, 6, 0, 175, 29, 0, 8, 11, 1, 0, 0, + ]); + const chunk1 = Buffer.from([ + 1, 29, 0, 0, 1, 143, 216, 162, 92, 254, 248, 63, 0, + ]); + const chunk2 = Buffer.from([ + 0, 0, 18, 184, 6, 0, 175, 29, 0, 8, 11, 1, 0, 0, + ]); + const middle = buf.length / 2; + + assert.deepStrictEqual(buf.slice(0, middle), chunk1); + assert.deepStrictEqual(buf.slice(middle), chunk2); +} diff --git a/test/js/node/test/parallel/test-buffer-slow.js b/test/js/node/test/parallel/test-buffer-slow.js new file mode 100644 index 0000000000..07138d5db0 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-slow.js @@ -0,0 +1,53 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const buffer = require('buffer'); +const SlowBuffer = buffer.SlowBuffer; + +const ones = [1, 1, 1, 1]; + +// Should create a Buffer +let sb = SlowBuffer(4); +assert(sb instanceof Buffer); +assert.strictEqual(sb.length, 4); +sb.fill(1); +for (const [key, value] of sb.entries()) { + assert.deepStrictEqual(value, ones[key]); +} + +// underlying ArrayBuffer should have the same length +assert.strictEqual(sb.buffer.byteLength, 4); + +// Should work without new +sb = SlowBuffer(4); +assert(sb instanceof Buffer); +assert.strictEqual(sb.length, 4); +sb.fill(1); +for (const [key, value] of sb.entries()) { + assert.deepStrictEqual(value, ones[key]); +} + +// Should work with edge cases +assert.strictEqual(SlowBuffer(0).length, 0); + +// Should throw with invalid length type +const bufferInvalidTypeMsg = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /^The "size" argument must be of type number/, +}; +assert.throws(() => SlowBuffer(), bufferInvalidTypeMsg); +assert.throws(() => SlowBuffer({}), bufferInvalidTypeMsg); +assert.throws(() => SlowBuffer('6'), bufferInvalidTypeMsg); +assert.throws(() => SlowBuffer(true), bufferInvalidTypeMsg); + +// Should throw with invalid length value +const bufferMaxSizeMsg = { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', +}; +assert.throws(() => SlowBuffer(NaN), bufferMaxSizeMsg); +assert.throws(() => SlowBuffer(Infinity), bufferMaxSizeMsg); +assert.throws(() => SlowBuffer(-1), bufferMaxSizeMsg); +assert.throws(() => SlowBuffer(buffer.kMaxLength + 1), bufferMaxSizeMsg); diff --git a/test/js/node/test/parallel/test-buffer-swap.js b/test/js/node/test/parallel/test-buffer-swap.js new file mode 100644 index 0000000000..82b550790e --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-swap.js @@ -0,0 +1,152 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Test buffers small enough to use the JS implementation +{ + const buf = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10]); + + assert.strictEqual(buf, buf.swap16()); + assert.deepStrictEqual(buf, Buffer.from([0x02, 0x01, 0x04, 0x03, 0x06, 0x05, + 0x08, 0x07, 0x0a, 0x09, 0x0c, 0x0b, + 0x0e, 0x0d, 0x10, 0x0f])); + buf.swap16(); // restore + + assert.strictEqual(buf, buf.swap32()); + assert.deepStrictEqual(buf, Buffer.from([0x04, 0x03, 0x02, 0x01, 0x08, 0x07, + 0x06, 0x05, 0x0c, 0x0b, 0x0a, 0x09, + 0x10, 0x0f, 0x0e, 0x0d])); + buf.swap32(); // restore + + assert.strictEqual(buf, buf.swap64()); + assert.deepStrictEqual(buf, Buffer.from([0x08, 0x07, 0x06, 0x05, 0x04, 0x03, + 0x02, 0x01, 0x10, 0x0f, 0x0e, 0x0d, + 0x0c, 0x0b, 0x0a, 0x09])); +} + +// Operates in-place +{ + const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7]); + buf.slice(1, 5).swap32(); + assert.deepStrictEqual(buf, Buffer.from([0x1, 0x5, 0x4, 0x3, 0x2, 0x6, 0x7])); + buf.slice(1, 5).swap16(); + assert.deepStrictEqual(buf, Buffer.from([0x1, 0x4, 0x5, 0x2, 0x3, 0x6, 0x7])); + + // Length assertions + const re16 = /Buffer size must be a multiple of 16-bits/; + const re32 = /Buffer size must be a multiple of 32-bits/; + const re64 = /Buffer size must be a multiple of 64-bits/; + + assert.throws(() => Buffer.from(buf).swap16(), re16); + assert.throws(() => Buffer.alloc(1025).swap16(), re16); + assert.throws(() => Buffer.from(buf).swap32(), re32); + assert.throws(() => buf.slice(1, 3).swap32(), re32); + assert.throws(() => Buffer.alloc(1025).swap32(), re32); + assert.throws(() => buf.slice(1, 3).swap64(), re64); + assert.throws(() => Buffer.alloc(1025).swap64(), re64); +} + +{ + const buf = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10]); + + buf.slice(2, 18).swap64(); + + assert.deepStrictEqual(buf, Buffer.from([0x01, 0x02, 0x0a, 0x09, 0x08, 0x07, + 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, + 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10])); +} + +// Force use of native code (Buffer size above threshold limit for js impl) +{ + const bufData = new Uint32Array(256).fill(0x04030201); + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + const otherBufData = new Uint32Array(256).fill(0x03040102); + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + buf.swap16(); + assert.deepStrictEqual(buf, otherBuf); +} + +{ + const bufData = new Uint32Array(256).fill(0x04030201); + const buf = Buffer.from(bufData.buffer); + const otherBufData = new Uint32Array(256).fill(0x01020304); + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + buf.swap32(); + assert.deepStrictEqual(buf, otherBuf); +} + +{ + const bufData = new Uint8Array(256 * 8); + const otherBufData = new Uint8Array(256 * 8); + for (let i = 0; i < bufData.length; i++) { + bufData[i] = i % 8; + otherBufData[otherBufData.length - i - 1] = i % 8; + } + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + buf.swap64(); + assert.deepStrictEqual(buf, otherBuf); +} + +// Test native code with buffers that are not memory-aligned +{ + const bufData = new Uint8Array(256 * 8); + const otherBufData = new Uint8Array(256 * 8 - 2); + for (let i = 0; i < bufData.length; i++) { + bufData[i] = i % 2; + } + for (let i = 1; i < otherBufData.length; i++) { + otherBufData[otherBufData.length - i] = (i + 1) % 2; + } + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + // 0|1 0|1 0|1... + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + // 0|0 1|0 1|0... + + buf.slice(1, buf.length - 1).swap16(); + assert.deepStrictEqual(buf.slice(0, otherBuf.length), otherBuf); +} + +{ + const bufData = new Uint8Array(256 * 8); + const otherBufData = new Uint8Array(256 * 8 - 4); + for (let i = 0; i < bufData.length; i++) { + bufData[i] = i % 4; + } + for (let i = 1; i < otherBufData.length; i++) { + otherBufData[otherBufData.length - i] = (i + 1) % 4; + } + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + // 0|1 2 3 0|1 2 3... + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + // 0|0 3 2 1|0 3 2... + + buf.slice(1, buf.length - 3).swap32(); + assert.deepStrictEqual(buf.slice(0, otherBuf.length), otherBuf); +} + +{ + const bufData = new Uint8Array(256 * 8); + const otherBufData = new Uint8Array(256 * 8 - 8); + for (let i = 0; i < bufData.length; i++) { + bufData[i] = i % 8; + } + for (let i = 1; i < otherBufData.length; i++) { + otherBufData[otherBufData.length - i] = (i + 1) % 8; + } + const buf = Buffer.from(bufData.buffer, bufData.byteOffset); + // 0|1 2 3 4 5 6 7 0|1 2 3 4... + const otherBuf = Buffer.from(otherBufData.buffer, otherBufData.byteOffset); + // 0|0 7 6 5 4 3 2 1|0 7 6 5... + + buf.slice(1, buf.length - 7).swap64(); + assert.deepStrictEqual(buf.slice(0, otherBuf.length), otherBuf); +} diff --git a/test/js/node/test/parallel/test-buffer-tojson.js b/test/js/node/test/parallel/test-buffer-tojson.js new file mode 100644 index 0000000000..d9a4a85e81 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-tojson.js @@ -0,0 +1,35 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +{ + assert.strictEqual(JSON.stringify(Buffer.alloc(0)), + '{"type":"Buffer","data":[]}'); + assert.strictEqual(JSON.stringify(Buffer.from([1, 2, 3, 4])), + '{"type":"Buffer","data":[1,2,3,4]}'); +} + +// issue GH-7849 +{ + const buf = Buffer.from('test'); + const json = JSON.stringify(buf); + const obj = JSON.parse(json); + const copy = Buffer.from(obj); + + assert.deepStrictEqual(buf, copy); +} + +// GH-5110 +{ + const buffer = Buffer.from('test'); + const string = JSON.stringify(buffer); + + assert.strictEqual(string, '{"type":"Buffer","data":[116,101,115,116]}'); + + function receiver(key, value) { + return value && value.type === 'Buffer' ? Buffer.from(value.data) : value; + } + + assert.deepStrictEqual(buffer, JSON.parse(string, receiver)); +} diff --git a/test/js/node/test/parallel/test-buffer-tostring-range.js b/test/js/node/test/parallel/test-buffer-tostring-range.js new file mode 100644 index 0000000000..f4adf64c8d --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-tostring-range.js @@ -0,0 +1,100 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const rangeBuffer = Buffer.from('abc'); + +// If start >= buffer's length, empty string will be returned +assert.strictEqual(rangeBuffer.toString('ascii', 3), ''); +assert.strictEqual(rangeBuffer.toString('ascii', +Infinity), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 3.14, 3), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 'Infinity', 3), ''); + +// If end <= 0, empty string will be returned +assert.strictEqual(rangeBuffer.toString('ascii', 1, 0), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 1, -1.2), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 1, -100), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 1, -Infinity), ''); + +// If start < 0, start will be taken as zero +assert.strictEqual(rangeBuffer.toString('ascii', -1, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', -1.99, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', -Infinity, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '-1', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '-1.99', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '-Infinity', 3), 'abc'); + +// If start is an invalid integer, start will be taken as zero +assert.strictEqual(rangeBuffer.toString('ascii', 'node.js', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', {}, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', [], 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', NaN, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', null, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', undefined, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', false, 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '', 3), 'abc'); + +// But, if start is an integer when coerced, then it will be coerced and used. +assert.strictEqual(rangeBuffer.toString('ascii', '-1', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '1', 3), 'bc'); +assert.strictEqual(rangeBuffer.toString('ascii', '-Infinity', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', '3', 3), ''); +assert.strictEqual(rangeBuffer.toString('ascii', Number(3), 3), ''); +assert.strictEqual(rangeBuffer.toString('ascii', '3.14', 3), ''); +assert.strictEqual(rangeBuffer.toString('ascii', '1.99', 3), 'bc'); +assert.strictEqual(rangeBuffer.toString('ascii', '-1.99', 3), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 1.99, 3), 'bc'); +assert.strictEqual(rangeBuffer.toString('ascii', true, 3), 'bc'); + +// If end > buffer's length, end will be taken as buffer's length +assert.strictEqual(rangeBuffer.toString('ascii', 0, 5), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, 6.99), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, Infinity), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '5'), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '6.99'), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, 'Infinity'), 'abc'); + +// If end is an invalid integer, end will be taken as buffer's length +assert.strictEqual(rangeBuffer.toString('ascii', 0, 'node.js'), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, {}), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, NaN), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, undefined), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, null), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, []), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, false), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, ''), ''); + +// But, if end is an integer when coerced, then it will be coerced and used. +assert.strictEqual(rangeBuffer.toString('ascii', 0, '-1'), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '1'), 'a'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '-Infinity'), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '3'), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, Number(3)), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '3.14'), 'abc'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '1.99'), 'a'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, '-1.99'), ''); +assert.strictEqual(rangeBuffer.toString('ascii', 0, 1.99), 'a'); +assert.strictEqual(rangeBuffer.toString('ascii', 0, true), 'a'); + +// Try toString() with an object as an encoding +assert.strictEqual(rangeBuffer.toString({ toString: function() { + return 'ascii'; +} }), 'abc'); + +// Try toString() with 0 and null as the encoding +assert.throws(() => { + rangeBuffer.toString(0, 1, 2); +}, { + code: 'ERR_UNKNOWN_ENCODING', + name: 'TypeError', + message: 'Unknown encoding: 0' +}); +assert.throws(() => { + rangeBuffer.toString(null, 1, 2); +}, { + code: 'ERR_UNKNOWN_ENCODING', + name: 'TypeError', + message: 'Unknown encoding: null' +}); diff --git a/test/js/node/test/parallel/test-buffer-tostring-rangeerror.js b/test/js/node/test/parallel/test-buffer-tostring-rangeerror.js new file mode 100644 index 0000000000..0ebea759b5 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-tostring-rangeerror.js @@ -0,0 +1,25 @@ +'use strict'; +require('../common'); + +// This test ensures that Node.js throws an Error when trying to convert a +// large buffer into a string. +// Regression test for https://github.com/nodejs/node/issues/649. + +const assert = require('assert'); +const { + SlowBuffer, + constants: { + MAX_STRING_LENGTH, + }, +} = require('buffer'); + +const len = MAX_STRING_LENGTH + 1; +const message = { + code: 'ERR_STRING_TOO_LONG', + name: 'Error', +}; +assert.throws(() => Buffer(len).toString('utf8'), message); +assert.throws(() => SlowBuffer(len).toString('utf8'), message); +assert.throws(() => Buffer.alloc(len).toString('utf8'), message); +assert.throws(() => Buffer.allocUnsafe(len).toString('utf8'), message); +assert.throws(() => Buffer.allocUnsafeSlow(len).toString('utf8'), message); diff --git a/test/js/node/test/parallel/test-buffer-tostring.js b/test/js/node/test/parallel/test-buffer-tostring.js new file mode 100644 index 0000000000..4219649fa3 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-tostring.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +// utf8, ucs2, ascii, latin1, utf16le +const encodings = ['utf8', 'utf-8', 'ucs2', 'ucs-2', 'ascii', 'latin1', + 'binary', 'utf16le', 'utf-16le']; + +encodings + .reduce((es, e) => es.concat(e, e.toUpperCase()), []) + .forEach((encoding) => { + assert.strictEqual(Buffer.from('foo', encoding).toString(encoding), 'foo'); + }); + +// base64 +['base64', 'BASE64'].forEach((encoding) => { + assert.strictEqual(Buffer.from('Zm9v', encoding).toString(encoding), 'Zm9v'); +}); + +// hex +['hex', 'HEX'].forEach((encoding) => { + assert.strictEqual(Buffer.from('666f6f', encoding).toString(encoding), + '666f6f'); +}); + +// Invalid encodings +for (let i = 1; i < 10; i++) { + const encoding = String(i).repeat(i); + const error = common.expectsError({ + code: 'ERR_UNKNOWN_ENCODING', + name: 'TypeError', + message: `Unknown encoding: ${encoding}` + }); + assert.ok(!Buffer.isEncoding(encoding)); + assert.throws(() => Buffer.from('foo').toString(encoding), error); +} diff --git a/test/js/node/test/parallel/test-buffer-zero-fill-reset.js b/test/js/node/test/parallel/test-buffer-zero-fill-reset.js new file mode 100644 index 0000000000..334ee1b618 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-zero-fill-reset.js @@ -0,0 +1,19 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + + +function testUint8Array(ui) { + const length = ui.length; + for (let i = 0; i < length; i++) + if (ui[i] !== 0) return false; + return true; +} + + +for (let i = 0; i < 100; i++) { + Buffer.alloc(0); + const ui = new Uint8Array(65); + assert.ok(testUint8Array(ui), `Uint8Array is not zero-filled: ${ui}`); +} diff --git a/test/js/node/test/parallel/test-buffer-zero-fill.js b/test/js/node/test/parallel/test-buffer-zero-fill.js new file mode 100644 index 0000000000..7a9f0c1250 --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-zero-fill.js @@ -0,0 +1,14 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Tests deprecated Buffer API on purpose +const buf1 = Buffer(100); +const buf2 = new Buffer(100); + +for (let n = 0; n < buf1.length; n++) + assert.strictEqual(buf1[n], 0); + +for (let n = 0; n < buf2.length; n++) + assert.strictEqual(buf2[n], 0); diff --git a/test/js/node/test/parallel/test-child-process-can-write-to-stdout.js b/test/js/node/test/parallel/test-child-process-can-write-to-stdout.js new file mode 100644 index 0000000000..069e07fb16 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-can-write-to-stdout.js @@ -0,0 +1,22 @@ +'use strict'; +// Tests that a spawned child process can write to stdout without throwing. +// See https://github.com/nodejs/node-v0.x-archive/issues/1899. + +require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const spawn = require('child_process').spawn; + +const child = spawn(process.argv[0], [ + fixtures.path('GH-1899-output.js'), +]); +let output = ''; + +child.stdout.on('data', function(data) { + output += data; +}); + +child.on('exit', function(code, signal) { + assert.strictEqual(code, 0); + assert.strictEqual(output, 'hello, world!\n'); +}); diff --git a/test/js/node/test/parallel/test-child-process-default-options.js b/test/js/node/test/parallel/test-child-process-default-options.js new file mode 100644 index 0000000000..39f90deaeb --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-default-options.js @@ -0,0 +1,51 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const { isWindows } = require('../common'); +const assert = require('assert'); + +const spawn = require('child_process').spawn; +const debug = require('util').debuglog('test'); + +process.env.HELLO = 'WORLD'; + +let child; +if (isWindows) { + child = spawn('cmd.exe', ['/c', 'set'], {}); +} else { + child = spawn('/usr/bin/env', [], {}); +} + +let response = ''; + +child.stdout.setEncoding('utf8'); + +child.stdout.on('data', function(chunk) { + debug(`stdout: ${chunk}`); + response += chunk; +}); + +process.on('exit', function() { + assert.ok(response.includes('HELLO=WORLD'), + 'spawn did not use process.env as default ' + + `(process.env.HELLO = ${process.env.HELLO})`); +}); diff --git a/test/js/node/test/parallel/test-child-process-destroy.js b/test/js/node/test/parallel/test-child-process-destroy.js new file mode 100644 index 0000000000..50763bb031 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-destroy.js @@ -0,0 +1,25 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; +const cat = spawn(common.isWindows ? 'cmd' : 'cat'); + +cat.stdout.on('end', common.mustCall()); +cat.stderr.on('data', common.mustNotCall()); +cat.stderr.on('end', common.mustCall()); + +cat.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, null); + assert.strictEqual(signal, 'SIGTERM'); + assert.strictEqual(cat.signalCode, 'SIGTERM'); +})); +cat.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, null); + assert.strictEqual(signal, 'SIGTERM'); + assert.strictEqual(cat.signalCode, 'SIGTERM'); +})); + +assert.strictEqual(cat.signalCode, null); +assert.strictEqual(cat.killed, false); +cat[Symbol.dispose](); +assert.strictEqual(cat.killed, true); diff --git a/test/js/node/test/parallel/test-child-process-double-pipe.js b/test/js/node/test/parallel/test-child-process-double-pipe.js new file mode 100644 index 0000000000..7a432d3892 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-double-pipe.js @@ -0,0 +1,122 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const { + isWindows, + mustCall, + mustCallAtLeast, +} = require('../common'); +const assert = require('assert'); +const os = require('os'); +const spawn = require('child_process').spawn; +const debug = require('util').debuglog('test'); + +// We're trying to reproduce: +// $ echo "hello\nnode\nand\nworld" | grep o | sed s/o/a/ + +let grep, sed, echo; + +if (isWindows) { + grep = spawn('grep', ['--binary', 'o']); + sed = spawn('sed', ['--binary', 's/o/O/']); + echo = spawn('cmd.exe', + ['/c', 'echo', 'hello&&', 'echo', + 'node&&', 'echo', 'and&&', 'echo', 'world']); +} else { + grep = spawn('grep', ['o']); + sed = spawn('sed', ['s/o/O/']); + echo = spawn('echo', ['hello\nnode\nand\nworld\n']); +} + +// If the spawn function leaks file descriptors to subprocesses, grep and sed +// hang. +// This happens when calling pipe(2) and then forgetting to set the +// FD_CLOEXEC flag on the resulting file descriptors. +// +// This test checks child processes exit, meaning they don't hang like +// explained above. + + +// pipe echo | grep +echo.stdout.on('data', mustCallAtLeast((data) => { + debug(`grep stdin write ${data.length}`); + if (!grep.stdin.write(data)) { + echo.stdout.pause(); + } +})); + +// TODO(@jasnell): This does not appear to ever be +// emitted. It's not clear if it is necessary. +grep.stdin.on('drain', (data) => { + echo.stdout.resume(); +}); + +// Propagate end from echo to grep +echo.stdout.on('end', mustCall((code) => { + grep.stdin.end(); +})); + +echo.on('exit', mustCall(() => { + debug('echo exit'); +})); + +grep.on('exit', mustCall(() => { + debug('grep exit'); +})); + +sed.on('exit', mustCall(() => { + debug('sed exit'); +})); + + +// pipe grep | sed +grep.stdout.on('data', mustCallAtLeast((data) => { + debug(`grep stdout ${data.length}`); + if (!sed.stdin.write(data)) { + grep.stdout.pause(); + } +})); + +// TODO(@jasnell): This does not appear to ever be +// emitted. It's not clear if it is necessary. +sed.stdin.on('drain', (data) => { + grep.stdout.resume(); +}); + +// Propagate end from grep to sed +grep.stdout.on('end', mustCall((code) => { + debug('grep stdout end'); + sed.stdin.end(); +})); + + +let result = ''; + +// print sed's output +sed.stdout.on('data', mustCallAtLeast((data) => { + result += data.toString('utf8', 0, data.length); + debug(data); +})); + +sed.stdout.on('end', mustCall((code) => { + assert.strictEqual(result, `hellO${os.EOL}nOde${os.EOL}wOrld${os.EOL}`); +})); diff --git a/test/js/node/test/parallel/test-child-process-env.js b/test/js/node/test/parallel/test-child-process-env.js new file mode 100644 index 0000000000..f9815ff015 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-env.js @@ -0,0 +1,77 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const { + isWindows, + mustCall, + mustCallAtLeast, +} = require('../common'); +const assert = require('assert'); +const os = require('os'); +const debug = require('util').debuglog('test'); + +const spawn = require('child_process').spawn; + +const env = { + ...process.env, + 'HELLO': 'WORLD', + 'UNDEFINED': undefined, + 'NULL': null, + 'EMPTY': '', + 'duplicate': 'lowercase', + 'DUPLICATE': 'uppercase', +}; +Object.setPrototypeOf(env, { + 'FOO': 'BAR' +}); + +let child; +if (isWindows) { + child = spawn('cmd.exe', ['/c', 'set'], { env }); +} else { + child = spawn('/usr/bin/env', [], { env }); +} + + +let response = ''; + +child.stdout.setEncoding('utf8'); + +child.stdout.on('data', mustCallAtLeast((chunk) => { + debug(`stdout: ${chunk}`); + response += chunk; +})); + +child.stdout.on('end', mustCall(() => { + assert.ok(response.includes('HELLO=WORLD')); + assert.ok(response.includes('FOO=BAR')); + assert.ok(!response.includes('UNDEFINED=undefined')); + assert.ok(response.includes('NULL=null')); + assert.ok(response.includes(`EMPTY=${os.EOL}`)); + if (isWindows) { + assert.ok(response.includes('DUPLICATE=uppercase')); + assert.ok(!response.includes('duplicate=lowercase')); + } else { + assert.ok(response.includes('DUPLICATE=uppercase')); + assert.ok(response.includes('duplicate=lowercase')); + } +})); diff --git a/test/js/node/test/parallel/test-child-process-exec-any-shells-windows.js b/test/js/node/test/parallel/test-child-process-exec-any-shells-windows.js new file mode 100644 index 0000000000..5c34bc7730 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-any-shells-windows.js @@ -0,0 +1,67 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); + +// This test is only relevant on Windows. +if (!common.isWindows) + common.skip('Windows specific test.'); + +// This test ensures that child_process.exec can work with any shells. + +tmpdir.refresh(); +const tmpPath = `${tmpdir.path}\\path with spaces`; +fs.mkdirSync(tmpPath); + +const test = (shell) => { + cp.exec('echo foo bar', { shell: shell }, + common.mustSucceed((stdout, stderror) => { + assert.ok(!stderror); + assert.ok(stdout.includes('foo') && stdout.includes('bar')); + })); +}; +const testCopy = (shellName, shellPath) => { + // Symlink the executable to a path with spaces, to ensure there are no issues + // related to quoting of argv0 + const copyPath = `${tmpPath}\\${shellName}`; + fs.symlinkSync(shellPath, copyPath); + test(copyPath); +}; + +const system32 = `${process.env.SystemRoot}\\System32`; + +// Test CMD +test(true); +test('cmd'); +testCopy('cmd.exe', `${system32}\\cmd.exe`); +test('cmd.exe'); +test('CMD'); + +// Test PowerShell +test('powershell'); +testCopy('powershell.exe', + `${system32}\\WindowsPowerShell\\v1.0\\powershell.exe`); +fs.writeFile(`${tmpPath}\\test file`, 'Test', common.mustSucceed(() => { + cp.exec(`Get-ChildItem "${tmpPath}" | Select-Object -Property Name`, + { shell: 'PowerShell' }, + common.mustSucceed((stdout, stderror) => { + assert.ok(!stderror); + assert.ok(stdout.includes( + 'test file')); + })); +})); + +// Test Bash (from WSL and Git), if available +cp.exec('where bash', common.mustCall((error, stdout) => { + if (error) { + return; + } + const lines = stdout.trim().split(/[\r\n]+/g); + for (let i = 0; i < lines.length; ++i) { + const bashPath = lines[i].trim(); + test(bashPath); + testCopy(`bash_${i}.exe`, bashPath); + } +})); diff --git a/test/js/node/test/parallel/test-child-process-exec-cwd.js b/test/js/node/test/parallel/test-child-process-exec-cwd.js new file mode 100644 index 0000000000..49e56ef551 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-cwd.js @@ -0,0 +1,39 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const exec = require('child_process').exec; + +let pwdcommand, dir; + +if (common.isWindows) { + pwdcommand = 'echo %cd%'; + dir = 'c:\\windows'; +} else { + pwdcommand = 'pwd'; + dir = '/dev'; +} + +exec(pwdcommand, { cwd: dir }, common.mustSucceed((stdout, stderr) => { + assert(stdout.toLowerCase().startsWith(dir)); +})); diff --git a/test/js/node/test/parallel/test-child-process-exec-encoding.js b/test/js/node/test/parallel/test-child-process-exec-encoding.js new file mode 100644 index 0000000000..0c3178e3f2 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-encoding.js @@ -0,0 +1,48 @@ +'use strict'; +const common = require('../common'); +const stdoutData = 'foo'; +const stderrData = 'bar'; + +if (process.argv[2] === 'child') { + // The following console calls are part of the test. + console.log(stdoutData); + console.error(stderrData); +} else { + const assert = require('assert'); + const cp = require('child_process'); + const expectedStdout = `${stdoutData}\n`; + const expectedStderr = `${stderrData}\n`; + function run(options, callback) { + const cmd = `"${process.execPath}" "${__filename}" child`; + + cp.exec(cmd, options, common.mustSucceed((stdout, stderr) => { + callback(stdout, stderr); + })); + } + + // Test default encoding, which should be utf8. + run({}, (stdout, stderr) => { + assert.strictEqual(typeof stdout, 'string'); + assert.strictEqual(typeof stderr, 'string'); + assert.strictEqual(stdout, expectedStdout); + assert.strictEqual(stderr, expectedStderr); + }); + + // Test explicit utf8 encoding. + run({ encoding: 'utf8' }, (stdout, stderr) => { + assert.strictEqual(typeof stdout, 'string'); + assert.strictEqual(typeof stderr, 'string'); + assert.strictEqual(stdout, expectedStdout); + assert.strictEqual(stderr, expectedStderr); + }); + + // Test cases that result in buffer encodings. + [undefined, null, 'buffer', 'invalid'].forEach((encoding) => { + run({ encoding }, (stdout, stderr) => { + assert(stdout instanceof Buffer); + assert(stdout instanceof Buffer); + assert.strictEqual(stdout.toString(), expectedStdout); + assert.strictEqual(stderr.toString(), expectedStderr); + }); + }); +} diff --git a/test/js/node/test/parallel/test-child-process-exec-env.js b/test/js/node/test/parallel/test-child-process-exec-env.js new file mode 100644 index 0000000000..f515643619 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-env.js @@ -0,0 +1,64 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const { isWindows } = require('../common'); +const assert = require('assert'); +const exec = require('child_process').exec; +const debug = require('util').debuglog('test'); + +let success_count = 0; +let error_count = 0; +let response = ''; +let child; + +function after(err, stdout, stderr) { + if (err) { + error_count++; + debug(`error!: ${err.code}`); + debug(`stdout: ${JSON.stringify(stdout)}`); + debug(`stderr: ${JSON.stringify(stderr)}`); + assert.strictEqual(err.killed, false); + } else { + success_count++; + assert.notStrictEqual(stdout, ''); + } +} + +if (!isWindows) { + child = exec('/usr/bin/env', { env: { 'HELLO': 'WORLD' } }, after); +} else { + child = exec('set', + { env: { ...process.env, 'HELLO': 'WORLD' } }, + after); +} + +child.stdout.setEncoding('utf8'); +child.stdout.on('data', function(chunk) { + response += chunk; +}); + +process.on('exit', function() { + debug('response: ', response); + assert.strictEqual(success_count, 1); + assert.strictEqual(error_count, 0); + assert.ok(response.includes('HELLO=WORLD')); +}); diff --git a/test/js/node/test/parallel/test-child-process-exec-std-encoding.js b/test/js/node/test/parallel/test-child-process-exec-std-encoding.js new file mode 100644 index 0000000000..0818731672 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-std-encoding.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const stdoutData = 'foo'; +const stderrData = 'bar'; +const expectedStdout = `${stdoutData}\n`; +const expectedStderr = `${stderrData}\n`; + +if (process.argv[2] === 'child') { + // The following console calls are part of the test. + console.log(stdoutData); + console.error(stderrData); +} else { + const cmd = `"${process.execPath}" "${__filename}" child`; + const child = cp.exec(cmd, common.mustSucceed((stdout, stderr) => { + assert.strictEqual(stdout, expectedStdout); + assert.strictEqual(stderr, expectedStderr); + })); + child.stdout.setEncoding('utf-8'); + child.stderr.setEncoding('utf-8'); +} diff --git a/test/js/node/test/parallel/test-child-process-exec-stdout-stderr-data-string.js b/test/js/node/test/parallel/test-child-process-exec-stdout-stderr-data-string.js new file mode 100644 index 0000000000..1fbdfbf8e4 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-stdout-stderr-data-string.js @@ -0,0 +1,13 @@ +'use strict'; +// Refs: https://github.com/nodejs/node/issues/7342 +const common = require('../common'); +const assert = require('assert'); +const exec = require('child_process').exec; + +const command = common.isWindows ? 'dir' : 'ls'; + +exec(command).stdout.on('data', common.mustCallAtLeast()); + +exec('fhqwhgads').stderr.on('data', common.mustCallAtLeast((data) => { + assert.strictEqual(typeof data, 'string'); +})); diff --git a/test/js/node/test/parallel/test-child-process-exec-timeout-expire.js b/test/js/node/test/parallel/test-child-process-exec-timeout-expire.js new file mode 100644 index 0000000000..6b62d131cb --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-timeout-expire.js @@ -0,0 +1,50 @@ +'use strict'; + +// Test exec() with a timeout that expires. + +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +const { + cleanupStaleProcess, + logAfterTime, + kExpiringChildRunTime, + kExpiringParentTimer +} = require('../common/child_process'); + +if (process.argv[2] === 'child') { + logAfterTime(kExpiringChildRunTime); + return; +} + +const cmd = `"${process.execPath}" "${__filename}" child`; + +cp.exec(cmd, { + timeout: kExpiringParentTimer, +}, common.mustCall((err, stdout, stderr) => { + console.log('[stdout]', stdout.trim()); + console.log('[stderr]', stderr.trim()); + + let sigterm = 'SIGTERM'; + assert.strictEqual(err.killed, true); + // TODO OpenBSD returns a null signal and 143 for code + if (common.isOpenBSD) { + assert.strictEqual(err.code, 143); + sigterm = null; + } else { + assert.strictEqual(err.code, null); + } + // At least starting with Darwin Kernel Version 16.4.0, sending a SIGTERM to a + // process that is still starting up kills it with SIGKILL instead of SIGTERM. + // See: https://github.com/libuv/libuv/issues/1226 + if (common.isMacOS) + assert.ok(err.signal === 'SIGTERM' || err.signal === 'SIGKILL'); + else + assert.strictEqual(err.signal, sigterm); + assert.strictEqual(err.cmd, cmd); + assert.strictEqual(stdout.trim(), ''); + assert.strictEqual(stderr.trim(), ''); +})); + +cleanupStaleProcess(__filename); diff --git a/test/js/node/test/parallel/test-child-process-exec-timeout-kill.js b/test/js/node/test/parallel/test-child-process-exec-timeout-kill.js new file mode 100644 index 0000000000..845fd1eaec --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-timeout-kill.js @@ -0,0 +1,39 @@ +'use strict'; + +// Test exec() with both a timeout and a killSignal. + +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +const { + cleanupStaleProcess, + logAfterTime, + kExpiringChildRunTime, + kExpiringParentTimer, +} = require('../common/child_process'); + +if (process.argv[2] === 'child') { + logAfterTime(kExpiringChildRunTime); + return; +} + +const cmd = `"${process.execPath}" "${__filename}" child`; + +// Test with a different kill signal. +cp.exec(cmd, { + timeout: kExpiringParentTimer, + killSignal: 'SIGKILL' +}, common.mustCall((err, stdout, stderr) => { + console.log('[stdout]', stdout.trim()); + console.log('[stderr]', stderr.trim()); + + assert.strictEqual(err.killed, true); + assert.strictEqual(err.code, null); + assert.strictEqual(err.signal, 'SIGKILL'); + assert.strictEqual(err.cmd, cmd); + assert.strictEqual(stdout.trim(), ''); + assert.strictEqual(stderr.trim(), ''); +})); + +cleanupStaleProcess(__filename); diff --git a/test/js/node/test/parallel/test-child-process-exec-timeout-not-expired.js b/test/js/node/test/parallel/test-child-process-exec-timeout-not-expired.js new file mode 100644 index 0000000000..fb0af5fa8f --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-exec-timeout-not-expired.js @@ -0,0 +1,34 @@ +'use strict'; + +// Test exec() when a timeout is set, but not expired. + +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +const { + cleanupStaleProcess, + logAfterTime +} = require('../common/child_process'); + +const kTimeoutNotSupposedToExpire = 2 ** 30; +const childRunTime = common.platformTimeout(100); + +// The time spent in the child should be smaller than the timeout below. +assert(childRunTime < kTimeoutNotSupposedToExpire); + +if (process.argv[2] === 'child') { + logAfterTime(childRunTime); + return; +} + +const cmd = `"${process.execPath}" "${__filename}" child`; + +cp.exec(cmd, { + timeout: kTimeoutNotSupposedToExpire +}, common.mustSucceed((stdout, stderr) => { + assert.strictEqual(stdout.trim(), 'child stdout'); + assert.strictEqual(stderr.trim(), 'child stderr'); +})); + +cleanupStaleProcess(__filename); diff --git a/test/js/node/test/parallel/test-child-process-execFile-promisified-abortController.js b/test/js/node/test/parallel/test-child-process-execFile-promisified-abortController.js new file mode 100644 index 0000000000..38c177eb33 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-execFile-promisified-abortController.js @@ -0,0 +1,56 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { promisify } = require('util'); +const execFile = require('child_process').execFile; +const fixtures = require('../common/fixtures'); + +const echoFixture = fixtures.path('echo.js'); +const promisified = promisify(execFile); +const invalidArgTypeError = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' +}; + +{ + // Verify that the signal option works properly + const ac = new AbortController(); + const signal = ac.signal; + const promise = promisified(process.execPath, [echoFixture, 0], { signal }); + + ac.abort(); + + assert.rejects( + promise, + { name: 'AbortError' } + ).then(common.mustCall()); +} + +{ + // Verify that the signal option works properly when already aborted + const signal = AbortSignal.abort(); + + assert.rejects( + promisified(process.execPath, [echoFixture, 0], { signal }), + { name: 'AbortError' } + ).then(common.mustCall()); +} + +{ + // Verify that if something different than Abortcontroller.signal + // is passed, ERR_INVALID_ARG_TYPE is thrown + const signal = {}; + assert.throws(() => { + promisified(process.execPath, [echoFixture, 0], { signal }); + }, invalidArgTypeError); +} + +{ + // Verify that if something different than Abortcontroller.signal + // is passed, ERR_INVALID_ARG_TYPE is thrown + const signal = 'world!'; + assert.throws(() => { + promisified(process.execPath, [echoFixture, 0], { signal }); + }, invalidArgTypeError); +} diff --git a/test/js/node/test/parallel/test-child-process-flush-stdio.js b/test/js/node/test/parallel/test-child-process-flush-stdio.js new file mode 100644 index 0000000000..7b9a2a049c --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-flush-stdio.js @@ -0,0 +1,33 @@ +'use strict'; +const common = require('../common'); +const cp = require('child_process'); +const assert = require('assert'); + +// Windows' `echo` command is a built-in shell command and not an external +// executable like on *nix +const opts = { shell: common.isWindows }; + +const p = cp.spawn('echo', [], opts); + +p.on('close', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + spawnWithReadable(); +})); + +p.stdout.read(); + +const spawnWithReadable = () => { + const buffer = []; + const p = cp.spawn('echo', ['123'], opts); + p.on('close', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + assert.strictEqual(Buffer.concat(buffer).toString().trim(), '123'); + })); + p.stdout.on('readable', () => { + let buf; + while ((buf = p.stdout.read()) !== null) + buffer.push(buf); + }); +}; diff --git a/test/js/node/test/parallel/test-child-process-fork-abort-signal.js b/test/js/node/test/parallel/test-child-process-fork-abort-signal.js new file mode 100644 index 0000000000..b963306fb1 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork-abort-signal.js @@ -0,0 +1,105 @@ +'use strict'; + +const { mustCall, mustNotCall } = require('../common'); +const { strictEqual } = require('assert'); +const fixtures = require('../common/fixtures'); +const { fork } = require('child_process'); + +{ + // Test aborting a forked child_process after calling fork + const ac = new AbortController(); + const { signal } = ac; + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + signal + }); + cp.on('exit', mustCall((code, killSignal) => { + strictEqual(code, null); + strictEqual(killSignal, 'SIGTERM'); + })); + cp.on('error', mustCall((err) => { + strictEqual(err.name, 'AbortError'); + })); + process.nextTick(() => ac.abort()); +} + +{ + // Test aborting with custom error + const ac = new AbortController(); + const { signal } = ac; + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + signal + }); + cp.on('exit', mustCall((code, killSignal) => { + strictEqual(code, null); + strictEqual(killSignal, 'SIGTERM'); + })); + cp.on('error', mustCall((err) => { + strictEqual(err.name, 'AbortError'); + strictEqual(err.cause.name, 'Error'); + strictEqual(err.cause.message, 'boom'); + })); + process.nextTick(() => ac.abort(new Error('boom'))); +} + +{ + // Test passing an already aborted signal to a forked child_process + const signal = AbortSignal.abort(); + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + signal + }); + cp.on('exit', mustCall((code, killSignal) => { + strictEqual(code, null); + strictEqual(killSignal, 'SIGTERM'); + })); + cp.on('error', mustCall((err) => { + strictEqual(err.name, 'AbortError'); + })); +} + +{ + // Test passing an aborted signal with custom error to a forked child_process + const signal = AbortSignal.abort(new Error('boom')); + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + signal + }); + cp.on('exit', mustCall((code, killSignal) => { + strictEqual(code, null); + strictEqual(killSignal, 'SIGTERM'); + })); + cp.on('error', mustCall((err) => { + strictEqual(err.name, 'AbortError'); + strictEqual(err.cause.name, 'Error'); + strictEqual(err.cause.message, 'boom'); + })); +} + +{ + // Test passing a different kill signal + const signal = AbortSignal.abort(); + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + signal, + killSignal: 'SIGKILL', + }); + cp.on('exit', mustCall((code, killSignal) => { + strictEqual(code, null); + strictEqual(killSignal, 'SIGKILL'); + })); + cp.on('error', mustCall((err) => { + strictEqual(err.name, 'AbortError'); + })); +} + +{ + // Test aborting a cp before close but after exit + const ac = new AbortController(); + const { signal } = ac; + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + signal + }); + cp.on('exit', mustCall(() => { + ac.abort(); + })); + cp.on('error', mustNotCall()); + + setTimeout(() => cp.kill(), 1); +} diff --git a/test/js/node/test/parallel/test-child-process-fork-and-spawn.js b/test/js/node/test/parallel/test-child-process-fork-and-spawn.js new file mode 100644 index 0000000000..88e634ba8b --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork-and-spawn.js @@ -0,0 +1,44 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { fork, spawn } = require('child_process'); + +// Fork, then spawn. The spawned process should not hang. +switch (process.argv[2] || '') { + case '': + fork(__filename, ['fork']).on('exit', common.mustCall(checkExit)); + break; + case 'fork': + spawn(process.execPath, [__filename, 'spawn']) + .on('exit', common.mustCall(checkExit)); + break; + case 'spawn': + break; + default: + assert.fail(); +} + +function checkExit(statusCode) { + assert.strictEqual(statusCode, 0); +} diff --git a/test/js/node/test/parallel/test-child-process-fork-args.js b/test/js/node/test/parallel/test-child-process-fork-args.js new file mode 100644 index 0000000000..2ed31fa4e9 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork-args.js @@ -0,0 +1,105 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { fork } = require('child_process'); + +// This test check the arguments of `fork` method +// Refs: https://github.com/nodejs/node/issues/20749 +const expectedEnv = { foo: 'bar' }; + +// Ensure that first argument `modulePath` must be provided +// and be of type string +{ + const invalidModulePath = [ + 0, + true, + undefined, + null, + [], + {}, + () => {}, + Symbol('t'), + ]; + invalidModulePath.forEach((modulePath) => { + assert.throws(() => fork(modulePath), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: /^The "modulePath" argument must be of type string/ + }); + }); + + const cp = fork(fixtures.path('child-process-echo-options.js')); + cp.on( + 'exit', + common.mustCall((code) => { + assert.strictEqual(code, 0); + }) + ); +} + +// Ensure that the second argument of `fork` +// and `fork` should parse options +// correctly if args is undefined or null +{ + const invalidSecondArgs = [ + 0, + true, + () => {}, + Symbol('t'), + ]; + invalidSecondArgs.forEach((arg) => { + assert.throws( + () => { + fork(fixtures.path('child-process-echo-options.js'), arg); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + }); + + const argsLists = [undefined, null, []]; + + argsLists.forEach((args) => { + const cp = fork(fixtures.path('child-process-echo-options.js'), args, { + env: { ...process.env, ...expectedEnv } + }); + + cp.on( + 'message', + common.mustCall(({ env }) => { + assert.strictEqual(env.foo, expectedEnv.foo); + }) + ); + + cp.on( + 'exit', + common.mustCall((code) => { + assert.strictEqual(code, 0); + }) + ); + }); +} + +// Ensure that the third argument should be type of object if provided +{ + const invalidThirdArgs = [ + 0, + true, + () => {}, + Symbol('t'), + ]; + invalidThirdArgs.forEach((arg) => { + assert.throws( + () => { + fork(fixtures.path('child-process-echo-options.js'), [], arg); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + }); +} diff --git a/test/js/node/test/parallel/test-child-process-fork-close.js b/test/js/node/test/parallel/test-child-process-fork-close.js new file mode 100644 index 0000000000..bfaabd3d2f --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork-close.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fork = require('child_process').fork; +const fixtures = require('../common/fixtures'); + +const cp = fork(fixtures.path('child-process-message-and-exit.js')); + +let gotMessage = false; +let gotExit = false; +let gotClose = false; + +cp.on('message', common.mustCall(function(message) { + assert(!gotMessage); + assert(!gotClose); + assert.strictEqual(message, 'hello'); + gotMessage = true; +})); + +cp.on('exit', common.mustCall(function() { + assert(!gotExit); + assert(!gotClose); + gotExit = true; +})); + +cp.on('close', common.mustCall(function() { + assert(gotMessage); + assert(gotExit); + assert(!gotClose); + gotClose = true; +})); diff --git a/test/js/node/test/parallel/test-child-process-fork-detached.js b/test/js/node/test/parallel/test-child-process-fork-detached.js new file mode 100644 index 0000000000..87c1517328 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork-detached.js @@ -0,0 +1,23 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fork = require('child_process').fork; +const fixtures = require('../common/fixtures'); + +const nonPersistentNode = fork( + fixtures.path('parent-process-nonpersistent-fork.js'), + [], + { silent: true }); + +let childId = -1; + +nonPersistentNode.stdout.on('data', (data) => { + childId = parseInt(data, 10); + nonPersistentNode.kill(); +}); + +process.on('exit', () => { + assert.notStrictEqual(childId, -1); + // Killing the child process should not throw an error + process.kill(childId); +}); diff --git a/test/js/node/test/parallel/test-child-process-fork-exec-path.js b/test/js/node/test/parallel/test-child-process-fork-exec-path.js new file mode 100644 index 0000000000..3417a102c6 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork-exec-path.js @@ -0,0 +1,60 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +// Test that `fork()` respects the `execPath` option. + +const tmpdir = require('../common/tmpdir'); +const { addLibraryPath } = require('../common/shared-lib-util'); +const assert = require('assert'); +const fs = require('fs'); +const { fork } = require('child_process'); + +const msg = { test: 'this' }; +const nodePath = process.execPath; +const copyPath = tmpdir.resolve('node-copy.exe'); + +addLibraryPath(process.env); + +// Child +if (process.env.FORK) { + assert.strictEqual(process.execPath, copyPath); + assert.ok(process.send); + process.send(msg); + return process.exit(); +} + +// Parent +tmpdir.refresh(); +assert.strictEqual(fs.existsSync(copyPath), false); +fs.copyFileSync(nodePath, copyPath, fs.constants.COPYFILE_FICLONE); +fs.chmodSync(copyPath, '0755'); + +const envCopy = { ...process.env, FORK: 'true' }; +const child = fork(__filename, { execPath: copyPath, env: envCopy }); +child.on('message', common.mustCall(function(recv) { + assert.deepStrictEqual(recv, msg); +})); +child.on('exit', common.mustCall(function(code) { + assert.strictEqual(code, 0); +})); diff --git a/test/js/node/test/parallel/test-child-process-fork-no-shell.js b/test/js/node/test/parallel/test-child-process-fork-no-shell.js new file mode 100644 index 0000000000..81ceab61fd --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork-no-shell.js @@ -0,0 +1,20 @@ +'use strict'; +// This test verifies that the shell option is not supported by fork(). +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const expected = common.isWindows ? '%foo%' : '$foo'; + +if (process.argv[2] === undefined) { + const child = cp.fork(__filename, [expected], { + shell: true, + env: { ...process.env, foo: 'bar' } + }); + + child.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + })); +} else { + assert.strictEqual(process.argv[2], expected); +} diff --git a/test/js/node/test/parallel/test-child-process-fork-url.mjs b/test/js/node/test/parallel/test-child-process-fork-url.mjs new file mode 100644 index 0000000000..9261b87563 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork-url.mjs @@ -0,0 +1,11 @@ +import { mustCall } from '../common/index.mjs'; +import { fork } from 'child_process'; + +if (process.argv[2] === 'child') { + process.disconnect(); +} else { + const child = fork(new URL(import.meta.url), ['child']); + + child.on('disconnect', mustCall()); + child.once('exit', mustCall()); +} diff --git a/test/js/node/test/parallel/test-child-process-fork3.js b/test/js/node/test/parallel/test-child-process-fork3.js new file mode 100644 index 0000000000..735a441950 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-fork3.js @@ -0,0 +1,27 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const child_process = require('child_process'); +const fixtures = require('../common/fixtures'); + +child_process.fork(fixtures.path('empty.js')); // should not hang diff --git a/test/js/node/test/parallel/test-child-process-ipc.js b/test/js/node/test/parallel/test-child-process-ipc.js new file mode 100644 index 0000000000..d776f9594b --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-ipc.js @@ -0,0 +1,63 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const { + mustCall, + mustNotCall, +} = require('../common'); +const assert = require('assert'); +const debug = require('util').debuglog('test'); + +const { spawn } = require('child_process'); +const fixtures = require('../common/fixtures'); + +const sub = fixtures.path('echo.js'); + +const child = spawn(process.argv[0], [sub]); + +child.stderr.on('data', mustNotCall()); + +child.stdout.setEncoding('utf8'); + +const messages = [ + 'hello world\r\n', + 'echo me\r\n', +]; + +child.stdout.on('data', mustCall((data) => { + debug(`child said: ${JSON.stringify(data)}`); + const test = messages.shift(); + debug(`testing for '${test}'`); + assert.strictEqual(data, test); + if (messages.length) { + debug(`writing '${messages[0]}'`); + child.stdin.write(messages[0]); + } else { + assert.strictEqual(messages.length, 0); + child.stdin.end(); + } +}, messages.length)); + +child.stdout.on('end', mustCall((data) => { + debug('child end'); +})); diff --git a/test/js/node/test/parallel/test-child-process-kill.js b/test/js/node/test/parallel/test-child-process-kill.js new file mode 100644 index 0000000000..1025c69ba1 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-kill.js @@ -0,0 +1,41 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; +const cat = spawn(common.isWindows ? 'cmd' : 'cat'); + +cat.stdout.on('end', common.mustCall()); +cat.stderr.on('data', common.mustNotCall()); +cat.stderr.on('end', common.mustCall()); + +cat.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, null); + assert.strictEqual(signal, 'SIGTERM'); + assert.strictEqual(cat.signalCode, 'SIGTERM'); +})); + +assert.strictEqual(cat.signalCode, null); +assert.strictEqual(cat.killed, false); +cat.kill(); +assert.strictEqual(cat.killed, true); diff --git a/test/js/node/test/parallel/test-child-process-no-deprecation.js b/test/js/node/test/parallel/test-child-process-no-deprecation.js new file mode 100644 index 0000000000..d12e5b882f --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-no-deprecation.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +process.noDeprecation = true; + +if (process.argv[2] === 'child') { + process.emitWarning('Something else is deprecated.', 'DeprecationWarning'); +} else { + // parent process + const spawn = require('child_process').spawn; + + // spawn self as child + const child = spawn(process.execPath, [process.argv[1], 'child']); + + child.stderr.on('data', common.mustNotCall()); +} diff --git a/test/js/node/test/parallel/test-child-process-prototype-tampering.mjs b/test/js/node/test/parallel/test-child-process-prototype-tampering.mjs new file mode 100644 index 0000000000..d94c4bdbc6 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-prototype-tampering.mjs @@ -0,0 +1,91 @@ +import * as common from '../common/index.mjs'; +import * as fixtures from '../common/fixtures.mjs'; +import { EOL } from 'node:os'; +import { strictEqual, notStrictEqual, throws } from 'node:assert'; +import cp from 'node:child_process'; + +// TODO(LiviaMedeiros): test on different platforms +if (!common.isLinux) + common.skip(); + +const expectedCWD = process.cwd(); +const expectedUID = process.getuid(); + +for (const tamperedCwd of ['', '/tmp', '/not/existing/malicious/path', 42n]) { + Object.prototype.cwd = tamperedCwd; + + cp.exec('pwd', common.mustSucceed((out) => { + strictEqual(`${out}`, `${expectedCWD}${EOL}`); + })); + strictEqual(`${cp.execSync('pwd')}`, `${expectedCWD}${EOL}`); + cp.execFile('pwd', common.mustSucceed((out) => { + strictEqual(`${out}`, `${expectedCWD}${EOL}`); + })); + strictEqual(`${cp.execFileSync('pwd')}`, `${expectedCWD}${EOL}`); + cp.spawn('pwd').stdout.on('data', common.mustCall((out) => { + strictEqual(`${out}`, `${expectedCWD}${EOL}`); + })); + strictEqual(`${cp.spawnSync('pwd').stdout}`, `${expectedCWD}${EOL}`); + + delete Object.prototype.cwd; +} + +for (const tamperedUID of [0, 1, 999, 1000, 0n, 'gwak']) { + Object.prototype.uid = tamperedUID; + + cp.exec('id -u', common.mustSucceed((out) => { + strictEqual(`${out}`, `${expectedUID}${EOL}`); + })); + strictEqual(`${cp.execSync('id -u')}`, `${expectedUID}${EOL}`); + cp.execFile('id', ['-u'], common.mustSucceed((out) => { + strictEqual(`${out}`, `${expectedUID}${EOL}`); + })); + strictEqual(`${cp.execFileSync('id', ['-u'])}`, `${expectedUID}${EOL}`); + cp.spawn('id', ['-u']).stdout.on('data', common.mustCall((out) => { + strictEqual(`${out}`, `${expectedUID}${EOL}`); + })); + strictEqual(`${cp.spawnSync('id', ['-u']).stdout}`, `${expectedUID}${EOL}`); + + delete Object.prototype.uid; +} + +{ + Object.prototype.execPath = '/not/existing/malicious/path'; + + // Does not throw ENOENT + cp.fork(fixtures.path('empty.js')); + + delete Object.prototype.execPath; +} + +for (const shellCommandArgument of ['-L && echo "tampered"']) { + Object.prototype.shell = true; + const cmd = 'pwd'; + let cmdExitCode = ''; + + const program = cp.spawn(cmd, [shellCommandArgument], { cwd: expectedCWD }); + program.stderr.on('data', common.mustCall()); + program.stdout.on('data', common.mustNotCall()); + + program.on('exit', common.mustCall((code) => { + notStrictEqual(code, 0); + })); + + cp.execFile(cmd, [shellCommandArgument], { cwd: expectedCWD }, + common.mustCall((err) => { + notStrictEqual(err.code, 0); + }) + ); + + throws(() => { + cp.execFileSync(cmd, [shellCommandArgument], { cwd: expectedCWD }); + }, (e) => { + notStrictEqual(e.status, 0); + return true; + }); + + cmdExitCode = cp.spawnSync(cmd, [shellCommandArgument], { cwd: expectedCWD }).status; + notStrictEqual(cmdExitCode, 0); + + delete Object.prototype.shell; +} diff --git a/test/js/node/test/parallel/test-child-process-send-type-error.js b/test/js/node/test/parallel/test-child-process-send-type-error.js new file mode 100644 index 0000000000..65c620dd29 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-send-type-error.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const cp = require('child_process'); + +function fail(proc, args) { + assert.throws(() => { + proc.send.apply(proc, args); + }, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }); +} + +let target = process; + +if (process.argv[2] !== 'child') { + target = cp.fork(__filename, ['child']); + target.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + })); +} + +fail(target, ['msg', null, null]); +fail(target, ['msg', null, '']); +fail(target, ['msg', null, 'foo']); +fail(target, ['msg', null, 0]); +fail(target, ['msg', null, NaN]); +fail(target, ['msg', null, 1]); +fail(target, ['msg', null, null, common.mustNotCall()]); diff --git a/test/js/node/test/parallel/test-child-process-set-blocking.js b/test/js/node/test/parallel/test-child-process-set-blocking.js new file mode 100644 index 0000000000..6dcfa93141 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-set-blocking.js @@ -0,0 +1,36 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const ch = require('child_process'); + +const SIZE = 100000; +const python = process.env.PYTHON || (common.isWindows ? 'python' : 'python3'); + +const cp = ch.spawn(python, ['-c', `print(${SIZE} * "C")`], { + stdio: 'inherit' +}); + +cp.on('exit', common.mustCall(function(code) { + assert.strictEqual(code, 0); +})); diff --git a/test/js/node/test/parallel/test-child-process-silent.js b/test/js/node/test/parallel/test-child-process-silent.js new file mode 100644 index 0000000000..892c4527c9 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-silent.js @@ -0,0 +1,106 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const childProcess = require('child_process'); + +// Child pipe test +if (process.argv[2] === 'pipe') { + process.stdout.write('stdout message'); + process.stderr.write('stderr message'); + +} else if (process.argv[2] === 'ipc') { + // Child IPC test + process.send('message from child'); + process.on('message', function() { + process.send('got message from primary'); + }); + +} else if (process.argv[2] === 'primary') { + // Primary | start child pipe test + + const child = childProcess.fork(process.argv[1], ['pipe'], { silent: true }); + + // Allow child process to self terminate + child.disconnect(); + + child.on('exit', function() { + process.exit(0); + }); + +} else { + // Testcase | start primary && child IPC test + + // testing: is stderr and stdout piped to primary + const args = [process.argv[1], 'primary']; + const primary = childProcess.spawn(process.execPath, args); + + // Got any stderr or std data + let stdoutData = false; + primary.stdout.on('data', function() { + stdoutData = true; + }); + let stderrData = false; + primary.stderr.on('data', function() { + stderrData = true; + }); + + // testing: do message system work when using silent + const child = childProcess.fork(process.argv[1], ['ipc'], { silent: true }); + + // Manual pipe so we will get errors + child.stderr.pipe(process.stderr, { end: false }); + child.stdout.pipe(process.stdout, { end: false }); + + let childSending = false; + let childReceiving = false; + child.on('message', function(message) { + if (childSending === false) { + childSending = (message === 'message from child'); + } + + if (childReceiving === false) { + childReceiving = (message === 'got message from primary'); + } + + if (childReceiving === true) { + child.kill(); + } + }); + child.send('message to child'); + + // Check all values + process.on('exit', function() { + // clean up + child.kill(); + primary.kill(); + + // Check std(out|err) pipes + assert.ok(!stdoutData); + assert.ok(!stderrData); + + // Check message system + assert.ok(childSending); + assert.ok(childReceiving); + }); +} diff --git a/test/js/node/test/parallel/test-child-process-spawn-args.js b/test/js/node/test/parallel/test-child-process-spawn-args.js new file mode 100644 index 0000000000..ec56f409fa --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-spawn-args.js @@ -0,0 +1,55 @@ +'use strict'; + +// This test confirms that `undefined`, `null`, and `[]` +// can be used as a placeholder for the second argument (`args`) of `spawn()`. +// Previously, there was a bug where using `undefined` for the second argument +// caused the third argument (`options`) to be ignored. +// See https://github.com/nodejs/node/issues/24912. + +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); + +const assert = require('assert'); +const { spawn } = require('child_process'); + +tmpdir.refresh(); + +const command = common.isWindows ? 'cd' : 'pwd'; +const options = { cwd: tmpdir.path }; + +if (common.isWindows) { + // This test is not the case for Windows based systems + // unless the `shell` options equals to `true` + + options.shell = true; +} + +const testCases = [ + undefined, + null, + [], +]; + +const expectedResult = tmpdir.path.trim().toLowerCase(); + +(async () => { + const results = await Promise.all( + testCases.map((testCase) => { + return new Promise((resolve) => { + const subprocess = spawn(command, testCase, options); + + let accumulatedData = Buffer.alloc(0); + + subprocess.stdout.on('data', common.mustCall((data) => { + accumulatedData = Buffer.concat([accumulatedData, data]); + })); + + subprocess.stdout.on('end', () => { + resolve(accumulatedData.toString().trim().toLowerCase()); + }); + }); + }) + ); + + assert.deepStrictEqual([...new Set(results)], [expectedResult]); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-child-process-spawn-controller.js b/test/js/node/test/parallel/test-child-process-spawn-controller.js new file mode 100644 index 0000000000..20facb09b3 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-spawn-controller.js @@ -0,0 +1,183 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { spawn } = require('child_process'); +const fixtures = require('../common/fixtures'); + +const aliveScript = fixtures.path('child-process-stay-alive-forever.js'); +{ + // Verify that passing an AbortSignal works + const controller = new AbortController(); + const { signal } = controller; + + const cp = spawn(process.execPath, [aliveScript], { + signal, + }); + + cp.on('exit', common.mustCall((code, killSignal) => { + assert.strictEqual(code, null); + assert.strictEqual(killSignal, 'SIGTERM'); + })); + + cp.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); + + controller.abort(); +} + +{ + // Verify that passing an AbortSignal with custom abort error works + const controller = new AbortController(); + const { signal } = controller; + const cp = spawn(process.execPath, [aliveScript], { + signal, + }); + + cp.on('exit', common.mustCall((code, killSignal) => { + assert.strictEqual(code, null); + assert.strictEqual(killSignal, 'SIGTERM'); + })); + + cp.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + assert.strictEqual(e.cause.name, 'Error'); + assert.strictEqual(e.cause.message, 'boom'); + })); + + controller.abort(new Error('boom')); +} + +{ + const controller = new AbortController(); + const { signal } = controller; + const cp = spawn(process.execPath, [aliveScript], { + signal, + }); + + cp.on('exit', common.mustCall((code, killSignal) => { + assert.strictEqual(code, null); + assert.strictEqual(killSignal, 'SIGTERM'); + })); + + cp.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + assert.strictEqual(e.cause, 'boom'); + })); + + controller.abort('boom'); +} + +{ + // Verify that passing an already-aborted signal works. + const signal = AbortSignal.abort(); + + const cp = spawn(process.execPath, [aliveScript], { + signal, + }); + cp.on('exit', common.mustCall((code, killSignal) => { + assert.strictEqual(code, null); + assert.strictEqual(killSignal, 'SIGTERM'); + })); + + cp.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); +} + +{ + // Verify that passing an already-aborted signal with custom abort error + // works. + const signal = AbortSignal.abort(new Error('boom')); + const cp = spawn(process.execPath, [aliveScript], { + signal, + }); + cp.on('exit', common.mustCall((code, killSignal) => { + assert.strictEqual(code, null); + assert.strictEqual(killSignal, 'SIGTERM'); + })); + + cp.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + assert.strictEqual(e.cause.name, 'Error'); + assert.strictEqual(e.cause.message, 'boom'); + })); +} + +{ + const signal = AbortSignal.abort('boom'); + const cp = spawn(process.execPath, [aliveScript], { + signal, + }); + cp.on('exit', common.mustCall((code, killSignal) => { + assert.strictEqual(code, null); + assert.strictEqual(killSignal, 'SIGTERM'); + })); + + cp.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + assert.strictEqual(e.cause, 'boom'); + })); +} + +{ + // Verify that waiting a bit and closing works + const controller = new AbortController(); + const { signal } = controller; + + const cp = spawn(process.execPath, [aliveScript], { + signal, + }); + + cp.on('exit', common.mustCall((code, killSignal) => { + assert.strictEqual(code, null); + assert.strictEqual(killSignal, 'SIGTERM'); + })); + + cp.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); + + setTimeout(() => controller.abort(), 1); +} + +{ + // Test passing a different killSignal + const controller = new AbortController(); + const { signal } = controller; + + const cp = spawn(process.execPath, [aliveScript], { + signal, + killSignal: 'SIGKILL', + }); + + cp.on('exit', common.mustCall((code, killSignal) => { + assert.strictEqual(code, null); + assert.strictEqual(killSignal, 'SIGKILL'); + })); + + cp.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); + + setTimeout(() => controller.abort(), 1); +} + +{ + // Test aborting a cp before close but after exit + const controller = new AbortController(); + const { signal } = controller; + + const cp = spawn(process.execPath, [aliveScript], { + signal, + }); + + cp.on('exit', common.mustCall(() => { + controller.abort(); + })); + + cp.on('error', common.mustNotCall()); + + setTimeout(() => cp.kill(), 1); +} diff --git a/test/js/node/test/parallel/test-child-process-spawn-event.js b/test/js/node/test/parallel/test-child-process-spawn-event.js new file mode 100644 index 0000000000..c025d86286 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-spawn-event.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); +const spawn = require('child_process').spawn; +const assert = require('assert'); + +const subprocess = spawn('echo', ['ok']); + +let didSpawn = false; +subprocess.on('spawn', function() { + didSpawn = true; +}); +function mustCallAfterSpawn() { + return common.mustCall(function() { + assert.ok(didSpawn); + }); +} + +subprocess.on('error', common.mustNotCall()); +subprocess.on('spawn', common.mustCall()); +subprocess.stdout.on('data', mustCallAfterSpawn()); +subprocess.stdout.on('end', mustCallAfterSpawn()); +subprocess.stdout.on('close', mustCallAfterSpawn()); +subprocess.stderr.on('data', common.mustNotCall()); +subprocess.stderr.on('end', mustCallAfterSpawn()); +subprocess.stderr.on('close', mustCallAfterSpawn()); +subprocess.on('exit', mustCallAfterSpawn()); +subprocess.on('close', mustCallAfterSpawn()); diff --git a/test/js/node/test/parallel/test-child-process-spawnsync-args.js b/test/js/node/test/parallel/test-child-process-spawnsync-args.js new file mode 100644 index 0000000000..4e65b22f6e --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-spawnsync-args.js @@ -0,0 +1,48 @@ +'use strict'; + +// This test confirms that `undefined`, `null`, and `[]` can be used +// as a placeholder for the second argument (`args`) of `spawnSync()`. +// Previously, there was a bug where using `undefined` for the second argument +// caused the third argument (`options`) to be ignored. +// See https://github.com/nodejs/node/issues/24912. + +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); + +const assert = require('assert'); +const { spawnSync } = require('child_process'); + +const command = common.isWindows ? 'cd' : 'pwd'; +const options = { cwd: tmpdir.path }; + +tmpdir.refresh(); + +if (common.isWindows) { + // This test is not the case for Windows based systems + // unless the `shell` options equals to `true` + + options.shell = true; +} + +const testCases = [ + undefined, + null, + [], +]; + +const expectedResult = tmpdir.path.trim().toLowerCase(); + +const results = testCases.map((testCase) => { + const { stdout, stderr, error } = spawnSync( + command, + testCase, + options + ); + + assert.ifError(error); + assert.deepStrictEqual(stderr, Buffer.alloc(0)); + + return stdout.toString().trim().toLowerCase(); +}); + +assert.deepStrictEqual([...new Set(results)], [expectedResult]); diff --git a/test/js/node/test/parallel/test-child-process-spawnsync-env.js b/test/js/node/test/parallel/test-child-process-spawnsync-env.js new file mode 100644 index 0000000000..c8e11b5067 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-spawnsync-env.js @@ -0,0 +1,36 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +if (process.argv[2] === 'child') { + console.log(process.env.foo); +} else { + const expected = 'bar'; + const child = cp.spawnSync(process.execPath, [__filename, 'child'], { + env: Object.assign(process.env, { foo: expected }) + }); + + assert.strictEqual(child.stdout.toString().trim(), expected); +} diff --git a/test/js/node/test/parallel/test-child-process-spawnsync-input.js b/test/js/node/test/parallel/test-child-process-spawnsync-input.js new file mode 100644 index 0000000000..62ae476ae1 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-spawnsync-input.js @@ -0,0 +1,125 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); + +const spawnSync = require('child_process').spawnSync; + +const msgOut = 'this is stdout'; +const msgErr = 'this is stderr'; + +// This is actually not os.EOL? +const msgOutBuf = Buffer.from(`${msgOut}\n`); +const msgErrBuf = Buffer.from(`${msgErr}\n`); + +const args = [ + '-e', + `console.log("${msgOut}"); console.error("${msgErr}");`, +]; + +let ret; + + +function checkSpawnSyncRet(ret) { + assert.strictEqual(ret.status, 0); + assert.strictEqual(ret.error, undefined); +} + +function verifyBufOutput(ret) { + checkSpawnSyncRet(ret); + assert.deepStrictEqual(ret.stdout, msgOutBuf); + assert.deepStrictEqual(ret.stderr, msgErrBuf); +} + +if (process.argv.includes('spawnchild')) { + switch (process.argv[3]) { + case '1': + ret = spawnSync(process.execPath, args, { stdio: 'inherit' }); + checkSpawnSyncRet(ret); + break; + case '2': + ret = spawnSync(process.execPath, args, { + stdio: ['inherit', 'inherit', 'inherit'] + }); + checkSpawnSyncRet(ret); + break; + } + process.exit(0); + return; +} + +verifyBufOutput(spawnSync(process.execPath, [__filename, 'spawnchild', 1])); +verifyBufOutput(spawnSync(process.execPath, [__filename, 'spawnchild', 2])); + +let options = { + input: 1234 +}; + +assert.throws( + () => spawnSync('cat', [], options), + { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }); + +options = { + input: 'hello world' +}; + +ret = spawnSync('cat', [], options); + +checkSpawnSyncRet(ret); +assert.strictEqual(ret.stdout.toString('utf8'), options.input); +assert.strictEqual(ret.stderr.toString('utf8'), ''); + +options = { + input: Buffer.from('hello world') +}; + +ret = spawnSync('cat', [], options); + +checkSpawnSyncRet(ret); +assert.deepStrictEqual(ret.stdout, options.input); +assert.deepStrictEqual(ret.stderr, Buffer.from('')); + +// common.getArrayBufferViews expects a buffer +// with length an multiple of 8 +const msgBuf = Buffer.from('hello world'.repeat(8)); +for (const arrayBufferView of common.getArrayBufferViews(msgBuf)) { + options = { + input: arrayBufferView + }; + + ret = spawnSync('cat', [], options); + + checkSpawnSyncRet(ret); + + assert.deepStrictEqual(ret.stdout, msgBuf); + assert.deepStrictEqual(ret.stderr, Buffer.from('')); +} + +verifyBufOutput(spawnSync(process.execPath, args)); + +ret = spawnSync(process.execPath, args, { encoding: 'utf8' }); + +checkSpawnSyncRet(ret); +assert.strictEqual(ret.stdout, `${msgOut}\n`); +assert.strictEqual(ret.stderr, `${msgErr}\n`); diff --git a/test/js/node/test/parallel/test-child-process-stdin-ipc.js b/test/js/node/test/parallel/test-child-process-stdin-ipc.js new file mode 100644 index 0000000000..945960b99b --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-stdin-ipc.js @@ -0,0 +1,40 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const spawn = require('child_process').spawn; + +if (process.argv[2] === 'child') { + // Just reference stdin, it should start it + process.stdin; // eslint-disable-line no-unused-expressions + return; +} + +const proc = spawn(process.execPath, [__filename, 'child'], { + stdio: ['ipc', 'inherit', 'inherit'] +}); + +proc.on('exit', common.mustCall(function(code) { + assert.strictEqual(code, 0); +})); diff --git a/test/js/node/test/parallel/test-child-process-stdio-big-write-end.js b/test/js/node/test/parallel/test-child-process-stdio-big-write-end.js new file mode 100644 index 0000000000..85e6a8b321 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-stdio-big-write-end.js @@ -0,0 +1,86 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const { + mustCall, + mustCallAtLeast, +} = require('../common'); +const assert = require('assert'); +const debug = require('util').debuglog('test'); + +let bufsize = 0; + +switch (process.argv[2]) { + case undefined: + return parent(); + case 'child': + return child(); + default: + throw new Error('invalid'); +} + +function parent() { + const spawn = require('child_process').spawn; + const child = spawn(process.execPath, [__filename, 'child']); + let sent = 0; + + let n = ''; + child.stdout.setEncoding('ascii'); + child.stdout.on('data', mustCallAtLeast((c) => { + n += c; + })); + child.stdout.on('end', mustCall(() => { + assert.strictEqual(+n, sent); + debug('ok'); + })); + + // Write until the buffer fills up. + let buf; + do { + bufsize += 1024; + buf = Buffer.alloc(bufsize, '.'); + sent += bufsize; + } while (child.stdin.write(buf)); + + // Then write a bunch more times. + for (let i = 0; i < 100; i++) { + const buf = Buffer.alloc(bufsize, '.'); + sent += bufsize; + child.stdin.write(buf); + } + + // Now end, before it's all flushed. + child.stdin.end(); + + // now we wait... +} + +function child() { + let received = 0; + process.stdin.on('data', mustCallAtLeast((c) => { + received += c.length; + })); + process.stdin.on('end', mustCall(() => { + // This console.log is part of the test. + console.log(received); + })); +} diff --git a/test/js/node/test/parallel/test-child-process-stdio-inherit.js b/test/js/node/test/parallel/test-child-process-stdio-inherit.js new file mode 100644 index 0000000000..034a077016 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-stdio-inherit.js @@ -0,0 +1,56 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; + +if (process.argv[2] === 'parent') + parent(); +else + grandparent(); + +function grandparent() { + const child = spawn(process.execPath, [__filename, 'parent']); + child.stderr.pipe(process.stderr); + let output = ''; + const input = 'asdfasdf'; + + child.stdout.on('data', function(chunk) { + output += chunk; + }); + child.stdout.setEncoding('utf8'); + + child.stdin.end(input); + + child.on('close', function(code, signal) { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + // 'cat' on windows adds a \r\n at the end. + assert.strictEqual(output.trim(), input.trim()); + }); +} + +function parent() { + // Should not immediately exit. + spawn('cat', [], { stdio: 'inherit' }); +} diff --git a/test/js/node/test/parallel/test-child-process-stdio-overlapped.js b/test/js/node/test/parallel/test-child-process-stdio-overlapped.js new file mode 100644 index 0000000000..5c48e7ee10 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-stdio-overlapped.js @@ -0,0 +1,79 @@ +// Test for "overlapped" stdio option. This test uses the "overlapped-checker" +// helper program which basically a specialized echo program. +// +// The test has two goals: +// +// - Verify that overlapped I/O works on windows. The test program will deadlock +// if stdin doesn't have the FILE_FLAG_OVERLAPPED flag set on startup (see +// test/overlapped-checker/main_win.c for more details). +// - Verify that "overlapped" stdio option works transparently as a pipe (on +// unix/windows) +// +// This is how the test works: +// +// - This script assumes only numeric strings are written to the test program +// stdout. +// - The test program will be spawned with "overlapped" set on stdin and "pipe" +// set on stdout/stderr and at startup writes a number to its stdout +// - When this script receives some data, it will parse the number, add 50 and +// write to the test program's stdin. +// - The test program will then echo the number back to us which will repeat the +// cycle until the number reaches 200, at which point we send the "exit" +// string, which causes the test program to exit. +// - Extra assertion: Every time the test program writes a string to its stdout, +// it will write the number of bytes written to stderr. +// - If overlapped I/O is not setup correctly, this test is going to hang. +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const child_process = require('child_process'); + +const exeExtension = process.platform === 'win32' ? '.exe' : ''; +const exe = 'overlapped-checker' + exeExtension; +const exePath = path.join(path.dirname(process.execPath), exe); + +if (!require('fs').existsSync(exePath)) { + common.skip(exe + ' binary is not available'); +} + +const child = child_process.spawn(exePath, [], { + stdio: ['overlapped', 'pipe', 'pipe'] +}); + +child.stdin.setEncoding('utf8'); +child.stdout.setEncoding('utf8'); +child.stderr.setEncoding('utf8'); + +function writeNext(n) { + child.stdin.write((n + 50).toString()); +} + +child.stdout.on('data', (s) => { + const n = Number(s); + if (n >= 200) { + child.stdin.write('exit'); + return; + } + writeNext(n); +}); + +let stderr = ''; +child.stderr.on('data', (s) => { + stderr += s; +}); + +child.stderr.on('end', common.mustCall(() => { + // This is the sequence of numbers sent to us: + // - 0 (1 byte written) + // - 50 (2 bytes written) + // - 100 (3 bytes written) + // - 150 (3 bytes written) + // - 200 (3 bytes written) + assert.strictEqual(stderr, '12333'); +})); + +child.on('exit', common.mustCall((status) => { + // The test program will return the number of writes as status code. + assert.strictEqual(status, 0); +})); diff --git a/test/js/node/test/parallel/test-child-process-stdout-flush-exit.js b/test/js/node/test/parallel/test-child-process-stdout-flush-exit.js new file mode 100644 index 0000000000..3c5f00d9bb --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-stdout-flush-exit.js @@ -0,0 +1,56 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// If child process output to console and exit +// The console.log statements here are part of the test. +if (process.argv[2] === 'child') { + console.log('hello'); + for (let i = 0; i < 200; i++) { + console.log('filler'); + } + console.log('goodbye'); + process.exit(0); +} else { + // parent process + const spawn = require('child_process').spawn; + + // spawn self as child + const child = spawn(process.argv[0], [process.argv[1], 'child']); + + let stdout = ''; + + child.stderr.on('data', common.mustNotCall()); + + // Check if we receive both 'hello' at start and 'goodbye' at end + child.stdout.setEncoding('utf8'); + child.stdout.on('data', common.mustCallAtLeast((data) => { + stdout += data; + })); + + child.on('close', common.mustCall(() => { + assert.strictEqual(stdout.slice(0, 6), 'hello\n'); + assert.strictEqual(stdout.slice(stdout.length - 8), 'goodbye\n'); + })); +} diff --git a/test/js/node/test/parallel/test-child-process-stdout-flush.js b/test/js/node/test/parallel/test-child-process-stdout-flush.js new file mode 100644 index 0000000000..bc549fb6f3 --- /dev/null +++ b/test/js/node/test/parallel/test-child-process-stdout-flush.js @@ -0,0 +1,48 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; +const fixtures = require('../common/fixtures'); + +const sub = fixtures.path('print-chars.js'); + +const n = 500000; + +const child = spawn(process.argv[0], [sub, n]); + +let count = 0; + +child.stderr.setEncoding('utf8'); +child.stderr.on('data', common.mustNotCall()); + +child.stdout.setEncoding('utf8'); +child.stdout.on('data', (data) => { + count += data.length; +}); + +child.on('close', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + assert.strictEqual(n, count); +})); diff --git a/test/js/node/test/parallel/test-cli-eval-event.js b/test/js/node/test/parallel/test-cli-eval-event.js new file mode 100644 index 0000000000..df356e50d3 --- /dev/null +++ b/test/js/node/test/parallel/test-cli-eval-event.js @@ -0,0 +1,15 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; + +const child = spawn(process.execPath, ['-e', ` + const server = require('net').createServer().listen(0); + server.once('listening', server.close); +`]); + +child.once('exit', common.mustCall(function(exitCode, signalCode) { + assert.strictEqual(exitCode, 0); + assert.strictEqual(signalCode, null); +})); diff --git a/test/js/node/test/parallel/test-client-request-destroy.js b/test/js/node/test/parallel/test-client-request-destroy.js new file mode 100644 index 0000000000..2f3efcf812 --- /dev/null +++ b/test/js/node/test/parallel/test-client-request-destroy.js @@ -0,0 +1,13 @@ +'use strict'; + +// Test that http.ClientRequest,prototype.destroy() returns `this`. +require('../common'); + +const assert = require('assert'); +const http = require('http'); +const clientRequest = new http.ClientRequest({ createConnection: () => {} }); + +assert.strictEqual(clientRequest.destroyed, false); +assert.strictEqual(clientRequest.destroy(), clientRequest); +assert.strictEqual(clientRequest.destroyed, true); +assert.strictEqual(clientRequest.destroy(), clientRequest); diff --git a/test/js/node/test/parallel/test-cluster-advanced-serialization.js b/test/js/node/test/parallel/test-cluster-advanced-serialization.js new file mode 100644 index 0000000000..ffca3a8f97 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-advanced-serialization.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isPrimary) { + cluster.settings.serialization = 'advanced'; + const worker = cluster.fork(); + const circular = {}; + circular.circular = circular; + + worker.on('online', common.mustCall(() => { + worker.send(circular); + + worker.on('message', common.mustCall((msg) => { + assert.deepStrictEqual(msg, circular); + worker.kill(); + })); + })); +} else { + process.on('message', (msg) => process.send(msg)); +} diff --git a/test/js/node/test/parallel/test-cluster-bind-privileged-port.js b/test/js/node/test/parallel/test-cluster-bind-privileged-port.js new file mode 100644 index 0000000000..3ac36543a2 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-bind-privileged-port.js @@ -0,0 +1,65 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const net = require('net'); +const { readFileSync } = require('fs'); + +if (common.isLinux) { + try { + const unprivilegedPortStart = parseInt(readFileSync('/proc/sys/net/ipv4/ip_unprivileged_port_start')); + if (unprivilegedPortStart <= 42) { + common.skip('Port 42 is unprivileged'); + } + } catch { + // Do nothing, feature doesn't exist, minimum is 1024 so 42 is usable. + // Continue... + } +} + +// Skip on macOS Mojave. https://github.com/nodejs/node/issues/21679 +if (common.isMacOS) + common.skip('macOS may allow ordinary processes to use any port'); + +if (common.isIBMi) + common.skip('IBMi may allow ordinary processes to use any port'); + +if (common.isWindows) + common.skip('not reliable on Windows.'); + +if (process.getuid() === 0) + common.skip('Test is not supposed to be run as root.'); + +if (cluster.isPrimary) { + cluster.fork().on('exit', common.mustCall((exitCode) => { + assert.strictEqual(exitCode, 0); + })); +} else { + const s = net.createServer(common.mustNotCall()); + s.listen(42, common.mustNotCall('listen should have failed')); + s.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'EACCES'); + process.disconnect(); + })); +} diff --git a/test/js/node/test/parallel/test-cluster-call-and-destroy.js b/test/js/node/test/parallel/test-cluster-call-and-destroy.js new file mode 100644 index 0000000000..2ff20cf03a --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-call-and-destroy.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const cluster = require('cluster'); +const assert = require('assert'); + +if (cluster.isPrimary) { + const worker = cluster.fork(); + worker.on('disconnect', common.mustCall(() => { + assert.strictEqual(worker.isConnected(), false); + worker.destroy(); + })); +} else { + assert.strictEqual(cluster.worker.isConnected(), true); + cluster.worker.disconnect(); +} diff --git a/test/js/node/test/parallel/test-cluster-child-index-dgram.js b/test/js/node/test/parallel/test-cluster-child-index-dgram.js new file mode 100644 index 0000000000..0df7bc175b --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-child-index-dgram.js @@ -0,0 +1,40 @@ +'use strict'; +const common = require('../common'); +const Countdown = require('../common/countdown'); +if (common.isWindows) + common.skip('dgram clustering is currently not supported on Windows.'); + +const cluster = require('cluster'); +const dgram = require('dgram'); + +// Test an edge case when using `cluster` and `dgram.Socket.bind()` +// the port of `0`. +const kPort = 0; + +function child() { + const kTime = 2; + const countdown = new Countdown(kTime * 2, () => { + process.exit(0); + }); + for (let i = 0; i < kTime; i += 1) { + const socket = new dgram.Socket('udp4'); + socket.bind(kPort, common.mustCall(() => { + // `process.nextTick()` or `socket2.close()` would throw + // ERR_SOCKET_DGRAM_NOT_RUNNING + process.nextTick(() => { + socket.close(countdown.dec()); + const socket2 = new dgram.Socket('udp4'); + socket2.bind(kPort, common.mustCall(() => { + process.nextTick(() => { + socket2.close(countdown.dec()); + }); + })); + }); + })); + } +} + +if (cluster.isMaster) + cluster.fork(__filename); +else + child(); diff --git a/test/js/node/test/parallel/test-cluster-child-index-net.js b/test/js/node/test/parallel/test-cluster-child-index-net.js new file mode 100644 index 0000000000..d8c3166d1b --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-child-index-net.js @@ -0,0 +1,31 @@ +'use strict'; +const common = require('../common'); +const Countdown = require('../common/countdown'); +const cluster = require('cluster'); +const net = require('net'); + +// Test an edge case when using `cluster` and `net.Server.listen()` to +// the port of `0`. +const kPort = 0; + +function child() { + const kTime = 2; + const countdown = new Countdown(kTime * 2, () => { + process.exit(0); + }); + for (let i = 0; i < kTime; i += 1) { + const server = net.createServer(); + server.listen(kPort, common.mustCall(() => { + server.close(countdown.dec()); + const server2 = net.createServer(); + server2.listen(kPort, common.mustCall(() => { + server2.close(countdown.dec()); + })); + })); + } +} + +if (cluster.isMaster) + cluster.fork(__filename); +else + child(); diff --git a/test/js/node/test/parallel/test-cluster-concurrent-disconnect.js b/test/js/node/test/parallel/test-cluster-concurrent-disconnect.js new file mode 100644 index 0000000000..b754fa221a --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-concurrent-disconnect.js @@ -0,0 +1,52 @@ +'use strict'; + +// Ref: https://github.com/nodejs/node/issues/32106 + +const common = require('../common'); + +const assert = require('assert'); +const cluster = require('cluster'); +const os = require('os'); + +if (cluster.isPrimary) { + const workers = []; + const numCPUs = os.availableParallelism(); + let waitOnline = numCPUs; + for (let i = 0; i < numCPUs; i++) { + const worker = cluster.fork(); + workers[i] = worker; + worker.once('online', common.mustCall(() => { + if (--waitOnline === 0) + for (const worker of workers) + if (worker.isConnected()) + worker.send(i % 2 ? 'disconnect' : 'destroy'); + })); + + // These errors can occur due to the nature of the test, we might be trying + // to send messages when the worker is disconnecting. + worker.on('error', (err) => { + assert.strictEqual(err.syscall, 'write'); + if (common.isMacOS) { + assert(['EPIPE', 'ENOTCONN'].includes(err.code), err); + } else { + assert(['EPIPE', 'ECONNRESET'].includes(err.code), err); + } + }); + + worker.once('disconnect', common.mustCall(() => { + for (const worker of workers) + if (worker.isConnected()) + worker.send('disconnect'); + })); + + worker.once('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + })); + } +} else { + process.on('message', (msg) => { + if (cluster.worker.isConnected()) + cluster.worker[msg](); + }); +} diff --git a/test/js/node/test/parallel/test-cluster-cwd.js b/test/js/node/test/parallel/test-cluster-cwd.js new file mode 100644 index 0000000000..c5d47e7301 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-cwd.js @@ -0,0 +1,23 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const tmpdir = require('../common/tmpdir'); + +if (cluster.isPrimary) { + tmpdir.refresh(); + + assert.strictEqual(cluster.settings.cwd, undefined); + cluster.fork().on('message', common.mustCall((msg) => { + assert.strictEqual(msg, process.cwd()); + })); + + cluster.setupPrimary({ cwd: tmpdir.path }); + assert.strictEqual(cluster.settings.cwd, tmpdir.path); + cluster.fork().on('message', common.mustCall((msg) => { + assert.strictEqual(msg, tmpdir.path); + })); +} else { + process.send(process.cwd()); + process.disconnect(); +} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-before-exit.js b/test/js/node/test/parallel/test-cluster-disconnect-before-exit.js similarity index 87% rename from test/js/node/cluster/upstream/parallel/test-cluster-disconnect-before-exit.js rename to test/js/node/test/parallel/test-cluster-disconnect-before-exit.js index bb5dda7aa7..f95f1384d5 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-before-exit.js +++ b/test/js/node/test/parallel/test-cluster-disconnect-before-exit.js @@ -19,12 +19,12 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const common = require("../common"); -const cluster = require("cluster"); +'use strict'; +const common = require('../common'); +const cluster = require('cluster'); if (cluster.isPrimary) { - const worker = cluster.fork().on("online", common.mustCall(disconnect)); + const worker = cluster.fork().on('online', common.mustCall(disconnect)); function disconnect() { worker.disconnect(); @@ -32,6 +32,6 @@ if (cluster.isPrimary) { // Disconnect is supposed to disconnect all workers, but not workers that // are already disconnected, since calling disconnect() on an already // disconnected worker would error. - worker.on("disconnect", common.mustCall(cluster.disconnect)); + worker.on('disconnect', common.mustCall(cluster.disconnect)); } } diff --git a/test/js/node/test/parallel/test-cluster-disconnect-exitedAfterDisconnect-race.js b/test/js/node/test/parallel/test-cluster-disconnect-exitedAfterDisconnect-race.js new file mode 100644 index 0000000000..f1a8dea0a9 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-disconnect-exitedAfterDisconnect-race.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); + +// Test should fail in Node.js 5.4.1 and pass in later versions. + +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isPrimary) { + cluster.on('exit', (worker, code) => { + assert.strictEqual(code, 0, `worker exited with code: ${code}, expected 0`); + }); + + return cluster.fork(); +} + +let eventFired = false; + +cluster.worker.disconnect(); + +process.nextTick(common.mustCall(() => { + assert.ok(!eventFired, 'disconnect event should wait for ack'); +})); + +cluster.worker.on('disconnect', common.mustCall(() => { + eventFired = true; +})); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-idle-worker.js b/test/js/node/test/parallel/test-cluster-disconnect-idle-worker.js similarity index 84% rename from test/js/node/cluster/upstream/parallel/test-cluster-disconnect-idle-worker.js rename to test/js/node/test/parallel/test-cluster-disconnect-idle-worker.js index f20bacdede..566f631312 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-idle-worker.js +++ b/test/js/node/test/parallel/test-cluster-disconnect-idle-worker.js @@ -19,18 +19,16 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); const fork = cluster.fork; if (cluster.isPrimary) { fork(); // It is intentionally called `fork` instead of fork(); // `cluster.fork` to test that `this` is not used - cluster.disconnect( - common.mustCall(() => { - assert.deepStrictEqual(Object.keys(cluster.workers), []); - }), - ); + cluster.disconnect(common.mustCall(() => { + assert.deepStrictEqual(Object.keys(cluster.workers), []); + })); } diff --git a/test/js/node/test/parallel/test-cluster-disconnect-leak.js b/test/js/node/test/parallel/test-cluster-disconnect-leak.js new file mode 100644 index 0000000000..e2a417e5cd --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-disconnect-leak.js @@ -0,0 +1,27 @@ +'use strict'; + +// Test fails in Node v5.4.0 and passes in v5.4.1 and newer. + +const common = require('../common'); +const net = require('net'); +const cluster = require('cluster'); + +cluster.schedulingPolicy = cluster.SCHED_NONE; + +if (cluster.isPrimary) { + const worker = cluster.fork(); + + // This is the important part of the test: Confirm that `disconnect` fires. + worker.on('disconnect', common.mustCall()); + + // These are just some extra stuff we're checking for good measure... + worker.on('exit', common.mustCall()); + cluster.on('exit', common.mustCall()); + + cluster.disconnect(); + return; +} + +const server = net.createServer(); + +server.listen(0); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-with-no-workers.js b/test/js/node/test/parallel/test-cluster-disconnect-with-no-workers.js similarity index 88% rename from test/js/node/cluster/upstream/parallel/test-cluster-disconnect-with-no-workers.js rename to test/js/node/test/parallel/test-cluster-disconnect-with-no-workers.js index 865dd25f3f..a34e04973c 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-disconnect-with-no-workers.js +++ b/test/js/node/test/parallel/test-cluster-disconnect-with-no-workers.js @@ -19,18 +19,18 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +'use strict'; +require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); let disconnected; -process.on("exit", function () { +process.on('exit', function() { assert(disconnected); }); -cluster.disconnect(function () { +cluster.disconnect(function() { disconnected = true; }); diff --git a/test/js/node/test/parallel/test-cluster-fork-env.js b/test/js/node/test/parallel/test-cluster-fork-env.js new file mode 100644 index 0000000000..90b456b196 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-fork-env.js @@ -0,0 +1,70 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); + +// This test checks that arguments provided to cluster.fork() will create +// new environment variables and override existing environment variables +// in the created worker process. + +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isWorker) { + const result = cluster.worker.send({ + prop: process.env.cluster_test_prop, + overwrite: process.env.cluster_test_overwrite + }); + + assert.strictEqual(result, true); +} else if (cluster.isPrimary) { + + const checks = { + using: false, + overwrite: false + }; + + // To check that the cluster extend on the process.env we will overwrite a + // property + process.env.cluster_test_overwrite = 'old'; + + // Fork worker + const worker = cluster.fork({ + 'cluster_test_prop': 'custom', + 'cluster_test_overwrite': 'new' + }); + + // Checks worker env + worker.on('message', function(data) { + checks.using = (data.prop === 'custom'); + checks.overwrite = (data.overwrite === 'new'); + process.exit(0); + }); + + process.once('exit', function() { + assert.ok(checks.using, 'The worker did not receive the correct env.'); + assert.ok( + checks.overwrite, + 'The custom environment did not overwrite the existing environment.'); + }); + +} diff --git a/test/js/node/test/parallel/test-cluster-fork-windowsHide.js b/test/js/node/test/parallel/test-cluster-fork-windowsHide.js new file mode 100644 index 0000000000..2b90713ceb --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-fork-windowsHide.js @@ -0,0 +1,74 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const child_process = require('child_process'); +const cluster = require('cluster'); + +if (!process.argv[2]) { + // It seems Windows only allocate new console window for + // attaching processes spawned by detached processes. i.e. + // - If process D is spawned by process C with `detached: true`, + // and process W is spawned by process D with `detached: false`, + // W will get a new black console window popped up. + // - If D is spawned by C with `detached: false` or W is spawned + // by D with `detached: true`, no console window will pop up for W. + // + // So, we have to spawn a detached process first to run the actual test. + const primary = child_process.spawn( + process.argv[0], + [process.argv[1], '--cluster'], + { detached: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'] }); + + const messageHandlers = { + workerOnline: common.mustCall(), + mainWindowHandle: common.mustCall((msg) => { + assert.match(msg.value, /0\s*/); + }), + workerExit: common.mustCall((msg) => { + assert.strictEqual(msg.code, 0); + assert.strictEqual(msg.signal, null); + }) + }; + + primary.on('message', (msg) => { + const handler = messageHandlers[msg.type]; + assert.ok(handler); + handler(msg); + }); + + primary.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + })); + +} else if (cluster.isPrimary) { + cluster.setupPrimary({ + silent: true, + windowsHide: true + }); + + const worker = cluster.fork(); + worker.on('exit', (code, signal) => { + process.send({ type: 'workerExit', code: code, signal: signal }); + }); + + worker.on('online', (msg) => { + process.send({ type: 'workerOnline' }); + + let output = '0'; + if (process.platform === 'win32') { + output = child_process.execSync( + 'powershell -NoProfile -c ' + + `"(Get-Process -Id ${worker.process.pid}).MainWindowHandle"`, + { windowsHide: true, encoding: 'utf8' }); + } + + process.send({ type: 'mainWindowHandle', value: output }); + worker.send('shutdown'); + }); + +} else { + cluster.worker.on('message', (msg) => { + cluster.worker.disconnect(); + }); +} diff --git a/test/js/node/test/parallel/test-cluster-http-pipe.js b/test/js/node/test/parallel/test-cluster-http-pipe.js new file mode 100644 index 0000000000..bdd8fe8c4f --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-http-pipe.js @@ -0,0 +1,59 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +if (common.isWindows) { + common.skip( + 'It is not possible to send pipe handles over the IPC pipe on Windows'); +} + +const assert = require('assert'); +const cluster = require('cluster'); +const http = require('http'); + +if (cluster.isPrimary) { + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + const worker = cluster.fork(); + worker.on('message', common.mustCall((msg) => { + assert.strictEqual(msg, 'DONE'); + })); + worker.on('exit', common.mustCall()); + return; +} + +http.createServer(common.mustCall((req, res) => { + assert.strictEqual(req.connection.remoteAddress, undefined); + assert.strictEqual(req.connection.localAddress, undefined); + + res.writeHead(200); + res.end('OK'); +})).listen(common.PIPE, common.mustCall(() => { + http.get({ socketPath: common.PIPE, path: '/' }, common.mustCall((res) => { + res.resume(); + res.on('end', common.mustSucceed(() => { + process.send('DONE'); + process.exit(); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-cluster-invalid-message.js b/test/js/node/test/parallel/test-cluster-invalid-message.js new file mode 100644 index 0000000000..a42f5284db --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-invalid-message.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isPrimary) { + const worker = cluster.fork(); + + worker.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + })); + + worker.on('online', () => { + worker.send({ + cmd: 'NODE_CLUSTER', + ack: -1 + }, () => { + worker.disconnect(); + }); + }); +} diff --git a/test/js/node/test/parallel/test-cluster-ipc-throw.js b/test/js/node/test/parallel/test-cluster-ipc-throw.js new file mode 100644 index 0000000000..c9640a23fc --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-ipc-throw.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); +const cluster = require('cluster'); +const assert = require('assert'); + +cluster.schedulingPolicy = cluster.SCHED_RR; + +const server = http.createServer(); + +if (cluster.isPrimary) { + server.listen({ port: 0 }, common.mustCall(() => { + const worker = cluster.fork({ PORT: server.address().port }); + worker.on('exit', common.mustCall(() => { + server.close(); + })); + })); +} else { + assert(process.env.PORT); + process.on('uncaughtException', common.mustCall()); + server.listen(process.env.PORT); + server.on('error', common.mustCall((e) => { + cluster.worker.disconnect(); + throw e; + })); +} diff --git a/test/js/node/test/parallel/test-cluster-kill-disconnect.js b/test/js/node/test/parallel/test-cluster-kill-disconnect.js new file mode 100644 index 0000000000..3e1f2f0841 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-kill-disconnect.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); + +// Check that cluster works perfectly for both `kill` and `disconnect` cases. +// Also take into account that the `disconnect` event may be received after the +// `exit` event. +// https://github.com/nodejs/node/issues/3238 + +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isPrimary) { + function forkWorker(action) { + const worker = cluster.fork({ action }); + worker.on('disconnect', common.mustCall(() => { + assert.strictEqual(worker.exitedAfterDisconnect, true); + })); + + worker.on('exit', common.mustCall(() => { + assert.strictEqual(worker.exitedAfterDisconnect, true); + })); + } + + forkWorker('disconnect'); + forkWorker('kill'); +} else { + cluster.worker[process.env.action](); +} diff --git a/test/js/node/test/parallel/test-cluster-kill-infinite-loop.js b/test/js/node/test/parallel/test-cluster-kill-infinite-loop.js new file mode 100644 index 0000000000..57781b6972 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-kill-infinite-loop.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); +const cluster = require('cluster'); +const assert = require('assert'); + +if (cluster.isPrimary) { + const worker = cluster.fork(); + + worker.on('online', common.mustCall(() => { + // Use worker.process.kill() instead of worker.kill() because the latter + // waits for a graceful disconnect, which will never happen. + worker.process.kill(); + })); + + worker.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, null); + assert.strictEqual(signal, 'SIGTERM'); + })); +} else { + while (true); +} diff --git a/test/js/node/test/parallel/test-cluster-listening-port.js b/test/js/node/test/parallel/test-cluster-listening-port.js new file mode 100644 index 0000000000..c09134e1de --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-listening-port.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const net = require('net'); + +if (cluster.isPrimary) { + cluster.fork(); + cluster.on('listening', common.mustCall(function(worker, address) { + const port = address.port; + // Ensure that the port is not 0 or null + assert(port); + // Ensure that the port is numerical + assert.strictEqual(typeof port, 'number'); + worker.kill(); + })); +} else { + net.createServer(common.mustNotCall()).listen(0); +} diff --git a/test/js/node/test/parallel/test-cluster-net-listen.js b/test/js/node/test/parallel/test-cluster-net-listen.js new file mode 100644 index 0000000000..9fa975aaaf --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-net-listen.js @@ -0,0 +1,36 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const net = require('net'); + +if (cluster.isPrimary) { + // Ensure that the worker exits peacefully + cluster.fork().on('exit', common.mustCall(function(statusCode) { + assert.strictEqual(statusCode, 0); + })); +} else { + // listen() without port should not trigger a libuv assert + net.createServer(common.mustNotCall()).listen(process.exit); +} diff --git a/test/js/node/test/parallel/test-cluster-primary-error.js b/test/js/node/test/parallel/test-cluster-primary-error.js new file mode 100644 index 0000000000..f48682da4e --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-primary-error.js @@ -0,0 +1,104 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +const totalWorkers = 2; + +// Cluster setup +if (cluster.isWorker) { + const http = require('http'); + http.Server(() => {}).listen(0, '127.0.0.1'); +} else if (process.argv[2] === 'cluster') { + // Send PID to testcase process + let forkNum = 0; + cluster.on('fork', common.mustCall(function forkEvent(worker) { + // Send PID + process.send({ + cmd: 'worker', + workerPID: worker.process.pid + }); + + // Stop listening when done + if (++forkNum === totalWorkers) { + cluster.removeListener('fork', forkEvent); + } + }, totalWorkers)); + + // Throw accidental error when all workers are listening + let listeningNum = 0; + cluster.on('listening', common.mustCall(function listeningEvent() { + // When all workers are listening + if (++listeningNum === totalWorkers) { + // Stop listening + cluster.removeListener('listening', listeningEvent); + + // Throw accidental error + process.nextTick(() => { + throw new Error('accidental error'); + }); + } + }, totalWorkers)); + + // Startup a basic cluster + cluster.fork(); + cluster.fork(); +} else { + // This is the testcase + + const fork = require('child_process').fork; + + // List all workers + const workers = []; + + // Spawn a cluster process + const primary = fork(process.argv[1], ['cluster'], { silent: true }); + + // Handle messages from the cluster + primary.on('message', common.mustCall((data) => { + // Add worker pid to list and progress tracker + if (data.cmd === 'worker') { + workers.push(data.workerPID); + } + }, totalWorkers)); + + // When cluster is dead + primary.on('exit', common.mustCall((code) => { + // Check that the cluster died accidentally (non-zero exit code) + assert.strictEqual(code, 1); + + // XXX(addaleax): The fact that this uses raw PIDs makes the test inherently + // flaky – another process might end up being started right after the + // workers finished and receive the same PID. + const pollWorkers = () => { + // When primary is dead all workers should be dead too + if (workers.some((pid) => common.isAlive(pid))) { + setTimeout(pollWorkers, 50); + } + }; + + // Loop indefinitely until worker exit + pollWorkers(); + })); +} diff --git a/test/js/node/test/parallel/test-cluster-primary-kill.js b/test/js/node/test/parallel/test-cluster-primary-kill.js new file mode 100644 index 0000000000..08c7809603 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-primary-kill.js @@ -0,0 +1,89 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isWorker) { + + // Keep the worker alive + const http = require('http'); + http.Server().listen(0, '127.0.0.1'); + +} else if (process.argv[2] === 'cluster') { + + const worker = cluster.fork(); + + // send PID info to testcase process + process.send({ + pid: worker.process.pid + }); + + // Terminate the cluster process + worker.once('listening', common.mustCall(() => { + setTimeout(() => { + process.exit(0); + }, 1000); + })); + +} else { + + // This is the testcase + const fork = require('child_process').fork; + + // Spawn a cluster process + const primary = fork(process.argv[1], ['cluster']); + + // get pid info + let pid = null; + primary.once('message', (data) => { + pid = data.pid; + }); + + // When primary is dead + let alive = true; + primary.on('exit', common.mustCall((code) => { + + // Make sure that the primary died on purpose + assert.strictEqual(code, 0); + + // Check worker process status + const pollWorker = () => { + alive = common.isAlive(pid); + if (alive) { + setTimeout(pollWorker, 50); + } + }; + // Loop indefinitely until worker exit. + pollWorker(); + })); + + process.once('exit', () => { + assert.strictEqual(typeof pid, 'number', + `got ${pid} instead of a worker pid`); + assert.strictEqual(alive, false, + `worker was alive after primary died (alive = ${alive})` + ); + }); + +} diff --git a/test/js/node/test/parallel/test-cluster-process-disconnect.js b/test/js/node/test/parallel/test-cluster-process-disconnect.js new file mode 100644 index 0000000000..378c4ef24d --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-process-disconnect.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isPrimary) { + const worker = cluster.fork(); + worker.on('exit', common.mustCall((code, signal) => { + assert.strictEqual( + code, + 0, + `Worker did not exit normally with code: ${code}` + ); + assert.strictEqual( + signal, + null, + `Worker did not exit normally with signal: ${signal}` + ); + })); +} else { + const net = require('net'); + const server = net.createServer(); + server.listen(0, common.mustCall(() => { + process.disconnect(); + })); +} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-rr-domain-listen.js b/test/js/node/test/parallel/test-cluster-rr-domain-listen.js similarity index 85% rename from test/js/node/cluster/upstream/parallel/test-cluster-rr-domain-listen.js rename to test/js/node/test/parallel/test-cluster-rr-domain-listen.js index c48ed0c55c..6043535d3b 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-rr-domain-listen.js +++ b/test/js/node/test/parallel/test-cluster-rr-domain-listen.js @@ -19,10 +19,10 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -require("../common"); -const cluster = require("cluster"); -const domain = require("domain"); +'use strict'; +require('../common'); +const cluster = require('cluster'); +const domain = require('domain'); // RR is the default for v0.11.9+ so the following line is redundant: // cluster.schedulingPolicy = cluster.SCHED_RR; @@ -31,16 +31,18 @@ if (cluster.isWorker) { const d = domain.create(); d.run(() => {}); - const http = require("http"); - http.Server(() => {}).listen(0, "127.0.0.1"); + const http = require('http'); + http.Server(() => {}).listen(0, '127.0.0.1'); + } else if (cluster.isPrimary) { + // Kill worker when listening - cluster.on("listening", function () { + cluster.on('listening', function() { worker.kill(); }); // Kill process when worker is killed - cluster.on("exit", function () { + cluster.on('exit', function() { process.exit(0); }); diff --git a/test/js/node/test/parallel/test-cluster-rr-handle-keep-loop-alive.js b/test/js/node/test/parallel/test-cluster-rr-handle-keep-loop-alive.js new file mode 100644 index 0000000000..0b18408a19 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-rr-handle-keep-loop-alive.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +const cluster = require('cluster'); +const net = require('net'); +const assert = require('assert'); + +cluster.schedulingPolicy = cluster.SCHED_RR; + +if (cluster.isPrimary) { + let exited = false; + const worker = cluster.fork(); + worker.on('exit', () => { + exited = true; + }); + setTimeout(() => { + assert.ok(!exited); + worker.kill(); + }, 3000); +} else { + const server = net.createServer(common.mustNotCall()); + server.listen(0, common.mustCall(() => process.channel.unref())); +} diff --git a/test/js/node/test/parallel/test-cluster-rr-ref.js b/test/js/node/test/parallel/test-cluster-rr-ref.js new file mode 100644 index 0000000000..92bb673ccd --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-rr-ref.js @@ -0,0 +1,20 @@ +'use strict'; + +const common = require('../common'); +const cluster = require('cluster'); +const net = require('net'); + +if (cluster.isPrimary) { + cluster.fork().on('message', function(msg) { + if (msg === 'done') this.kill(); + }); +} else { + const server = net.createServer(common.mustNotCall()); + server.listen(0, function() { + server.unref(); + server.ref(); + server.close(function() { + process.send('done'); + }); + }); +} diff --git a/test/js/node/test/parallel/test-cluster-send-deadlock.js b/test/js/node/test/parallel/test-cluster-send-deadlock.js new file mode 100644 index 0000000000..8ddc40c252 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-send-deadlock.js @@ -0,0 +1,73 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Testing mutual send of handles: from primary to worker, and from worker to +// primary. + +require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const net = require('net'); + +if (cluster.isPrimary) { + const worker = cluster.fork(); + worker.on('exit', (code, signal) => { + assert.strictEqual(code, 0, `Worker exited with an error code: ${code}`); + assert(!signal, `Worker exited by a signal: ${signal}`); + server.close(); + }); + + const server = net.createServer((socket) => { + worker.send('handle', socket); + }); + + server.listen(0, () => { + worker.send({ message: 'listen', port: server.address().port }); + }); +} else { + process.on('message', (msg, handle) => { + if (msg.message && msg.message === 'listen') { + assert(msg.port); + const client1 = net.connect({ + host: 'localhost', + port: msg.port + }, () => { + const client2 = net.connect({ + host: 'localhost', + port: msg.port + }, () => { + client1.on('close', onclose); + client2.on('close', onclose); + client1.end(); + client2.end(); + }); + }); + let waiting = 2; + const onclose = () => { + if (--waiting === 0) + cluster.worker.disconnect(); + }; + } else { + process.send('reply', handle); + } + }); +} diff --git a/test/js/node/test/parallel/test-cluster-setup-primary-argv.js b/test/js/node/test/parallel/test-cluster-setup-primary-argv.js new file mode 100644 index 0000000000..4c465bb72a --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-setup-primary-argv.js @@ -0,0 +1,39 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +setTimeout(common.mustNotCall('setup not emitted'), 1000).unref(); + +cluster.on('setup', common.mustCall(function() { + const clusterArgs = cluster.settings.args; + const realArgs = process.argv; + assert.strictEqual(clusterArgs[clusterArgs.length - 1], + realArgs[realArgs.length - 1]); +})); + +assert.notStrictEqual(process.argv[process.argv.length - 1], 'OMG,OMG'); +process.argv.push('OMG,OMG'); +process.argv.push('OMG,OMG'); +cluster.setupPrimary(); diff --git a/test/js/node/test/parallel/test-cluster-setup-primary-cumulative.js b/test/js/node/test/parallel/test-cluster-setup-primary-cumulative.js new file mode 100644 index 0000000000..cf62291e9d --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-setup-primary-cumulative.js @@ -0,0 +1,62 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +assert(cluster.isPrimary); + +// cluster.settings should not be initialized until needed +assert.deepStrictEqual(cluster.settings, {}); + +cluster.setupPrimary(); +assert.deepStrictEqual(cluster.settings, { + args: process.argv.slice(2), + exec: process.argv[1], + execArgv: process.execArgv, + silent: false, +}); +console.log('ok sets defaults'); + +cluster.setupPrimary({ exec: 'overridden' }); +assert.strictEqual(cluster.settings.exec, 'overridden'); +console.log('ok overrides defaults'); + +cluster.setupPrimary({ args: ['foo', 'bar'] }); +assert.strictEqual(cluster.settings.exec, 'overridden'); +assert.deepStrictEqual(cluster.settings.args, ['foo', 'bar']); + +cluster.setupPrimary({ execArgv: ['baz', 'bang'] }); +assert.strictEqual(cluster.settings.exec, 'overridden'); +assert.deepStrictEqual(cluster.settings.args, ['foo', 'bar']); +assert.deepStrictEqual(cluster.settings.execArgv, ['baz', 'bang']); +console.log('ok preserves unchanged settings on repeated calls'); + +cluster.setupPrimary(); +assert.deepStrictEqual(cluster.settings, { + args: ['foo', 'bar'], + exec: 'overridden', + execArgv: ['baz', 'bang'], + silent: false, +}); +console.log('ok preserves current settings'); diff --git a/test/js/node/test/parallel/test-cluster-setup-primary-emit.js b/test/js/node/test/parallel/test-cluster-setup-primary-emit.js new file mode 100644 index 0000000000..08414d5b21 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-setup-primary-emit.js @@ -0,0 +1,47 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +assert(cluster.isPrimary); + +function emitAndCatch(next) { + cluster.once('setup', common.mustCall(function(settings) { + assert.strictEqual(settings.exec, 'new-exec'); + setImmediate(next); + })); + cluster.setupPrimary({ exec: 'new-exec' }); +} + +function emitAndCatch2(next) { + cluster.once('setup', common.mustCall(function(settings) { + assert('exec' in settings); + setImmediate(next); + })); + cluster.setupPrimary(); +} + +emitAndCatch(common.mustCall(function() { + emitAndCatch2(common.mustCall()); +})); diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-multiple.js b/test/js/node/test/parallel/test-cluster-setup-primary-multiple.js similarity index 83% rename from test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-multiple.js rename to test/js/node/test/parallel/test-cluster-setup-primary-multiple.js index 381642cf58..0fd8c0943c 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-setup-primary-multiple.js +++ b/test/js/node/test/parallel/test-cluster-setup-primary-multiple.js @@ -19,11 +19,11 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); -const debug = require("util").debuglog("test"); +'use strict'; +require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const debug = require('util').debuglog('test'); assert(cluster.isPrimary); @@ -31,19 +31,23 @@ assert(cluster.isPrimary); // makes that unnecessary. This is to make the test less fragile if the // implementation ever changes such that cluster.settings is mutated instead of // replaced. -const cheapClone = obj => JSON.parse(JSON.stringify(obj)); +const cheapClone = (obj) => JSON.parse(JSON.stringify(obj)); const configs = []; // Capture changes -cluster.on("setup", () => { +cluster.on('setup', () => { debug(`"setup" emitted ${JSON.stringify(cluster.settings)}`); configs.push(cheapClone(cluster.settings)); }); -const execs = ["node-next", "node-next-2", "node-next-3"]; +const execs = [ + 'node-next', + 'node-next-2', + 'node-next-3', +]; -process.on("exit", () => { +process.on('exit', () => { // Tests that "setup" is emitted for every call to setupPrimary assert.strictEqual(configs.length, execs.length); @@ -61,9 +65,6 @@ execs.forEach((v, i) => { // Cluster emits 'setup' asynchronously, so we must stay alive long // enough for that to happen -setTimeout( - () => { - debug("cluster setup complete"); - }, - (execs.length + 1) * 100, -); +setTimeout(() => { + debug('cluster setup complete'); +}, (execs.length + 1) * 100); diff --git a/test/js/node/test/parallel/test-cluster-setup-primary.js b/test/js/node/test/parallel/test-cluster-setup-primary.js new file mode 100644 index 0000000000..efba017fd7 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-setup-primary.js @@ -0,0 +1,93 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isWorker) { + + // Just keep the worker alive + process.send(process.argv[2]); + +} else if (cluster.isPrimary) { + + const checks = { + args: false, + setupEvent: false, + settingsObject: false + }; + + const totalWorkers = 2; + let settings; + + // Setup primary + cluster.setupPrimary({ + args: ['custom argument'], + silent: true + }); + + cluster.once('setup', function() { + checks.setupEvent = true; + + settings = cluster.settings; + if (settings && + settings.args && settings.args[0] === 'custom argument' && + settings.silent === true && + settings.exec === process.argv[1]) { + checks.settingsObject = true; + } + }); + + let correctInput = 0; + + cluster.on('online', common.mustCall(function listener(worker) { + + worker.once('message', function(data) { + correctInput += (data === 'custom argument' ? 1 : 0); + if (correctInput === totalWorkers) { + checks.args = true; + } + worker.kill(); + }); + + }, totalWorkers)); + + // Start all workers + cluster.fork(); + cluster.fork(); + + // Check all values + process.once('exit', function() { + const argsMsg = 'Arguments was not send for one or more worker. ' + + `${correctInput} workers receive argument, ` + + `but ${totalWorkers} were expected.`; + assert.ok(checks.args, argsMsg); + + assert.ok(checks.setupEvent, 'The setup event was never emitted'); + + const settingObjectMsg = 'The settingsObject do not have correct ' + + `properties : ${JSON.stringify(settings)}`; + assert.ok(checks.settingsObject, settingObjectMsg); + }); + +} diff --git a/test/js/node/test/parallel/test-cluster-shared-handle-bind-privileged-port.js b/test/js/node/test/parallel/test-cluster-shared-handle-bind-privileged-port.js new file mode 100644 index 0000000000..8bdde0a332 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-shared-handle-bind-privileged-port.js @@ -0,0 +1,55 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +// Skip on macOS Mojave. https://github.com/nodejs/node/issues/21679 +if (common.isMacOS) + common.skip('macOS may allow ordinary processes to use any port'); + +if (common.isIBMi) + common.skip('IBMi may allow ordinary processes to use any port'); + +if (common.isWindows) + common.skip('not reliable on Windows'); + +if (process.getuid() === 0) + common.skip('as this test should not be run as `root`'); + +const assert = require('assert'); +const cluster = require('cluster'); +const net = require('net'); + +if (cluster.isPrimary) { + // Primary opens and binds the socket and shares it with the worker. + cluster.schedulingPolicy = cluster.SCHED_NONE; + cluster.fork().on('exit', common.mustCall(function(exitCode) { + assert.strictEqual(exitCode, 0); + })); +} else { + const s = net.createServer(common.mustNotCall()); + s.listen(42, common.mustNotCall('listen should have failed')); + s.on('error', common.mustCall(function(err) { + assert.strictEqual(err.code, 'EACCES'); + process.disconnect(); + })); +} diff --git a/test/js/node/test/parallel/test-cluster-uncaught-exception.js b/test/js/node/test/parallel/test-cluster-uncaught-exception.js new file mode 100644 index 0000000000..80d1ec6118 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-uncaught-exception.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Installing a custom uncaughtException handler should override the default +// one that the cluster module installs. +// https://github.com/joyent/node/issues/2556 + +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const fork = require('child_process').fork; + +const MAGIC_EXIT_CODE = 42; + +const isTestRunner = process.argv[2] !== 'child'; + +if (isTestRunner) { + const primary = fork(__filename, ['child']); + primary.on('exit', common.mustCall((code) => { + assert.strictEqual(code, MAGIC_EXIT_CODE); + })); +} else if (cluster.isPrimary) { + process.on('uncaughtException', common.mustCall(() => { + process.nextTick(() => process.exit(MAGIC_EXIT_CODE)); + })); + cluster.fork(); + throw new Error('kill primary'); +} else { // worker + process.exit(); +} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-constructor.js b/test/js/node/test/parallel/test-cluster-worker-constructor.js similarity index 88% rename from test/js/node/cluster/upstream/parallel/test-cluster-worker-constructor.js rename to test/js/node/test/parallel/test-cluster-worker-constructor.js index 904eb2e2ab..c116e622e5 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-constructor.js +++ b/test/js/node/test/parallel/test-cluster-worker-constructor.js @@ -19,28 +19,28 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; // test-cluster-worker-constructor.js // validates correct behavior of the cluster.Worker constructor -require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); let worker; worker = new cluster.Worker(); assert.strictEqual(worker.exitedAfterDisconnect, undefined); -assert.strictEqual(worker.state, "none"); +assert.strictEqual(worker.state, 'none'); assert.strictEqual(worker.id, 0); assert.strictEqual(worker.process, undefined); worker = new cluster.Worker({ id: 3, - state: "online", - process: process, + state: 'online', + process: process }); assert.strictEqual(worker.exitedAfterDisconnect, undefined); -assert.strictEqual(worker.state, "online"); +assert.strictEqual(worker.state, 'online'); assert.strictEqual(worker.id, 3); assert.strictEqual(worker.process, process); diff --git a/test/js/node/test/parallel/test-cluster-worker-death.js b/test/js/node/test/parallel/test-cluster-worker-death.js new file mode 100644 index 0000000000..700cae7c52 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-worker-death.js @@ -0,0 +1,38 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +if (!cluster.isPrimary) { + process.exit(42); +} else { + const worker = cluster.fork(); + worker.on('exit', common.mustCall(function(exitCode, signalCode) { + assert.strictEqual(exitCode, 42); + assert.strictEqual(signalCode, null); + })); + cluster.on('exit', common.mustCall(function(worker_) { + assert.strictEqual(worker_, worker); + })); +} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-destroy.js b/test/js/node/test/parallel/test-cluster-worker-destroy.js similarity index 85% rename from test/js/node/cluster/upstream/parallel/test-cluster-worker-destroy.js rename to test/js/node/test/parallel/test-cluster-worker-destroy.js index ebffb4fb04..91eee51a5a 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-destroy.js +++ b/test/js/node/test/parallel/test-cluster-worker-destroy.js @@ -19,7 +19,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; // The goal of this test is to cover the Workers' implementation of // Worker.prototype.destroy. Worker.prototype.destroy is called within @@ -27,22 +27,22 @@ // primary, and another time when it's not connected to it, so that we cover // both code paths. -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); let worker1, worker2; if (cluster.isPrimary) { worker1 = cluster.fork(); worker2 = cluster.fork(); - [worker1, worker2].forEach(function (worker) { - worker.on("disconnect", common.mustCall()); - worker.on("exit", common.mustCall()); + [worker1, worker2].forEach(function(worker) { + worker.on('disconnect', common.mustCall()); + worker.on('exit', common.mustCall()); }); } else if (cluster.worker.id === 1) { // Call destroy when worker is disconnected - cluster.worker.process.on("disconnect", function () { + cluster.worker.process.on('disconnect', function() { cluster.worker.destroy(); }); diff --git a/test/js/node/test/parallel/test-cluster-worker-disconnect-on-error.js b/test/js/node/test/parallel/test-cluster-worker-disconnect-on-error.js new file mode 100644 index 0000000000..122d31a62e --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-worker-disconnect-on-error.js @@ -0,0 +1,32 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); +const cluster = require('cluster'); +const assert = require('assert'); + +cluster.schedulingPolicy = cluster.SCHED_NONE; + +const server = http.createServer(); +if (cluster.isPrimary) { + let worker; + + server.listen(0, common.mustSucceed(() => { + assert(worker); + + worker.send({ port: server.address().port }); + })); + + worker = cluster.fork(); + worker.on('exit', common.mustCall(() => { + server.close(); + })); +} else { + process.on('message', common.mustCall((msg) => { + assert(msg.port); + + server.listen(msg.port); + server.on('error', common.mustCall((e) => { + cluster.worker.disconnect(); + })); + })); +} diff --git a/test/js/node/test/parallel/test-cluster-worker-disconnect.js b/test/js/node/test/parallel/test-cluster-worker-disconnect.js new file mode 100644 index 0000000000..b28c0fbd8f --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-worker-disconnect.js @@ -0,0 +1,106 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isWorker) { + const http = require('http'); + http.Server(() => { + + }).listen(0, '127.0.0.1'); + + cluster.worker.on('disconnect', common.mustCall(() => { + process.exit(42); + })); + +} else if (cluster.isPrimary) { + + const checks = { + cluster: { + emitDisconnect: false, + emitExit: false, + callback: false + }, + worker: { + emitDisconnect: false, + emitDisconnectInsideWorker: false, + emitExit: false, + state: false, + voluntaryMode: false, + died: false + } + }; + + // start worker + const worker = cluster.fork(); + + // Disconnect worker when it is ready + worker.once('listening', common.mustCall(() => { + const w = worker.disconnect(); + assert.strictEqual(worker, w, `${worker.id} did not return a reference`); + })); + + // Check cluster events + cluster.once('disconnect', common.mustCall(() => { + checks.cluster.emitDisconnect = true; + })); + cluster.once('exit', common.mustCall(() => { + checks.cluster.emitExit = true; + })); + + // Check worker events and properties + worker.once('disconnect', common.mustCall(() => { + checks.worker.emitDisconnect = true; + checks.worker.voluntaryMode = worker.exitedAfterDisconnect; + checks.worker.state = worker.state; + })); + + // Check that the worker died + worker.once('exit', common.mustCall((code) => { + checks.worker.emitExit = true; + checks.worker.died = !common.isAlive(worker.process.pid); + checks.worker.emitDisconnectInsideWorker = code === 42; + })); + + process.once('exit', () => { + + const w = checks.worker; + const c = checks.cluster; + + // events + assert.ok(w.emitDisconnect, 'Disconnect event did not emit'); + assert.ok(w.emitDisconnectInsideWorker, + 'Disconnect event did not emit inside worker'); + assert.ok(c.emitDisconnect, 'Disconnect event did not emit'); + assert.ok(w.emitExit, 'Exit event did not emit'); + assert.ok(c.emitExit, 'Exit event did not emit'); + + // flags + assert.strictEqual(w.state, 'disconnected'); + assert.strictEqual(w.voluntaryMode, true); + + // is process alive + assert.ok(w.died, 'The worker did not die'); + }); +} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-events.js b/test/js/node/test/parallel/test-cluster-worker-events.js similarity index 82% rename from test/js/node/cluster/upstream/parallel/test-cluster-worker-events.js rename to test/js/node/test/parallel/test-cluster-worker-events.js index aaf355a581..6c044ace8d 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-events.js +++ b/test/js/node/test/parallel/test-cluster-worker-events.js @@ -19,22 +19,23 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +'use strict'; +require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); const OK = 2; if (cluster.isPrimary) { + const worker = cluster.fork(); - worker.on("exit", code => { + worker.on('exit', (code) => { assert.strictEqual(code, OK); process.exit(0); }); - const result = worker.send("SOME MESSAGE"); + const result = worker.send('SOME MESSAGE'); assert.strictEqual(result, true); return; @@ -50,28 +51,28 @@ let sawWorker; const messages = []; -const check = m => { +const check = (m) => { messages.push(m); if (messages.length < 2) return; assert.deepStrictEqual(messages[0], messages[1]); - cluster.worker.once("error", e => { - assert.strictEqual(e, "HI"); + cluster.worker.once('error', (e) => { + assert.strictEqual(e, 'HI'); process.exit(OK); }); - process.emit("error", "HI"); + process.emit('error', 'HI'); }; -process.on("message", m => { +process.on('message', (m) => { assert(!sawProcess); sawProcess = true; check(m); }); -cluster.worker.on("message", m => { +cluster.worker.on('message', (m) => { assert(!sawWorker); sawWorker = true; check(m); diff --git a/test/js/node/test/parallel/test-cluster-worker-exit.js b/test/js/node/test/parallel/test-cluster-worker-exit.js new file mode 100644 index 0000000000..09e2a83701 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-worker-exit.js @@ -0,0 +1,130 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// test-cluster-worker-exit.js +// verifies that, when a child process exits (by calling `process.exit(code)`) +// - the primary receives the proper events in the proper order, no duplicates +// - the exitCode and signalCode are correct in the 'exit' event +// - the worker.exitedAfterDisconnect flag, and worker.state are correct +// - the worker process actually goes away + +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +const EXIT_CODE = 42; + +if (cluster.isWorker) { + const http = require('http'); + const server = http.Server(() => { }); + + server.once('listening', common.mustCall(() => { + process.exit(EXIT_CODE); + })); + server.listen(0, '127.0.0.1'); + +} else if (cluster.isPrimary) { + + const expected_results = { + cluster_emitDisconnect: [1, "the cluster did not emit 'disconnect'"], + cluster_emitExit: [1, "the cluster did not emit 'exit'"], + cluster_exitCode: [EXIT_CODE, 'the cluster exited w/ incorrect exitCode'], + cluster_signalCode: [null, 'the cluster exited w/ incorrect signalCode'], + worker_emitDisconnect: [1, "the worker did not emit 'disconnect'"], + worker_emitExit: [1, "the worker did not emit 'exit'"], + worker_state: ['disconnected', 'the worker state is incorrect'], + worker_exitedAfterDisconnect: [ + false, 'the .exitedAfterDisconnect flag is incorrect', + ], + worker_died: [true, 'the worker is still running'], + worker_exitCode: [EXIT_CODE, 'the worker exited w/ incorrect exitCode'], + worker_signalCode: [null, 'the worker exited w/ incorrect signalCode'] + }; + const results = { + cluster_emitDisconnect: 0, + cluster_emitExit: 0, + worker_emitDisconnect: 0, + worker_emitExit: 0 + }; + + + // start worker + const worker = cluster.fork(); + + // Check cluster events + cluster.on('disconnect', common.mustCall(() => { + results.cluster_emitDisconnect += 1; + })); + cluster.on('exit', common.mustCall((worker) => { + results.cluster_exitCode = worker.process.exitCode; + results.cluster_signalCode = worker.process.signalCode; + results.cluster_emitExit += 1; + })); + + // Check worker events and properties + worker.on('disconnect', common.mustCall(() => { + results.worker_emitDisconnect += 1; + results.worker_exitedAfterDisconnect = worker.exitedAfterDisconnect; + results.worker_state = worker.state; + if (results.worker_emitExit > 0) { + process.nextTick(() => finish_test()); + } + })); + + // Check that the worker died + worker.once('exit', common.mustCall((exitCode, signalCode) => { + results.worker_exitCode = exitCode; + results.worker_signalCode = signalCode; + results.worker_emitExit += 1; + results.worker_died = !common.isAlive(worker.process.pid); + if (results.worker_emitDisconnect > 0) { + process.nextTick(() => finish_test()); + } + })); + + const finish_test = () => { + try { + checkResults(expected_results, results); + } catch (exc) { + if (exc.name !== 'AssertionError') { + console.trace(exc); + } + + process.exit(1); + return; + } + process.exit(0); + }; +} + +// Some helper functions ... + +function checkResults(expected_results, results) { + for (const k in expected_results) { + const actual = results[k]; + const expected = expected_results[k]; + + assert.strictEqual( + actual, expected && expected.length ? expected[0] : expected, + `${expected[1] || ''} [expected: ${expected[0]} / actual: ${actual}]`); + } +} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-forced-exit.js b/test/js/node/test/parallel/test-cluster-worker-forced-exit.js similarity index 76% rename from test/js/node/cluster/upstream/parallel/test-cluster-worker-forced-exit.js rename to test/js/node/test/parallel/test-cluster-worker-forced-exit.js index 901868973d..6d2bf4f537 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-forced-exit.js +++ b/test/js/node/test/parallel/test-cluster-worker-forced-exit.js @@ -19,10 +19,10 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); const SENTINEL = 42; @@ -37,7 +37,7 @@ const SENTINEL = 42; // 3 disconnect worker with child_process's disconnect, confirm // no sentinel value if (cluster.isWorker) { - process.on("disconnect", msg => { + process.on('disconnect', (msg) => { setTimeout(() => process.exit(SENTINEL), 10); }); return; @@ -49,27 +49,15 @@ checkForced(); function checkUnforced() { const worker = cluster.fork(); worker - .on( - "online", - common.mustCall(() => worker.disconnect()), - ) - .on( - "exit", - common.mustCall(status => { - assert.strictEqual(status, SENTINEL); - }), - ); + .on('online', common.mustCall(() => worker.disconnect())) + .on('exit', common.mustCall((status) => { + assert.strictEqual(status, SENTINEL); + })); } function checkForced() { const worker = cluster.fork(); worker - .on( - "online", - common.mustCall(() => worker.process.disconnect()), - ) - .on( - "exit", - common.mustCall(status => assert.strictEqual(status, 0)), - ); + .on('online', common.mustCall(() => worker.process.disconnect())) + .on('exit', common.mustCall((status) => assert.strictEqual(status, 0))); } diff --git a/test/js/node/test/parallel/test-cluster-worker-handle-close.js b/test/js/node/test/parallel/test-cluster-worker-handle-close.js new file mode 100644 index 0000000000..47a80ef1cd --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-worker-handle-close.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); +const cluster = require('cluster'); +const net = require('net'); + +if (cluster.isPrimary) { + cluster.schedulingPolicy = cluster.SCHED_RR; + cluster.fork(); +} else { + const server = net.createServer(common.mustNotCall()); + server.listen(0, common.mustCall(() => { + net.connect(server.address().port); + })); + process.prependListener('internalMessage', common.mustCallAtLeast((message, handle) => { + if (message.act !== 'newconn') { + return; + } + // Make the worker drops the connection, see `rr` and `onconnection` in child.js + server.close(); + const close = handle.close; + handle.close = common.mustCall(() => { + close.call(handle, common.mustCall(() => { + process.exit(); + })); + }); + })); +} diff --git a/test/js/node/cluster/upstream/parallel/test-cluster-worker-init.js b/test/js/node/test/parallel/test-cluster-worker-init.js similarity index 78% rename from test/js/node/cluster/upstream/parallel/test-cluster-worker-init.js rename to test/js/node/test/parallel/test-cluster-worker-init.js index f2dafdf66d..2acc55c148 100644 --- a/test/js/node/cluster/upstream/parallel/test-cluster-worker-init.js +++ b/test/js/node/test/parallel/test-cluster-worker-init.js @@ -19,34 +19,31 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -"use strict"; +'use strict'; // test-cluster-worker-init.js // verifies that, when a child process is forked, the cluster.worker // object can receive messages as expected -const common = require("../common"); -const assert = require("assert"); -const cluster = require("cluster"); -const msg = "foo"; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const msg = 'foo'; if (cluster.isPrimary) { const worker = cluster.fork(); - worker.on( - "message", - common.mustCall(message => { - assert.strictEqual(message, true); - const w = worker.disconnect(); - assert.strictEqual(worker, w); - }), - ); + worker.on('message', common.mustCall((message) => { + assert.strictEqual(message, true); + const w = worker.disconnect(); + assert.strictEqual(worker, w); + })); - worker.on("online", () => { + worker.on('online', () => { worker.send(msg); }); } else { // https://github.com/nodejs/node-v0.x-archive/issues/7998 - cluster.worker.on("message", message => { + cluster.worker.on('message', (message) => { process.send(message === msg); }); } diff --git a/test/js/node/test/parallel/test-cluster-worker-isdead.js b/test/js/node/test/parallel/test-cluster-worker-isdead.js new file mode 100644 index 0000000000..6f2aa3c52e --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-worker-isdead.js @@ -0,0 +1,32 @@ +'use strict'; +require('../common'); +const cluster = require('cluster'); +const assert = require('assert'); + +if (cluster.isPrimary) { + const worker = cluster.fork(); + let workerDead = worker.isDead(); + assert.ok(!workerDead, + `isDead() returned ${workerDead}. isDead() should return ` + + 'false right after the worker has been created.'); + + worker.on('exit', function() { + workerDead = worker.isDead(); + assert.ok(workerDead, + `isDead() returned ${workerDead}. After an event has been ` + + 'emitted, isDead should return true'); + }); + + worker.on('message', function(msg) { + if (msg === 'readyToDie') { + worker.kill(); + } + }); + +} else if (cluster.isWorker) { + const workerDead = cluster.worker.isDead(); + assert.ok(!workerDead, + `isDead() returned ${workerDead}. isDead() should return ` + + 'false when called from within a worker'); + process.send('readyToDie'); +} diff --git a/test/js/node/test/parallel/test-cluster-worker-kill-signal.js b/test/js/node/test/parallel/test-cluster-worker-kill-signal.js new file mode 100644 index 0000000000..53e3739eba --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-worker-kill-signal.js @@ -0,0 +1,49 @@ +'use strict'; +// test-cluster-worker-kill-signal.js +// verifies that when we're killing a worker using Worker.prototype.kill +// and the worker's process was killed with the given signal (SIGKILL) + + +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isWorker) { + // Make the worker run something + const http = require('http'); + const server = http.Server(() => { }); + + server.once('listening', common.mustCall()); + server.listen(0, '127.0.0.1'); + +} else if (cluster.isMaster) { + const KILL_SIGNAL = 'SIGKILL'; + + // Start worker + const worker = cluster.fork(); + + // When the worker is up and running, kill it + worker.once('listening', common.mustCall(() => { + worker.kill(KILL_SIGNAL); + })); + + // Check worker events and properties + worker.on('disconnect', common.mustCall(() => { + assert.strictEqual(worker.exitedAfterDisconnect, false); + assert.strictEqual(worker.state, 'disconnected'); + }, 1)); + + // Check that the worker died + worker.once('exit', common.mustCall((exitCode, signalCode) => { + const isWorkerProcessStillAlive = common.isAlive(worker.process.pid); + const numOfRunningWorkers = Object.keys(cluster.workers).length; + + assert.strictEqual(exitCode, null); + assert.strictEqual(signalCode, KILL_SIGNAL); + assert.strictEqual(isWorkerProcessStillAlive, false); + assert.strictEqual(numOfRunningWorkers, 0); + }, 1)); + + // Check if the cluster was killed as well + cluster.on('exit', common.mustCall(1)); +} diff --git a/test/js/node/test/parallel/test-cluster-worker-kill.js b/test/js/node/test/parallel/test-cluster-worker-kill.js new file mode 100644 index 0000000000..7307a93e1b --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-worker-kill.js @@ -0,0 +1,117 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// test-cluster-worker-kill.js +// verifies that, when a child process is killed (we use SIGKILL) +// - the primary receives the proper events in the proper order, no duplicates +// - the exitCode and signalCode are correct in the 'exit' event +// - the worker.exitedAfterDisconnect flag, and worker.state are correct +// - the worker process actually goes away + +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isWorker) { + const http = require('http'); + const server = http.Server(() => { }); + + server.once('listening', common.mustCall()); + server.listen(0, '127.0.0.1'); + +} else if (cluster.isPrimary) { + + const KILL_SIGNAL = 'SIGKILL'; + const expected_results = { + cluster_emitDisconnect: [1, "the cluster did not emit 'disconnect'"], + cluster_emitExit: [1, "the cluster did not emit 'exit'"], + cluster_exitCode: [null, 'the cluster exited w/ incorrect exitCode'], + cluster_signalCode: [KILL_SIGNAL, + 'the cluster exited w/ incorrect signalCode'], + worker_emitDisconnect: [1, "the worker did not emit 'disconnect'"], + worker_emitExit: [1, "the worker did not emit 'exit'"], + worker_state: ['disconnected', 'the worker state is incorrect'], + worker_exitedAfter: [false, 'the .exitedAfterDisconnect flag is incorrect'], + worker_died: [true, 'the worker is still running'], + worker_exitCode: [null, 'the worker exited w/ incorrect exitCode'], + worker_signalCode: [KILL_SIGNAL, + 'the worker exited w/ incorrect signalCode'] + }; + const results = { + cluster_emitDisconnect: 0, + cluster_emitExit: 0, + worker_emitDisconnect: 0, + worker_emitExit: 0 + }; + + + // start worker + const worker = cluster.fork(); + + // When the worker is up and running, kill it + worker.once('listening', common.mustCall(() => { + worker.process.kill(KILL_SIGNAL); + })); + + + // Check cluster events + cluster.on('disconnect', common.mustCall(() => { + results.cluster_emitDisconnect += 1; + })); + cluster.on('exit', common.mustCall((worker) => { + results.cluster_exitCode = worker.process.exitCode; + results.cluster_signalCode = worker.process.signalCode; + results.cluster_emitExit += 1; + })); + + // Check worker events and properties + worker.on('disconnect', common.mustCall(() => { + results.worker_emitDisconnect += 1; + results.worker_exitedAfter = worker.exitedAfterDisconnect; + results.worker_state = worker.state; + })); + + // Check that the worker died + worker.once('exit', common.mustCall((exitCode, signalCode) => { + results.worker_exitCode = exitCode; + results.worker_signalCode = signalCode; + results.worker_emitExit += 1; + results.worker_died = !common.isAlive(worker.process.pid); + })); + + process.on('exit', () => { + checkResults(expected_results, results); + }); +} + +// Some helper functions ... + +function checkResults(expected_results, results) { + for (const k in expected_results) { + const actual = results[k]; + const expected = expected_results[k]; + + assert.strictEqual( + actual, expected && expected.length ? expected[0] : expected, + `${expected[1] || ''} [expected: ${expected[0]} / actual: ${actual}]`); + } +} diff --git a/test/js/node/test/parallel/test-cluster-worker-no-exit.js b/test/js/node/test/parallel/test-cluster-worker-no-exit.js new file mode 100644 index 0000000000..e4694a4a3a --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-worker-no-exit.js @@ -0,0 +1,74 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const net = require('net'); + +let destroyed; +let success; +let worker; +let server; + +// Workers do not exit on disconnect, they exit under normal node rules: when +// they have nothing keeping their loop alive, like an active connection +// +// test this by: +// +// 1 creating a server, so worker can make a connection to something +// 2 disconnecting worker +// 3 wait to confirm it did not exit +// 4 destroy connection +// 5 confirm it does exit +if (cluster.isPrimary) { + server = net.createServer(function(conn) { + server.close(); + worker.disconnect(); + worker.once('disconnect', function() { + setTimeout(function() { + conn.destroy(); + destroyed = true; + }, 1000); + }).once('exit', function() { + // Worker should not exit while it has a connection + assert(destroyed, 'worker exited before connection destroyed'); + success = true; + }); + + }).listen(0, function() { + const port = this.address().port; + + worker = cluster.fork() + .on('online', function() { + this.send({ port }); + }); + }); + process.on('exit', function() { + assert(success); + }); +} else { + process.on('message', function(msg) { + // We shouldn't exit, not while a network connection exists + net.connect(msg.port); + }); +} diff --git a/test/js/node/test/parallel/test-cluster-worker-wait-server-close.js b/test/js/node/test/parallel/test-cluster-worker-wait-server-close.js new file mode 100644 index 0000000000..71a8cacb52 --- /dev/null +++ b/test/js/node/test/parallel/test-cluster-worker-wait-server-close.js @@ -0,0 +1,48 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const net = require('net'); + +let serverClosed = false; + +if (cluster.isWorker) { + const server = net.createServer(function(socket) { + // Wait for any data, then close connection + socket.write('.'); + socket.on('data', () => {}); + }).listen(0, common.localhostIPv4); + + server.once('close', function() { + serverClosed = true; + }); + + // Although not typical, the worker process can exit before the disconnect + // event fires. Use this to keep the process open until the event has fired. + const keepOpen = setInterval(() => {}, 9999); + + // Check worker events and properties + process.once('disconnect', function() { + // Disconnect should occur after socket close + assert(serverClosed); + clearInterval(keepOpen); + }); +} else if (cluster.isPrimary) { + // start worker + const worker = cluster.fork(); + + // Disconnect worker when it is ready + worker.once('listening', function(address) { + const socket = net.createConnection(address.port, common.localhostIPv4); + + socket.on('connect', function() { + socket.on('data', function() { + console.log('got data from client'); + // Socket definitely connected to worker if we got data + worker.disconnect(); + socket.end(); + }); + }); + }); +} diff --git a/test/js/node/test/parallel/test-common-countdown.js b/test/js/node/test/parallel/test-common-countdown.js new file mode 100644 index 0000000000..d3c0daf599 --- /dev/null +++ b/test/js/node/test/parallel/test-common-countdown.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const Countdown = require('../common/countdown'); +const fixtures = require('../common/fixtures'); +const { execFile } = require('child_process'); + +let done = ''; +const countdown = new Countdown(2, () => done = true); +assert.strictEqual(countdown.remaining, 2); +countdown.dec(); +assert.strictEqual(countdown.remaining, 1); +countdown.dec(); +assert.strictEqual(countdown.remaining, 0); +assert.strictEqual(done, true); + +const failFixtures = [ + [ + fixtures.path('failcounter.js'), + 'Mismatched function calls. Expected exactly 1, actual 0.', + ], +]; + +for (const p of failFixtures) { + const [file, expected] = p; + execFile(process.argv[0], [file], common.mustCall((ex, stdout, stderr) => { + assert.ok(ex); + assert.strictEqual(stderr, ''); + const firstLine = stdout.split('\n').shift(); + assert.strictEqual(firstLine, expected); + })); +} diff --git a/test/js/node/test/parallel/test-common-must-not-call.js b/test/js/node/test/parallel/test-common-must-not-call.js new file mode 100644 index 0000000000..b3c94a2390 --- /dev/null +++ b/test/js/node/test/parallel/test-common-must-not-call.js @@ -0,0 +1,56 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const util = require('util'); + +const message = 'message'; +const testFunction1 = common.mustNotCall(message); + +const testFunction2 = common.mustNotCall(message); + +const createValidate = (line, args = []) => common.mustCall((e) => { + const prefix = `${message} at `; + assert.ok(e.message.startsWith(prefix)); + if (process.platform === 'win32') { + e.message = e.message.substring(2); // remove 'C:' + } + const msg = e.message.substring(prefix.length); + const firstColon = msg.indexOf(':'); + const fileName = msg.substring(0, firstColon); + const rest = msg.substring(firstColon + 1); + assert.strictEqual(path.basename(fileName), 'test-common-must-not-call.js'); + const argsInfo = args.length > 0 ? + `\ncalled with arguments: ${args.map(util.inspect).join(', ')}` : ''; + assert.strictEqual(rest, line + argsInfo); +}); + +const validate1 = createValidate('9'); +try { + testFunction1(); +} catch (e) { + validate1(e); +} + +const validate2 = createValidate('11', ['hello', 42]); +try { + testFunction2('hello', 42); +} catch (e) { + validate2(e); +} + +assert.throws( + () => new Proxy({ prop: Symbol() }, { get: common.mustNotCall() }).prop, + { code: 'ERR_ASSERTION' } +); + +{ + const { inspect } = util; + delete util.inspect; + assert.throws( + () => common.mustNotCall()(null), + { code: 'ERR_ASSERTION' } + ); + util.inspect = inspect; +} diff --git a/test/js/node/test/parallel/test-console-assign-undefined.js b/test/js/node/test/parallel/test-console-assign-undefined.js new file mode 100644 index 0000000000..1021307b3c --- /dev/null +++ b/test/js/node/test/parallel/test-console-assign-undefined.js @@ -0,0 +1,28 @@ +'use strict'; + +// Patch global.console before importing modules that may modify the console +// object. + +const tmp = global.console; +global.console = 42; + +require('../common'); +const assert = require('assert'); + +// Originally the console had a getter. Test twice to verify it had no side +// effect. +assert.strictEqual(global.console, 42); +assert.strictEqual(global.console, 42); + +assert.throws( + () => console.log('foo'), + { name: 'TypeError' } +); + +global.console = 1; +assert.strictEqual(global.console, 1); +assert.strictEqual(console, 1); + +// Reset the console +global.console = tmp; +console.log('foo'); diff --git a/test/js/node/test/parallel/test-console-async-write-error.js b/test/js/node/test/parallel/test-console-async-write-error.js new file mode 100644 index 0000000000..be76c89832 --- /dev/null +++ b/test/js/node/test/parallel/test-console-async-write-error.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const { Console } = require('console'); +const { Writable } = require('stream'); + +for (const method of ['dir', 'log', 'warn']) { + const out = new Writable({ + write: common.mustCall((chunk, enc, callback) => { + process.nextTick(callback, new Error('foobar')); + }) + }); + + const c = new Console(out, out, true); + c[method]('abc'); // Should not throw. +} diff --git a/test/js/node/test/parallel/test-console-issue-43095.js b/test/js/node/test/parallel/test-console-issue-43095.js new file mode 100644 index 0000000000..647f4af2df --- /dev/null +++ b/test/js/node/test/parallel/test-console-issue-43095.js @@ -0,0 +1,12 @@ +'use strict'; + +require('../common'); +const { inspect } = require('node:util'); + +const r = Proxy.revocable({}, {}); +r.revoke(); + +console.dir(r); +console.dir(r.proxy); +console.log(r.proxy); +console.log(inspect(r.proxy, { showProxy: true })); diff --git a/test/js/node/test/parallel/test-console-log-stdio-broken-dest.js b/test/js/node/test/parallel/test-console-log-stdio-broken-dest.js new file mode 100644 index 0000000000..bdd1b79427 --- /dev/null +++ b/test/js/node/test/parallel/test-console-log-stdio-broken-dest.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('../common'); +const { Writable } = require('stream'); +const { Console } = require('console'); +const { EventEmitter } = require('events'); + +const stream = new Writable({ + write(chunk, enc, cb) { + cb(); + }, + writev(chunks, cb) { + setTimeout(cb, 10, new Error('kaboom')); + } +}); +const myConsole = new Console(stream, stream); + +process.on('warning', common.mustNotCall()); + +stream.cork(); +for (let i = 0; i < EventEmitter.defaultMaxListeners + 1; i++) { + myConsole.log('a message'); +} +stream.uncork(); diff --git a/test/js/node/test/parallel/test-console-log-throw-primitive.js b/test/js/node/test/parallel/test-console-log-throw-primitive.js new file mode 100644 index 0000000000..a1a9ca2572 --- /dev/null +++ b/test/js/node/test/parallel/test-console-log-throw-primitive.js @@ -0,0 +1,14 @@ +'use strict'; +require('../common'); +const { Writable } = require('stream'); +const { Console } = require('console'); + +const stream = new Writable({ + write() { + throw null; // eslint-disable-line no-throw-literal + } +}); + +const console = new Console({ stdout: stream }); + +console.log('test'); // Should not throw diff --git a/test/js/node/test/parallel/test-console-methods.js b/test/js/node/test/parallel/test-console-methods.js new file mode 100644 index 0000000000..d338cc1f80 --- /dev/null +++ b/test/js/node/test/parallel/test-console-methods.js @@ -0,0 +1,63 @@ +'use strict'; +require('../common'); + +// This test ensures that console methods cannot be invoked as constructors and +// that their name is always correct. + +const assert = require('assert'); + +const { Console } = console; +const newInstance = new Console(process.stdout); +const err = TypeError; + +const methods = [ + 'log', + 'warn', + 'dir', + 'time', + 'timeEnd', + 'timeLog', + 'trace', + 'assert', + 'clear', + 'count', + 'countReset', + 'group', + 'groupEnd', + 'table', + 'debug', + 'info', + 'dirxml', + 'error', + 'groupCollapsed', +]; + +const alternateNames = { + debug: 'log', + info: 'log', + dirxml: 'log', + error: 'warn', + groupCollapsed: 'group' +}; + +function assertEqualName(method) { + try { + assert.strictEqual(console[method].name, method); + } catch { + assert.strictEqual(console[method].name, alternateNames[method]); + } + try { + assert.strictEqual(newInstance[method].name, method); + } catch { + assert.strictEqual(newInstance[method].name, alternateNames[method]); + } +} + +for (const method of methods) { + assertEqualName(method); + + assert.throws(() => new console[method](), err); + assert.throws(() => new newInstance[method](), err); + assert.throws(() => Reflect.construct({}, [], console[method]), err); + assert.throws(() => Reflect.construct({}, [], newInstance[method]), err); +} diff --git a/test/js/node/test/parallel/test-console-not-call-toString.js b/test/js/node/test/parallel/test-console-not-call-toString.js new file mode 100644 index 0000000000..0f6f2624c5 --- /dev/null +++ b/test/js/node/test/parallel/test-console-not-call-toString.js @@ -0,0 +1,34 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +function func() {} +let toStringCalled = false; +func.toString = function() { + toStringCalled = true; +}; + +require('util').inspect(func); + +assert.ok(!toStringCalled); diff --git a/test/js/node/test/parallel/test-console-self-assign.js b/test/js/node/test/parallel/test-console-self-assign.js new file mode 100644 index 0000000000..53c54ab9a3 --- /dev/null +++ b/test/js/node/test/parallel/test-console-self-assign.js @@ -0,0 +1,6 @@ +'use strict'; + +require('../common'); + +// Assigning to itself should not throw. +global.console = global.console; // eslint-disable-line no-self-assign diff --git a/test/js/node/test/parallel/test-console-sync-write-error.js b/test/js/node/test/parallel/test-console-sync-write-error.js new file mode 100644 index 0000000000..bf916ff5b8 --- /dev/null +++ b/test/js/node/test/parallel/test-console-sync-write-error.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../common'); +const { Console } = require('console'); +const { Writable } = require('stream'); + +for (const method of ['dir', 'log', 'warn']) { + { + const out = new Writable({ + write: common.mustCall((chunk, enc, callback) => { + callback(new Error('foobar')); + }) + }); + + const c = new Console(out, out, true); + c[method]('abc'); // Should not throw. + } + + { + const out = new Writable({ + write: common.mustCall((chunk, enc, callback) => { + throw new Error('foobar'); + }) + }); + + const c = new Console(out, out, true); + c[method]('abc'); // Should not throw. + } + + { + const out = new Writable({ + write: common.mustCall((chunk, enc, callback) => { + setImmediate(() => callback(new Error('foobar'))); + }) + }); + + const c = new Console(out, out, true); + c[method]('abc'); // Should not throw. + } +} diff --git a/test/js/node/test/parallel/test-coverage-with-inspector-disabled.js b/test/js/node/test/parallel/test-coverage-with-inspector-disabled.js new file mode 100644 index 0000000000..f2ba070859 --- /dev/null +++ b/test/js/node/test/parallel/test-coverage-with-inspector-disabled.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +if (process.features.inspector) { + common.skip('V8 inspector is enabled'); +} + +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { spawnSync } = require('child_process'); +const env = { ...process.env, NODE_V8_COVERAGE: '/foo/bar' }; +const childPath = fixtures.path('v8-coverage/subprocess'); +const { status, stderr } = spawnSync( + process.execPath, + [childPath], + { env } +); + +const warningMessage = 'The inspector is disabled, ' + + 'coverage could not be collected'; + +assert.strictEqual(status, 0); +assert.strictEqual( + stderr.toString().includes(`Warning: ${warningMessage}`), + true +); diff --git a/test/js/node/test/parallel/test-crypto-dh-odd-key.js b/test/js/node/test/parallel/test-crypto-dh-odd-key.js new file mode 100644 index 0000000000..69a1eb56c8 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-dh-odd-key.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); + +function test() { + const odd = Buffer.alloc(39, 'A'); + + const c = crypto.createDiffieHellman(common.hasOpenSSL3 ? 1024 : 32); + c.setPrivateKey(odd); + c.generateKeys(); +} + +// FIPS requires a length of at least 1024 +if (!common.hasFipsCrypto) { + test(); +} else { + assert.throws(function() { test(); }, /key size too small/); +} diff --git a/test/js/node/test/parallel/test-crypto-dh-shared.js b/test/js/node/test/parallel/test-crypto-dh-shared.js new file mode 100644 index 0000000000..515405034d --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-dh-shared.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); + +const alice = crypto.createDiffieHellmanGroup('modp5'); +const bob = crypto.createDiffieHellmanGroup('modp5'); +alice.generateKeys(); +bob.generateKeys(); +const aSecret = alice.computeSecret(bob.getPublicKey()).toString('hex'); +const bSecret = bob.computeSecret(alice.getPublicKey()).toString('hex'); +assert.strictEqual(aSecret, bSecret); diff --git a/test/js/node/test/parallel/test-crypto-domain.js b/test/js/node/test/parallel/test-crypto-domain.js new file mode 100644 index 0000000000..62e2be4c0f --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-domain.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); +const domain = require('domain'); + +const test = (fn) => { + const ex = new Error('BAM'); + const d = domain.create(); + d.on('error', common.mustCall(function(err) { + assert.strictEqual(err, ex); + })); + const cb = common.mustCall(function() { + throw ex; + }); + d.run(cb); +}; + +test(function(cb) { + crypto.pbkdf2('password', 'salt', 1, 8, cb); +}); + +test(function(cb) { + crypto.randomBytes(32, cb); +}); diff --git a/test/js/node/test/parallel/test-crypto-from-binary.js b/test/js/node/test/parallel/test-crypto-from-binary.js new file mode 100644 index 0000000000..3b3c6a81ef --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-from-binary.js @@ -0,0 +1,65 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// This is the same as test/simple/test-crypto, but from before the shift +// to use buffers by default. + + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); + +const EXTERN_APEX = 0xFBEE9; + +// Manually controlled string for checking binary output +let ucs2_control = 'a\u0000'; + +// Grow the strings to proper length +while (ucs2_control.length <= EXTERN_APEX) { + ucs2_control = ucs2_control.repeat(2); +} + + +// Check resultant buffer and output string +const b = Buffer.from(ucs2_control + ucs2_control, 'ucs2'); + +// +// Test updating from birant data +// +{ + const datum1 = b.slice(700000); + const hash1_converted = crypto.createHash('sha1') + .update(datum1.toString('base64'), 'base64') + .digest('hex'); + const hash1_direct = crypto.createHash('sha1').update(datum1).digest('hex'); + assert.strictEqual(hash1_direct, hash1_converted); + + const datum2 = b; + const hash2_converted = crypto.createHash('sha1') + .update(datum2.toString('base64'), 'base64') + .digest('hex'); + const hash2_direct = crypto.createHash('sha1').update(datum2).digest('hex'); + assert.strictEqual(hash2_direct, hash2_converted); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-async-elliptic-curve-jwk-rsa.js b/test/js/node/test/parallel/test-crypto-keygen-async-elliptic-curve-jwk-rsa.js new file mode 100644 index 0000000000..449d1a97f9 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-async-elliptic-curve-jwk-rsa.js @@ -0,0 +1,38 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, +} = require('crypto'); + +// Test async elliptic curve key generation with 'jwk' encoding and RSA. +{ + generateKeyPair('rsa', { + modulusLength: 1024, + publicKeyEncoding: { + format: 'jwk' + }, + privateKeyEncoding: { + format: 'jwk' + } + }, common.mustSucceed((publicKey, privateKey) => { + assert.strictEqual(typeof publicKey, 'object'); + assert.strictEqual(typeof privateKey, 'object'); + assert.strictEqual(publicKey.kty, 'RSA'); + assert.strictEqual(publicKey.kty, privateKey.kty); + assert.strictEqual(typeof publicKey.n, 'string'); + assert.strictEqual(publicKey.n, privateKey.n); + assert.strictEqual(typeof publicKey.e, 'string'); + assert.strictEqual(publicKey.e, privateKey.e); + assert.strictEqual(typeof privateKey.d, 'string'); + assert.strictEqual(typeof privateKey.p, 'string'); + assert.strictEqual(typeof privateKey.q, 'string'); + assert.strictEqual(typeof privateKey.dp, 'string'); + assert.strictEqual(typeof privateKey.dq, 'string'); + assert.strictEqual(typeof privateKey.qi, 'string'); + })); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-async-encrypted-private-key-der.js b/test/js/node/test/parallel/test-crypto-keygen-async-encrypted-private-key-der.js new file mode 100644 index 0000000000..3203dfe16e --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-async-encrypted-private-key-der.js @@ -0,0 +1,50 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, +} = require('crypto'); +const { + assertApproximateSize, + testEncryptDecrypt, + testSignVerify, +} = require('../common/crypto'); + +// Test async RSA key generation with an encrypted private key, but encoded as DER. +{ + generateKeyPair('rsa', { + publicExponent: 0x10001, + modulusLength: 512, + publicKeyEncoding: { + type: 'pkcs1', + format: 'der' + }, + privateKeyEncoding: { + type: 'pkcs8', + format: 'der' + } + }, common.mustSucceed((publicKeyDER, privateKeyDER) => { + assert(Buffer.isBuffer(publicKeyDER)); + assertApproximateSize(publicKeyDER, 74); + + assert(Buffer.isBuffer(privateKeyDER)); + + const publicKey = { + key: publicKeyDER, + type: 'pkcs1', + format: 'der', + }; + const privateKey = { + key: privateKeyDER, + format: 'der', + type: 'pkcs8', + passphrase: 'secret' + }; + testEncryptDecrypt(publicKey, privateKey); + testSignVerify(publicKey, privateKey); + })); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve.js b/test/js/node/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve.js new file mode 100644 index 0000000000..46223f08d7 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, +} = require('crypto'); +const { + testSignVerify, + spkiExp, + sec1Exp, +} = require('../common/crypto'); + +// Test async explicit elliptic curve key generation, e.g. for ECDSA, +// with a SEC1 private key with paramEncoding explicit. +{ + generateKeyPair('ec', { + namedCurve: 'prime256v1', + paramEncoding: 'explicit', + publicKeyEncoding: { + type: 'spki', + format: 'pem' + }, + privateKeyEncoding: { + type: 'sec1', + format: 'pem' + } + }, common.mustSucceed((publicKey, privateKey) => { + assert.strictEqual(typeof publicKey, 'string'); + assert.match(publicKey, spkiExp); + assert.strictEqual(typeof privateKey, 'string'); + assert.match(privateKey, sec1Exp); + + testSignVerify(publicKey, privateKey); + })); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-async-named-elliptic-curve.js b/test/js/node/test/parallel/test-crypto-keygen-async-named-elliptic-curve.js new file mode 100644 index 0000000000..a1dfdbce1f --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-async-named-elliptic-curve.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, +} = require('crypto'); +const { + testSignVerify, + spkiExp, + sec1Exp, +} = require('../common/crypto'); + +// Test async named elliptic curve key generation, e.g. for ECDSA, +// with a SEC1 private key. +{ + generateKeyPair('ec', { + namedCurve: 'prime256v1', + paramEncoding: 'named', + publicKeyEncoding: { + type: 'spki', + format: 'pem' + }, + privateKeyEncoding: { + type: 'sec1', + format: 'pem' + } + }, common.mustSucceed((publicKey, privateKey) => { + assert.strictEqual(typeof publicKey, 'string'); + assert.match(publicKey, spkiExp); + assert.strictEqual(typeof privateKey, 'string'); + assert.match(privateKey, sec1Exp); + + testSignVerify(publicKey, privateKey); + })); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-eddsa.js b/test/js/node/test/parallel/test-crypto-keygen-eddsa.js new file mode 100644 index 0000000000..5a097c2524 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-eddsa.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, +} = require('crypto'); + +// Test EdDSA key generation. +{ + if (!/^1\.1\.0/.test(process.versions.openssl)) { + ['ed25519', 'ed448', 'x25519', 'x448'].forEach((keyType) => { + generateKeyPair(keyType, common.mustSucceed((publicKey, privateKey) => { + assert.strictEqual(publicKey.type, 'public'); + assert.strictEqual(publicKey.asymmetricKeyType, keyType); + assert.deepStrictEqual(publicKey.asymmetricKeyDetails, {}); + + assert.strictEqual(privateKey.type, 'private'); + assert.strictEqual(privateKey.asymmetricKeyType, keyType); + assert.deepStrictEqual(privateKey.asymmetricKeyDetails, {}); + })); + }); + } +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-empty-passphrase-no-error.js b/test/js/node/test/parallel/test-crypto-keygen-empty-passphrase-no-error.js new file mode 100644 index 0000000000..6c7938f99e --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-empty-passphrase-no-error.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, +} = require('crypto'); + +// Passing an empty passphrase string should not throw ERR_OSSL_CRYPTO_MALLOC_FAILURE even on OpenSSL 3. +// Regression test for https://github.com/nodejs/node/issues/41428. +generateKeyPair('rsa', { + modulusLength: 1024, + publicKeyEncoding: { + type: 'spki', + format: 'pem' + }, + privateKeyEncoding: { + type: 'pkcs8', + format: 'pem', + cipher: 'aes-256-cbc', + passphrase: '' + } +}, common.mustSucceed((publicKey, privateKey) => { + assert.strictEqual(typeof publicKey, 'string'); + assert.strictEqual(typeof privateKey, 'string'); +})); diff --git a/test/js/node/test/parallel/test-crypto-keygen-key-object-without-encoding.js b/test/js/node/test/parallel/test-crypto-keygen-key-object-without-encoding.js new file mode 100644 index 0000000000..abcd282871 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-key-object-without-encoding.js @@ -0,0 +1,55 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, +} = require('crypto'); +const { + testEncryptDecrypt, + testSignVerify, +} = require('../common/crypto'); + +// Tests key objects are returned when key encodings are not specified. +{ + // If no publicKeyEncoding is specified, a key object should be returned. + generateKeyPair('rsa', { + modulusLength: 1024, + privateKeyEncoding: { + type: 'pkcs1', + format: 'pem' + } + }, common.mustSucceed((publicKey, privateKey) => { + assert.strictEqual(typeof publicKey, 'object'); + assert.strictEqual(publicKey.type, 'public'); + assert.strictEqual(publicKey.asymmetricKeyType, 'rsa'); + + // The private key should still be a string. + assert.strictEqual(typeof privateKey, 'string'); + + testEncryptDecrypt(publicKey, privateKey); + testSignVerify(publicKey, privateKey); + })); + + // If no privateKeyEncoding is specified, a key object should be returned. + generateKeyPair('rsa', { + modulusLength: 1024, + publicKeyEncoding: { + type: 'pkcs1', + format: 'pem' + } + }, common.mustSucceed((publicKey, privateKey) => { + // The public key should still be a string. + assert.strictEqual(typeof publicKey, 'string'); + + assert.strictEqual(typeof privateKey, 'object'); + assert.strictEqual(privateKey.type, 'private'); + assert.strictEqual(privateKey.asymmetricKeyType, 'rsa'); + + testEncryptDecrypt(publicKey, privateKey); + testSignVerify(publicKey, privateKey); + })); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-key-objects.js b/test/js/node/test/parallel/test-crypto-keygen-key-objects.js new file mode 100644 index 0000000000..a0f1bdf2bc --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-key-objects.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPairSync, +} = require('crypto'); + +// Test sync key generation with key objects. +{ + const { publicKey, privateKey } = generateKeyPairSync('rsa', { + modulusLength: 512 + }); + + assert.strictEqual(typeof publicKey, 'object'); + assert.strictEqual(publicKey.type, 'public'); + assert.strictEqual(publicKey.asymmetricKeyType, 'rsa'); + assert.deepStrictEqual(publicKey.asymmetricKeyDetails, { + modulusLength: 512, + publicExponent: 65537n + }); + + assert.strictEqual(typeof privateKey, 'object'); + assert.strictEqual(privateKey.type, 'private'); + assert.strictEqual(privateKey.asymmetricKeyType, 'rsa'); + assert.deepStrictEqual(privateKey.asymmetricKeyDetails, { + modulusLength: 512, + publicExponent: 65537n + }); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-missing-oid.js b/test/js/node/test/parallel/test-crypto-keygen-missing-oid.js new file mode 100644 index 0000000000..f7fefe1384 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-missing-oid.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, + generateKeyPairSync, + getCurves, +} = require('crypto'); + +// This test creates EC key pairs on curves without associated OIDs. +// Specifying a key encoding should not crash. +{ + if (process.versions.openssl >= '1.1.1i') { + for (const namedCurve of ['Oakley-EC2N-3', 'Oakley-EC2N-4']) { + if (!getCurves().includes(namedCurve)) + continue; + + const expectedErrorCode = + common.hasOpenSSL3 ? 'ERR_OSSL_MISSING_OID' : 'ERR_OSSL_EC_MISSING_OID'; + const params = { + namedCurve, + publicKeyEncoding: { + format: 'der', + type: 'spki' + } + }; + + assert.throws(() => { + generateKeyPairSync('ec', params); + }, { + code: expectedErrorCode + }); + + generateKeyPair('ec', params, common.mustCall((err) => { + assert.strictEqual(err.code, expectedErrorCode); + })); + } + } +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-non-standard-public-exponent.js b/test/js/node/test/parallel/test-crypto-keygen-non-standard-public-exponent.js new file mode 100644 index 0000000000..f54a9e8a6d --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-non-standard-public-exponent.js @@ -0,0 +1,35 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPairSync, +} = require('crypto'); + +// Test sync key generation with key objects with a non-standard +// publicExponent +{ + const { publicKey, privateKey } = generateKeyPairSync('rsa', { + publicExponent: 3, + modulusLength: 512 + }); + + assert.strictEqual(typeof publicKey, 'object'); + assert.strictEqual(publicKey.type, 'public'); + assert.strictEqual(publicKey.asymmetricKeyType, 'rsa'); + assert.deepStrictEqual(publicKey.asymmetricKeyDetails, { + modulusLength: 512, + publicExponent: 3n + }); + + assert.strictEqual(typeof privateKey, 'object'); + assert.strictEqual(privateKey.type, 'private'); + assert.strictEqual(privateKey.asymmetricKeyType, 'rsa'); + assert.deepStrictEqual(privateKey.asymmetricKeyDetails, { + modulusLength: 512, + publicExponent: 3n + }); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-promisify.js b/test/js/node/test/parallel/test-crypto-keygen-promisify.js new file mode 100644 index 0000000000..cd6ca7d6e3 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-promisify.js @@ -0,0 +1,46 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPair, +} = require('crypto'); +const { + assertApproximateSize, + testEncryptDecrypt, + testSignVerify, + pkcs1PubExp, + pkcs1PrivExp, +} = require('../common/crypto'); +const { promisify } = require('util'); + +// Test the util.promisified API with async RSA key generation. +{ + promisify(generateKeyPair)('rsa', { + publicExponent: 0x10001, + modulusLength: 512, + publicKeyEncoding: { + type: 'pkcs1', + format: 'pem' + }, + privateKeyEncoding: { + type: 'pkcs1', + format: 'pem' + } + }).then(common.mustCall((keys) => { + const { publicKey, privateKey } = keys; + assert.strictEqual(typeof publicKey, 'string'); + assert.match(publicKey, pkcs1PubExp); + assertApproximateSize(publicKey, 180); + + assert.strictEqual(typeof privateKey, 'string'); + assert.match(privateKey, pkcs1PrivExp); + assertApproximateSize(privateKey, 512); + + testEncryptDecrypt(publicKey, privateKey); + testSignVerify(publicKey, privateKey); + })); +} diff --git a/test/js/node/test/parallel/test-crypto-keygen-sync.js b/test/js/node/test/parallel/test-crypto-keygen-sync.js new file mode 100644 index 0000000000..a100379e21 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-keygen-sync.js @@ -0,0 +1,47 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { + generateKeyPairSync, +} = require('crypto'); +const { + assertApproximateSize, + testEncryptDecrypt, + testSignVerify, + pkcs1PubExp, + pkcs8Exp, +} = require('../common/crypto'); + +// To make the test faster, we will only test sync key generation once and +// with a relatively small key. +{ + const ret = generateKeyPairSync('rsa', { + publicExponent: 3, + modulusLength: 512, + publicKeyEncoding: { + type: 'pkcs1', + format: 'pem' + }, + privateKeyEncoding: { + type: 'pkcs8', + format: 'pem' + } + }); + + assert.strictEqual(Object.keys(ret).length, 2); + const { publicKey, privateKey } = ret; + + assert.strictEqual(typeof publicKey, 'string'); + assert.match(publicKey, pkcs1PubExp); + assertApproximateSize(publicKey, 162); + assert.strictEqual(typeof privateKey, 'string'); + assert.match(privateKey, pkcs8Exp); + assertApproximateSize(privateKey, 512); + + testEncryptDecrypt(publicKey, privateKey); + testSignVerify(publicKey, privateKey); +} diff --git a/test/js/node/test/parallel/test-crypto-lazy-transform-writable.js b/test/js/node/test/parallel/test-crypto-lazy-transform-writable.js new file mode 100644 index 0000000000..000c6693c8 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-lazy-transform-writable.js @@ -0,0 +1,36 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); +const Stream = require('stream'); + +const hasher1 = crypto.createHash('sha256'); +const hasher2 = crypto.createHash('sha256'); + +// Calculate the expected result. +hasher1.write(Buffer.from('hello world')); +hasher1.end(); + +const expected = hasher1.read().toString('hex'); + +class OldStream extends Stream { + constructor() { + super(); + this.readable = true; + } +} + +const stream = new OldStream(); + +stream.pipe(hasher2).on('finish', common.mustCall(function() { + const hash = hasher2.read().toString('hex'); + assert.strictEqual(hash, expected); +})); + +stream.emit('data', Buffer.from('hello')); +stream.emit('data', Buffer.from(' world')); +stream.emit('end'); diff --git a/test/js/node/test/parallel/test-crypto-no-algorithm.js b/test/js/node/test/parallel/test-crypto-no-algorithm.js new file mode 100644 index 0000000000..37db38cf61 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-no-algorithm.js @@ -0,0 +1,38 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.hasOpenSSL3) + common.skip('this test requires OpenSSL 3.x'); + +const assert = require('node:assert/strict'); +const crypto = require('node:crypto'); + +if (common.isMainThread) { + // TODO(richardlau): Decide if `crypto.setFips` should error if the + // provider named "fips" is not available. + crypto.setFips(1); + crypto.randomBytes(20, common.mustCall((err) => { + // crypto.randomBytes should either succeed or fail but not hang. + if (err) { + assert.match(err.message, /digital envelope routines::unsupported/); + const expected = /random number generator::unable to fetch drbg/; + assert(err.opensslErrorStack.some((msg) => expected.test(msg)), + `did not find ${expected} in ${err.opensslErrorStack}`); + } + })); +} + +{ + // Startup test. Should not hang. + const { path } = require('../common/fixtures'); + const { spawnSync } = require('node:child_process'); + const baseConf = path('openssl3-conf', 'base_only.cnf'); + const cp = spawnSync(process.execPath, + [ `--openssl-config=${baseConf}`, '-p', '"hello"' ], + { encoding: 'utf8' }); + assert(common.nodeProcessAborted(cp.status, cp.signal), + `process did not abort, code:${cp.status} signal:${cp.signal}`); +} diff --git a/test/js/node/test/parallel/test-crypto-op-during-process-exit.js b/test/js/node/test/parallel/test-crypto-op-during-process-exit.js new file mode 100644 index 0000000000..a9a70c39e0 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-op-during-process-exit.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) { common.skip('missing crypto'); } +const assert = require('assert'); +const { generateKeyPair } = require('crypto'); + +if (common.isWindows) { + // Remove this conditional once the libuv change is in Node.js. + common.skip('crashing due to https://github.com/libuv/libuv/pull/2983'); +} + +// Regression test for a race condition: process.exit() might lead to OpenSSL +// cleaning up state from the exit() call via calling its destructor, but +// running OpenSSL operations on another thread might lead to them attempting +// to initialize OpenSSL, leading to a crash. +// This test crashed consistently on x64 Linux on Node v14.9.0. + +generateKeyPair('rsa', { + modulusLength: 2048, + privateKeyEncoding: { + type: 'pkcs1', + format: 'pem' + } +}, (err/* , publicKey, privateKey */) => { + assert.ifError(err); +}); + +setTimeout(() => process.exit(), common.platformTimeout(10)); diff --git a/test/js/node/test/parallel/test-crypto-padding-aes256.js b/test/js/node/test/parallel/test-crypto-padding-aes256.js new file mode 100644 index 0000000000..14d853bdfd --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-padding-aes256.js @@ -0,0 +1,60 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); + +const iv = Buffer.from('00000000000000000000000000000000', 'hex'); +const key = Buffer.from('0123456789abcdef0123456789abcdef' + + '0123456789abcdef0123456789abcdef', 'hex'); + +function encrypt(val, pad) { + const c = crypto.createCipheriv('aes256', key, iv); + c.setAutoPadding(pad); + return c.update(val, 'utf8', 'latin1') + c.final('latin1'); +} + +function decrypt(val, pad) { + const c = crypto.createDecipheriv('aes256', key, iv); + c.setAutoPadding(pad); + return c.update(val, 'latin1', 'utf8') + c.final('utf8'); +} + +// echo 0123456789abcdef0123456789abcdef \ +// | openssl enc -e -aes256 -nopad -K -iv \ +// | openssl enc -d -aes256 -nopad -K -iv +let plaintext = '0123456789abcdef0123456789abcdef'; // Multiple of block size +let encrypted = encrypt(plaintext, false); +let decrypted = decrypt(encrypted, false); +assert.strictEqual(decrypted, plaintext); + +// echo 0123456789abcdef0123456789abcde \ +// | openssl enc -e -aes256 -K -iv \ +// | openssl enc -d -aes256 -K -iv +plaintext = '0123456789abcdef0123456789abcde'; // not a multiple +encrypted = encrypt(plaintext, true); +decrypted = decrypt(encrypted, true); +assert.strictEqual(decrypted, plaintext); diff --git a/test/js/node/test/parallel/test-crypto-publicDecrypt-fails-first-time.js b/test/js/node/test/parallel/test-crypto-publicDecrypt-fails-first-time.js new file mode 100644 index 0000000000..a60b87dbf6 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-publicDecrypt-fails-first-time.js @@ -0,0 +1,41 @@ +'use strict'; +const common = require('../common'); + +// Test for https://github.com/nodejs/node/issues/40814 + +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.hasOpenSSL3) + common.skip('only openssl3'); // https://github.com/nodejs/node/pull/42793#issuecomment-1107491901 + +const assert = require('assert'); +const crypto = require('crypto'); + +const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', { + modulusLength: 2048, + publicKeyEncoding: { + type: 'spki', + format: 'pem' + }, + privateKeyEncoding: { + type: 'pkcs8', + format: 'pem', + cipher: 'aes-128-ecb', + passphrase: 'abcdef' + } +}); +assert.notStrictEqual(privateKey.toString(), ''); + +const msg = 'The quick brown fox jumps over the lazy dog'; + +const encryptedString = crypto.privateEncrypt({ + key: privateKey, + passphrase: 'abcdef' +}, Buffer.from(msg)).toString('base64'); +const decryptedString = crypto.publicDecrypt(publicKey, Buffer.from(encryptedString, 'base64')).toString(); +console.log(`Encrypted: ${encryptedString}`); +console.log(`Decrypted: ${decryptedString}`); + +assert.notStrictEqual(encryptedString, ''); +assert.strictEqual(decryptedString, msg); diff --git a/test/js/node/test/parallel/test-crypto-randomfillsync-regression.js b/test/js/node/test/parallel/test-crypto-randomfillsync-regression.js new file mode 100644 index 0000000000..7a37864258 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-randomfillsync-regression.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const { randomFillSync } = require('crypto'); +const { notStrictEqual } = require('assert'); + +const ab = new ArrayBuffer(20); +const buf = Buffer.from(ab, 10); + +const before = buf.toString('hex'); + +randomFillSync(buf); + +const after = buf.toString('hex'); + +notStrictEqual(before, after); diff --git a/test/js/node/test/parallel/test-crypto-subtle-zero-length.js b/test/js/node/test/parallel/test-crypto-subtle-zero-length.js new file mode 100644 index 0000000000..f5a84727b0 --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-subtle-zero-length.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { subtle } = globalThis.crypto; + +(async () => { + const k = await subtle.importKey( + 'raw', + new Uint8Array(32), + { name: 'AES-GCM' }, + false, + [ 'encrypt', 'decrypt' ]); + assert(k instanceof CryptoKey); + + const e = await subtle.encrypt({ + name: 'AES-GCM', + iv: new Uint8Array(12), + }, k, new Uint8Array(0)); + assert(e instanceof ArrayBuffer); + assert.deepStrictEqual( + Buffer.from(e), + Buffer.from([ + 0x53, 0x0f, 0x8a, 0xfb, 0xc7, 0x45, 0x36, 0xb9, + 0xa9, 0x63, 0xb4, 0xf1, 0xc4, 0xcb, 0x73, 0x8b ])); + + const v = await subtle.decrypt({ + name: 'AES-GCM', + iv: new Uint8Array(12), + }, k, e); + assert(v instanceof ArrayBuffer); + assert.strictEqual(v.byteLength, 0); +})().then(common.mustCall()).catch((e) => { + assert.ifError(e); +}); diff --git a/test/js/node/test/parallel/test-crypto-update-encoding.js b/test/js/node/test/parallel/test-crypto-update-encoding.js new file mode 100644 index 0000000000..e1e6d029aa --- /dev/null +++ b/test/js/node/test/parallel/test-crypto-update-encoding.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const crypto = require('crypto'); + +const zeros = Buffer.alloc; +const key = zeros(16); +const iv = zeros(16); + +const cipher = () => crypto.createCipheriv('aes-128-cbc', key, iv); +const decipher = () => crypto.createDecipheriv('aes-128-cbc', key, iv); +const hash = () => crypto.createSign('sha256'); +const hmac = () => crypto.createHmac('sha256', key); +const sign = () => crypto.createSign('sha256'); +const verify = () => crypto.createVerify('sha256'); + +for (const f of [cipher, decipher, hash, hmac, sign, verify]) + for (const n of [15, 16]) + f().update(zeros(n), 'hex'); // Should ignore inputEncoding. diff --git a/test/js/node/test/parallel/test-datetime-change-notify.js b/test/js/node/test/parallel/test-datetime-change-notify.js new file mode 100644 index 0000000000..0184351190 --- /dev/null +++ b/test/js/node/test/parallel/test-datetime-change-notify.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common'); +const { isMainThread } = require('worker_threads'); + +if (!common.hasIntl) + common.skip('Intl not present.'); + +if (!isMainThread) + common.skip('Test not support running within a worker'); + +const assert = require('assert'); + +const cases = [ + { + timeZone: 'Etc/UTC', + expected: /Coordinated Universal Time/, + }, + { + timeZone: 'America/New_York', + expected: /Eastern (?:Standard|Daylight) Time/, + }, + { + timeZone: 'America/Los_Angeles', + expected: /Pacific (?:Standard|Daylight) Time/, + }, + { + timeZone: 'Europe/Dublin', + expected: /Irish Standard Time|Greenwich Mean Time/, + }, +]; + +for (const { timeZone, expected } of cases) { + process.env.TZ = timeZone; + const date = new Date().toLocaleString('en-US', { timeZoneName: 'long' }); + assert.match(date, expected); +} diff --git a/test/js/node/test/parallel/test-debugger-backtrace.js b/test/js/node/test/parallel/test-debugger-backtrace.js new file mode 100644 index 0000000000..f66cc11d70 --- /dev/null +++ b/test/js/node/test/parallel/test-debugger-backtrace.js @@ -0,0 +1,33 @@ +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const fixtures = require('../common/fixtures'); +const startCLI = require('../common/debugger'); + +const assert = require('assert'); +const path = require('path'); + +// Display and navigate backtrace. +{ + const scriptFullPath = fixtures.path('debugger', 'backtrace.js'); + const script = path.relative(process.cwd(), scriptFullPath); + const cli = startCLI(['--port=0', script]); + + async function runTest() { + try { + await cli.waitForInitialBreak(); + await cli.waitForPrompt(); + await cli.stepCommand('c'); + await cli.command('bt'); + assert.ok(cli.output.includes(`#0 topFn ${script}:7:2`)); + await cli.command('backtrace'); + assert.ok(cli.output.includes(`#0 topFn ${script}:7:2`)); + } finally { + await cli.quit(); + } + } + + runTest(); +} diff --git a/test/js/node/test/parallel/test-debugger-exec.js b/test/js/node/test/parallel/test-debugger-exec.js new file mode 100644 index 0000000000..51bc749734 --- /dev/null +++ b/test/js/node/test/parallel/test-debugger-exec.js @@ -0,0 +1,68 @@ +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const fixtures = require('../common/fixtures'); +const startCLI = require('../common/debugger'); + +const assert = require('assert'); + +const cli = startCLI(['--port=0', fixtures.path('debugger/alive.js')]); + +async function waitInitialBreak() { + try { + await cli.waitForInitialBreak(); + await cli.waitForPrompt(); + await cli.command('exec [typeof heartbeat, typeof process.exit]'); + assert.match(cli.output, /\[ 'function', 'function' \]/, 'works w/o paren'); + + await cli.command('p [typeof heartbeat, typeof process.exit]'); + assert.match( + cli.output, + /\[ 'function', 'function' \]/, + 'works w/o paren, short' + ); + + await cli.command('repl'); + assert.match( + cli.output, + /Press Ctrl\+C to leave debug repl\n+> /, + 'shows hint for how to leave repl' + ); + assert.doesNotMatch(cli.output, /debug>/, 'changes the repl style'); + + await cli.command('[typeof heartbeat, typeof process.exit]'); + await cli.waitFor(/function/); + await cli.waitForPrompt(); + assert.match( + cli.output, + /\[ 'function', 'function' \]/, + 'can evaluate in the repl' + ); + assert.match(cli.output, /> $/); + + await cli.ctrlC(); + await cli.waitFor(/debug> $/); + await cli.command('exec("[typeof heartbeat, typeof process.exit]")'); + assert.match(cli.output, /\[ 'function', 'function' \]/, 'works w/ paren'); + await cli.command('p("[typeof heartbeat, typeof process.exit]")'); + assert.match( + cli.output, + /\[ 'function', 'function' \]/, + 'works w/ paren, short' + ); + + await cli.command('cont'); + await cli.command('exec [typeof heartbeat, typeof process.exit]'); + assert.match( + cli.output, + /\[ 'undefined', 'function' \]/, + 'non-paused exec can see global but not module-scope values' + ); + } finally { + await cli.quit(); + } +} + +waitInitialBreak(); diff --git a/test/js/node/test/parallel/test-debugger-invalid-json.mjs b/test/js/node/test/parallel/test-debugger-invalid-json.mjs new file mode 100644 index 0000000000..e4754a465f --- /dev/null +++ b/test/js/node/test/parallel/test-debugger-invalid-json.mjs @@ -0,0 +1,46 @@ +import { skipIfInspectorDisabled, mustCall } from '../common/index.mjs'; + +skipIfInspectorDisabled(); + +import startCLI from '../common/debugger.js'; + +import assert from 'assert'; +import http from 'http'; + +const host = '127.0.0.1'; + +{ + const server = http.createServer((req, res) => { + res.statusCode = 400; + res.end('Bad Request'); + }); + + server.listen(0, mustCall(async () => { + const port = server.address().port; + const cli = startCLI([`${host}:${port}`]); + try { + const code = await cli.quit(); + assert.strictEqual(code, 1); + } finally { + server.close(); + } + })); +} + +{ + const server = http.createServer((req, res) => { + res.statusCode = 200; + res.end('some data that is invalid json'); + }); + + server.listen(0, host, mustCall(async () => { + const port = server.address().port; + const cli = startCLI([`${host}:${port}`]); + try { + const code = await cli.quit(); + assert.strictEqual(code, 1); + } finally { + server.close(); + } + })); +} diff --git a/test/js/node/test/parallel/test-debugger-low-level.js b/test/js/node/test/parallel/test-debugger-low-level.js new file mode 100644 index 0000000000..31f67849f5 --- /dev/null +++ b/test/js/node/test/parallel/test-debugger-low-level.js @@ -0,0 +1,35 @@ +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const fixtures = require('../common/fixtures'); +const startCLI = require('../common/debugger'); +const assert = require('assert'); + +// Debugger agent direct access. +{ + const cli = startCLI(['--port=0', fixtures.path('debugger/three-lines.js')]); + const scriptPattern = /^\* (\d+): \S+debugger(?:\/|\\)three-lines\.js/m; + + async function testDebuggerLowLevel() { + try { + await cli.waitForInitialBreak(); + await cli.waitForPrompt(); + await cli.command('scripts'); + const [, scriptId] = cli.output.match(scriptPattern); + await cli.command( + `Debugger.getScriptSource({ scriptId: '${scriptId}' })` + ); + assert.match( + cli.output, + /scriptSource:[ \n]*'(?:\(function \(|let x = 1)/); + assert.match( + cli.output, + /let x = 1;/); + } finally { + await cli.quit(); + } + } + testDebuggerLowLevel(); +} diff --git a/test/js/node/test/parallel/test-debugger-pid.js b/test/js/node/test/parallel/test-debugger-pid.js new file mode 100644 index 0000000000..157939c05c --- /dev/null +++ b/test/js/node/test/parallel/test-debugger-pid.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); +common.skipIfInspectorDisabled(); + +if (common.isWindows) + common.skip('unsupported function on windows'); + +const assert = require('assert'); +const spawn = require('child_process').spawn; + +let buffer = ''; + +// Connect to debug agent +const interfacer = spawn(process.execPath, ['inspect', '-p', '655555']); + +interfacer.stdout.setEncoding('utf-8'); +interfacer.stderr.setEncoding('utf-8'); +const onData = (data) => { + data = (buffer + data).split('\n'); + buffer = data.pop(); + data.forEach((line) => interfacer.emit('line', line)); +}; +interfacer.stdout.on('data', onData); +interfacer.stderr.on('data', onData); + +interfacer.on('line', common.mustCall((line) => { + assert.strictEqual(line, 'Target process: 655555 doesn\'t exist.'); +})); diff --git a/test/js/node/test/parallel/test-debugger-preserve-breaks.js b/test/js/node/test/parallel/test-debugger-preserve-breaks.js new file mode 100644 index 0000000000..00168c570d --- /dev/null +++ b/test/js/node/test/parallel/test-debugger-preserve-breaks.js @@ -0,0 +1,47 @@ +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const fixtures = require('../common/fixtures'); +const startCLI = require('../common/debugger'); + +const assert = require('assert'); +const path = require('path'); + +const scriptFullPath = fixtures.path('debugger', 'three-lines.js'); +const script = path.relative(process.cwd(), scriptFullPath); + +// Run after quit. +const runTest = async () => { + const cli = startCLI(['--port=0', script]); + try { + await cli.waitForInitialBreak(); + await cli.waitForPrompt(); + await cli.command('breakpoints'); + assert.match(cli.output, /No breakpoints yet/); + await cli.command('sb(2)'); + await cli.command('sb(3)'); + await cli.command('breakpoints'); + assert.ok(cli.output.includes(`#0 ${script}:2`)); + assert.ok(cli.output.includes(`#1 ${script}:3`)); + await cli.stepCommand('c'); // hit line 2 + await cli.stepCommand('c'); // hit line 3 + assert.deepStrictEqual(cli.breakInfo, { filename: script, line: 3 }); + await cli.command('restart'); + await cli.waitForInitialBreak(); + assert.deepStrictEqual(cli.breakInfo, { filename: script, line: 1 }); + await cli.stepCommand('c'); + assert.deepStrictEqual(cli.breakInfo, { filename: script, line: 2 }); + await cli.stepCommand('c'); + assert.deepStrictEqual(cli.breakInfo, { filename: script, line: 3 }); + await cli.command('breakpoints'); + const msg = `SCRIPT: ${script}, OUTPUT: ${cli.output}`; + assert.ok(cli.output.includes(`#0 ${script}:2`), msg); + assert.ok(cli.output.includes(`#1 ${script}:3`), msg); + } finally { + await cli.quit(); + } +}; + +runTest(); diff --git a/test/js/node/test/parallel/test-debugger-repeat-last.js b/test/js/node/test/parallel/test-debugger-repeat-last.js new file mode 100644 index 0000000000..9a9b8eecdc --- /dev/null +++ b/test/js/node/test/parallel/test-debugger-repeat-last.js @@ -0,0 +1,45 @@ +'use strict'; +const common = require('../common'); +common.skipIfInspectorDisabled(); +const path = require('../common/fixtures').path; +const spawn = require('child_process').spawn; +const assert = require('assert'); +const fixture = path('debugger-repeat-last.js'); + +const args = [ + 'inspect', + '--port=0', + fixture, +]; + +const proc = spawn(process.execPath, args, { stdio: 'pipe' }); +proc.stdout.setEncoding('utf8'); + +let stdout = ''; + +let sentCommand = false; +let sentExit = false; + +proc.stdout.on('data', (data) => { + stdout += data; + + // Send 'n' as the first step. + if (!sentCommand && stdout.includes('> 1 ')) { + setImmediate(() => { proc.stdin.write('n\n'); }); + return sentCommand = true; + } + // Send empty (repeat last command) until we reach line 5. + if (sentCommand && !stdout.includes('> 5')) { + setImmediate(() => { proc.stdin.write('\n'); }); + return true; + } + if (!sentExit && stdout.includes('> 5')) { + setTimeout(() => { proc.stdin.write('\n\n\n.exit\n\n\n'); }, 1); + return sentExit = true; + } +}); + +process.on('exit', (exitCode) => { + assert.strictEqual(exitCode, 0); + console.log(stdout); +}); diff --git a/test/js/node/test/parallel/test-debugger-restart-message.js b/test/js/node/test/parallel/test-debugger-restart-message.js new file mode 100644 index 0000000000..e4001b47ee --- /dev/null +++ b/test/js/node/test/parallel/test-debugger-restart-message.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const assert = require('assert'); + +const RESTARTS = 10; + +const fixtures = require('../common/fixtures'); +const startCLI = require('../common/debugger'); + +// Using `restart` should result in only one "Connect/For help" message. +{ + const script = fixtures.path('debugger', 'three-lines.js'); + const cli = startCLI(['--port=0', script]); + + const listeningRegExp = /Debugger listening on/g; + + async function onWaitForInitialBreak() { + try { + await cli.waitForInitialBreak(); + await cli.waitForPrompt(); + assert.strictEqual(cli.output.match(listeningRegExp).length, 1); + + for (let i = 0; i < RESTARTS; i++) { + await cli.stepCommand('restart'); + assert.strictEqual(cli.output.match(listeningRegExp).length, 1); + } + } finally { + await cli.quit(); + } + } + + onWaitForInitialBreak(); +} diff --git a/test/js/node/test/parallel/test-delayed-require.js b/test/js/node/test/parallel/test-delayed-require.js new file mode 100644 index 0000000000..355d503d26 --- /dev/null +++ b/test/js/node/test/parallel/test-delayed-require.js @@ -0,0 +1,32 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +setTimeout(common.mustCall(function() { + const a = require(fixtures.path('a')); + assert.strictEqual('A' in a, true); + assert.strictEqual(a.A(), 'A'); + assert.strictEqual(a.D(), 'D'); +}), 50); diff --git a/test/js/node/test/parallel/test-dgram-abort-closed.js b/test/js/node/test/parallel/test-dgram-abort-closed.js new file mode 100644 index 0000000000..6346b5feae --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-abort-closed.js @@ -0,0 +1,10 @@ +'use strict'; +require('../common'); +const dgram = require('dgram'); + +const controller = new AbortController(); +const socket = dgram.createSocket({ type: 'udp4', signal: controller.signal }); + +socket.close(); + +controller.abort(); diff --git a/test/js/node/test/parallel/test-dgram-bind-default-address.js b/test/js/node/test/parallel/test-dgram-bind-default-address.js new file mode 100644 index 0000000000..130d614c58 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-bind-default-address.js @@ -0,0 +1,53 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +// Skip test in FreeBSD jails since 0.0.0.0 will resolve to default interface +if (common.inFreeBSDJail) + common.skip('In a FreeBSD jail'); + +const assert = require('assert'); +const dgram = require('dgram'); + +dgram.createSocket('udp4').bind(0, common.mustCall(function() { + assert.strictEqual(typeof this.address().port, 'number'); + assert.ok(isFinite(this.address().port)); + assert.ok(this.address().port > 0); + assert.strictEqual(this.address().address, '0.0.0.0'); + this.close(); +})); + +if (!common.hasIPv6) { + common.printSkipMessage('udp6 part of test, because no IPv6 support'); + return; +} + +dgram.createSocket('udp6').bind(0, common.mustCall(function() { + assert.strictEqual(typeof this.address().port, 'number'); + assert.ok(isFinite(this.address().port)); + assert.ok(this.address().port > 0); + let address = this.address().address; + if (address === '::ffff:0.0.0.0') + address = '::'; + assert.strictEqual(address, '::'); + this.close(); +})); diff --git a/test/js/node/test/parallel/test-dgram-bind.js b/test/js/node/test/parallel/test-dgram-bind.js new file mode 100644 index 0000000000..8ae1213c7f --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-bind.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const socket = dgram.createSocket('udp4'); + +socket.on('listening', common.mustCall(() => { + assert.throws(() => { + socket.bind(); + }, { + code: 'ERR_SOCKET_ALREADY_BOUND', + name: 'Error', + message: /^Socket is already bound$/ + }); + + socket.close(); +})); + +const result = socket.bind(); // Should not throw. + +assert.strictEqual(result, socket); // Should have returned itself. diff --git a/test/js/node/test/parallel/test-dgram-bytes-length.js b/test/js/node/test/parallel/test-dgram-bytes-length.js new file mode 100644 index 0000000000..abf8209b11 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-bytes-length.js @@ -0,0 +1,39 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const message = Buffer.from('Some bytes'); +const client = dgram.createSocket('udp4'); +client.send( + message, + 0, + message.length, + 41234, + 'localhost', + function(err, bytes) { + assert.strictEqual(bytes, message.length); + client.close(); + } +); diff --git a/test/js/node/test/parallel/test-dgram-close-in-listening.js b/test/js/node/test/parallel/test-dgram-close-in-listening.js new file mode 100644 index 0000000000..ae3ab71d7e --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-close-in-listening.js @@ -0,0 +1,26 @@ +'use strict'; +// Ensure that if a dgram socket is closed before the sendQueue is drained +// will not crash + +const common = require('../common'); +const dgram = require('dgram'); + +const buf = Buffer.alloc(1024, 42); + +const socket = dgram.createSocket('udp4'); + +socket.on('listening', function() { + socket.close(); +}); + +// Get a random port for send +const portGetter = dgram.createSocket('udp4') + .bind(0, 'localhost', common.mustCall(() => { + // Adds a listener to 'listening' to send the data when + // the socket is available + socket.send(buf, 0, buf.length, + portGetter.address().port, + portGetter.address().address); + + portGetter.close(); + })); diff --git a/test/js/node/test/parallel/test-dgram-close-is-not-callback.js b/test/js/node/test/parallel/test-dgram-close-is-not-callback.js new file mode 100644 index 0000000000..bfb0bcd2bb --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-close-is-not-callback.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); +const dgram = require('dgram'); + +const buf = Buffer.alloc(1024, 42); + +const socket = dgram.createSocket('udp4'); + +// Get a random port for send +const portGetter = dgram.createSocket('udp4') + .bind(0, 'localhost', common.mustCall(() => { + socket.send(buf, 0, buf.length, + portGetter.address().port, + portGetter.address().address); + + // If close callback is not function, ignore the argument. + socket.close('bad argument'); + portGetter.close(); + + socket.on('close', common.mustCall()); + })); diff --git a/test/js/node/test/parallel/test-dgram-close-signal.js b/test/js/node/test/parallel/test-dgram-close-signal.js new file mode 100644 index 0000000000..26de38b504 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-close-signal.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +{ + // Test bad signal. + assert.throws( + () => dgram.createSocket({ type: 'udp4', signal: {} }), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +} + +{ + // Test close. + const controller = new AbortController(); + const { signal } = controller; + const server = dgram.createSocket({ type: 'udp4', signal }); + server.on('close', common.mustCall()); + controller.abort(); +} + +{ + // Test close with pre-aborted signal. + const signal = AbortSignal.abort(); + const server = dgram.createSocket({ type: 'udp4', signal }); + server.on('close', common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-dgram-cluster-close-during-bind.js b/test/js/node/test/parallel/test-dgram-cluster-close-during-bind.js new file mode 100644 index 0000000000..065ff094f1 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-cluster-close-during-bind.js @@ -0,0 +1,38 @@ +'use strict'; +const common = require('../common'); +if (common.isWindows) + common.skip('dgram clustering is currently not supported on windows.'); + +const assert = require('assert'); +const cluster = require('cluster'); +const dgram = require('dgram'); + +if (cluster.isPrimary) { + cluster.fork(); +} else { + // When the socket attempts to bind, it requests a handle from the cluster. + // Close the socket before returning the handle from the cluster. + const socket = dgram.createSocket('udp4'); + const _getServer = cluster._getServer; + + cluster._getServer = common.mustCall(function(self, options, callback) { + socket.close(common.mustCall(() => { + _getServer.call(this, self, options, common.mustCall((err, handle) => { + assert.strictEqual(err, 0); + + // When the socket determines that it was already closed, it will + // close the handle. Use handle.close() to terminate the test. + const close = handle.close; + + handle.close = common.mustCall(function() { + setImmediate(() => cluster.worker.disconnect()); + return close.call(this); + }); + + callback(err, handle); + })); + })); + }); + + socket.bind(common.mustNotCall('Socket should not bind.')); +} diff --git a/test/js/node/test/parallel/test-dgram-cluster-close-in-listening.js b/test/js/node/test/parallel/test-dgram-cluster-close-in-listening.js new file mode 100644 index 0000000000..8cce540271 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-cluster-close-in-listening.js @@ -0,0 +1,29 @@ +'use strict'; +// Ensure that closing dgram sockets in 'listening' callbacks of cluster workers +// won't throw errors. + +const common = require('../common'); +const dgram = require('dgram'); +const cluster = require('cluster'); +if (common.isWindows) + common.skip('dgram clustering is currently not supported on windows.'); + +if (cluster.isPrimary) { + for (let i = 0; i < 3; i += 1) { + cluster.fork(); + } +} else { + const socket = dgram.createSocket('udp4'); + + socket.on('error', common.mustNotCall()); + + socket.on('listening', common.mustCall(() => { + socket.close(); + })); + + socket.on('close', common.mustCall(() => { + cluster.worker.disconnect(); + })); + + socket.bind(0); +} diff --git a/test/js/node/test/parallel/test-dgram-connect-send-callback-buffer-length.js b/test/js/node/test/parallel/test-dgram-connect-send-callback-buffer-length.js new file mode 100644 index 0000000000..723b2738e2 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-callback-buffer-length.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const dgram = require('dgram'); +const client = dgram.createSocket('udp4'); + +const buf = Buffer.allocUnsafe(256); +const offset = 20; +const len = buf.length - offset; + +const messageSent = common.mustSucceed(function messageSent(bytes) { + assert.notStrictEqual(bytes, buf.length); + assert.strictEqual(bytes, buf.length - offset); + client.close(); +}); + +client.bind(0, common.mustCall(() => { + client.connect(client.address().port, common.mustCall(() => { + client.send(buf, offset, len, messageSent); + })); +})); diff --git a/test/js/node/test/parallel/test-dgram-connect-send-callback-buffer.js b/test/js/node/test/parallel/test-dgram-connect-send-callback-buffer.js new file mode 100644 index 0000000000..d6ef1f510c --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-callback-buffer.js @@ -0,0 +1,20 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const buf = Buffer.allocUnsafe(256); + +const onMessage = common.mustSucceed((bytes) => { + assert.strictEqual(bytes, buf.length); + client.close(); +}); + +client.bind(0, common.mustCall(() => { + client.connect(client.address().port, common.mustCall(() => { + client.send(buf, onMessage); + })); +})); diff --git a/test/js/node/test/parallel/test-dgram-connect-send-callback-multi-buffer.js b/test/js/node/test/parallel/test-dgram-connect-send-callback-multi-buffer.js new file mode 100644 index 0000000000..defc73697c --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-callback-multi-buffer.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const messageSent = common.mustCall((err, bytes) => { + assert.strictEqual(bytes, buf1.length + buf2.length); +}); + +const buf1 = Buffer.alloc(256, 'x'); +const buf2 = Buffer.alloc(256, 'y'); + +client.on('listening', common.mustCall(() => { + const port = client.address().port; + client.connect(port, common.mustCall(() => { + client.send([buf1, buf2], messageSent); + })); +})); + +client.on('message', common.mustCall((buf, info) => { + const expected = Buffer.concat([buf1, buf2]); + assert.ok(buf.equals(expected), 'message was received correctly'); + client.close(); +})); + +client.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-connect-send-default-host.js b/test/js/node/test/parallel/test-dgram-connect-send-default-host.js new file mode 100644 index 0000000000..cd22cd2b64 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-default-host.js @@ -0,0 +1,48 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); +const server = dgram.createSocket('udp4'); + +const toSend = [Buffer.alloc(256, 'x'), + Buffer.alloc(256, 'y'), + Buffer.alloc(256, 'z'), + 'hello']; + +const received = []; + +server.on('listening', common.mustCall(() => { + const port = server.address().port; + client.connect(port, (err) => { + assert.ifError(err); + client.send(toSend[0], 0, toSend[0].length); + client.send(toSend[1]); + client.send([toSend[2]]); + client.send(toSend[3], 0, toSend[3].length); + + client.send(new Uint8Array(toSend[0]), 0, toSend[0].length); + client.send(new Uint8Array(toSend[1])); + client.send([new Uint8Array(toSend[2])]); + client.send(new Uint8Array(Buffer.from(toSend[3])), + 0, toSend[3].length); + }); +})); + +server.on('message', common.mustCall((buf, info) => { + received.push(buf.toString()); + + if (received.length === toSend.length * 2) { + // The replies may arrive out of order -> sort them before checking. + received.sort(); + + const expected = toSend.concat(toSend).map(String).sort(); + assert.deepStrictEqual(received, expected); + client.close(); + server.close(); + } +}, toSend.length * 2)); + +server.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-connect-send-empty-array.js b/test/js/node/test/parallel/test-dgram-connect-send-empty-array.js new file mode 100644 index 0000000000..7727e43041 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-empty-array.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +client.on('message', common.mustCall((buf, info) => { + const expected = Buffer.alloc(0); + assert.ok(buf.equals(expected), `Expected empty message but got ${buf}`); + client.close(); +})); + +client.on('listening', common.mustCall(() => { + client.connect(client.address().port, + common.localhostIPv4, + common.mustCall(() => client.send([]))); +})); + +client.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-connect-send-empty-buffer.js b/test/js/node/test/parallel/test-dgram-connect-send-empty-buffer.js new file mode 100644 index 0000000000..bce4c82e5c --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-empty-buffer.js @@ -0,0 +1,20 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +client.bind(0, common.mustCall(function() { + const port = this.address().port; + client.connect(port, common.mustCall(() => { + const buf = Buffer.alloc(0); + client.send(buf, 0, 0, common.mustSucceed()); + })); + + client.on('message', common.mustCall((buffer) => { + assert.strictEqual(buffer.length, 0); + client.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-dgram-connect-send-empty-packet.js b/test/js/node/test/parallel/test-dgram-connect-send-empty-packet.js new file mode 100644 index 0000000000..577a9cbefd --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-empty-packet.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +client.bind(0, common.mustCall(function() { + client.connect(client.address().port, common.mustCall(() => { + client.on('message', common.mustCall(callback)); + const buf = Buffer.alloc(1); + + const interval = setInterval(function() { + client.send(buf, 0, 0, common.mustCall(callback)); + }, 10); + + function callback(firstArg) { + // If client.send() callback, firstArg should be null. + // If client.on('message') listener, firstArg should be a 0-length buffer. + if (firstArg instanceof Buffer) { + assert.strictEqual(firstArg.length, 0); + clearInterval(interval); + client.close(); + } + } + })); +})); diff --git a/test/js/node/test/parallel/test-dgram-connect-send-multi-buffer-copy.js b/test/js/node/test/parallel/test-dgram-connect-send-multi-buffer-copy.js new file mode 100644 index 0000000000..0859a0cf86 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-multi-buffer-copy.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const onMessage = common.mustCall(common.mustSucceed((bytes) => { + assert.strictEqual(bytes, buf1.length + buf2.length); +})); + +const buf1 = Buffer.alloc(256, 'x'); +const buf2 = Buffer.alloc(256, 'y'); + +client.on('listening', common.mustCall(function() { + const toSend = [buf1, buf2]; + client.connect(client.address().port, common.mustCall(() => { + client.send(toSend, onMessage); + })); +})); + +client.on('message', common.mustCall(function onMessage(buf, info) { + const expected = Buffer.concat([buf1, buf2]); + assert.ok(buf.equals(expected), 'message was received correctly'); + client.close(); +})); + +client.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-connect-send-multi-string-array.js b/test/js/node/test/parallel/test-dgram-connect-send-multi-string-array.js new file mode 100644 index 0000000000..e69aa82d47 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-connect-send-multi-string-array.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const socket = dgram.createSocket('udp4'); +const data = ['foo', 'bar', 'baz']; + +socket.on('message', common.mustCall((msg, rinfo) => { + socket.close(); + assert.deepStrictEqual(msg.toString(), data.join('')); +})); + +socket.bind(0, () => { + socket.connect(socket.address().port, common.mustCall(() => { + socket.send(data); + })); +}); diff --git a/test/js/node/test/parallel/test-dgram-implicit-bind.js b/test/js/node/test/parallel/test-dgram-implicit-bind.js new file mode 100644 index 0000000000..b5aa2781ce --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-implicit-bind.js @@ -0,0 +1,46 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const dgram = require('dgram'); + +const source = dgram.createSocket('udp4'); +const target = dgram.createSocket('udp4'); +let messages = 0; + +target.on('message', common.mustCall(function(buf) { + if (buf.toString() === 'abc') ++messages; + if (buf.toString() === 'def') ++messages; + if (messages === 2) { + source.close(); + target.close(); + } +}, 2)); + +target.on('listening', common.mustCall(function() { + // Second .send() call should not throw a bind error. + const port = this.address().port; + source.send(Buffer.from('abc'), 0, 3, port, '127.0.0.1'); + source.send(Buffer.from('def'), 0, 3, port, '127.0.0.1'); +})); + +target.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-listen-after-bind.js b/test/js/node/test/parallel/test-dgram-listen-after-bind.js new file mode 100644 index 0000000000..a580a2386b --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-listen-after-bind.js @@ -0,0 +1,45 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const socket = dgram.createSocket('udp4'); + +socket.bind(); + +let fired = false; +const timer = setTimeout(() => { + socket.close(); +}, 100); + +socket.on('listening', common.mustCall(() => { + clearTimeout(timer); + fired = true; + socket.close(); +})); + +socket.on('close', common.mustCall(() => { + assert(fired, 'listening should fire after bind'); +})); diff --git a/test/js/node/test/parallel/test-dgram-oob-buffer.js b/test/js/node/test/parallel/test-dgram-oob-buffer.js new file mode 100644 index 0000000000..1e71815927 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-oob-buffer.js @@ -0,0 +1,45 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Some operating systems report errors when an UDP message is sent to an +// unreachable host. This error can be reported by sendto() and even by +// recvfrom(). Node should not propagate this error to the user. + +const common = require('../common'); +const dgram = require('dgram'); + +const socket = dgram.createSocket('udp4'); +const buf = Buffer.from([1, 2, 3, 4]); +const portGetter = dgram.createSocket('udp4') + .bind(0, 'localhost', common.mustCall(() => { + const { address, port } = portGetter.address(); + portGetter.close(common.mustCall(() => { + socket.send(buf, 0, 0, port, address, common.mustNotCall()); + socket.send(buf, 0, 4, port, address, common.mustNotCall()); + socket.send(buf, 1, 3, port, address, common.mustNotCall()); + socket.send(buf, 3, 1, port, address, common.mustNotCall()); + // Since length of zero means nothing, don't error despite OOB. + socket.send(buf, 4, 0, port, address, common.mustNotCall()); + + socket.close(); + })); + })); diff --git a/test/js/node/test/parallel/test-dgram-ref.js b/test/js/node/test/parallel/test-dgram-ref.js new file mode 100644 index 0000000000..0c5474b118 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-ref.js @@ -0,0 +1,35 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const dgram = require('dgram'); + +// Should not hang, see https://github.com/nodejs/node-v0.x-archive/issues/1282 +dgram.createSocket('udp4'); +dgram.createSocket('udp6'); + +{ + // Test the case of ref()'ing a socket with no handle. + const s = dgram.createSocket('udp4'); + + s.close(common.mustCall(() => s.ref())); +} diff --git a/test/js/node/test/parallel/test-dgram-send-callback-buffer-empty-address.js b/test/js/node/test/parallel/test-dgram-send-callback-buffer-empty-address.js new file mode 100644 index 0000000000..f6254ec43e --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-callback-buffer-empty-address.js @@ -0,0 +1,16 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const buf = Buffer.alloc(256, 'x'); + +const onMessage = common.mustSucceed((bytes) => { + assert.strictEqual(bytes, buf.length); + client.close(); +}); + +client.bind(0, () => client.send(buf, client.address().port, onMessage)); diff --git a/test/js/node/test/parallel/test-dgram-send-callback-buffer-length-empty-address.js b/test/js/node/test/parallel/test-dgram-send-callback-buffer-length-empty-address.js new file mode 100644 index 0000000000..a95018d14b --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-callback-buffer-length-empty-address.js @@ -0,0 +1,21 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const dgram = require('dgram'); +const client = dgram.createSocket('udp4'); + +const buf = Buffer.alloc(256, 'x'); +const offset = 20; +const len = buf.length - offset; + +const onMessage = common.mustSucceed(function messageSent(bytes) { + assert.notStrictEqual(bytes, buf.length); + assert.strictEqual(bytes, buf.length - offset); + client.close(); +}); + +client.bind(0, () => client.send(buf, offset, len, + client.address().port, + onMessage)); diff --git a/test/js/node/test/parallel/test-dgram-send-callback-buffer-length.js b/test/js/node/test/parallel/test-dgram-send-callback-buffer-length.js new file mode 100644 index 0000000000..f570ecb20d --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-callback-buffer-length.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const dgram = require('dgram'); +const client = dgram.createSocket('udp4'); + +const buf = Buffer.allocUnsafe(256); +const offset = 20; +const len = buf.length - offset; + +const messageSent = common.mustSucceed(function messageSent(bytes) { + assert.notStrictEqual(bytes, buf.length); + assert.strictEqual(bytes, buf.length - offset); + client.close(); +}); + +client.bind(0, () => client.send(buf, offset, len, + client.address().port, + '127.0.0.1', + messageSent)); diff --git a/test/js/node/test/parallel/test-dgram-send-callback-buffer.js b/test/js/node/test/parallel/test-dgram-send-callback-buffer.js new file mode 100644 index 0000000000..11f8208de9 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-callback-buffer.js @@ -0,0 +1,19 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const buf = Buffer.allocUnsafe(256); + +const onMessage = common.mustSucceed((bytes) => { + assert.strictEqual(bytes, buf.length); + client.close(); +}); + +client.bind(0, () => client.send(buf, + client.address().port, + common.localhostIPv4, + onMessage)); diff --git a/test/js/node/test/parallel/test-dgram-send-callback-multi-buffer-empty-address.js b/test/js/node/test/parallel/test-dgram-send-callback-multi-buffer-empty-address.js new file mode 100644 index 0000000000..37e9e04c44 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-callback-multi-buffer-empty-address.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const messageSent = common.mustSucceed(function messageSent(bytes) { + assert.strictEqual(bytes, buf1.length + buf2.length); +}); + +const buf1 = Buffer.alloc(256, 'x'); +const buf2 = Buffer.alloc(256, 'y'); + +client.on('listening', function() { + const port = this.address().port; + client.send([buf1, buf2], port, messageSent); +}); + +client.on('message', common.mustCall(function onMessage(buf) { + const expected = Buffer.concat([buf1, buf2]); + assert.ok(buf.equals(expected), 'message was received correctly'); + client.close(); +})); + +client.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-send-callback-multi-buffer.js b/test/js/node/test/parallel/test-dgram-send-callback-multi-buffer.js new file mode 100644 index 0000000000..09f01f6e8a --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-callback-multi-buffer.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const messageSent = common.mustCall((err, bytes) => { + assert.strictEqual(bytes, buf1.length + buf2.length); +}); + +const buf1 = Buffer.alloc(256, 'x'); +const buf2 = Buffer.alloc(256, 'y'); + +client.on('listening', () => { + const port = client.address().port; + client.send([buf1, buf2], port, common.localhostIPv4, messageSent); +}); + +client.on('message', common.mustCall((buf, info) => { + const expected = Buffer.concat([buf1, buf2]); + assert.ok(buf.equals(expected), 'message was received correctly'); + client.close(); +})); + +client.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-send-callback-recursive.js b/test/js/node/test/parallel/test-dgram-send-callback-recursive.js new file mode 100644 index 0000000000..1a4c7c84fc --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-callback-recursive.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const dgram = require('dgram'); +const client = dgram.createSocket('udp4'); +const chunk = 'abc'; +let received = 0; +let sent = 0; +const limit = 10; +let async = false; +let port; + +function onsend() { + if (sent++ < limit) { + client.send(chunk, 0, chunk.length, port, common.localhostIPv4, onsend); + } else { + assert.strictEqual(async, true); + } +} + +client.on('listening', function() { + port = this.address().port; + + process.nextTick(() => { + async = true; + }); + + onsend(); +}); + +client.on('message', (buf, info) => { + received++; + if (received === limit) { + client.close(); + } +}); + +client.on('close', common.mustCall(function() { + assert.strictEqual(received, limit); +})); + +client.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-send-empty-array.js b/test/js/node/test/parallel/test-dgram-send-empty-array.js new file mode 100644 index 0000000000..178b72bb12 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-empty-array.js @@ -0,0 +1,25 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +let interval; + +client.on('message', common.mustCall(function onMessage(buf, info) { + const expected = Buffer.alloc(0); + assert.ok(buf.equals(expected), `Expected empty message but got ${buf}`); + clearInterval(interval); + client.close(); +})); + +client.on('listening', common.mustCall(function() { + interval = setInterval(function() { + client.send([], client.address().port, common.localhostIPv4); + }, 10); +})); + +client.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-send-empty-buffer.js b/test/js/node/test/parallel/test-dgram-send-empty-buffer.js new file mode 100644 index 0000000000..76b014b893 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-empty-buffer.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +client.bind(0, common.mustCall(function() { + const port = this.address().port; + + client.on('message', common.mustCall(function onMessage(buffer) { + assert.strictEqual(buffer.length, 0); + clearInterval(interval); + client.close(); + })); + + const buf = Buffer.alloc(0); + const interval = setInterval(function() { + client.send(buf, 0, 0, port, '127.0.0.1', common.mustCall()); + }, 10); +})); diff --git a/test/js/node/test/parallel/test-dgram-send-empty-packet.js b/test/js/node/test/parallel/test-dgram-send-empty-packet.js new file mode 100644 index 0000000000..eb4f79e8aa --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-empty-packet.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +client.bind(0, common.mustCall(function() { + + client.on('message', common.mustCall(callback)); + + const port = this.address().port; + const buf = Buffer.alloc(1); + + const interval = setInterval(function() { + client.send(buf, 0, 0, port, '127.0.0.1', common.mustCall(callback)); + }, 10); + + function callback(firstArg) { + // If client.send() callback, firstArg should be null. + // If client.on('message') listener, firstArg should be a 0-length buffer. + if (firstArg instanceof Buffer) { + assert.strictEqual(firstArg.length, 0); + clearInterval(interval); + client.close(); + } + } +})); diff --git a/test/js/node/test/parallel/test-dgram-send-multi-buffer-copy.js b/test/js/node/test/parallel/test-dgram-send-multi-buffer-copy.js new file mode 100644 index 0000000000..2ee87494b0 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-multi-buffer-copy.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); + +const client = dgram.createSocket('udp4'); + +const onMessage = common.mustCall(function(err, bytes) { + assert.strictEqual(bytes, buf1.length + buf2.length); +}); + +const buf1 = Buffer.alloc(256, 'x'); +const buf2 = Buffer.alloc(256, 'y'); + +client.on('listening', function() { + const toSend = [buf1, buf2]; + client.send(toSend, this.address().port, common.localhostIPv4, onMessage); + toSend.splice(0, 2); +}); + +client.on('message', common.mustCall(function onMessage(buf, info) { + const expected = Buffer.concat([buf1, buf2]); + assert.ok(buf.equals(expected), 'message was received correctly'); + client.close(); +})); + +client.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-send-multi-string-array.js b/test/js/node/test/parallel/test-dgram-send-multi-string-array.js new file mode 100644 index 0000000000..8d73a6d183 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-send-multi-string-array.js @@ -0,0 +1,13 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const socket = dgram.createSocket('udp4'); +const data = ['foo', 'bar', 'baz']; + +socket.on('message', common.mustCall((msg, rinfo) => { + socket.close(); + assert.deepStrictEqual(msg.toString(), data.join('')); +})); + +socket.bind(() => socket.send(data, socket.address().port, 'localhost')); diff --git a/test/js/node/test/parallel/test-dgram-udp4.js b/test/js/node/test/parallel/test-dgram-udp4.js new file mode 100644 index 0000000000..c7ee34b2c1 --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-udp4.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const message_to_send = 'A message to send'; + +const server = dgram.createSocket('udp4'); +server.on('message', common.mustCall((msg, rinfo) => { + assert.strictEqual(rinfo.address, common.localhostIPv4); + assert.strictEqual(msg.toString(), message_to_send.toString()); + server.send(msg, 0, msg.length, rinfo.port, rinfo.address); +})); +server.on('listening', common.mustCall(() => { + const client = dgram.createSocket('udp4'); + const port = server.address().port; + client.on('message', common.mustCall((msg, rinfo) => { + assert.strictEqual(rinfo.address, common.localhostIPv4); + assert.strictEqual(rinfo.port, port); + assert.strictEqual(msg.toString(), message_to_send.toString()); + client.close(); + server.close(); + })); + client.send(message_to_send, + 0, + message_to_send.length, + port, + 'localhost'); + client.on('close', common.mustCall()); +})); +server.on('close', common.mustCall()); +server.bind(0); diff --git a/test/js/node/test/parallel/test-dgram-unref-in-cluster.js b/test/js/node/test/parallel/test-dgram-unref-in-cluster.js new file mode 100644 index 0000000000..40833a129d --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-unref-in-cluster.js @@ -0,0 +1,23 @@ +'use strict'; +const common = require('../common'); +const dgram = require('dgram'); +const cluster = require('cluster'); +const assert = require('assert'); + +if (common.isWindows) + common.skip('dgram clustering is currently not supported on Windows.'); + +if (cluster.isPrimary) { + cluster.fork(); +} else { + const socket = dgram.createSocket('udp4'); + socket.unref(); + socket.bind(); + socket.on('listening', common.mustCall(() => { + const sockets = process.getActiveResourcesInfo().filter((item) => { + return item === 'UDPWrap'; + }); + assert.ok(sockets.length === 0); + process.disconnect(); + })); +} diff --git a/test/js/node/test/parallel/test-dgram-unref.js b/test/js/node/test/parallel/test-dgram-unref.js new file mode 100644 index 0000000000..930cbf095c --- /dev/null +++ b/test/js/node/test/parallel/test-dgram-unref.js @@ -0,0 +1,40 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const dgram = require('dgram'); + +{ + // Test the case of unref()'ing a socket with a handle. + const s = dgram.createSocket('udp4'); + s.bind(); + s.unref(); +} + +{ + // Test the case of unref()'ing a socket with no handle. + const s = dgram.createSocket('udp4'); + + s.close(common.mustCall(() => s.unref())); +} + +setTimeout(common.mustNotCall(), 1000).unref(); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-bind-store.js b/test/js/node/test/parallel/test-diagnostics-channel-bind-store.js new file mode 100644 index 0000000000..81fb299c2f --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-bind-store.js @@ -0,0 +1,108 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const dc = require('diagnostics_channel'); +const { AsyncLocalStorage } = require('async_hooks'); + +let n = 0; +const thisArg = new Date(); +const inputs = [ + { foo: 'bar' }, + { baz: 'buz' }, +]; + +const channel = dc.channel('test'); + +// Bind a storage directly to published data +const store1 = new AsyncLocalStorage(); +channel.bindStore(store1); +let store1bound = true; + +// Bind a store with transformation of published data +const store2 = new AsyncLocalStorage(); +channel.bindStore(store2, common.mustCall((data) => { + assert.strictEqual(data, inputs[n]); + return { data }; +}, 4)); + +// Regular subscribers should see publishes from runStores calls +channel.subscribe(common.mustCall((data) => { + if (store1bound) { + assert.deepStrictEqual(data, store1.getStore()); + } + assert.deepStrictEqual({ data }, store2.getStore()); + assert.strictEqual(data, inputs[n]); +}, 4)); + +// Verify stores are empty before run +assert.strictEqual(store1.getStore(), undefined); +assert.strictEqual(store2.getStore(), undefined); + +channel.runStores(inputs[n], common.mustCall(function(a, b) { + // Verify this and argument forwarding + assert.strictEqual(this, thisArg); + assert.strictEqual(a, 1); + assert.strictEqual(b, 2); + + // Verify store 1 state matches input + assert.strictEqual(store1.getStore(), inputs[n]); + + // Verify store 2 state has expected transformation + assert.deepStrictEqual(store2.getStore(), { data: inputs[n] }); + + // Should support nested contexts + n++; + channel.runStores(inputs[n], common.mustCall(function() { + // Verify this and argument forwarding + assert.strictEqual(this, undefined); + + // Verify store 1 state matches input + assert.strictEqual(store1.getStore(), inputs[n]); + + // Verify store 2 state has expected transformation + assert.deepStrictEqual(store2.getStore(), { data: inputs[n] }); + })); + n--; + + // Verify store 1 state matches input + assert.strictEqual(store1.getStore(), inputs[n]); + + // Verify store 2 state has expected transformation + assert.deepStrictEqual(store2.getStore(), { data: inputs[n] }); +}), thisArg, 1, 2); + +// Verify stores are empty after run +assert.strictEqual(store1.getStore(), undefined); +assert.strictEqual(store2.getStore(), undefined); + +// Verify unbinding works +assert.ok(channel.unbindStore(store1)); +store1bound = false; + +// Verify unbinding a store that is not bound returns false +assert.ok(!channel.unbindStore(store1)); + +n++; +channel.runStores(inputs[n], common.mustCall(() => { + // Verify after unbinding store 1 will remain undefined + assert.strictEqual(store1.getStore(), undefined); + + // Verify still bound store 2 receives expected data + assert.deepStrictEqual(store2.getStore(), { data: inputs[n] }); +})); + +// Contain transformer errors and emit on next tick +const fail = new Error('fail'); +channel.bindStore(store1, () => { + throw fail; +}); + +let calledRunStores = false; +process.once('uncaughtException', common.mustCall((err) => { + assert.strictEqual(calledRunStores, true); + assert.strictEqual(err, fail); +})); + +channel.runStores(inputs[n], common.mustCall()); +calledRunStores = true; diff --git a/test/js/node/test/parallel/test-diagnostics-channel-has-subscribers.js b/test/js/node/test/parallel/test-diagnostics-channel-has-subscribers.js new file mode 100644 index 0000000000..de37267555 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-has-subscribers.js @@ -0,0 +1,10 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { channel, hasSubscribers } = require('diagnostics_channel'); + +const dc = channel('test'); +assert.ok(!hasSubscribers('test')); + +dc.subscribe(() => {}); +assert.ok(hasSubscribers('test')); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-object-channel-pub-sub.js b/test/js/node/test/parallel/test-diagnostics-channel-object-channel-pub-sub.js new file mode 100644 index 0000000000..9498419b80 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-object-channel-pub-sub.js @@ -0,0 +1,46 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); +const { Channel } = dc; + +const input = { + foo: 'bar' +}; + +// Should not have named channel +assert.ok(!dc.hasSubscribers('test')); + +// Individual channel objects can be created to avoid future lookups +const channel = dc.channel('test'); +assert.ok(channel instanceof Channel); + +// No subscribers yet, should not publish +assert.ok(!channel.hasSubscribers); + +const subscriber = common.mustCall((message, name) => { + assert.strictEqual(name, channel.name); + assert.deepStrictEqual(message, input); +}); + +// Now there's a subscriber, should publish +channel.subscribe(subscriber); +assert.ok(channel.hasSubscribers); + +// The ActiveChannel prototype swap should not fail instanceof +assert.ok(channel instanceof Channel); + +// Should trigger the subscriber once +channel.publish(input); + +// Should not publish after subscriber is unsubscribed +assert.ok(channel.unsubscribe(subscriber)); +assert.ok(!channel.hasSubscribers); + +// unsubscribe() should return false when subscriber is not found +assert.ok(!channel.unsubscribe(subscriber)); + +assert.throws(() => { + channel.subscribe(null); +}, { code: 'ERR_INVALID_ARG_TYPE' }); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-pub-sub.js b/test/js/node/test/parallel/test-diagnostics-channel-pub-sub.js new file mode 100644 index 0000000000..a7232ab58c --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-pub-sub.js @@ -0,0 +1,51 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); +const { Channel } = dc; + +const name = 'test'; +const input = { + foo: 'bar' +}; + +// Individual channel objects can be created to avoid future lookups +const channel = dc.channel(name); +assert.ok(channel instanceof Channel); + +// No subscribers yet, should not publish +assert.ok(!channel.hasSubscribers); + +const subscriber = common.mustCall((message, name) => { + assert.strictEqual(name, channel.name); + assert.deepStrictEqual(message, input); +}); + +// Now there's a subscriber, should publish +dc.subscribe(name, subscriber); +assert.ok(channel.hasSubscribers); + +// The ActiveChannel prototype swap should not fail instanceof +assert.ok(channel instanceof Channel); + +// Should trigger the subscriber once +channel.publish(input); + +// Should not publish after subscriber is unsubscribed +assert.ok(dc.unsubscribe(name, subscriber)); +assert.ok(!channel.hasSubscribers); + +// unsubscribe() should return false when subscriber is not found +assert.ok(!dc.unsubscribe(name, subscriber)); + +assert.throws(() => { + dc.subscribe(name, null); +}, { code: 'ERR_INVALID_ARG_TYPE' }); + +// Reaching zero subscribers should not delete from the channels map as there +// will be no more weakref to incRef if another subscribe happens while the +// channel object itself exists. +channel.subscribe(subscriber); +channel.unsubscribe(subscriber); +channel.subscribe(subscriber); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-safe-subscriber-errors.js b/test/js/node/test/parallel/test-diagnostics-channel-safe-subscriber-errors.js new file mode 100644 index 0000000000..b0c5ab2480 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-safe-subscriber-errors.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const input = { + foo: 'bar' +}; + +const channel = dc.channel('fail'); + +const error = new Error('nope'); + +process.on('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err, error); +})); + +channel.subscribe(common.mustCall((message, name) => { + throw error; +})); + +// The failing subscriber should not stop subsequent subscribers from running +channel.subscribe(common.mustCall()); + +// Publish should continue without throwing +const fn = common.mustCall(); +channel.publish(input); +fn(); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-sync-unsubscribe.js b/test/js/node/test/parallel/test-diagnostics-channel-sync-unsubscribe.js new file mode 100644 index 0000000000..87bf44249f --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-sync-unsubscribe.js @@ -0,0 +1,14 @@ +'use strict'; + +const common = require('../common'); +const dc = require('node:diagnostics_channel'); + +const channel_name = 'test:channel'; +const published_data = 'some message'; + +const onMessageHandler = common.mustCall(() => dc.unsubscribe(channel_name, onMessageHandler)); + +dc.subscribe(channel_name, onMessageHandler); + +// This must not throw. +dc.channel(channel_name).publish(published_data); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback-error.js b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback-error.js new file mode 100644 index 0000000000..672500e768 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback-error.js @@ -0,0 +1,36 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); + +const expectedError = new Error('test'); +const input = { foo: 'bar' }; +const thisArg = { baz: 'buz' }; + +function check(found) { + assert.deepStrictEqual(found, input); +} + +const handlers = { + start: common.mustCall(check), + end: common.mustCall(check), + asyncStart: common.mustCall(check), + asyncEnd: common.mustCall(check), + error: common.mustCall((found) => { + check(found); + assert.deepStrictEqual(found.error, expectedError); + }) +}; + +channel.subscribe(handlers); + +channel.traceCallback(function(cb, err) { + assert.deepStrictEqual(this, thisArg); + setImmediate(cb, err); +}, 0, input, thisArg, common.mustCall((err, res) => { + assert.strictEqual(err, expectedError); + assert.strictEqual(res, undefined); +}), expectedError); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback-run-stores.js b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback-run-stores.js new file mode 100644 index 0000000000..874433efd2 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback-run-stores.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +const { AsyncLocalStorage } = require('async_hooks'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); +const store = new AsyncLocalStorage(); + +const firstContext = { foo: 'bar' }; +const secondContext = { baz: 'buz' }; + +channel.start.bindStore(store, common.mustCall(() => { + return firstContext; +})); + +channel.asyncStart.bindStore(store, common.mustCall(() => { + return secondContext; +})); + +assert.strictEqual(store.getStore(), undefined); +channel.traceCallback(common.mustCall((cb) => { + assert.deepStrictEqual(store.getStore(), firstContext); + setImmediate(cb); +}), 0, {}, null, common.mustCall(() => { + assert.deepStrictEqual(store.getStore(), secondContext); +})); +assert.strictEqual(store.getStore(), undefined); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback.js b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback.js new file mode 100644 index 0000000000..d306f0f51b --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-callback.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); + +const expectedResult = { foo: 'bar' }; +const input = { foo: 'bar' }; +const thisArg = { baz: 'buz' }; + +function check(found) { + assert.deepStrictEqual(found, input); +} + +function checkAsync(found) { + check(found); + assert.strictEqual(found.error, undefined); + assert.deepStrictEqual(found.result, expectedResult); +} + +const handlers = { + start: common.mustCall(check), + end: common.mustCall(check), + asyncStart: common.mustCall(checkAsync), + asyncEnd: common.mustCall(checkAsync), + error: common.mustNotCall() +}; + +channel.subscribe(handlers); + +channel.traceCallback(function(cb, err, res) { + assert.deepStrictEqual(this, thisArg); + setImmediate(cb, err, res); +}, 0, input, thisArg, common.mustCall((err, res) => { + assert.strictEqual(err, null); + assert.deepStrictEqual(res, expectedResult); +}), null, expectedResult); + +assert.throws(() => { + channel.traceCallback(common.mustNotCall(), 0, input, thisArg, 1, 2, 3); +}, /"callback" argument must be of type function/); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-promise-run-stores.js b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-promise-run-stores.js new file mode 100644 index 0000000000..5292a6fe09 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-promise-run-stores.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const { setTimeout } = require('node:timers/promises'); +const { AsyncLocalStorage } = require('async_hooks'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); +const store = new AsyncLocalStorage(); + +const firstContext = { foo: 'bar' }; +const secondContext = { baz: 'buz' }; + +channel.start.bindStore(store, common.mustCall(() => { + return firstContext; +})); + +channel.asyncStart.bindStore(store, common.mustNotCall(() => { + return secondContext; +})); + +assert.strictEqual(store.getStore(), undefined); +channel.tracePromise(common.mustCall(async () => { + assert.deepStrictEqual(store.getStore(), firstContext); + await setTimeout(1); + // Should _not_ switch to second context as promises don't have an "after" + // point at which to do a runStores. + assert.deepStrictEqual(store.getStore(), firstContext); +})); +assert.strictEqual(store.getStore(), undefined); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-promise.js b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-promise.js new file mode 100644 index 0000000000..20892ca40f --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-promise.js @@ -0,0 +1,41 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); + +const expectedResult = { foo: 'bar' }; +const input = { foo: 'bar' }; +const thisArg = { baz: 'buz' }; + +function check(found) { + assert.deepStrictEqual(found, input); +} + +function checkAsync(found) { + check(found); + assert.strictEqual(found.error, undefined); + assert.deepStrictEqual(found.result, expectedResult); +} + +const handlers = { + start: common.mustCall(check), + end: common.mustCall(check), + asyncStart: common.mustCall(checkAsync), + asyncEnd: common.mustCall(checkAsync), + error: common.mustNotCall() +}; + +channel.subscribe(handlers); + +channel.tracePromise(function(value) { + assert.deepStrictEqual(this, thisArg); + return Promise.resolve(value); +}, input, thisArg, expectedResult).then( + common.mustCall((value) => { + assert.deepStrictEqual(value, expectedResult); + }), + common.mustNotCall() +); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync-error.js b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync-error.js new file mode 100644 index 0000000000..0965bf3fb4 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync-error.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); + +const expectedError = new Error('test'); +const input = { foo: 'bar' }; +const thisArg = { baz: 'buz' }; + +function check(found) { + assert.deepStrictEqual(found, input); +} + +const handlers = { + start: common.mustCall(check), + end: common.mustCall(check), + asyncStart: common.mustNotCall(), + asyncEnd: common.mustNotCall(), + error: common.mustCall((found) => { + check(found); + assert.deepStrictEqual(found.error, expectedError); + }) +}; + +channel.subscribe(handlers); +try { + channel.traceSync(function(err) { + assert.deepStrictEqual(this, thisArg); + assert.strictEqual(err, expectedError); + throw err; + }, input, thisArg, expectedError); + + throw new Error('It should not reach this error'); +} catch (error) { + assert.deepStrictEqual(error, expectedError); +} diff --git a/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync-run-stores.js b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync-run-stores.js new file mode 100644 index 0000000000..3ffe5e6720 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync-run-stores.js @@ -0,0 +1,21 @@ +'use strict'; + +const common = require('../common'); +const { AsyncLocalStorage } = require('async_hooks'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); +const store = new AsyncLocalStorage(); + +const context = { foo: 'bar' }; + +channel.start.bindStore(store, common.mustCall(() => { + return context; +})); + +assert.strictEqual(store.getStore(), undefined); +channel.traceSync(common.mustCall(() => { + assert.deepStrictEqual(store.getStore(), context); +})); +assert.strictEqual(store.getStore(), undefined); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync.js b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync.js new file mode 100644 index 0000000000..b28b47256b --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-tracing-channel-sync.js @@ -0,0 +1,46 @@ +'use strict'; + +const common = require('../common'); +const dc = require('diagnostics_channel'); +const assert = require('assert'); + +const channel = dc.tracingChannel('test'); + +const expectedResult = { foo: 'bar' }; +const input = { foo: 'bar' }; +const thisArg = { baz: 'buz' }; +const arg = { baz: 'buz' }; + +function check(found) { + assert.strictEqual(found, input); +} + +const handlers = { + start: common.mustCall(check), + end: common.mustCall((found) => { + check(found); + assert.strictEqual(found.result, expectedResult); + }), + asyncStart: common.mustNotCall(), + asyncEnd: common.mustNotCall(), + error: common.mustNotCall() +}; + +assert.strictEqual(channel.start.hasSubscribers, false); +channel.subscribe(handlers); +assert.strictEqual(channel.start.hasSubscribers, true); +const result1 = channel.traceSync(function(arg1) { + assert.strictEqual(arg1, arg); + assert.strictEqual(this, thisArg); + return expectedResult; +}, input, thisArg, arg); +assert.strictEqual(result1, expectedResult); + +channel.unsubscribe(handlers); +assert.strictEqual(channel.start.hasSubscribers, false); +const result2 = channel.traceSync(function(arg1) { + assert.strictEqual(arg1, arg); + assert.strictEqual(this, thisArg); + return expectedResult; +}, input, thisArg, arg); +assert.strictEqual(result2, expectedResult); diff --git a/test/js/node/test/parallel/test-diagnostics-channel-udp.js b/test/js/node/test/parallel/test-diagnostics-channel-udp.js new file mode 100644 index 0000000000..79869c6d80 --- /dev/null +++ b/test/js/node/test/parallel/test-diagnostics-channel-udp.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const dc = require('diagnostics_channel'); + +const udpSocketChannel = dc.channel('udp.socket'); + +const isUDPSocket = (socket) => socket instanceof dgram.Socket; + +udpSocketChannel.subscribe(common.mustCall(({ socket }) => { + assert.strictEqual(isUDPSocket(socket), true); +})); +const socket = dgram.createSocket('udp4'); +socket.close(); diff --git a/test/js/node/test/parallel/test-domain-crypto.js b/test/js/node/test/parallel/test-domain-crypto.js new file mode 100644 index 0000000000..e0a470bd9d --- /dev/null +++ b/test/js/node/test/parallel/test-domain-crypto.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('node compiled without OpenSSL.'); + +const crypto = require('crypto'); + +// Pollution of global is intentional as part of test. +common.allowGlobals(require('domain')); +// See https://github.com/nodejs/node/commit/d1eff9ab +global.domain = require('domain'); + +// Should not throw a 'TypeError: undefined is not a function' exception +crypto.randomBytes(8); +crypto.randomBytes(8, common.mustSucceed()); +const buf = Buffer.alloc(8); +crypto.randomFillSync(buf); +crypto.pseudoRandomBytes(8); +crypto.pseudoRandomBytes(8, common.mustSucceed()); +crypto.pbkdf2('password', 'salt', 8, 8, 'sha1', common.mustSucceed()); diff --git a/test/js/node/test/parallel/test-domain-ee-error-listener.js b/test/js/node/test/parallel/test-domain-ee-error-listener.js new file mode 100644 index 0000000000..69041c7523 --- /dev/null +++ b/test/js/node/test/parallel/test-domain-ee-error-listener.js @@ -0,0 +1,20 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain').create(); +const EventEmitter = require('events'); + +domain.on('error', common.mustNotCall()); + +const ee = new EventEmitter(); + +const plainObject = { justAn: 'object' }; +ee.once('error', common.mustCall((err) => { + assert.deepStrictEqual(err, plainObject); +})); +ee.emit('error', plainObject); + +const err = new Error('test error'); +ee.once('error', common.expectsError(err)); +ee.emit('error', err); diff --git a/test/js/node/test/parallel/test-domain-nested-throw.js b/test/js/node/test/parallel/test-domain-nested-throw.js new file mode 100644 index 0000000000..ec016ada72 --- /dev/null +++ b/test/js/node/test/parallel/test-domain-nested-throw.js @@ -0,0 +1,101 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const domain = require('domain'); + +if (process.argv[2] !== 'child') { + parent(); + return; +} + +function parent() { + const node = process.execPath; + const spawn = require('child_process').spawn; + const opt = { stdio: 'inherit' }; + const child = spawn(node, [__filename, 'child'], opt); + child.on('exit', function(c) { + assert(!c); + console.log('ok'); + }); +} + +let gotDomain1Error = false; +let gotDomain2Error = false; + +let threw1 = false; +let threw2 = false; + +function throw1() { + threw1 = true; + throw new Error('handled by domain1'); +} + +function throw2() { + threw2 = true; + throw new Error('handled by domain2'); +} + +function inner(throw1, throw2) { + const domain1 = domain.createDomain(); + + domain1.on('error', function(err) { + if (gotDomain1Error) { + console.error('got domain 1 twice'); + process.exit(1); + } + gotDomain1Error = true; + throw2(); + }); + + domain1.run(function() { + throw1(); + }); +} + +function outer() { + const domain2 = domain.createDomain(); + + domain2.on('error', function(err) { + if (gotDomain2Error) { + console.error('got domain 2 twice'); + process.exit(1); + } + gotDomain2Error = true; + }); + + domain2.run(function() { + inner(throw1, throw2); + }); +} + +process.on('exit', function() { + assert(gotDomain1Error); + assert(gotDomain2Error); + assert(threw1); + assert(threw2); + console.log('ok'); +}); + +outer(); diff --git a/test/js/node/test/parallel/test-domain-vm-promise-isolation.js b/test/js/node/test/parallel/test-domain-vm-promise-isolation.js new file mode 100644 index 0000000000..41aed1ee33 --- /dev/null +++ b/test/js/node/test/parallel/test-domain-vm-promise-isolation.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const domain = require('domain'); +const vm = require('vm'); + +// A promise created in a VM should not include a domain field but +// domains should still be able to propagate through them. +// +// See; https://github.com/nodejs/node/issues/40999 + +const context = vm.createContext({}); + +function run(code) { + const d = domain.createDomain(); + d.run(common.mustCall(() => { + const p = vm.runInContext(code, context)(); + assert.strictEqual(p.domain, undefined); + p.then(common.mustCall(() => { + assert.strictEqual(process.domain, d); + })); + })); +} + +for (let i = 0; i < 1000; i++) { + run('async () => null'); +} diff --git a/test/js/node/test/parallel/test-dsa-fips-invalid-key.js b/test/js/node/test/parallel/test-dsa-fips-invalid-key.js new file mode 100644 index 0000000000..05cc1d143a --- /dev/null +++ b/test/js/node/test/parallel/test-dsa-fips-invalid-key.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +if (!common.hasFipsCrypto) + common.skip('node compiled without FIPS OpenSSL.'); + +const assert = require('assert'); +const crypto = require('crypto'); + +const input = 'hello'; + +const dsapri = fixtures.readKey('dsa_private_1025.pem'); +const sign = crypto.createSign('SHA1'); +sign.update(input); + +assert.throws(function() { + sign.sign(dsapri); +}, /PEM_read_bio_PrivateKey failed/); diff --git a/test/js/node/test/parallel/test-error-prepare-stack-trace.js b/test/js/node/test/parallel/test-error-prepare-stack-trace.js new file mode 100644 index 0000000000..1ad29efb16 --- /dev/null +++ b/test/js/node/test/parallel/test-error-prepare-stack-trace.js @@ -0,0 +1,30 @@ +'use strict'; + +require('../common'); +const { spawnSyncAndExitWithoutError } = require('../common/child_process'); +const assert = require('assert'); + +// Verify that the default Error.prepareStackTrace is present. +assert.strictEqual(typeof Error.prepareStackTrace, 'function'); + +// Error.prepareStackTrace() can be overridden. +{ + let prepareCalled = false; + Error.prepareStackTrace = (_error, trace) => { + prepareCalled = true; + }; + try { + throw new Error('foo'); + } catch (err) { + err.stack; // eslint-disable-line no-unused-expressions + } + assert(prepareCalled); +} + +if (process.argv[2] !== 'child') { + // Verify that the above test still passes when source-maps support is + // enabled. + spawnSyncAndExitWithoutError( + process.execPath, + ['--enable-source-maps', __filename, 'child']); +} diff --git a/test/js/node/test/parallel/test-eslint-alphabetize-errors.js b/test/js/node/test/parallel/test-eslint-alphabetize-errors.js new file mode 100644 index 0000000000..18c3aed3af --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-alphabetize-errors.js @@ -0,0 +1,63 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/alphabetize-errors'); + +new RuleTester().run('alphabetize-errors', rule, { + valid: [ + { code: ` + E('AAA', 'foo'); + E('BBB', 'bar'); + E('CCC', 'baz'); + `, options: [{ checkErrorDeclarations: true }] }, + ` + E('AAA', 'foo'); + E('CCC', 'baz'); + E('BBB', 'bar'); + `, + `const { + codes: { + ERR_A, + ERR_B, + }, + } = require("internal/errors")`, + ], + invalid: [ + { + code: ` + E('BBB', 'bar'); + E('AAA', 'foo'); + E('CCC', 'baz'); + `, + options: [{ checkErrorDeclarations: true }], + errors: [{ message: 'Out of ASCIIbetical order - BBB >= AAA', line: 3 }] + }, + { + code: `const { + codes: { + ERR_B, + ERR_A, + }, + } = require("internal/errors")`, + errors: [{ message: 'Out of ASCIIbetical order - ERR_B >= ERR_A', line: 4 }] + }, + { + code: 'const internalErrors = require("internal/errors")', + errors: [{ message: /Use destructuring/ }] + }, + { + code: 'const {codes} = require("internal/errors")', + errors: [{ message: /Use destructuring/ }] + }, + { + code: 'const {codes:{ERR_A}} = require("internal/errors")', + errors: [{ message: /Use multiline destructuring/ }] + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-alphabetize-primordials.js b/test/js/node/test/parallel/test-eslint-alphabetize-primordials.js new file mode 100644 index 0000000000..3f63e1ecbd --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-alphabetize-primordials.js @@ -0,0 +1,54 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/alphabetize-primordials'); + +new RuleTester() + .run('alphabetize-primordials', rule, { + valid: [ + 'new Array()', + '"use strict";const {\nArray\n} = primordials;', + '"use strict";const {\n\tArray,\n\tDate,\n} = primordials', + '"use strict";const {\nDate,Array\n} = notPrimordials', + '"use strict";const {\nDate,globalThis:{Array}\n} = primordials', + '"use strict";const {\nBigInt,globalThis:{Array,Date,SharedArrayBuffer,parseInt}\n} = primordials', + '"use strict";const {\nFunctionPrototypeBind,Uint32Array,globalThis:{SharedArrayBuffer}\n} = primordials', + { + code: '"use strict";const fs = require("fs");const {\nArray\n} = primordials', + options: [{ enforceTopPosition: false }], + }, + ], + invalid: [ + { + code: '"use strict";const {Array} = primordials;', + errors: [{ message: /destructuring from primordials should be multiline/ }], + }, + { + code: '"use strict";const fs = require("fs");const {Date,Array} = primordials', + errors: [ + { message: /destructuring from primordials should be multiline/ }, + { message: /destructuring from primordials should be the first expression/ }, + { message: /Date >= Array/ }, + ], + }, + { + code: 'function fn() {"use strict";const {\nArray,\n} = primordials}', + errors: [{ message: /destructuring from primordials should be the first expression/ }], + }, + { + code: '"use strict";const {\nDate,Array} = primordials', + errors: [{ message: /Date >= Array/ }], + }, + { + code: '"use strict";const {\nglobalThis:{Date, Array}} = primordials', + errors: [{ message: /Date >= Array/ }], + }, + ] + }); diff --git a/test/js/node/test/parallel/test-eslint-async-iife-no-unused-result.js b/test/js/node/test/parallel/test-eslint-async-iife-no-unused-result.js new file mode 100644 index 0000000000..9f74b65868 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-async-iife-no-unused-result.js @@ -0,0 +1,48 @@ +'use strict'; +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/async-iife-no-unused-result'); + +const message = 'The result of an immediately-invoked async function needs ' + + 'to be used (e.g. with `.then(common.mustCall())`)'; + +const tester = new RuleTester(); +tester.run('async-iife-no-unused-result', rule, { + valid: [ + '(() => {})()', + '(async () => {})', + '(async () => {})().then()', + '(async () => {})().catch()', + '(function () {})()', + '(async function () {})', + '(async function () {})().then()', + '(async function () {})().catch()', + ], + invalid: [ + { + code: '(async () => {})()', + errors: [{ message }], + }, + { + code: '(async function() {})()', + errors: [{ message }], + }, + { + code: "const common = require('../common');(async () => {})()", + errors: [{ message }], + output: "const common = require('../common');(async () => {})()" + + '.then(common.mustCall())', + }, + { + code: "const common = require('../common');(async function() {})()", + errors: [{ message }], + output: "const common = require('../common');(async function() {})()" + + '.then(common.mustCall())', + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-avoid-prototype-pollution.js b/test/js/node/test/parallel/test-eslint-avoid-prototype-pollution.js new file mode 100644 index 0000000000..c6b0fe6386 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-avoid-prototype-pollution.js @@ -0,0 +1,334 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/avoid-prototype-pollution'); + +new RuleTester() + .run('property-descriptor-no-prototype-pollution', rule, { + valid: [ + 'ObjectDefineProperties({}, {})', + 'ObjectCreate(null, {})', + 'ObjectDefineProperties({}, { key })', + 'ObjectCreate(null, { key })', + 'ObjectDefineProperties({}, { ...spread })', + 'ObjectCreate(null, { ...spread })', + 'ObjectDefineProperties({}, { key: valueDescriptor })', + 'ObjectCreate(null, { key: valueDescriptor })', + 'ObjectDefineProperties({}, { key: { ...{}, __proto__: null } })', + 'ObjectCreate(null, { key: { ...{}, __proto__: null } })', + 'ObjectDefineProperties({}, { key: { __proto__: null } })', + 'ObjectCreate(null, { key: { __proto__: null } })', + 'ObjectDefineProperties({}, { key: { __proto__: null, enumerable: true } })', + 'ObjectCreate(null, { key: { __proto__: null, enumerable: true } })', + 'ObjectDefineProperties({}, { key: { "__proto__": null } })', + 'ObjectCreate(null, { key: { "__proto__": null } })', + 'ObjectDefineProperties({}, { key: { \'__proto__\': null } })', + 'ObjectCreate(null, { key: { \'__proto__\': null } })', + 'ObjectDefineProperty({}, "key", ObjectCreate(null))', + 'ReflectDefineProperty({}, "key", ObjectCreate(null))', + 'ObjectDefineProperty({}, "key", valueDescriptor)', + 'ReflectDefineProperty({}, "key", valueDescriptor)', + 'ObjectDefineProperty({}, "key", { __proto__: null })', + 'ReflectDefineProperty({}, "key", { __proto__: null })', + 'ObjectDefineProperty({}, "key", { __proto__: null, enumerable: true })', + 'ReflectDefineProperty({}, "key", { __proto__: null, enumerable: true })', + 'ObjectDefineProperty({}, "key", { "__proto__": null })', + 'ReflectDefineProperty({}, "key", { "__proto__": null })', + 'ObjectDefineProperty({}, "key", { \'__proto__\': null })', + 'ReflectDefineProperty({}, "key", { \'__proto__\': null })', + 'async function myFn() { return { __proto__: null } }', + 'async function myFn() { function myFn() { return {} } return { __proto__: null } }', + 'const myFn = async function myFn() { return { __proto__: null } }', + 'const myFn = async function () { return { __proto__: null } }', + 'const myFn = async () => { return { __proto__: null } }', + 'const myFn = async () => ({ __proto__: null })', + 'function myFn() { return {} }', + 'const myFn = function myFn() { return {} }', + 'const myFn = function () { return {} }', + 'const myFn = () => { return {} }', + 'const myFn = () => ({})', + 'StringPrototypeReplace("some string", "some string", "some replacement")', + 'StringPrototypeReplaceAll("some string", "some string", "some replacement")', + 'StringPrototypeSplit("some string", "some string")', + 'new Proxy({}, otherObject)', + 'new Proxy({}, someFactory())', + 'new Proxy({}, { __proto__: null })', + 'new Proxy({}, { __proto__: null, ...{} })', + 'async function name(){return await SafePromiseAll([])}', + 'async function name(){const val = await SafePromiseAll([])}', + ], + invalid: [ + { + code: 'ObjectDefineProperties({}, ObjectGetOwnPropertyDescriptors({}))', + errors: [{ message: /prototype pollution/ }], + }, + { + code: 'ObjectCreate(null, ObjectGetOwnPropertyDescriptors({}))', + errors: [{ message: /prototype pollution/ }], + }, + { + code: 'ObjectDefineProperties({}, { key: {} })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectCreate(null, { key: {} })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectDefineProperties({}, { key: { [void 0]: { ...{ __proto__: null } } } })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectCreate(null, { key: { [void 0]: { ...{ __proto__: null } } } })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectDefineProperties({}, { key: { __proto__: Object.prototype } })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectCreate(null, { key: { __proto__: Object.prototype } })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectDefineProperties({}, { key: { [`__proto__`]: null } })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectCreate(null, { key: { [`__proto__`]: null } })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectDefineProperties({}, { key: { enumerable: true } })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectCreate(null, { key: { enumerable: true } })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectDefineProperty({}, "key", {})', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ReflectDefineProperty({}, "key", {})', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectDefineProperty({}, "key", ObjectGetOwnPropertyDescriptor({}, "key"))', + errors: [{ + message: /prototype pollution/, + suggestions: [{ + desc: 'Wrap the property descriptor in a null-prototype object', + output: 'ObjectDefineProperty({}, "key", { __proto__: null,...ObjectGetOwnPropertyDescriptor({}, "key") })', + }], + }], + }, + { + code: 'ReflectDefineProperty({}, "key", ObjectGetOwnPropertyDescriptor({}, "key"))', + errors: [{ + message: /prototype pollution/, + suggestions: [{ + desc: 'Wrap the property descriptor in a null-prototype object', + output: + 'ReflectDefineProperty({}, "key", { __proto__: null,...ObjectGetOwnPropertyDescriptor({}, "key") })', + }], + }], + }, + { + code: 'ObjectDefineProperty({}, "key", ReflectGetOwnPropertyDescriptor({}, "key"))', + errors: [{ + message: /prototype pollution/, + suggestions: [{ + desc: 'Wrap the property descriptor in a null-prototype object', + output: + 'ObjectDefineProperty({}, "key", { __proto__: null,...ReflectGetOwnPropertyDescriptor({}, "key") })', + }], + }], + }, + { + code: 'ReflectDefineProperty({}, "key", ReflectGetOwnPropertyDescriptor({}, "key"))', + errors: [{ + message: /prototype pollution/, + suggestions: [{ + desc: 'Wrap the property descriptor in a null-prototype object', + output: + 'ReflectDefineProperty({}, "key", { __proto__: null,...ReflectGetOwnPropertyDescriptor({}, "key") })', + }], + }], + }, + { + code: 'ObjectDefineProperty({}, "key", { __proto__: Object.prototype })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ReflectDefineProperty({}, "key", { __proto__: Object.prototype })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectDefineProperty({}, "key", { [`__proto__`]: null })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ReflectDefineProperty({}, "key", { [`__proto__`]: null })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ObjectDefineProperty({}, "key", { enumerable: true })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'ReflectDefineProperty({}, "key", { enumerable: true })', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'async function myFn(){ return {} }', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'async function myFn(){ async function someOtherFn() { return { __proto__: null } } return {} }', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'async function myFn(){ if (true) { return {} } return { __proto__: null } }', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'const myFn = async function myFn(){ return {} }', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'const myFn = async function (){ return {} }', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'const myFn = async () => { return {} }', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'const myFn = async () => ({})', + errors: [{ message: /null-prototype/ }], + }, + { + code: 'RegExpPrototypeTest(/some regex/, "some string")', + errors: [{ + message: /looks up the "exec" property/, + suggestions: [{ + desc: 'Use RegexpPrototypeExec instead', + output: 'RegExpPrototypeExec(/some regex/, "some string") !== null', + }], + }], + }, + { + code: 'RegExpPrototypeSymbolMatch(/some regex/, "some string")', + errors: [{ message: /looks up the "exec" property/ }], + }, + { + code: 'RegExpPrototypeSymbolMatchAll(/some regex/, "some string")', + errors: [{ message: /looks up the "exec" property/ }], + }, + { + code: 'RegExpPrototypeSymbolSearch(/some regex/, "some string")', + errors: [{ message: /SafeStringPrototypeSearch/ }], + }, + { + code: 'StringPrototypeMatch("some string", /some regex/)', + errors: [{ message: /looks up the Symbol\.match property/ }], + }, + { + code: 'let v = StringPrototypeMatch("some string", /some regex/)', + errors: [{ message: /looks up the Symbol\.match property/ }], + }, + { + code: 'let v = StringPrototypeMatch("some string", new RegExp("some regex"))', + errors: [{ message: /looks up the Symbol\.match property/ }], + }, + { + code: 'StringPrototypeMatchAll("some string", /some regex/)', + errors: [{ message: /looks up the Symbol\.matchAll property/ }], + }, + { + code: 'let v = StringPrototypeMatchAll("some string", new RegExp("some regex"))', + errors: [{ message: /looks up the Symbol\.matchAll property/ }], + }, + { + code: 'StringPrototypeReplace("some string", /some regex/, "some replacement")', + errors: [{ message: /looks up the Symbol\.replace property/ }], + }, + { + code: 'StringPrototypeReplace("some string", new RegExp("some regex"), "some replacement")', + errors: [{ message: /looks up the Symbol\.replace property/ }], + }, + { + code: 'StringPrototypeReplaceAll("some string", /some regex/, "some replacement")', + errors: [{ message: /looks up the Symbol\.replace property/ }], + }, + { + code: 'StringPrototypeReplaceAll("some string", new RegExp("some regex"), "some replacement")', + errors: [{ message: /looks up the Symbol\.replace property/ }], + }, + { + code: 'StringPrototypeSearch("some string", /some regex/)', + errors: [{ message: /SafeStringPrototypeSearch/ }], + }, + { + code: 'StringPrototypeSplit("some string", /some regex/)', + errors: [{ message: /looks up the Symbol\.split property/ }], + }, + { + code: 'new Proxy({}, {})', + errors: [{ message: /null-prototype/ }] + }, + { + code: 'new Proxy({}, { [`__proto__`]: null })', + errors: [{ message: /null-prototype/ }] + }, + { + code: 'new Proxy({}, { __proto__: Object.prototype })', + errors: [{ message: /null-prototype/ }] + }, + { + code: 'new Proxy({}, { ...{ __proto__: null } })', + errors: [{ message: /null-prototype/ }] + }, + { + code: 'PromisePrototypeCatch(promise, ()=>{})', + errors: [{ message: /\bPromisePrototypeThen\b/ }] + }, + { + code: 'PromiseAll([])', + errors: [{ message: /\bSafePromiseAll\b/ }] + }, + { + code: 'async function fn(){await SafePromiseAll([])}', + errors: [{ message: /\bSafePromiseAllReturnVoid\b/ }] + }, + { + code: 'async function fn(){await SafePromiseAllSettled([])}', + errors: [{ message: /\bSafePromiseAllSettledReturnVoid\b/ }] + }, + { + code: 'PromiseAllSettled([])', + errors: [{ message: /\bSafePromiseAllSettled\b/ }] + }, + { + code: 'PromiseAny([])', + errors: [{ message: /\bSafePromiseAny\b/ }] + }, + { + code: 'PromiseRace([])', + errors: [{ message: /\bSafePromiseRace\b/ }] + }, + { + code: 'ArrayPrototypeConcat([])', + errors: [{ message: /\bisConcatSpreadable\b/ }] + }, + ] + }); diff --git a/test/js/node/test/parallel/test-eslint-crypto-check.js b/test/js/node/test/parallel/test-eslint-crypto-check.js new file mode 100644 index 0000000000..2b2c0c2dd1 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-crypto-check.js @@ -0,0 +1,77 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/crypto-check'); + +const message = 'Please add a hasCrypto check to allow this test to be ' + + 'skipped when Node is built "--without-ssl".'; + +new RuleTester().run('crypto-check', rule, { + valid: [ + 'foo', + 'crypto', + ` + if (!common.hasCrypto) { + common.skip("missing crypto"); + } + require("crypto"); + `, + ` + if (!common.hasCrypto) { + common.skip("missing crypto"); + } + internalBinding("crypto"); + `, + ], + invalid: [ + { + code: 'require("common")\n' + + 'require("crypto")\n' + + 'if (!common.hasCrypto) {\n' + + ' common.skip("missing crypto");\n' + + '}', + errors: [{ message }] + }, + { + code: 'require("common")\n' + + 'require("crypto")', + errors: [{ message }], + output: 'require("common")\n' + + 'if (!common.hasCrypto) {' + + ' common.skip("missing crypto");' + + '}\n' + + 'require("crypto")' + }, + { + code: 'require("common")\n' + + 'if (common.foo) {}\n' + + 'require("crypto")', + errors: [{ message }], + output: 'require("common")\n' + + 'if (!common.hasCrypto) {' + + ' common.skip("missing crypto");' + + '}\n' + + 'if (common.foo) {}\n' + + 'require("crypto")' + }, + { + code: 'require("common")\n' + + 'if (common.foo) {}\n' + + 'internalBinding("crypto")', + errors: [{ message }], + output: 'require("common")\n' + + 'if (!common.hasCrypto) {' + + ' common.skip("missing crypto");' + + '}\n' + + 'if (common.foo) {}\n' + + 'internalBinding("crypto")' + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-documented-deprecation-codes.js b/test/js/node/test/parallel/test-eslint-documented-deprecation-codes.js new file mode 100644 index 0000000000..dc3dc46b7e --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-documented-deprecation-codes.js @@ -0,0 +1,42 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +if (!common.hasIntl) + common.skip('missing Intl'); +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/documented-deprecation-codes'); + +const mdFile = 'doc/api/deprecations.md'; + +const invalidCode = 'UNDOCUMENTED INVALID CODE'; + +new RuleTester().run('documented-deprecation-codes', rule, { + valid: [ + ` + deprecate(function() { + return this.getHeaders(); + }, 'OutgoingMessage.prototype._headers is deprecated', 'DEP0066') + `, + ], + invalid: [ + { + code: ` + deprecate(function foo(){}, 'bar', '${invalidCode}'); + `, + errors: [ + { + message: `"${invalidCode}" does not match the expected pattern`, + line: 2 + }, + { + message: `"${invalidCode}" is not documented in ${mdFile}`, + line: 2 + }, + ] + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-documented-errors.js b/test/js/node/test/parallel/test-eslint-documented-errors.js new file mode 100644 index 0000000000..03131306d7 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-documented-errors.js @@ -0,0 +1,38 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/documented-errors'); + +const invalidCode = 'UNDOCUMENTED ERROR CODE'; + +new RuleTester().run('documented-errors', rule, { + valid: [ + ` + E('ERR_ASSERTION', 'foo'); + `, + ], + invalid: [ + { + code: ` + E('${invalidCode}', 'bar'); + `, + errors: [ + { + message: `"${invalidCode}" is not documented in doc/api/errors.md`, + line: 2 + }, + { + message: + `doc/api/errors.md does not have an anchor for "${invalidCode}"`, + line: 2 + }, + ] + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-duplicate-requires.js b/test/js/node/test/parallel/test-eslint-duplicate-requires.js new file mode 100644 index 0000000000..f2a11b37ca --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-duplicate-requires.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const { RuleTester } = require('../../tools/eslint/node_modules/eslint'); +const rule = require('../../tools/eslint-rules/no-duplicate-requires'); + +new RuleTester({ + languageOptions: { + sourceType: 'script', + }, +}).run('no-duplicate-requires', rule, { + valid: [ + { + code: 'require("a"); require("b"); (function() { require("a"); });', + }, + { + code: 'require(a); require(a);', + }, + ], + invalid: [ + { + code: 'require("a"); require("a");', + errors: [{ message: '\'a\' require is duplicated.' }], + }, + ], +}); diff --git a/test/js/node/test/parallel/test-eslint-eslint-check.js b/test/js/node/test/parallel/test-eslint-eslint-check.js new file mode 100644 index 0000000000..ca34497c32 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-eslint-check.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/eslint-check'); + +const message = 'Please add a skipIfEslintMissing() call to allow this ' + + 'test to be skipped when Node.js is built ' + + 'from a source tarball.'; + +new RuleTester().run('eslint-check', rule, { + valid: [ + 'foo;', + 'require("common")\n' + + 'common.skipIfEslintMissing();\n' + + 'require("../../tools/eslint/node_modules/eslint")', + ], + invalid: [ + { + code: 'require("common")\n' + + 'require("../../tools/eslint/node_modules/eslint").RuleTester', + errors: [{ message }], + output: 'require("common")\n' + + 'common.skipIfEslintMissing();\n' + + 'require("../../tools/eslint/node_modules/eslint").RuleTester' + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-inspector-check.js b/test/js/node/test/parallel/test-eslint-inspector-check.js new file mode 100644 index 0000000000..c60dcf0874 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-inspector-check.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/inspector-check'); + +const message = 'Please add a skipIfInspectorDisabled() call to allow this ' + + 'test to be skipped when Node is built ' + + '\'--without-inspector\'.'; + +new RuleTester().run('inspector-check', rule, { + valid: [ + 'foo;', + 'require("common")\n' + + 'common.skipIfInspectorDisabled();\n' + + 'require("inspector")', + ], + invalid: [ + { + code: 'require("common")\n' + + 'require("inspector")', + errors: [{ message }], + output: 'require("common")\n' + + 'common.skipIfInspectorDisabled();\n' + + 'require("inspector")' + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-lowercase-name-for-primitive.js b/test/js/node/test/parallel/test-eslint-lowercase-name-for-primitive.js new file mode 100644 index 0000000000..f8029d7c8b --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-lowercase-name-for-primitive.js @@ -0,0 +1,42 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/lowercase-name-for-primitive'); + +new RuleTester().run('lowercase-name-for-primitive', rule, { + valid: [ + 'new errors.TypeError("ERR_INVALID_ARG_TYPE", "a", ["string", "number"])', + 'new errors.TypeError("ERR_INVALID_ARG_TYPE", "name", "string")', + 'new errors.TypeError("ERR_INVALID_ARG_TYPE", "name", "number")', + 'new errors.TypeError("ERR_INVALID_ARG_TYPE", "name", "boolean")', + 'new errors.TypeError("ERR_INVALID_ARG_TYPE", "name", "null")', + 'new errors.TypeError("ERR_INVALID_ARG_TYPE", "name", "undefined")', + ], + invalid: [ + { + code: "new errors.TypeError('ERR_INVALID_ARG_TYPE', 'a', 'Number')", + errors: [{ message: 'primitive should use lowercase: Number' }], + output: "new errors.TypeError('ERR_INVALID_ARG_TYPE', 'a', 'number')", + }, + { + code: "new errors.TypeError('ERR_INVALID_ARG_TYPE', 'a', 'STRING')", + errors: [{ message: 'primitive should use lowercase: STRING' }], + output: "new errors.TypeError('ERR_INVALID_ARG_TYPE', 'a', 'string')", + }, + { + code: "new e.TypeError('ERR_INVALID_ARG_TYPE', a, ['String','Number'])", + errors: [ + { message: 'primitive should use lowercase: String' }, + { message: 'primitive should use lowercase: Number' }, + ], + output: "new e.TypeError('ERR_INVALID_ARG_TYPE', a, ['string','number'])", + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-no-array-destructuring.js b/test/js/node/test/parallel/test-eslint-no-array-destructuring.js new file mode 100644 index 0000000000..3f9b6e0094 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-no-array-destructuring.js @@ -0,0 +1,139 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const { RuleTester } = require('../../tools/eslint/node_modules/eslint'); +const rule = require('../../tools/eslint-rules/no-array-destructuring'); + +const USE_OBJ_DESTRUCTURING = + 'Use object destructuring instead of array destructuring.'; +const USE_ARRAY_METHODS = + 'Use primordials.ArrayPrototypeSlice to avoid unsafe array iteration.'; + +new RuleTester() + .run('no-array-destructuring', rule, { + valid: [ + 'const first = [1, 2, 3][0];', + 'const {0:first} = [1, 2, 3];', + '({1:elem} = array);', + 'function name(param, { 0: key, 1: value },) {}', + ], + invalid: [ + { + code: 'const [Array] = args;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'const {0:Array} = args;' + }, + { + code: 'const [ , res] = args;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'const { 1:res} = args;', + }, + { + code: '[, elem] = options;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: '({ 1:elem} = options);', + }, + { + code: 'const {values:[elem]} = options;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'const {values:{0:elem}} = options;', + }, + { + code: '[[[elem]]] = options;', + errors: [ + { message: USE_OBJ_DESTRUCTURING }, + { message: USE_OBJ_DESTRUCTURING }, + { message: USE_OBJ_DESTRUCTURING }, + ], + output: '({0:[[elem]]} = options);', + }, + { + code: '[, ...rest] = options;', + errors: [{ message: USE_ARRAY_METHODS }], + }, + { + code: 'for(const [key, value] of new Map);', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'for(const {0:key, 1:value} of new Map);', + }, + { + code: 'let [first,,,fourth] = array;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'let {0:first,3:fourth} = array;', + }, + { + code: 'let [,second,,fourth] = array;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'let {1:second,3:fourth} = array;', + }, + { + code: 'let [ ,,,fourth ] = array;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'let { 3:fourth } = array;', + }, + { + code: 'let [,,,fourth, fifth,, minorFall, majorLift,...music] = arr;', + errors: [{ message: USE_ARRAY_METHODS }], + }, + { + code: 'function map([key, value]) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'function map({0:key, 1:value}) {}', + }, + { + code: 'function map([key, value],) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'function map({0:key, 1:value},) {}', + }, + { + code: '(function([key, value]) {})', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: '(function({0:key, 1:value}) {})', + }, + { + code: '(function([key, value] = [null, 0]) {})', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: '(function({0:key, 1:value} = [null, 0]) {})', + }, + { + code: 'function map([key, ...values]) {}', + errors: [{ message: USE_ARRAY_METHODS }], + }, + { + code: 'function map([key, value], ...args) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'function map({0:key, 1:value}, ...args) {}', + }, + { + code: 'async function map([key, value], ...args) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'async function map({0:key, 1:value}, ...args) {}', + }, + { + code: 'async function* generator([key, value], ...args) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'async function* generator({0:key, 1:value}, ...args) {}', + }, + { + code: 'function* generator([key, value], ...args) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'function* generator({0:key, 1:value}, ...args) {}', + }, + { + code: 'const cb = ([key, value], ...args) => {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'const cb = ({0:key, 1:value}, ...args) => {}', + }, + { + code: 'class name{ method([key], ...args){} }', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'class name{ method({0:key}, ...args){} }', + }, + ] + }); diff --git a/test/js/node/test/parallel/test-eslint-no-unescaped-regexp-dot.js b/test/js/node/test/parallel/test-eslint-no-unescaped-regexp-dot.js new file mode 100644 index 0000000000..457b76a2fc --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-no-unescaped-regexp-dot.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/no-unescaped-regexp-dot'); + +new RuleTester().run('no-unescaped-regexp-dot', rule, { + valid: [ + '/foo/', + String.raw`/foo\./`, + '/.+/', + '/.*/', + '/.?/', + '/.{5}/', + String.raw`/\\\./`, + ], + invalid: [ + { + code: '/./', + errors: [{ message: 'Unescaped dot character in regular expression' }] + }, + { + code: String.raw`/\\./`, + errors: [{ message: 'Unescaped dot character in regular expression' }] + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-non-ascii-character.js b/test/js/node/test/parallel/test-eslint-non-ascii-character.js new file mode 100644 index 0000000000..2d71fda279 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-non-ascii-character.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/non-ascii-character'); + +new RuleTester().run('non-ascii-characters', rule, { + valid: [ + { + code: 'console.log("fhqwhgads")', + options: [] + }, + ], + invalid: [ + { + code: 'console.log("μ")', + options: [], + errors: [{ message: "Non-ASCII character 'μ' detected." }], + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-prefer-assert-iferror.js b/test/js/node/test/parallel/test-eslint-prefer-assert-iferror.js new file mode 100644 index 0000000000..cd8f46146d --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-prefer-assert-iferror.js @@ -0,0 +1,36 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/prefer-assert-iferror'); + +new RuleTester().run('prefer-assert-iferror', rule, { + valid: [ + 'assert.ifError(err);', + 'if (err) throw somethingElse;', + 'throw err;', + 'if (err) { throw somethingElse; }', + ], + invalid: [ + { + code: 'require("assert");\n' + + 'if (err) throw err;', + errors: [{ message: 'Use assert.ifError(err) instead.' }], + output: 'require("assert");\n' + + 'assert.ifError(err);' + }, + { + code: 'require("assert");\n' + + 'if (error) { throw error; }', + errors: [{ message: 'Use assert.ifError(error) instead.' }], + output: 'require("assert");\n' + + 'assert.ifError(error);' + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-prefer-assert-methods.js b/test/js/node/test/parallel/test-eslint-prefer-assert-methods.js new file mode 100644 index 0000000000..a77ffd01d4 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-prefer-assert-methods.js @@ -0,0 +1,57 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/prefer-assert-methods'); + +new RuleTester().run('prefer-assert-methods', rule, { + valid: [ + 'assert.strictEqual(foo, bar);', + 'assert(foo === bar && baz);', + 'assert.notStrictEqual(foo, bar);', + 'assert(foo !== bar && baz);', + 'assert.equal(foo, bar);', + 'assert(foo == bar && baz);', + 'assert.notEqual(foo, bar);', + 'assert(foo != bar && baz);', + 'assert.ok(foo);', + 'assert.ok(foo != bar);', + 'assert.ok(foo === bar && baz);', + ], + invalid: [ + { + code: 'assert(foo == bar);', + errors: [{ + message: "'assert.equal' should be used instead of '=='" + }], + output: 'assert.equal(foo, bar);' + }, + { + code: 'assert(foo === bar);', + errors: [{ + message: "'assert.strictEqual' should be used instead of '==='" + }], + output: 'assert.strictEqual(foo, bar);' + }, + { + code: 'assert(foo != bar);', + errors: [{ + message: "'assert.notEqual' should be used instead of '!='" + }], + output: 'assert.notEqual(foo, bar);' + }, + { + code: 'assert(foo !== bar);', + errors: [{ + message: "'assert.notStrictEqual' should be used instead of '!=='" + }], + output: 'assert.notStrictEqual(foo, bar);' + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-prefer-common-mustnotcall.js b/test/js/node/test/parallel/test-eslint-prefer-common-mustnotcall.js new file mode 100644 index 0000000000..a805b3c4ae --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-prefer-common-mustnotcall.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/prefer-common-mustnotcall'); + +const message = 'Please use common.mustNotCall(msg) instead of ' + + 'common.mustCall(fn, 0) or common.mustCall(0).'; + +new RuleTester().run('prefer-common-mustnotcall', rule, { + valid: [ + 'common.mustNotCall(fn)', + 'common.mustCall(fn)', + 'common.mustCall(fn, 1)', + ], + invalid: [ + { + code: 'common.mustCall(fn, 0)', + errors: [{ message }] + }, + { + code: 'common.mustCall(0)', + errors: [{ message }] + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-prefer-common-mustsucceed.js b/test/js/node/test/parallel/test-eslint-prefer-common-mustsucceed.js new file mode 100644 index 0000000000..134d8bbc05 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-prefer-common-mustsucceed.js @@ -0,0 +1,51 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/prefer-common-mustsucceed'); + +const msg1 = 'Please use common.mustSucceed instead of ' + + 'common.mustCall(assert.ifError).'; +const msg2 = 'Please use common.mustSucceed instead of ' + + 'common.mustCall with assert.ifError.'; + +new RuleTester().run('prefer-common-mustsucceed', rule, { + valid: [ + 'foo((err) => assert.ifError(err))', + 'foo(function(err) { assert.ifError(err) })', + 'foo(assert.ifError)', + 'common.mustCall((err) => err)', + ], + invalid: [ + { + code: 'common.mustCall(assert.ifError)', + errors: [{ message: msg1 }] + }, + { + code: 'common.mustCall((err) => assert.ifError(err))', + errors: [{ message: msg2 }] + }, + { + code: 'common.mustCall((e) => assert.ifError(e))', + errors: [{ message: msg2 }] + }, + { + code: 'common.mustCall(function(e) { assert.ifError(e); })', + errors: [{ message: msg2 }] + }, + { + code: 'common.mustCall(function(e) { return assert.ifError(e); })', + errors: [{ message: msg2 }] + }, + { + code: 'common.mustCall(function(e) {{ assert.ifError(e); }})', + errors: [{ message: msg2 }] + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-prefer-primordials.js b/test/js/node/test/parallel/test-eslint-prefer-primordials.js new file mode 100644 index 0000000000..61c84cbadd --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-prefer-primordials.js @@ -0,0 +1,265 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/prefer-primordials'); + +new RuleTester({ + languageOptions: { + sourceType: 'script', + }, +}) + .run('prefer-primordials', rule, { + valid: [ + 'new Array()', + 'JSON.stringify({})', + 'class A { *[Symbol.iterator] () { yield "a"; } }', + 'const a = { *[Symbol.iterator] () { yield "a"; } }', + 'Object.defineProperty(o, Symbol.toStringTag, { value: "o" })', + 'parseInt("10")', + ` + const { Reflect } = primordials; + module.exports = function() { + const { ownKeys } = Reflect; + } + `, + { + code: 'const { Array } = primordials; new Array()', + options: [{ name: 'Array' }] + }, + { + code: 'const { JSONStringify } = primordials; JSONStringify({})', + options: [{ name: 'JSON' }] + }, + { + code: 'const { SymbolFor } = primordials; SymbolFor("xxx")', + options: [{ name: 'Symbol' }] + }, + { + code: ` + const { SymbolIterator } = primordials; + class A { *[SymbolIterator] () { yield "a"; } } + `, + options: [{ name: 'Symbol' }] + }, + { + code: ` + const { Symbol } = primordials; + const a = { *[Symbol.iterator] () { yield "a"; } } + `, + options: [{ name: 'Symbol', ignore: ['iterator'] }] + }, + { + code: ` + const { ObjectDefineProperty, Symbol } = primordials; + ObjectDefineProperty(o, Symbol.toStringTag, { value: "o" }); + const val = Symbol.toStringTag; + const { toStringTag } = Symbol; + `, + options: [{ name: 'Symbol', ignore: ['toStringTag'] }] + }, + { + code: 'const { Symbol } = primordials; Symbol.for("xxx")', + options: [{ name: 'Symbol', ignore: ['for'] }] + }, + { + code: 'const { NumberParseInt } = primordials; NumberParseInt("xxx")', + options: [{ name: 'parseInt', into: 'Number' }] + }, + { + code: ` + const { ReflectOwnKeys } = primordials; + module.exports = function() { + ReflectOwnKeys({}) + } + `, + options: [{ name: 'Reflect' }], + }, + { + code: 'const { Map } = primordials; new Map()', + options: [{ name: 'Map', into: 'Safe' }], + }, + { + code: ` + const { Function } = primordials; + const rename = Function; + const obj = { rename }; + `, + options: [{ name: 'Function' }], + }, + { + code: ` + const { Function } = primordials; + let rename; + rename = Function; + const obj = { rename }; + `, + options: [{ name: 'Function' }], + }, + { + code: 'function identifier() {}', + options: [{ name: 'identifier' }] + }, + { + code: 'function* identifier() {}', + options: [{ name: 'identifier' }] + }, + { + code: 'class identifier {}', + options: [{ name: 'identifier' }] + }, + { + code: 'new class { identifier(){} }', + options: [{ name: 'identifier' }] + }, + { + code: 'const a = { identifier: \'4\' }', + options: [{ name: 'identifier' }] + }, + { + code: 'identifier:{const a = 4}', + options: [{ name: 'identifier' }] + }, + { + code: 'switch(0){case identifier:}', + options: [{ name: 'identifier' }] + }, + ], + invalid: [ + { + code: 'new Array()', + options: [{ name: 'Array' }], + errors: [{ message: /const { Array } = primordials/ }] + }, + { + code: 'JSON.parse("{}")', + options: [{ name: 'JSON' }], + errors: [ + { message: /const { JSONParse } = primordials/ }, + ] + }, + { + code: 'const { JSON } = primordials; JSON.parse("{}")', + options: [{ name: 'JSON' }], + errors: [{ message: /const { JSONParse } = primordials/ }] + }, + { + code: 'Symbol.for("xxx")', + options: [{ name: 'Symbol' }], + errors: [ + { message: /const { SymbolFor } = primordials/ }, + ] + }, + { + code: 'const { Symbol } = primordials; Symbol.for("xxx")', + options: [{ name: 'Symbol' }], + errors: [{ message: /const { SymbolFor } = primordials/ }] + }, + { + code: ` + const { Symbol } = primordials; + class A { *[Symbol.iterator] () { yield "a"; } } + `, + options: [{ name: 'Symbol' }], + errors: [{ message: /const { SymbolIterator } = primordials/ }] + }, + { + code: ` + const { Symbol } = primordials; + const a = { *[Symbol.iterator] () { yield "a"; } } + `, + options: [{ name: 'Symbol' }], + errors: [{ message: /const { SymbolIterator } = primordials/ }] + }, + { + code: ` + const { ObjectDefineProperty, Symbol } = primordials; + ObjectDefineProperty(o, Symbol.toStringTag, { value: "o" }) + `, + options: [{ name: 'Symbol' }], + errors: [{ message: /const { SymbolToStringTag } = primordials/ }] + }, + { + code: ` + const { Number } = primordials; + Number.parseInt('10') + `, + options: [{ name: 'Number' }], + errors: [{ message: /const { NumberParseInt } = primordials/ }] + }, + { + code: 'parseInt("10")', + options: [{ name: 'parseInt', into: 'Number' }], + errors: [{ message: /const { NumberParseInt } = primordials/ }] + }, + { + code: ` + module.exports = function() { + const { ownKeys } = Reflect; + } + `, + options: [{ name: 'Reflect' }], + errors: [{ message: /const { ReflectOwnKeys } = primordials/ }] + }, + { + code: ` + const { Reflect } = primordials; + module.exports = function() { + const { ownKeys } = Reflect; + } + `, + options: [{ name: 'Reflect' }], + errors: [{ message: /const { ReflectOwnKeys } = primordials/ }] + }, + { + code: 'new Map()', + options: [{ name: 'Map', into: 'Safe' }], + errors: [{ message: /const { SafeMap } = primordials/ }] + }, + { + code: ` + const { Function } = primordials; + const noop = Function.prototype; + `, + options: [{ name: 'Function' }], + errors: [{ message: /const { FunctionPrototype } = primordials/ }] + }, + { + code: ` + const obj = { Function }; + `, + options: [{ name: 'Function' }], + errors: [{ message: /const { Function } = primordials/ }] + }, + { + code: ` + const rename = Function; + `, + options: [{ name: 'Function' }], + errors: [{ message: /const { Function } = primordials/ }] + }, + { + code: ` + const rename = Function; + const obj = { rename }; + `, + options: [{ name: 'Function' }], + errors: [{ message: /const { Function } = primordials/ }] + }, + { + code: ` + let rename; + rename = Function; + const obj = { rename }; + `, + options: [{ name: 'Function' }], + errors: [{ message: /const { Function } = primordials/ }] + }, + ] + }); diff --git a/test/js/node/test/parallel/test-eslint-prefer-proto.js b/test/js/node/test/parallel/test-eslint-prefer-proto.js new file mode 100644 index 0000000000..7e927c967b --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-prefer-proto.js @@ -0,0 +1,69 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/prefer-proto'); + +new RuleTester().run('prefer-common-mustsucceed', rule, { + valid: [ + '({ __proto__: null })', + 'const x = { __proto__: null };', + ` + class X { + field = { __proto__: null }; + + constructor() { + this.x = { __proto__: X }; + } + } + `, + 'foo({ __proto__: Array.prototype })', + '({ "__proto__": null })', + "({ '__proto__': null })", + 'ObjectCreate(null, undefined)', + 'Object.create(null, undefined)', + 'Object.create(null, undefined, undefined)', + 'ObjectCreate(null, descriptors)', + 'Object.create(null, descriptors)', + 'ObjectCreate(Foo.prototype, descriptors)', + 'Object.create(Foo.prototype, descriptors)', + ], + invalid: [ + { + code: 'ObjectCreate(null)', + output: '{ __proto__: null }', + errors: [{ messageId: 'error', data: { value: 'null' } }], + }, + { + code: 'Object.create(null)', + output: '{ __proto__: null }', + errors: [{ messageId: 'error', data: { value: 'null' } }], + }, + { + code: 'ObjectCreate(null,)', + output: '{ __proto__: null }', + errors: [{ messageId: 'error', data: { value: 'null' } }], + }, + { + code: 'Object.create(null,)', + output: '{ __proto__: null }', + errors: [{ messageId: 'error', data: { value: 'null' } }], + }, + { + code: 'ObjectCreate(Foo)', + output: '{ __proto__: Foo }', + errors: [{ messageId: 'error', data: { value: 'Foo' } }], + }, + { + code: 'Object.create(Foo)', + output: '{ __proto__: Foo }', + errors: [{ messageId: 'error', data: { value: 'Foo' } }], + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-prefer-util-format-errors.js b/test/js/node/test/parallel/test-eslint-prefer-util-format-errors.js new file mode 100644 index 0000000000..3410265e9f --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-prefer-util-format-errors.js @@ -0,0 +1,32 @@ +'use strict'; + +/* eslint-disable no-template-curly-in-string */ + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/prefer-util-format-errors'); + +new RuleTester() + .run('prefer-util-format-errors', rule, { + valid: [ + 'E(\'ABC\', \'abc\');', + 'E(\'ABC\', (arg1, arg2) => `${arg2}${arg1}`);', + 'E(\'ABC\', (arg1, arg2) => `${arg1}{arg2.something}`);', + 'E(\'ABC\', (arg1, arg2) => fn(arg1, arg2));', + ], + invalid: [ + { + code: 'E(\'ABC\', (arg1, arg2) => `${arg1}${arg2}`);', + errors: [{ + message: 'Please use a printf-like formatted string that ' + + 'util.format can consume.' + }] + }, + ] + }); diff --git a/test/js/node/test/parallel/test-eslint-require-common-first.js b/test/js/node/test/parallel/test-eslint-require-common-first.js new file mode 100644 index 0000000000..ef19f95b97 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-require-common-first.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/require-common-first'); + +new RuleTester({ + languageOptions: { + sourceType: 'script', + }, +}).run('require-common-first', rule, { + valid: [ + { + code: 'require("common")\n' + + 'require("assert")' + }, + ], + invalid: [ + { + code: 'require("assert")\n' + + 'require("common")', + errors: [{ message: 'Mandatory module "common" must be loaded ' + + 'before any other modules.' }] + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eslint-required-modules.js b/test/js/node/test/parallel/test-eslint-required-modules.js new file mode 100644 index 0000000000..4704163a38 --- /dev/null +++ b/test/js/node/test/parallel/test-eslint-required-modules.js @@ -0,0 +1,56 @@ +'use strict'; + +const common = require('../common'); +if ((!common.hasCrypto) || (!common.hasIntl)) { + common.skip('ESLint tests require crypto and Intl'); +} + +common.skipIfEslintMissing(); + +const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester; +const rule = require('../../tools/eslint-rules/required-modules'); + +new RuleTester({ + languageOptions: { + sourceType: 'script', + }, +}).run('required-modules', rule, { + valid: [ + { + code: 'require("common")', + options: [{ common: 'common' }] + }, + { + code: 'foo', + options: [] + }, + { + code: 'require("common")', + options: [{ common: 'common(/index\\.(m)?js)?$' }] + }, + { + code: 'require("common/index.js")', + options: [{ common: 'common(/index\\.(m)?js)?$' }] + }, + ], + invalid: [ + { + code: 'foo', + options: [{ common: 'common' }], + errors: [{ message: 'Mandatory module "common" must be loaded.' }] + }, + { + code: 'require("common/fixtures.js")', + options: [{ common: 'common(/index\\.(m)?js)?$' }], + errors: [{ + message: + 'Mandatory module "common" must be loaded.' + }] + }, + { + code: 'require("somethingElse")', + options: [{ common: 'common' }], + errors: [{ message: 'Mandatory module "common" must be loaded.' }] + }, + ] +}); diff --git a/test/js/node/test/parallel/test-eval-strict-referenceerror.js b/test/js/node/test/parallel/test-eval-strict-referenceerror.js new file mode 100644 index 0000000000..97f2b15ba0 --- /dev/null +++ b/test/js/node/test/parallel/test-eval-strict-referenceerror.js @@ -0,0 +1,27 @@ +/* eslint-disable strict */ +require('../common'); + +// In Node.js 0.10, a bug existed that caused strict functions to not capture +// their environment when evaluated. When run in 0.10 `test()` fails with a +// `ReferenceError`. See https://github.com/nodejs/node/issues/2245 for details. + +const assert = require('assert'); + +function test() { + + const code = [ + 'var foo = {m: 1};', + '', + 'function bar() {', + '\'use strict\';', + 'return foo; // foo isn\'t captured in 0.10', + '};', + ].join('\n'); + + eval(code); + + return bar(); // eslint-disable-line no-undef + +} + +assert.deepStrictEqual(test(), { m: 1 }); diff --git a/test/js/node/test/parallel/test-eval.js b/test/js/node/test/parallel/test-eval.js new file mode 100644 index 0000000000..46a4b7c546 --- /dev/null +++ b/test/js/node/test/parallel/test-eval.js @@ -0,0 +1,7 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Verify that eval is allowed by default. +assert.strictEqual(eval('"eval"'), 'eval'); diff --git a/test/js/node/test/parallel/test-event-emitter-add-listeners.js b/test/js/node/test/parallel/test-event-emitter-add-listeners.js new file mode 100644 index 0000000000..f42d1f2487 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-add-listeners.js @@ -0,0 +1,86 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +{ + const ee = new EventEmitter(); + const events_new_listener_emitted = []; + const listeners_new_listener_emitted = []; + + // Sanity check + assert.strictEqual(ee.addListener, ee.on); + + ee.on('newListener', function(event, listener) { + // Don't track newListener listeners. + if (event === 'newListener') + return; + + events_new_listener_emitted.push(event); + listeners_new_listener_emitted.push(listener); + }); + + const hello = common.mustCall(function(a, b) { + assert.strictEqual(a, 'a'); + assert.strictEqual(b, 'b'); + }); + + ee.once('newListener', function(name, listener) { + assert.strictEqual(name, 'hello'); + assert.strictEqual(listener, hello); + assert.deepStrictEqual(this.listeners('hello'), []); + }); + + ee.on('hello', hello); + ee.once('foo', assert.fail); + assert.deepStrictEqual(['hello', 'foo'], events_new_listener_emitted); + assert.deepStrictEqual([hello, assert.fail], listeners_new_listener_emitted); + + ee.emit('hello', 'a', 'b'); +} + +// Just make sure that this doesn't throw: +{ + const f = new EventEmitter(); + + f.setMaxListeners(0); +} + +{ + const listen1 = () => {}; + const listen2 = () => {}; + const ee = new EventEmitter(); + + ee.once('newListener', function() { + assert.deepStrictEqual(ee.listeners('hello'), []); + ee.once('newListener', function() { + assert.deepStrictEqual(ee.listeners('hello'), []); + }); + ee.on('hello', listen2); + }); + ee.on('hello', listen1); + // The order of listeners on an event is not always the order in which the + // listeners were added. + assert.deepStrictEqual(ee.listeners('hello'), [listen2, listen1]); +} diff --git a/test/js/node/test/parallel/test-event-emitter-check-listener-leaks.js b/test/js/node/test/parallel/test-event-emitter-check-listener-leaks.js new file mode 100644 index 0000000000..04ef3ddab1 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-check-listener-leaks.js @@ -0,0 +1,103 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); + +// default +{ + const e = new events.EventEmitter(); + + for (let i = 0; i < 10; i++) { + e.on('default', common.mustNotCall()); + } + assert.ok(!Object.hasOwn(e._events.default, 'warned')); + e.on('default', common.mustNotCall()); + assert.ok(e._events.default.warned); + + // symbol + const symbol = Symbol('symbol'); + e.setMaxListeners(1); + e.on(symbol, common.mustNotCall()); + assert.ok(!Object.hasOwn(e._events[symbol], 'warned')); + e.on(symbol, common.mustNotCall()); + assert.ok(Object.hasOwn(e._events[symbol], 'warned')); + + // specific + e.setMaxListeners(5); + for (let i = 0; i < 5; i++) { + e.on('specific', common.mustNotCall()); + } + assert.ok(!Object.hasOwn(e._events.specific, 'warned')); + e.on('specific', common.mustNotCall()); + assert.ok(e._events.specific.warned); + + // only one + e.setMaxListeners(1); + e.on('only one', common.mustNotCall()); + assert.ok(!Object.hasOwn(e._events['only one'], 'warned')); + e.on('only one', common.mustNotCall()); + assert.ok(Object.hasOwn(e._events['only one'], 'warned')); + + // unlimited + e.setMaxListeners(0); + for (let i = 0; i < 1000; i++) { + e.on('unlimited', common.mustNotCall()); + } + assert.ok(!Object.hasOwn(e._events.unlimited, 'warned')); +} + +// process-wide +{ + events.EventEmitter.defaultMaxListeners = 42; + const e = new events.EventEmitter(); + + for (let i = 0; i < 42; ++i) { + e.on('fortytwo', common.mustNotCall()); + } + assert.ok(!Object.hasOwn(e._events.fortytwo, 'warned')); + e.on('fortytwo', common.mustNotCall()); + assert.ok(Object.hasOwn(e._events.fortytwo, 'warned')); + delete e._events.fortytwo.warned; + + events.EventEmitter.defaultMaxListeners = 44; + e.on('fortytwo', common.mustNotCall()); + assert.ok(!Object.hasOwn(e._events.fortytwo, 'warned')); + e.on('fortytwo', common.mustNotCall()); + assert.ok(Object.hasOwn(e._events.fortytwo, 'warned')); +} + +// But _maxListeners still has precedence over defaultMaxListeners +{ + events.EventEmitter.defaultMaxListeners = 42; + const e = new events.EventEmitter(); + e.setMaxListeners(1); + e.on('uno', common.mustNotCall()); + assert.ok(!Object.hasOwn(e._events.uno, 'warned')); + e.on('uno', common.mustNotCall()); + assert.ok(Object.hasOwn(e._events.uno, 'warned')); + + // chainable + assert.strictEqual(e, e.setMaxListeners(1)); +} diff --git a/test/js/node/test/parallel/test-event-emitter-emit-context.js b/test/js/node/test/parallel/test-event-emitter-emit-context.js new file mode 100644 index 0000000000..e4c73cadc5 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-emit-context.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +// Test emit called by other context +const EE = new EventEmitter(); + +// Works as expected if the context has no `constructor.name` +{ + const ctx = { __proto__: null }; + assert.throws( + () => EE.emit.call(ctx, 'error', new Error('foo')), + common.expectsError({ name: 'Error', message: 'foo' }) + ); +} + +assert.strictEqual(EE.emit.call({}, 'foo'), false); diff --git a/test/js/node/test/parallel/test-event-emitter-error-monitor.js b/test/js/node/test/parallel/test-event-emitter-error-monitor.js new file mode 100644 index 0000000000..8254fc6254 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-error-monitor.js @@ -0,0 +1,32 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +const EE = new EventEmitter(); +const theErr = new Error('MyError'); + +EE.on( + EventEmitter.errorMonitor, + common.mustCall(function onErrorMonitor(e) { + assert.strictEqual(e, theErr); + }, 3) +); + +// Verify with no error listener +assert.throws( + () => EE.emit('error', theErr), theErr +); + +// Verify with error listener +EE.once('error', common.mustCall((e) => assert.strictEqual(e, theErr))); +EE.emit('error', theErr); + + +// Verify it works with once +process.nextTick(() => EE.emit('error', theErr)); +assert.rejects(EventEmitter.once(EE, 'notTriggered'), theErr).then(common.mustCall()); + +// Only error events trigger error monitor +EE.on('aEvent', common.mustCall()); +EE.emit('aEvent'); diff --git a/test/js/node/test/parallel/test-event-emitter-get-max-listeners.js b/test/js/node/test/parallel/test-event-emitter-get-max-listeners.js new file mode 100644 index 0000000000..43f9f0cebc --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-get-max-listeners.js @@ -0,0 +1,19 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +const emitter = new EventEmitter(); + +assert.strictEqual(emitter.getMaxListeners(), EventEmitter.defaultMaxListeners); + +emitter.setMaxListeners(0); +assert.strictEqual(emitter.getMaxListeners(), 0); + +emitter.setMaxListeners(3); +assert.strictEqual(emitter.getMaxListeners(), 3); + +// https://github.com/nodejs/node/issues/523 - second call should not throw. +const recv = {}; +EventEmitter.prototype.on.call(recv, 'event', () => {}); +EventEmitter.prototype.on.call(recv, 'event', () => {}); diff --git a/test/js/node/test/parallel/test-event-emitter-listener-count.js b/test/js/node/test/parallel/test-event-emitter-listener-count.js new file mode 100644 index 0000000000..117d38f575 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-listener-count.js @@ -0,0 +1,18 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +const emitter = new EventEmitter(); +emitter.on('foo', () => {}); +emitter.on('foo', () => {}); +emitter.on('baz', () => {}); +// Allow any type +emitter.on(123, () => {}); + +assert.strictEqual(EventEmitter.listenerCount(emitter, 'foo'), 2); +assert.strictEqual(emitter.listenerCount('foo'), 2); +assert.strictEqual(emitter.listenerCount('bar'), 0); +assert.strictEqual(emitter.listenerCount('baz'), 1); +assert.strictEqual(emitter.listenerCount(123), 1); diff --git a/test/js/node/test/parallel/test-event-emitter-method-names.js b/test/js/node/test/parallel/test-event-emitter-method-names.js new file mode 100644 index 0000000000..684024d027 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-method-names.js @@ -0,0 +1,35 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const events = require('events'); + +const E = events.EventEmitter.prototype; +assert.strictEqual(E.constructor.name, 'EventEmitter'); +assert.strictEqual(E.on, E.addListener); // Same method. +assert.strictEqual(E.off, E.removeListener); // Same method. +Object.getOwnPropertyNames(E).forEach(function(name) { + if (name === 'constructor' || name === 'on' || name === 'off') return; + if (typeof E[name] !== 'function') return; + assert.strictEqual(E[name].name, name); +}); diff --git a/test/js/node/test/parallel/test-event-emitter-modify-in-emit.js b/test/js/node/test/parallel/test-event-emitter-modify-in-emit.js new file mode 100644 index 0000000000..995fa01d11 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-modify-in-emit.js @@ -0,0 +1,80 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const events = require('events'); + +let callbacks_called = []; + +const e = new events.EventEmitter(); + +function callback1() { + callbacks_called.push('callback1'); + e.on('foo', callback2); + e.on('foo', callback3); + e.removeListener('foo', callback1); +} + +function callback2() { + callbacks_called.push('callback2'); + e.removeListener('foo', callback2); +} + +function callback3() { + callbacks_called.push('callback3'); + e.removeListener('foo', callback3); +} + +e.on('foo', callback1); +assert.strictEqual(e.listeners('foo').length, 1); + +e.emit('foo'); +assert.strictEqual(e.listeners('foo').length, 2); +assert.deepStrictEqual(['callback1'], callbacks_called); + +e.emit('foo'); +assert.strictEqual(e.listeners('foo').length, 0); +assert.deepStrictEqual(['callback1', 'callback2', 'callback3'], + callbacks_called); + +e.emit('foo'); +assert.strictEqual(e.listeners('foo').length, 0); +assert.deepStrictEqual(['callback1', 'callback2', 'callback3'], + callbacks_called); + +e.on('foo', callback1); +e.on('foo', callback2); +assert.strictEqual(e.listeners('foo').length, 2); +e.removeAllListeners('foo'); +assert.strictEqual(e.listeners('foo').length, 0); + +// Verify that removing callbacks while in emit allows emits to propagate to +// all listeners +callbacks_called = []; + +e.on('foo', callback2); +e.on('foo', callback3); +assert.strictEqual(e.listeners('foo').length, 2); +e.emit('foo'); +assert.deepStrictEqual(['callback2', 'callback3'], callbacks_called); +assert.strictEqual(e.listeners('foo').length, 0); diff --git a/test/js/node/test/parallel/test-event-emitter-num-args.js b/test/js/node/test/parallel/test-event-emitter-num-args.js new file mode 100644 index 0000000000..8bb2274303 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-num-args.js @@ -0,0 +1,54 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const events = require('events'); + +const e = new events.EventEmitter(); +const num_args_emitted = []; + +e.on('numArgs', function() { + const numArgs = arguments.length; + num_args_emitted.push(numArgs); +}); + +e.on('foo', function() { + num_args_emitted.push(arguments.length); +}); + +e.on('foo', function() { + num_args_emitted.push(arguments.length); +}); + +e.emit('numArgs'); +e.emit('numArgs', null); +e.emit('numArgs', null, null); +e.emit('numArgs', null, null, null); +e.emit('numArgs', null, null, null, null); +e.emit('numArgs', null, null, null, null, null); + +e.emit('foo', null, null, null, null); + +process.on('exit', function() { + assert.deepStrictEqual(num_args_emitted, [0, 1, 2, 3, 4, 5, 4, 4]); +}); diff --git a/test/js/node/test/parallel/test-event-emitter-prepend.js b/test/js/node/test/parallel/test-event-emitter-prepend.js new file mode 100644 index 0000000000..ffe8544911 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-prepend.js @@ -0,0 +1,42 @@ +'use strict'; + +const common = require('../common'); +const EventEmitter = require('events'); +const assert = require('assert'); + +const myEE = new EventEmitter(); +let m = 0; +// This one comes last. +myEE.on('foo', common.mustCall(() => assert.strictEqual(m, 2))); + +// This one comes second. +myEE.prependListener('foo', common.mustCall(() => assert.strictEqual(m++, 1))); + +// This one comes first. +myEE.prependOnceListener('foo', + common.mustCall(() => assert.strictEqual(m++, 0))); + +myEE.emit('foo'); + +// Test fallback if prependListener is undefined. +const stream = require('stream'); + +delete EventEmitter.prototype.prependListener; + +function Writable() { + this.writable = true; + stream.Stream.call(this); +} +Object.setPrototypeOf(Writable.prototype, stream.Stream.prototype); +Object.setPrototypeOf(Writable, stream.Stream); + +function Readable() { + this.readable = true; + stream.Stream.call(this); +} +Object.setPrototypeOf(Readable.prototype, stream.Stream.prototype); +Object.setPrototypeOf(Readable, stream.Stream); + +const w = new Writable(); +const r = new Readable(); +r.pipe(w); diff --git a/test/js/node/test/parallel/test-event-emitter-set-max-listeners-side-effects.js b/test/js/node/test/parallel/test-event-emitter-set-max-listeners-side-effects.js new file mode 100644 index 0000000000..8e66e999a5 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-set-max-listeners-side-effects.js @@ -0,0 +1,32 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const events = require('events'); + +const e = new events.EventEmitter(); + +assert(!(e._events instanceof Object)); +assert.deepStrictEqual(Object.keys(e._events), []); +e.setMaxListeners(5); +assert.deepStrictEqual(Object.keys(e._events), []); diff --git a/test/js/node/test/parallel/test-event-emitter-special-event-names.js b/test/js/node/test/parallel/test-event-emitter-special-event-names.js new file mode 100644 index 0000000000..f34faba946 --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-special-event-names.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common'); +const EventEmitter = require('events'); +const assert = require('assert'); + +const ee = new EventEmitter(); +const handler = () => {}; + +assert.deepStrictEqual(ee.eventNames(), []); + +assert.strictEqual(ee._events.hasOwnProperty, undefined); +assert.strictEqual(ee._events.toString, undefined); + +ee.on('__proto__', handler); +ee.on('__defineGetter__', handler); +ee.on('toString', handler); + +assert.deepStrictEqual(ee.eventNames(), [ + '__proto__', + '__defineGetter__', + 'toString', +]); + +assert.deepStrictEqual(ee.listeners('__proto__'), [handler]); +assert.deepStrictEqual(ee.listeners('__defineGetter__'), [handler]); +assert.deepStrictEqual(ee.listeners('toString'), [handler]); + +ee.on('__proto__', common.mustCall(function(val) { + assert.strictEqual(val, 1); +})); +ee.emit('__proto__', 1); + +process.on('__proto__', common.mustCall(function(val) { + assert.strictEqual(val, 1); +})); +process.emit('__proto__', 1); diff --git a/test/js/node/test/parallel/test-event-emitter-subclass.js b/test/js/node/test/parallel/test-event-emitter-subclass.js new file mode 100644 index 0000000000..a6ef54e5fa --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-subclass.js @@ -0,0 +1,67 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events').EventEmitter; + +Object.setPrototypeOf(MyEE.prototype, EventEmitter.prototype); +Object.setPrototypeOf(MyEE, EventEmitter); + +function MyEE(cb) { + this.once(1, cb); + this.emit(1); + this.removeAllListeners(); + EventEmitter.call(this); +} + +const myee = new MyEE(common.mustCall()); + +Object.setPrototypeOf(ErrorEE.prototype, EventEmitter.prototype); +Object.setPrototypeOf(ErrorEE, EventEmitter); +function ErrorEE() { + this.emit('error', new Error('blerg')); +} + +assert.throws(function() { + new ErrorEE(); +}, /blerg/); + +process.on('exit', function() { + assert(!(myee._events instanceof Object)); + assert.deepStrictEqual(Object.keys(myee._events), []); + console.log('ok'); +}); + + +function MyEE2() { + EventEmitter.call(this); +} + +MyEE2.prototype = new EventEmitter(); + +const ee1 = new MyEE2(); +const ee2 = new MyEE2(); + +ee1.on('x', () => {}); + +assert.strictEqual(ee2.listenerCount('x'), 0); diff --git a/test/js/node/test/parallel/test-event-emitter-symbols.js b/test/js/node/test/parallel/test-event-emitter-symbols.js new file mode 100644 index 0000000000..98d44ff3fa --- /dev/null +++ b/test/js/node/test/parallel/test-event-emitter-symbols.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +const EventEmitter = require('events'); +const assert = require('assert'); + +const ee = new EventEmitter(); +const foo = Symbol('foo'); +const listener = common.mustCall(); + +ee.on(foo, listener); +assert.deepStrictEqual(ee.listeners(foo), [listener]); + +ee.emit(foo); + +ee.removeAllListeners(); +assert.deepStrictEqual(ee.listeners(foo), []); + +ee.on(foo, listener); +assert.deepStrictEqual(ee.listeners(foo), [listener]); + +ee.removeListener(foo, listener); +assert.deepStrictEqual(ee.listeners(foo), []); diff --git a/test/js/node/test/parallel/test-event-target.js b/test/js/node/test/parallel/test-event-target.js new file mode 100644 index 0000000000..12246b15ae --- /dev/null +++ b/test/js/node/test/parallel/test-event-target.js @@ -0,0 +1,21 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const eventPhases = { + 'NONE': 0, + 'CAPTURING_PHASE': 1, + 'AT_TARGET': 2, + 'BUBBLING_PHASE': 3 +}; + +for (const [prop, value] of Object.entries(eventPhases)) { + // Check if the value of the property matches the expected value + assert.strictEqual(Event[prop], value, `Expected Event.${prop} to be ${value}, but got ${Event[prop]}`); + + const desc = Object.getOwnPropertyDescriptor(Event, prop); + assert.strictEqual(desc.writable, false, `${prop} should not be writable`); + assert.strictEqual(desc.configurable, false, `${prop} should not be configurable`); + assert.strictEqual(desc.enumerable, true, `${prop} should be enumerable`); +} diff --git a/test/js/node/test/parallel/test-events-getmaxlisteners.js b/test/js/node/test/parallel/test-events-getmaxlisteners.js new file mode 100644 index 0000000000..05b4e75b72 --- /dev/null +++ b/test/js/node/test/parallel/test-events-getmaxlisteners.js @@ -0,0 +1,19 @@ +'use strict'; + +require('../common'); +const assert = require('node:assert'); +const { getMaxListeners, EventEmitter, defaultMaxListeners, setMaxListeners } = require('node:events'); + +{ + const ee = new EventEmitter(); + assert.strictEqual(getMaxListeners(ee), defaultMaxListeners); + setMaxListeners(101, ee); + assert.strictEqual(getMaxListeners(ee), 101); +} + +{ + const et = new EventTarget(); + assert.strictEqual(getMaxListeners(et), defaultMaxListeners); + setMaxListeners(101, et); + assert.strictEqual(getMaxListeners(et), 101); +} diff --git a/test/js/node/test/parallel/test-events-list.js b/test/js/node/test/parallel/test-events-list.js new file mode 100644 index 0000000000..4e589b07f2 --- /dev/null +++ b/test/js/node/test/parallel/test-events-list.js @@ -0,0 +1,19 @@ +'use strict'; + +require('../common'); +const EventEmitter = require('events'); +const assert = require('assert'); + +const EE = new EventEmitter(); +const m = () => {}; +EE.on('foo', () => {}); +assert.deepStrictEqual(['foo'], EE.eventNames()); +EE.on('bar', m); +assert.deepStrictEqual(['foo', 'bar'], EE.eventNames()); +EE.removeListener('bar', m); +assert.deepStrictEqual(['foo'], EE.eventNames()); +const s = Symbol('s'); +EE.on(s, m); +assert.deepStrictEqual(['foo', s], EE.eventNames()); +EE.removeListener(s, m); +assert.deepStrictEqual(['foo'], EE.eventNames()); diff --git a/test/js/node/test/parallel/test-events-uncaught-exception-stack.js b/test/js/node/test/parallel/test-events-uncaught-exception-stack.js new file mode 100644 index 0000000000..e330f254ae --- /dev/null +++ b/test/js/node/test/parallel/test-events-uncaught-exception-stack.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const EventEmitter = require('events'); + +// Tests that the error stack where the exception was thrown is *not* appended. + +process.on('uncaughtException', common.mustCall((err) => { + const lines = err.stack.split('\n'); + assert.strictEqual(lines[0], 'Error'); + lines.slice(1).forEach((line) => { + assert.match(line, /^ {4}at/); + }); +})); + +new EventEmitter().emit('error', new Error()); diff --git a/test/js/node/test/parallel/test-eventsource-disabled.js b/test/js/node/test/parallel/test-eventsource-disabled.js new file mode 100644 index 0000000000..ade4f51d99 --- /dev/null +++ b/test/js/node/test/parallel/test-eventsource-disabled.js @@ -0,0 +1,6 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(typeof EventSource, 'undefined'); diff --git a/test/js/node/test/parallel/test-eventtarget-once-twice.js b/test/js/node/test/parallel/test-eventtarget-once-twice.js new file mode 100644 index 0000000000..3358bab90e --- /dev/null +++ b/test/js/node/test/parallel/test-eventtarget-once-twice.js @@ -0,0 +1,14 @@ +'use strict'; +const common = require('../common'); +const { once } = require('events'); + +const et = new EventTarget(); +(async function() { + await once(et, 'foo'); + await once(et, 'foo'); +})().then(common.mustCall()); + +et.dispatchEvent(new Event('foo')); +setImmediate(() => { + et.dispatchEvent(new Event('foo')); +}); diff --git a/test/js/node/test/parallel/test-exception-handler.js b/test/js/node/test/parallel/test-exception-handler.js new file mode 100644 index 0000000000..ca25ccd6ef --- /dev/null +++ b/test/js/node/test/parallel/test-exception-handler.js @@ -0,0 +1,40 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const MESSAGE = 'catch me if you can'; + +process.on('uncaughtException', common.mustCall((e) => { + console.log('uncaught exception! 1'); + assert.strictEqual(MESSAGE, e.message); +})); + +process.on('uncaughtException', common.mustCall((e) => { + console.log('uncaught exception! 2'); + assert.strictEqual(MESSAGE, e.message); +})); + +setTimeout(() => { + throw new Error(MESSAGE); +}, 10); diff --git a/test/js/node/test/parallel/test-exception-handler2.js b/test/js/node/test/parallel/test-exception-handler2.js new file mode 100644 index 0000000000..ae95d45245 --- /dev/null +++ b/test/js/node/test/parallel/test-exception-handler2.js @@ -0,0 +1,36 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +process.on('uncaughtException', function(err) { + console.log(`Caught exception: ${err}`); +}); + +setTimeout(common.mustCall(function() { + console.log('This will still run.'); +}), 50); + +// Intentionally cause an exception, but don't catch it. +nonexistentFunc(); // eslint-disable-line no-undef +assert.fail('This will not run.'); diff --git a/test/js/node/test/parallel/test-fetch.mjs b/test/js/node/test/parallel/test-fetch.mjs new file mode 100644 index 0000000000..bbdb7130ed --- /dev/null +++ b/test/js/node/test/parallel/test-fetch.mjs @@ -0,0 +1,36 @@ +import * as common from '../common/index.mjs'; + +import assert from 'assert'; +import events from 'events'; +import http from 'http'; + +assert.strictEqual(typeof globalThis.fetch, 'function'); +assert.strictEqual(typeof globalThis.FormData, 'function'); +assert.strictEqual(typeof globalThis.Headers, 'function'); +assert.strictEqual(typeof globalThis.Request, 'function'); +assert.strictEqual(typeof globalThis.Response, 'function'); + +{ + const asyncFunction = async function() {}.constructor; + + assert.ok(!(fetch instanceof asyncFunction)); + assert.notStrictEqual(Reflect.getPrototypeOf(fetch), Reflect.getPrototypeOf(async function() {})); + assert.strictEqual(Reflect.getPrototypeOf(fetch), Reflect.getPrototypeOf(function() {})); +} + +const server = http.createServer(common.mustCall((req, res) => { + res.end('Hello world'); +})); +server.listen(0); +await events.once(server, 'listening'); +const port = server.address().port; + +const response = await fetch(`http://localhost:${port}`); + +assert(response instanceof Response); +assert.strictEqual(response.status, 200); +assert.strictEqual(response.statusText, 'OK'); +const body = await response.text(); +assert.strictEqual(body, 'Hello world'); + +server.close(); diff --git a/test/js/node/test/parallel/test-file-read-noexist.js b/test/js/node/test/parallel/test-file-read-noexist.js new file mode 100644 index 0000000000..7293113f22 --- /dev/null +++ b/test/js/node/test/parallel/test-file-read-noexist.js @@ -0,0 +1,32 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const fs = require('fs'); + +const filename = fixtures.path('does_not_exist.txt'); +fs.readFile(filename, 'latin1', common.mustCall(function(err, content) { + assert.ok(err); + assert.strictEqual(err.code, 'ENOENT'); +})); diff --git a/test/js/node/test/parallel/test-file-validate-mode-flag.js b/test/js/node/test/parallel/test-file-validate-mode-flag.js new file mode 100644 index 0000000000..62a9ef2ca2 --- /dev/null +++ b/test/js/node/test/parallel/test-file-validate-mode-flag.js @@ -0,0 +1,40 @@ +'use strict'; + +// Checks for crash regression: https://github.com/nodejs/node/issues/37430 + +const common = require('../common'); +const assert = require('assert'); +const { + open, + openSync, + promises: { + open: openPromise, + }, +} = require('fs'); + +// These should throw, not crash. +const invalid = 4_294_967_296; + +assert.throws(() => open(__filename, invalid, common.mustNotCall()), { + code: 'ERR_OUT_OF_RANGE' +}); + +assert.throws(() => open(__filename, 0, invalid, common.mustNotCall()), { + code: 'ERR_OUT_OF_RANGE' +}); + +assert.throws(() => openSync(__filename, invalid), { + code: 'ERR_OUT_OF_RANGE' +}); + +assert.throws(() => openSync(__filename, 0, invalid), { + code: 'ERR_OUT_OF_RANGE' +}); + +assert.rejects(openPromise(__filename, invalid), { + code: 'ERR_OUT_OF_RANGE' +}).then(common.mustCall()); + +assert.rejects(openPromise(__filename, 0, invalid), { + code: 'ERR_OUT_OF_RANGE' +}).then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-filehandle-close.js b/test/js/node/test/parallel/test-filehandle-close.js new file mode 100644 index 0000000000..6e55f3f06d --- /dev/null +++ b/test/js/node/test/parallel/test-filehandle-close.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +// Test that using FileHandle.close to close an already-closed fd fails +// with EBADF. + +(async function() { + const fh = await fs.promises.open(__filename); + fs.closeSync(fh.fd); + + await assert.rejects(() => fh.close(), { + code: 'EBADF', + syscall: 'close' + }); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-buffertype-writesync.js b/test/js/node/test/parallel/test-fs-buffertype-writesync.js new file mode 100644 index 0000000000..5649a00569 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-buffertype-writesync.js @@ -0,0 +1,16 @@ +'use strict'; +require('../common'); + +// This test ensures that writeSync throws for invalid data input. + +const assert = require('assert'); +const fs = require('fs'); + +[ + true, false, 0, 1, Infinity, () => {}, {}, [], undefined, null, +].forEach((value) => { + assert.throws( + () => fs.writeSync(1, value), + { message: /"buffer"/, code: 'ERR_INVALID_ARG_TYPE' } + ); +}); diff --git a/test/js/node/test/parallel/test-fs-chmod-mask.js b/test/js/node/test/parallel/test-fs-chmod-mask.js new file mode 100644 index 0000000000..53f1931be4 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-chmod-mask.js @@ -0,0 +1,89 @@ +'use strict'; + +// This tests that the lower bits of mode > 0o777 still works in fs APIs. + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +let mode; +// On Windows chmod is only able to manipulate write permission +if (common.isWindows) { + mode = 0o444; // read-only +} else { + mode = 0o777; +} + +const maskToIgnore = 0o10000; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +function test(mode, asString) { + const suffix = asString ? 'str' : 'num'; + const input = asString ? + (mode | maskToIgnore).toString(8) : (mode | maskToIgnore); + + { + const file = tmpdir.resolve(`chmod-async-${suffix}.txt`); + fs.writeFileSync(file, 'test', 'utf-8'); + + fs.chmod(file, input, common.mustSucceed(() => { + assert.strictEqual(fs.statSync(file).mode & 0o777, mode); + })); + } + + { + const file = tmpdir.resolve(`chmodSync-${suffix}.txt`); + fs.writeFileSync(file, 'test', 'utf-8'); + + fs.chmodSync(file, input); + assert.strictEqual(fs.statSync(file).mode & 0o777, mode); + } + + { + const file = tmpdir.resolve(`fchmod-async-${suffix}.txt`); + fs.writeFileSync(file, 'test', 'utf-8'); + fs.open(file, 'w', common.mustSucceed((fd) => { + fs.fchmod(fd, input, common.mustSucceed(() => { + assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode); + fs.close(fd, assert.ifError); + })); + })); + } + + { + const file = tmpdir.resolve(`fchmodSync-${suffix}.txt`); + fs.writeFileSync(file, 'test', 'utf-8'); + const fd = fs.openSync(file, 'w'); + + fs.fchmodSync(fd, input); + assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode); + + fs.close(fd, assert.ifError); + } + + if (fs.lchmod) { + const link = tmpdir.resolve(`lchmod-src-${suffix}`); + const file = tmpdir.resolve(`lchmod-dest-${suffix}`); + fs.writeFileSync(file, 'test', 'utf-8'); + fs.symlinkSync(file, link); + + fs.lchmod(link, input, common.mustSucceed(() => { + assert.strictEqual(fs.lstatSync(link).mode & 0o777, mode); + })); + } + + if (fs.lchmodSync) { + const link = tmpdir.resolve(`lchmodSync-src-${suffix}`); + const file = tmpdir.resolve(`lchmodSync-dest-${suffix}`); + fs.writeFileSync(file, 'test', 'utf-8'); + fs.symlinkSync(file, link); + + fs.lchmodSync(link, input); + assert.strictEqual(fs.lstatSync(link).mode & 0o777, mode); + } +} + +test(mode, true); +test(mode, false); diff --git a/test/js/node/test/parallel/test-fs-chown-type-check.js b/test/js/node/test/parallel/test-fs-chown-type-check.js new file mode 100644 index 0000000000..0ca78aa86e --- /dev/null +++ b/test/js/node/test/parallel/test-fs-chown-type-check.js @@ -0,0 +1,53 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +[false, 1, {}, [], null, undefined].forEach((i) => { + assert.throws( + () => fs.chown(i, 1, 1, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.chownSync(i, 1, 1), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); + +[false, 'test', {}, [], null, undefined].forEach((i) => { + assert.throws( + () => fs.chown('not_a_file_that_exists', i, 1, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.chown('not_a_file_that_exists', 1, i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.chownSync('not_a_file_that_exists', i, 1), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.chownSync('not_a_file_that_exists', 1, i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/test/js/node/test/parallel/test-fs-constants.js b/test/js/node/test/parallel/test-fs-constants.js new file mode 100644 index 0000000000..49bcabd808 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-constants.js @@ -0,0 +1,8 @@ +'use strict'; +require('../common'); +const fs = require('fs'); +const assert = require('assert'); + +// Check if the two constants accepted by chmod() on Windows are defined. +assert.notStrictEqual(fs.constants.S_IRUSR, undefined); +assert.notStrictEqual(fs.constants.S_IWUSR, undefined); diff --git a/test/js/node/test/parallel/test-fs-copyfile-respect-permissions.js b/test/js/node/test/parallel/test-fs-copyfile-respect-permissions.js new file mode 100644 index 0000000000..d668ec63ec --- /dev/null +++ b/test/js/node/test/parallel/test-fs-copyfile-respect-permissions.js @@ -0,0 +1,58 @@ +'use strict'; + +// Test that fs.copyFile() respects file permissions. +// Ref: https://github.com/nodejs/node/issues/26936 + +const common = require('../common'); + +if (!common.isWindows && process.getuid() === 0) + common.skip('as this test should not be run as `root`'); + +if (common.isIBMi) + common.skip('IBMi has a different access permission mechanism'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const assert = require('assert'); +const fs = require('fs'); + +let n = 0; + +function beforeEach() { + n++; + const source = tmpdir.resolve(`source${n}`); + const dest = tmpdir.resolve(`dest${n}`); + fs.writeFileSync(source, 'source'); + fs.writeFileSync(dest, 'dest'); + fs.chmodSync(dest, '444'); + + const check = (err) => { + const expected = ['EACCES', 'EPERM']; + assert(expected.includes(err.code), `${err.code} not in ${expected}`); + assert.strictEqual(fs.readFileSync(dest, 'utf8'), 'dest'); + return true; + }; + + return { source, dest, check }; +} + +// Test synchronous API. +{ + const { source, dest, check } = beforeEach(); + assert.throws(() => { fs.copyFileSync(source, dest); }, check); +} + +// Test promises API. +{ + const { source, dest, check } = beforeEach(); + (async () => { + await assert.rejects(fs.promises.copyFile(source, dest), check); + })().then(common.mustCall()); +} + +// Test callback API. +{ + const { source, dest, check } = beforeEach(); + fs.copyFile(source, dest, common.mustCall(check)); +} diff --git a/test/js/node/test/parallel/test-fs-empty-readStream.js b/test/js/node/test/parallel/test-fs-empty-readStream.js new file mode 100644 index 0000000000..7cbe4d5005 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-empty-readStream.js @@ -0,0 +1,50 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +const emptyFile = fixtures.path('empty.txt'); + +fs.open(emptyFile, 'r', common.mustSucceed((fd) => { + const read = fs.createReadStream(emptyFile, { fd }); + + read.once('data', common.mustNotCall('data event should not emit')); + + read.once('end', common.mustCall()); +})); + +fs.open(emptyFile, 'r', common.mustSucceed((fd) => { + const read = fs.createReadStream(emptyFile, { fd }); + + read.pause(); + + read.once('data', common.mustNotCall('data event should not emit')); + + read.once('end', common.mustNotCall('end event should not emit')); + + setTimeout(common.mustCall(() => { + assert.strictEqual(read.isPaused(), true); + }), common.platformTimeout(50)); +})); diff --git a/test/js/node/test/parallel/test-fs-existssync-false.js b/test/js/node/test/parallel/test-fs-existssync-false.js new file mode 100644 index 0000000000..e81e6c7a31 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-existssync-false.js @@ -0,0 +1,34 @@ +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); + +// This test ensures that fs.existsSync doesn't incorrectly return false. +// (especially on Windows) +// https://github.com/nodejs/node-v0.x-archive/issues/3739 + +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +let dir = path.resolve(tmpdir.path); + +// Make sure that the tmp directory is clean +tmpdir.refresh(); + +// Make a long path. +for (let i = 0; i < 50; i++) { + dir = `${dir}/1234567890`; + try { + fs.mkdirSync(dir, '0777'); + } catch (e) { + if (e.code !== 'EEXIST') { + throw e; + } + } +} + +// Test if file exists synchronously +assert(fs.existsSync(dir), 'Directory is not accessible'); + +// Test if file exists asynchronously +fs.access(dir, common.mustSucceed()); diff --git a/test/js/node/test/parallel/test-fs-fmap.js b/test/js/node/test/parallel/test-fs-fmap.js new file mode 100644 index 0000000000..c4298f0d0e --- /dev/null +++ b/test/js/node/test/parallel/test-fs-fmap.js @@ -0,0 +1,28 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +const { + O_CREAT = 0, + O_RDONLY = 0, + O_TRUNC = 0, + O_WRONLY = 0, + UV_FS_O_FILEMAP = 0 +} = fs.constants; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Run this test on all platforms. While UV_FS_O_FILEMAP is only available on +// Windows, it should be silently ignored on other platforms. + +const filename = tmpdir.resolve('fmap.txt'); +const text = 'Memory File Mapping Test'; + +const mw = UV_FS_O_FILEMAP | O_TRUNC | O_CREAT | O_WRONLY; +const mr = UV_FS_O_FILEMAP | O_RDONLY; + +fs.writeFileSync(filename, text, { flag: mw }); +const r1 = fs.readFileSync(filename, { encoding: 'utf8', flag: mr }); +assert.strictEqual(r1, text); diff --git a/test/js/node/test/parallel/test-fs-fsync.js b/test/js/node/test/parallel/test-fs-fsync.js new file mode 100644 index 0000000000..6168c08d5b --- /dev/null +++ b/test/js/node/test/parallel/test-fs-fsync.js @@ -0,0 +1,58 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const tmpdir = require('../common/tmpdir'); + +const fs = require('fs'); + +const fileFixture = fixtures.path('a.js'); +const fileTemp = tmpdir.resolve('a.js'); + +// Copy fixtures to temp. +tmpdir.refresh(); +fs.copyFileSync(fileFixture, fileTemp); + +fs.open(fileTemp, 'a', 0o777, common.mustSucceed((fd) => { + fs.fdatasyncSync(fd); + + fs.fsyncSync(fd); + + fs.fdatasync(fd, common.mustSucceed(() => { + fs.fsync(fd, common.mustSucceed(() => { + fs.closeSync(fd); + })); + })); +})); + +['', false, null, undefined, {}, []].forEach((input) => { + const errObj = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }; + assert.throws(() => fs.fdatasync(input), errObj); + assert.throws(() => fs.fdatasyncSync(input), errObj); + assert.throws(() => fs.fsync(input), errObj); + assert.throws(() => fs.fsyncSync(input), errObj); +}); diff --git a/test/js/node/test/parallel/test-fs-link.js b/test/js/node/test/parallel/test-fs-link.js new file mode 100644 index 0000000000..df5f606d0c --- /dev/null +++ b/test/js/node/test/parallel/test-fs-link.js @@ -0,0 +1,53 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Test creating and reading hard link +const srcPath = tmpdir.resolve('hardlink-target.txt'); +const dstPath = tmpdir.resolve('link1.js'); +fs.writeFileSync(srcPath, 'hello world'); + +function callback(err) { + assert.ifError(err); + const dstContent = fs.readFileSync(dstPath, 'utf8'); + assert.strictEqual(dstContent, 'hello world'); +} + +fs.link(srcPath, dstPath, common.mustCall(callback)); + +// test error outputs + +[false, 1, [], {}, null, undefined].forEach((i) => { + assert.throws( + () => fs.link(i, '', common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.link('', i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.linkSync(i, ''), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.linkSync('', i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/test/js/node/test/parallel/test-fs-long-path.js b/test/js/node/test/parallel/test-fs-long-path.js new file mode 100644 index 0000000000..11724a88dc --- /dev/null +++ b/test/js/node/test/parallel/test-fs-long-path.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.isWindows) + common.skip('this test is Windows-specific.'); + +const fs = require('fs'); +const path = require('path'); + +const tmpdir = require('../common/tmpdir'); + +// Make a path that will be at least 260 chars long. +const fileNameLen = Math.max(260 - tmpdir.path.length - 1, 1); +const fileName = tmpdir.resolve('x'.repeat(fileNameLen)); +const fullPath = path.resolve(fileName); + +tmpdir.refresh(); + +console.log({ + filenameLength: fileName.length, + fullPathLength: fullPath.length +}); + +fs.writeFile(fullPath, 'ok', common.mustSucceed(() => { + fs.stat(fullPath, common.mustSucceed()); + + // Tests https://github.com/nodejs/node/issues/39721 + fs.realpath.native(fullPath, common.mustSucceed()); + + // Tests https://github.com/nodejs/node/issues/51031 + fs.promises.realpath(fullPath).then(common.mustCall(), common.mustNotCall()); +})); diff --git a/test/js/node/test/parallel/test-fs-make-callback.js b/test/js/node/test/parallel/test-fs-make-callback.js new file mode 100644 index 0000000000..d9341ab06f --- /dev/null +++ b/test/js/node/test/parallel/test-fs-make-callback.js @@ -0,0 +1,28 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const callbackThrowValues = [null, true, false, 0, 1, 'foo', /foo/, [], {}]; + +const { sep } = require('path'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +function testMakeCallback(cb) { + return function() { + // fs.mkdtemp() calls makeCallback() on its third argument + fs.mkdtemp(`${tmpdir.path}${sep}`, {}, cb); + }; +} + +function invalidCallbackThrowsTests() { + callbackThrowValues.forEach((value) => { + assert.throws(testMakeCallback(value), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + }); +} + +invalidCallbackThrowsTests(); diff --git a/test/js/node/test/parallel/test-fs-makeStatsCallback.js b/test/js/node/test/parallel/test-fs-makeStatsCallback.js new file mode 100644 index 0000000000..953fc065e8 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-makeStatsCallback.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const callbackThrowValues = [null, true, false, 0, 1, 'foo', /foo/, [], {}]; + +function testMakeStatsCallback(cb) { + return function() { + // fs.stat() calls makeStatsCallback() on its second argument + fs.stat(__filename, cb); + }; +} + +// Verify the case where a callback function is provided +testMakeStatsCallback(common.mustCall())(); + +function invalidCallbackThrowsTests() { + callbackThrowValues.forEach((value) => { + assert.throws(testMakeStatsCallback(value), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + }); +} + +invalidCallbackThrowsTests(); diff --git a/test/js/node/test/parallel/test-fs-mkdir-recursive-eaccess.js b/test/js/node/test/parallel/test-fs-mkdir-recursive-eaccess.js new file mode 100644 index 0000000000..034a230948 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-mkdir-recursive-eaccess.js @@ -0,0 +1,70 @@ +'use strict'; + +// Test that mkdir with recursive option returns appropriate error +// when executed on folder it does not have permission to access. +// Ref: https://github.com/nodejs/node/issues/31481 + +const common = require('../common'); + +if (!common.isWindows && process.getuid() === 0) + common.skip('as this test should not be run as `root`'); + +if (common.isIBMi) + common.skip('IBMi has a different access permission mechanism'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const assert = require('assert'); +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +let n = 0; + +function makeDirectoryReadOnly(dir) { + let accessErrorCode = 'EACCES'; + if (common.isWindows) { + accessErrorCode = 'EPERM'; + execSync(`icacls ${dir} /deny "everyone:(OI)(CI)(DE,DC,AD,WD)"`); + } else { + fs.chmodSync(dir, '444'); + } + return accessErrorCode; +} + +function makeDirectoryWritable(dir) { + if (common.isWindows) { + execSync(`icacls ${dir} /remove:d "everyone"`); + } +} + +// Synchronous API should return an EACCESS error with path populated. +{ + const dir = tmpdir.resolve(`mkdirp_${n++}`); + fs.mkdirSync(dir); + const codeExpected = makeDirectoryReadOnly(dir); + let err = null; + try { + fs.mkdirSync(path.join(dir, '/foo'), { recursive: true }); + } catch (_err) { + err = _err; + } + makeDirectoryWritable(dir); + assert(err); + assert.strictEqual(err.code, codeExpected); + assert(err.path); +} + +// Asynchronous API should return an EACCESS error with path populated. +{ + const dir = tmpdir.resolve(`mkdirp_${n++}`); + fs.mkdirSync(dir); + const codeExpected = makeDirectoryReadOnly(dir); + fs.mkdir(path.join(dir, '/bar'), { recursive: true }, (err) => { + makeDirectoryWritable(dir); + assert(err); + assert.strictEqual(err.code, codeExpected); + assert(err.path); + }); +} diff --git a/test/js/node/test/parallel/test-fs-mkdtemp-prefix-check.js b/test/js/node/test/parallel/test-fs-mkdtemp-prefix-check.js new file mode 100644 index 0000000000..af5cb6ab7d --- /dev/null +++ b/test/js/node/test/parallel/test-fs-mkdtemp-prefix-check.js @@ -0,0 +1,33 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +const prefixValues = [undefined, null, 0, true, false, 1]; + +function fail(value) { + assert.throws( + () => { + fs.mkdtempSync(value, {}); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +} + +function failAsync(value) { + assert.throws( + () => { + fs.mkdtemp(value, common.mustNotCall()); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +} + +for (const prefixValue of prefixValues) { + fail(prefixValue); + failAsync(prefixValue); +} diff --git a/test/js/node/test/parallel/test-fs-non-number-arguments-throw.js b/test/js/node/test/parallel/test-fs-non-number-arguments-throw.js new file mode 100644 index 0000000000..dbcec683cd --- /dev/null +++ b/test/js/node/test/parallel/test-fs-non-number-arguments-throw.js @@ -0,0 +1,47 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); +const tempFile = tmpdir.resolve('fs-non-number-arguments-throw'); + +tmpdir.refresh(); +fs.writeFileSync(tempFile, 'abc\ndef'); + +// A sanity check when using numbers instead of strings +const sanity = 'def'; +const saneEmitter = fs.createReadStream(tempFile, { start: 4, end: 6 }); + +assert.throws( + () => { + fs.createReadStream(tempFile, { start: '4', end: 6 }); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + +assert.throws( + () => { + fs.createReadStream(tempFile, { start: 4, end: '6' }); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + +assert.throws( + () => { + fs.createWriteStream(tempFile, { start: '4' }); + }, + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + +saneEmitter.on('data', common.mustCall(function(data) { + assert.strictEqual( + sanity, data.toString('utf8'), + `read ${data.toString('utf8')} instead of ${sanity}`); +})); diff --git a/test/js/node/test/parallel/test-fs-open-mode-mask.js b/test/js/node/test/parallel/test-fs-open-mode-mask.js new file mode 100644 index 0000000000..a79591c0ae --- /dev/null +++ b/test/js/node/test/parallel/test-fs-open-mode-mask.js @@ -0,0 +1,40 @@ +'use strict'; + +// This tests that the lower bits of mode > 0o777 still works in fs.open(). + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +const mode = common.isWindows ? 0o444 : 0o644; + +const maskToIgnore = 0o10000; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +function test(mode, asString) { + const suffix = asString ? 'str' : 'num'; + const input = asString ? + (mode | maskToIgnore).toString(8) : (mode | maskToIgnore); + + { + const file = tmpdir.resolve(`openSync-${suffix}.txt`); + const fd = fs.openSync(file, 'w+', input); + assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode); + fs.closeSync(fd); + assert.strictEqual(fs.statSync(file).mode & 0o777, mode); + } + + { + const file = tmpdir.resolve(`open-${suffix}.txt`); + fs.open(file, 'w+', input, common.mustSucceed((fd) => { + assert.strictEqual(fs.fstatSync(fd).mode & 0o777, mode); + fs.closeSync(fd); + assert.strictEqual(fs.statSync(file).mode & 0o777, mode); + })); + } +} + +test(mode, true); +test(mode, false); diff --git a/test/js/node/test/parallel/test-fs-open-no-close.js b/test/js/node/test/parallel/test-fs-open-no-close.js new file mode 100644 index 0000000000..41b58f5ef7 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-open-no-close.js @@ -0,0 +1,31 @@ +'use strict'; + +// Refs: https://github.com/nodejs/node/issues/34266 +// Failing to close a file should not keep the event loop open. + +const common = require('../common'); +const assert = require('assert'); + +const fs = require('fs'); + +const debuglog = (arg) => { + console.log(new Date().toLocaleString(), arg); +}; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +let openFd; + +fs.open(`${tmpdir.path}/dummy`, 'wx+', common.mustCall((err, fd) => { + debuglog('fs open() callback'); + assert.ifError(err); + openFd = fd; +})); +debuglog('waiting for callback'); + +process.on('beforeExit', common.mustCall(() => { + if (openFd) { + fs.closeSync(openFd); + } +})); diff --git a/test/js/node/test/parallel/test-fs-open-numeric-flags.js b/test/js/node/test/parallel/test-fs-open-numeric-flags.js new file mode 100644 index 0000000000..3237b2764e --- /dev/null +++ b/test/js/node/test/parallel/test-fs-open-numeric-flags.js @@ -0,0 +1,15 @@ +'use strict'; +require('../common'); + +const assert = require('assert'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// O_WRONLY without O_CREAT shall fail with ENOENT +const pathNE = tmpdir.resolve('file-should-not-exist'); +assert.throws( + () => fs.openSync(pathNE, fs.constants.O_WRONLY), + (e) => e.code === 'ENOENT' +); diff --git a/test/js/node/test/parallel/test-fs-open.js b/test/js/node/test/parallel/test-fs-open.js new file mode 100644 index 0000000000..0855e521f7 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-open.js @@ -0,0 +1,120 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +let caughtException = false; + +try { + // Should throw ENOENT, not EBADF + // see https://github.com/joyent/node/pull/1228 + fs.openSync('/8hvftyuncxrt/path/to/file/that/does/not/exist', 'r'); +} catch (e) { + assert.strictEqual(e.code, 'ENOENT'); + caughtException = true; +} +assert.strictEqual(caughtException, true); + +fs.openSync(__filename); + +fs.open(__filename, common.mustSucceed()); + +fs.open(__filename, 'r', common.mustSucceed()); + +fs.open(__filename, 'rs', common.mustSucceed()); + +fs.open(__filename, 'r', 0, common.mustSucceed()); + +fs.open(__filename, 'r', null, common.mustSucceed()); + +async function promise() { + await fs.promises.open(__filename); + await fs.promises.open(__filename, 'r'); +} + +promise().then(common.mustCall()).catch(common.mustNotCall()); + +assert.throws( + () => fs.open(__filename, 'r', 'boom', common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError' + } +); + +for (const extra of [[], ['r'], ['r', 0], ['r', 0, 'bad callback']]) { + assert.throws( + () => fs.open(__filename, ...extra), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +} + +[false, 1, [], {}, null, undefined].forEach((i) => { + assert.throws( + () => fs.open(i, 'r', common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.openSync(i, 'r', common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.rejects( + fs.promises.open(i, 'r'), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ).then(common.mustCall()); +}); + +// Check invalid modes. +[false, [], {}].forEach((mode) => { + assert.throws( + () => fs.open(__filename, 'r', mode, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE' + } + ); + assert.throws( + () => fs.openSync(__filename, 'r', mode, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE' + } + ); + assert.rejects( + fs.promises.open(__filename, 'r', mode), + { + code: 'ERR_INVALID_ARG_TYPE' + } + ).then(common.mustCall()); +}); diff --git a/test/js/node/test/parallel/test-fs-options-immutable.js b/test/js/node/test/parallel/test-fs-options-immutable.js new file mode 100644 index 0000000000..fffdecdc87 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-options-immutable.js @@ -0,0 +1,70 @@ +'use strict'; +const common = require('../common'); + +// These tests make sure that the `options` object passed to these functions are +// never altered. +// +// Refer: https://github.com/nodejs/node/issues/7655 + +const fs = require('fs'); + +const options = common.mustNotMutateObjectDeep({}); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +fs.readFile(__filename, options, common.mustSucceed()); +fs.readFileSync(__filename, options); + +fs.readdir(__dirname, options, common.mustSucceed()); +fs.readdirSync(__dirname, options); + +if (common.canCreateSymLink()) { + const sourceFile = tmpdir.resolve('test-readlink'); + const linkFile = tmpdir.resolve('test-readlink-link'); + + fs.writeFileSync(sourceFile, ''); + fs.symlinkSync(sourceFile, linkFile); + + fs.readlink(linkFile, options, common.mustSucceed()); + fs.readlinkSync(linkFile, options); +} + +{ + const fileName = tmpdir.resolve('writeFile'); + fs.writeFileSync(fileName, 'ABCD', options); + fs.writeFile(fileName, 'ABCD', options, common.mustSucceed()); +} + +{ + const fileName = tmpdir.resolve('appendFile'); + fs.appendFileSync(fileName, 'ABCD', options); + fs.appendFile(fileName, 'ABCD', options, common.mustSucceed()); +} + +if (!common.isIBMi) { // IBMi does not support fs.watch() + const watch = fs.watch(__filename, options, common.mustNotCall()); + watch.close(); +} + +{ + fs.watchFile(__filename, options, common.mustNotCall()); + fs.unwatchFile(__filename); +} + +{ + fs.realpathSync(__filename, options); + fs.realpath(__filename, options, common.mustSucceed()); +} + +{ + const tempFileName = tmpdir.resolve('mkdtemp-'); + fs.mkdtempSync(tempFileName, options); + fs.mkdtemp(tempFileName, options, common.mustSucceed()); +} + +{ + const fileName = tmpdir.resolve('streams'); + fs.WriteStream(fileName, options).once('open', common.mustCall(() => { + fs.ReadStream(fileName, options).destroy(); + })).end(); +} diff --git a/test/js/node/test/parallel/test-fs-promises-exists.js b/test/js/node/test/parallel/test-fs-promises-exists.js new file mode 100644 index 0000000000..b9bbeee1de --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-exists.js @@ -0,0 +1,9 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const fsPromises = require('fs/promises'); + +assert.strictEqual(fsPromises, fs.promises); +assert.strictEqual(fsPromises.constants, fs.constants); diff --git a/test/js/node/test/parallel/test-fs-promises-file-handle-append-file.js b/test/js/node/test/parallel/test-fs-promises-file-handle-append-file.js new file mode 100644 index 0000000000..90bb6e3925 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-file-handle-append-file.js @@ -0,0 +1,44 @@ +'use strict'; + +const common = require('../common'); + +// The following tests validate base functionality for the fs.promises +// FileHandle.appendFile method. + +const fs = require('fs'); +const { open } = fs.promises; +const path = require('path'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const tmpDir = tmpdir.path; + +tmpdir.refresh(); + +async function validateAppendBuffer() { + const filePath = path.resolve(tmpDir, 'tmp-append-file-buffer.txt'); + const fileHandle = await open(filePath, 'a'); + const buffer = Buffer.from('a&Dp'.repeat(100), 'utf8'); + + await fileHandle.appendFile(buffer); + const appendedFileData = fs.readFileSync(filePath); + assert.deepStrictEqual(appendedFileData, buffer); + + await fileHandle.close(); +} + +async function validateAppendString() { + const filePath = path.resolve(tmpDir, 'tmp-append-file-string.txt'); + const fileHandle = await open(filePath, 'a'); + const string = 'x~yz'.repeat(100); + + await fileHandle.appendFile(string); + const stringAsBuffer = Buffer.from(string, 'utf8'); + const appendedFileData = fs.readFileSync(filePath); + assert.deepStrictEqual(appendedFileData, stringAsBuffer); + + await fileHandle.close(); +} + +validateAppendBuffer() + .then(validateAppendString) + .then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promises-file-handle-chmod.js b/test/js/node/test/parallel/test-fs-promises-file-handle-chmod.js new file mode 100644 index 0000000000..5c7414a9b1 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-file-handle-chmod.js @@ -0,0 +1,45 @@ +'use strict'; + +const common = require('../common'); + +// The following tests validate base functionality for the fs.promises +// FileHandle.chmod method. + +const fs = require('fs'); +const { open } = fs.promises; +const path = require('path'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const tmpDir = tmpdir.path; + +tmpdir.refresh(); + +async function validateFilePermission() { + const filePath = path.resolve(tmpDir, 'tmp-chmod.txt'); + const fileHandle = await open(filePath, 'w+', 0o444); + // File created with r--r--r-- 444 + const statsBeforeMod = fs.statSync(filePath); + assert.strictEqual(statsBeforeMod.mode & 0o444, 0o444); + + let expectedAccess; + const newPermissions = 0o765; + + if (common.isWindows) { + // Chmod in Windows will only toggle read only/write access. The + // fs.Stats.mode in Windows is computed using read/write + // bits (not exec). Read-only at best returns 444; r/w 666. + // Refer: /deps/uv/src/win/fs.cfs; + expectedAccess = 0o664; + } else { + expectedAccess = newPermissions; + } + + // Change the permissions to rwxr--r-x + await fileHandle.chmod(newPermissions); + const statsAfterMod = fs.statSync(filePath); + assert.deepStrictEqual(statsAfterMod.mode & expectedAccess, expectedAccess); + + await fileHandle.close(); +} + +validateFilePermission().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promises-file-handle-dispose.js b/test/js/node/test/parallel/test-fs-promises-file-handle-dispose.js new file mode 100644 index 0000000000..406430eef4 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-file-handle-dispose.js @@ -0,0 +1,12 @@ +'use strict'; + +const common = require('../common'); +const { promises: fs } = require('fs'); + +async function doOpen() { + const fh = await fs.open(__filename); + fh.on('close', common.mustCall()); + await fh[Symbol.asyncDispose](); +} + +doOpen().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promises-file-handle-read-worker.js b/test/js/node/test/parallel/test-fs-promises-file-handle-read-worker.js new file mode 100644 index 0000000000..7ae881801a --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-file-handle-read-worker.js @@ -0,0 +1,54 @@ +'use strict'; +const common = require('../common'); +const fs = require('fs'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const file = tmpdir.resolve('read_stream_filehandle_worker.txt'); +const input = 'hello world'; +const { Worker, isMainThread, workerData } = require('worker_threads'); + +if (isMainThread || !workerData) { + tmpdir.refresh(); + fs.writeFileSync(file, input); + + fs.promises.open(file, 'r').then((handle) => { + handle.on('close', common.mustNotCall()); + new Worker(__filename, { + workerData: { handle }, + transferList: [handle] + }); + }); + fs.promises.open(file, 'r').then(async (handle) => { + try { + fs.createReadStream(null, { fd: handle }); + assert.throws(() => { + new Worker(__filename, { + workerData: { handle }, + transferList: [handle] + }); + }, { + code: 25, + name: 'DataCloneError', + }); + } finally { + await handle.close(); + } + }); +} else { + let output = ''; + + const handle = workerData.handle; + handle.on('close', common.mustCall()); + const stream = fs.createReadStream(null, { fd: handle }); + + stream.on('data', common.mustCallAtLeast((data) => { + output += data; + })); + + stream.on('end', common.mustCall(() => { + handle.close(); + assert.strictEqual(output, input); + })); + + stream.on('close', common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-fs-promises-file-handle-stat.js b/test/js/node/test/parallel/test-fs-promises-file-handle-stat.js new file mode 100644 index 0000000000..c989c405c5 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-file-handle-stat.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); + +// The following tests validate base functionality for the fs.promises +// FileHandle.stat method. + +const { open } = require('fs').promises; +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); + +tmpdir.refresh(); + +async function validateStat() { + const filePath = tmpdir.resolve('tmp-read-file.txt'); + const fileHandle = await open(filePath, 'w+'); + const stats = await fileHandle.stat(); + assert.ok(stats.mtime instanceof Date); + await fileHandle.close(); +} + +validateStat() + .then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promises-file-handle-truncate.js b/test/js/node/test/parallel/test-fs-promises-file-handle-truncate.js new file mode 100644 index 0000000000..687f8c2725 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-file-handle-truncate.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { open, readFile } = require('fs').promises; +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +async function validateTruncate() { + const text = 'Hello world'; + const filename = tmpdir.resolve('truncate-file.txt'); + const fileHandle = await open(filename, 'w+'); + + const buffer = Buffer.from(text, 'utf8'); + await fileHandle.write(buffer, 0, buffer.length); + + assert.strictEqual((await readFile(filename)).toString(), text); + + await fileHandle.truncate(5); + assert.strictEqual((await readFile(filename)).toString(), 'Hello'); + + await fileHandle.close(); +} + +validateTruncate().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promises-file-handle-write.js b/test/js/node/test/parallel/test-fs-promises-file-handle-write.js new file mode 100644 index 0000000000..7f3d12d481 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-file-handle-write.js @@ -0,0 +1,77 @@ +'use strict'; + +const common = require('../common'); + +// The following tests validate base functionality for the fs.promises +// FileHandle.write method. + +const fs = require('fs'); +const { open } = fs.promises; +const path = require('path'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const tmpDir = tmpdir.path; + +tmpdir.refresh(); + +async function validateWrite() { + const filePathForHandle = path.resolve(tmpDir, 'tmp-write.txt'); + const fileHandle = await open(filePathForHandle, 'w+'); + const buffer = Buffer.from('Hello world'.repeat(100), 'utf8'); + + await fileHandle.write(buffer, 0, buffer.length); + const readFileData = fs.readFileSync(filePathForHandle); + assert.deepStrictEqual(buffer, readFileData); + + await fileHandle.close(); +} + +async function validateEmptyWrite() { + const filePathForHandle = path.resolve(tmpDir, 'tmp-empty-write.txt'); + const fileHandle = await open(filePathForHandle, 'w+'); + const buffer = Buffer.from(''); // empty buffer + + await fileHandle.write(buffer, 0, buffer.length); + const readFileData = fs.readFileSync(filePathForHandle); + assert.deepStrictEqual(buffer, readFileData); + + await fileHandle.close(); +} + +async function validateNonUint8ArrayWrite() { + const filePathForHandle = path.resolve(tmpDir, 'tmp-data-write.txt'); + const fileHandle = await open(filePathForHandle, 'w+'); + const buffer = Buffer.from('Hello world', 'utf8').toString('base64'); + + await fileHandle.write(buffer, 0, buffer.length); + const readFileData = fs.readFileSync(filePathForHandle); + assert.deepStrictEqual(Buffer.from(buffer, 'utf8'), readFileData); + + await fileHandle.close(); +} + +async function validateNonStringValuesWrite() { + const filePathForHandle = path.resolve(tmpDir, 'tmp-non-string-write.txt'); + const fileHandle = await open(filePathForHandle, 'w+'); + const nonStringValues = [ + 123, {}, new Map(), null, undefined, 0n, () => {}, Symbol(), true, + new String('notPrimitive'), + { toString() { return 'amObject'; } }, + { [Symbol.toPrimitive]: (hint) => 'amObject' }, + ]; + for (const nonStringValue of nonStringValues) { + await assert.rejects( + fileHandle.write(nonStringValue), + { message: /"buffer"/, code: 'ERR_INVALID_ARG_TYPE' } + ); + } + + await fileHandle.close(); +} + +Promise.all([ + validateWrite(), + validateEmptyWrite(), + validateNonUint8ArrayWrite(), + validateNonStringValuesWrite(), +]).then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promises-readfile-empty.js b/test/js/node/test/parallel/test-fs-promises-readfile-empty.js new file mode 100644 index 0000000000..ef15a26811 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-readfile-empty.js @@ -0,0 +1,17 @@ +'use strict'; +require('../common'); + +const assert = require('assert'); +const { promises: fs } = require('fs'); +const fixtures = require('../common/fixtures'); + +const fn = fixtures.path('empty.txt'); + +fs.readFile(fn) + .then(assert.ok); + +fs.readFile(fn, 'utf8') + .then(assert.strictEqual.bind(this, '')); + +fs.readFile(fn, { encoding: 'utf8' }) + .then(assert.strictEqual.bind(this, '')); diff --git a/test/js/node/test/parallel/test-fs-promises-readfile-with-fd.js b/test/js/node/test/parallel/test-fs-promises-readfile-with-fd.js new file mode 100644 index 0000000000..b5d1c26168 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-readfile-with-fd.js @@ -0,0 +1,34 @@ +'use strict'; + +// This test makes sure that `readFile()` always reads from the current +// position of the file, instead of reading from the beginning of the file. + +const common = require('../common'); +const assert = require('assert'); +const { writeFileSync } = require('fs'); +const { open } = require('fs').promises; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const fn = tmpdir.resolve('test.txt'); +writeFileSync(fn, 'Hello World'); + +async function readFileTest() { + const handle = await open(fn, 'r'); + + /* Read only five bytes, so that the position moves to five. */ + const buf = Buffer.alloc(5); + const { bytesRead } = await handle.read(buf, 0, 5, null); + assert.strictEqual(bytesRead, 5); + assert.strictEqual(buf.toString(), 'Hello'); + + /* readFile() should read from position five, instead of zero. */ + assert.strictEqual((await handle.readFile()).toString(), ' World'); + + await handle.close(); +} + + +readFileTest() + .then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promises-writefile-typedarray.js b/test/js/node/test/parallel/test-fs-promises-writefile-typedarray.js new file mode 100644 index 0000000000..32d9cffa23 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-writefile-typedarray.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('../common'); +const fs = require('fs'); +const fsPromises = fs.promises; +const path = require('path'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const tmpDir = tmpdir.path; + +tmpdir.refresh(); + +const dest = path.resolve(tmpDir, 'tmp.txt'); +// Use a file size larger than `kReadFileMaxChunkSize`. +const buffer = Buffer.from('012'.repeat(2 ** 14)); + +(async () => { + for (const Constructor of [Uint8Array, Uint16Array, Uint32Array]) { + const array = new Constructor(buffer.buffer); + await fsPromises.writeFile(dest, array); + const data = await fsPromises.readFile(dest); + assert.deepStrictEqual(data, buffer); + } +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promises-writefile-with-fd.js b/test/js/node/test/parallel/test-fs-promises-writefile-with-fd.js new file mode 100644 index 0000000000..7cb9eeeca9 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promises-writefile-with-fd.js @@ -0,0 +1,35 @@ +'use strict'; + +// This test makes sure that `writeFile()` always writes from the current +// position of the file, instead of truncating the file. + +const common = require('../common'); +const assert = require('assert'); +const { readFileSync } = require('fs'); +const { open } = require('fs').promises; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const fn = tmpdir.resolve('test.txt'); + +async function writeFileTest() { + const handle = await open(fn, 'w'); + + /* Write only five bytes, so that the position moves to five. */ + const buf = Buffer.from('Hello'); + const { bytesWritten } = await handle.write(buf, 0, 5, null); + assert.strictEqual(bytesWritten, 5); + + /* Write some more with writeFile(). */ + await handle.writeFile('World'); + + /* New content should be written at position five, instead of zero. */ + assert.strictEqual(readFileSync(fn).toString(), 'HelloWorld'); + + await handle.close(); +} + + +writeFileTest() + .then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-promisified.js b/test/js/node/test/parallel/test-fs-promisified.js new file mode 100644 index 0000000000..63430f64a8 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-promisified.js @@ -0,0 +1,36 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const { promisify } = require('util'); + +const read = promisify(fs.read); +const write = promisify(fs.write); +const exists = promisify(fs.exists); + +{ + const fd = fs.openSync(__filename, 'r'); + read(fd, Buffer.alloc(1024), 0, 1024, null).then(common.mustCall((obj) => { + assert.strictEqual(typeof obj.bytesRead, 'number'); + assert(obj.buffer instanceof Buffer); + fs.closeSync(fd); + })); +} + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +{ + const filename = tmpdir.resolve('write-promise.txt'); + const fd = fs.openSync(filename, 'w'); + write(fd, Buffer.from('foobar')).then(common.mustCall((obj) => { + assert.strictEqual(typeof obj.bytesWritten, 'number'); + assert.strictEqual(obj.buffer.toString(), 'foobar'); + fs.closeSync(fd); + })); +} + +{ + exists(__filename).then(common.mustCall((x) => { + assert.strictEqual(x, true); + })); +} diff --git a/test/js/node/test/parallel/test-fs-read-file-assert-encoding.js b/test/js/node/test/parallel/test-fs-read-file-assert-encoding.js new file mode 100644 index 0000000000..77cd0e3ab1 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-file-assert-encoding.js @@ -0,0 +1,12 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +const encoding = 'foo-8'; +const filename = 'bar.txt'; +assert.throws( + () => fs.readFile(filename, { encoding }, common.mustNotCall()), + { code: 'ERR_INVALID_ARG_VALUE', name: 'TypeError' } +); diff --git a/test/js/node/test/parallel/test-fs-read-file-sync-hostname.js b/test/js/node/test/parallel/test-fs-read-file-sync-hostname.js new file mode 100644 index 0000000000..599f48b6cc --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-file-sync-hostname.js @@ -0,0 +1,33 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.isLinux) + common.skip('Test is linux specific.'); + +const assert = require('assert'); +const fs = require('fs'); + +// Test to make sure reading a file under the /proc directory works. See: +// https://groups.google.com/forum/#!topic/nodejs-dev/rxZ_RoH1Gn0 +const hostname = fs.readFileSync('/proc/sys/kernel/hostname'); +assert.ok(hostname.length > 0); diff --git a/test/js/node/test/parallel/test-fs-read-offset-null.js b/test/js/node/test/parallel/test-fs-read-offset-null.js new file mode 100644 index 0000000000..012c94e41e --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-offset-null.js @@ -0,0 +1,64 @@ +'use strict'; + + +// Test to assert the desired functioning of fs.read +// when {offset:null} is passed as options parameter + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const fsPromises = fs.promises; +const fixtures = require('../common/fixtures'); +const filepath = fixtures.path('x.txt'); + +const buf = Buffer.alloc(1); +// Reading only one character, hence buffer of one byte is enough. + +// Tests are done by making sure the first letter in buffer is +// same as first letter in file. +// 120 is the ascii code of letter x. + +// Tests for callback API. +fs.open(filepath, 'r', common.mustSucceed((fd) => { + fs.read(fd, { offset: null, buffer: buf }, + common.mustSucceed((bytesRead, buffer) => { + assert.strictEqual(buffer[0], 120); + fs.close(fd, common.mustSucceed(() => {})); + })); +})); + +fs.open(filepath, 'r', common.mustSucceed((fd) => { + fs.read(fd, buf, { offset: null }, + common.mustSucceed((bytesRead, buffer) => { + assert.strictEqual(buffer[0], 120); + fs.close(fd, common.mustSucceed(() => {})); + })); +})); + +let filehandle = null; + +// Tests for promises api +(async () => { + filehandle = await fsPromises.open(filepath, 'r'); + const readObject = await filehandle.read(buf, { offset: null }); + assert.strictEqual(readObject.buffer[0], 120); +})() +.finally(() => filehandle?.close()) +.then(common.mustCall()); + +// Undocumented: omitted position works the same as position === null +(async () => { + filehandle = await fsPromises.open(filepath, 'r'); + const readObject = await filehandle.read(buf, null, buf.length); + assert.strictEqual(readObject.buffer[0], 120); +})() +.finally(() => filehandle?.close()) +.then(common.mustCall()); + +(async () => { + filehandle = await fsPromises.open(filepath, 'r'); + const readObject = await filehandle.read(buf, null, buf.length, 0); + assert.strictEqual(readObject.buffer[0], 120); +})() +.finally(() => filehandle?.close()) +.then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-read-optional-params.js b/test/js/node/test/parallel/test-fs-read-optional-params.js new file mode 100644 index 0000000000..e89f86ee5f --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-optional-params.js @@ -0,0 +1,38 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const fs = require('fs'); +const assert = require('assert'); +const filepath = fixtures.path('x.txt'); + +const expected = Buffer.from('xyz\n'); +const defaultBufferAsync = Buffer.alloc(16384); +const bufferAsOption = Buffer.allocUnsafe(expected.byteLength); + +function testValid(message, ...options) { + const paramsMsg = `${message} (as params)`; + const paramsFilehandle = fs.openSync(filepath, 'r'); + fs.read(paramsFilehandle, ...options, common.mustSucceed((bytesRead, buffer) => { + assert.strictEqual(bytesRead, expected.byteLength, paramsMsg); + assert.deepStrictEqual(defaultBufferAsync.byteLength, buffer.byteLength, paramsMsg); + fs.closeSync(paramsFilehandle); + })); + + const optionsMsg = `${message} (as options)`; + const optionsFilehandle = fs.openSync(filepath, 'r'); + fs.read(optionsFilehandle, bufferAsOption, ...options, common.mustSucceed((bytesRead, buffer) => { + assert.strictEqual(bytesRead, expected.byteLength, optionsMsg); + assert.deepStrictEqual(bufferAsOption.byteLength, buffer.byteLength, optionsMsg); + fs.closeSync(optionsFilehandle); + })); +} + +testValid('Not passing in any object'); +testValid('Passing in a null', null); +testValid('Passing in an empty object', common.mustNotMutateObjectDeep({})); +testValid('Passing in an object', common.mustNotMutateObjectDeep({ + offset: 0, + length: bufferAsOption.byteLength, + position: 0, +})); diff --git a/test/js/node/test/parallel/test-fs-read-promises-optional-params.js b/test/js/node/test/parallel/test-fs-read-promises-optional-params.js new file mode 100644 index 0000000000..f9007a69ba --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-promises-optional-params.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const fs = require('fs'); +const read = require('util').promisify(fs.read); +const assert = require('assert'); +const filepath = fixtures.path('x.txt'); +const fd = fs.openSync(filepath, 'r'); + +const expected = Buffer.from('xyz\n'); +const defaultBufferAsync = Buffer.alloc(16384); +const bufferAsOption = Buffer.allocUnsafe(expected.byteLength); + +read(fd, common.mustNotMutateObjectDeep({})) + .then(function({ bytesRead, buffer }) { + assert.strictEqual(bytesRead, expected.byteLength); + assert.deepStrictEqual(defaultBufferAsync.byteLength, buffer.byteLength); + }) + .then(common.mustCall()); + +read(fd, bufferAsOption, common.mustNotMutateObjectDeep({ position: 0 })) + .then(function({ bytesRead, buffer }) { + assert.strictEqual(bytesRead, expected.byteLength); + assert.deepStrictEqual(bufferAsOption.byteLength, buffer.byteLength); + }) + .then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-read-stream-autoClose.js b/test/js/node/test/parallel/test-fs-read-stream-autoClose.js new file mode 100644 index 0000000000..728d4f5eee --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-stream-autoClose.js @@ -0,0 +1,15 @@ +'use strict'; + +const common = require('../common'); +const fs = require('fs'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const writeFile = tmpdir.resolve('write-autoClose.txt'); +tmpdir.refresh(); + +const file = fs.createWriteStream(writeFile, { autoClose: true }); + +file.on('finish', common.mustCall(() => { + assert.strictEqual(file.destroyed, false); +})); +file.end('asd'); diff --git a/test/js/node/test/parallel/test-fs-read-stream-double-close.js b/test/js/node/test/parallel/test-fs-read-stream-double-close.js new file mode 100644 index 0000000000..32117a0a1e --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-stream-double-close.js @@ -0,0 +1,19 @@ +'use strict'; + +const common = require('../common'); +const fs = require('fs'); + +{ + const s = fs.createReadStream(__filename); + + s.close(common.mustCall()); + s.close(common.mustCall()); +} + +{ + const s = fs.createReadStream(__filename); + + // This is a private API, but it is worth testing. close calls this + s.destroy(null, common.mustCall()); + s.destroy(null, common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-fs-read-stream-encoding.js b/test/js/node/test/parallel/test-fs-read-stream-encoding.js new file mode 100644 index 0000000000..8eeaee6572 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-stream-encoding.js @@ -0,0 +1,17 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const stream = require('stream'); +const fixtures = require('../common/fixtures'); +const encoding = 'base64'; + +const example = fixtures.path('x.txt'); +const assertStream = new stream.Writable({ + write: function(chunk, enc, next) { + const expected = Buffer.from('xyz'); + assert(chunk.equals(expected)); + } +}); +assertStream.setDefaultEncoding(encoding); +fs.createReadStream(example, encoding).pipe(assertStream); diff --git a/test/js/node/test/parallel/test-fs-read-stream-fd-leak.js b/test/js/node/test/parallel/test-fs-read-stream-fd-leak.js new file mode 100644 index 0000000000..81712def76 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-stream-fd-leak.js @@ -0,0 +1,62 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +let openCount = 0; +const _fsopen = fs.open; +const _fsclose = fs.close; + +const loopCount = 50; +const totalCheck = 50; +const emptyTxt = fixtures.path('empty.txt'); + +fs.open = function() { + openCount++; + return _fsopen.apply(null, arguments); +}; + +fs.close = function() { + openCount--; + return _fsclose.apply(null, arguments); +}; + +function testLeak(endFn, callback) { + console.log(`testing for leaks from fs.createReadStream().${endFn}()...`); + + let i = 0; + let check = 0; + + function checkFunction() { + if (openCount !== 0 && check < totalCheck) { + check++; + setTimeout(checkFunction, 100); + return; + } + + assert.strictEqual( + openCount, + 0, + `no leaked file descriptors using ${endFn}() (got ${openCount})` + ); + + openCount = 0; + callback && setTimeout(callback, 100); + } + + setInterval(function() { + const s = fs.createReadStream(emptyTxt); + s[endFn](); + + if (++i === loopCount) { + clearTimeout(this); + setTimeout(checkFunction, 100); + } + }, 2); +} + +testLeak('close', function() { + testLeak('destroy'); +}); diff --git a/test/js/node/test/parallel/test-fs-read-stream-fd.js b/test/js/node/test/parallel/test-fs-read-stream-fd.js new file mode 100644 index 0000000000..f40d35b967 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-stream-fd.js @@ -0,0 +1,45 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fs = require('fs'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const file = tmpdir.resolve('read_stream_fd_test.txt'); +const input = 'hello world'; + +let output = ''; +tmpdir.refresh(); +fs.writeFileSync(file, input); + +const fd = fs.openSync(file, 'r'); +const stream = fs.createReadStream(null, { fd: fd, encoding: 'utf8' }); + +assert.strictEqual(stream.path, undefined); + +stream.on('data', common.mustCallAtLeast((data) => { + output += data; +})); + +process.on('exit', () => { + assert.strictEqual(output, input); +}); diff --git a/test/js/node/test/parallel/test-fs-read-stream-resume.js b/test/js/node/test/parallel/test-fs-read-stream-resume.js new file mode 100644 index 0000000000..36e3d809cd --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-stream-resume.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); + +const fs = require('fs'); + +const file = fixtures.path('x.txt'); +let data = ''; +let first = true; + +const stream = fs.createReadStream(file); +stream.setEncoding('utf8'); +stream.on('data', common.mustCallAtLeast(function(chunk) { + data += chunk; + if (first) { + first = false; + stream.resume(); + } +})); + +process.nextTick(function() { + stream.pause(); + setTimeout(function() { + stream.resume(); + }, 100); +}); + +process.on('exit', function() { + assert.strictEqual(data, 'xyz\n'); +}); diff --git a/test/js/node/test/parallel/test-fs-read-zero-length.js b/test/js/node/test/parallel/test-fs-read-zero-length.js new file mode 100644 index 0000000000..ac2efc73f5 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-read-zero-length.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const fs = require('fs'); +const filepath = fixtures.path('x.txt'); +const fd = fs.openSync(filepath, 'r'); +const bufferAsync = Buffer.alloc(0); +const bufferSync = Buffer.alloc(0); + +fs.read(fd, bufferAsync, 0, 0, 0, common.mustCall((err, bytesRead) => { + assert.strictEqual(bytesRead, 0); + assert.deepStrictEqual(bufferAsync, Buffer.alloc(0)); +})); + +const r = fs.readSync(fd, bufferSync, 0, 0, 0); +assert.deepStrictEqual(bufferSync, Buffer.alloc(0)); +assert.strictEqual(r, 0); diff --git a/test/js/node/test/parallel/test-fs-readdir-buffer.js b/test/js/node/test/parallel/test-fs-readdir-buffer.js new file mode 100644 index 0000000000..54b7353cb1 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readdir-buffer.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../common'); +const fs = require('fs'); + +if (!common.isMacOS) { + common.skip('this tests works only on MacOS'); +} + +const assert = require('assert'); + +fs.readdir( + Buffer.from('/dev'), + { withFileTypes: true, encoding: 'buffer' }, + common.mustCall((e, d) => { + assert.strictEqual(e, null); + }) +); diff --git a/test/js/node/test/parallel/test-fs-readdir-recursive.js b/test/js/node/test/parallel/test-fs-readdir-recursive.js new file mode 100644 index 0000000000..f32e600d2a --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readdir-recursive.js @@ -0,0 +1,14 @@ +'use strict'; +const common = require('../common'); +const fs = require('fs'); +const net = require('net'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const server = net.createServer().listen(common.PIPE, common.mustCall(() => { + // The process should not crash + // See https://github.com/nodejs/node/issues/52159 + fs.readdirSync(tmpdir.path, { recursive: true }); + server.close(); +})); diff --git a/test/js/node/test/parallel/test-fs-readdir-ucs2.js b/test/js/node/test/parallel/test-fs-readdir-ucs2.js new file mode 100644 index 0000000000..264858ec6a --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readdir-ucs2.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +if (!common.isLinux) + common.skip('Test is linux specific.'); + +const path = require('path'); +const fs = require('fs'); +const assert = require('assert'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +const filename = '\uD83D\uDC04'; +const root = Buffer.from(`${tmpdir.path}${path.sep}`); +const filebuff = Buffer.from(filename, 'ucs2'); +const fullpath = Buffer.concat([root, filebuff]); + +try { + fs.closeSync(fs.openSync(fullpath, 'w+')); +} catch (e) { + if (e.code === 'EINVAL') + common.skip('test requires filesystem that supports UCS2'); + throw e; +} + +fs.readdir(tmpdir.path, 'ucs2', common.mustSucceed((list) => { + assert.strictEqual(list.length, 1); + const fn = list[0]; + assert.deepStrictEqual(Buffer.from(fn, 'ucs2'), filebuff); + assert.strictEqual(fn, filename); +})); diff --git a/test/js/node/test/parallel/test-fs-readfile-empty.js b/test/js/node/test/parallel/test-fs-readfile-empty.js new file mode 100644 index 0000000000..f6303777b2 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readfile-empty.js @@ -0,0 +1,45 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +// Trivial test of fs.readFile on an empty file. +const common = require('../common'); +const fs = require('fs'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const fn = fixtures.path('empty.txt'); + +fs.readFile(fn, common.mustCall((err, data) => { + assert.ok(data); +})); + +fs.readFile(fn, 'utf8', common.mustCall((err, data) => { + assert.strictEqual(data, ''); +})); + +fs.readFile(fn, { encoding: 'utf8' }, common.mustCall((err, data) => { + assert.strictEqual(data, ''); +})); + +assert.ok(fs.readFileSync(fn)); +assert.strictEqual(fs.readFileSync(fn, 'utf8'), ''); diff --git a/test/js/node/test/parallel/test-fs-readfile-eof.js b/test/js/node/test/parallel/test-fs-readfile-eof.js new file mode 100644 index 0000000000..94354b915b --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readfile-eof.js @@ -0,0 +1,46 @@ +'use strict'; +const common = require('../common'); + +if (common.isWindows || common.isAIX || common.isIBMi) + common.skip(`No /dev/stdin on ${process.platform}.`); + +const assert = require('assert'); +const fs = require('fs/promises'); +const childType = ['child-encoding', 'child-non-encoding']; + +if (process.argv[2] === childType[0]) { + fs.readFile('/dev/stdin', 'utf8').then((data) => { + process.stdout.write(data); + }); + return; +} else if (process.argv[2] === childType[1]) { + fs.readFile('/dev/stdin').then((data) => { + process.stdout.write(data); + }); + return; +} + +const data1 = 'Hello'; +const data2 = 'World'; +const expected = `${data1}\n${data2}\n`; + +const exec = require('child_process').exec; +const f = JSON.stringify(__filename); +const node = JSON.stringify(process.execPath); + +function test(child) { + const cmd = `(echo ${data1}; sleep 0.5; echo ${data2}) | ${node} ${f} ${child}`; + exec(cmd, common.mustSucceed((stdout, stderr) => { + assert.strictEqual( + stdout, + expected, + `expected to read(${child === childType[0] ? 'with' : 'without'} encoding): '${expected}' but got: '${stdout}'`); + assert.strictEqual( + stderr, + '', + `expected not to read anything from stderr but got: '${stderr}'`); + })); +} + +test(childType[0]); +test(childType[1]); diff --git a/test/js/node/test/parallel/test-fs-readfile-fd.js b/test/js/node/test/parallel/test-fs-readfile-fd.js new file mode 100644 index 0000000000..1779d9f97d --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readfile-fd.js @@ -0,0 +1,94 @@ +'use strict'; +const common = require('../common'); + +// Test fs.readFile using a file descriptor. + +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const fs = require('fs'); +const fn = fixtures.path('empty.txt'); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +tempFd(function(fd, close) { + fs.readFile(fd, function(err, data) { + assert.ok(data); + close(); + }); +}); + +tempFd(function(fd, close) { + fs.readFile(fd, 'utf8', function(err, data) { + assert.strictEqual(data, ''); + close(); + }); +}); + +tempFdSync(function(fd) { + assert.ok(fs.readFileSync(fd)); +}); + +tempFdSync(function(fd) { + assert.strictEqual(fs.readFileSync(fd, 'utf8'), ''); +}); + +function tempFd(callback) { + fs.open(fn, 'r', function(err, fd) { + assert.ifError(err); + callback(fd, function() { + fs.close(fd, function(err) { + assert.ifError(err); + }); + }); + }); +} + +function tempFdSync(callback) { + const fd = fs.openSync(fn, 'r'); + callback(fd); + fs.closeSync(fd); +} + +{ + // This test makes sure that `readFile()` always reads from the current + // position of the file, instead of reading from the beginning of the file, + // when used with file descriptors. + + const filename = tmpdir.resolve('test.txt'); + fs.writeFileSync(filename, 'Hello World'); + + { + // Tests the fs.readFileSync(). + const fd = fs.openSync(filename, 'r'); + + // Read only five bytes, so that the position moves to five. + const buf = Buffer.alloc(5); + assert.strictEqual(fs.readSync(fd, buf, 0, 5), 5); + assert.strictEqual(buf.toString(), 'Hello'); + + // readFileSync() should read from position five, instead of zero. + assert.strictEqual(fs.readFileSync(fd).toString(), ' World'); + + fs.closeSync(fd); + } + + { + // Tests the fs.readFile(). + fs.open(filename, 'r', common.mustSucceed((fd) => { + const buf = Buffer.alloc(5); + + // Read only five bytes, so that the position moves to five. + fs.read(fd, buf, 0, 5, null, common.mustSucceed((bytes) => { + assert.strictEqual(bytes, 5); + assert.strictEqual(buf.toString(), 'Hello'); + + fs.readFile(fd, common.mustSucceed((data) => { + // readFile() should read from position five, instead of zero. + assert.strictEqual(data.toString(), ' World'); + + fs.closeSync(fd); + })); + })); + })); + } +} diff --git a/test/js/node/test/parallel/test-fs-readfile-pipe-large.js b/test/js/node/test/parallel/test-fs-readfile-pipe-large.js new file mode 100644 index 0000000000..4376774bb4 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readfile-pipe-large.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); + +// Simulate `cat readfile.js | node readfile.js` + +if (common.isWindows || common.isAIX || common.isIBMi) + common.skip(`No /dev/stdin on ${process.platform}.`); + +const assert = require('assert'); +const fs = require('fs'); + +if (process.argv[2] === 'child') { + fs.readFile('/dev/stdin', function(er, data) { + assert.ifError(er); + process.stdout.write(data); + }); + return; +} + +const tmpdir = require('../common/tmpdir'); + +const filename = tmpdir.resolve('readfile_pipe_large_test.txt'); +const dataExpected = 'a'.repeat(999999); +tmpdir.refresh(); +fs.writeFileSync(filename, dataExpected); + +const exec = require('child_process').exec; +const f = JSON.stringify(__filename); +const node = JSON.stringify(process.execPath); +const cmd = `cat ${filename} | ${node} ${f} child`; +exec(cmd, { maxBuffer: 1000000 }, common.mustSucceed((stdout, stderr) => { + assert.strictEqual( + stdout, + dataExpected, + `expect it reads the file and outputs 999999 'a' but got : ${stdout}` + ); + assert.strictEqual( + stderr, + '', + `expect that it does not write to stderr, but got : ${stderr}` + ); + console.log('ok'); +})); diff --git a/test/js/node/test/parallel/test-fs-readfile-pipe.js b/test/js/node/test/parallel/test-fs-readfile-pipe.js new file mode 100644 index 0000000000..79d5699fef --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readfile-pipe.js @@ -0,0 +1,59 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +// Simulate `cat readfile.js | node readfile.js` + +if (common.isWindows || common.isAIX || common.isIBMi) + common.skip(`No /dev/stdin on ${process.platform}.`); + +const assert = require('assert'); +const fs = require('fs'); + +if (process.argv[2] === 'child') { + fs.readFile('/dev/stdin', common.mustSucceed((data) => { + process.stdout.write(data); + })); + return; +} + +const fixtures = require('../common/fixtures'); + +const filename = fixtures.path('readfile_pipe_test.txt'); +const dataExpected = fs.readFileSync(filename).toString(); + +const exec = require('child_process').exec; +const f = JSON.stringify(__filename); +const node = JSON.stringify(process.execPath); +const cmd = `cat ${filename} | ${node} ${f} child`; +exec(cmd, common.mustSucceed((stdout, stderr) => { + assert.strictEqual( + stdout, + dataExpected, + `expected to read: '${dataExpected}' but got: '${stdout}'`); + assert.strictEqual( + stderr, + '', + `expected not to read anything from stderr but got: '${stderr}'`); + console.log('ok'); +})); diff --git a/test/js/node/test/parallel/test-fs-readfile-unlink.js b/test/js/node/test/parallel/test-fs-readfile-unlink.js new file mode 100644 index 0000000000..1688567fd6 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readfile-unlink.js @@ -0,0 +1,46 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +// Test that unlink succeeds immediately after readFile completes. + +const assert = require('assert'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); + +const fileName = tmpdir.resolve('test.bin'); +const buf = Buffer.alloc(512 * 1024, 42); + +tmpdir.refresh(); + +fs.writeFileSync(fileName, buf); + +fs.readFile(fileName, common.mustSucceed((data) => { + assert.strictEqual(data.length, buf.length); + assert.strictEqual(buf[0], 42); + + // Unlink should not throw. This is part of the test. It used to throw on + // Windows due to a bug. + fs.unlinkSync(fileName); +})); diff --git a/test/js/node/test/parallel/test-fs-readfile-zero-byte-liar.js b/test/js/node/test/parallel/test-fs-readfile-zero-byte-liar.js new file mode 100644 index 0000000000..a3ba3985ab --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readfile-zero-byte-liar.js @@ -0,0 +1,55 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +// Test that readFile works even when stat returns size 0. + +const assert = require('assert'); +const fs = require('fs'); + +const dataExpected = fs.readFileSync(__filename, 'utf8'); + +// Sometimes stat returns size=0, but it's a lie. +fs._fstat = fs.fstat; +fs._fstatSync = fs.fstatSync; + +fs.fstat = (fd, cb) => { + fs._fstat(fd, (er, st) => { + if (er) return cb(er); + st.size = 0; + return cb(er, st); + }); +}; + +fs.fstatSync = (fd) => { + const st = fs._fstatSync(fd); + st.size = 0; + return st; +}; + +const d = fs.readFileSync(__filename, 'utf8'); +assert.strictEqual(d, dataExpected); + +fs.readFile(__filename, 'utf8', common.mustCall((er, d) => { + assert.strictEqual(d, dataExpected); +})); diff --git a/test/js/node/test/parallel/test-fs-readfilesync-enoent.js b/test/js/node/test/parallel/test-fs-readfilesync-enoent.js new file mode 100644 index 0000000000..baf87ff990 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readfilesync-enoent.js @@ -0,0 +1,32 @@ +'use strict'; +const common = require('../common'); + +// This test is only relevant on Windows. +if (!common.isWindows) + common.skip('Windows specific test.'); + +// This test ensures fs.realpathSync works on properly on Windows without +// throwing ENOENT when the path involves a fileserver. +// https://github.com/nodejs/node-v0.x-archive/issues/3542 + +const assert = require('assert'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); + +function test(p) { + const result = fs.realpathSync(p); + assert.strictEqual(result.toLowerCase(), path.resolve(p).toLowerCase()); + + fs.realpath(p, common.mustSucceed((result) => { + assert.strictEqual(result.toLowerCase(), path.resolve(p).toLowerCase()); + })); +} + +test(`//${os.hostname()}/c$/Windows/System32`); +test(`//${os.hostname()}/c$/Windows`); +test(`//${os.hostname()}/c$/`); +test(`\\\\${os.hostname()}\\c$\\`); +test('C:\\'); +test('C:'); +test(process.env.windir); diff --git a/test/js/node/test/parallel/test-fs-readfilesync-pipe-large.js b/test/js/node/test/parallel/test-fs-readfilesync-pipe-large.js new file mode 100644 index 0000000000..5450337c4f --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readfilesync-pipe-large.js @@ -0,0 +1,36 @@ +'use strict'; +const common = require('../common'); + +// Simulate `cat readfile.js | node readfile.js` + +if (common.isWindows || common.isAIX || common.isIBMi) + common.skip(`No /dev/stdin on ${process.platform}.`); + +const assert = require('assert'); +const fs = require('fs'); + +if (process.argv[2] === 'child') { + process.stdout.write(fs.readFileSync('/dev/stdin', 'utf8')); + return; +} + +const tmpdir = require('../common/tmpdir'); + +const filename = tmpdir.resolve('readfilesync_pipe_large_test.txt'); +const dataExpected = 'a'.repeat(999999); +tmpdir.refresh(); +fs.writeFileSync(filename, dataExpected); + +const exec = require('child_process').exec; +const f = JSON.stringify(__filename); +const node = JSON.stringify(process.execPath); +const cmd = `cat ${filename} | ${node} ${f} child`; +exec( + cmd, + { maxBuffer: 1000000 }, + common.mustSucceed((stdout, stderr) => { + assert.strictEqual(stdout, dataExpected); + assert.strictEqual(stderr, ''); + console.log('ok'); + }) +); diff --git a/test/js/node/test/parallel/test-fs-readlink-type-check.js b/test/js/node/test/parallel/test-fs-readlink-type-check.js new file mode 100644 index 0000000000..58d431308c --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readlink-type-check.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +[false, 1, {}, [], null, undefined].forEach((i) => { + assert.throws( + () => fs.readlink(i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.readlinkSync(i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/test/js/node/test/parallel/test-fs-readv-promises.js b/test/js/node/test/parallel/test-fs-readv-promises.js new file mode 100644 index 0000000000..cdfc3d3b21 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readv-promises.js @@ -0,0 +1,63 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs').promises; +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +const expected = 'ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف'; +const exptectedBuff = Buffer.from(expected); + +let cnt = 0; +function getFileName() { + return tmpdir.resolve(`readv_promises_${++cnt}.txt`); +} + +const allocateEmptyBuffers = (combinedLength) => { + const bufferArr = []; + // Allocate two buffers, each half the size of exptectedBuff + bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)); + bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length); + + return bufferArr; +}; + +(async () => { + { + const filename = getFileName(); + await fs.writeFile(filename, exptectedBuff); + const handle = await fs.open(filename, 'r'); + const bufferArr = allocateEmptyBuffers(exptectedBuff.length); + const expectedLength = exptectedBuff.length; + + let { bytesRead, buffers } = await handle.readv([Buffer.from('')], + null); + assert.strictEqual(bytesRead, 0); + assert.deepStrictEqual(buffers, [Buffer.from('')]); + + ({ bytesRead, buffers } = await handle.readv(bufferArr, null)); + assert.strictEqual(bytesRead, expectedLength); + assert.deepStrictEqual(buffers, bufferArr); + assert(Buffer.concat(bufferArr).equals(await fs.readFile(filename))); + handle.close(); + } + + { + const filename = getFileName(); + await fs.writeFile(filename, exptectedBuff); + const handle = await fs.open(filename, 'r'); + const bufferArr = allocateEmptyBuffers(exptectedBuff.length); + const expectedLength = exptectedBuff.length; + + let { bytesRead, buffers } = await handle.readv([Buffer.from('')]); + assert.strictEqual(bytesRead, 0); + assert.deepStrictEqual(buffers, [Buffer.from('')]); + + ({ bytesRead, buffers } = await handle.readv(bufferArr)); + assert.strictEqual(bytesRead, expectedLength); + assert.deepStrictEqual(buffers, bufferArr); + assert(Buffer.concat(bufferArr).equals(await fs.readFile(filename))); + handle.close(); + } +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-readv-sync.js b/test/js/node/test/parallel/test-fs-readv-sync.js new file mode 100644 index 0000000000..548f54cbb9 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readv-sync.js @@ -0,0 +1,92 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +const expected = 'ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف'; + +const exptectedBuff = Buffer.from(expected); +const expectedLength = exptectedBuff.length; + +const filename = tmpdir.resolve('readv_sync.txt'); +fs.writeFileSync(filename, exptectedBuff); + +const allocateEmptyBuffers = (combinedLength) => { + const bufferArr = []; + // Allocate two buffers, each half the size of exptectedBuff + bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)); + bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length); + + return bufferArr; +}; + +// fs.readvSync with array of buffers with all parameters +{ + const fd = fs.openSync(filename, 'r'); + + const bufferArr = allocateEmptyBuffers(exptectedBuff.length); + + let read = fs.readvSync(fd, [Buffer.from('')], 0); + assert.strictEqual(read, 0); + + read = fs.readvSync(fd, bufferArr, 0); + assert.strictEqual(read, expectedLength); + + fs.closeSync(fd); + + assert(Buffer.concat(bufferArr).equals(fs.readFileSync(filename))); +} + +// fs.readvSync with array of buffers without position +{ + const fd = fs.openSync(filename, 'r'); + + const bufferArr = allocateEmptyBuffers(exptectedBuff.length); + + let read = fs.readvSync(fd, [Buffer.from('')]); + assert.strictEqual(read, 0); + + read = fs.readvSync(fd, bufferArr); + assert.strictEqual(read, expectedLength); + + fs.closeSync(fd); + + assert(Buffer.concat(bufferArr).equals(fs.readFileSync(filename))); +} + +/** + * Testing with incorrect arguments + */ +const wrongInputs = [false, 'test', {}, [{}], ['sdf'], null, undefined]; + +{ + const fd = fs.openSync(filename, 'r'); + + for (const wrongInput of wrongInputs) { + assert.throws( + () => fs.readvSync(fd, wrongInput, null), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + } + + fs.closeSync(fd); +} + +{ + // fs.readv with wrong fd argument + for (const wrongInput of wrongInputs) { + assert.throws( + () => fs.readvSync(wrongInput), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + } +} diff --git a/test/js/node/test/parallel/test-fs-readv.js b/test/js/node/test/parallel/test-fs-readv.js new file mode 100644 index 0000000000..111719a7ef --- /dev/null +++ b/test/js/node/test/parallel/test-fs-readv.js @@ -0,0 +1,93 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +const expected = 'ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف'; + +let cnt = 0; +const getFileName = () => tmpdir.resolve(`readv_${++cnt}.txt`); +const exptectedBuff = Buffer.from(expected); + +const allocateEmptyBuffers = (combinedLength) => { + const bufferArr = []; + // Allocate two buffers, each half the size of exptectedBuff + bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)); + bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length); + + return bufferArr; +}; + +const getCallback = (fd, bufferArr) => { + return common.mustSucceed((bytesRead, buffers) => { + assert.deepStrictEqual(bufferArr, buffers); + const expectedLength = exptectedBuff.length; + assert.deepStrictEqual(bytesRead, expectedLength); + fs.closeSync(fd); + + assert(Buffer.concat(bufferArr).equals(exptectedBuff)); + }); +}; + +// fs.readv with array of buffers with all parameters +{ + const filename = getFileName(); + const fd = fs.openSync(filename, 'w+'); + fs.writeSync(fd, exptectedBuff); + + const bufferArr = allocateEmptyBuffers(exptectedBuff.length); + const callback = getCallback(fd, bufferArr); + + fs.readv(fd, bufferArr, 0, callback); +} + +// fs.readv with array of buffers without position +{ + const filename = getFileName(); + fs.writeFileSync(filename, exptectedBuff); + const fd = fs.openSync(filename, 'r'); + + const bufferArr = allocateEmptyBuffers(exptectedBuff.length); + const callback = getCallback(fd, bufferArr); + + fs.readv(fd, bufferArr, callback); +} + +/** + * Testing with incorrect arguments + */ +const wrongInputs = [false, 'test', {}, [{}], ['sdf'], null, undefined]; + +{ + const filename = getFileName(2); + fs.writeFileSync(filename, exptectedBuff); + const fd = fs.openSync(filename, 'r'); + + for (const wrongInput of wrongInputs) { + assert.throws( + () => fs.readv(fd, wrongInput, null, common.mustNotCall()), { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + } + + fs.closeSync(fd); +} + +{ + // fs.readv with wrong fd argument + for (const wrongInput of wrongInputs) { + assert.throws( + () => fs.readv(wrongInput, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + } +} diff --git a/test/js/node/test/parallel/test-fs-realpath-on-substed-drive.js b/test/js/node/test/parallel/test-fs-realpath-on-substed-drive.js new file mode 100644 index 0000000000..aea53f642f --- /dev/null +++ b/test/js/node/test/parallel/test-fs-realpath-on-substed-drive.js @@ -0,0 +1,51 @@ +'use strict'; + +const common = require('../common'); +if (!common.isWindows) + common.skip('Test for Windows only'); + +const fixtures = require('../common/fixtures'); + +const assert = require('assert'); +const fs = require('fs'); +const spawnSync = require('child_process').spawnSync; + +let result; + +// Create a subst drive +const driveLetters = 'ABCDEFGHIJKLMNOPQRSTUWXYZ'; +let drive; +let i; +for (i = 0; i < driveLetters.length; ++i) { + drive = `${driveLetters[i]}:`; + result = spawnSync('subst', [drive, fixtures.fixturesDir]); + if (result.status === 0) + break; +} +if (i === driveLetters.length) + common.skip('Cannot create subst drive'); + +// Schedule cleanup (and check if all callbacks where called) +process.on('exit', function() { + spawnSync('subst', ['/d', drive]); +}); + +// test: +const filename = `${drive}\\empty.js`; +const filenameBuffer = Buffer.from(filename); + +result = fs.realpathSync(filename); +assert.strictEqual(result, filename); + +result = fs.realpathSync(filename, 'buffer'); +assert(Buffer.isBuffer(result)); +assert(result.equals(filenameBuffer)); + +fs.realpath(filename, common.mustSucceed((result) => { + assert.strictEqual(result, filename); +})); + +fs.realpath(filename, 'buffer', common.mustSucceed((result) => { + assert(Buffer.isBuffer(result)); + assert(result.equals(filenameBuffer)); +})); diff --git a/test/js/node/test/parallel/test-fs-realpath-pipe.js b/test/js/node/test/parallel/test-fs-realpath-pipe.js new file mode 100644 index 0000000000..f637642ca2 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-realpath-pipe.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); + +if (common.isWindows || common.isAIX || common.isIBMi) + common.skip(`No /dev/stdin on ${process.platform}.`); + +const assert = require('assert'); + +const { spawnSync } = require('child_process'); + +for (const code of [ + `require('fs').realpath('/dev/stdin', (err, resolvedPath) => { + if (err) { + console.error(err); + process.exit(1); + } + if (resolvedPath) { + process.exit(2); + } + });`, + `try { + if (require('fs').realpathSync('/dev/stdin')) { + process.exit(2); + } + } catch (e) { + console.error(e); + process.exit(1); + }`, +]) { + const child = spawnSync(process.execPath, ['-e', code], { + stdio: 'pipe' + }); + if (child.status !== 2) { + console.log(code); + console.log(child.stderr.toString()); + } + assert.strictEqual(child.status, 2); +} diff --git a/test/js/node/test/parallel/test-fs-rmdir-type-check.js b/test/js/node/test/parallel/test-fs-rmdir-type-check.js new file mode 100644 index 0000000000..7014ce27f8 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-rmdir-type-check.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +[false, 1, [], {}, null, undefined].forEach((i) => { + assert.throws( + () => fs.rmdir(i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.rmdirSync(i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/test/js/node/test/parallel/test-fs-sir-writes-alot.js b/test/js/node/test/parallel/test-fs-sir-writes-alot.js new file mode 100644 index 0000000000..7d213d6c6f --- /dev/null +++ b/test/js/node/test/parallel/test-fs-sir-writes-alot.js @@ -0,0 +1,70 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const fs = require('fs'); +const assert = require('assert'); + +const tmpdir = require('../common/tmpdir'); + +const filename = tmpdir.resolve('out.txt'); + +tmpdir.refresh(); + +const fd = fs.openSync(filename, 'w'); + +const line = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaa\n'; + +const N = 10240; +let complete = 0; + +for (let i = 0; i < N; i++) { + // Create a new buffer for each write. Before the write is actually + // executed by the thread pool, the buffer will be collected. + const buffer = Buffer.from(line); + fs.write(fd, buffer, 0, buffer.length, null, function(er, written) { + complete++; + if (complete === N) { + fs.closeSync(fd); + const s = fs.createReadStream(filename); + s.on('data', testBuffer); + } + }); +} + +let bytesChecked = 0; + +function testBuffer(b) { + for (let i = 0; i < b.length; i++) { + bytesChecked++; + if (b[i] !== 'a'.charCodeAt(0) && b[i] !== '\n'.charCodeAt(0)) { + throw new Error(`invalid char ${i},${b[i]}`); + } + } +} + +process.on('exit', function() { + // Probably some of the writes are going to overlap, so we can't assume + // that we get (N * line.length). Let's just make sure we've checked a + // few... + assert.ok(bytesChecked > 1000); +}); diff --git a/test/js/node/test/parallel/test-fs-stat-bigint.js b/test/js/node/test/parallel/test-fs-stat-bigint.js new file mode 100644 index 0000000000..0a2bea92e5 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-stat-bigint.js @@ -0,0 +1,247 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const promiseFs = require('fs').promises; +const tmpdir = require('../common/tmpdir'); +const { isDate } = require('util').types; +const { inspect } = require('util'); + +tmpdir.refresh(); + +let testIndex = 0; + +function getFilename() { + const filename = tmpdir.resolve(`test-file-${++testIndex}`); + fs.writeFileSync(filename, 'test'); + return filename; +} + +function verifyStats(bigintStats, numStats, allowableDelta) { + // allowableDelta: It's possible that the file stats are updated between the + // two stat() calls so allow for a small difference. + for (const key of Object.keys(numStats)) { + const val = numStats[key]; + if (isDate(val)) { + const time = val.getTime(); + const time2 = bigintStats[key].getTime(); + assert( + time - time2 <= allowableDelta, + `difference of ${key}.getTime() should <= ${allowableDelta}.\n` + + `Number version ${time}, BigInt version ${time2}n`); + } else if (key === 'mode') { + assert.strictEqual(bigintStats[key], BigInt(val)); + assert.strictEqual( + bigintStats.isBlockDevice(), + numStats.isBlockDevice() + ); + assert.strictEqual( + bigintStats.isCharacterDevice(), + numStats.isCharacterDevice() + ); + assert.strictEqual( + bigintStats.isDirectory(), + numStats.isDirectory() + ); + assert.strictEqual( + bigintStats.isFIFO(), + numStats.isFIFO() + ); + assert.strictEqual( + bigintStats.isFile(), + numStats.isFile() + ); + assert.strictEqual( + bigintStats.isSocket(), + numStats.isSocket() + ); + assert.strictEqual( + bigintStats.isSymbolicLink(), + numStats.isSymbolicLink() + ); + } else if (key.endsWith('Ms')) { + const nsKey = key.replace('Ms', 'Ns'); + const msFromBigInt = bigintStats[key]; + const nsFromBigInt = bigintStats[nsKey]; + const msFromBigIntNs = Number(nsFromBigInt / (10n ** 6n)); + const msFromNum = numStats[key]; + + assert( + msFromNum - Number(msFromBigInt) <= allowableDelta, + `Number version ${key} = ${msFromNum}, ` + + `BigInt version ${key} = ${msFromBigInt}n, ` + + `Allowable delta = ${allowableDelta}`); + + assert( + msFromNum - Number(msFromBigIntNs) <= allowableDelta, + `Number version ${key} = ${msFromNum}, ` + + `BigInt version ${nsKey} = ${nsFromBigInt}n` + + ` = ${msFromBigIntNs}ms, Allowable delta = ${allowableDelta}`); + } else if (Number.isSafeInteger(val)) { + assert.strictEqual( + bigintStats[key], BigInt(val), + `${inspect(bigintStats[key])} !== ${inspect(BigInt(val))}\n` + + `key=${key}, val=${val}` + ); + } else { + assert( + Number(bigintStats[key]) - val < 1, + `${key} is not a safe integer, difference should < 1.\n` + + `Number version ${val}, BigInt version ${bigintStats[key]}n`); + } + } +} + +const runSyncTest = (func, arg) => { + const startTime = process.hrtime.bigint(); + const bigintStats = func(arg, common.mustNotMutateObjectDeep({ bigint: true })); + const numStats = func(arg); + const endTime = process.hrtime.bigint(); + const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6); + verifyStats(bigintStats, numStats, allowableDelta); +}; + +{ + const filename = getFilename(); + runSyncTest(fs.statSync, filename); +} + +if (!common.isWindows) { + const filename = getFilename(); + const link = `${filename}-link`; + fs.symlinkSync(filename, link); + runSyncTest(fs.lstatSync, link); +} + +{ + const filename = getFilename(); + const fd = fs.openSync(filename, 'r'); + runSyncTest(fs.fstatSync, fd); + fs.closeSync(fd); +} + +{ + assert.throws( + () => fs.statSync('does_not_exist'), + { code: 'ENOENT' }); + assert.strictEqual( + fs.statSync('does_not_exist', common.mustNotMutateObjectDeep({ throwIfNoEntry: false })), + undefined); +} + +{ + assert.throws( + () => fs.lstatSync('does_not_exist'), + { code: 'ENOENT' }); + assert.strictEqual( + fs.lstatSync('does_not_exist', common.mustNotMutateObjectDeep({ throwIfNoEntry: false })), + undefined); +} + +{ + assert.throws( + () => fs.fstatSync(9999), + { code: 'EBADF' }); + assert.throws( + () => fs.fstatSync(9999, common.mustNotMutateObjectDeep({ throwIfNoEntry: false })), + { code: 'EBADF' }); +} + +const runCallbackTest = (func, arg, done) => { + const startTime = process.hrtime.bigint(); + func(arg, common.mustNotMutateObjectDeep({ bigint: true }), common.mustCall((err, bigintStats) => { + func(arg, common.mustCall((err, numStats) => { + const endTime = process.hrtime.bigint(); + const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6); + verifyStats(bigintStats, numStats, allowableDelta); + if (done) { + done(); + } + })); + })); +}; + +{ + const filename = getFilename(); + runCallbackTest(fs.stat, filename); +} + +if (!common.isWindows) { + const filename = getFilename(); + const link = `${filename}-link`; + fs.symlinkSync(filename, link); + runCallbackTest(fs.lstat, link); +} + +{ + const filename = getFilename(); + const fd = fs.openSync(filename, 'r'); + runCallbackTest(fs.fstat, fd, () => { fs.closeSync(fd); }); +} + +const runPromiseTest = async (func, arg) => { + const startTime = process.hrtime.bigint(); + const bigintStats = await func(arg, common.mustNotMutateObjectDeep({ bigint: true })); + const numStats = await func(arg); + const endTime = process.hrtime.bigint(); + const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6); + verifyStats(bigintStats, numStats, allowableDelta); +}; + +{ + const filename = getFilename(); + runPromiseTest(promiseFs.stat, filename); +} + +if (!common.isWindows) { + const filename = getFilename(); + const link = `${filename}-link`; + fs.symlinkSync(filename, link); + runPromiseTest(promiseFs.lstat, link); +} + +(async function() { + const filename = getFilename(); + const handle = await promiseFs.open(filename, 'r'); + const startTime = process.hrtime.bigint(); + const bigintStats = await handle.stat(common.mustNotMutateObjectDeep({ bigint: true })); + const numStats = await handle.stat(); + const endTime = process.hrtime.bigint(); + const allowableDelta = Math.ceil(Number(endTime - startTime) / 1e6); + verifyStats(bigintStats, numStats, allowableDelta); + await handle.close(); +})().then(common.mustCall()); + +{ + // These two tests have an equivalent in ./test-fs-stat.js + + // BigIntStats Date properties can be set before reading them + fs.stat(__filename, { bigint: true }, common.mustSucceed((s) => { + s.atime = 2; + s.mtime = 3; + s.ctime = 4; + s.birthtime = 5; + + assert.strictEqual(s.atime, 2); + assert.strictEqual(s.mtime, 3); + assert.strictEqual(s.ctime, 4); + assert.strictEqual(s.birthtime, 5); + })); + + // BigIntStats Date properties can be set after reading them + fs.stat(__filename, { bigint: true }, common.mustSucceed((s) => { + // eslint-disable-next-line no-unused-expressions + s.atime, s.mtime, s.ctime, s.birthtime; + + s.atime = 2; + s.mtime = 3; + s.ctime = 4; + s.birthtime = 5; + + assert.strictEqual(s.atime, 2); + assert.strictEqual(s.mtime, 3); + assert.strictEqual(s.ctime, 4); + assert.strictEqual(s.birthtime, 5); + })); +} diff --git a/test/js/node/test/parallel/test-fs-stream-double-close.js b/test/js/node/test/parallel/test-fs-stream-double-close.js new file mode 100644 index 0000000000..8c0037b243 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-stream-double-close.js @@ -0,0 +1,54 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +test1(fs.createReadStream(__filename)); +test2(fs.createReadStream(__filename)); +test3(fs.createReadStream(__filename)); + +test1(fs.createWriteStream(`${tmpdir.path}/dummy1`)); +test2(fs.createWriteStream(`${tmpdir.path}/dummy2`)); +test3(fs.createWriteStream(`${tmpdir.path}/dummy3`)); + +function test1(stream) { + stream.destroy(); + stream.destroy(); +} + +function test2(stream) { + stream.destroy(); + stream.on('open', common.mustCall(function(fd) { + stream.destroy(); + })); +} + +function test3(stream) { + stream.on('open', common.mustCall(function(fd) { + stream.destroy(); + stream.destroy(); + })); +} diff --git a/test/js/node/test/parallel/test-fs-symlink-buffer-path.js b/test/js/node/test/parallel/test-fs-symlink-buffer-path.js new file mode 100644 index 0000000000..ecad001d5e --- /dev/null +++ b/test/js/node/test/parallel/test-fs-symlink-buffer-path.js @@ -0,0 +1,59 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.canCreateSymLink()) + common.skip('insufficient privileges'); + +const fixtures = require('../common/fixtures'); + +const assert = require('assert'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Test creating and reading symbolic link +const linkData = fixtures.path('/cycles/root.js'); +const linkPath = tmpdir.resolve('symlink1.js'); + +let linkTime; +let fileTime; + +// Refs: https://github.com/nodejs/node/issues/34514 +fs.symlinkSync(Buffer.from(linkData), linkPath); + +fs.lstat(linkPath, common.mustSucceed((stats) => { + linkTime = stats.mtime.getTime(); +})); + +fs.stat(linkPath, common.mustSucceed((stats) => { + fileTime = stats.mtime.getTime(); +})); + +fs.readlink(linkPath, common.mustSucceed((destination) => { + assert.strictEqual(destination, linkData); +})); + +process.on('exit', () => { + assert.notStrictEqual(linkTime, fileTime); +}); diff --git a/test/js/node/test/parallel/test-fs-symlink-dir-junction-relative.js b/test/js/node/test/parallel/test-fs-symlink-dir-junction-relative.js new file mode 100644 index 0000000000..01ef8c8bdc --- /dev/null +++ b/test/js/node/test/parallel/test-fs-symlink-dir-junction-relative.js @@ -0,0 +1,58 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Test creating and resolving relative junction or symbolic link + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); + +const linkPath1 = tmpdir.resolve('junction1'); +const linkPath2 = tmpdir.resolve('junction2'); +const linkTarget = fixtures.fixturesDir; +const linkData = fixtures.fixturesDir; + +tmpdir.refresh(); + +// Test fs.symlink() +fs.symlink(linkData, linkPath1, 'junction', common.mustSucceed(() => { + verifyLink(linkPath1); +})); + +// Test fs.symlinkSync() +fs.symlinkSync(linkData, linkPath2, 'junction'); +verifyLink(linkPath2); + +function verifyLink(linkPath) { + const stats = fs.lstatSync(linkPath); + assert.ok(stats.isSymbolicLink()); + + const data1 = fs.readFileSync(`${linkPath}/x.txt`, 'ascii'); + const data2 = fs.readFileSync(`${linkTarget}/x.txt`, 'ascii'); + assert.strictEqual(data1, data2); + + // Clean up. + fs.unlinkSync(linkPath); +} diff --git a/test/js/node/test/parallel/test-fs-symlink-dir-junction.js b/test/js/node/test/parallel/test-fs-symlink-dir-junction.js new file mode 100644 index 0000000000..3990467c6f --- /dev/null +++ b/test/js/node/test/parallel/test-fs-symlink-dir-junction.js @@ -0,0 +1,63 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); + +// Test creating and reading symbolic link +const linkData = fixtures.path('cycles/'); +const linkPath = tmpdir.resolve('cycles_link'); + +tmpdir.refresh(); + +fs.symlink(linkData, linkPath, 'junction', common.mustSucceed(() => { + fs.lstat(linkPath, common.mustSucceed((stats) => { + assert.ok(stats.isSymbolicLink()); + + fs.readlink(linkPath, common.mustSucceed((destination) => { + assert.strictEqual(destination, linkData); + + fs.unlink(linkPath, common.mustSucceed(() => { + assert(!fs.existsSync(linkPath)); + assert(fs.existsSync(linkData)); + })); + })); + })); +})); + +// Test invalid symlink +{ + const linkData = fixtures.path('/not/exists/dir'); + const linkPath = tmpdir.resolve('invalid_junction_link'); + + fs.symlink(linkData, linkPath, 'junction', common.mustSucceed(() => { + assert(!fs.existsSync(linkPath)); + + fs.unlink(linkPath, common.mustSucceed(() => { + assert(!fs.existsSync(linkPath)); + })); + })); +} diff --git a/test/js/node/test/parallel/test-fs-symlink-dir.js b/test/js/node/test/parallel/test-fs-symlink-dir.js new file mode 100644 index 0000000000..690e3302ed --- /dev/null +++ b/test/js/node/test/parallel/test-fs-symlink-dir.js @@ -0,0 +1,81 @@ +'use strict'; +const common = require('../common'); + +// Test creating a symbolic link pointing to a directory. +// Ref: https://github.com/nodejs/node/pull/23724 +// Ref: https://github.com/nodejs/node/issues/23596 + + +if (!common.canCreateSymLink()) + common.skip('insufficient privileges'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); +const fsPromises = fs.promises; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const linkTargets = [ + 'relative-target', + tmpdir.resolve('absolute-target'), +]; +const linkPaths = [ + path.relative(process.cwd(), tmpdir.resolve('relative-path')), + tmpdir.resolve('absolute-path'), +]; + +function testSync(target, path) { + fs.symlinkSync(target, path); + fs.readdirSync(path); +} + +function testAsync(target, path) { + fs.symlink(target, path, common.mustSucceed(() => { + fs.readdirSync(path); + })); +} + +async function testPromises(target, path) { + await fsPromises.symlink(target, path); + fs.readdirSync(path); +} + +for (const linkTarget of linkTargets) { + fs.mkdirSync(tmpdir.resolve(linkTarget)); + for (const linkPath of linkPaths) { + testSync(linkTarget, `${linkPath}-${path.basename(linkTarget)}-sync`); + testAsync(linkTarget, `${linkPath}-${path.basename(linkTarget)}-async`); + testPromises(linkTarget, `${linkPath}-${path.basename(linkTarget)}-promises`) + .then(common.mustCall()); + } +} + +// Test invalid symlink +{ + function testSync(target, path) { + fs.symlinkSync(target, path); + assert(!fs.existsSync(path)); + } + + function testAsync(target, path) { + fs.symlink(target, path, common.mustSucceed(() => { + assert(!fs.existsSync(path)); + })); + } + + async function testPromises(target, path) { + await fsPromises.symlink(target, path); + assert(!fs.existsSync(path)); + } + + for (const linkTarget of linkTargets.map((p) => p + '-broken')) { + for (const linkPath of linkPaths) { + testSync(linkTarget, `${linkPath}-${path.basename(linkTarget)}-sync`); + testAsync(linkTarget, `${linkPath}-${path.basename(linkTarget)}-async`); + testPromises(linkTarget, `${linkPath}-${path.basename(linkTarget)}-promises`) + .then(common.mustCall()); + } + } +} diff --git a/test/js/node/test/parallel/test-fs-symlink-longpath.js b/test/js/node/test/parallel/test-fs-symlink-longpath.js new file mode 100644 index 0000000000..f3586317c2 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-symlink-longpath.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +const tmpDir = tmpdir.path; +const longPath = path.join(...[tmpDir].concat(Array(30).fill('1234567890'))); +fs.mkdirSync(longPath, { recursive: true }); + +// Test if we can have symlinks to files and folders with long filenames +const targetDirectory = path.join(longPath, 'target-directory'); +fs.mkdirSync(targetDirectory); +const pathDirectory = path.join(tmpDir, 'new-directory'); +fs.symlink(targetDirectory, pathDirectory, 'dir', common.mustSucceed(() => { + assert(fs.existsSync(pathDirectory)); +})); + +const targetFile = path.join(longPath, 'target-file'); +fs.writeFileSync(targetFile, 'data'); +const pathFile = path.join(tmpDir, 'new-file'); +fs.symlink(targetFile, pathFile, common.mustSucceed(() => { + assert(fs.existsSync(pathFile)); +})); diff --git a/test/js/node/test/parallel/test-fs-truncate-clear-file-zero.js b/test/js/node/test/parallel/test-fs-truncate-clear-file-zero.js new file mode 100644 index 0000000000..234e65e580 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-truncate-clear-file-zero.js @@ -0,0 +1,56 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); + +// This test ensures that `fs.truncate` opens the file with `r+` and not `w`, +// which had earlier resulted in the target file's content getting zeroed out. +// https://github.com/nodejs/node-v0.x-archive/issues/6233 + +const assert = require('assert'); +const fs = require('fs'); + +const filename = `${tmpdir.path}/truncate-file.txt`; + +tmpdir.refresh(); + +// Synchronous test. +{ + fs.writeFileSync(filename, '0123456789'); + assert.strictEqual(fs.readFileSync(filename).toString(), '0123456789'); + fs.truncateSync(filename, 5); + assert.strictEqual(fs.readFileSync(filename).toString(), '01234'); +} + +// Asynchronous test. +{ + fs.writeFileSync(filename, '0123456789'); + assert.strictEqual(fs.readFileSync(filename).toString(), '0123456789'); + fs.truncate( + filename, + 5, + common.mustSucceed(() => { + assert.strictEqual(fs.readFileSync(filename).toString(), '01234'); + }) + ); +} diff --git a/test/js/node/test/parallel/test-fs-truncate-sync.js b/test/js/node/test/parallel/test-fs-truncate-sync.js new file mode 100644 index 0000000000..66250cf438 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-truncate-sync.js @@ -0,0 +1,21 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); +const tmp = tmpdir.path; + +tmpdir.refresh(); + +const filename = path.resolve(tmp, 'truncate-sync-file.txt'); + +fs.writeFileSync(filename, 'hello world', 'utf8'); + +const fd = fs.openSync(filename, 'r+'); + +fs.truncateSync(fd, 5); +assert(fs.readFileSync(fd).equals(Buffer.from('hello'))); + +fs.closeSync(fd); +fs.unlinkSync(filename); diff --git a/test/js/node/test/parallel/test-fs-unlink-type-check.js b/test/js/node/test/parallel/test-fs-unlink-type-check.js new file mode 100644 index 0000000000..006e9ad734 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-unlink-type-check.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +[false, 1, {}, [], null, undefined].forEach((i) => { + assert.throws( + () => fs.unlink(i, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); + assert.throws( + () => fs.unlinkSync(i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/test/js/node/test/parallel/test-fs-utimes-y2K38.js b/test/js/node/test/parallel/test-fs-utimes-y2K38.js new file mode 100644 index 0000000000..9e42e90feb --- /dev/null +++ b/test/js/node/test/parallel/test-fs-utimes-y2K38.js @@ -0,0 +1,66 @@ +'use strict'; +const common = require('../common'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const assert = require('assert'); +const fs = require('fs'); + +// Check for Y2K38 support. For Windows, assume it's there. Windows +// doesn't have `touch` and `date -r` which are used in the check for support. +if (!common.isWindows) { + const testFilePath = `${tmpdir.path}/y2k38-test`; + const testFileDate = '204001020304'; + const { spawnSync } = require('child_process'); + const touchResult = spawnSync('touch', + ['-t', testFileDate, testFilePath], + { encoding: 'utf8' }); + if (touchResult.status !== 0) { + common.skip('File system appears to lack Y2K38 support (touch failed)'); + } + + // On some file systems that lack Y2K38 support, `touch` will succeed but + // the time will be incorrect. + const dateResult = spawnSync('date', + ['-r', testFilePath, '+%Y%m%d%H%M'], + { encoding: 'utf8' }); + if (dateResult.status === 0) { + if (dateResult.stdout.trim() !== testFileDate) { + common.skip('File system appears to lack Y2k38 support (date failed)'); + } + } else { + // On some platforms `date` may not support the `-r` option. Usually + // this will result in a non-zero status and usage information printed. + // In this case optimistically proceed -- the earlier `touch` succeeded + // but validation that the file has the correct time is not easily possible. + assert.match(dateResult.stderr, /[Uu]sage:/); + } +} + +// Ref: https://github.com/nodejs/node/issues/13255 +const path = `${tmpdir.path}/test-utimes-precision`; +fs.writeFileSync(path, ''); + +const Y2K38_mtime = 2 ** 31; +fs.utimesSync(path, Y2K38_mtime, Y2K38_mtime); +const Y2K38_stats = fs.statSync(path); +assert.strictEqual(Y2K38_stats.mtime.getTime() / 1000, Y2K38_mtime); + +if (common.isWindows) { + // This value would get converted to (double)1713037251359.9998 + const truncate_mtime = 1713037251360; + fs.utimesSync(path, truncate_mtime / 1000, truncate_mtime / 1000); + const truncate_stats = fs.statSync(path); + assert.strictEqual(truncate_stats.mtime.getTime(), truncate_mtime); + + // test Y2K38 for windows + // This value if treaded as a `signed long` gets converted to -2135622133469. + // POSIX systems stores timestamps in {long t_sec, long t_usec}. + // NTFS stores times in nanoseconds in a single `uint64_t`, so when libuv + // calculates (long)`uv_timespec_t.tv_sec` we get 2's complement. + const overflow_mtime = 2159345162531; + fs.utimesSync(path, overflow_mtime / 1000, overflow_mtime / 1000); + const overflow_stats = fs.statSync(path); + assert.strictEqual(overflow_stats.mtime.getTime(), overflow_mtime); +} diff --git a/test/js/node/test/parallel/test-fs-watch-abort-signal.js b/test/js/node/test/parallel/test-fs-watch-abort-signal.js new file mode 100644 index 0000000000..33936d2d49 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-abort-signal.js @@ -0,0 +1,30 @@ +// Flags: --expose-internals +'use strict'; + +// Verify that AbortSignal integration works for fs.watch + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + + +{ + // Signal aborted after creating the watcher + const file = fixtures.path('empty.js'); + const ac = new AbortController(); + const { signal } = ac; + const watcher = fs.watch(file, { signal }); + watcher.once('close', common.mustCall()); + setImmediate(() => ac.abort()); +} +{ + // Signal aborted before creating the watcher + const file = fixtures.path('empty.js'); + const signal = AbortSignal.abort(); + const watcher = fs.watch(file, { signal }); + watcher.once('close', common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-fs-watch-close-when-destroyed.js b/test/js/node/test/parallel/test-fs-watch-close-when-destroyed.js new file mode 100644 index 0000000000..afa5307e4d --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-close-when-destroyed.js @@ -0,0 +1,48 @@ +'use strict'; + +// This tests that closing a watcher when the underlying handle is +// already destroyed will result in a noop instead of a crash. + +const common = require('../common'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +const tmpdir = require('../common/tmpdir'); +const fs = require('fs'); + +tmpdir.refresh(); +const root = tmpdir.resolve('watched-directory'); +fs.mkdirSync(root); + +const watcher = fs.watch(root, { persistent: false, recursive: false }); + +// The following listeners may or may not be invoked. + +watcher.addListener('error', () => { + setTimeout( + () => { watcher.close(); }, // Should not crash if it's invoked + common.platformTimeout(10) + ); +}); + +watcher.addListener('change', () => { + setTimeout( + () => { watcher.close(); }, + common.platformTimeout(10) + ); +}); + +fs.rmdirSync(root); +// Wait for the listener to hit +setTimeout( + common.mustCall(), + common.platformTimeout(100) +); diff --git a/test/js/node/test/parallel/test-fs-watch-encoding.js b/test/js/node/test/parallel/test-fs-watch-encoding.js new file mode 100644 index 0000000000..758c6c26be --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-encoding.js @@ -0,0 +1,95 @@ +'use strict'; + +// This test is a bit more complicated than it ideally needs to be to work +// around issues on OS X and SmartOS. +// +// On OS X, watch events are subject to peculiar timing oddities such that an +// event might fire out of order. The synchronous refreshing of the tmp +// directory might trigger an event on the watchers that are instantiated after +// it! +// +// On SmartOS, the watch events fire but the filename is null. + +const common = require('../common'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const fn = '新建文夹件.txt'; +const a = tmpdir.resolve(fn); + +const watchers = new Set(); + +function registerWatcher(watcher) { + watchers.add(watcher); +} + +function unregisterWatcher(watcher) { + watcher.close(); + watchers.delete(watcher); + if (watchers.size === 0) { + clearInterval(interval); + } +} + +{ + // Test that using the `encoding` option has the expected result. + const watcher = fs.watch( + tmpdir.path, + { encoding: 'hex' }, + (event, filename) => { + if (['e696b0e5bbbae69687e5a4b9e4bbb62e747874', null].includes(filename)) + done(watcher); + } + ); + registerWatcher(watcher); +} + +{ + // Test that in absence of `encoding` option has the expected result. + const watcher = fs.watch( + tmpdir.path, + (event, filename) => { + if ([fn, null].includes(filename)) + done(watcher); + } + ); + registerWatcher(watcher); +} + +{ + // Test that using the `encoding` option has the expected result. + const watcher = fs.watch( + tmpdir.path, + { encoding: 'buffer' }, + (event, filename) => { + if (filename instanceof Buffer && filename.toString('utf8') === fn) + done(watcher); + else if (filename === null) + done(watcher); + } + ); + registerWatcher(watcher); +} + +const done = common.mustCall(unregisterWatcher, watchers.size); + +// OS X and perhaps other systems can have surprising race conditions with +// file events. So repeat the operation in case it is missed the first time. +const interval = setInterval(() => { + const fd = fs.openSync(a, 'w+'); + fs.closeSync(fd); + fs.unlinkSync(a); +}, common.platformTimeout(100)); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-add-file-to-existing-subfolder.js b/test/js/node/test/parallel/test-fs-watch-recursive-add-file-to-existing-subfolder.js new file mode 100644 index 0000000000..628ca4b2fd --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-add-file-to-existing-subfolder.js @@ -0,0 +1,58 @@ +'use strict'; + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +// Add a file to subfolder of a watching folder + +const rootDirectory = fs.mkdtempSync(testDir + path.sep); +const testDirectory = path.join(rootDirectory, 'test-4'); +fs.mkdirSync(testDirectory); + +const file = 'folder-5'; +const filePath = path.join(testDirectory, file); +fs.mkdirSync(filePath); + +const subfolderPath = path.join(filePath, 'subfolder-6'); +fs.mkdirSync(subfolderPath); + +const childrenFile = 'file-7.txt'; +const childrenAbsolutePath = path.join(subfolderPath, childrenFile); +const relativePath = path.join(file, path.basename(subfolderPath), childrenFile); + +const watcher = fs.watch(testDirectory, { recursive: true }); +let watcherClosed = false; +watcher.on('change', function(event, filename) { + assert.strictEqual(event, 'rename'); + + if (filename === relativePath) { + watcher.close(); + watcherClosed = true; + } +}); + +// Do the write with a delay to ensure that the OS is ready to notify us. +setTimeout(() => { + fs.writeFileSync(childrenAbsolutePath, 'world'); +}, common.platformTimeout(200)); + +process.once('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); +}); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-add-file-to-new-folder.js b/test/js/node/test/parallel/test-fs-watch-recursive-add-file-to-new-folder.js new file mode 100644 index 0000000000..2f91c968f7 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-add-file-to-new-folder.js @@ -0,0 +1,55 @@ +'use strict'; + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +// Add a file to newly created folder to already watching folder + +const rootDirectory = fs.mkdtempSync(testDir + path.sep); +const testDirectory = path.join(rootDirectory, 'test-3'); +fs.mkdirSync(testDirectory); + +const filePath = path.join(testDirectory, 'folder-3'); + +const childrenFile = 'file-4.txt'; +const childrenAbsolutePath = path.join(filePath, childrenFile); +const childrenRelativePath = path.join(path.basename(filePath), childrenFile); + +const watcher = fs.watch(testDirectory, { recursive: true }); +let watcherClosed = false; +watcher.on('change', function(event, filename) { + assert.strictEqual(event, 'rename'); + assert.ok(filename === path.basename(filePath) || filename === childrenRelativePath); + + if (filename === childrenRelativePath) { + watcher.close(); + watcherClosed = true; + } +}); + +// Do the write with a delay to ensure that the OS is ready to notify us. +setTimeout(() => { + fs.mkdirSync(filePath); + fs.writeFileSync(childrenAbsolutePath, 'world'); +}, common.platformTimeout(200)); + +process.once('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); +}); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-add-file-with-url.js b/test/js/node/test/parallel/test-fs-watch-recursive-add-file-with-url.js new file mode 100644 index 0000000000..ee726961c4 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-add-file-with-url.js @@ -0,0 +1,52 @@ +'use strict'; + +const common = require('../common'); +const { setTimeout } = require('timers/promises'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); +const { pathToFileURL } = require('url'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +(async () => { + // Add a file to already watching folder, and use URL as the path + + const rootDirectory = fs.mkdtempSync(testDir + path.sep); + const testDirectory = path.join(rootDirectory, 'test-5'); + fs.mkdirSync(testDirectory); + + const filePath = path.join(testDirectory, 'file-8.txt'); + const url = pathToFileURL(testDirectory); + + const watcher = fs.watch(url, { recursive: true }); + let watcherClosed = false; + watcher.on('change', function(event, filename) { + assert.strictEqual(event, 'rename'); + + if (filename === path.basename(filePath)) { + watcher.close(); + watcherClosed = true; + } + }); + + await setTimeout(common.platformTimeout(100)); + fs.writeFileSync(filePath, 'world'); + + process.on('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); + }); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-add-file.js b/test/js/node/test/parallel/test-fs-watch-recursive-add-file.js new file mode 100644 index 0000000000..27b933871c --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-add-file.js @@ -0,0 +1,49 @@ +'use strict'; + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +// Add a file to already watching folder + +const rootDirectory = fs.mkdtempSync(testDir + path.sep); +const testDirectory = path.join(rootDirectory, 'test-1'); +fs.mkdirSync(testDirectory); + +const testFile = path.join(testDirectory, 'file-1.txt'); + +const watcher = fs.watch(testDirectory, { recursive: true }); +let watcherClosed = false; +watcher.on('change', function(event, filename) { + assert.strictEqual(event, 'rename'); + + if (filename === path.basename(testFile)) { + watcher.close(); + watcherClosed = true; + } +}); + +// Do the write with a delay to ensure that the OS is ready to notify us. +setTimeout(() => { + fs.writeFileSync(testFile, 'world'); +}, common.platformTimeout(200)); + +process.once('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); +}); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-add-folder.js b/test/js/node/test/parallel/test-fs-watch-recursive-add-folder.js new file mode 100644 index 0000000000..1851a7850f --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-add-folder.js @@ -0,0 +1,50 @@ +'use strict'; + +const common = require('../common'); +const { setTimeout } = require('timers/promises'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +(async () => { + // Add a folder to already watching folder + + const rootDirectory = fs.mkdtempSync(testDir + path.sep); + const testDirectory = path.join(rootDirectory, 'test-2'); + fs.mkdirSync(testDirectory); + + const testFile = path.join(testDirectory, 'folder-2'); + + const watcher = fs.watch(testDirectory, { recursive: true }); + let watcherClosed = false; + watcher.on('change', function(event, filename) { + assert.strictEqual(event, 'rename'); + + if (filename === path.basename(testFile)) { + watcher.close(); + watcherClosed = true; + } + }); + + await setTimeout(common.platformTimeout(100)); + fs.mkdirSync(testFile); + + process.once('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); + }); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-assert-leaks.js b/test/js/node/test/parallel/test-fs-watch-recursive-assert-leaks.js new file mode 100644 index 0000000000..f5950e38ce --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-assert-leaks.js @@ -0,0 +1,50 @@ +'use strict'; + +const common = require('../common'); +const { setTimeout } = require('timers/promises'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +// Assert recursive watch does not leak handles +const rootDirectory = fs.mkdtempSync(testDir + path.sep); +const testDirectory = path.join(rootDirectory, 'test-7'); +const filePath = path.join(testDirectory, 'only-file.txt'); +fs.mkdirSync(testDirectory); + +let watcherClosed = false; +const watcher = fs.watch(testDirectory, { recursive: true }); +watcher.on('change', common.mustCallAtLeast(async (event, filename) => { + await setTimeout(common.platformTimeout(100)); + if (filename === path.basename(filePath)) { + watcher.close(); + watcherClosed = true; + } + await setTimeout(common.platformTimeout(100)); + assert(!process._getActiveHandles().some((handle) => handle.constructor.name === 'StatWatcher')); +})); + +process.on('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); +}); + +// Do the write with a delay to ensure that the OS is ready to notify us. +(async () => { + await setTimeout(200); + fs.writeFileSync(filePath, 'content'); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-delete.js b/test/js/node/test/parallel/test-fs-watch-recursive-delete.js new file mode 100644 index 0000000000..8e78ad54d6 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-delete.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const fs = require('fs'); + +if (common.isSunOS) + common.skip('SunOS behaves differently'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +tmpdir.refresh(); + +fs.mkdirSync(tmpdir.resolve('./parent/child'), { recursive: true }); + +fs.writeFileSync(tmpdir.resolve('./parent/child/test.tmp'), 'test'); + +const toWatch = tmpdir.resolve('./parent'); + +const onFileUpdate = common.mustCallAtLeast((eventType, filename) => { + // We are only checking for the filename to avoid having Windows, Linux and Mac specific assertions + if (fs.readdirSync(tmpdir.resolve('./parent')).length === 0) { + watcher.close(); + } +}, 1); + +const watcher = fs.watch(toWatch, { recursive: true }, onFileUpdate); + +// We must wait a bit `fs.rm()` to let the watcher be set up properly +setTimeout(() => { + fs.rm(tmpdir.resolve('./parent/child'), { recursive: true }, common.mustCall()); +}, common.platformTimeout(500)); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-linux-parallel-remove.js b/test/js/node/test/parallel/test-fs-watch-recursive-linux-parallel-remove.js new file mode 100644 index 0000000000..145b3314f2 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-linux-parallel-remove.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); + +if (!common.isLinux) + common.skip('This test can run only on Linux'); + +// Test that the watcher do not crash if the file "disappears" while +// watch is being set up. + +const path = require('node:path'); +const fs = require('node:fs'); +const { spawn } = require('node:child_process'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +const watcher = fs.watch(testDir, { recursive: true }); +watcher.on('change', function(event, filename) { + // This console.log makes the error happen + // do not remove + console.log(filename, event); +}); + +const testFile = path.join(testDir, 'a'); +const child = spawn(process.argv[0], ['-e', `const fs = require('node:fs'); for (let i = 0; i < 10000; i++) { const fd = fs.openSync('${testFile}', 'w'); fs.writeSync(fd, Buffer.from('hello')); fs.rmSync('${testFile}') }`], { + stdio: 'inherit' +}); + +child.on('exit', function() { + watcher.close(); +}); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-symlink.js b/test/js/node/test/parallel/test-fs-watch-recursive-symlink.js new file mode 100644 index 0000000000..602ec58eab --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-symlink.js @@ -0,0 +1,100 @@ +'use strict'; + +const common = require('../common'); +const { setTimeout } = require('timers/promises'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +(async () => { + // Add a recursive symlink to the parent folder + + const testDirectory = fs.mkdtempSync(testDir + path.sep); + + // Do not use `testDirectory` as base. It will hang the tests. + const rootDirectory = path.join(testDirectory, 'test-1'); + fs.mkdirSync(rootDirectory); + + const filePath = path.join(rootDirectory, 'file.txt'); + + const symlinkFolder = path.join(rootDirectory, 'symlink-folder'); + fs.symlinkSync(rootDirectory, symlinkFolder); + + + const watcher = fs.watch(rootDirectory, { recursive: true }); + let watcherClosed = false; + watcher.on('change', function(event, filename) { + assert.ok(event === 'rename', `Received ${event}`); + assert.ok(filename === path.basename(symlinkFolder) || filename === path.basename(filePath), `Received ${filename}`); + + if (filename === path.basename(filePath)) { + watcher.close(); + watcherClosed = true; + } + }); + + await setTimeout(common.platformTimeout(100)); + fs.writeFileSync(filePath, 'world'); + + process.once('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); + }); +})().then(common.mustCall()); + +(async () => { + // This test checks how a symlink to outside the tracking folder can trigger change + // tmp/sub-directory/tracking-folder/symlink-folder -> tmp/sub-directory + + const rootDirectory = fs.mkdtempSync(testDir + path.sep); + + const subDirectory = path.join(rootDirectory, 'sub-directory'); + fs.mkdirSync(subDirectory); + + const trackingSubDirectory = path.join(subDirectory, 'tracking-folder'); + fs.mkdirSync(trackingSubDirectory); + + const symlinkFolder = path.join(trackingSubDirectory, 'symlink-folder'); + fs.symlinkSync(subDirectory, symlinkFolder); + + const forbiddenFile = path.join(subDirectory, 'forbidden.txt'); + const acceptableFile = path.join(trackingSubDirectory, 'acceptable.txt'); + + const watcher = fs.watch(trackingSubDirectory, { recursive: true }); + let watcherClosed = false; + watcher.on('change', function(event, filename) { + // macOS will only change the following events: + // { event: 'rename', filename: 'symlink-folder' } + // { event: 'rename', filename: 'acceptable.txt' } + assert.ok(event === 'rename', `Received ${event}`); + assert.ok(filename === path.basename(symlinkFolder) || filename === path.basename(acceptableFile), `Received ${filename}`); + + if (filename === path.basename(acceptableFile)) { + watcher.close(); + watcherClosed = true; + } + }); + + await setTimeout(common.platformTimeout(100)); + fs.writeFileSync(forbiddenFile, 'world'); + await setTimeout(common.platformTimeout(100)); + fs.writeFileSync(acceptableFile, 'acceptable'); + + process.once('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); + }); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-sync-write.js b/test/js/node/test/parallel/test-fs-watch-recursive-sync-write.js new file mode 100644 index 0000000000..ecc380d190 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-sync-write.js @@ -0,0 +1,38 @@ +'use strict'; + +const common = require('../common'); +const { watch, writeFileSync } = require('node:fs'); +const { join } = require('node:path'); +const tmpdir = require('../common/tmpdir.js'); +const assert = require('assert'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +tmpdir.refresh(); + +const tmpDir = tmpdir.path; +const filename = join(tmpDir, 'test.file'); + +const keepalive = setTimeout(() => { + throw new Error('timed out'); +}, common.platformTimeout(30_000)); + +const watcher = watch(tmpDir, { recursive: true }, common.mustCall((eventType, _filename) => { + clearTimeout(keepalive); + watcher.close(); + assert.strictEqual(eventType, 'rename'); + assert.strictEqual(join(tmpDir, _filename), filename); +})); + +// Do the write with a delay to ensure that the OS is ready to notify us. +setTimeout(() => { + writeFileSync(filename, 'foobar2'); +}, common.platformTimeout(200)); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-update-file.js b/test/js/node/test/parallel/test-fs-watch-recursive-update-file.js new file mode 100644 index 0000000000..7100b015ab --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-update-file.js @@ -0,0 +1,45 @@ +'use strict'; + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +// Watch a folder and update an already existing file in it. + +const rootDirectory = fs.mkdtempSync(testDir + path.sep); +const testDirectory = path.join(rootDirectory, 'test-0'); +fs.mkdirSync(testDirectory); + +const testFile = path.join(testDirectory, 'file-1.txt'); +fs.writeFileSync(testFile, 'hello'); + +const watcher = fs.watch(testDirectory, { recursive: true }); +watcher.on('change', common.mustCallAtLeast(function(event, filename) { + // Libuv inconsistenly emits a rename event for the file we are watching + assert.ok(event === 'change' || event === 'rename'); + + if (filename === path.basename(testFile)) { + watcher.close(); + } +})); + +// Do the write with a delay to ensure that the OS is ready to notify us. +setTimeout(() => { + fs.writeFileSync(testFile, 'hello'); +}, common.platformTimeout(200)); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-validate.js b/test/js/node/test/parallel/test-fs-watch-recursive-validate.js new file mode 100644 index 0000000000..09eccc2d7e --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-validate.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +(async () => { + // Handle non-boolean values for options.recursive + + if (!common.isWindows && !common.isMacOS) { + assert.throws(() => { + const testsubdir = fs.mkdtempSync(testDir + path.sep); + fs.watch(testsubdir, { recursive: '1' }); + }, { + code: 'ERR_INVALID_ARG_TYPE', + }); + } +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-watch-recursive-watch-file.js b/test/js/node/test/parallel/test-fs-watch-recursive-watch-file.js new file mode 100644 index 0000000000..3449db8e59 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-recursive-watch-file.js @@ -0,0 +1,55 @@ +'use strict'; + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// fs-watch on folders have limited capability in AIX. +// The testcase makes use of folder watching, and causes +// hang. This behavior is documented. Skip this for AIX. + +if (common.isAIX) + common.skip('folder watch capability is limited in AIX.'); + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +(async () => { + // Watch a file (not a folder) using fs.watch + + const rootDirectory = fs.mkdtempSync(testDir + path.sep); + const testDirectory = path.join(rootDirectory, 'test-6'); + fs.mkdirSync(testDirectory); + + const filePath = path.join(testDirectory, 'only-file.txt'); + fs.writeFileSync(filePath, 'hello'); + + const watcher = fs.watch(filePath, { recursive: true }); + let watcherClosed = false; + let interval; + watcher.on('change', function(event, filename) { + assert.strictEqual(event, 'change'); + + if (filename === path.basename(filePath)) { + clearInterval(interval); + interval = null; + watcher.close(); + watcherClosed = true; + } + }); + + interval = setInterval(() => { + fs.writeFileSync(filePath, 'world'); + }, common.platformTimeout(10)); + + process.on('exit', function() { + assert(watcherClosed, 'watcher Object was not closed'); + assert.strictEqual(interval, null); + }); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-fs-watch-ref-unref.js b/test/js/node/test/parallel/test-fs-watch-ref-unref.js new file mode 100644 index 0000000000..e51ecaf5b2 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-ref-unref.js @@ -0,0 +1,20 @@ +'use strict'; + +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +const fs = require('fs'); + +const watcher = fs.watch(__filename, common.mustNotCall()); + +watcher.unref(); + +setTimeout( + common.mustCall(() => { + watcher.ref(); + watcher.unref(); + }), + common.platformTimeout(100) +); diff --git a/test/js/node/test/parallel/test-fs-watch-stop-sync.js b/test/js/node/test/parallel/test-fs-watch-stop-sync.js new file mode 100644 index 0000000000..7f0882e489 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch-stop-sync.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); + +// This test checks that the `stop` event is emitted asynchronously. +// +// If it isn't asynchronous, then the listener will be called during the +// execution of `watch.stop()`. That would be a bug. +// +// If it is asynchronous, then the listener will be removed before the event is +// emitted. + +const fs = require('fs'); + +const listener = common.mustNotCall( + 'listener should have been removed before the event was emitted' +); + +const watch = fs.watchFile(__filename, common.mustNotCall()); +watch.once('stop', listener); +watch.stop(); +watch.removeListener('stop', listener); diff --git a/test/js/node/test/parallel/test-fs-watch.js b/test/js/node/test/parallel/test-fs-watch.js new file mode 100644 index 0000000000..a494b9f8df --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watch.js @@ -0,0 +1,98 @@ +'use strict'; +const common = require('../common'); + +if (common.isIBMi) + common.skip('IBMi does not support `fs.watch()`'); + +// Tests if `filename` is provided to watcher on supported platforms + +const fs = require('fs'); +const assert = require('assert'); +const { join } = require('path'); + +class WatchTestCase { + constructor(shouldInclude, dirName, fileName, field) { + this.dirName = dirName; + this.fileName = fileName; + this.field = field; + this.shouldSkip = !shouldInclude; + } + get dirPath() { return tmpdir.resolve(this.dirName); } + get filePath() { return join(this.dirPath, this.fileName); } +} + +const cases = [ + // Watch on a file should callback with a filename on supported systems + new WatchTestCase( + common.isLinux || common.isMacOS || common.isWindows || common.isAIX, + 'watch1', + 'foo', + 'filePath' + ), + // Watch on a directory should callback with a filename on supported systems + new WatchTestCase( + common.isLinux || common.isMacOS || common.isWindows, + 'watch2', + 'bar', + 'dirPath' + ), +]; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +for (const testCase of cases) { + if (testCase.shouldSkip) continue; + fs.mkdirSync(testCase.dirPath); + // Long content so it's actually flushed. + const content1 = Date.now() + testCase.fileName.toLowerCase().repeat(1e4); + fs.writeFileSync(testCase.filePath, content1); + + let interval; + const pathToWatch = testCase[testCase.field]; + const watcher = fs.watch(pathToWatch); + watcher.on('error', (err) => { + if (interval) { + clearInterval(interval); + interval = null; + } + assert.fail(err); + }); + watcher.on('close', common.mustCall(() => { + watcher.close(); // Closing a closed watcher should be a noop + })); + watcher.on('change', common.mustCall(function(eventType, argFilename) { + if (interval) { + clearInterval(interval); + interval = null; + } + if (common.isMacOS) + assert.strictEqual(['rename', 'change'].includes(eventType), true); + else + assert.strictEqual(eventType, 'change'); + assert.strictEqual(argFilename, testCase.fileName); + + watcher.close(); + + // We document that watchers cannot be used anymore when it's closed, + // here we turn the methods into noops instead of throwing + watcher.close(); // Closing a closed watcher should be a noop + })); + + // Long content so it's actually flushed. toUpperCase so there's real change. + const content2 = Date.now() + testCase.fileName.toUpperCase().repeat(1e4); + interval = setInterval(() => { + fs.writeFileSync(testCase.filePath, ''); + fs.writeFileSync(testCase.filePath, content2); + }, 100); +} + +[false, 1, {}, [], null, undefined].forEach((input) => { + assert.throws( + () => fs.watch(input, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } + ); +}); diff --git a/test/js/node/test/parallel/test-fs-watchfile-ref-unref.js b/test/js/node/test/parallel/test-fs-watchfile-ref-unref.js new file mode 100644 index 0000000000..4ac2691ea9 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-watchfile-ref-unref.js @@ -0,0 +1,35 @@ +'use strict'; + +const common = require('../common'); + +const fs = require('fs'); +const assert = require('assert'); + +const uncalledListener = common.mustNotCall(); +const uncalledListener2 = common.mustNotCall(); +const watcher = fs.watchFile(__filename, uncalledListener); + +watcher.unref(); +watcher.unref(); +watcher.ref(); +watcher.unref(); +watcher.ref(); +watcher.ref(); +watcher.unref(); + +fs.unwatchFile(__filename, uncalledListener); + +// Watch the file with two different listeners. +fs.watchFile(__filename, uncalledListener); +const watcher2 = fs.watchFile(__filename, uncalledListener2); + +setTimeout( + common.mustCall(() => { + fs.unwatchFile(__filename, common.mustNotCall()); + assert.strictEqual(watcher2.listenerCount('change'), 2); + fs.unwatchFile(__filename, uncalledListener); + assert.strictEqual(watcher2.listenerCount('change'), 1); + watcher2.unref(); + }), + common.platformTimeout(100) +); diff --git a/test/js/node/test/parallel/test-fs-write-file-buffer.js b/test/js/node/test/parallel/test-fs-write-file-buffer.js new file mode 100644 index 0000000000..ec6c60e1a4 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-write-file-buffer.js @@ -0,0 +1,54 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const util = require('util'); +const fs = require('fs'); + +let data = [ + '/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcH', + 'Bw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/', + '2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e', + 'Hh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCAAQABADASIAAhEBAxEB/8QA', + 'HwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUF', + 'BAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK', + 'FhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1', + 'dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG', + 'x8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEB', + 'AQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAEC', + 'AxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRom', + 'JygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE', + 'hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU', + '1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDhfBUFl/wk', + 'OmPqKJJZw3aiZFBw4z93jnkkc9u9dj8XLfSI/EBt7DTo7ea2Ox5YXVo5FC7g', + 'Tjq24nJPXNVtO0KATRvNHCIg3zoWJWQHqp+o4pun+EtJ0zxBq8mnLJa2d1L5', + '0NvnKRjJBUE5PAx3NYxxUY0pRtvYHSc5Ka2X9d7H/9k=']; + +data = data.join('\n'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const buf = Buffer.from(data, 'base64'); +fs.writeFileSync(tmpdir.resolve('test.jpg'), buf); + +util.log('Done!'); diff --git a/test/js/node/test/parallel/test-fs-write-file-invalid-path.js b/test/js/node/test/parallel/test-fs-write-file-invalid-path.js new file mode 100644 index 0000000000..aaa7eacde5 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-write-file-invalid-path.js @@ -0,0 +1,45 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +if (!common.isWindows) + common.skip('This test is for Windows only.'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const DATA_VALUE = 'hello'; + +// Refs: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx +// Ignore '/', '\\' and ':' +const RESERVED_CHARACTERS = '<>"|?*'; + +[...RESERVED_CHARACTERS].forEach((ch) => { + const pathname = tmpdir.resolve(`somefile_${ch}`); + assert.throws( + () => { + fs.writeFileSync(pathname, DATA_VALUE); + }, + /^Error: ENOENT: no such file or directory, open '.*'$/, + `failed with '${ch}'`); +}); + +// Test for ':' (NTFS data streams). +// Refs: https://msdn.microsoft.com/en-us/library/windows/desktop/bb540537.aspx +const pathname = tmpdir.resolve('foo:bar'); +fs.writeFileSync(pathname, DATA_VALUE); + +let content = ''; +const fileDataStream = fs.createReadStream(pathname, { + encoding: 'utf8' +}); + +fileDataStream.on('data', (data) => { + content += data; +}); + +fileDataStream.on('end', common.mustCall(() => { + assert.strictEqual(content, DATA_VALUE); +})); diff --git a/test/js/node/test/parallel/test-fs-write-no-fd.js b/test/js/node/test/parallel/test-fs-write-no-fd.js new file mode 100644 index 0000000000..576457203e --- /dev/null +++ b/test/js/node/test/parallel/test-fs-write-no-fd.js @@ -0,0 +1,12 @@ +'use strict'; +const common = require('../common'); +const fs = require('fs'); +const assert = require('assert'); + +assert.throws(function() { + fs.write(null, Buffer.allocUnsafe(1), 0, 1, common.mustNotCall()); +}, /TypeError/); + +assert.throws(function() { + fs.write(null, '1', 0, 1, common.mustNotCall()); +}, /TypeError/); diff --git a/test/js/node/test/parallel/test-fs-write-stream-close-without-callback.js b/test/js/node/test/parallel/test-fs-write-stream-close-without-callback.js new file mode 100644 index 0000000000..7bf83cd809 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-write-stream-close-without-callback.js @@ -0,0 +1,12 @@ +'use strict'; + +require('../common'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const s = fs.createWriteStream(tmpdir.resolve('nocallback')); + +s.end('hello world'); +s.close(); diff --git a/test/js/node/test/parallel/test-fs-write-stream-encoding.js b/test/js/node/test/parallel/test-fs-write-stream-encoding.js new file mode 100644 index 0000000000..f06fae923c --- /dev/null +++ b/test/js/node/test/parallel/test-fs-write-stream-encoding.js @@ -0,0 +1,35 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const fs = require('fs'); +const stream = require('stream'); +const tmpdir = require('../common/tmpdir'); +const firstEncoding = 'base64'; +const secondEncoding = 'latin1'; + +const examplePath = fixtures.path('x.txt'); +const dummyPath = tmpdir.resolve('x.txt'); + +tmpdir.refresh(); + +const exampleReadStream = fs.createReadStream(examplePath, { + encoding: firstEncoding +}); + +const dummyWriteStream = fs.createWriteStream(dummyPath, { + encoding: firstEncoding +}); + +exampleReadStream.pipe(dummyWriteStream).on('finish', function() { + const assertWriteStream = new stream.Writable({ + write: function(chunk, enc, next) { + const expected = Buffer.from('xyz\n'); + assert(chunk.equals(expected)); + } + }); + assertWriteStream.setDefaultEncoding(secondEncoding); + fs.createReadStream(dummyPath, { + encoding: secondEncoding + }).pipe(assertWriteStream); +}); diff --git a/test/js/node/test/parallel/test-fs-write-stream-end.js b/test/js/node/test/parallel/test-fs-write-stream-end.js new file mode 100644 index 0000000000..7f0cc65540 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-write-stream-end.js @@ -0,0 +1,59 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +{ + const file = tmpdir.resolve('write-end-test0.txt'); + const stream = fs.createWriteStream(file); + stream.end(); + stream.on('close', common.mustCall()); +} + +{ + const file = tmpdir.resolve('write-end-test1.txt'); + const stream = fs.createWriteStream(file); + stream.end('a\n', 'utf8'); + stream.on('close', common.mustCall(function() { + const content = fs.readFileSync(file, 'utf8'); + assert.strictEqual(content, 'a\n'); + })); +} + +{ + const file = tmpdir.resolve('write-end-test2.txt'); + const stream = fs.createWriteStream(file); + stream.end(); + + let calledOpen = false; + stream.on('open', () => { + calledOpen = true; + }); + stream.on('finish', common.mustCall(() => { + assert.strictEqual(calledOpen, true); + })); +} diff --git a/test/js/node/test/parallel/test-fs-write-stream.js b/test/js/node/test/parallel/test-fs-write-stream.js new file mode 100644 index 0000000000..a3dccf7cdc --- /dev/null +++ b/test/js/node/test/parallel/test-fs-write-stream.js @@ -0,0 +1,66 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); + +const file = tmpdir.resolve('write.txt'); + +tmpdir.refresh(); + +{ + const stream = fs.WriteStream(file); + const _fs_close = fs.close; + + fs.close = function(fd) { + assert.ok(fd, 'fs.close must not be called without an undefined fd.'); + fs.close = _fs_close; + fs.closeSync(fd); + }; + stream.destroy(); +} + +{ + const stream = fs.createWriteStream(file); + + stream.on('drain', function() { + assert.fail('\'drain\' event must not be emitted before ' + + 'stream.write() has been called at least once.'); + }); + stream.destroy(); +} + +// Throws if data is not of type Buffer. +{ + const stream = fs.createWriteStream(file); + stream.on('error', common.mustNotCall()); + assert.throws(() => { + stream.write(42); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + stream.destroy(); +} diff --git a/test/js/node/test/parallel/test-fs-write-sync.js b/test/js/node/test/parallel/test-fs-write-sync.js new file mode 100644 index 0000000000..733892c35e --- /dev/null +++ b/test/js/node/test/parallel/test-fs-write-sync.js @@ -0,0 +1,55 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); +const filename = tmpdir.resolve('write.txt'); + +tmpdir.refresh(); + +{ + const parameters = [Buffer.from('bár'), 0, Buffer.byteLength('bár')]; + + // The first time fs.writeSync is called with all parameters provided. + // After that, each pop in the cycle removes the final parameter. So: + // - The 2nd time fs.writeSync with a buffer, without the length parameter. + // - The 3rd time fs.writeSync with a buffer, without the offset and length + // parameters. + while (parameters.length > 0) { + const fd = fs.openSync(filename, 'w'); + + let written = fs.writeSync(fd, ''); + assert.strictEqual(written, 0); + + fs.writeSync(fd, 'foo'); + + written = fs.writeSync(fd, ...parameters); + assert.ok(written > 3); + fs.closeSync(fd); + + assert.strictEqual(fs.readFileSync(filename, 'utf-8'), 'foobár'); + + parameters.pop(); + } +} diff --git a/test/js/node/test/parallel/test-fs-writestream-open-write.js b/test/js/node/test/parallel/test-fs-writestream-open-write.js new file mode 100644 index 0000000000..af02d90ae6 --- /dev/null +++ b/test/js/node/test/parallel/test-fs-writestream-open-write.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const { strictEqual } = require('assert'); +const fs = require('fs'); + +// Regression test for https://github.com/nodejs/node/issues/51993 + +tmpdir.refresh(); + +const file = tmpdir.resolve('test-fs-writestream-open-write.txt'); + +const w = fs.createWriteStream(file); + +w.on('open', common.mustCall(() => { + w.write('hello'); + + process.nextTick(() => { + w.write('world'); + w.end(); + }); +})); + +w.on('close', common.mustCall(() => { + strictEqual(fs.readFileSync(file, 'utf8'), 'helloworld'); + fs.unlinkSync(file); +})); diff --git a/test/js/node/test/parallel/test-global-domexception.js b/test/js/node/test/parallel/test-global-domexception.js new file mode 100644 index 0000000000..d19b5a5e4f --- /dev/null +++ b/test/js/node/test/parallel/test-global-domexception.js @@ -0,0 +1,11 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); + +assert.strictEqual(typeof DOMException, 'function'); + +assert.throws(() => { + atob('我要抛错!'); +}, DOMException); diff --git a/test/js/node/test/parallel/test-global-encoder.js b/test/js/node/test/parallel/test-global-encoder.js new file mode 100644 index 0000000000..0e98bc806c --- /dev/null +++ b/test/js/node/test/parallel/test-global-encoder.js @@ -0,0 +1,8 @@ +'use strict'; + +require('../common'); +const { strictEqual } = require('assert'); +const util = require('util'); + +strictEqual(TextDecoder, util.TextDecoder); +strictEqual(TextEncoder, util.TextEncoder); diff --git a/test/js/node/test/parallel/test-global-webcrypto.js b/test/js/node/test/parallel/test-global-webcrypto.js new file mode 100644 index 0000000000..9eb18ca9d1 --- /dev/null +++ b/test/js/node/test/parallel/test-global-webcrypto.js @@ -0,0 +1,13 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); + +/* eslint-disable no-restricted-properties */ +assert.strictEqual(globalThis.crypto, crypto.webcrypto); +assert.strictEqual(Crypto, crypto.webcrypto.constructor); +assert.strictEqual(SubtleCrypto, crypto.webcrypto.subtle.constructor); diff --git a/test/js/node/test/parallel/test-handle-wrap-close-abort.js b/test/js/node/test/parallel/test-handle-wrap-close-abort.js new file mode 100644 index 0000000000..b91f9dd349 --- /dev/null +++ b/test/js/node/test/parallel/test-handle-wrap-close-abort.js @@ -0,0 +1,37 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +process.on('uncaughtException', common.mustCall(2)); + +setTimeout(function() { + process.nextTick(function() { + const c = setInterval(function() { + clearInterval(c); + throw new Error('setInterval'); + }, 1); + }); + setTimeout(function() { + throw new Error('setTimeout'); + }, 1); +}, 1); diff --git a/test/js/node/test/parallel/test-http-abort-before-end.js b/test/js/node/test/parallel/test-http-abort-before-end.js new file mode 100644 index 0000000000..5577f256ca --- /dev/null +++ b/test/js/node/test/parallel/test-http-abort-before-end.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); + +const server = http.createServer(common.mustNotCall()); + +server.listen(0, common.mustCall(() => { + const req = http.request({ + method: 'GET', + host: '127.0.0.1', + port: server.address().port + }); + + req.on('abort', common.mustCall(() => { + server.close(); + })); + + req.on('error', common.mustNotCall()); + + req.abort(); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http-agent-false.js b/test/js/node/test/parallel/test-http-agent-false.js new file mode 100644 index 0000000000..2f4505ef66 --- /dev/null +++ b/test/js/node/test/parallel/test-http-agent-false.js @@ -0,0 +1,46 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); + +// Sending `agent: false` when `port: null` is also passed in (i.e. the result +// of a `url.parse()` call with the default port used, 80 or 443), should not +// result in an assertion error... +const opts = { + host: '127.0.0.1', + port: null, + path: '/', + method: 'GET', + agent: false +}; + +// We just want an "error" (no local HTTP server on port 80) or "response" +// to happen (user happens ot have HTTP server running on port 80). +// As long as the process doesn't crash from a C++ assertion then we're good. +const req = http.request(opts); + +// Will be called by either the response event or error event, not both +const oneResponse = common.mustCall(); +req.on('response', oneResponse); +req.on('error', oneResponse); +req.end(); diff --git a/test/js/node/test/parallel/test-http-agent-keepalive-delay.js b/test/js/node/test/parallel/test-http-agent-keepalive-delay.js new file mode 100644 index 0000000000..b5edd78b66 --- /dev/null +++ b/test/js/node/test/parallel/test-http-agent-keepalive-delay.js @@ -0,0 +1,36 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const { Agent } = require('_http_agent'); + +const agent = new Agent({ + keepAlive: true, + keepAliveMsecs: 1000, +}); + +const server = http.createServer(common.mustCall((req, res) => { + res.end('ok'); +})); + +server.listen(0, common.mustCall(() => { + const createConnection = agent.createConnection; + agent.createConnection = (options, ...args) => { + assert.strictEqual(options.keepAlive, true); + assert.strictEqual(options.keepAliveInitialDelay, agent.keepAliveMsecs); + return createConnection.call(agent, options, ...args); + }; + http.get({ + host: 'localhost', + port: server.address().port, + agent: agent, + path: '/' + }, common.mustCall((res) => { + // for emit end event + res.on('data', () => {}); + res.on('end', () => { + server.close(); + }); + })); +})); diff --git a/test/js/node/test/parallel/test-http-agent-no-protocol.js b/test/js/node/test/parallel/test-http-agent-no-protocol.js new file mode 100644 index 0000000000..d1eaf242a5 --- /dev/null +++ b/test/js/node/test/parallel/test-http-agent-no-protocol.js @@ -0,0 +1,41 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); +const url = require('url'); + +const server = http.createServer(common.mustCall((req, res) => { + res.end(); +})).listen(0, '127.0.0.1', common.mustCall(() => { + const opts = url.parse(`http://127.0.0.1:${server.address().port}/`); + + // Remove the `protocol` field… the `http` module should fall back + // to "http:", as defined by the global, default `http.Agent` instance. + opts.agent = new http.Agent(); + opts.agent.protocol = null; + + http.get(opts, common.mustCall((res) => { + res.resume(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http-agent-null.js b/test/js/node/test/parallel/test-http-agent-null.js new file mode 100644 index 0000000000..0f87d09813 --- /dev/null +++ b/test/js/node/test/parallel/test-http-agent-null.js @@ -0,0 +1,37 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); + +const server = http.createServer(common.mustCall((req, res) => { + res.end(); +})).listen(0, common.mustCall(() => { + const options = { + agent: null, + port: server.address().port + }; + http.get(options, common.mustCall((res) => { + res.resume(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http-agent-uninitialized-with-handle.js b/test/js/node/test/parallel/test-http-agent-uninitialized-with-handle.js new file mode 100644 index 0000000000..77f0177173 --- /dev/null +++ b/test/js/node/test/parallel/test-http-agent-uninitialized-with-handle.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); +const net = require('net'); + +const agent = new http.Agent({ + keepAlive: true, +}); +const socket = new net.Socket(); +// If _handle exists then internals assume a couple methods exist. +socket._handle = { + ref() { }, + readStart() { }, +}; + +const server = http.createServer(common.mustCall((req, res) => { + res.end(); +})).listen(0, common.mustCall(() => { + const req = new http.ClientRequest(`http://localhost:${server.address().port}/`); + + // Manually add the socket without a _handle. + agent.freeSockets[agent.getName(req)] = [socket]; + // Now force the agent to use the socket and check that _handle exists before + // calling asyncReset(). + agent.addRequest(req, {}); + req.on('response', common.mustCall(() => { + server.close(); + })); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http-agent-uninitialized.js b/test/js/node/test/parallel/test-http-agent-uninitialized.js new file mode 100644 index 0000000000..dbb38e3b0f --- /dev/null +++ b/test/js/node/test/parallel/test-http-agent-uninitialized.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); +const net = require('net'); + +const agent = new http.Agent({ + keepAlive: true, +}); +const socket = new net.Socket(); + +const server = http.createServer(common.mustCall((req, res) => { + res.end(); +})).listen(0, common.mustCall(() => { + const req = new http.ClientRequest(`http://localhost:${server.address().port}/`); + + // Manually add the socket without a _handle. + agent.freeSockets[agent.getName(req)] = [socket]; + // Now force the agent to use the socket and check that _handle exists before + // calling asyncReset(). + agent.addRequest(req, {}); + req.on('response', common.mustCall(() => { + server.close(); + })); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http-allow-req-after-204-res.js b/test/js/node/test/parallel/test-http-allow-req-after-204-res.js new file mode 100644 index 0000000000..84dd876985 --- /dev/null +++ b/test/js/node/test/parallel/test-http-allow-req-after-204-res.js @@ -0,0 +1,61 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); +const Countdown = require('../common/countdown'); + +// first 204 or 304 works, subsequent anything fails +const codes = [204, 200]; + +const countdown = new Countdown(codes.length, () => server.close()); + +const server = http.createServer(common.mustCall((req, res) => { + const code = codes.shift(); + assert.strictEqual(typeof code, 'number'); + assert.ok(code > 0); + res.writeHead(code, {}); + res.end(); +}, codes.length)); + +function nextRequest() { + + const request = http.get({ + port: server.address().port, + path: '/' + }, common.mustCall((response) => { + response.on('end', common.mustCall(() => { + if (countdown.dec()) { + // throws error: + nextRequest(); + // TODO: investigate why this does not work fine even though it should. + // works just fine: + // process.nextTick(nextRequest); + } + })); + response.resume(); + })); + request.end(); +} + +server.listen(0, nextRequest); diff --git a/test/js/node/test/parallel/test-http-bind-twice.js b/test/js/node/test/parallel/test-http-bind-twice.js new file mode 100644 index 0000000000..50834cc5cb --- /dev/null +++ b/test/js/node/test/parallel/test-http-bind-twice.js @@ -0,0 +1,36 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server1 = http.createServer(common.mustNotCall()); +server1.listen(0, '127.0.0.1', common.mustCall(function() { + const server2 = http.createServer(common.mustNotCall()); + server2.listen(this.address().port, '127.0.0.1', common.mustNotCall()); + + server2.on('error', common.mustCall(function(e) { + assert.strictEqual(e.code, 'EADDRINUSE'); + server1.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http-buffer-sanity.js b/test/js/node/test/parallel/test-http-buffer-sanity.js new file mode 100644 index 0000000000..05c027fd48 --- /dev/null +++ b/test/js/node/test/parallel/test-http-buffer-sanity.js @@ -0,0 +1,71 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const bufferSize = 5 * 1024 * 1024; +let measuredSize = 0; + +const buffer = Buffer.allocUnsafe(bufferSize); +for (let i = 0; i < buffer.length; i++) { + buffer[i] = i % 256; +} + +const server = http.Server(function(req, res) { + server.close(); + + let i = 0; + + req.on('data', (d) => { + measuredSize += d.length; + for (let j = 0; j < d.length; j++) { + assert.strictEqual(d[j], buffer[i]); + i++; + } + }); + + req.on('end', common.mustCall(() => { + assert.strictEqual(measuredSize, bufferSize); + res.writeHead(200); + res.write('thanks'); + res.end(); + })); +}); + +server.listen(0, common.mustCall(() => { + const req = http.request({ + port: server.address().port, + method: 'POST', + path: '/', + headers: { 'content-length': buffer.length } + }, common.mustCall((res) => { + res.setEncoding('utf8'); + let data = ''; + res.on('data', (chunk) => data += chunk); + res.on('end', common.mustCall(() => { + assert.strictEqual(data, 'thanks'); + })); + })); + req.end(buffer); +})); diff --git a/test/js/node/test/parallel/test-http-chunk-problem.js b/test/js/node/test/parallel/test-http-chunk-problem.js new file mode 100644 index 0000000000..a3c354aecd --- /dev/null +++ b/test/js/node/test/parallel/test-http-chunk-problem.js @@ -0,0 +1,96 @@ +'use strict'; +// http://groups.google.com/group/nodejs/browse_thread/thread/f66cd3c960406919 +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); + +if (process.argv[2] === 'request') { + const http = require('http'); + const options = { + port: +process.argv[3], + path: '/' + }; + + http.get(options, (res) => { + res.pipe(process.stdout); + }); + + return; +} + +if (process.argv[2] === 'shasum') { + const crypto = require('crypto'); + const shasum = crypto.createHash('sha1'); + process.stdin.on('data', (d) => { + shasum.update(d); + }); + + process.stdin.on('close', () => { + process.stdout.write(shasum.digest('hex')); + }); + + return; +} + +const http = require('http'); +const cp = require('child_process'); + +const tmpdir = require('../common/tmpdir'); + +const filename = tmpdir.resolve('big'); +let server; + +function executeRequest(cb) { + cp.exec([`"${process.execPath}"`, + `"${__filename}"`, + 'request', + server.address().port, + '|', + `"${process.execPath}"`, + `"${__filename}"`, + 'shasum' ].join(' '), + (err, stdout, stderr) => { + if (stderr.trim() !== '') { + console.log(stderr); + } + assert.ifError(err); + assert.strictEqual(stdout.slice(0, 40), + '8c206a1a87599f532ce68675536f0b1546900d7a'); + cb(); + } + ); +} + + +tmpdir.refresh(); + +common.createZeroFilledFile(filename); + +server = http.createServer(function(req, res) { + res.writeHead(200); + + // Create the subprocess + const cat = cp.spawn('cat', [filename]); + + // Stream the data through to the response as binary chunks + cat.stdout.on('data', (data) => { + res.write(data); + }); + + cat.stdout.on('end', () => res.end()); + + // End the response on exit (and log errors) + cat.on('exit', (code) => { + if (code !== 0) { + console.error(`subprocess exited with code ${code}`); + process.exit(1); + } + }); + +}); + +server.listen(0, () => { + executeRequest(() => server.close()); +}); diff --git a/test/js/node/test/parallel/test-http-chunked-smuggling.js b/test/js/node/test/parallel/test-http-chunked-smuggling.js new file mode 100644 index 0000000000..dbad45e1be --- /dev/null +++ b/test/js/node/test/parallel/test-http-chunked-smuggling.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); +const net = require('net'); +const assert = require('assert'); + +// Verify that invalid chunk extensions cannot be used to perform HTTP request +// smuggling attacks. + +const server = http.createServer(common.mustCall((request, response) => { + assert.notStrictEqual(request.url, '/admin'); + response.end('hello world'); +}), 1); + +server.listen(0, common.mustCall(start)); + +function start() { + const sock = net.connect(server.address().port); + + sock.write('' + + 'GET / HTTP/1.1\r\n' + + 'Host: localhost:8080\r\n' + + 'Transfer-Encoding: chunked\r\n' + + '\r\n' + + '2;\n' + + 'xx\r\n' + + '4c\r\n' + + '0\r\n' + + '\r\n' + + 'GET /admin HTTP/1.1\r\n' + + 'Host: localhost:8080\r\n' + + 'Transfer-Encoding: chunked\r\n' + + '\r\n' + + '0\r\n' + + '\r\n' + ); + + sock.resume(); + sock.on('end', common.mustCall(function() { + server.close(); + })); +} diff --git a/test/js/node/test/parallel/test-http-chunked.js b/test/js/node/test/parallel/test-http-chunked.js new file mode 100644 index 0000000000..cfa34e3afe --- /dev/null +++ b/test/js/node/test/parallel/test-http-chunked.js @@ -0,0 +1,48 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const fixtures = require('../common/fixtures'); +const UTF8_STRING = fixtures.utf8TestText; + +const server = http.createServer(common.mustCall((req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf8' }); + res.end(UTF8_STRING, 'utf8'); +})); +server.listen(0, common.mustCall(() => { + let data = ''; + http.get({ + path: '/', + host: 'localhost', + port: server.address().port + }, common.mustCall((x) => { + x.setEncoding('utf8'); + x.on('data', (c) => data += c); + x.on('end', common.mustCall(() => { + assert.strictEqual(typeof data, 'string'); + assert.strictEqual(UTF8_STRING, data); + server.close(); + })); + })).end(); +})); diff --git a/test/js/node/test/parallel/test-http-client-abort2.js b/test/js/node/test/parallel/test-http-client-abort2.js new file mode 100644 index 0000000000..bc4b0e4083 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-abort2.js @@ -0,0 +1,38 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); + +const server = http.createServer(common.mustCall((req, res) => { + res.end('Hello'); +})); + +server.listen(0, common.mustCall(() => { + const options = { port: server.address().port }; + const req = http.get(options, common.mustCall((res) => { + res.on('data', (data) => { + req.abort(); + server.close(); + }); + })); +})); diff --git a/test/js/node/test/parallel/test-http-client-close-with-default-agent.js b/test/js/node/test/parallel/test-http-client-close-with-default-agent.js new file mode 100644 index 0000000000..ea1e1481ba --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-close-with-default-agent.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer(function(req, res) { + res.writeHead(200); + res.end(); +}); + +server.listen(0, common.mustCall(() => { + const req = http.get({ port: server.address().port }, (res) => { + assert.strictEqual(res.statusCode, 200); + res.resume(); + server.close(); + }); + + req.end(); +})); + +// This timer should never go off as the server will close the socket +setTimeout(common.mustNotCall(), common.platformTimeout(1000)).unref(); diff --git a/test/js/node/test/parallel/test-http-client-defaults.js b/test/js/node/test/parallel/test-http-client-defaults.js new file mode 100644 index 0000000000..43419d1dfd --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-defaults.js @@ -0,0 +1,23 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const ClientRequest = require('http').ClientRequest; + +{ + const req = new ClientRequest({ createConnection: () => {} }); + assert.strictEqual(req.path, '/'); + assert.strictEqual(req.method, 'GET'); +} + +{ + const req = new ClientRequest({ method: '', createConnection: () => {} }); + assert.strictEqual(req.path, '/'); + assert.strictEqual(req.method, 'GET'); +} + +{ + const req = new ClientRequest({ path: '', createConnection: () => {} }); + assert.strictEqual(req.path, '/'); + assert.strictEqual(req.method, 'GET'); +} diff --git a/test/js/node/test/parallel/test-http-client-encoding.js b/test/js/node/test/parallel/test-http-client-encoding.js new file mode 100644 index 0000000000..a4701cdbd0 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-encoding.js @@ -0,0 +1,39 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer((req, res) => { + res.end('ok'); + server.close(); +}).listen(0, common.mustCall(() => { + http.request({ + port: server.address().port, + encoding: 'utf8' + }, common.mustCall((res) => { + let data = ''; + res.on('data', (chunk) => data += chunk); + res.on('end', common.mustCall(() => assert.strictEqual(data, 'ok'))); + })).end(); +})); diff --git a/test/js/node/test/parallel/test-http-client-get-url.js b/test/js/node/test/parallel/test-http-client-get-url.js new file mode 100644 index 0000000000..3b091a72ed --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-get-url.js @@ -0,0 +1,46 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); +const testPath = '/foo?bar'; + +const server = http.createServer(common.mustCall((req, res) => { + assert.strictEqual(req.method, 'GET'); + assert.strictEqual(req.url, testPath); + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('hello\n'); + res.end(); +}, 3)); + +server.listen(0, common.localhostIPv4, common.mustCall(() => { + const u = `http://${common.localhostIPv4}:${server.address().port}${testPath}`; + http.get(u, common.mustCall(() => { + http.get(url.parse(u), common.mustCall(() => { + http.get(new URL(u), common.mustCall(() => { + server.close(); + })); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http-client-input-function.js b/test/js/node/test/parallel/test-http-client-input-function.js new file mode 100644 index 0000000000..3a2f93aa0e --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-input-function.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); + +{ + const server = http.createServer(common.mustCall((req, res) => { + res.writeHead(200); + res.end('hello world'); + })).listen(0, '127.0.0.1'); + + server.on('listening', common.mustCall(() => { + const req = new http.ClientRequest(server.address(), common.mustCall((response) => { + let body = ''; + response.setEncoding('utf8'); + response.on('data', (chunk) => { + body += chunk; + }); + + response.on('end', common.mustCall(() => { + assert.strictEqual(body, 'hello world'); + server.close(); + })); + })); + + req.end(); + })); +} diff --git a/test/js/node/test/parallel/test-http-client-keep-alive-hint.js b/test/js/node/test/parallel/test-http-client-keep-alive-hint.js new file mode 100644 index 0000000000..2618dfd552 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-keep-alive-hint.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer( + { keepAliveTimeout: common.platformTimeout(60000) }, + function(req, res) { + req.resume(); + res.writeHead(200, { 'Connection': 'keep-alive', 'Keep-Alive': 'timeout=1' }); + res.end('FOO'); + } +); + +server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port }, (res) => { + assert.strictEqual(res.statusCode, 200); + + res.resume(); + server.close(); + }); +})); + + +// This timer should never go off as the agent will parse the hint and terminate earlier +setTimeout(common.mustNotCall(), common.platformTimeout(3000)).unref(); diff --git a/test/js/node/test/parallel/test-http-client-keep-alive-release-before-finish.js b/test/js/node/test/parallel/test-http-client-keep-alive-release-before-finish.js new file mode 100644 index 0000000000..e6e0bac1bb --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-keep-alive-release-before-finish.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); + +const server = http.createServer((req, res) => { + res.end(); +}).listen(0, common.mustCall(() => { + const agent = new http.Agent({ + maxSockets: 1, + keepAlive: true + }); + + const port = server.address().port; + + const post = http.request({ + agent, + method: 'POST', + port, + }, common.mustCall((res) => { + res.resume(); + })); + + // What happens here is that the server `end`s the response before we send + // `something`, and the client thought that this is a green light for sending + // next GET request + post.write(Buffer.alloc(16 * 1024, 'X')); + setTimeout(() => { + post.end('something'); + }, 100); + + http.request({ + agent, + method: 'GET', + port, + }, common.mustCall((res) => { + server.close(); + res.connection.end(); + })).end(); +})); diff --git a/test/js/node/test/parallel/test-http-client-pipe-end.js b/test/js/node/test/parallel/test-http-client-pipe-end.js new file mode 100644 index 0000000000..ee88ce3d96 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-pipe-end.js @@ -0,0 +1,62 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// See https://github.com/joyent/node/issues/3257 + +const common = require('../common'); +const http = require('http'); + +const server = http.createServer(function(req, res) { + req.resume(); + req.once('end', function() { + res.writeHead(200); + res.end(); + server.close(); + }); +}); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +server.listen(common.PIPE, function() { + const req = http.request({ + socketPath: common.PIPE, + headers: { 'Content-Length': '1' }, + method: 'POST', + path: '/' + }); + + req.write('.'); + + sched(function() { req.end(); }, 5); +}); + +// Schedule a callback after `ticks` event loop ticks +function sched(cb, ticks) { + function fn() { + if (--ticks) + setImmediate(fn); + else + cb(); + } + setImmediate(fn); +} diff --git a/test/js/node/test/parallel/test-http-client-race-2.js b/test/js/node/test/parallel/test-http-client-race-2.js new file mode 100644 index 0000000000..951b8e0d74 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-race-2.js @@ -0,0 +1,112 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +// +// Slight variation on test-http-client-race to test for another race +// condition involving the parsers FreeList used internally by http.Client. +// + +const body1_s = '1111111111111111'; +const body2_s = '22222'; +const body3_s = '3333333333333333333'; + +const server = http.createServer(function(req, res) { + const pathname = url.parse(req.url).pathname; + + let body; + switch (pathname) { + case '/1': body = body1_s; break; + case '/2': body = body2_s; break; + default: body = body3_s; + } + + res.writeHead(200, { + 'Content-Type': 'text/plain', + 'Content-Length': body.length + }); + res.end(body); +}); +server.listen(0); + +let body1 = ''; +let body2 = ''; +let body3 = ''; + +server.on('listening', function() { + // + // Client #1 is assigned Parser #1 + // + const req1 = http.get({ port: this.address().port, path: '/1' }); + req1.on('response', function(res1) { + res1.setEncoding('utf8'); + + res1.on('data', function(chunk) { + body1 += chunk; + }); + + res1.on('end', function() { + // + // Delay execution a little to allow the 'close' event to be processed + // (required to trigger this bug!) + // + setTimeout(function() { + // + // The bug would introduce itself here: Client #2 would be allocated the + // parser that previously belonged to Client #1. But we're not finished + // with Client #1 yet! + // + // At this point, the bug would manifest itself and crash because the + // internal state of the parser was no longer valid for use by Client #1 + // + const req2 = http.get({ port: server.address().port, path: '/2' }); + req2.on('response', function(res2) { + res2.setEncoding('utf8'); + res2.on('data', function(chunk) { body2 += chunk; }); + res2.on('end', function() { + + // + // Just to be really sure we've covered all our bases, execute a + // request using client2. + // + const req3 = http.get({ port: server.address().port, path: '/3' }); + req3.on('response', function(res3) { + res3.setEncoding('utf8'); + res3.on('data', function(chunk) { body3 += chunk; }); + res3.on('end', function() { server.close(); }); + }); + }); + }); + }, 500); + }); + }); +}); + +process.on('exit', function() { + assert.strictEqual(body1_s, body1); + assert.strictEqual(body2_s, body2); + assert.strictEqual(body3_s, body3); +}); diff --git a/test/js/node/test/parallel/test-http-client-race.js b/test/js/node/test/parallel/test-http-client-race.js new file mode 100644 index 0000000000..60b6b49737 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-race.js @@ -0,0 +1,69 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +const body1_s = '1111111111111111'; +const body2_s = '22222'; + +const server = http.createServer(function(req, res) { + const body = url.parse(req.url).pathname === '/1' ? body1_s : body2_s; + res.writeHead(200, { + 'Content-Type': 'text/plain', + 'Content-Length': body.length + }); + res.end(body); +}); +server.listen(0); + +let body1 = ''; +let body2 = ''; + +server.on('listening', function() { + const req1 = http.request({ port: this.address().port, path: '/1' }); + req1.end(); + req1.on('response', function(res1) { + res1.setEncoding('utf8'); + + res1.on('data', function(chunk) { + body1 += chunk; + }); + + res1.on('end', function() { + const req2 = http.request({ port: server.address().port, path: '/2' }); + req2.end(); + req2.on('response', function(res2) { + res2.setEncoding('utf8'); + res2.on('data', function(chunk) { body2 += chunk; }); + res2.on('end', function() { server.close(); }); + }); + }); + }); +}); + +process.on('exit', function() { + assert.strictEqual(body1_s, body1); + assert.strictEqual(body2_s, body2); +}); diff --git a/test/js/node/test/parallel/test-http-client-read-in-error.js b/test/js/node/test/parallel/test-http-client-read-in-error.js new file mode 100644 index 0000000000..5e38e49c1f --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-read-in-error.js @@ -0,0 +1,41 @@ +'use strict'; +const common = require('../common'); +const net = require('net'); +const http = require('http'); + +class Agent extends http.Agent { + createConnection() { + const socket = new net.Socket(); + + socket.on('error', function() { + socket.push('HTTP/1.1 200\r\n\r\n'); + }); + + let onNewListener; + socket.on('newListener', onNewListener = (name) => { + if (name !== 'error') + return; + socket.removeListener('newListener', onNewListener); + + // Let other listeners to be set up too + process.nextTick(() => { + this.breakSocket(socket); + }); + }); + + return socket; + } + + breakSocket(socket) { + socket.emit('error', new Error('Intentional error')); + } +} + +const agent = new Agent(); + +http.request({ + agent +}).once('error', function() { + console.log('ignore'); + this.on('data', common.mustNotCall()); +}); diff --git a/test/js/node/test/parallel/test-http-client-res-destroyed.js b/test/js/node/test/parallel/test-http-client-res-destroyed.js new file mode 100644 index 0000000000..188ab06c15 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-res-destroyed.js @@ -0,0 +1,41 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +{ + const server = http.createServer(common.mustCall((req, res) => { + res.end('asd'); + })); + + server.listen(0, common.mustCall(() => { + http.get({ + port: server.address().port + }, common.mustCall((res) => { + assert.strictEqual(res.destroyed, false); + res.destroy(); + assert.strictEqual(res.destroyed, true); + res.on('close', common.mustCall(() => { + server.close(); + })); + })); + })); +} + +{ + const server = http.createServer(common.mustCall((req, res) => { + res.end('asd'); + })); + + server.listen(0, common.mustCall(() => { + http.get({ + port: server.address().port + }, common.mustCall((res) => { + assert.strictEqual(res.destroyed, false); + res.on('close', common.mustCall(() => { + assert.strictEqual(res.destroyed, true); + server.close(); + })).resume(); + })); + })); +} diff --git a/test/js/node/test/parallel/test-http-client-timeout-agent.js b/test/js/node/test/parallel/test-http-client-timeout-agent.js new file mode 100644 index 0000000000..66c0c0f6b9 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-timeout-agent.js @@ -0,0 +1,94 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); + +let requests_sent = 0; +let requests_done = 0; +const options = { + method: 'GET', + port: undefined, + host: '127.0.0.1', +}; + +const server = http.createServer((req, res) => { + const m = /\/(.*)/.exec(req.url); + const reqid = parseInt(m[1], 10); + if (reqid % 2) { + // Do not reply the request + } else { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write(reqid.toString()); + res.end(); + } +}); + +server.listen(0, options.host, function() { + options.port = this.address().port; + + for (requests_sent = 0; requests_sent < 30; requests_sent += 1) { + options.path = `/${requests_sent}`; + const req = http.request(options); + req.id = requests_sent; + req.on('response', (res) => { + res.on('data', function(data) { + console.log(`res#${this.req.id} data:${data}`); + }); + res.on('end', function(data) { + console.log(`res#${this.req.id} end`); + requests_done += 1; + req.destroy(); + }); + }); + req.on('close', function() { + console.log(`req#${this.id} close`); + }); + req.on('error', function() { + console.log(`req#${this.id} error`); + this.destroy(); + }); + req.setTimeout(50, function() { + console.log(`req#${this.id} timeout`); + this.abort(); + requests_done += 1; + }); + req.end(); + } + + setTimeout(function maybeDone() { + if (requests_done >= requests_sent) { + setTimeout(() => { + server.close(); + }, 100); + } else { + setTimeout(maybeDone, 100); + } + }, 100); +}); + +process.on('exit', () => { + console.error(`done=${requests_done} sent=${requests_sent}`); + // Check that timeout on http request was not called too much + assert.strictEqual(requests_done, requests_sent); +}); diff --git a/test/js/node/test/parallel/test-http-client-timeout-connect-listener.js b/test/js/node/test/parallel/test-http-client-timeout-connect-listener.js new file mode 100644 index 0000000000..ea09aff718 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-timeout-connect-listener.js @@ -0,0 +1,42 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that `ClientRequest.prototype.setTimeout()` does +// not add a listener for the `'connect'` event to the socket if the +// socket is already connected. + +const assert = require('assert'); +const http = require('http'); + +// Maximum allowed value for timeouts. +const timeout = 2 ** 31 - 1; + +const server = http.createServer((req, res) => { + res.end(); +}); + +server.listen(0, common.mustCall(() => { + const agent = new http.Agent({ keepAlive: true, maxSockets: 1 }); + const options = { port: server.address().port, agent: agent }; + + doRequest(options, common.mustCall(() => { + const req = doRequest(options, common.mustCall(() => { + agent.destroy(); + server.close(); + })); + + req.on('socket', common.mustCall((socket) => { + assert.strictEqual(socket.listenerCount('connect'), 0); + })); + })); +})); + +function doRequest(options, callback) { + const req = http.get(options, (res) => { + res.on('end', callback); + res.resume(); + }); + + req.setTimeout(timeout); + return req; +} diff --git a/test/js/node/test/parallel/test-http-client-timeout.js b/test/js/node/test/parallel/test-http-client-timeout.js new file mode 100644 index 0000000000..b83bd1ddf0 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-timeout.js @@ -0,0 +1,54 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); + +const options = { + method: 'GET', + port: undefined, + host: '127.0.0.1', + path: '/' +}; + +const server = http.createServer(function(req, res) { + // This space intentionally left blank +}); + +server.listen(0, options.host, function() { + options.port = this.address().port; + const req = http.request(options, function(res) { + // This space intentionally left blank + }); + req.on('close', function() { + assert.strictEqual(req.destroyed, true); + server.close(); + }); + function destroy() { + req.destroy(); + } + const s = req.setTimeout(1, destroy); + assert.ok(s instanceof http.ClientRequest); + req.on('error', destroy); + req.end(); +}); diff --git a/test/js/node/test/parallel/test-http-client-upload-buf.js b/test/js/node/test/parallel/test-http-client-upload-buf.js new file mode 100644 index 0000000000..1c75612c80 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-upload-buf.js @@ -0,0 +1,64 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const N = 1024; + +const server = http.createServer(common.mustCall(function(req, res) { + assert.strictEqual(req.method, 'POST'); + let bytesReceived = 0; + + req.on('data', function(chunk) { + bytesReceived += chunk.length; + }); + + req.on('end', common.mustCall(function() { + assert.strictEqual(N, bytesReceived); + console.log('request complete from server'); + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('hello\n'); + res.end(); + })); +})); +server.listen(0); + +server.on('listening', common.mustCall(function() { + const req = http.request({ + port: this.address().port, + method: 'POST', + path: '/' + }, common.mustCall(function(res) { + res.setEncoding('utf8'); + res.on('data', function(chunk) { + console.log(chunk); + }); + res.on('end', common.mustCall(function() { + server.close(); + })); + })); + + req.write(Buffer.allocUnsafe(N)); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http-client-upload.js b/test/js/node/test/parallel/test-http-client-upload.js new file mode 100644 index 0000000000..830c37da8e --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-upload.js @@ -0,0 +1,67 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer(common.mustCall(function(req, res) { + assert.strictEqual(req.method, 'POST'); + req.setEncoding('utf8'); + + let sent_body = ''; + + req.on('data', function(chunk) { + console.log(`server got: ${JSON.stringify(chunk)}`); + sent_body += chunk; + }); + + req.on('end', common.mustCall(function() { + assert.strictEqual(sent_body, '1\n2\n3\n'); + console.log('request complete from server'); + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('hello\n'); + res.end(); + })); +})); +server.listen(0); + +server.on('listening', common.mustCall(function() { + const req = http.request({ + port: this.address().port, + method: 'POST', + path: '/' + }, common.mustCall(function(res) { + res.setEncoding('utf8'); + res.on('data', function(chunk) { + console.log(chunk); + }); + res.on('end', common.mustCall(function() { + server.close(); + })); + })); + + req.write('1\n'); + req.write('2\n'); + req.write('3\n'); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http-client-with-create-connection.js b/test/js/node/test/parallel/test-http-client-with-create-connection.js new file mode 100644 index 0000000000..5c99de6c49 --- /dev/null +++ b/test/js/node/test/parallel/test-http-client-with-create-connection.js @@ -0,0 +1,55 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); +const net = require('net'); +const tmpdir = require('../common/tmpdir'); + +tmpdir.refresh(); + +let count = 0; +let server1; +let server2; + +function request(options) { + count++; + http.get({ + ...options, + createConnection: (...args) => { + return net.connect(...args); + } + }, (res) => { + res.resume(); + res.on('end', () => { + if (--count === 0) { + server1.close(); + server2.close(); + } + }); + }); +} + +server1 = http.createServer((req, res) => { + res.end('ok'); +}).listen(common.PIPE, () => { + server2 = http.createServer((req, res) => { + res.end('ok'); + }).listen(() => { + request({ + path: '/', + socketPath: common.PIPE, + }); + + request({ + socketPath: common.PIPE, + }); + + request({ + path: '/', + port: server2.address().port, + }); + + request({ + port: server2.address().port, + }); + }); +}); diff --git a/test/js/node/test/parallel/test-http-contentLength0.js b/test/js/node/test/parallel/test-http-contentLength0.js new file mode 100644 index 0000000000..975e2abe88 --- /dev/null +++ b/test/js/node/test/parallel/test-http-contentLength0.js @@ -0,0 +1,44 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const http = require('http'); + +// Simple test of Node's HTTP Client choking on a response +// with a 'Content-Length: 0 ' response header. +// I.E. a space character after the 'Content-Length' throws an `error` event. + + +const s = http.createServer(function(req, res) { + res.writeHead(200, { 'Content-Length': '0 ' }); + res.end(); +}); +s.listen(0, function() { + + const request = http.request({ port: this.address().port }, (response) => { + console.log(`STATUS: ${response.statusCode}`); + s.close(); + response.resume(); + }); + + request.end(); +}); diff --git a/test/js/node/test/parallel/test-http-date-header.js b/test/js/node/test/parallel/test-http-date-header.js new file mode 100644 index 0000000000..169af2bf2a --- /dev/null +++ b/test/js/node/test/parallel/test-http-date-header.js @@ -0,0 +1,55 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); + +const testResBody = 'other stuff!\n'; + +const server = http.createServer((req, res) => { + assert.ok(!('date' in req.headers), + 'Request headers contained a Date.'); + res.writeHead(200, { + 'Content-Type': 'text/plain' + }); + res.end(testResBody); +}); +server.listen(0); + + +server.addListener('listening', () => { + const options = { + port: server.address().port, + path: '/', + method: 'GET' + }; + const req = http.request(options, (res) => { + assert.ok('date' in res.headers, + 'Response headers didn\'t contain a Date.'); + res.addListener('end', () => { + server.close(); + }); + res.resume(); + }); + req.end(); +}); diff --git a/test/js/node/test/parallel/test-http-decoded-auth.js b/test/js/node/test/parallel/test-http-decoded-auth.js new file mode 100644 index 0000000000..7b09f47cc7 --- /dev/null +++ b/test/js/node/test/parallel/test-http-decoded-auth.js @@ -0,0 +1,48 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); + +const testCases = [ + { + username: 'test@test"', + password: '123456^', + expected: 'dGVzdEB0ZXN0IjoxMjM0NTZe' + }, + { + username: 'test%40test', + password: '123456', + expected: 'dGVzdEB0ZXN0OjEyMzQ1Ng==' + }, + { + username: 'not%3Agood', + password: 'god', + expected: 'bm90Omdvb2Q6Z29k' + }, + { + username: 'not%22good', + password: 'g%5Eod', + expected: 'bm90Imdvb2Q6Z15vZA==' + }, + { + username: 'test1234::::', + password: 'mypass', + expected: 'dGVzdDEyMzQ6Ojo6Om15cGFzcw==' + }, +]; + +for (const testCase of testCases) { + const server = http.createServer(function(request, response) { + // The correct authorization header is be passed + assert.strictEqual(request.headers.authorization, `Basic ${testCase.expected}`); + response.writeHead(200, {}); + response.end('ok'); + server.close(); + }); + + server.listen(0, function() { + // make the request + const url = new URL(`http://${testCase.username}:${testCase.password}@localhost:${this.address().port}`); + http.request(url).end(); + }); +} diff --git a/test/js/node/test/parallel/test-http-default-encoding.js b/test/js/node/test/parallel/test-http-default-encoding.js new file mode 100644 index 0000000000..0c0de0808a --- /dev/null +++ b/test/js/node/test/parallel/test-http-default-encoding.js @@ -0,0 +1,58 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); + +const expected = 'This is a unicode text: سلام'; +let result = ''; + +const server = http.Server((req, res) => { + req.setEncoding('utf8'); + req.on('data', (chunk) => { + result += chunk; + }).on('end', () => { + res.writeHead(200); + res.end('hello world\n'); + server.close(); + }); + +}); + +server.listen(0, function() { + http.request({ + port: this.address().port, + path: '/', + method: 'POST' + }, (res) => { + console.log(res.statusCode); + res.resume(); + }).on('error', (e) => { + console.log(e.message); + process.exit(1); + }).end(expected); +}); + +process.on('exit', () => { + assert.strictEqual(result, expected); +}); diff --git a/test/js/node/test/parallel/test-http-end-throw-socket-handling.js b/test/js/node/test/parallel/test-http-end-throw-socket-handling.js new file mode 100644 index 0000000000..956096270e --- /dev/null +++ b/test/js/node/test/parallel/test-http-end-throw-socket-handling.js @@ -0,0 +1,53 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const Countdown = require('../common/countdown'); + +// Make sure that throwing in 'end' handler doesn't lock +// up the socket forever. +// +// This is NOT a good way to handle errors in general, but all +// the same, we should not be so brittle and easily broken. + +const http = require('http'); +const countdown = new Countdown(10, () => server.close()); + +const server = http.createServer((req, res) => { + countdown.dec(); + res.end('ok'); +}); + +server.listen(0, common.mustCall(() => { + for (let i = 0; i < 10; i++) { + const options = { port: server.address().port }; + const req = http.request(options, (res) => { + res.resume(); + res.on('end', common.mustCall(() => { + throw new Error('gleep glorp'); + })); + }); + req.end(); + } +})); + +process.on('uncaughtException', common.mustCall(10)); diff --git a/test/js/node/test/parallel/test-http-eof-on-connect.js b/test/js/node/test/parallel/test-http-eof-on-connect.js new file mode 100644 index 0000000000..5e885bb91a --- /dev/null +++ b/test/js/node/test/parallel/test-http-eof-on-connect.js @@ -0,0 +1,41 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +const net = require('net'); +const http = require('http'); + +// This is a regression test for https://github.com/joyent/node/issues/44 +// It is separate from test-http-malformed-request.js because it is only +// reproducible on the first packet on the first connection to a server. + +const server = http.createServer(common.mustNotCall()); +server.listen(0); + +server.on('listening', function() { + net.createConnection(this.address().port).on('connect', function() { + this.destroy(); + }).on('close', function() { + server.close(); + }); +}); diff --git a/test/js/node/test/parallel/test-http-extra-response.js b/test/js/node/test/parallel/test-http-extra-response.js new file mode 100644 index 0000000000..6d1a770487 --- /dev/null +++ b/test/js/node/test/parallel/test-http-extra-response.js @@ -0,0 +1,80 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const net = require('net'); + +// If an HTTP server is broken and sends data after the end of the response, +// node should ignore it and drop the connection. +// Demos this bug: https://github.com/joyent/node/issues/680 + +const body = 'hello world\r\n'; +const fullResponse = + 'HTTP/1.1 500 Internal Server Error\r\n' + + `Content-Length: ${body.length}\r\n` + + 'Content-Type: text/plain\r\n' + + 'Date: Fri + 18 Feb 2011 06:22:45 GMT\r\n' + + 'Host: 10.20.149.2\r\n' + + 'Access-Control-Allow-Credentials: true\r\n' + + 'Server: badly broken/0.1 (OS NAME)\r\n' + + '\r\n' + + body; + +const server = net.createServer(function(socket) { + let postBody = ''; + + socket.setEncoding('utf8'); + + socket.on('data', function(chunk) { + postBody += chunk; + + if (postBody.includes('\r\n')) { + socket.write(fullResponse); + socket.end(fullResponse); + } + }); + + socket.on('error', function(err) { + assert.strictEqual(err.code, 'ECONNRESET'); + }); +}); + + +server.listen(0, common.mustCall(function() { + http.get({ port: this.address().port }, common.mustCall(function(res) { + let buffer = ''; + console.log(`Got res code: ${res.statusCode}`); + + res.setEncoding('utf8'); + res.on('data', function(chunk) { + buffer += chunk; + }); + + res.on('end', common.mustCall(function() { + console.log(`Response ended, read ${buffer.length} bytes`); + assert.strictEqual(body, buffer); + server.close(); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http-full-response.js b/test/js/node/test/parallel/test-http-full-response.js new file mode 100644 index 0000000000..d08e091ebd --- /dev/null +++ b/test/js/node/test/parallel/test-http-full-response.js @@ -0,0 +1,81 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +// This test requires the program 'ab' +const http = require('http'); +const exec = require('child_process').exec; + +const bodyLength = 12345; + +const body = 'c'.repeat(bodyLength); + +const server = http.createServer(function(req, res) { + res.writeHead(200, { + 'Content-Length': bodyLength, + 'Content-Type': 'text/plain' + }); + res.end(body); +}); + +function runAb(opts, callback) { + const command = `ab ${opts} http://127.0.0.1:${server.address().port}/`; + exec(command, function(err, stdout, stderr) { + if (err) { + if (/ab|apr/i.test(stderr)) { + common.printSkipMessage(`problem spawning \`ab\`.\n${stderr}`); + process.reallyExit(0); + } + throw err; + } + + let m = /Document Length:\s*(\d+) bytes/i.exec(stdout); + const documentLength = parseInt(m[1]); + + m = /Complete requests:\s*(\d+)/i.exec(stdout); + const completeRequests = parseInt(m[1]); + + m = /HTML transferred:\s*(\d+) bytes/i.exec(stdout); + const htmlTransferred = parseInt(m[1]); + + assert.strictEqual(bodyLength, documentLength); + assert.strictEqual(completeRequests * documentLength, htmlTransferred); + + if (callback) callback(); + }); +} + +server.listen(0, common.mustCall(function() { + runAb('-c 1 -n 10', common.mustCall(function() { + console.log('-c 1 -n 10 okay'); + + runAb('-c 1 -n 100', common.mustCall(function() { + console.log('-c 1 -n 100 okay'); + + runAb('-c 1 -n 1000', common.mustCall(function() { + console.log('-c 1 -n 1000 okay'); + server.close(); + })); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http-get-pipeline-problem.js b/test/js/node/test/parallel/test-http-get-pipeline-problem.js new file mode 100644 index 0000000000..b8b11e7e77 --- /dev/null +++ b/test/js/node/test/parallel/test-http-get-pipeline-problem.js @@ -0,0 +1,100 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// In previous versions of Node.js (e.g., 0.6.0), this sort of thing would halt +// after http.globalAgent.maxSockets number of files. +// See https://groups.google.com/forum/#!topic/nodejs-dev/V5fB69hFa9o +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const http = require('http'); +const fs = require('fs'); +const Countdown = require('../common/countdown'); + +http.globalAgent.maxSockets = 1; + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const image = fixtures.readSync('/person.jpg'); + +console.log(`image.length = ${image.length}`); + +const total = 10; +const responseCountdown = new Countdown(total, common.mustCall(() => { + checkFiles(); + server.close(); +})); + +const server = http.Server(function(req, res) { + setTimeout(function() { + res.writeHead(200, { + 'content-type': 'image/jpeg', + 'connection': 'close', + 'content-length': image.length + }); + res.end(image); + }, 1); +}); + + +server.listen(0, function() { + for (let i = 0; i < total; i++) { + (function() { + const x = i; + + const opts = { + port: server.address().port, + headers: { connection: 'close' } + }; + + http.get(opts, function(res) { + console.error(`recv ${x}`); + const s = fs.createWriteStream(`${tmpdir.path}/${x}.jpg`); + res.pipe(s); + + s.on('finish', function() { + console.error(`done ${x}`); + responseCountdown.dec(); + }); + }).on('error', function(e) { + console.error('error! ', e.message); + throw e; + }); + })(); + } +}); + +function checkFiles() { + // Should see 1.jpg, 2.jpg, ..., 100.jpg in tmpDir + const files = fs.readdirSync(tmpdir.path); + assert(total <= files.length); + + for (let i = 0; i < total; i++) { + const fn = `${i}.jpg`; + assert.ok(files.includes(fn), `couldn't find '${fn}'`); + const stat = fs.statSync(`${tmpdir.path}/${fn}`); + assert.strictEqual( + image.length, stat.size, + `size doesn't match on '${fn}'. Got ${stat.size} bytes`); + } +} diff --git a/test/js/node/test/parallel/test-http-head-request.js b/test/js/node/test/parallel/test-http-head-request.js new file mode 100644 index 0000000000..26d490d357 --- /dev/null +++ b/test/js/node/test/parallel/test-http-head-request.js @@ -0,0 +1,57 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); + +const body = 'hello world\n'; + +function test(headers) { + const server = http.createServer(function(req, res) { + console.error('req: %s headers: %j', req.method, headers); + res.writeHead(200, headers); + res.end(); + server.close(); + }); + + server.listen(0, common.mustCall(function() { + const request = http.request({ + port: this.address().port, + method: 'HEAD', + path: '/' + }, common.mustCall(function(response) { + console.error('response start'); + response.on('end', common.mustCall(function() { + console.error('response end'); + })); + response.resume(); + })); + request.end(); + })); +} + +test({ + 'Transfer-Encoding': 'chunked' +}); +test({ + 'Content-Length': body.length +}); diff --git a/test/js/node/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js b/test/js/node/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js new file mode 100644 index 0000000000..5ebd9f8a90 --- /dev/null +++ b/test/js/node/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); + +// This test is to make sure that when the HTTP server +// responds to a HEAD request with data to res.end, +// it does not send any body but the response is sent +// anyway. + +const server = http.createServer(function(req, res) { + res.end('FAIL'); // broken: sends FAIL from hot path. +}); +server.listen(0); + +server.on('listening', common.mustCall(function() { + const req = http.request({ + port: this.address().port, + method: 'HEAD', + path: '/' + }, common.mustCall(function(res) { + res.on('end', common.mustCall(function() { + server.close(); + })); + res.resume(); + })); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http-head-response-has-no-body-end.js b/test/js/node/test/parallel/test-http-head-response-has-no-body-end.js new file mode 100644 index 0000000000..824a1bafe3 --- /dev/null +++ b/test/js/node/test/parallel/test-http-head-response-has-no-body-end.js @@ -0,0 +1,48 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); + +// This test is to make sure that when the HTTP server +// responds to a HEAD request with data to res.end, +// it does not send any body. + +const server = http.createServer(function(req, res) { + res.writeHead(200); + res.end('FAIL'); // broken: sends FAIL from hot path. +}); +server.listen(0); + +server.on('listening', common.mustCall(function() { + const req = http.request({ + port: this.address().port, + method: 'HEAD', + path: '/' + }, common.mustCall(function(res) { + res.on('end', common.mustCall(function() { + server.close(); + })); + res.resume(); + })); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http-head-response-has-no-body.js b/test/js/node/test/parallel/test-http-head-response-has-no-body.js new file mode 100644 index 0000000000..bd96d7161b --- /dev/null +++ b/test/js/node/test/parallel/test-http-head-response-has-no-body.js @@ -0,0 +1,48 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); + +// This test is to make sure that when the HTTP server +// responds to a HEAD request, it does not send any body. +// In this case it was sending '0\r\n\r\n' + +const server = http.createServer(function(req, res) { + res.writeHead(200); // broken: defaults to TE chunked + res.end(); +}); +server.listen(0); + +server.on('listening', common.mustCall(function() { + const req = http.request({ + port: this.address().port, + method: 'HEAD', + path: '/' + }, common.mustCall(function(res) { + res.on('end', common.mustCall(function() { + server.close(); + })); + res.resume(); + })); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http-header-owstext.js b/test/js/node/test/parallel/test-http-header-owstext.js new file mode 100644 index 0000000000..bc094137a2 --- /dev/null +++ b/test/js/node/test/parallel/test-http-header-owstext.js @@ -0,0 +1,49 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that the http-parser strips leading and trailing OWS from +// header values. It sends the header values in chunks to force the parser to +// build the string up through multiple calls to on_header_value(). + +const assert = require('assert'); +const http = require('http'); +const net = require('net'); + +function check(hdr, snd, rcv) { + const server = http.createServer(common.mustCall((req, res) => { + assert.strictEqual(req.headers[hdr], rcv); + req.pipe(res); + })); + + server.listen(0, common.mustCall(function() { + const client = net.connect(this.address().port, start); + function start() { + client.write('GET / HTTP/1.1\r\n' + hdr + ':', drain); + } + + function drain() { + if (snd.length === 0) { + return client.write('\r\nConnection: close\r\n\r\n'); + } + client.write(snd.shift(), drain); + } + + const bufs = []; + client.on('data', function(chunk) { + bufs.push(chunk); + }); + client.on('end', common.mustCall(function() { + const head = Buffer.concat(bufs) + .toString('latin1') + .split('\r\n')[0]; + assert.strictEqual(head, 'HTTP/1.1 200 OK'); + server.close(); + })); + })); +} + +check('host', [' \t foo.com\t'], 'foo.com'); +check('host', [' \t foo\tcom\t'], 'foo\tcom'); +check('host', [' \t', ' ', ' foo.com\t', '\t '], 'foo.com'); +check('host', [' \t', ' \t'.repeat(100), '\t '], ''); +check('host', [' \t', ' - - - - ', '\t '], '- - - -'); diff --git a/test/js/node/test/parallel/test-http-highwatermark.js b/test/js/node/test/parallel/test-http-highwatermark.js new file mode 100644 index 0000000000..79d9c46a55 --- /dev/null +++ b/test/js/node/test/parallel/test-http-highwatermark.js @@ -0,0 +1,52 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); +const http = require('http'); + +// These test cases to check socketOnDrain where needPause becomes false. +// When send large response enough to exceed highWaterMark, it expect the socket +// to be paused and res.write would be failed. +// And it should be resumed when outgoingData falls below highWaterMark. + +let requestReceived = 0; + +const server = http.createServer(function(req, res) { + const id = ++requestReceived; + const enoughToDrain = req.connection.writableHighWaterMark; + const body = 'x'.repeat(enoughToDrain * 100); + + if (id === 1) { + // Case of needParse = false + req.connection.once('pause', common.mustCall(() => { + assert(req.connection._paused, '_paused must be true because it exceeds' + + 'highWaterMark by second request'); + })); + } else { + // Case of needParse = true + const resume = req.connection.parser.resume.bind(req.connection.parser); + req.connection.parser.resume = common.mustCall((...args) => { + const paused = req.connection._paused; + assert(!paused, '_paused must be false because it become false by ' + + 'socketOnDrain when outgoingData falls below ' + + 'highWaterMark'); + return resume(...args); + }); + } + assert(!res.write(body), 'res.write must return false because it will ' + + 'exceed highWaterMark on this call'); + res.end(); +}).on('listening', () => { + const c = net.createConnection(server.address().port, () => { + c.write('GET / HTTP/1.1\r\n\r\n'); + c.write('GET / HTTP/1.1\r\n\r\n', + () => setImmediate(() => c.resume())); + c.end(); + }); + + c.on('end', () => { + server.close(); + }); +}); + +server.listen(0); diff --git a/test/js/node/test/parallel/test-http-host-headers.js b/test/js/node/test/parallel/test-http-host-headers.js new file mode 100644 index 0000000000..97d200ade1 --- /dev/null +++ b/test/js/node/test/parallel/test-http-host-headers.js @@ -0,0 +1,96 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); +const httpServer = http.createServer(reqHandler); + +function reqHandler(req, res) { + if (req.url === '/setHostFalse5') { + assert.strictEqual(req.headers.host, undefined); + } else { + assert.strictEqual( + req.headers.host, `localhost:${this.address().port}`, + `Wrong host header for req[${req.url}]: ${req.headers.host}`); + } + res.writeHead(200, {}); + res.end('ok'); +} + +testHttp(); + +function testHttp() { + + let counter = 0; + + function cb(res) { + counter--; + if (counter === 0) { + httpServer.close(); + } + res.resume(); + } + + httpServer.listen(0, (er) => { + assert.ifError(er); + http.get({ + method: 'GET', + path: `/${counter++}`, + host: 'localhost', + port: httpServer.address().port, + rejectUnauthorized: false + }, cb).on('error', common.mustNotCall()); + + http.request({ + method: 'GET', + path: `/${counter++}`, + host: 'localhost', + port: httpServer.address().port, + rejectUnauthorized: false + }, cb).on('error', common.mustNotCall()).end(); + + http.request({ + method: 'POST', + path: `/${counter++}`, + host: 'localhost', + port: httpServer.address().port, + rejectUnauthorized: false + }, cb).on('error', common.mustNotCall()).end(); + + http.request({ + method: 'PUT', + path: `/${counter++}`, + host: 'localhost', + port: httpServer.address().port, + rejectUnauthorized: false + }, cb).on('error', common.mustNotCall()).end(); + + http.request({ + method: 'DELETE', + path: `/${counter++}`, + host: 'localhost', + port: httpServer.address().port, + rejectUnauthorized: false + }, cb).on('error', common.mustNotCall()).end(); + }); +} diff --git a/test/js/node/test/parallel/test-http-keep-alive-timeout-custom.js b/test/js/node/test/parallel/test-http-keep-alive-timeout-custom.js new file mode 100644 index 0000000000..a74aa5a212 --- /dev/null +++ b/test/js/node/test/parallel/test-http-keep-alive-timeout-custom.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); + +const server = http.createServer(common.mustCall((req, res) => { + const body = 'hello world\n'; + + res.writeHead(200, { + 'Content-Length': body.length, + 'Keep-Alive': 'timeout=50' + }); + res.write(body); + res.end(); +})); +server.keepAliveTimeout = 12010; + +const agent = new http.Agent({ maxSockets: 1, keepAlive: true }); + +server.listen(0, common.mustCall(function() { + http.get({ + path: '/', port: this.address().port, agent: agent + }, common.mustCall((response) => { + response.resume(); + assert.strictEqual( + response.headers['keep-alive'], 'timeout=50'); + server.close(); + agent.destroy(); + })); +})); diff --git a/test/js/node/test/parallel/test-http-localaddress-bind-error.js b/test/js/node/test/parallel/test-http-localaddress-bind-error.js new file mode 100644 index 0000000000..d4bd72bae1 --- /dev/null +++ b/test/js/node/test/parallel/test-http-localaddress-bind-error.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const invalidLocalAddress = '1.2.3.4'; + +const server = http.createServer(function(req, res) { + console.log(`Connect from: ${req.connection.remoteAddress}`); + + req.on('end', function() { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end(`You are from: ${req.connection.remoteAddress}`); + }); + req.resume(); +}); + +server.listen(0, '127.0.0.1', common.mustCall(function() { + http.request({ + host: 'localhost', + port: this.address().port, + path: '/', + method: 'GET', + localAddress: invalidLocalAddress + }, function(res) { + assert.fail('unexpectedly got response from server'); + }).on('error', common.mustCall(function(e) { + console.log(`client got error: ${e.message}`); + server.close(); + })).end(); +})); diff --git a/test/js/node/test/parallel/test-http-malformed-request.js b/test/js/node/test/parallel/test-http-malformed-request.js new file mode 100644 index 0000000000..1f2fecc117 --- /dev/null +++ b/test/js/node/test/parallel/test-http-malformed-request.js @@ -0,0 +1,47 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); +const http = require('http'); +const url = require('url'); + +// Make sure no exceptions are thrown when receiving malformed HTTP +// requests. +const server = http.createServer(common.mustCall((req, res) => { + console.log(`req: ${JSON.stringify(url.parse(req.url))}`); + + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('Hello World'); + res.end(); + + server.close(); +})); +server.listen(0); + +server.on('listening', function() { + const c = net.createConnection(this.address().port); + c.on('connect', function() { + c.write('GET /hello?foo=%99bar HTTP/1.1\r\nHost: example.com\r\n\r\n'); + c.end(); + }); +}); diff --git a/test/js/node/test/parallel/test-http-missing-header-separator-cr.js b/test/js/node/test/parallel/test-http-missing-header-separator-cr.js new file mode 100644 index 0000000000..1129ec4ed9 --- /dev/null +++ b/test/js/node/test/parallel/test-http-missing-header-separator-cr.js @@ -0,0 +1,83 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const http = require('http'); +const net = require('net'); + +function serverHandler(server, msg) { + const client = net.connect(server.address().port, 'localhost'); + + let response = ''; + + client.on('data', common.mustCall((chunk) => { + response += chunk; + })); + + client.setEncoding('utf8'); + client.on('error', common.mustNotCall()); + client.on('end', common.mustCall(() => { + assert.strictEqual( + response, + 'HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n' + ); + server.close(); + })); + client.write(msg); + client.resume(); +} + +{ + const msg = [ + 'GET / HTTP/1.1', + 'Host: localhost', + 'Dummy: x\nContent-Length: 23', + '', + 'GET / HTTP/1.1', + 'Dummy: GET /admin HTTP/1.1', + 'Host: localhost', + '', + '', + ].join('\r\n'); + + const server = http.createServer(common.mustNotCall()); + + server.listen(0, common.mustSucceed(serverHandler.bind(null, server, msg))); +} + +{ + const msg = [ + 'POST / HTTP/1.1', + 'Host: localhost', + 'x:x\nTransfer-Encoding: chunked', + '', + '1', + 'A', + '0', + '', + '', + ].join('\r\n'); + + const server = http.createServer(common.mustNotCall()); + + server.listen(0, common.mustSucceed(serverHandler.bind(null, server, msg))); +} + +{ + const msg = [ + 'POST / HTTP/1.1', + 'Host: localhost', + 'x:\nTransfer-Encoding: chunked', + '', + '1', + 'A', + '0', + '', + '', + ].join('\r\n'); + + const server = http.createServer(common.mustNotCall()); + + server.listen(0, common.mustSucceed(serverHandler.bind(null, server, msg))); +} diff --git a/test/js/node/test/parallel/test-http-no-content-length.js b/test/js/node/test/parallel/test-http-no-content-length.js new file mode 100644 index 0000000000..a3a51c015e --- /dev/null +++ b/test/js/node/test/parallel/test-http-no-content-length.js @@ -0,0 +1,44 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); +const http = require('http'); + +const server = net.createServer(function(socket) { + // Neither Content-Length nor Connection + socket.end('HTTP/1.1 200 ok\r\n\r\nHello'); +}).listen(0, common.mustCall(function() { + http.get({ port: this.address().port }, common.mustCall(function(res) { + let body = ''; + + res.setEncoding('utf8'); + res.on('data', function(chunk) { + body += chunk; + }); + res.on('end', common.mustCall(function() { + assert.strictEqual(body, 'Hello'); + server.close(); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http-outgoing-destroy.js b/test/js/node/test/parallel/test-http-outgoing-destroy.js new file mode 100644 index 0000000000..47d5e948ab --- /dev/null +++ b/test/js/node/test/parallel/test-http-outgoing-destroy.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const http = require('http'); +const OutgoingMessage = http.OutgoingMessage; + +{ + const msg = new OutgoingMessage(); + assert.strictEqual(msg.destroyed, false); + msg.destroy(); + assert.strictEqual(msg.destroyed, true); + msg.write('asd', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED'); + })); + msg.on('error', common.mustNotCall()); +} diff --git a/test/js/node/test/parallel/test-http-outgoing-end-types.js b/test/js/node/test/parallel/test-http-outgoing-end-types.js new file mode 100644 index 0000000000..20b443bff2 --- /dev/null +++ b/test/js/node/test/parallel/test-http-outgoing-end-types.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const httpServer = http.createServer(common.mustCall(function(req, res) { + httpServer.close(); + assert.throws(() => { + res.end(['Throws.']); + }, { + code: 'ERR_INVALID_ARG_TYPE' + }); + res.end(); +})); + +httpServer.listen(0, common.mustCall(function() { + http.get({ port: this.address().port }); +})); diff --git a/test/js/node/test/parallel/test-http-outgoing-finish.js b/test/js/node/test/parallel/test-http-outgoing-finish.js new file mode 100644 index 0000000000..0f71cccdf8 --- /dev/null +++ b/test/js/node/test/parallel/test-http-outgoing-finish.js @@ -0,0 +1,76 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const http = require('http'); + +http.createServer(function(req, res) { + req.resume(); + req.on('end', function() { + write(res); + }); + this.close(); +}).listen(0, function() { + const req = http.request({ + port: this.address().port, + method: 'PUT' + }); + write(req); + req.on('response', function(res) { + res.resume(); + }); +}); + +const buf = Buffer.alloc(1024 * 16, 'x'); +function write(out) { + const name = out.constructor.name; + let finishEvent = false; + let endCb = false; + + // First, write until it gets some backpressure + while (out.write(buf, common.mustSucceed())); + + // Now end, and make sure that we don't get the 'finish' event + // before the tick where the cb gets called. We give it until + // nextTick because this is added as a listener before the endcb + // is registered. The order is not what we're testing here, just + // that 'finish' isn't emitted until the stream is fully flushed. + out.on('finish', function() { + finishEvent = true; + console.error(`${name} finish event`); + process.nextTick(function() { + assert(endCb, `${name} got finish event before endcb!`); + console.log(`ok - ${name} finishEvent`); + }); + }); + + out.end(buf, common.mustCall(function() { + endCb = true; + console.error(`${name} endCb`); + process.nextTick(function() { + assert(finishEvent, `${name} got endCb event before finishEvent!`); + console.log(`ok - ${name} endCb`); + }); + })); +} diff --git a/test/js/node/test/parallel/test-http-outgoing-finished.js b/test/js/node/test/parallel/test-http-outgoing-finished.js new file mode 100644 index 0000000000..7da1b7429a --- /dev/null +++ b/test/js/node/test/parallel/test-http-outgoing-finished.js @@ -0,0 +1,32 @@ +'use strict'; +const common = require('../common'); +const { finished } = require('stream'); + +const http = require('http'); +const assert = require('assert'); + +const server = http.createServer(function(req, res) { + let closed = false; + res + .on('close', common.mustCall(() => { + closed = true; + finished(res, common.mustCall(() => { + server.close(); + })); + })) + .end(); + finished(res, common.mustCall(() => { + assert.strictEqual(closed, true); + })); + +}).listen(0, function() { + http + .request({ + port: this.address().port, + method: 'GET' + }) + .on('response', function(res) { + res.resume(); + }) + .end(); +}); diff --git a/test/js/node/test/parallel/test-http-outgoing-writableFinished.js b/test/js/node/test/parallel/test-http-outgoing-writableFinished.js new file mode 100644 index 0000000000..6f84d91e71 --- /dev/null +++ b/test/js/node/test/parallel/test-http-outgoing-writableFinished.js @@ -0,0 +1,32 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer(common.mustCall(function(req, res) { + assert.strictEqual(res.writableFinished, false); + res + .on('finish', common.mustCall(() => { + assert.strictEqual(res.writableFinished, true); + server.close(); + })) + .end(); +})); + +server.listen(0); + +server.on('listening', common.mustCall(function() { + const clientRequest = http.request({ + port: server.address().port, + method: 'GET', + path: '/' + }); + + assert.strictEqual(clientRequest.writableFinished, false); + clientRequest + .on('finish', common.mustCall(() => { + assert.strictEqual(clientRequest.writableFinished, true); + })) + .end(); + assert.strictEqual(clientRequest.writableFinished, false); +})); diff --git a/test/js/node/test/parallel/test-http-outgoing-write-types.js b/test/js/node/test/parallel/test-http-outgoing-write-types.js new file mode 100644 index 0000000000..6257b87eea --- /dev/null +++ b/test/js/node/test/parallel/test-http-outgoing-write-types.js @@ -0,0 +1,24 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const httpServer = http.createServer(common.mustCall(function(req, res) { + httpServer.close(); + assert.throws(() => { + res.write(['Throws.']); + }, { + code: 'ERR_INVALID_ARG_TYPE' + }); + // should not throw + res.write('1a2b3c'); + // should not throw + res.write(new Uint8Array(1024)); + // should not throw + res.write(Buffer.from('1'.repeat(1024))); + res.end(); +})); + +httpServer.listen(0, common.mustCall(function() { + http.get({ port: this.address().port }); +})); diff --git a/test/js/node/test/parallel/test-http-pause-no-dump.js b/test/js/node/test/parallel/test-http-pause-no-dump.js new file mode 100644 index 0000000000..b794634c48 --- /dev/null +++ b/test/js/node/test/parallel/test-http-pause-no-dump.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer(common.mustCall(function(req, res) { + req.once('data', common.mustCall(() => { + req.pause(); + res.writeHead(200); + res.end(); + res.on('finish', common.mustCall(() => { + assert(!req._dumped); + })); + })); +})); +server.listen(0); + +server.on('listening', common.mustCall(function() { + const req = http.request({ + port: this.address().port, + method: 'POST', + path: '/' + }, common.mustCall(function(res) { + assert.strictEqual(res.statusCode, 200); + res.resume(); + res.on('end', common.mustCall(() => { + server.close(); + })); + })); + + req.end(Buffer.allocUnsafe(1024)); +})); diff --git a/test/js/node/test/parallel/test-http-pause-resume-one-end.js b/test/js/node/test/parallel/test-http-pause-resume-one-end.js new file mode 100644 index 0000000000..34bf3be478 --- /dev/null +++ b/test/js/node/test/parallel/test-http-pause-resume-one-end.js @@ -0,0 +1,55 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); + +const server = http.Server(function(req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('Hello World\n'); + server.close(); +}); + +server.listen(0, common.mustCall(function() { + const opts = { + port: this.address().port, + headers: { connection: 'close' } + }; + + http.get(opts, common.mustCall(function(res) { + res.on('data', common.mustCall(function() { + res.pause(); + setImmediate(function() { + res.resume(); + }); + })); + + res.on('end', common.mustCall(() => { + assert.strictEqual(res.destroyed, false); + })); + assert.strictEqual(res.destroyed, false); + res.on('close', common.mustCall(() => { + assert.strictEqual(res.destroyed, true); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http-pause.js b/test/js/node/test/parallel/test-http-pause.js new file mode 100644 index 0000000000..555eece2e3 --- /dev/null +++ b/test/js/node/test/parallel/test-http-pause.js @@ -0,0 +1,78 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); + +const expectedServer = 'Request Body from Client'; +let resultServer = ''; +const expectedClient = 'Response Body from Server'; +let resultClient = ''; + +const server = http.createServer((req, res) => { + console.error('pause server request'); + req.pause(); + setTimeout(() => { + console.error('resume server request'); + req.resume(); + req.setEncoding('utf8'); + req.on('data', (chunk) => { + resultServer += chunk; + }); + req.on('end', () => { + console.error(resultServer); + res.writeHead(200); + res.end(expectedClient); + }); + }, 100); +}); + +server.listen(0, function() { + // Anonymous function rather than arrow function to test `this` value. + assert.strictEqual(this, server); + const req = http.request({ + port: this.address().port, + path: '/', + method: 'POST' + }, (res) => { + console.error('pause client response'); + res.pause(); + setTimeout(() => { + console.error('resume client response'); + res.resume(); + res.on('data', (chunk) => { + resultClient += chunk; + }); + res.on('end', () => { + console.error(resultClient); + server.close(); + }); + }, 100); + }); + req.end(expectedServer); +}); + +process.on('exit', () => { + assert.strictEqual(resultServer, expectedServer); + assert.strictEqual(resultClient, expectedClient); +}); diff --git a/test/js/node/test/parallel/test-http-pipe-fs.js b/test/js/node/test/parallel/test-http-pipe-fs.js new file mode 100644 index 0000000000..b7c1a02918 --- /dev/null +++ b/test/js/node/test/parallel/test-http-pipe-fs.js @@ -0,0 +1,65 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const http = require('http'); +const fs = require('fs'); +const Countdown = require('../common/countdown'); +const NUMBER_OF_STREAMS = 2; + +const countdown = new Countdown(NUMBER_OF_STREAMS, () => server.close()); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const file = tmpdir.resolve('http-pipe-fs-test.txt'); + +const server = http.createServer(common.mustCall(function(req, res) { + const stream = fs.createWriteStream(file); + req.pipe(stream); + stream.on('close', function() { + res.writeHead(200); + res.end(); + }); +}, 2)).listen(0, function() { + http.globalAgent.maxSockets = 1; + + for (let i = 0; i < NUMBER_OF_STREAMS; ++i) { + const req = http.request({ + port: server.address().port, + method: 'POST', + headers: { + 'Content-Length': 5 + } + }, function(res) { + res.on('end', function() { + console.error(`res${i + 1} end`); + countdown.dec(); + }); + res.resume(); + }); + req.on('socket', function(s) { + console.error(`req${i + 1} start`); + }); + req.end('12345'); + } +}); diff --git a/test/js/node/test/parallel/test-http-pipeline-socket-parser-typeerror.js b/test/js/node/test/parallel/test-http-pipeline-socket-parser-typeerror.js new file mode 100644 index 0000000000..e092154b97 --- /dev/null +++ b/test/js/node/test/parallel/test-http-pipeline-socket-parser-typeerror.js @@ -0,0 +1,64 @@ +'use strict'; +require('../common'); + +// This test ensures that Node.js doesn't crash because of a TypeError by +// checking in `connectionListener` that the socket still has the parser. +// https://github.com/nodejs/node/issues/3508 + +const http = require('http'); +const net = require('net'); + +let once = false; +let first = null; +let second = null; + +const chunk = Buffer.alloc(1024, 'X'); + +let size = 0; + +let more; +let done; + +const server = http + .createServer((req, res) => { + if (!once) server.close(); + once = true; + + if (first === null) { + first = res; + return; + } + if (second === null) { + second = res; + res.write(chunk); + } else { + res.end(chunk); + } + size += res.outputSize; + if (size <= req.socket.writableHighWaterMark) { + more(); + return; + } + done(); + }) + .on('upgrade', (req, socket) => { + second.end(chunk, () => { + socket.end(); + }); + first.end('hello'); + }) + .listen(0, () => { + const s = net.connect(server.address().port); + more = () => { + s.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'); + }; + done = () => { + s.write( + 'GET / HTTP/1.1\r\n\r\n' + + 'GET / HTTP/1.1\r\nConnection: upgrade\r\nUpgrade: ws\r\n\r\naaa' + ); + }; + more(); + more(); + s.resume(); + }); diff --git a/test/js/node/test/parallel/test-http-proxy.js b/test/js/node/test/parallel/test-http-proxy.js new file mode 100644 index 0000000000..af3497630b --- /dev/null +++ b/test/js/node/test/parallel/test-http-proxy.js @@ -0,0 +1,107 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +const cookies = [ + 'session_token=; path=/; expires=Sun, 15-Sep-2030 13:48:52 GMT', + 'prefers_open_id=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT', +]; + +const headers = { 'content-type': 'text/plain', + 'set-cookie': cookies, + 'hello': 'world' }; + +const backend = http.createServer(function(req, res) { + console.error('backend request'); + res.writeHead(200, headers); + res.write('hello world\n'); + res.end(); +}); + +const proxy = http.createServer(function(req, res) { + console.error(`proxy req headers: ${JSON.stringify(req.headers)}`); + http.get({ + port: backend.address().port, + path: url.parse(req.url).pathname + }, function(proxy_res) { + + console.error(`proxy res headers: ${JSON.stringify(proxy_res.headers)}`); + + assert.strictEqual(proxy_res.headers.hello, 'world'); + assert.strictEqual(proxy_res.headers['content-type'], 'text/plain'); + assert.deepStrictEqual(proxy_res.headers['set-cookie'], cookies); + + res.writeHead(proxy_res.statusCode, proxy_res.headers); + + proxy_res.on('data', function(chunk) { + res.write(chunk); + }); + + proxy_res.on('end', function() { + res.end(); + console.error('proxy res'); + }); + }); +}); + +let body = ''; + +let nlistening = 0; +function startReq() { + nlistening++; + if (nlistening < 2) return; + + http.get({ + port: proxy.address().port, + path: '/test' + }, function(res) { + console.error('got res'); + assert.strictEqual(res.statusCode, 200); + + assert.strictEqual(res.headers.hello, 'world'); + assert.strictEqual(res.headers['content-type'], 'text/plain'); + assert.deepStrictEqual(res.headers['set-cookie'], cookies); + + res.setEncoding('utf8'); + res.on('data', function(chunk) { body += chunk; }); + res.on('end', function() { + proxy.close(); + backend.close(); + console.error('closed both'); + }); + }); + console.error('client req'); +} + +console.error('listen proxy'); +proxy.listen(0, startReq); + +console.error('listen backend'); +backend.listen(0, startReq); + +process.on('exit', function() { + assert.strictEqual(body, 'hello world\n'); +}); diff --git a/test/js/node/test/parallel/test-http-request-agent.js b/test/js/node/test/parallel/test-http-request-agent.js new file mode 100644 index 0000000000..453ac380d0 --- /dev/null +++ b/test/js/node/test/parallel/test-http-request-agent.js @@ -0,0 +1,40 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const fixtures = require('../common/fixtures'); + +// This test ensures that a http request callback is called when the agent +// option is set. +// See https://github.com/nodejs/node-v0.x-archive/issues/1531 + +const https = require('https'); + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + +const server = https.createServer(options, function(req, res) { + res.writeHead(200); + res.end('hello world\n'); +}); + +server.listen(0, common.mustCall(function() { + console.error('listening'); + https.get({ + agent: false, + path: '/', + port: this.address().port, + rejectUnauthorized: false + }, common.mustCall(function(res) { + console.error(res.statusCode, res.headers); + res.resume(); + server.close(); + })).on('error', function(e) { + console.error(e); + process.exit(1); + }); +})); diff --git a/test/js/node/test/parallel/test-http-request-arguments.js b/test/js/node/test/parallel/test-http-request-arguments.js new file mode 100644 index 0000000000..5cdd514fd5 --- /dev/null +++ b/test/js/node/test/parallel/test-http-request-arguments.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +// Test providing both a url and options, with the options partially +// replacing address and port portions of the URL provided. +{ + const server = http.createServer( + common.mustCall((req, res) => { + assert.strictEqual(req.url, '/testpath'); + res.end(); + server.close(); + }) + ); + server.listen( + 0, + common.mustCall(() => { + http.get( + 'http://example.com/testpath', + { hostname: 'localhost', port: server.address().port }, + common.mustCall((res) => { + res.resume(); + }) + ); + }) + ); +} diff --git a/test/js/node/test/parallel/test-http-request-end-twice.js b/test/js/node/test/parallel/test-http-request-end-twice.js new file mode 100644 index 0000000000..47f08fd6e4 --- /dev/null +++ b/test/js/node/test/parallel/test-http-request-end-twice.js @@ -0,0 +1,39 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.Server(function(req, res) { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('hello world\n'); +}); +server.listen(0, function() { + const req = http.get({ port: this.address().port }, function(res) { + res.on('end', function() { + assert.strictEqual(req.end(), req); + server.close(); + }); + res.resume(); + }); +}); diff --git a/test/js/node/test/parallel/test-http-request-end.js b/test/js/node/test/parallel/test-http-request-end.js new file mode 100644 index 0000000000..6f141fda1e --- /dev/null +++ b/test/js/node/test/parallel/test-http-request-end.js @@ -0,0 +1,60 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const expected = 'Post Body For Test'; +const expectedStatusCode = 200; + +const server = http.Server(function(req, res) { + let result = ''; + + req.setEncoding('utf8'); + req.on('data', function(chunk) { + result += chunk; + }); + + req.on('end', common.mustCall(() => { + assert.strictEqual(result, expected); + res.writeHead(expectedStatusCode); + res.end('hello world\n'); + server.close(); + })); + +}); + +server.listen(0, function() { + const req = http.request({ + port: this.address().port, + path: '/', + method: 'POST' + }, function(res) { + assert.strictEqual(res.statusCode, expectedStatusCode); + res.resume(); + }).on('error', common.mustNotCall()); + + const result = req.end(expected); + + assert.strictEqual(req, result); +}); diff --git a/test/js/node/test/parallel/test-http-request-large-payload.js b/test/js/node/test/parallel/test-http-request-large-payload.js new file mode 100644 index 0000000000..3be100b740 --- /dev/null +++ b/test/js/node/test/parallel/test-http-request-large-payload.js @@ -0,0 +1,26 @@ +'use strict'; +require('../common'); + +// This test ensures Node.js doesn't throw an error when making requests with +// the payload 16kb or more in size. +// https://github.com/nodejs/node/issues/2821 + +const http = require('http'); + +const server = http.createServer(function(req, res) { + res.writeHead(200); + res.end(); + + server.close(); +}); + +server.listen(0, function() { + const req = http.request({ + method: 'POST', + port: this.address().port + }); + + const payload = Buffer.alloc(16390, 'Й'); + req.write(payload); + req.end(); +}); diff --git a/test/js/node/test/parallel/test-http-request-method-delete-payload.js b/test/js/node/test/parallel/test-http-request-method-delete-payload.js new file mode 100644 index 0000000000..03728846df --- /dev/null +++ b/test/js/node/test/parallel/test-http-request-method-delete-payload.js @@ -0,0 +1,30 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const http = require('http'); + +const data = 'PUT / HTTP/1.1\r\n\r\n'; + +const server = http.createServer(common.mustCall(function(req, res) { + req.on('data', function(chunk) { + assert.strictEqual(chunk, Buffer.from(data)); + }); + res.setHeader('Content-Type', 'text/plain'); + for (let i = 0; i < req.rawHeaders.length; i += 2) { + if (req.rawHeaders[i].toLowerCase() === 'host') continue; + if (req.rawHeaders[i].toLowerCase() === 'connection') continue; + res.write(`${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\r\n`); + } + res.end(); +})).unref(); + +server.listen(0, common.mustCall(() => { + const port = server.address().port; + const req = http.request({ method: 'DELETE', port }, function(res) { + res.resume(); + }); + + req.write(data); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http-request-smuggling-content-length.js b/test/js/node/test/parallel/test-http-request-smuggling-content-length.js new file mode 100644 index 0000000000..4ae39b93f4 --- /dev/null +++ b/test/js/node/test/parallel/test-http-request-smuggling-content-length.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); +const net = require('net'); +const assert = require('assert'); + +// Verify that a request with a space before the content length will result +// in a 400 Bad Request. + +const server = http.createServer(common.mustNotCall()); + +server.listen(0, common.mustCall(start)); + +function start() { + const sock = net.connect(server.address().port); + + sock.write('GET / HTTP/1.1\r\nHost: localhost:5000\r\n' + + 'Content-Length : 5\r\n\r\nhello'); + + let body = ''; + sock.setEncoding('utf8'); + sock.on('data', (chunk) => { + body += chunk; + }); + sock.on('end', common.mustCall(function() { + assert.strictEqual(body, 'HTTP/1.1 400 Bad Request\r\n' + + 'Connection: close\r\n\r\n'); + server.close(); + })); +} diff --git a/test/js/node/test/parallel/test-http-res-write-after-end.js b/test/js/node/test/parallel/test-http-res-write-after-end.js new file mode 100644 index 0000000000..285938c8fe --- /dev/null +++ b/test/js/node/test/parallel/test-http-res-write-after-end.js @@ -0,0 +1,45 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.Server(common.mustCall(function(req, res) { + res.on('error', common.expectsError({ + code: 'ERR_STREAM_WRITE_AFTER_END', + name: 'Error' + })); + + res.write('This should write.'); + res.end(); + + const r = res.write('This should raise an error.'); + // Write after end should return false + assert.strictEqual(r, false); +})); + +server.listen(0, function() { + http.get({ port: this.address().port }, function(res) { + server.close(); + }); +}); diff --git a/test/js/node/test/parallel/test-http-res-write-end-dont-take-array.js b/test/js/node/test/parallel/test-http-res-write-end-dont-take-array.js new file mode 100644 index 0000000000..8bebfc14e4 --- /dev/null +++ b/test/js/node/test/parallel/test-http-res-write-end-dont-take-array.js @@ -0,0 +1,73 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer(); + +server.once('request', common.mustCall((req, res) => { + server.on('request', common.mustCall((req, res) => { + res.end(Buffer.from('asdf')); + })); + // `res.write()` should accept `string`. + res.write('string'); + // `res.write()` should accept `buffer`. + res.write(Buffer.from('asdf')); + + const expectedError = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + }; + + // `res.write()` should not accept an Array. + assert.throws( + () => { + res.write(['array']); + }, + expectedError + ); + + // `res.end()` should not accept an Array. + assert.throws( + () => { + res.end(['moo']); + }, + expectedError + ); + + // `res.end()` should accept `string`. + res.end('string'); +})); + +server.listen(0, function() { + // Just make a request, other tests handle responses. + http.get({ port: this.address().port }, (res) => { + res.resume(); + // Do it again to test .end(Buffer); + http.get({ port: server.address().port }, (res) => { + res.resume(); + server.close(); + }); + }); +}); diff --git a/test/js/node/test/parallel/test-http-response-readable.js b/test/js/node/test/parallel/test-http-response-readable.js new file mode 100644 index 0000000000..9ecfbc4ca9 --- /dev/null +++ b/test/js/node/test/parallel/test-http-response-readable.js @@ -0,0 +1,41 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); + +const testServer = new http.Server(function(req, res) { + res.writeHead(200); + res.end('Hello world'); +}); + +testServer.listen(0, function() { + http.get({ port: this.address().port }, function(res) { + assert.strictEqual(res.readable, true); + res.on('end', function() { + assert.strictEqual(res.readable, false); + testServer.close(); + }); + res.resume(); + }); +}); diff --git a/test/js/node/test/parallel/test-http-response-writehead-returns-this.js b/test/js/node/test/parallel/test-http-response-writehead-returns-this.js new file mode 100644 index 0000000000..a62c2eca03 --- /dev/null +++ b/test/js/node/test/parallel/test-http-response-writehead-returns-this.js @@ -0,0 +1,22 @@ +'use strict'; +require('../common'); +const http = require('http'); +const assert = require('assert'); + +const server = http.createServer((req, res) => { + res.writeHead(200, { 'a-header': 'a-header-value' }).end('abc'); +}); + +server.listen(0, () => { + http.get({ port: server.address().port }, (res) => { + assert.strictEqual(res.headers['a-header'], 'a-header-value'); + + const chunks = []; + + res.on('data', (chunk) => chunks.push(chunk)); + res.on('end', () => { + assert.strictEqual(Buffer.concat(chunks).toString(), 'abc'); + server.close(); + }); + }); +}); diff --git a/test/js/node/test/parallel/test-http-server-delete-parser.js b/test/js/node/test/parallel/test-http-server-delete-parser.js new file mode 100644 index 0000000000..4215ee2f9d --- /dev/null +++ b/test/js/node/test/parallel/test-http-server-delete-parser.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('../common'); + +const http = require('http'); + +const server = http.createServer(common.mustCall((req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('okay', common.mustCall(() => { + delete res.socket.parser; + })); + res.end(); +})); + +server.listen(0, '127.0.0.1', common.mustCall(() => { + const req = http.request({ + port: server.address().port, + host: '127.0.0.1', + method: 'GET', + }); + req.end(); +})); + +server.unref(); diff --git a/test/js/node/test/parallel/test-http-server-non-utf8-header.js b/test/js/node/test/parallel/test-http-server-non-utf8-header.js new file mode 100644 index 0000000000..8ce82ac4ca --- /dev/null +++ b/test/js/node/test/parallel/test-http-server-non-utf8-header.js @@ -0,0 +1,69 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); + +const nonUtf8Header = 'bår'; +const nonUtf8ToLatin1 = Buffer.from(nonUtf8Header).toString('latin1'); + +{ + const server = http.createServer(common.mustCall((req, res) => { + res.writeHead(200, [ + 'content-disposition', + Buffer.from(nonUtf8Header).toString('binary'), + ]); + res.end('hello'); + })); + + server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port }, (res) => { + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers['content-disposition'], nonUtf8ToLatin1); + res.resume().on('end', common.mustCall(() => { + server.close(); + })); + }); + })); +} + +{ + // Test multi-value header + const server = http.createServer(common.mustCall((req, res) => { + res.writeHead(200, [ + 'content-disposition', + [Buffer.from(nonUtf8Header).toString('binary')], + ]); + res.end('hello'); + })); + + server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port }, (res) => { + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers['content-disposition'], nonUtf8ToLatin1); + res.resume().on('end', common.mustCall(() => { + server.close(); + })); + }); + })); +} + +{ + const server = http.createServer(common.mustCall((req, res) => { + res.writeHead(200, [ + 'Content-Length', '5', + 'content-disposition', + Buffer.from(nonUtf8Header).toString('binary'), + ]); + res.end('hello'); + })); + + server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port }, (res) => { + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers['content-disposition'], nonUtf8ToLatin1); + res.resume().on('end', common.mustCall(() => { + server.close(); + })); + }); + })); +} diff --git a/test/js/node/test/parallel/test-http-server-options-incoming-message.js b/test/js/node/test/parallel/test-http-server-options-incoming-message.js new file mode 100644 index 0000000000..d0f4a769d7 --- /dev/null +++ b/test/js/node/test/parallel/test-http-server-options-incoming-message.js @@ -0,0 +1,41 @@ +'use strict'; + +/** + * This test covers http.Server({ IncomingMessage }) option: + * With IncomingMessage option the server should use + * the new class for creating req Object instead of the default + * http.IncomingMessage. + */ +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +class MyIncomingMessage extends http.IncomingMessage { + getUserAgent() { + return this.headers['user-agent'] || 'unknown'; + } +} + +const server = http.createServer({ + IncomingMessage: MyIncomingMessage +}, common.mustCall(function(req, res) { + assert.strictEqual(req.getUserAgent(), 'node-test'); + res.statusCode = 200; + res.end(); +})); +server.listen(); + +server.on('listening', function makeRequest() { + http.get({ + port: this.address().port, + headers: { + 'User-Agent': 'node-test' + } + }, (res) => { + assert.strictEqual(res.statusCode, 200); + res.on('end', () => { + server.close(); + }); + res.resume(); + }); +}); diff --git a/test/js/node/test/parallel/test-http-server-options-server-response.js b/test/js/node/test/parallel/test-http-server-options-server-response.js new file mode 100644 index 0000000000..f5adf39bed --- /dev/null +++ b/test/js/node/test/parallel/test-http-server-options-server-response.js @@ -0,0 +1,35 @@ +'use strict'; + +/** + * This test covers http.Server({ ServerResponse }) option: + * With ServerResponse option the server should use + * the new class for creating res Object instead of the default + * http.ServerResponse. + */ +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +class MyServerResponse extends http.ServerResponse { + status(code) { + return this.writeHead(code, { 'Content-Type': 'text/plain' }); + } +} + +const server = http.Server({ + ServerResponse: MyServerResponse +}, common.mustCall(function(req, res) { + res.status(200); + res.end(); +})); +server.listen(); + +server.on('listening', function makeRequest() { + http.get({ port: this.address().port }, (res) => { + assert.strictEqual(res.statusCode, 200); + res.on('end', () => { + server.close(); + }); + res.resume(); + }); +}); diff --git a/test/js/node/test/parallel/test-http-server-stale-close.js b/test/js/node/test/parallel/test-http-server-stale-close.js new file mode 100644 index 0000000000..b9322ed9fc --- /dev/null +++ b/test/js/node/test/parallel/test-http-server-stale-close.js @@ -0,0 +1,53 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const http = require('http'); +const fork = require('child_process').fork; +const assert = require('assert'); + +if (process.env.NODE_TEST_FORK_PORT) { + const req = http.request({ + headers: { 'Content-Length': '42' }, + method: 'POST', + host: '127.0.0.1', + port: +process.env.NODE_TEST_FORK_PORT, + }, process.exit); + req.write('BAM'); + req.end(); +} else { + const server = http.createServer((req, res) => { + res.writeHead(200, { 'Content-Length': '42' }); + req.pipe(res); + assert.strictEqual(req.destroyed, false); + req.on('close', () => { + assert.strictEqual(req.destroyed, true); + server.close(); + res.end(); + }); + }); + server.listen(0, function() { + fork(__filename, { + env: { ...process.env, NODE_TEST_FORK_PORT: this.address().port } + }); + }); +} diff --git a/test/js/node/test/parallel/test-http-server-write-after-end.js b/test/js/node/test/parallel/test-http-server-write-after-end.js new file mode 100644 index 0000000000..ba28771312 --- /dev/null +++ b/test/js/node/test/parallel/test-http-server-write-after-end.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); + +// Fix for https://github.com/nodejs/node/issues/14368 + +const server = http.createServer(handle); + +function handle(req, res) { + res.on('error', common.mustNotCall()); + + res.write('hello'); + res.end(); + + setImmediate(common.mustCall(() => { + res.write('world', common.mustCall((err) => { + common.expectsError({ + code: 'ERR_STREAM_WRITE_AFTER_END', + name: 'Error' + })(err); + server.close(); + })); + })); +} + +server.listen(0, common.mustCall(() => { + http.get(`http://localhost:${server.address().port}`); +})); diff --git a/test/js/node/test/parallel/test-http-set-header-chain.js b/test/js/node/test/parallel/test-http-set-header-chain.js new file mode 100644 index 0000000000..aa9519129a --- /dev/null +++ b/test/js/node/test/parallel/test-http-set-header-chain.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); +const expected = { + '__proto__': null, + 'testheader1': 'foo', + 'testheader2': 'bar', + 'testheader3': 'xyz' +}; +const server = http.createServer(common.mustCall((req, res) => { + let retval = res.setHeader('testheader1', 'foo'); + + // Test that the setHeader returns the same response object. + assert.strictEqual(retval, res); + + retval = res.setHeader('testheader2', 'bar').setHeader('testheader3', 'xyz'); + // Test that chaining works for setHeader. + assert.deepStrictEqual(res.getHeaders(), expected); + res.end('ok'); +})); +server.listen(0, () => { + http.get({ port: server.address().port }, common.mustCall((res) => { + res.on('data', () => {}); + res.on('end', common.mustCall(() => { + server.close(); + })); + })); +}); diff --git a/test/js/node/test/parallel/test-http-status-code.js b/test/js/node/test/parallel/test-http-status-code.js new file mode 100644 index 0000000000..246d22c131 --- /dev/null +++ b/test/js/node/test/parallel/test-http-status-code.js @@ -0,0 +1,58 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const Countdown = require('../common/countdown'); + +// Simple test of Node's HTTP ServerResponse.statusCode +// ServerResponse.prototype.statusCode + +const tests = [200, 202, 300, 404, 451, 500]; +let test; +const countdown = new Countdown(tests.length, () => s.close()); + +const s = http.createServer(function(req, res) { + res.writeHead(test, { 'Content-Type': 'text/plain' }); + console.log(`--\nserver: statusCode after writeHead: ${res.statusCode}`); + assert.strictEqual(res.statusCode, test); + res.end('hello world\n'); +}); + +s.listen(0, nextTest); + + +function nextTest() { + test = tests.shift(); + + http.get({ port: s.address().port }, function(response) { + console.log(`client: expected status: ${test}`); + console.log(`client: statusCode: ${response.statusCode}`); + assert.strictEqual(response.statusCode, test); + response.on('end', function() { + if (countdown.dec()) + nextTest(); + }); + response.resume(); + }); +} diff --git a/test/js/node/test/parallel/test-http-upgrade-reconsume-stream.js b/test/js/node/test/parallel/test-http-upgrade-reconsume-stream.js new file mode 100644 index 0000000000..e712ea647b --- /dev/null +++ b/test/js/node/test/parallel/test-http-upgrade-reconsume-stream.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tls = require('tls'); +const http = require('http'); + +// Tests that, after the HTTP parser stopped owning a socket that emits an +// 'upgrade' event, another C++ stream can start owning it (e.g. a TLSSocket). + +const server = http.createServer(common.mustNotCall()); + +server.on('upgrade', common.mustCall((request, socket, head) => { + // This should not crash. + new tls.TLSSocket(socket); + server.close(); + socket.destroy(); +})); + +server.listen(0, common.mustCall(() => { + http.get({ + port: server.address().port, + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket' + } + }).on('error', () => {}); +})); diff --git a/test/js/node/test/parallel/test-http-url.parse-auth-with-header-in-request.js b/test/js/node/test/parallel/test-http-url.parse-auth-with-header-in-request.js new file mode 100644 index 0000000000..ea5793ee18 --- /dev/null +++ b/test/js/node/test/parallel/test-http-url.parse-auth-with-header-in-request.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +function check(request) { + // The correct authorization header is be passed + assert.strictEqual(request.headers.authorization, 'NoAuthForYOU'); +} + +const server = http.createServer(function(request, response) { + // Run the check function + check(request); + response.writeHead(200, {}); + response.end('ok'); + server.close(); +}); + +server.listen(0, function() { + const testURL = + url.parse(`http://asdf:qwer@localhost:${this.address().port}`); + // The test here is if you set a specific authorization header in the + // request we should not override that with basic auth + testURL.headers = { + Authorization: 'NoAuthForYOU' + }; + + // make the request + http.request(testURL).end(); +}); diff --git a/test/js/node/test/parallel/test-http-url.parse-auth.js b/test/js/node/test/parallel/test-http-url.parse-auth.js new file mode 100644 index 0000000000..2bb5311586 --- /dev/null +++ b/test/js/node/test/parallel/test-http-url.parse-auth.js @@ -0,0 +1,48 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +function check(request) { + // The correct authorization header is be passed + assert.strictEqual(request.headers.authorization, 'Basic dXNlcjpwYXNzOg=='); +} + +const server = http.createServer(function(request, response) { + // Run the check function + check(request); + response.writeHead(200, {}); + response.end('ok'); + server.close(); +}); + +server.listen(0, function() { + const port = this.address().port; + // username = "user", password = "pass:" + const testURL = url.parse(`http://user:pass%3A@localhost:${port}`); + + // make the request + http.request(testURL).end(); +}); diff --git a/test/js/node/test/parallel/test-http-url.parse-basic.js b/test/js/node/test/parallel/test-http-url.parse-basic.js new file mode 100644 index 0000000000..71885b4bd0 --- /dev/null +++ b/test/js/node/test/parallel/test-http-url.parse-basic.js @@ -0,0 +1,58 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +let testURL; + +// Make sure the basics work +function check(request) { + // Default method should still be 'GET' + assert.strictEqual(request.method, 'GET'); + // There are no URL params, so you should not see any + assert.strictEqual(request.url, '/'); + // The host header should use the url.parse.hostname + assert.strictEqual(request.headers.host, + `${testURL.hostname}:${testURL.port}`); +} + +const server = http.createServer(function(request, response) { + // Run the check function + check(request); + response.writeHead(200, {}); + response.end('ok'); + server.close(); +}); + +server.listen(0, function() { + testURL = url.parse(`http://localhost:${this.address().port}`); + + // make the request + const clientRequest = http.request(testURL); + // Since there is a little magic with the agent + // make sure that an http request uses the http.Agent + assert.ok(clientRequest.agent instanceof http.Agent); + clientRequest.end(); +}); diff --git a/test/js/node/test/parallel/test-http-url.parse-only-support-http-https-protocol.js b/test/js/node/test/parallel/test-http-url.parse-only-support-http-https-protocol.js new file mode 100644 index 0000000000..3f6633075c --- /dev/null +++ b/test/js/node/test/parallel/test-http-url.parse-only-support-http-https-protocol.js @@ -0,0 +1,45 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +const invalidUrls = [ + 'file:///whatever', + 'mailto:asdf@asdf.com', + 'ftp://www.example.com', + 'javascript:alert(\'hello\');', + 'xmpp:foo@bar.com', + 'f://some.host/path', +]; + +for (const invalid of invalidUrls) { + assert.throws( + () => { http.request(url.parse(invalid)); }, + { + code: 'ERR_INVALID_PROTOCOL', + name: 'TypeError' + } + ); +} diff --git a/test/js/node/test/parallel/test-http-url.parse-path.js b/test/js/node/test/parallel/test-http-url.parse-path.js new file mode 100644 index 0000000000..25e4838c4a --- /dev/null +++ b/test/js/node/test/parallel/test-http-url.parse-path.js @@ -0,0 +1,46 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +function check(request) { + // A path should come over + assert.strictEqual(request.url, '/asdf'); +} + +const server = http.createServer(function(request, response) { + // Run the check function + check(request); + response.writeHead(200, {}); + response.end('ok'); + server.close(); +}); + +server.listen(0, function() { + const testURL = url.parse(`http://localhost:${this.address().port}/asdf`); + + // make the request + http.request(testURL).end(); +}); diff --git a/test/js/node/test/parallel/test-http-url.parse-post.js b/test/js/node/test/parallel/test-http-url.parse-post.js new file mode 100644 index 0000000000..db5ee78fe6 --- /dev/null +++ b/test/js/node/test/parallel/test-http-url.parse-post.js @@ -0,0 +1,54 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +let testURL; + +function check(request) { + // url.parse should not mess with the method + assert.strictEqual(request.method, 'POST'); + // Everything else should be right + assert.strictEqual(request.url, '/asdf?qwer=zxcv'); + // The host header should use the url.parse.hostname + assert.strictEqual(request.headers.host, + `${testURL.hostname}:${testURL.port}`); +} + +const server = http.createServer(function(request, response) { + // Run the check function + check(request); + response.writeHead(200, {}); + response.end('ok'); + server.close(); +}); + +server.listen(0, function() { + testURL = url.parse(`http://localhost:${this.address().port}/asdf?qwer=zxcv`); + testURL.method = 'POST'; + + // make the request + http.request(testURL).end(); +}); diff --git a/test/js/node/test/parallel/test-http-url.parse-search.js b/test/js/node/test/parallel/test-http-url.parse-search.js new file mode 100644 index 0000000000..0759c779d3 --- /dev/null +++ b/test/js/node/test/parallel/test-http-url.parse-search.js @@ -0,0 +1,47 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const http = require('http'); +const url = require('url'); + +function check(request) { + // A path should come over with params + assert.strictEqual(request.url, '/asdf?qwer=zxcv'); +} + +const server = http.createServer(function(request, response) { + // Run the check function + check(request); + response.writeHead(200, {}); + response.end('ok'); + server.close(); +}); + +server.listen(0, function() { + const port = this.address().port; + const testURL = url.parse(`http://localhost:${port}/asdf?qwer=zxcv`); + + // make the request + http.request(testURL).end(); +}); diff --git a/test/js/node/test/parallel/test-http-write-empty-string.js b/test/js/node/test/parallel/test-http-write-empty-string.js new file mode 100644 index 0000000000..88eff08f76 --- /dev/null +++ b/test/js/node/test/parallel/test-http-write-empty-string.js @@ -0,0 +1,54 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const http = require('http'); + +const server = http.createServer(function(request, response) { + console.log(`responding to ${request.url}`); + + response.writeHead(200, { 'Content-Type': 'text/plain' }); + response.write('1\n'); + response.write(''); + response.write('2\n'); + response.write(''); + response.end('3\n'); + + this.close(); +}); + +server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port }, common.mustCall((res) => { + let response = ''; + + assert.strictEqual(res.statusCode, 200); + res.setEncoding('ascii'); + res.on('data', (chunk) => { + response += chunk; + }); + res.on('end', common.mustCall(() => { + assert.strictEqual(response, '1\n2\n3\n'); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http-zero-length-write.js b/test/js/node/test/parallel/test-http-zero-length-write.js new file mode 100644 index 0000000000..f765ad0545 --- /dev/null +++ b/test/js/node/test/parallel/test-http-zero-length-write.js @@ -0,0 +1,94 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const http = require('http'); + +const Stream = require('stream'); + +function getSrc() { + // An old-style readable stream. + // The Readable class prevents this behavior. + const src = new Stream(); + + // Start out paused, just so we don't miss anything yet. + let paused = false; + src.pause = function() { + paused = true; + }; + src.resume = function() { + paused = false; + }; + + const chunks = [ '', 'asdf', '', 'foo', '', 'bar', '' ]; + const interval = setInterval(function() { + if (paused) + return; + + const chunk = chunks.shift(); + if (chunk !== undefined) { + src.emit('data', chunk); + } else { + src.emit('end'); + clearInterval(interval); + } + }, 1); + + return src; +} + + +const expect = 'asdffoobar'; + +const server = http.createServer(function(req, res) { + let actual = ''; + req.setEncoding('utf8'); + req.on('data', function(c) { + actual += c; + }); + req.on('end', function() { + assert.strictEqual(actual, expect); + getSrc().pipe(res); + }); + server.close(); +}); + +server.listen(0, function() { + const req = http.request({ port: this.address().port, method: 'POST' }); + let actual = ''; + req.on('response', function(res) { + res.setEncoding('utf8'); + res.on('data', function(c) { + actual += c; + }); + res.on('end', function() { + assert.strictEqual(actual, expect); + }); + }); + getSrc().pipe(req); +}); + +process.on('exit', function(c) { + if (!c) console.log('ok'); +}); diff --git a/test/js/node/test/parallel/test-http-zerolengthbuffer.js b/test/js/node/test/parallel/test-http-zerolengthbuffer.js new file mode 100644 index 0000000000..c59fc18108 --- /dev/null +++ b/test/js/node/test/parallel/test-http-zerolengthbuffer.js @@ -0,0 +1,23 @@ +'use strict'; +// Serving up a zero-length buffer should work. + +const common = require('../common'); +const http = require('http'); + +const server = http.createServer((req, res) => { + const buffer = Buffer.alloc(0); + res.writeHead(200, { 'Content-Type': 'text/html', + 'Content-Length': buffer.length }); + res.end(buffer); +}); + +server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port }, common.mustCall((res) => { + + res.on('data', common.mustNotCall()); + + res.on('end', (d) => { + server.close(); + }); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-clean-output.js b/test/js/node/test/parallel/test-http2-clean-output.js new file mode 100644 index 0000000000..27b7c338c9 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-clean-output.js @@ -0,0 +1,40 @@ +'use strict'; + +const { + hasCrypto, + mustCall, + skip +} = require('../common'); +if (!hasCrypto) + skip('missing crypto'); + +const { + strictEqual +} = require('assert'); +const { + createServer, + connect +} = require('http2'); +const { + spawnSync +} = require('child_process'); + +// Validate that there is no unexpected output when +// using http2 +if (process.argv[2] !== 'child') { + const { + stdout, stderr, status + } = spawnSync(process.execPath, [__filename, 'child'], { encoding: 'utf8' }); + strictEqual(stderr, ''); + strictEqual(stdout, ''); + strictEqual(status, 0); +} else { + const server = createServer(); + server.listen(0, mustCall(() => { + const client = connect(`http://localhost:${server.address().port}`); + client.on('connect', mustCall(() => { + client.close(); + server.close(); + })); + })); +} diff --git a/test/js/node/test/parallel/test-http2-client-priority-before-connect.js b/test/js/node/test/parallel/test-http2-client-priority-before-connect.js new file mode 100644 index 0000000000..7aa13a5e45 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-client-priority-before-connect.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const h2 = require('http2'); + +const server = h2.createServer(); + +// We use the lower-level API here +server.on('stream', common.mustCall((stream) => { + stream.respond(); + stream.end('ok'); +})); + +server.listen(0, common.mustCall(() => { + const client = h2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + req.priority({}); + + req.on('response', common.mustCall()); + req.resume(); + req.on('end', common.mustCall()); + req.on('close', common.mustCall(() => { + server.close(); + client.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-client-request-listeners-warning.js b/test/js/node/test/parallel/test-http2-client-request-listeners-warning.js new file mode 100644 index 0000000000..854e9535fd --- /dev/null +++ b/test/js/node/test/parallel/test-http2-client-request-listeners-warning.js @@ -0,0 +1,41 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const http2 = require('http2'); +const EventEmitter = require('events'); + +// This test ensures that a MaxListenersExceededWarning isn't emitted if +// more than EventEmitter.defaultMaxListeners requests are started on a +// ClientHttp2Session before it has finished connecting. + +process.on('warning', common.mustNotCall('A warning was emitted')); + +const server = http2.createServer(); +server.on('stream', (stream) => { + stream.respond(); + stream.end(); +}); + +server.listen(common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + + function request() { + return new Promise((resolve, reject) => { + const stream = client.request(); + stream.on('error', reject); + stream.on('response', resolve); + stream.end(); + }); + } + + const requests = []; + for (let i = 0; i < EventEmitter.defaultMaxListeners + 1; i++) { + requests.push(request()); + } + + Promise.all(requests).then(common.mustCall()).finally(common.mustCall(() => { + server.close(); + client.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-client-upload-reject.js b/test/js/node/test/parallel/test-http2-client-upload-reject.js new file mode 100644 index 0000000000..7234493031 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-client-upload-reject.js @@ -0,0 +1,49 @@ +'use strict'; + +// Verifies that uploading data from a client works + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +const loc = fixtures.path('person-large.jpg'); + +assert(fs.existsSync(loc)); + +fs.readFile(loc, common.mustSucceed((data) => { + const server = http2.createServer(); + + server.on('stream', common.mustCall((stream) => { + // Wait for some data to come through. + setImmediate(() => { + stream.on('close', common.mustCall(() => { + assert.strictEqual(stream.rstCode, 0); + })); + + stream.respond({ ':status': 400 }); + stream.end(); + }); + })); + + server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + + const req = client.request({ ':method': 'POST' }); + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], 400); + })); + + req.resume(); + req.on('end', common.mustCall(() => { + server.close(); + client.close(); + })); + + const str = fs.createReadStream(loc); + str.pipe(req); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-client-upload.js b/test/js/node/test/parallel/test-http2-client-upload.js new file mode 100644 index 0000000000..d073cd94e6 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-client-upload.js @@ -0,0 +1,58 @@ +'use strict'; + +// Verifies that uploading data from a client works + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); +const Countdown = require('../common/countdown'); + +const loc = fixtures.path('person-large.jpg'); +let fileData; + +assert(fs.existsSync(loc)); + +fs.readFile(loc, common.mustSucceed((data) => { + fileData = data; + + const server = http2.createServer(); + let client; + + const countdown = new Countdown(2, () => { + server.close(); + client.close(); + }); + + server.on('stream', common.mustCall((stream) => { + let data = Buffer.alloc(0); + stream.on('data', (chunk) => data = Buffer.concat([data, chunk])); + stream.on('end', common.mustCall(() => { + assert.deepStrictEqual(data, fileData); + })); + // Waiting on close avoids spurious ECONNRESET seen in windows CI. + // Not sure if this is actually a bug; more details at + // https://github.com/nodejs/node/issues/20750#issuecomment-511015247 + stream.on('close', () => countdown.dec()); + stream.respond(); + stream.end(); + })); + + server.listen(0, common.mustCall(() => { + client = http2.connect(`http://localhost:${server.address().port}`); + + const req = client.request({ ':method': 'POST' }); + req.on('response', common.mustCall()); + + req.resume(); + req.on('end', common.mustCall()); + + const str = fs.createReadStream(loc); + str.on('end', common.mustCall()); + str.on('close', () => countdown.dec()); + str.pipe(req); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-client-write-before-connect.js b/test/js/node/test/parallel/test-http2-client-write-before-connect.js new file mode 100644 index 0000000000..6efefc5870 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-client-write-before-connect.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +const server = h2.createServer(); + +// We use the lower-level API here +server.on('stream', common.mustCall((stream, headers, flags) => { + let data = ''; + stream.setEncoding('utf8'); + stream.on('data', (chunk) => data += chunk); + stream.on('end', common.mustCall(() => { + assert.strictEqual(data, 'some data more data'); + })); + stream.respond(); + stream.end('ok'); +})); + +server.listen(0, common.mustCall(() => { + const client = h2.connect(`http://localhost:${server.address().port}`); + + const req = client.request({ ':method': 'POST' }); + req.write('some data '); + req.end('more data'); + + req.on('response', common.mustCall()); + req.resume(); + req.on('end', common.mustCall()); + req.on('close', common.mustCall(() => { + server.close(); + client.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-client-write-empty-string.js b/test/js/node/test/parallel/test-http2-client-write-empty-string.js new file mode 100644 index 0000000000..f0f0b8f012 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-client-write-empty-string.js @@ -0,0 +1,54 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const http2 = require('http2'); + +for (const chunkSequence of [ + [ '' ], + [ '', '' ], +]) { + const server = http2.createServer(); + server.on('stream', common.mustCall((stream, headers, flags) => { + stream.respond({ 'content-type': 'text/html' }); + + let data = ''; + stream.on('data', common.mustNotCall((chunk) => { + data += chunk.toString(); + })); + stream.on('end', common.mustCall(() => { + stream.end(`"${data}"`); + })); + })); + + server.listen(0, common.mustCall(() => { + const port = server.address().port; + const client = http2.connect(`http://localhost:${port}`); + + const req = client.request({ + ':method': 'POST', + ':path': '/' + }); + + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], 200); + assert.strictEqual(headers['content-type'], 'text/html'); + })); + + let data = ''; + req.setEncoding('utf8'); + req.on('data', common.mustCallAtLeast((d) => data += d)); + req.on('end', common.mustCall(() => { + assert.strictEqual(data, '""'); + server.close(); + client.close(); + })); + + for (const chunk of chunkSequence) + req.write(chunk); + req.end(); + })); +} diff --git a/test/js/node/test/parallel/test-http2-compat-errors.js b/test/js/node/test/parallel/test-http2-compat-errors.js new file mode 100644 index 0000000000..18dc385422 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-errors.js @@ -0,0 +1,35 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const h2 = require('http2'); + +// Errors should not be reported both in Http2ServerRequest +// and Http2ServerResponse + +let expected = null; + +const server = h2.createServer(common.mustCall(function(req, res) { + res.stream.on('error', common.mustCall()); + req.on('error', common.mustNotCall()); + res.on('error', common.mustNotCall()); + req.on('aborted', common.mustCall()); + res.on('aborted', common.mustNotCall()); + + res.write('hello'); + + expected = new Error('kaboom'); + res.stream.destroy(expected); + server.close(); +})); + +server.listen(0, common.mustCall(function() { + const url = `http://localhost:${server.address().port}`; + const client = h2.connect(url, common.mustCall(() => { + const request = client.request(); + request.on('data', common.mustCall((chunk) => { + client.destroy(); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-expect-continue.js b/test/js/node/test/parallel/test-http2-compat-expect-continue.js new file mode 100644 index 0000000000..d0decb1472 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-expect-continue.js @@ -0,0 +1,95 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +{ + const testResBody = 'other stuff!\n'; + + // Checks the full 100-continue flow from client sending 'expect: 100-continue' + // through server receiving it, sending back :status 100, writing the rest of + // the request to finally the client receiving to. + + const server = http2.createServer(); + + let sentResponse = false; + + server.on('request', common.mustCall((req, res) => { + res.end(testResBody); + sentResponse = true; + })); + + server.listen(0); + + server.on('listening', common.mustCall(() => { + let body = ''; + + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request({ + ':method': 'POST', + 'expect': '100-continue' + }); + + let gotContinue = false; + req.on('continue', common.mustCall(() => { + gotContinue = true; + })); + + req.on('response', common.mustCall((headers) => { + assert.strictEqual(gotContinue, true); + assert.strictEqual(sentResponse, true); + assert.strictEqual(headers[':status'], 200); + req.end(); + })); + + req.setEncoding('utf8'); + req.on('data', common.mustCall((chunk) => { body += chunk; })); + req.on('end', common.mustCall(() => { + assert.strictEqual(body, testResBody); + client.close(); + server.close(); + })); + })); +} + +{ + // Checks the full 100-continue flow from client sending 'expect: 100-continue' + // through server receiving it and ending the request. + + const server = http2.createServer(); + + server.on('request', common.mustCall((req, res) => { + res.end(); + })); + + server.listen(0); + + server.on('listening', common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request({ + ':path': '/', + 'expect': '100-continue' + }); + + let gotContinue = false; + req.on('continue', common.mustCall(() => { + gotContinue = true; + })); + + let gotResponse = false; + req.on('response', common.mustCall(() => { + gotResponse = true; + })); + + req.setEncoding('utf8'); + req.on('end', common.mustCall(() => { + assert.strictEqual(gotContinue, true); + assert.strictEqual(gotResponse, true); + client.close(); + server.close(); + })); + })); +} diff --git a/test/js/node/test/parallel/test-http2-compat-expect-handling.js b/test/js/node/test/parallel/test-http2-compat-expect-handling.js new file mode 100644 index 0000000000..77f2275834 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-expect-handling.js @@ -0,0 +1,45 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const expectValue = 'meoww'; + +const server = http2.createServer(common.mustNotCall()); + +server.once('checkExpectation', common.mustCall((req, res) => { + assert.strictEqual(req.headers.expect, expectValue); + res.statusCode = 417; + res.end(); +})); + +server.listen(0, common.mustCall(() => nextTest(2))); + +function nextTest(testsToRun) { + if (!testsToRun) { + return server.close(); + } + + const port = server.address().port; + const client = http2.connect(`http://localhost:${port}`); + const req = client.request({ + ':path': '/', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}`, + 'expect': expectValue + }); + + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], 417); + req.resume(); + })); + + req.on('end', common.mustCall(() => { + client.close(); + nextTest(testsToRun - 1); + })); +} diff --git a/test/js/node/test/parallel/test-http2-compat-method-connect.js b/test/js/node/test/parallel/test-http2-compat-method-connect.js new file mode 100644 index 0000000000..21ad23e92b --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-method-connect.js @@ -0,0 +1,40 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const server = http2.createServer(common.mustNotCall()); + +server.listen(0, common.mustCall(() => testMethodConnect(2))); + +server.once('connect', common.mustCall((req, res) => { + assert.strictEqual(req.headers[':method'], 'CONNECT'); + res.statusCode = 405; + res.end(); +})); + +function testMethodConnect(testsToRun) { + if (!testsToRun) { + return server.close(); + } + + const port = server.address().port; + const client = http2.connect(`http://localhost:${port}`); + const req = client.request({ + ':method': 'CONNECT', + ':authority': `localhost:${port}` + }); + + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], 405); + })); + req.resume(); + req.on('end', common.mustCall(() => { + client.close(); + testMethodConnect(testsToRun - 1); + })); + req.end(); +} diff --git a/test/js/node/test/parallel/test-http2-compat-serverrequest-pause.js b/test/js/node/test/parallel/test-http2-compat-serverrequest-pause.js new file mode 100644 index 0000000000..2abc9e3da4 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverrequest-pause.js @@ -0,0 +1,52 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +// Check that pause & resume work as expected with Http2ServerRequest + +const testStr = 'Request Body from Client'; + +const server = h2.createServer(); + +server.on('request', common.mustCall((req, res) => { + let data = ''; + req.pause(); + req.setEncoding('utf8'); + req.on('data', common.mustCall((chunk) => (data += chunk))); + setTimeout(common.mustCall(() => { + assert.strictEqual(data, ''); + req.resume(); + }), common.platformTimeout(100)); + req.on('end', common.mustCall(() => { + assert.strictEqual(data, testStr); + res.end(); + })); + + // Shouldn't throw if underlying Http2Stream no longer exists + res.on('finish', common.mustCall(() => process.nextTick(() => { + req.pause(); + req.resume(); + }))); +})); + +server.listen(0, common.mustCall(() => { + const port = server.address().port; + + const client = h2.connect(`http://localhost:${port}`); + const request = client.request({ + ':path': '/foobar', + ':method': 'POST', + ':scheme': 'http', + ':authority': `localhost:${port}` + }); + request.resume(); + request.end(testStr); + request.on('end', common.mustCall(function() { + client.close(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverrequest-pipe.js b/test/js/node/test/parallel/test-http2-compat-serverrequest-pipe.js new file mode 100644 index 0000000000..64beb6472b --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverrequest-pipe.js @@ -0,0 +1,49 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const http2 = require('http2'); +const fs = require('fs'); + +// Piping should work as expected with createWriteStream + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +const loc = fixtures.path('person-large.jpg'); +const fn = tmpdir.resolve('http2-url-tests.js'); + +const server = http2.createServer(); + +server.on('request', common.mustCall((req, res) => { + const dest = req.pipe(fs.createWriteStream(fn)); + dest.on('finish', common.mustCall(() => { + assert.strictEqual(req.complete, true); + assert.strictEqual(fs.readFileSync(loc).length, fs.readFileSync(fn).length); + fs.unlinkSync(fn); + res.end(); + })); +})); + +server.listen(0, common.mustCall(() => { + const port = server.address().port; + const client = http2.connect(`http://localhost:${port}`); + + let remaining = 2; + function maybeClose() { + if (--remaining === 0) { + server.close(); + client.close(); + } + } + + const req = client.request({ ':method': 'POST' }); + req.on('response', common.mustCall()); + req.resume(); + req.on('end', common.mustCall(maybeClose)); + const str = fs.createReadStream(loc); + str.on('end', common.mustCall(maybeClose)); + str.pipe(req); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse-drain.js b/test/js/node/test/parallel/test-http2-compat-serverresponse-drain.js new file mode 100644 index 0000000000..7ccbb1f4d2 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse-drain.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +// Check that drain event is passed from Http2Stream + +const testString = 'tests'; + +const server = h2.createServer(); + +server.on('request', common.mustCall((req, res) => { + res.stream._writableState.highWaterMark = testString.length; + assert.strictEqual(res.write(testString), false); + res.on('drain', common.mustCall(() => res.end(testString))); +})); + +server.listen(0, common.mustCall(() => { + const port = server.address().port; + + const client = h2.connect(`http://localhost:${port}`); + const request = client.request({ + ':path': '/foobar', + ':method': 'POST', + ':scheme': 'http', + ':authority': `localhost:${port}` + }); + request.resume(); + request.end(); + + let data = ''; + request.setEncoding('utf8'); + request.on('data', (chunk) => (data += chunk)); + + request.on('end', common.mustCall(function() { + assert.strictEqual(data, testString.repeat(2)); + client.close(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse-end-after-statuses-without-body.js b/test/js/node/test/parallel/test-http2-compat-serverresponse-end-after-statuses-without-body.js new file mode 100644 index 0000000000..ce8cbe600c --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse-end-after-statuses-without-body.js @@ -0,0 +1,47 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const h2 = require('http2'); + +// This test case ensures that calling of res.end after sending +// 204, 205 and 304 HTTP statuses will not cause an error +// See issue: https://github.com/nodejs/node/issues/21740 + +const { + HTTP_STATUS_NO_CONTENT, + HTTP_STATUS_RESET_CONTENT, + HTTP_STATUS_NOT_MODIFIED +} = h2.constants; + +const statusWithoutBody = [ + HTTP_STATUS_NO_CONTENT, + HTTP_STATUS_RESET_CONTENT, + HTTP_STATUS_NOT_MODIFIED, +]; +const STATUS_CODES_COUNT = statusWithoutBody.length; + +const server = h2.createServer(common.mustCall(function(req, res) { + res.writeHead(statusWithoutBody.pop()); + res.end(); +}, STATUS_CODES_COUNT)); + +server.listen(0, common.mustCall(function() { + const url = `http://localhost:${server.address().port}`; + const client = h2.connect(url, common.mustCall(() => { + let responseCount = 0; + const closeAfterResponse = () => { + if (STATUS_CODES_COUNT === ++responseCount) { + client.destroy(); + server.close(); + } + }; + + for (let i = 0; i < STATUS_CODES_COUNT; i++) { + const request = client.request(); + request.on('response', common.mustCall(closeAfterResponse)); + } + + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse-finished.js b/test/js/node/test/parallel/test-http2-compat-serverresponse-finished.js new file mode 100644 index 0000000000..a42d40227c --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse-finished.js @@ -0,0 +1,49 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); +const net = require('net'); + +// Http2ServerResponse.finished +const server = h2.createServer(); +server.listen(0, common.mustCall(() => { + const port = server.address().port; + server.once('request', common.mustCall((request, response) => { + assert.ok(response.socket instanceof net.Socket); + assert.ok(response.connection instanceof net.Socket); + assert.strictEqual(response.socket, response.connection); + + response.on('finish', common.mustCall(() => { + assert.strictEqual(response.socket, undefined); + assert.strictEqual(response.connection, undefined); + process.nextTick(common.mustCall(() => { + assert.ok(response.stream); + server.close(); + })); + })); + assert.strictEqual(response.finished, false); + assert.strictEqual(response.writableEnded, false); + response.end(); + assert.strictEqual(response.finished, true); + assert.strictEqual(response.writableEnded, true); + })); + + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(() => { + const headers = { + ':path': '/', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}` + }; + const request = client.request(headers); + request.on('end', common.mustCall(() => { + client.close(); + })); + request.end(); + request.resume(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse-flushheaders.js b/test/js/node/test/parallel/test-http2-compat-serverresponse-flushheaders.js new file mode 100644 index 0000000000..7760bf8c7d --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse-flushheaders.js @@ -0,0 +1,59 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +// Http2ServerResponse.flushHeaders + +let serverResponse; + +const server = h2.createServer(); +server.listen(0, common.mustCall(function() { + const port = server.address().port; + server.once('request', common.mustCall(function(request, response) { + assert.strictEqual(response.headersSent, false); + assert.strictEqual(response._header, false); // Alias for headersSent + response.flushHeaders(); + assert.strictEqual(response.headersSent, true); + assert.strictEqual(response._header, true); + response.flushHeaders(); // Idempotent + + assert.throws(() => { + response.writeHead(400, { 'foo-bar': 'abc123' }); + }, { + code: 'ERR_HTTP2_HEADERS_SENT' + }); + + response.on('finish', common.mustCall(function() { + server.close(); + process.nextTick(() => { + response.flushHeaders(); // Idempotent + }); + })); + serverResponse = response; + })); + + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(function() { + const headers = { + ':path': '/', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}` + }; + const request = client.request(headers); + request.on('response', common.mustCall(function(headers, flags) { + assert.strictEqual(headers['foo-bar'], undefined); + assert.strictEqual(headers[':status'], 200); + serverResponse.end(); + }, 1)); + request.on('end', common.mustCall(function() { + client.close(); + })); + request.end(); + request.resume(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse-headers-send-date.js b/test/js/node/test/parallel/test-http2-compat-serverresponse-headers-send-date.js new file mode 100644 index 0000000000..b22b1f7304 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse-headers-send-date.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) { common.skip('missing crypto'); } +const assert = require('assert'); +const http2 = require('http2'); + +const server = http2.createServer(common.mustCall((request, response) => { + response.sendDate = false; + response.writeHead(200); + response.end(); +})); + +server.listen(0, common.mustCall(() => { + const session = http2.connect(`http://localhost:${server.address().port}`); + const req = session.request(); + + req.on('response', common.mustCall((headers, flags) => { + assert.strictEqual('Date' in headers, false); + assert.strictEqual('date' in headers, false); + })); + + req.on('end', common.mustCall(() => { + session.close(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse-statuscode.js b/test/js/node/test/parallel/test-http2-compat-serverresponse-statuscode.js new file mode 100644 index 0000000000..6064a5936e --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse-statuscode.js @@ -0,0 +1,76 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +// Http2ServerResponse should have a statusCode property + +const server = h2.createServer(); +server.listen(0, common.mustCall(function() { + const port = server.address().port; + server.once('request', common.mustCall(function(request, response) { + const expectedDefaultStatusCode = 200; + const realStatusCodes = { + continue: 100, + ok: 200, + multipleChoices: 300, + badRequest: 400, + internalServerError: 500 + }; + const fakeStatusCodes = { + tooLow: 99, + tooHigh: 600 + }; + + assert.strictEqual(response.statusCode, expectedDefaultStatusCode); + + // Setting the response.statusCode should not throw. + response.statusCode = realStatusCodes.ok; + response.statusCode = realStatusCodes.multipleChoices; + response.statusCode = realStatusCodes.badRequest; + response.statusCode = realStatusCodes.internalServerError; + + assert.throws(() => { + response.statusCode = realStatusCodes.continue; + }, { + code: 'ERR_HTTP2_INFO_STATUS_NOT_ALLOWED', + name: 'RangeError' + }); + assert.throws(() => { + response.statusCode = fakeStatusCodes.tooLow; + }, { + code: 'ERR_HTTP2_STATUS_INVALID', + name: 'RangeError' + }); + assert.throws(() => { + response.statusCode = fakeStatusCodes.tooHigh; + }, { + code: 'ERR_HTTP2_STATUS_INVALID', + name: 'RangeError' + }); + + response.on('finish', common.mustCall(function() { + server.close(); + })); + response.end(); + })); + + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(function() { + const headers = { + ':path': '/', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}` + }; + const request = client.request(headers); + request.on('end', common.mustCall(function() { + client.close(); + })); + request.end(); + request.resume(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse-writehead-array.js b/test/js/node/test/parallel/test-http2-compat-serverresponse-writehead-array.js new file mode 100644 index 0000000000..a0cb65d4bf --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse-writehead-array.js @@ -0,0 +1,96 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +// Http2ServerResponse.writeHead should support arrays and nested arrays + +{ + const server = http2.createServer(); + server.listen(0, common.mustCall(() => { + const port = server.address().port; + + server.once('request', common.mustCall((request, response) => { + const returnVal = response.writeHead(200, [ + ['foo', 'bar'], + ['foo', 'baz'], + ['ABC', 123], + ]); + assert.strictEqual(returnVal, response); + response.end(common.mustCall(() => { server.close(); })); + })); + + const client = http2.connect(`http://localhost:${port}`, common.mustCall(() => { + const request = client.request(); + + request.on('response', common.mustCall((headers) => { + assert.strictEqual(headers.foo, 'bar, baz'); + assert.strictEqual(headers.abc, '123'); + assert.strictEqual(headers[':status'], 200); + }, 1)); + request.on('end', common.mustCall(() => { + client.close(); + })); + request.end(); + request.resume(); + })); + })); +} + +{ + const server = http2.createServer(); + server.listen(0, common.mustCall(() => { + const port = server.address().port; + + server.once('request', common.mustCall((request, response) => { + const returnVal = response.writeHead(200, ['foo', 'bar', 'foo', 'baz', 'ABC', 123]); + assert.strictEqual(returnVal, response); + response.end(common.mustCall(() => { server.close(); })); + })); + + const client = http2.connect(`http://localhost:${port}`, common.mustCall(() => { + const request = client.request(); + + request.on('response', common.mustCall((headers) => { + assert.strictEqual(headers.foo, 'bar, baz'); + assert.strictEqual(headers.abc, '123'); + assert.strictEqual(headers[':status'], 200); + }, 1)); + request.on('end', common.mustCall(() => { + client.close(); + })); + request.end(); + request.resume(); + })); + })); +} + +{ + const server = http2.createServer(); + server.listen(0, common.mustCall(() => { + const port = server.address().port; + + server.once('request', common.mustCall((request, response) => { + try { + response.writeHead(200, ['foo', 'bar', 'ABC', 123, 'extra']); + } catch (err) { + assert.strictEqual(err.code, 'ERR_INVALID_ARG_VALUE'); + } + + response.end(common.mustCall(() => { server.close(); })); + })); + + const client = http2.connect(`http://localhost:${port}`, common.mustCall(() => { + const request = client.request(); + + request.on('end', common.mustCall(() => { + client.close(); + })); + request.end(); + request.resume(); + })); + })); +} diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse-writehead.js b/test/js/node/test/parallel/test-http2-compat-serverresponse-writehead.js new file mode 100644 index 0000000000..8157dcbedd --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse-writehead.js @@ -0,0 +1,56 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +// Http2ServerResponse.writeHead should override previous headers + +const server = h2.createServer(); +server.listen(0, common.mustCall(function() { + const port = server.address().port; + server.once('request', common.mustCall(function(request, response) { + response.setHeader('foo-bar', 'def456'); + + // Override + const returnVal = response.writeHead(418, { 'foo-bar': 'abc123' }); + + assert.strictEqual(returnVal, response); + + assert.throws(() => { response.writeHead(300); }, { + code: 'ERR_HTTP2_HEADERS_SENT' + }); + + response.on('finish', common.mustCall(function() { + server.close(); + process.nextTick(common.mustCall(() => { + // The stream is invalid at this point, + // and this line verifies this does not throw. + response.writeHead(300); + })); + })); + response.end(); + })); + + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(function() { + const headers = { + ':path': '/', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}` + }; + const request = client.request(headers); + request.on('response', common.mustCall(function(headers) { + assert.strictEqual(headers['foo-bar'], 'abc123'); + assert.strictEqual(headers[':status'], 418); + }, 1)); + request.on('end', common.mustCall(function() { + client.close(); + })); + request.end(); + request.resume(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-serverresponse.js b/test/js/node/test/parallel/test-http2-compat-serverresponse.js new file mode 100644 index 0000000000..fbde58693b --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-serverresponse.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +// Http2ServerResponse should expose convenience properties + +const server = h2.createServer(); +server.listen(0, common.mustCall(function() { + const port = server.address().port; + server.once('request', common.mustCall(function(request, response) { + assert.strictEqual(response.req, request); + + // Verify that writing to response.req is allowed. + response.req = null; + + response.on('finish', common.mustCall(function() { + process.nextTick(() => { + server.close(); + }); + })); + response.end(); + })); + + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(function() { + const headers = { + ':path': '/foobar', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}` + }; + const request = client.request(headers); + request.on('end', common.mustCall(function() { + client.close(); + })); + request.end(); + request.resume(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-compat-write-early-hints-invalid-argument-type.js b/test/js/node/test/parallel/test-http2-compat-write-early-hints-invalid-argument-type.js new file mode 100644 index 0000000000..caf5824e07 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-write-early-hints-invalid-argument-type.js @@ -0,0 +1,42 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const assert = require('node:assert'); +const http2 = require('node:http2'); +const debug = require('node:util').debuglog('test'); + +const testResBody = 'response content'; + +{ + // Invalid object value + + const server = http2.createServer(); + + server.on('request', common.mustCall((req, res) => { + debug('Server sending early hints...'); + res.writeEarlyHints('this should not be here'); + + debug('Server sending full response...'); + res.end(testResBody); + })); + + server.listen(0); + + server.on('listening', common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + + debug('Client sending request...'); + + req.on('headers', common.mustNotCall()); + + process.on('uncaughtException', (err) => { + debug(`Caught an exception: ${JSON.stringify(err)}`); + if (err.name === 'AssertionError') throw err; + assert.strictEqual(err.code, 'ERR_INVALID_ARG_TYPE'); + process.exit(0); + }); + })); +} diff --git a/test/js/node/test/parallel/test-http2-compat-write-early-hints-invalid-argument-value.js b/test/js/node/test/parallel/test-http2-compat-write-early-hints-invalid-argument-value.js new file mode 100644 index 0000000000..d640f13fae --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-write-early-hints-invalid-argument-value.js @@ -0,0 +1,42 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const assert = require('node:assert'); +const http2 = require('node:http2'); +const debug = require('node:util').debuglog('test'); + +const testResBody = 'response content'; + +{ + // Invalid link header value + + const server = http2.createServer(); + + server.on('request', common.mustCall((req, res) => { + debug('Server sending early hints...'); + res.writeEarlyHints({ link: BigInt(100) }); + + debug('Server sending full response...'); + res.end(testResBody); + })); + + server.listen(0); + + server.on('listening', common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + + debug('Client sending request...'); + + req.on('headers', common.mustNotCall()); + + process.on('uncaughtException', (err) => { + debug(`Caught an exception: ${JSON.stringify(err)}`); + if (err.name === 'AssertionError') throw err; + assert.strictEqual(err.code, 'ERR_INVALID_ARG_VALUE'); + process.exit(0); + }); + })); +} diff --git a/test/js/node/test/parallel/test-http2-compat-write-head-destroyed.js b/test/js/node/test/parallel/test-http2-compat-write-head-destroyed.js new file mode 100644 index 0000000000..842bf0e9ab --- /dev/null +++ b/test/js/node/test/parallel/test-http2-compat-write-head-destroyed.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const http2 = require('http2'); + +// Check that writeHead, write and end do not crash in compatibility mode + +const server = http2.createServer(common.mustCall((req, res) => { + // Destroy the stream first + req.stream.destroy(); + + res.writeHead(200); + res.write('hello '); + res.end('world'); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + + const req = client.request(); + req.on('response', common.mustNotCall()); + req.on('close', common.mustCall((arg) => { + client.close(); + server.close(); + })); + req.resume(); +})); diff --git a/test/js/node/test/parallel/test-http2-connect-tls-with-delay.js b/test/js/node/test/parallel/test-http2-connect-tls-with-delay.js new file mode 100644 index 0000000000..0b3753ae38 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-connect-tls-with-delay.js @@ -0,0 +1,50 @@ +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const http2 = require('http2'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const serverOptions = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + +const server = http2.createSecureServer(serverOptions, (req, res) => { + res.end(); +}); + +server.listen(0, '127.0.0.1', common.mustCall(() => { + const options = { + ALPNProtocols: ['h2'], + host: '127.0.0.1', + servername: 'localhost', + port: server.address().port, + rejectUnauthorized: false + }; + + const socket = tls.connect(options, async () => { + socket.once('readable', () => { + const client = http2.connect( + 'https://localhost:' + server.address().port, + { ...options, createConnection: () => socket } + ); + + client.once('remoteSettings', common.mustCall(() => { + const req = client.request({ + ':path': '/' + }); + req.on('data', () => req.resume()); + req.on('end', common.mustCall(() => { + client.close(); + req.close(); + server.close(); + })); + req.end(); + })); + }); + }); +})); diff --git a/test/js/node/test/parallel/test-http2-cookies.js b/test/js/node/test/parallel/test-http2-cookies.js new file mode 100644 index 0000000000..a270c1d73b --- /dev/null +++ b/test/js/node/test/parallel/test-http2-cookies.js @@ -0,0 +1,60 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +const server = h2.createServer(); + +const setCookie = [ + 'a=b', + 'c=d; Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly', + 'e=f', +]; + +// We use the lower-level API here +server.on('stream', common.mustCall(onStream)); + +function onStream(stream, headers, flags) { + + assert.strictEqual(typeof headers.abc, 'string'); + assert.strictEqual(headers.abc, '1, 2, 3'); + assert.strictEqual(typeof headers.cookie, 'string'); + assert.strictEqual(headers.cookie, 'a=b; c=d; e=f'); + + stream.respond({ + 'content-type': 'text/html', + ':status': 200, + 'set-cookie': setCookie + }); + + stream.end('hello world'); +} + +server.listen(0); + +server.on('listening', common.mustCall(() => { + + const client = h2.connect(`http://localhost:${server.address().port}`); + + const req = client.request({ + ':path': '/', + 'abc': [1, 2, 3], + 'cookie': ['a=b', 'c=d', 'e=f'], + }); + req.resume(); + + req.on('response', common.mustCall((headers) => { + assert(Array.isArray(headers['set-cookie'])); + assert.deepStrictEqual(headers['set-cookie'], setCookie); + })); + + req.on('end', common.mustCall(() => { + server.close(); + client.close(); + })); + req.end(); + +})); diff --git a/test/js/node/test/parallel/test-http2-date-header.js b/test/js/node/test/parallel/test-http2-date-header.js new file mode 100644 index 0000000000..2b63e1b789 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-date-header.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const server = http2.createServer(); + +server.on('stream', common.mustCall((stream) => { + // Date header is defaulted + stream.respond(); + stream.end(); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + req.on('response', common.mustCall((headers) => { + // The date header must be set to a non-invalid value + assert.notStrictEqual((new Date()).toString(), 'Invalid Date'); + })); + req.resume(); + req.on('end', common.mustCall(() => { + server.close(); + client.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-dont-override.js b/test/js/node/test/parallel/test-http2-dont-override.js new file mode 100644 index 0000000000..3f87e14be1 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-dont-override.js @@ -0,0 +1,49 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const options = {}; + +const server = http2.createServer(options); + +// Options are defaulted but the options are not modified +assert.deepStrictEqual(Object.keys(options), []); + +server.on('stream', common.mustCall((stream) => { + const headers = {}; + const options = {}; + stream.respond(headers, options); + + // The headers are defaulted but the original object is not modified + assert.deepStrictEqual(Object.keys(headers), []); + + // Options are defaulted but the original object is not modified + assert.deepStrictEqual(Object.keys(options), []); + + stream.end(); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + + const headers = {}; + const options = {}; + + const req = client.request(headers, options); + + // The headers are defaulted but the original object is not modified + assert.deepStrictEqual(Object.keys(headers), []); + + // Options are defaulted but the original object is not modified + assert.deepStrictEqual(Object.keys(options), []); + + req.resume(); + req.on('end', common.mustCall(() => { + server.close(); + client.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-large-write-close.js b/test/js/node/test/parallel/test-http2-large-write-close.js new file mode 100644 index 0000000000..f9dee357d6 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-large-write-close.js @@ -0,0 +1,44 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const http2 = require('http2'); + +const content = Buffer.alloc(1e5, 0x44); + +const server = http2.createSecureServer({ + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}); +server.on('stream', common.mustCall((stream) => { + stream.respond({ + 'Content-Type': 'application/octet-stream', + 'Content-Length': (content.length.toString() * 2), + 'Vary': 'Accept-Encoding' + }); + + stream.write(content); + stream.write(content); + stream.end(); + stream.close(); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`https://localhost:${server.address().port}`, + { rejectUnauthorized: false }); + + const req = client.request({ ':path': '/' }); + req.end(); + + let receivedBufferLength = 0; + req.on('data', common.mustCallAtLeast((buf) => { + receivedBufferLength += buf.length; + }, 1)); + req.on('close', common.mustCall(() => { + assert.strictEqual(receivedBufferLength, content.length * 2); + client.close(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-large-write-destroy.js b/test/js/node/test/parallel/test-http2-large-write-destroy.js new file mode 100644 index 0000000000..b59c66bb04 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-large-write-destroy.js @@ -0,0 +1,41 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const http2 = require('http2'); + +// This test will result in a crash due to a missed CHECK in C++ or +// a straight-up segfault if the C++ doesn't send RST_STREAM through +// properly when calling destroy. + +const content = Buffer.alloc(60000, 0x44); + +const server = http2.createSecureServer({ + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}); +server.on('stream', common.mustCall((stream) => { + stream.respond({ + 'Content-Type': 'application/octet-stream', + 'Content-Length': (content.length.toString() * 2), + 'Vary': 'Accept-Encoding' + }, { waitForTrailers: true }); + + stream.write(content); + stream.destroy(); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`https://localhost:${server.address().port}`, + { rejectUnauthorized: false }); + + const req = client.request({ ':path': '/' }); + req.end(); + req.resume(); // Otherwise close won't be emitted if there's pending data. + + req.on('close', common.mustCall(() => { + client.close(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-large-write-multiple-requests.js b/test/js/node/test/parallel/test-http2-large-write-multiple-requests.js new file mode 100644 index 0000000000..bcbb1434cb --- /dev/null +++ b/test/js/node/test/parallel/test-http2-large-write-multiple-requests.js @@ -0,0 +1,53 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +// This tests that the http2 server sends data early when it accumulates +// enough from ongoing requests to avoid DoS as mitigation for +// CVE-2019-9517 and CVE-2019-9511. +// Added by https://github.com/nodejs/node/commit/8a4a193 +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const http2 = require('http2'); + +const content = fixtures.readSync('person-large.jpg'); + +const server = http2.createServer({ + maxSessionMemory: 1000 +}); +let streamCount = 0; +server.on('stream', (stream, headers) => { + stream.respond({ + 'content-type': 'image/jpeg', + ':status': 200 + }); + stream.end(content); + console.log('server sends content', ++streamCount); +}); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}/`); + + let endCount = 0; + let finished = 0; + for (let i = 0; i < 100; i++) { + const req = client.request({ ':path': '/' }).end(); + const chunks = []; + req.on('data', (chunk) => { + chunks.push(chunk); + }); + req.on('end', common.mustCall(() => { + console.log('client receives content', ++endCount); + assert.deepStrictEqual(Buffer.concat(chunks), content); + + if (++finished === 100) { + client.close(); + server.close(); + } + })); + req.on('error', (e) => { + console.log('client error', e); + }); + } +})); diff --git a/test/js/node/test/parallel/test-http2-large-writes-session-memory-leak.js b/test/js/node/test/parallel/test-http2-large-writes-session-memory-leak.js new file mode 100644 index 0000000000..641923c06c --- /dev/null +++ b/test/js/node/test/parallel/test-http2-large-writes-session-memory-leak.js @@ -0,0 +1,55 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const http2 = require('http2'); + +// Regression test for https://github.com/nodejs/node/issues/29223. +// There was a "leak" in the accounting of session memory leading +// to streams eventually failing with NGHTTP2_ENHANCE_YOUR_CALM. + +const server = http2.createSecureServer({ + key: fixtures.readKey('agent2-key.pem'), + cert: fixtures.readKey('agent2-cert.pem'), +}); + +// Simple server that sends 200k and closes the stream. +const data200k = 'a'.repeat(200 * 1024); +server.on('stream', (stream) => { + stream.write(data200k); + stream.end(); +}); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`https://localhost:${server.address().port}`, { + ca: fixtures.readKey('agent2-cert.pem'), + servername: 'agent2', + + // Set maxSessionMemory to 1MB so the leak causes errors faster. + maxSessionMemory: 1 + }); + + // Repeatedly create a new stream and read the incoming data. Even though we + // only have one stream active at a time, prior to the fix for #29223, + // session memory would steadily increase and we'd eventually hit the 1MB + // maxSessionMemory limit and get NGHTTP2_ENHANCE_YOUR_CALM errors trying to + // create new streams. + let streamsLeft = 50; + function newStream() { + const stream = client.request({ ':path': '/' }); + + stream.on('data', () => { }); + + stream.on('close', () => { + if (streamsLeft-- > 0) { + newStream(); + } else { + client.destroy(); + server.close(); + } + }); + } + + newStream(); +})); diff --git a/test/js/node/test/parallel/test-http2-malformed-altsvc.js b/test/js/node/test/parallel/test-http2-malformed-altsvc.js new file mode 100644 index 0000000000..28c0fb46b4 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-malformed-altsvc.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const http2 = require('http2'); +const net = require('net'); +const h2test = require('../common/http2'); + +const server = http2.createServer(); +server.on('stream', common.mustNotCall()); + +const settings = new h2test.SettingsFrame(); +const settingsAck = new h2test.SettingsFrame(true); +const altsvc = new h2test.AltSvcFrame((1 << 14) + 1); + +server.listen(0, () => { + const client = net.connect(server.address().port, () => { + client.write(h2test.kClientMagic, () => { + client.write(settings.data, () => { + client.write(settingsAck.data); + // Prior to nghttp2 1.31.1, sending this malformed altsvc frame + // would cause a segfault. This test is successful if a segfault + // does not occur. + client.write(altsvc.data, common.mustCall(() => { + client.destroy(); + })); + }); + }); + }); + + // An error may or may not be emitted on the client side, we don't care + // either way if it is, but we don't want to die if it is. + client.on('error', () => {}); + client.on('close', common.mustCall(() => server.close())); + client.resume(); +}); diff --git a/test/js/node/test/parallel/test-http2-many-writes-and-destroy.js b/test/js/node/test/parallel/test-http2-many-writes-and-destroy.js new file mode 100644 index 0000000000..78db76e001 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-many-writes-and-destroy.js @@ -0,0 +1,30 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const http2 = require('http2'); + +{ + const server = http2.createServer((req, res) => { + req.pipe(res); + }); + + server.listen(0, () => { + const url = `http://localhost:${server.address().port}`; + const client = http2.connect(url); + const req = client.request({ ':method': 'POST' }); + + for (let i = 0; i < 4000; i++) { + req.write(Buffer.alloc(6)); + } + + req.on('close', common.mustCall(() => { + console.log('(req onclose)'); + server.close(); + client.close(); + })); + + req.once('data', common.mustCall(() => req.destroy())); + }); +} diff --git a/test/js/node/test/parallel/test-http2-max-session-memory-leak.js b/test/js/node/test/parallel/test-http2-max-session-memory-leak.js new file mode 100644 index 0000000000..476c605783 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-max-session-memory-leak.js @@ -0,0 +1,46 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const http2 = require('http2'); + +// Regression test for https://github.com/nodejs/node/issues/27416. +// Check that received data is accounted for correctly in the maxSessionMemory +// mechanism. + +const bodyLength = 8192; +const maxSessionMemory = 1; // 1 MiB +const requestCount = 1000; + +const server = http2.createServer({ maxSessionMemory }); +server.on('stream', (stream) => { + stream.respond(); + stream.end(); +}); + +server.listen(common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`, { + maxSessionMemory + }); + + function request() { + return new Promise((resolve, reject) => { + const stream = client.request({ + ':method': 'POST', + 'content-length': bodyLength + }); + stream.on('error', reject); + stream.on('response', resolve); + stream.end('a'.repeat(bodyLength)); + }); + } + + (async () => { + for (let i = 0; i < requestCount; i++) { + await request(); + } + + client.close(); + server.close(); + })().then(common.mustCall()); +})); diff --git a/test/js/node/test/parallel/test-http2-methods.js b/test/js/node/test/parallel/test-http2-methods.js new file mode 100644 index 0000000000..936a264e99 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-methods.js @@ -0,0 +1,49 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +const server = h2.createServer(); + +const methods = ['GET', 'POST', 'PATCH', 'FOO', 'A_B_C']; +let expected = methods.length; + +// We use the lower-level API here +server.on('stream', common.mustCall(onStream, expected)); + +function onStream(stream, headers, flags) { + const method = headers[':method']; + assert.notStrictEqual(method, undefined); + assert(methods.includes(method), `method ${method} not included`); + stream.respond({ + 'content-type': 'text/html', + ':status': 200 + }); + stream.end('hello world'); +} + +server.listen(0); + +server.on('listening', common.mustCall(() => { + + const client = h2.connect(`http://localhost:${server.address().port}`); + + const headers = { ':path': '/' }; + + methods.forEach((method) => { + headers[':method'] = method; + const req = client.request(headers); + req.on('response', common.mustCall()); + req.resume(); + req.on('end', common.mustCall(() => { + if (--expected === 0) { + server.close(); + client.close(); + } + })); + req.end(); + }); +})); diff --git a/test/js/node/test/parallel/test-http2-multiplex.js b/test/js/node/test/parallel/test-http2-multiplex.js new file mode 100644 index 0000000000..4c157d0ede --- /dev/null +++ b/test/js/node/test/parallel/test-http2-multiplex.js @@ -0,0 +1,58 @@ +'use strict'; + +// Tests opening 100 concurrent simultaneous uploading streams over a single +// connection and makes sure that the data for each is appropriately echoed. + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); +const Countdown = require('../common/countdown'); + +const server = http2.createServer(); + +const count = 100; + +server.on('stream', common.mustCall((stream) => { + stream.respond(); + stream.pipe(stream); +}, count)); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + client.setMaxListeners(100); + + const countdown = new Countdown(count, () => { + server.close(); + client.close(); + }); + + function doRequest() { + const req = client.request({ ':method': 'POST' }); + + let data = ''; + req.setEncoding('utf8'); + req.on('data', (chunk) => data += chunk); + req.on('end', common.mustCall(() => { + assert.strictEqual(data, 'abcdefghij'); + })); + req.on('close', common.mustCall(() => countdown.dec())); + + let n = 0; + function writeChunk() { + if (n < 10) { + req.write(String.fromCharCode(97 + n)); + setTimeout(writeChunk, 10); + } else { + req.end(); + } + n++; + } + + writeChunk(); + } + + for (let n = 0; n < count; n++) + doRequest(); +})); diff --git a/test/js/node/test/parallel/test-http2-multistream-destroy-on-read-tls.js b/test/js/node/test/parallel/test-http2-multistream-destroy-on-read-tls.js new file mode 100644 index 0000000000..91cbec6b2d --- /dev/null +++ b/test/js/node/test/parallel/test-http2-multistream-destroy-on-read-tls.js @@ -0,0 +1,47 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const http2 = require('http2'); + +// Regression test for https://github.com/nodejs/node/issues/29353. +// Test that it’s okay for an HTTP2 + TLS server to destroy a stream instance +// while reading it. + +const server = http2.createSecureServer({ + key: fixtures.readKey('agent2-key.pem'), + cert: fixtures.readKey('agent2-cert.pem') +}); + +const filenames = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']; + +server.on('stream', common.mustCall((stream) => { + function write() { + stream.write('a'.repeat(10240)); + stream.once('drain', write); + } + write(); +}, filenames.length)); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`https://localhost:${server.address().port}`, { + ca: fixtures.readKey('agent2-cert.pem'), + servername: 'agent2' + }); + + let destroyed = 0; + for (const entry of filenames) { + const stream = client.request({ + ':path': `/${entry}` + }); + stream.once('data', common.mustCall(() => { + stream.destroy(); + + if (++destroyed === filenames.length) { + client.destroy(); + server.close(); + } + })); + } +})); diff --git a/test/js/node/test/parallel/test-http2-no-wanttrailers-listener.js b/test/js/node/test/parallel/test-http2-no-wanttrailers-listener.js new file mode 100644 index 0000000000..09293f2584 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-no-wanttrailers-listener.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const h2 = require('http2'); + +const server = h2.createServer(); + +// We use the lower-level API here +server.on('stream', common.mustCall(onStream)); + +function onStream(stream, headers, flags) { + stream.respond(undefined, { waitForTrailers: true }); + // There is no wantTrailers handler so this should close naturally + // without hanging. If the test completes without timing out, then + // it passes. + stream.end('ok'); +} + +server.listen(0); + +server.on('listening', common.mustCall(function() { + const client = h2.connect(`http://localhost:${this.address().port}`); + const req = client.request(); + req.resume(); + req.on('trailers', common.mustNotCall()); + req.on('close', common.mustCall(() => { + server.close(); + client.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-options-server-response.js b/test/js/node/test/parallel/test-http2-options-server-response.js new file mode 100644 index 0000000000..6f1ae1881d --- /dev/null +++ b/test/js/node/test/parallel/test-http2-options-server-response.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const h2 = require('http2'); + +class MyServerResponse extends h2.Http2ServerResponse { + status(code) { + return this.writeHead(code, { 'Content-Type': 'text/plain' }); + } +} + +const server = h2.createServer({ + Http2ServerResponse: MyServerResponse +}, (req, res) => { + res.status(200); + res.end(); +}); +server.listen(0); + +server.on('listening', common.mustCall(() => { + + const client = h2.connect(`http://localhost:${server.address().port}`); + const req = client.request({ ':path': '/' }); + + req.on('response', common.mustCall()); + + req.resume(); + req.on('end', common.mustCall(() => { + server.close(); + client.destroy(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-pipe-named-pipe.js b/test/js/node/test/parallel/test-http2-pipe-named-pipe.js new file mode 100644 index 0000000000..eb9b1b568c --- /dev/null +++ b/test/js/node/test/parallel/test-http2-pipe-named-pipe.js @@ -0,0 +1,54 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const http2 = require('http2'); +const fs = require('fs'); +const net = require('net'); + +// HTTP/2 servers can listen on a named pipe. + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +const loc = fixtures.path('person-large.jpg'); +const fn = tmpdir.resolve('person-large.jpg'); + +const server = http2.createServer(); + +server.on('stream', common.mustCall((stream) => { + const dest = stream.pipe(fs.createWriteStream(fn)); + + stream.on('end', common.mustCall(() => { + stream.respond(); + stream.end(); + })); + + dest.on('finish', common.mustCall(() => { + assert.strictEqual(fs.readFileSync(fn).length, + fs.readFileSync(loc).length); + })); +})); + +server.listen(common.PIPE, common.mustCall(() => { + const client = http2.connect('http://localhost', { + createConnection(url) { + return net.connect(server.address()); + } + }); + + const req = client.request({ ':method': 'POST' }); + req.on('response', common.mustCall()); + req.resume(); + + req.on('close', common.mustCall(() => { + server.close(); + client.close(); + })); + + const str = fs.createReadStream(loc); + str.on('end', common.mustCall()); + str.pipe(req); +})); diff --git a/test/js/node/test/parallel/test-http2-pipe.js b/test/js/node/test/parallel/test-http2-pipe.js new file mode 100644 index 0000000000..ebd89e23d8 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-pipe.js @@ -0,0 +1,47 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const http2 = require('http2'); +const fs = require('fs'); + +// Piping should work as expected with createWriteStream + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +const loc = fixtures.path('person-large.jpg'); +const fn = tmpdir.resolve('http2-url-tests.js'); + +const server = http2.createServer(); + +server.on('stream', common.mustCall((stream) => { + const dest = stream.pipe(fs.createWriteStream(fn)); + + dest.on('finish', () => { + assert.strictEqual(fs.readFileSync(loc).length, + fs.readFileSync(fn).length); + }); + stream.respond(); + stream.end(); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + + const req = client.request({ ':method': 'POST' }); + + req.on('response', common.mustCall()); + req.resume(); + + req.on('close', common.mustCall(() => { + server.close(); + client.close(); + })); + + const str = fs.createReadStream(loc); + str.on('end', common.mustCall()); + str.pipe(req); +})); diff --git a/test/js/node/test/parallel/test-http2-removed-header-stays-removed.js b/test/js/node/test/parallel/test-http2-removed-header-stays-removed.js new file mode 100644 index 0000000000..663249749a --- /dev/null +++ b/test/js/node/test/parallel/test-http2-removed-header-stays-removed.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) { common.skip('missing crypto'); } +const assert = require('assert'); +const http2 = require('http2'); + +const server = http2.createServer(common.mustCall((request, response) => { + response.setHeader('date', 'snacks o clock'); + response.end(); +})); + +server.listen(0, common.mustCall(() => { + const session = http2.connect(`http://localhost:${server.address().port}`); + const req = session.request(); + req.on('response', (headers, flags) => { + assert.strictEqual(headers.date, 'snacks o clock'); + }); + req.on('end', () => { + session.close(); + server.close(); + }); +})); diff --git a/test/js/node/test/parallel/test-http2-request-remove-connect-listener.js b/test/js/node/test/parallel/test-http2-request-remove-connect-listener.js new file mode 100644 index 0000000000..61de140c22 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-request-remove-connect-listener.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + + +const server = http2.createServer(); +server.on('stream', common.mustCall((stream) => { + stream.respond(); + stream.end(); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + client.once('connect', common.mustCall()); + + req.on('response', common.mustCall(() => { + assert.strictEqual(client.listenerCount('connect'), 0); + })); + req.on('close', common.mustCall(() => { + server.close(); + client.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-request-response-proto.js b/test/js/node/test/parallel/test-http2-request-response-proto.js new file mode 100644 index 0000000000..49b15dfc70 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-request-response-proto.js @@ -0,0 +1,18 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const { + Http2ServerRequest, + Http2ServerResponse, +} = http2; + +const protoRequest = { __proto__: Http2ServerRequest.prototype }; +const protoResponse = { __proto__: Http2ServerResponse.prototype }; + +assert.strictEqual(protoRequest instanceof Http2ServerRequest, true); +assert.strictEqual(protoResponse instanceof Http2ServerResponse, true); diff --git a/test/js/node/test/parallel/test-http2-res-corked.js b/test/js/node/test/parallel/test-http2-res-corked.js new file mode 100644 index 0000000000..5a6c623edf --- /dev/null +++ b/test/js/node/test/parallel/test-http2-res-corked.js @@ -0,0 +1,54 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) { common.skip('missing crypto'); } + +// Test for Http2ServerResponse#[writableCorked,cork,uncork] + +const { strictEqual } = require('assert'); +const http2 = require('http2'); + +{ + let corksLeft = 0; + const server = http2.createServer(common.mustCall((req, res) => { + strictEqual(res.writableCorked, corksLeft); + res.write(Buffer.from('1'.repeat(1024))); + res.cork(); + corksLeft++; + strictEqual(res.writableCorked, corksLeft); + res.write(Buffer.from('1'.repeat(1024))); + res.cork(); + corksLeft++; + strictEqual(res.writableCorked, corksLeft); + res.write(Buffer.from('1'.repeat(1024))); + res.cork(); + corksLeft++; + strictEqual(res.writableCorked, corksLeft); + res.write(Buffer.from('1'.repeat(1024))); + res.cork(); + corksLeft++; + strictEqual(res.writableCorked, corksLeft); + res.uncork(); + corksLeft--; + strictEqual(res.writableCorked, corksLeft); + res.uncork(); + corksLeft--; + strictEqual(res.writableCorked, corksLeft); + res.uncork(); + corksLeft--; + strictEqual(res.writableCorked, corksLeft); + res.uncork(); + corksLeft--; + strictEqual(res.writableCorked, corksLeft); + res.end(); + })); + server.listen(0, common.mustCall(() => { + const URL = `http://localhost:${server.address().port}`; + const client = http2.connect(URL); + const req = client.request(); + req.on('data', common.mustCall(2)); + req.on('end', common.mustCall(() => { + server.close(); + client.close(); + })); + })); +} diff --git a/test/js/node/test/parallel/test-http2-respond-file-204.js b/test/js/node/test/parallel/test-http2-respond-file-204.js new file mode 100644 index 0000000000..0c59b0e729 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-respond-file-204.js @@ -0,0 +1,42 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const http2 = require('http2'); + +const { + HTTP2_HEADER_CONTENT_TYPE, + HTTP2_HEADER_STATUS +} = http2.constants; + +const fname = fixtures.path('elipses.txt'); + +const server = http2.createServer(); +server.on('stream', (stream) => { + assert.throws(() => { + stream.respondWithFile(fname, { + [HTTP2_HEADER_STATUS]: 204, + [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain' + }); + }, { + code: 'ERR_HTTP2_PAYLOAD_FORBIDDEN', + name: 'Error', + message: 'Responses with 204 status must not have a payload' + }); + stream.respond({}); + stream.end(); +}); +server.listen(0, () => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + req.on('response', common.mustCall()); + req.on('data', common.mustNotCall()); + req.on('end', common.mustCall(() => { + client.close(); + server.close(); + })); + req.end(); +}); diff --git a/test/js/node/test/parallel/test-http2-respond-file-compat.js b/test/js/node/test/parallel/test-http2-respond-file-compat.js new file mode 100644 index 0000000000..0205f2d0d8 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-respond-file-compat.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const http2 = require('http2'); +const fixtures = require('../common/fixtures'); + +const fname = fixtures.path('elipses.txt'); + +const server = http2.createServer(common.mustCall((request, response) => { + response.stream.respondWithFile(fname); +})); +server.listen(0, () => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + req.on('response', common.mustCall()); + req.on('end', common.mustCall(() => { + client.close(); + server.close(); + })); + req.end(); + req.resume(); +}); diff --git a/test/js/node/test/parallel/test-http2-respond-file-error-dir.js b/test/js/node/test/parallel/test-http2-respond-file-error-dir.js new file mode 100644 index 0000000000..155e005432 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-respond-file-error-dir.js @@ -0,0 +1,41 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const http2 = require('http2'); +const assert = require('assert'); + +const server = http2.createServer(); +server.on('stream', (stream) => { + stream.respondWithFile(process.cwd(), { + 'content-type': 'text/plain' + }, { + onError(err) { + common.expectsError({ + code: 'ERR_HTTP2_SEND_FILE', + name: 'Error', + message: 'Directories cannot be sent' + })(err); + + stream.respond({ ':status': 404 }); + stream.end(); + }, + statCheck: common.mustNotCall() + }); +}); +server.listen(0, () => { + + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], 404); + })); + req.on('data', common.mustNotCall()); + req.on('end', common.mustCall(() => { + client.close(); + server.close(); + })); + req.end(); +}); diff --git a/test/js/node/test/parallel/test-http2-respond-file-error-pipe-offset.js b/test/js/node/test/parallel/test-http2-respond-file-error-pipe-offset.js new file mode 100644 index 0000000000..bd043e42f4 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-respond-file-error-pipe-offset.js @@ -0,0 +1,65 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +if (common.isWindows) + common.skip('no mkfifo on Windows'); +const child_process = require('child_process'); +const fs = require('fs'); +const http2 = require('http2'); +const assert = require('assert'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const pipeName = tmpdir.resolve('pipe'); + +const mkfifo = child_process.spawnSync('mkfifo', [ pipeName ]); +if (mkfifo.error && mkfifo.error.code === 'ENOENT') { + common.skip('missing mkfifo'); +} + +const server = http2.createServer(); +server.on('stream', (stream) => { + stream.respondWithFile(pipeName, { + 'content-type': 'text/plain' + }, { + offset: 10, + onError(err) { + common.expectsError({ + code: 'ERR_HTTP2_SEND_FILE_NOSEEK', + name: 'Error', + message: 'Offset or length can only be specified for regular files' + })(err); + + stream.respond({ ':status': 404 }); + stream.end(); + }, + statCheck: common.mustNotCall() + }); +}); +server.listen(0, () => { + + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], 404); + })); + req.on('data', common.mustNotCall()); + req.on('end', common.mustCall(() => { + client.close(); + server.close(); + })); + req.end(); +}); + +fs.writeFile(pipeName, 'Hello, world!\n', common.mustCall((err) => { + // It's possible for the reading end of the pipe to get the expected error + // and break everything down before we're finished, so allow `EPIPE` but + // no other errors. + if (err?.code !== 'EPIPE') { + assert.ifError(err); + } +})); diff --git a/test/js/node/test/parallel/test-http2-respond-with-file-connection-abort.js b/test/js/node/test/parallel/test-http2-respond-with-file-connection-abort.js new file mode 100644 index 0000000000..d5ed364570 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-respond-with-file-connection-abort.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); +const net = require('net'); + +const { + HTTP2_HEADER_CONTENT_TYPE +} = http2.constants; + +const server = http2.createServer(); +server.on('stream', common.mustCall((stream) => { + stream.on('error', (err) => assert.strictEqual(err.code, 'ECONNRESET')); + stream.respondWithFile(process.execPath, { + [HTTP2_HEADER_CONTENT_TYPE]: 'application/octet-stream' + }); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + + req.on('response', common.mustCall()); + req.once('data', common.mustCall(() => { + net.Socket.prototype.destroy.call(client.socket); + server.close(); + })); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http2-serve-file.js b/test/js/node/test/parallel/test-http2-serve-file.js new file mode 100644 index 0000000000..7b73fe639e --- /dev/null +++ b/test/js/node/test/parallel/test-http2-serve-file.js @@ -0,0 +1,79 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const http2 = require('http2'); +const fs = require('fs'); +const tls = require('tls'); + +const ajs_data = fixtures.readSync('a.js', 'utf8'); + +const { + HTTP2_HEADER_PATH, + HTTP2_HEADER_STATUS +} = http2.constants; + +const key = fixtures.readKey('agent8-key.pem', 'binary'); +const cert = fixtures.readKey('agent8-cert.pem', 'binary'); +const ca = fixtures.readKey('fake-startcom-root-cert.pem', 'binary'); + +const server = http2.createSecureServer({ key, cert }); + +server.on('stream', (stream, headers) => { + const name = headers[HTTP2_HEADER_PATH].slice(1); + const file = fixtures.path(name); + fs.stat(file, (err, stat) => { + if (err != null || stat.isDirectory()) { + stream.respond({ [HTTP2_HEADER_STATUS]: 404 }); + stream.end(); + } else { + stream.respond({ [HTTP2_HEADER_STATUS]: 200 }); + const str = fs.createReadStream(file); + str.pipe(stream); + } + }); +}); + +server.listen(0, () => { + + const secureContext = tls.createSecureContext({ ca }); + const client = http2.connect(`https://localhost:${server.address().port}`, + { secureContext }); + + let remaining = 2; + function maybeClose() { + if (--remaining === 0) { + client.close(); + server.close(); + } + } + + // Request for a file that does exist, response is 200 + const req1 = client.request({ [HTTP2_HEADER_PATH]: '/a.js' }, + { endStream: true }); + req1.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[HTTP2_HEADER_STATUS], 200); + })); + let req1_data = ''; + req1.setEncoding('utf8'); + req1.on('data', (chunk) => req1_data += chunk); + req1.on('end', common.mustCall(() => { + assert.strictEqual(req1_data, ajs_data); + maybeClose(); + })); + + // Request for a file that does not exist, response is 404 + const req2 = client.request({ [HTTP2_HEADER_PATH]: '/does_not_exist' }, + { endStream: true }); + req2.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[HTTP2_HEADER_STATUS], 404); + })); + req2.on('data', common.mustNotCall()); + req2.on('end', common.mustCall(() => maybeClose())); + +}); diff --git a/test/js/node/test/parallel/test-http2-server-async-dispose.js b/test/js/node/test/parallel/test-http2-server-async-dispose.js new file mode 100644 index 0000000000..4782e12e41 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-server-async-dispose.js @@ -0,0 +1,15 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const http2 = require('http2'); + +const server = http2.createServer(); + +server.listen(0, common.mustCall(() => { + server.on('close', common.mustCall()); + server[Symbol.asyncDispose]().then(common.mustCall()); +})); diff --git a/test/js/node/test/parallel/test-http2-server-close-callback.js b/test/js/node/test/parallel/test-http2-server-close-callback.js new file mode 100644 index 0000000000..e4cd24ce20 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-server-close-callback.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const Countdown = require('../common/countdown'); +const http2 = require('http2'); + +const server = http2.createServer(); + +let session; + +const countdown = new Countdown(2, () => { + server.close(common.mustSucceed()); + session.close(); +}); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + client.on('connect', common.mustCall(() => countdown.dec())); +})); + +server.on('session', common.mustCall((s) => { + session = s; + countdown.dec(); +})); diff --git a/test/js/node/test/parallel/test-http2-server-errors.js b/test/js/node/test/parallel/test-http2-server-errors.js new file mode 100644 index 0000000000..959ddccdc7 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-server-errors.js @@ -0,0 +1,88 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +// Errors should not be reported both in Http2ServerRequest +// and Http2ServerResponse + +{ + let expected = null; + + const server = h2.createServer(); + + server.on('stream', common.mustCall(function(stream) { + stream.on('error', common.mustCall(function(err) { + assert.strictEqual(err, expected); + })); + + stream.resume(); + stream.write('hello'); + + expected = new Error('kaboom'); + stream.destroy(expected); + server.close(); + })); + + server.listen(0, common.mustCall(function() { + const port = server.address().port; + + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(function() { + const headers = { + ':path': '/foobar', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}`, + }; + const request = client.request(headers); + request.on('data', common.mustCall(function(chunk) { + // Cause an error on the server side + client.destroy(); + })); + request.end(); + })); + })); +} + +{ + let expected = null; + + const server = h2.createServer(); + + process.on('uncaughtException', common.mustCall(function(err) { + assert.strictEqual(err.message, 'kaboom no handler'); + })); + + server.on('stream', common.mustCall(function(stream) { + // There is no 'error' handler, and this will crash + stream.write('hello'); + stream.resume(); + + expected = new Error('kaboom no handler'); + stream.destroy(expected); + server.close(); + })); + + server.listen(0, common.mustCall(function() { + const port = server.address().port; + + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(function() { + const headers = { + ':path': '/foobar', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}`, + }; + const request = client.request(headers); + request.on('data', common.mustCall(function(chunk) { + // Cause an error on the server side + client.destroy(); + })); + request.end(); + })); + })); +} diff --git a/test/js/node/test/parallel/test-http2-server-rst-before-respond.js b/test/js/node/test/parallel/test-http2-server-rst-before-respond.js new file mode 100644 index 0000000000..d551c7121f --- /dev/null +++ b/test/js/node/test/parallel/test-http2-server-rst-before-respond.js @@ -0,0 +1,37 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); + +const server = h2.createServer(); + +// We use the lower-level API here +server.on('stream', common.mustCall(onStream)); + +function onStream(stream, headers, flags) { + stream.close(); + + assert.throws(() => { + stream.additionalHeaders({ + ':status': 123, + 'abc': 123 + }); + }, { code: 'ERR_HTTP2_INVALID_STREAM' }); +} + +server.listen(0); + +server.on('listening', common.mustCall(() => { + const client = h2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); + req.on('headers', common.mustNotCall()); + req.on('close', common.mustCall(() => { + assert.strictEqual(h2.constants.NGHTTP2_NO_ERROR, req.rstCode); + server.close(); + client.close(); + })); + req.on('response', common.mustNotCall()); +})); diff --git a/test/js/node/test/parallel/test-http2-session-timeout.js b/test/js/node/test/parallel/test-http2-session-timeout.js new file mode 100644 index 0000000000..c1dacdcb45 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-session-timeout.js @@ -0,0 +1,64 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); +const hrtime = process.hrtime.bigint; +const NS_PER_MS = 1_000_000n; + +let requests = 0; +const mustNotCall = () => { + assert.fail(`Timeout after ${requests} request(s)`); +}; + +const server = http2.createServer(); +// Disable server timeout until first request. We will set the timeout based on +// how long the first request takes. +server.timeout = 0n; + +server.on('request', (req, res) => res.end()); +server.on('timeout', mustNotCall); + +server.listen(0, common.mustCall(() => { + const port = server.address().port; + + const url = `http://localhost:${port}`; + const client = http2.connect(url); + let startTime = hrtime(); + makeReq(); + + function makeReq() { + const request = client.request({ + ':path': '/foobar', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}`, + }); + request.resume(); + request.end(); + + requests += 1; + + request.on('end', () => { + const diff = hrtime() - startTime; + const milliseconds = diff / NS_PER_MS; + if (server.timeout === 0n) { + // Set the timeout now. First connection will take significantly longer + // than subsequent connections, so using the duration of the first + // connection as the timeout should be robust. Double it anyway for good + // measure. + server.timeout = milliseconds * 2n; + startTime = hrtime(); + makeReq(); + } else if (milliseconds < server.timeout * 2n) { + makeReq(); + } else { + server.removeListener('timeout', mustNotCall); + server.close(); + client.close(); + } + }); + } +})); diff --git a/test/js/node/test/parallel/test-http2-socket-proxy-handler-for-has.js b/test/js/node/test/parallel/test-http2-socket-proxy-handler-for-has.js new file mode 100644 index 0000000000..a8dfbfe07a --- /dev/null +++ b/test/js/node/test/parallel/test-http2-socket-proxy-handler-for-has.js @@ -0,0 +1,38 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) { + common.skip('missing crypto'); +} + +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const http2 = require('http2'); + +const serverOptions = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; +const server = http2.createSecureServer(serverOptions, common.mustCall( + (req, res) => { + const request = req; + assert.strictEqual(request.socket.encrypted, true); + assert.ok('encrypted' in request.socket); + res.end(); + } +)); +server.listen(common.mustCall(() => { + const port = server.address().port; + const client = http2.connect('https://localhost:' + port, { + ca: fixtures.readKey('agent1-cert.pem'), + rejectUnauthorized: false + }); + const req = client.request({}); + req.on('response', common.mustCall((headers, flags) => { + console.log(headers); + server.close(common.mustCall()); + })); + req.on('end', common.mustCall(() => { + client.close(); + })); + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http2-status-code.js b/test/js/node/test/parallel/test-http2-status-code.js new file mode 100644 index 0000000000..d3642b4ff0 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-status-code.js @@ -0,0 +1,41 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const codes = [ 200, 202, 300, 400, 404, 451, 500 ]; +let test = 0; + +const server = http2.createServer(); + +server.on('stream', common.mustCall((stream) => { + const status = codes[test++]; + stream.respond({ ':status': status }, { endStream: true }); +}, 7)); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + + let remaining = codes.length; + function maybeClose() { + if (--remaining === 0) { + client.close(); + server.close(); + } + } + + function doTest(expected) { + const req = client.request(); + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], expected); + })); + req.resume(); + req.on('end', common.mustCall(maybeClose)); + } + + for (let n = 0; n < codes.length; n++) + doTest(codes[n]); +})); diff --git a/test/js/node/test/parallel/test-http2-trailers-after-session-close.js b/test/js/node/test/parallel/test-http2-trailers-after-session-close.js new file mode 100644 index 0000000000..f7c7387eb0 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-trailers-after-session-close.js @@ -0,0 +1,51 @@ +'use strict'; + +// Fixes: https://github.com/nodejs/node/issues/42713 +const common = require('../common'); +if (!common.hasCrypto) { + common.skip('missing crypto'); +} +const assert = require('assert'); +const http2 = require('http2'); + +const { + HTTP2_HEADER_PATH, + HTTP2_HEADER_STATUS, + HTTP2_HEADER_METHOD, +} = http2.constants; + +const server = http2.createServer(); +server.on('stream', common.mustCall((stream) => { + server.close(); + stream.session.close(); + stream.on('wantTrailers', common.mustCall(() => { + stream.sendTrailers({ xyz: 'abc' }); + })); + + stream.respond({ [HTTP2_HEADER_STATUS]: 200 }, { waitForTrailers: true }); + stream.write('some data'); + stream.end(); +})); + +server.listen(0, common.mustCall(() => { + const port = server.address().port; + const client = http2.connect(`http://localhost:${port}`); + client.socket.on('close', common.mustCall()); + const req = client.request({ + [HTTP2_HEADER_PATH]: '/', + [HTTP2_HEADER_METHOD]: 'POST', + }); + req.end(); + req.on('response', common.mustCall()); + let data = ''; + req.on('data', (chunk) => { + data += chunk; + }); + req.on('end', common.mustCall(() => { + assert.strictEqual(data, 'some data'); + })); + req.on('trailers', common.mustCall((headers) => { + assert.strictEqual(headers.xyz, 'abc'); + })); + req.on('close', common.mustCall()); +})); diff --git a/test/js/node/test/parallel/test-http2-trailers.js b/test/js/node/test/parallel/test-http2-trailers.js new file mode 100644 index 0000000000..dba9aac12d --- /dev/null +++ b/test/js/node/test/parallel/test-http2-trailers.js @@ -0,0 +1,73 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const h2 = require('http2'); +const body = + '

this is some data

'; +const trailerKey = 'test-trailer'; +const trailerValue = 'testing'; + +const server = h2.createServer(); + +// We use the lower-level API here +server.on('stream', common.mustCall(onStream)); + +function onStream(stream, headers, flags) { + stream.on('trailers', common.mustCall((headers) => { + assert.strictEqual(headers[trailerKey], trailerValue); + stream.end(body); + })); + stream.respond({ + 'content-type': 'text/html', + ':status': 200 + }, { waitForTrailers: true }); + stream.on('wantTrailers', () => { + stream.sendTrailers({ [trailerKey]: trailerValue }); + assert.throws( + () => stream.sendTrailers({}), + { + code: 'ERR_HTTP2_TRAILERS_ALREADY_SENT', + name: 'Error' + } + ); + }); + + assert.throws( + () => stream.sendTrailers({}), + { + code: 'ERR_HTTP2_TRAILERS_NOT_READY', + name: 'Error' + } + ); +} + +server.listen(0); + +server.on('listening', common.mustCall(function() { + const client = h2.connect(`http://localhost:${this.address().port}`); + const req = client.request({ ':path': '/', ':method': 'POST' }, + { waitForTrailers: true }); + req.on('wantTrailers', () => { + req.sendTrailers({ [trailerKey]: trailerValue }); + }); + req.on('data', common.mustCall()); + req.on('trailers', common.mustCall((headers) => { + assert.strictEqual(headers[trailerKey], trailerValue); + })); + req.on('close', common.mustCall(() => { + assert.throws( + () => req.sendTrailers({}), + { + code: 'ERR_HTTP2_INVALID_STREAM', + name: 'Error' + } + ); + server.close(); + client.close(); + })); + req.end('data'); + +})); diff --git a/test/js/node/test/parallel/test-http2-unbound-socket-proxy.js b/test/js/node/test/parallel/test-http2-unbound-socket-proxy.js new file mode 100644 index 0000000000..74ca016944 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-unbound-socket-proxy.js @@ -0,0 +1,46 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); +const net = require('net'); + +const server = http2.createServer(); +server.on('stream', common.mustCall((stream) => { + stream.respond(); + stream.end('ok'); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const socket = client.socket; + const req = client.request(); + req.resume(); + req.on('close', common.mustCall(() => { + client.close(); + server.close(); + + // Tests to make sure accessing the socket proxy fails with an + // informative error. + setImmediate(common.mustCall(() => { + assert.throws(() => { + socket.example; // eslint-disable-line no-unused-expressions + }, { + code: 'ERR_HTTP2_SOCKET_UNBOUND' + }); + assert.throws(() => { + socket.example = 1; + }, { + code: 'ERR_HTTP2_SOCKET_UNBOUND' + }); + assert.throws(() => { + // eslint-disable-next-line no-unused-expressions + socket instanceof net.Socket; + }, { + code: 'ERR_HTTP2_SOCKET_UNBOUND' + }); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-write-callbacks.js b/test/js/node/test/parallel/test-http2-write-callbacks.js new file mode 100644 index 0000000000..eca7f00ea7 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-write-callbacks.js @@ -0,0 +1,37 @@ +'use strict'; + +// Verifies that write callbacks are called + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const server = http2.createServer(); + +server.on('stream', common.mustCall((stream) => { + stream.write('abc', common.mustCall(() => { + stream.end('xyz'); + })); + let actual = ''; + stream.setEncoding('utf8'); + stream.on('data', (chunk) => actual += chunk); + stream.on('end', common.mustCall(() => assert.strictEqual(actual, 'abcxyz'))); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request({ ':method': 'POST' }); + req.write('abc', common.mustCall(() => { + req.end('xyz'); + })); + let actual = ''; + req.setEncoding('utf8'); + req.on('data', (chunk) => actual += chunk); + req.on('end', common.mustCall(() => assert.strictEqual(actual, 'abcxyz'))); + req.on('close', common.mustCall(() => { + client.close(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-http2-write-empty-string.js b/test/js/node/test/parallel/test-http2-write-empty-string.js new file mode 100644 index 0000000000..ea591176a4 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-write-empty-string.js @@ -0,0 +1,41 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const server = http2.createServer(function(request, response) { + response.writeHead(200, { 'Content-Type': 'text/plain' }); + response.write('1\n'); + response.write(''); + response.write('2\n'); + response.write(''); + response.end('3\n'); + + this.close(); +}); + +server.listen(0, common.mustCall(function() { + const client = http2.connect(`http://localhost:${this.address().port}`); + const headers = { ':path': '/' }; + const req = client.request(headers).setEncoding('ascii'); + + let res = ''; + + req.on('response', common.mustCall(function(headers) { + assert.strictEqual(headers[':status'], 200); + })); + + req.on('data', (chunk) => { + res += chunk; + }); + + req.on('end', common.mustCall(function() { + assert.strictEqual(res, '1\n2\n3\n'); + client.close(); + })); + + req.end(); +})); diff --git a/test/js/node/test/parallel/test-http2-zero-length-header.js b/test/js/node/test/parallel/test-http2-zero-length-header.js new file mode 100644 index 0000000000..2e7876858a --- /dev/null +++ b/test/js/node/test/parallel/test-http2-zero-length-header.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const http2 = require('http2'); + +const server = http2.createServer(); +server.on('stream', (stream, headers) => { + assert.deepStrictEqual(headers, { + ':scheme': 'http', + ':authority': `localhost:${server.address().port}`, + ':method': 'GET', + ':path': '/', + 'bar': '', + '__proto__': null, + [http2.sensitiveHeaders]: [] + }); + stream.session.destroy(); + server.close(); +}); +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}/`); + client.request({ ':path': '/', '': 'foo', 'bar': '' }).end(); +})); diff --git a/test/js/node/test/parallel/test-http2-zero-length-write.js b/test/js/node/test/parallel/test-http2-zero-length-write.js new file mode 100644 index 0000000000..0b50715330 --- /dev/null +++ b/test/js/node/test/parallel/test-http2-zero-length-write.js @@ -0,0 +1,52 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); + +const { Readable } = require('stream'); + +function getSrc() { + const chunks = [ '', 'asdf', '', 'foo', '', 'bar', '' ]; + return new Readable({ + read() { + const chunk = chunks.shift(); + if (chunk !== undefined) + this.push(chunk); + else + this.push(null); + } + }); +} + +const expect = 'asdffoobar'; + +const server = http2.createServer(); +server.on('stream', common.mustCall((stream) => { + let actual = ''; + stream.respond(); + stream.resume(); + stream.setEncoding('utf8'); + stream.on('data', (chunk) => actual += chunk); + stream.on('end', common.mustCall(() => { + getSrc().pipe(stream); + assert.strictEqual(actual, expect); + })); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + let actual = ''; + const req = client.request({ ':method': 'POST' }); + req.on('response', common.mustCall()); + req.setEncoding('utf8'); + req.on('data', (chunk) => actual += chunk); + req.on('end', common.mustCall(() => { + assert.strictEqual(actual, expect); + server.close(); + client.close(); + })); + getSrc().pipe(req); +})); diff --git a/test/js/node/test/parallel/test-https-agent-constructor.js b/test/js/node/test/parallel/test-https-agent-constructor.js new file mode 100644 index 0000000000..69156ba0f6 --- /dev/null +++ b/test/js/node/test/parallel/test-https-agent-constructor.js @@ -0,0 +1,9 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const https = require('https'); + +assert.ok(https.Agent() instanceof https.Agent); diff --git a/test/js/node/test/parallel/test-https-agent-session-eviction.js b/test/js/node/test/parallel/test-https-agent-session-eviction.js new file mode 100644 index 0000000000..da56007105 --- /dev/null +++ b/test/js/node/test/parallel/test-https-agent-session-eviction.js @@ -0,0 +1,73 @@ +// Flags: --tls-min-v1.0 +'use strict'; + +const common = require('../common'); +const { readKey } = require('../common/fixtures'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const https = require('https'); +const { SSL_OP_NO_TICKET } = require('crypto').constants; + +const options = { + key: readKey('agent1-key.pem'), + cert: readKey('agent1-cert.pem'), + secureOptions: SSL_OP_NO_TICKET, + ciphers: 'RSA@SECLEVEL=0' +}; + +// Create TLS1.2 server +https.createServer(options, function(req, res) { + res.writeHead(200, { 'Connection': 'close' }); + res.end('ohai'); +}).listen(0, function() { + first(this); +}); + +// Do request and let agent cache the session +function first(server) { + const port = server.address().port; + const req = https.request({ + port: port, + rejectUnauthorized: false + }, function(res) { + res.resume(); + + server.close(function() { + faultyServer(port); + }); + }); + req.end(); +} + +// Create TLS1 server +function faultyServer(port) { + options.secureProtocol = 'TLSv1_method'; + https.createServer(options, function(req, res) { + res.writeHead(200, { 'Connection': 'close' }); + res.end('hello faulty'); + }).listen(port, function() { + second(this); + }); +} + +// Attempt to request using cached session +function second(server, session) { + const req = https.request({ + port: server.address().port, + ciphers: (common.hasOpenSSL31 ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), + rejectUnauthorized: false + }, function(res) { + res.resume(); + }); + + // Although we have a TLS 1.2 session to offer to the TLS 1.0 server, + // connection to the TLS 1.0 server should work. + req.on('response', common.mustCall(function(res) { + // The test is now complete for OpenSSL 1.1.0. + server.close(); + })); + + req.end(); +} diff --git a/test/js/node/test/parallel/test-https-agent.js b/test/js/node/test/parallel/test-https-agent.js new file mode 100644 index 0000000000..ce4bc6e5bd --- /dev/null +++ b/test/js/node/test/parallel/test-https-agent.js @@ -0,0 +1,72 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const https = require('https'); + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + + +const server = https.Server(options, (req, res) => { + res.writeHead(200); + res.end('hello world\n'); +}); + + +let responses = 0; +const N = 4; +const M = 4; + + +server.listen(0, () => { + for (let i = 0; i < N; i++) { + setTimeout(() => { + for (let j = 0; j < M; j++) { + https.get({ + path: '/', + port: server.address().port, + rejectUnauthorized: false + }, function(res) { + res.resume(); + assert.strictEqual(res.statusCode, 200); + if (++responses === N * M) server.close(); + }).on('error', (e) => { + throw e; + }); + } + }, i); + } +}); + + +process.on('exit', () => { + assert.strictEqual(responses, N * M); +}); diff --git a/test/js/node/test/parallel/test-https-client-get-url.js b/test/js/node/test/parallel/test-https-client-get-url.js new file mode 100644 index 0000000000..fb91a4f1e7 --- /dev/null +++ b/test/js/node/test/parallel/test-https-client-get-url.js @@ -0,0 +1,57 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +// Disable strict server certificate validation by the client +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + +const assert = require('assert'); +const https = require('https'); +const url = require('url'); + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + +const server = https.createServer(options, common.mustCall((req, res) => { + assert.strictEqual(req.method, 'GET'); + assert.strictEqual(req.url, '/foo?bar'); + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.write('hello\n'); + res.end(); +}, 3)); + +server.listen(0, common.mustCall(() => { + const u = `https://${common.localhostIPv4}:${server.address().port}/foo?bar`; + https.get(u, common.mustCall(() => { + https.get(url.parse(u), common.mustCall(() => { + https.get(new URL(u), common.mustCall(() => { + server.close(); + })); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-https-client-renegotiation-limit.js b/test/js/node/test/parallel/test-https-client-renegotiation-limit.js new file mode 100644 index 0000000000..35fcc6bfcc --- /dev/null +++ b/test/js/node/test/parallel/test-https-client-renegotiation-limit.js @@ -0,0 +1,111 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('node compiled without OpenSSL CLI.'); + +const assert = require('assert'); +const tls = require('tls'); +const https = require('https'); +const fixtures = require('../common/fixtures'); + +// Renegotiation as a protocol feature was dropped after TLS1.2. +tls.DEFAULT_MAX_VERSION = 'TLSv1.2'; + +// Renegotiation limits to test +const LIMITS = [0, 1, 2, 3, 5, 10, 16]; + +{ + let n = 0; + function next() { + if (n >= LIMITS.length) return; + tls.CLIENT_RENEG_LIMIT = LIMITS[n++]; + test(next); + } + next(); +} + +function test(next) { + const options = { + cert: fixtures.readKey('rsa_cert.crt'), + key: fixtures.readKey('rsa_private.pem'), + }; + + const server = https.createServer(options, (req, res) => { + const conn = req.connection; + conn.on('error', (err) => { + console.error(`Caught exception: ${err}`); + assert.match(err.message, /TLS session renegotiation attack/); + conn.destroy(); + }); + res.end('ok'); + }); + + server.listen(0, () => { + const agent = https.Agent({ + keepAlive: true, + }); + + let client; + let renegs = 0; + + const options = { + rejectUnauthorized: false, + agent, + }; + + const { port } = server.address(); + + https.get(`https://localhost:${port}/`, options, (res) => { + client = res.socket; + + client.on('close', (hadErr) => { + assert.strictEqual(hadErr, false); + assert.strictEqual(renegs, tls.CLIENT_RENEG_LIMIT + 1); + server.close(); + process.nextTick(next); + }); + + client.on('error', (err) => { + console.log('CLIENT ERR', err); + throw err; + }); + + spam(); + + // Simulate renegotiation attack + function spam() { + client.renegotiate({}, (err) => { + assert.ifError(err); + assert.ok(renegs <= tls.CLIENT_RENEG_LIMIT); + setImmediate(spam); + }); + renegs++; + } + }); + + }); +} diff --git a/test/js/node/test/parallel/test-https-connecting-to-http.js b/test/js/node/test/parallel/test-https-connecting-to-http.js new file mode 100644 index 0000000000..195ad38ed4 --- /dev/null +++ b/test/js/node/test/parallel/test-https-connecting-to-http.js @@ -0,0 +1,40 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// This tests the situation where you try to connect a https client +// to an http server. You should get an error and exit. +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const http = require('http'); +const https = require('https'); +const server = http.createServer(common.mustNotCall()); + +server.listen(0, common.mustCall(function() { + const req = https.get({ port: this.address().port }, common.mustNotCall()); + + req.on('error', common.mustCall(function(e) { + console.log('Got expected error: ', e.message); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-https-foafssl.js b/test/js/node/test/parallel/test-https-foafssl.js new file mode 100644 index 0000000000..d6dde97a41 --- /dev/null +++ b/test/js/node/test/parallel/test-https-foafssl.js @@ -0,0 +1,89 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('node compiled without OpenSSL CLI.'); + +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const https = require('https'); +const spawn = require('child_process').spawn; + +const options = { + key: fixtures.readKey('rsa_private.pem'), + cert: fixtures.readKey('rsa_cert.crt'), + requestCert: true, + rejectUnauthorized: false +}; + +const webIdUrl = 'URI:http://example.com/#me'; +const modulus = fixtures.readKey('rsa_cert_foafssl_b.modulus', 'ascii').replace(/\n/g, ''); +const exponent = fixtures.readKey('rsa_cert_foafssl_b.exponent', 'ascii').replace(/\n/g, ''); + +const CRLF = '\r\n'; +const body = 'hello world\n'; +let cert; + +const server = https.createServer(options, common.mustCall(function(req, res) { + console.log('got request'); + + cert = req.connection.getPeerCertificate(); + + assert.strictEqual(cert.subjectaltname, webIdUrl); + assert.strictEqual(cert.exponent, exponent); + assert.strictEqual(cert.modulus, modulus); + res.writeHead(200, { 'content-type': 'text/plain' }); + res.end(body, () => { console.log('stream finished'); }); + console.log('sent response'); +})); + +server.listen(0, function() { + const args = ['s_client', + '-quiet', + '-connect', `127.0.0.1:${this.address().port}`, + '-cert', fixtures.path('keys/rsa_cert_foafssl_b.crt'), + '-key', fixtures.path('keys/rsa_private_b.pem')]; + + const client = spawn(common.opensslCli, args); + + client.stdout.on('data', function(data) { + console.log('response received'); + const message = data.toString(); + const contents = message.split(CRLF + CRLF).pop(); + assert.strictEqual(body, contents); + server.close((e) => { + assert.ifError(e); + console.log('server closed'); + }); + console.log('server.close() called'); + }); + + client.stdin.write('GET /\r\n\r\n'); + + client.on('error', function(error) { + throw error; + }); +}); diff --git a/test/js/node/test/parallel/test-https-localaddress-bind-error.js b/test/js/node/test/parallel/test-https-localaddress-bind-error.js new file mode 100644 index 0000000000..57e4dd054d --- /dev/null +++ b/test/js/node/test/parallel/test-https-localaddress-bind-error.js @@ -0,0 +1,62 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const https = require('https'); + +const fixtures = require('../common/fixtures'); + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + +const invalidLocalAddress = '1.2.3.4'; + +const server = https.createServer(options, function(req, res) { + console.log(`Connect from: ${req.connection.remoteAddress}`); + + req.on('end', function() { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end(`You are from: ${req.connection.remoteAddress}`); + }); + req.resume(); +}); + +server.listen(0, '127.0.0.1', common.mustCall(function() { + https.request({ + host: 'localhost', + port: this.address().port, + path: '/', + method: 'GET', + localAddress: invalidLocalAddress + }, function(res) { + assert.fail('unexpectedly got response from server'); + }).on('error', common.mustCall(function(e) { + console.log(`client got error: ${e.message}`); + server.close(); + })).end(); +})); diff --git a/test/js/node/test/parallel/test-https-selfsigned-no-keycertsign-no-crash.js b/test/js/node/test/parallel/test-https-selfsigned-no-keycertsign-no-crash.js new file mode 100644 index 0000000000..2dd46ac878 --- /dev/null +++ b/test/js/node/test/parallel/test-https-selfsigned-no-keycertsign-no-crash.js @@ -0,0 +1,63 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +// This test starts an https server and tries +// to connect to it using a self-signed certificate. +// This certificate´s keyUsage does not include the keyCertSign +// bit, which used to crash node. The test ensures node +// will not crash. Key and certificate are from #37889. +// Note: This test assumes that the connection will succeed. + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const crypto = require('crypto'); + +// See #37990 for details on why this is problematic with FIPS. +if (process.config.variables.openssl_is_fips) + common.skip('Skipping as test uses non-fips compliant EC curve'); + +// This test will fail for OpenSSL < 1.1.1h +const minOpenSSL = 269488271; + +if (crypto.constants.OPENSSL_VERSION_NUMBER < minOpenSSL) + common.skip('OpenSSL < 1.1.1h'); + +const https = require('https'); +const path = require('path'); + +const key = + fixtures.readKey(path.join('selfsigned-no-keycertsign', 'key.pem')); + +const cert = + fixtures.readKey(path.join('selfsigned-no-keycertsign', 'cert.pem')); + +const serverOptions = { + key: key, + cert: cert +}; + +// Start the server +const httpsServer = https.createServer(serverOptions, (req, res) => { + res.writeHead(200); + res.end('hello world\n'); +}); +httpsServer.listen(0); + +httpsServer.on('listening', () => { + // Once the server started listening, built the client config + // with the server´s used port + const clientOptions = { + hostname: '127.0.0.1', + port: httpsServer.address().port, + ca: cert + }; + // Try to connect + const req = https.request(clientOptions, common.mustCall((res) => { + httpsServer.close(); + })); + + req.on('error', common.mustNotCall()); + req.end(); +}); diff --git a/test/js/node/test/parallel/test-https-socket-options.js b/test/js/node/test/parallel/test-https-socket-options.js new file mode 100644 index 0000000000..b41054d5aa --- /dev/null +++ b/test/js/node/test/parallel/test-https-socket-options.js @@ -0,0 +1,85 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const fixtures = require('../common/fixtures'); +const https = require('https'); +const http = require('http'); + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + +const body = 'hello world\n'; + +// Try first with http server + +const server_http = http.createServer(function(req, res) { + console.log('got HTTP request'); + res.writeHead(200, { 'content-type': 'text/plain' }); + res.end(body); +}); + + +server_http.listen(0, function() { + const req = http.request({ + port: this.address().port, + rejectUnauthorized: false + }, function(res) { + server_http.close(); + res.resume(); + }); + // These methods should exist on the request and get passed down to the socket + req.setNoDelay(true); + req.setTimeout(1000, () => {}); + req.setSocketKeepAlive(true, 1000); + req.end(); +}); + +// Then try https server (requires functions to be +// mirrored in tls.js's CryptoStream) + +const server_https = https.createServer(options, function(req, res) { + console.log('got HTTPS request'); + res.writeHead(200, { 'content-type': 'text/plain' }); + res.end(body); +}); + +server_https.listen(0, function() { + const req = https.request({ + port: this.address().port, + rejectUnauthorized: false + }, function(res) { + server_https.close(); + res.resume(); + }); + // These methods should exist on the request and get passed down to the socket + req.setNoDelay(true); + req.setTimeout(1000, () => {}); + req.setSocketKeepAlive(true, 1000); + req.end(); +}); diff --git a/test/js/node/test/parallel/test-https-truncate.js b/test/js/node/test/parallel/test-https-truncate.js new file mode 100644 index 0000000000..beed36cd7c --- /dev/null +++ b/test/js/node/test/parallel/test-https-truncate.js @@ -0,0 +1,72 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const https = require('https'); + +const key = fixtures.readKey('agent1-key.pem'); +const cert = fixtures.readKey('agent1-cert.pem'); + +// Number of bytes discovered empirically to trigger the bug +const data = Buffer.alloc(1024 * 32 + 1); + +httpsTest(); + +function httpsTest() { + const sopt = { key, cert }; + + const server = https.createServer(sopt, function(req, res) { + res.setHeader('content-length', data.length); + res.end(data); + server.close(); + }); + + server.listen(0, function() { + const opts = { port: this.address().port, rejectUnauthorized: false }; + https.get(opts).on('response', function(res) { + test(res); + }); + }); +} + + +const test = common.mustCall(function(res) { + res.on('end', common.mustCall(function() { + assert.strictEqual(res.readableLength, 0); + assert.strictEqual(bytes, data.length); + })); + + // Pause and then resume on each chunk, to ensure that there will be + // a lone byte hanging out at the very end. + let bytes = 0; + res.on('data', function(chunk) { + bytes += chunk.length; + this.pause(); + setTimeout(() => { this.resume(); }, 1); + }); +}); diff --git a/test/js/node/test/parallel/test-https-unix-socket-self-signed.js b/test/js/node/test/parallel/test-https-unix-socket-self-signed.js new file mode 100644 index 0000000000..9db92ac2ae --- /dev/null +++ b/test/js/node/test/parallel/test-https-unix-socket-self-signed.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const fixtures = require('../common/fixtures'); +const https = require('https'); +const options = { + cert: fixtures.readKey('rsa_cert.crt'), + key: fixtures.readKey('rsa_private.pem') +}; + +const server = https.createServer(options, common.mustCall((req, res) => { + res.end('bye\n'); + server.close(); +})); + +server.listen(common.PIPE, common.mustCall(() => { + https.get({ + socketPath: common.PIPE, + rejectUnauthorized: false + }); +})); diff --git a/test/js/node/test/parallel/test-icu-env.js b/test/js/node/test/parallel/test-icu-env.js new file mode 100644 index 0000000000..45b9fea8db --- /dev/null +++ b/test/js/node/test/parallel/test-icu-env.js @@ -0,0 +1,288 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { execFileSync } = require('child_process'); + +// system-icu should not be tested +const hasBuiltinICU = process.config.variables.icu_gyp_path === 'tools/icu/icu-generic.gyp'; +if (!hasBuiltinICU) + common.skip('system ICU'); + +// small-icu doesn't support non-English locales +const hasFullICU = (() => { + try { + const january = new Date(9e8); + const spanish = new Intl.DateTimeFormat('es', { month: 'long' }); + return spanish.format(january) === 'enero'; + } catch { + return false; + } +})(); +if (!hasFullICU) + common.skip('small ICU'); + +const icuVersionMajor = Number(process.config.variables.icu_ver_major ?? 0); +if (icuVersionMajor < 71) + common.skip('ICU too old'); + + +function runEnvOutside(addEnv, code, ...args) { + return execFileSync( + process.execPath, + ['-e', `process.stdout.write(String(${code}));`], + { env: { ...process.env, ...addEnv }, encoding: 'utf8' } + ); +} + +function runEnvInside(addEnv, func, ...args) { + Object.assign(process.env, addEnv); // side effects! + return func(...args); +} + +function isPack(array) { + const firstItem = array[0]; + return array.every((item) => item === firstItem); +} + +function isSet(array) { + const deduped = new Set(array); + return array.length === deduped.size; +} + + +const localesISO639 = [ + 'eng', 'cmn', 'hin', 'spa', + 'fra', 'arb', 'ben', 'rus', + 'por', 'urd', 'ind', 'deu', + 'jpn', 'pcm', 'mar', 'tel', +]; + +const locales = [ + 'en', 'zh', 'hi', 'es', + 'fr', 'ar', 'bn', 'ru', + 'pt', 'ur', 'id', 'de', + 'ja', 'pcm', 'mr', 'te', +]; + +// These must not overlap +const zones = [ + 'America/New_York', + 'UTC', + 'Asia/Irkutsk', + 'Australia/North', + 'Antarctica/South_Pole', +]; + + +assert.deepStrictEqual(Intl.getCanonicalLocales(localesISO639), locales); + + +// On some platforms these keep original locale (for example, 'January') +const enero = runEnvOutside( + { LANG: 'es' }, + 'new Intl.DateTimeFormat(undefined, { month: "long" } ).format(new Date(9e8))' +); +const janvier = runEnvOutside( + { LANG: 'fr' }, + 'new Intl.DateTimeFormat(undefined, { month: "long" } ).format(new Date(9e8))' +); +const isMockable = enero !== janvier; + +// Tests with mocked env +if (isMockable) { + assert.strictEqual( + isSet(zones.map((TZ) => runEnvOutside({ TZ }, 'new Date(333333333333).toString()'))), + true + ); + assert.strictEqual( + isSet(zones.map((TZ) => runEnvOutside({ TZ }, 'new Date(333333333333).toLocaleString()'))), + true + ); + assert.deepStrictEqual( + locales.map((LANG) => runEnvOutside({ LANG, TZ: 'Europe/Zurich' }, 'new Date(333333333333).toString()')), + [ + 'Fri Jul 25 1980 01:35:33 GMT+0100 (Central European Standard Time)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (中欧标准时间)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (मध्य यूरोपीय मानक समय)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (hora estándar de Europa central)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (heure normale d’Europe centrale)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (توقيت وسط أوروبا الرسمي)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (মধ্য ইউরোপীয় মানক সময়)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (Центральная Европа, стандартное время)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (Horário Padrão da Europa Central)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (وسطی یورپ کا معیاری وقت)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (Waktu Standar Eropa Tengah)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (Mitteleuropäische Normalzeit)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (中央ヨーロッパ標準時)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (Mídúl Yúrop Fíksd Taim)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (मध्‍य युरोपियन प्रमाण वेळ)', + 'Fri Jul 25 1980 01:35:33 GMT+0100 (సెంట్రల్ యూరోపియన్ ప్రామాణిక సమయం)', + ] + ); + assert.deepStrictEqual( + locales.map((LANG) => runEnvOutside({ LANG, TZ: 'Europe/Zurich' }, 'new Date(333333333333).toLocaleString()')), + [ + '7/25/1980, 1:35:33 AM', + '1980/7/25 01:35:33', + '25/7/1980, 1:35:33 am', + '25/7/1980, 1:35:33', + '25/07/1980 01:35:33', + '٢٥‏/٧‏/١٩٨٠، ١:٣٥:٣٣ ص', + '২৫/৭/১৯৮০, ১:৩৫:৩৩ AM', + '25.07.1980, 01:35:33', + '25/07/1980, 01:35:33', + '25/7/1980، 1:35:33 AM', + '25/7/1980, 01.35.33', + '25.7.1980, 01:35:33', + '1980/7/25 1:35:33', + '25/7/1980 01:35:33', + '२५/७/१९८०, १:३५:३३ AM', + '25/7/1980 1:35:33 AM', + ] + ); + assert.strictEqual( + runEnvOutside({ LANG: 'en' }, '["z", "ä"].sort(new Intl.Collator().compare)'), + 'ä,z' + ); + assert.strictEqual( + runEnvOutside({ LANG: 'sv' }, '["z", "ä"].sort(new Intl.Collator().compare)'), + 'z,ä' + ); + assert.deepStrictEqual( + locales.map( + (LANG) => runEnvOutside({ LANG, TZ: 'Europe/Zurich' }, 'new Intl.DateTimeFormat().format(333333333333)') + ), + [ + '7/25/1980', '1980/7/25', + '25/7/1980', '25/7/1980', + '25/07/1980', '٢٥‏/٧‏/١٩٨٠', + '২৫/৭/১৯৮০', '25.07.1980', + '25/07/1980', '25/7/1980', + '25/7/1980', '25.7.1980', + '1980/7/25', '25/7/1980', + '२५/७/१९८०', '25/7/1980', + ] + ); + assert.deepStrictEqual( + locales.map((LANG) => runEnvOutside({ LANG }, 'new Intl.DisplayNames(undefined, { type: "region" }).of("CH")')), + [ + 'Switzerland', '瑞士', + 'स्विट्ज़रलैंड', 'Suiza', + 'Suisse', 'سويسرا', + 'সুইজারল্যান্ড', 'Швейцария', + 'Suíça', 'سوئٹزر لینڈ', + 'Swiss', 'Schweiz', + 'スイス', 'Swítsaland', + 'स्वित्झर्लंड', 'స్విట్జర్లాండ్', + ] + ); + assert.deepStrictEqual( + locales.map((LANG) => runEnvOutside({ LANG }, 'new Intl.NumberFormat().format(275760.913)')), + [ + '275,760.913', '275,760.913', + '2,75,760.913', '275.760,913', + '275 760,913', '٢٧٥٬٧٦٠٫٩١٣', + '২,৭৫,৭৬০.৯১৩', '275 760,913', + '275.760,913', '275,760.913', + '275.760,913', '275.760,913', + '275,760.913', '275,760.913', + '२,७५,७६०.९१३', '2,75,760.913', + ] + ); + assert.deepStrictEqual( + locales.map((LANG) => runEnvOutside({ LANG }, 'new Intl.PluralRules().select(0)')), + [ + 'other', 'other', 'one', 'other', + 'one', 'zero', 'one', 'many', + 'one', 'other', 'other', 'other', + 'other', 'one', 'other', 'other', + ] + ); + assert.deepStrictEqual( + locales.map((LANG) => runEnvOutside({ LANG }, 'new Intl.RelativeTimeFormat().format(-586920.617, "hour")')), + [ + '586,920.617 hours ago', + '586,920.617小时前', + '5,86,920.617 घंटे पहले', + 'hace 586.920,617 horas', + 'il y a 586 920,617 heures', + 'قبل ٥٨٦٬٩٢٠٫٦١٧ ساعة', + '৫,৮৬,৯২০.৬১৭ ঘন্টা আগে', + '586 920,617 часа назад', + 'há 586.920,617 horas', + '586,920.617 گھنٹے پہلے', + '586.920,617 jam yang lalu', + 'vor 586.920,617 Stunden', + '586,920.617 時間前', + '586,920.617 áwa wé dọ́n pas', + '५,८६,९२०.६१७ तासांपूर्वी', + '5,86,920.617 గంటల క్రితం', + ] + ); +} + + +// Tests with process.env mutated inside +{ + // process.env.TZ is not intercepted in Workers + if (common.isMainThread) { + assert.strictEqual( + isSet(zones.map((TZ) => runEnvInside({ TZ }, () => new Date(333333333333).toString()))), + true + ); + assert.strictEqual( + isSet(zones.map((TZ) => runEnvInside({ TZ }, () => new Date(333333333333).toLocaleString()))), + true + ); + } else { + assert.strictEqual( + isPack(zones.map((TZ) => runEnvInside({ TZ }, () => new Date(333333333333).toString()))), + true + ); + assert.strictEqual( + isPack(zones.map((TZ) => runEnvInside({ TZ }, () => new Date(333333333333).toLocaleString()))), + true + ); + } + + assert.strictEqual( + isPack(locales.map((LANG) => runEnvInside({ LANG, TZ: 'Europe/Zurich' }, () => new Date(333333333333).toString()))), + true + ); + assert.strictEqual( + isPack(locales.map( + (LANG) => runEnvInside({ LANG, TZ: 'Europe/Zurich' }, () => new Date(333333333333).toLocaleString()) + )), + true + ); + assert.deepStrictEqual( + runEnvInside({ LANG: 'en' }, () => ['z', 'ä'].sort(new Intl.Collator().compare)), + runEnvInside({ LANG: 'sv' }, () => ['z', 'ä'].sort(new Intl.Collator().compare)) + ); + assert.strictEqual( + isPack(locales.map( + (LANG) => runEnvInside({ LANG, TZ: 'Europe/Zurich' }, () => new Intl.DateTimeFormat().format(333333333333)) + )), + true + ); + assert.strictEqual( + isPack(locales.map( + (LANG) => runEnvInside({ LANG }, () => new Intl.DisplayNames(undefined, { type: 'region' }).of('CH')) + )), + true + ); + assert.strictEqual( + isPack(locales.map((LANG) => runEnvInside({ LANG }, () => new Intl.NumberFormat().format(275760.913)))), + true + ); + assert.strictEqual( + isPack(locales.map((LANG) => runEnvInside({ LANG }, () => new Intl.PluralRules().select(0)))), + true + ); + assert.strictEqual( + isPack(locales.map( + (LANG) => runEnvInside({ LANG }, () => new Intl.RelativeTimeFormat().format(-586920.617, 'hour')) + )), + true + ); +} diff --git a/test/js/node/test/parallel/test-icu-punycode.js b/test/js/node/test/parallel/test-icu-punycode.js new file mode 100644 index 0000000000..29e88f9b9a --- /dev/null +++ b/test/js/node/test/parallel/test-icu-punycode.js @@ -0,0 +1,57 @@ +'use strict'; +// Flags: --expose-internals +const common = require('../common'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +const { internalBinding } = require('internal/test/binding'); +const icu = internalBinding('icu'); +const assert = require('assert'); + +// Test hasConverter method +assert(icu.hasConverter('utf-8'), + 'hasConverter should report converter exists for utf-8'); +assert(!icu.hasConverter('x'), + 'hasConverter should report converter does not exist for x'); + +const tests = require('../fixtures/url-idna.js'); +const fixtures = require('../fixtures/icu-punycode-toascii.json'); + +{ + for (const [i, { ascii, unicode }] of tests.entries()) { + assert.strictEqual(ascii, icu.toASCII(unicode), `toASCII(${i + 1})`); + assert.strictEqual(unicode, icu.toUnicode(ascii), `toUnicode(${i + 1})`); + assert.strictEqual(ascii, icu.toASCII(icu.toUnicode(ascii)), + `toASCII(toUnicode(${i + 1}))`); + assert.strictEqual(unicode, icu.toUnicode(icu.toASCII(unicode)), + `toUnicode(toASCII(${i + 1}))`); + } +} + +{ + for (const [i, test] of fixtures.entries()) { + if (typeof test === 'string') + continue; // skip comments + const { comment, input, output } = test; + let caseComment = `case ${i + 1}`; + if (comment) + caseComment += ` (${comment})`; + if (output === null) { + assert.throws( + () => icu.toASCII(input), + { + code: 'ERR_INVALID_ARG_VALUE', + name: 'TypeError', + message: 'Cannot convert name to ASCII' + } + ); + icu.toASCII(input, true); // Should not throw. + } else { + assert.strictEqual(icu.toASCII(input), output, `ToASCII ${caseComment}`); + assert.strictEqual(icu.toASCII(input, true), output, + `ToASCII ${caseComment} in lenient mode`); + } + icu.toUnicode(input); // Should not throw. + } +} diff --git a/test/js/node/test/parallel/test-icu-transcode.js b/test/js/node/test/parallel/test-icu-transcode.js new file mode 100644 index 0000000000..e9aced128e --- /dev/null +++ b/test/js/node/test/parallel/test-icu-transcode.js @@ -0,0 +1,90 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +const buffer = require('buffer'); +const assert = require('assert'); +const orig = Buffer.from('těst ☕', 'utf8'); + +// Test Transcoding +const tests = { + 'latin1': [0x74, 0x3f, 0x73, 0x74, 0x20, 0x3f], + 'ascii': [0x74, 0x3f, 0x73, 0x74, 0x20, 0x3f], + 'ucs2': [0x74, 0x00, 0x1b, 0x01, 0x73, + 0x00, 0x74, 0x00, 0x20, 0x00, + 0x15, 0x26] +}; + +for (const test in tests) { + const dest = buffer.transcode(orig, 'utf8', test); + assert.strictEqual(dest.length, tests[test].length, `utf8->${test} length`); + for (let n = 0; n < tests[test].length; n++) + assert.strictEqual(dest[n], tests[test][n], `utf8->${test} char ${n}`); +} + +{ + const dest = buffer.transcode(Buffer.from(tests.ucs2), 'ucs2', 'utf8'); + assert.strictEqual(dest.toString(), orig.toString()); +} + +{ + const utf8 = Buffer.from('€'.repeat(4000), 'utf8'); + const ucs2 = Buffer.from('€'.repeat(4000), 'ucs2'); + const utf8_to_ucs2 = buffer.transcode(utf8, 'utf8', 'ucs2'); + const ucs2_to_utf8 = buffer.transcode(ucs2, 'ucs2', 'utf8'); + assert.deepStrictEqual(utf8, ucs2_to_utf8); + assert.deepStrictEqual(ucs2, utf8_to_ucs2); + assert.strictEqual(ucs2_to_utf8.toString('utf8'), + utf8_to_ucs2.toString('ucs2')); +} + +assert.throws( + () => buffer.transcode(null, 'utf8', 'ascii'), + { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "source" argument must be an instance of Buffer ' + + 'or Uint8Array. Received null' + } +); + +assert.throws( + () => buffer.transcode(Buffer.from('a'), 'b', 'utf8'), + /^Error: Unable to transcode Buffer \[U_ILLEGAL_ARGUMENT_ERROR\]/ +); + +assert.throws( + () => buffer.transcode(Buffer.from('a'), 'uf8', 'b'), + /^Error: Unable to transcode Buffer \[U_ILLEGAL_ARGUMENT_ERROR\]$/ +); + +assert.deepStrictEqual( + buffer.transcode(Buffer.from('hi', 'ascii'), 'ascii', 'utf16le'), + Buffer.from('hi', 'utf16le')); +assert.deepStrictEqual( + buffer.transcode(Buffer.from('hi', 'latin1'), 'latin1', 'utf16le'), + Buffer.from('hi', 'utf16le')); +assert.deepStrictEqual( + buffer.transcode(Buffer.from('hä', 'latin1'), 'latin1', 'utf16le'), + Buffer.from('hä', 'utf16le')); + +// Test that Uint8Array arguments are okay. +{ + const uint8array = new Uint8Array([...Buffer.from('hä', 'latin1')]); + assert.deepStrictEqual( + buffer.transcode(uint8array, 'latin1', 'utf16le'), + Buffer.from('hä', 'utf16le')); +} + +{ + const dest = buffer.transcode(new Uint8Array(), 'utf8', 'latin1'); + assert.strictEqual(dest.length, 0); +} + +// Test that it doesn't crash +{ + buffer.transcode(new buffer.SlowBuffer(1), 'utf16le', 'ucs2'); +} diff --git a/test/js/node/test/parallel/test-inspect-support-for-node_options.js b/test/js/node/test/parallel/test-inspect-support-for-node_options.js new file mode 100644 index 0000000000..05bb3b2c42 --- /dev/null +++ b/test/js/node/test/parallel/test-inspect-support-for-node_options.js @@ -0,0 +1,30 @@ +'use strict'; +const common = require('../common'); +const cluster = require('cluster'); +const assert = require('assert'); + +common.skipIfInspectorDisabled(); + +checkForInspectSupport('--inspect'); + +function checkForInspectSupport(flag) { + + const nodeOptions = JSON.stringify(flag); + const numWorkers = 2; + process.env.NODE_OPTIONS = flag; + + if (cluster.isPrimary) { + for (let i = 0; i < numWorkers; i++) { + cluster.fork(); + } + + cluster.on('online', (worker) => { + worker.disconnect(); + }); + + cluster.on('exit', common.mustCall((worker, code, signal) => { + const errMsg = `For NODE_OPTIONS ${nodeOptions}, failed to start cluster`; + assert.strictEqual(worker.exitedAfterDisconnect, true, errMsg); + }, numWorkers)); + } +} diff --git a/test/js/node/test/parallel/test-inspector-has-inspector-false.js b/test/js/node/test/parallel/test-inspector-has-inspector-false.js new file mode 100644 index 0000000000..56a50408bb --- /dev/null +++ b/test/js/node/test/parallel/test-inspector-has-inspector-false.js @@ -0,0 +1,15 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); + +if (process.features.inspector) { + common.skip('V8 inspector is enabled'); +} + +const inspector = require('internal/util/inspector'); + +inspector.sendInspectorCommand( + common.mustNotCall('Inspector callback should not be called'), + common.mustCall(1), +); diff --git a/test/js/node/test/parallel/test-inspector-stops-no-file.js b/test/js/node/test/parallel/test-inspector-stops-no-file.js new file mode 100644 index 0000000000..9ec09fb15d --- /dev/null +++ b/test/js/node/test/parallel/test-inspector-stops-no-file.js @@ -0,0 +1,16 @@ +'use strict'; +require('../common'); + +const spawn = require('child_process').spawn; + +const child = spawn(process.execPath, + [ '--inspect', 'no-such-script.js' ], + { 'stdio': 'inherit' }); + +function signalHandler() { + child.kill(); + process.exit(1); +} + +process.on('SIGINT', signalHandler); +process.on('SIGTERM', signalHandler); diff --git a/test/js/node/test/parallel/test-instanceof.js b/test/js/node/test/parallel/test-instanceof.js new file mode 100644 index 0000000000..5a8b588e7d --- /dev/null +++ b/test/js/node/test/parallel/test-instanceof.js @@ -0,0 +1,11 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + + +// Regression test for instanceof, see +// https://github.com/nodejs/node/issues/7592 +const F = () => {}; +F.prototype = {}; +assert({ __proto__: F.prototype } instanceof F); diff --git a/test/js/node/test/parallel/test-internal-module-require.js b/test/js/node/test/parallel/test-internal-module-require.js new file mode 100644 index 0000000000..c6e2057d3d --- /dev/null +++ b/test/js/node/test/parallel/test-internal-module-require.js @@ -0,0 +1,112 @@ +'use strict'; + +// Flags: --expose-internals +// This verifies that +// 1. We do not leak internal modules unless the --require-internals option +// is on. +// 2. We do not accidentally leak any modules to the public global scope. +// 3. Deprecated modules are properly deprecated. + +const common = require('../common'); + +if (!common.isMainThread) { + common.skip('Cannot test the existence of --expose-internals from worker'); +} + +const assert = require('assert'); +const fork = require('child_process').fork; + +const expectedPublicModules = new Set([ + '_http_agent', + '_http_client', + '_http_common', + '_http_incoming', + '_http_outgoing', + '_http_server', + '_stream_duplex', + '_stream_passthrough', + '_stream_readable', + '_stream_transform', + '_stream_wrap', + '_stream_writable', + '_tls_common', + '_tls_wrap', + 'assert', + 'async_hooks', + 'buffer', + 'child_process', + 'cluster', + 'console', + 'constants', + 'crypto', + 'dgram', + 'dns', + 'domain', + 'events', + 'fs', + 'http', + 'http2', + 'https', + 'inspector', + 'module', + 'net', + 'os', + 'path', + 'perf_hooks', + 'process', + 'punycode', + 'querystring', + 'readline', + 'repl', + 'stream', + 'string_decoder', + 'sys', + 'timers', + 'tls', + 'trace_events', + 'tty', + 'url', + 'util', + 'v8', + 'vm', + 'worker_threads', + 'zlib', +]); + +if (process.argv[2] === 'child') { + assert(!process.execArgv.includes('--expose-internals')); + process.once('message', ({ allBuiltins }) => { + const publicModules = new Set(); + for (const id of allBuiltins) { + if (id.startsWith('internal/')) { + assert.throws(() => { + require(id); + }, { + code: 'MODULE_NOT_FOUND', + message: `Cannot find module '${id}'` + }); + } else { + require(id); + publicModules.add(id); + } + } + assert(allBuiltins.length > publicModules.size); + // Make sure all the public modules are available through + // require('module').builtinModules + assert.deepStrictEqual( + publicModules, + new Set(require('module').builtinModules) + ); + assert.deepStrictEqual(publicModules, expectedPublicModules); + }); +} else { + assert(process.execArgv.includes('--expose-internals')); + const child = fork(__filename, ['child'], { + execArgv: [] + }); + const { builtinModules } = require('module'); + // When --expose-internals is on, require('module').builtinModules + // contains internal modules. + const message = { allBuiltins: builtinModules }; + child.send(message); +} diff --git a/test/js/node/test/parallel/test-internal-process-binding.js b/test/js/node/test/parallel/test-internal-process-binding.js new file mode 100644 index 0000000000..09e3f31096 --- /dev/null +++ b/test/js/node/test/parallel/test-internal-process-binding.js @@ -0,0 +1,10 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(undefined, process._internalBinding); +assert.strictEqual(undefined, process.internalBinding); +assert.throws(() => { + process.binding('module_wrap'); +}, /No such module/); diff --git a/test/js/node/test/parallel/test-intl-v8BreakIterator.js b/test/js/node/test/parallel/test-intl-v8BreakIterator.js new file mode 100644 index 0000000000..257d6b2a76 --- /dev/null +++ b/test/js/node/test/parallel/test-intl-v8BreakIterator.js @@ -0,0 +1,10 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +assert(!('v8BreakIterator' in Intl)); +assert(!vm.runInNewContext('"v8BreakIterator" in Intl')); diff --git a/test/js/node/test/parallel/test-intl.js b/test/js/node/test/parallel/test-intl.js new file mode 100644 index 0000000000..7d1742f2c7 --- /dev/null +++ b/test/js/node/test/parallel/test-intl.js @@ -0,0 +1,163 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { execFile } = require('child_process'); + +// Does node think that i18n was enabled? +let enablei18n = process.config.variables.v8_enable_i18n_support; +if (enablei18n === undefined) { + enablei18n = 0; +} + +// Returns true if no specific locale ids were configured (i.e. "all") +// Else, returns true if loc is in the configured list +// Else, returns false +function haveLocale(loc) { + const locs = process.config.variables.icu_locales.split(','); + return locs.includes(loc); +} + +// Always run these. They should always pass, even if the locale +// param is ignored. +assert.strictEqual('Ç'.toLocaleLowerCase('el'), 'ç'); +assert.strictEqual('Ç'.toLocaleLowerCase('tr'), 'ç'); +assert.strictEqual('Ç'.toLowerCase(), 'ç'); + +assert.strictEqual('ç'.toLocaleUpperCase('el'), 'Ç'); +assert.strictEqual('ç'.toLocaleUpperCase('tr'), 'Ç'); +assert.strictEqual('ç'.toUpperCase(), 'Ç'); + +if (!common.hasIntl) { + const erMsg = + `"Intl" object is NOT present but v8_enable_i18n_support is ${enablei18n}`; + assert.strictEqual(enablei18n, 0, erMsg); + common.skip('Intl tests because Intl object not present.'); +} else { + const erMsg = + `"Intl" object is present but v8_enable_i18n_support is ${ + enablei18n}. Is this test out of date?`; + assert.strictEqual(enablei18n, 1, erMsg); + + // Construct a new date at the beginning of Unix time + const date0 = new Date(0); + + // Use the GMT time zone + const GMT = 'Etc/GMT'; + + // Construct an English formatter. Should format to "Jan 70" + const dtf = new Intl.DateTimeFormat(['en'], { + timeZone: GMT, + month: 'short', + year: '2-digit' + }); + + // If list is specified and doesn't contain 'en' then return. + if (process.config.variables.icu_locales && !haveLocale('en')) { + common.printSkipMessage( + 'detailed Intl tests because English is not listed as supported.'); + // Smoke test. Does it format anything, or fail? + console.log(`Date(0) formatted to: ${dtf.format(date0)}`); + return; + } + + // Check casing + { + assert.strictEqual('I'.toLocaleLowerCase('tr'), 'ı'); + } + + // Check with toLocaleString + { + const localeString = dtf.format(date0); + assert.strictEqual(localeString, 'Jan 70'); + } + // Options to request GMT + const optsGMT = { timeZone: GMT }; + + // Test format + { + const localeString = date0.toLocaleString(['en'], optsGMT); + assert.strictEqual(localeString, '1/1/1970, 12:00:00 AM'); + } + // number format + { + const numberFormat = new Intl.NumberFormat(['en']).format(12345.67890); + assert.strictEqual(numberFormat, '12,345.679'); + } + // If list is specified and doesn't contain 'en-US' then return. + if (process.config.variables.icu_locales && !haveLocale('en-US')) { + common.printSkipMessage('detailed Intl tests because American English is ' + + 'not listed as supported.'); + return; + } + // Number format resolved options + { + const numberFormat = new Intl.NumberFormat('en-US', { style: 'percent' }); + const resolvedOptions = numberFormat.resolvedOptions(); + assert.strictEqual(resolvedOptions.locale, 'en-US'); + assert.strictEqual(resolvedOptions.style, 'percent'); + } + // Significant Digits + { + const loc = ['en-US']; + const opts = { maximumSignificantDigits: 4 }; + const num = 10.001; + const numberFormat = new Intl.NumberFormat(loc, opts).format(num); + assert.strictEqual(numberFormat, '10'); + } + + const collOpts = { sensitivity: 'base', ignorePunctuation: true }; + const coll = new Intl.Collator(['en'], collOpts); + + // Ignore punctuation + assert.strictEqual(coll.compare('blackbird', 'black-bird'), 0); + // Compare less + assert.strictEqual(coll.compare('blackbird', 'red-bird'), -1); + // Compare greater + assert.strictEqual(coll.compare('bluebird', 'blackbird'), 1); + // Ignore case + assert.strictEqual(coll.compare('Bluebird', 'bluebird'), 0); + // `ffi` ligature (contraction) + assert.strictEqual(coll.compare('\ufb03', 'ffi'), 0); + + { + // Regression test for https://github.com/nodejs/node/issues/27379 + const env = { ...process.env, LC_ALL: 'ja' }; + execFile( + process.execPath, ['-p', 'new Date().toLocaleString()'], + { env }, + common.mustSucceed() + ); + } + + { + // Regression test for https://github.com/nodejs/node/issues/27418 + const env = { ...process.env, LC_ALL: 'fr@EURO' }; + execFile( + process.execPath, + ['-p', 'new Intl.NumberFormat().resolvedOptions().locale'], + { env }, + common.mustSucceed() + ); + } +} diff --git a/test/js/node/test/parallel/test-kill-segfault-freebsd.js b/test/js/node/test/parallel/test-kill-segfault-freebsd.js new file mode 100644 index 0000000000..e17b00741b --- /dev/null +++ b/test/js/node/test/parallel/test-kill-segfault-freebsd.js @@ -0,0 +1,19 @@ +'use strict'; +require('../common'); + +// This test ensures Node.js doesn't crash on hitting Ctrl+C in order to +// terminate the currently running process (especially on FreeBSD). +// https://github.com/nodejs/node-v0.x-archive/issues/9326 + +const assert = require('assert'); +const child_process = require('child_process'); + +// NOTE: Was crashing on FreeBSD +const cp = child_process.spawn(process.execPath, [ + '-e', + 'process.kill(process.pid, "SIGINT")', +]); + +cp.on('exit', function(code) { + assert.notStrictEqual(code, 0); +}); diff --git a/test/js/node/test/parallel/test-listen-fd-detached-inherit.js b/test/js/node/test/parallel/test-listen-fd-detached-inherit.js new file mode 100644 index 0000000000..2a8e70f0f9 --- /dev/null +++ b/test/js/node/test/parallel/test-listen-fd-detached-inherit.js @@ -0,0 +1,118 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (common.isWindows) + common.skip('This test is disabled on windows.'); + +const assert = require('assert'); +const http = require('http'); +const net = require('net'); +const spawn = require('child_process').spawn; + +switch (process.argv[2]) { + case 'child': return child(); + case 'parent': return parent(); + default: return test(); +} + +// Spawn the parent, and listen for it to tell us the pid of the child. +// WARNING: This is an example of listening on some arbitrary FD number +// that has already been bound elsewhere in advance. However, binding +// server handles to stdio fd's is NOT a good or reliable way to do +// concurrency in HTTP servers! Use the cluster module, or if you want +// a more low-level approach, use child process IPC manually. +function test() { + const parent = spawn(process.execPath, [__filename, 'parent'], { + stdio: [ 0, 'pipe', 2 ] + }); + let json = ''; + parent.stdout.on('data', function(c) { + json += c.toString(); + if (json.includes('\n')) next(); + }); + function next() { + console.error('output from parent = %s', json); + const child = JSON.parse(json); + // Now make sure that we can request to the subprocess, then kill it. + http.get({ + server: 'localhost', + port: child.port, + path: '/', + }).on('response', function(res) { + let s = ''; + res.on('data', function(c) { + s += c.toString(); + }); + res.on('end', function() { + // Kill the subprocess before we start doing asserts. + // It's really annoying when tests leave orphans! + process.kill(child.pid, 'SIGKILL'); + try { + parent.kill(); + } catch { + // Continue regardless of error. + } + + assert.strictEqual(s, 'hello from child\n'); + assert.strictEqual(res.statusCode, 200); + }); + }); + } +} + +// Listen on port, and then pass the handle to the detached child. +// Then output the child's pid, and immediately exit. +function parent() { + const server = net.createServer(function(conn) { + conn.end('HTTP/1.1 403 Forbidden\r\n\r\nI got problems.\r\n'); + throw new Error('Should not see connections on parent'); + }).listen(0, function() { + console.error('server listening on %d', this.address().port); + + const child = spawn(process.execPath, [__filename, 'child'], { + stdio: [ 0, 1, 2, server._handle ], + detached: true + }); + + console.log('%j\n', { pid: child.pid, port: this.address().port }); + + // Now close the parent, so that the child is the only thing + // referencing that handle. Note that connections will still + // be accepted, because the child has the fd open, but the parent + // will exit gracefully. + server.close(); + child.unref(); + }); +} + +// Run as a child of the parent() mode. +function child() { + // Start a server on fd=3 + http.createServer(function(req, res) { + console.error('request on child'); + console.error('%s %s', req.method, req.url, req.headers); + res.end('hello from child\n'); + }).listen({ fd: 3 }, function() { + console.error('child listening on fd=3'); + }); +} diff --git a/test/js/node/test/parallel/test-listen-fd-detached.js b/test/js/node/test/parallel/test-listen-fd-detached.js new file mode 100644 index 0000000000..fba96a112f --- /dev/null +++ b/test/js/node/test/parallel/test-listen-fd-detached.js @@ -0,0 +1,115 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (common.isWindows) + common.skip('This test is disabled on windows.'); + +const assert = require('assert'); +const http = require('http'); +const net = require('net'); +const spawn = require('child_process').spawn; + +switch (process.argv[2]) { + case 'child': return child(); + case 'parent': return parent(); + default: return test(); +} + +// Spawn the parent, and listen for it to tell us the pid of the child. +// WARNING: This is an example of listening on some arbitrary FD number +// that has already been bound elsewhere in advance. However, binding +// server handles to stdio fd's is NOT a good or reliable way to do +// concurrency in HTTP servers! Use the cluster module, or if you want +// a more low-level approach, use child process IPC manually. +function test() { + const parent = spawn(process.execPath, [__filename, 'parent'], { + stdio: [ 0, 'pipe', 2 ] + }); + let json = ''; + parent.stdout.on('data', function(c) { + json += c.toString(); + if (json.includes('\n')) next(); + }); + function next() { + console.error('output from parent = %s', json); + const child = JSON.parse(json); + // Now make sure that we can request to the subprocess, then kill it. + http.get({ + server: 'localhost', + port: child.port, + path: '/', + }).on('response', function(res) { + let s = ''; + res.on('data', function(c) { + s += c.toString(); + }); + res.on('end', function() { + // Kill the subprocess before we start doing asserts. + // it's really annoying when tests leave orphans! + process.kill(child.pid, 'SIGKILL'); + try { + parent.kill(); + } catch { + // Continue regardless of error. + } + + assert.strictEqual(s, 'hello from child\n'); + assert.strictEqual(res.statusCode, 200); + }); + }); + } +} + +function parent() { + const server = net.createServer(function(conn) { + console.error('connection on parent'); + conn.end('hello from parent\n'); + }).listen(0, function() { + console.error('server listening on %d', this.address().port); + + const child = spawn(process.execPath, [__filename, 'child'], { + stdio: [ 'ignore', 'ignore', 'ignore', server._handle ], + detached: true + }); + + console.log('%j\n', { pid: child.pid, port: this.address().port }); + + // Now close the parent, so that the child is the only thing + // referencing that handle. Note that connections will still + // be accepted, because the child has the fd open, but the parent + // will exit gracefully. + server.close(); + child.unref(); + }); +} + +function child() { + // Start a server on fd=3 + http.createServer(function(req, res) { + console.error('request on child'); + console.error('%s %s', req.method, req.url, req.headers); + res.end('hello from child\n'); + }).listen({ fd: 3 }, function() { + console.error('child listening on fd=3'); + }); +} diff --git a/test/js/node/test/parallel/test-memory-usage-emfile.js b/test/js/node/test/parallel/test-memory-usage-emfile.js new file mode 100644 index 0000000000..05b112e918 --- /dev/null +++ b/test/js/node/test/parallel/test-memory-usage-emfile.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); + +// On IBMi, the rss memory always returns zero +if (common.isIBMi) + common.skip('On IBMi, the rss memory always returns zero'); + +const assert = require('assert'); + +const fs = require('fs'); + +const files = []; + +while (files.length < 256) + files.push(fs.openSync(__filename, 'r')); + +const r = process.memoryUsage.rss(); +assert.strictEqual(r > 0, true); diff --git a/test/js/node/test/parallel/test-memory-usage.js b/test/js/node/test/parallel/test-memory-usage.js new file mode 100644 index 0000000000..8e5ea4de5b --- /dev/null +++ b/test/js/node/test/parallel/test-memory-usage.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Flags: --predictable-gc-schedule +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const r = process.memoryUsage(); +// On IBMi, the rss memory always returns zero +if (!common.isIBMi) { + assert.ok(r.rss > 0); + assert.ok(process.memoryUsage.rss() > 0); +} + +assert.ok(r.heapTotal > 0); +assert.ok(r.heapUsed > 0); +assert.ok(r.external > 0); + +assert.strictEqual(typeof r.arrayBuffers, 'number'); +if (r.arrayBuffers > 0) { + const size = 10 * 1024 * 1024; + // eslint-disable-next-line no-unused-vars + const ab = new ArrayBuffer(size); + + const after = process.memoryUsage(); + assert.ok(after.external - r.external >= size, + `${after.external} - ${r.external} >= ${size}`); + assert.strictEqual(after.arrayBuffers - r.arrayBuffers, size, + `${after.arrayBuffers} - ${r.arrayBuffers} === ${size}`); +} diff --git a/test/js/node/test/parallel/test-messagechannel.js b/test/js/node/test/parallel/test-messagechannel.js new file mode 100644 index 0000000000..4f92924daa --- /dev/null +++ b/test/js/node/test/parallel/test-messagechannel.js @@ -0,0 +1,12 @@ +'use strict'; + +const common = require('../common'); + +// See: https://github.com/nodejs/node/issues/49940 +(async () => { + new MessageChannel().port1.postMessage({}, { + transfer: { + *[Symbol.iterator]() {} + } + }); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-messageevent-brandcheck.js b/test/js/node/test/parallel/test-messageevent-brandcheck.js new file mode 100644 index 0000000000..17f2b708cc --- /dev/null +++ b/test/js/node/test/parallel/test-messageevent-brandcheck.js @@ -0,0 +1,14 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +[ + 'data', + 'origin', + 'lastEventId', + 'source', + 'ports', +].forEach((i) => { + assert.throws(() => Reflect.get(MessageEvent.prototype, i, {}), TypeError); +}); diff --git a/test/js/node/test/parallel/test-microtask-queue-integration.js b/test/js/node/test/parallel/test-microtask-queue-integration.js new file mode 100644 index 0000000000..69d55253a2 --- /dev/null +++ b/test/js/node/test/parallel/test-microtask-queue-integration.js @@ -0,0 +1,63 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const implementations = [ + function(fn) { + Promise.resolve().then(fn); + }, +]; + +let expected = 0; +let done = 0; + +process.on('exit', function() { + assert.strictEqual(done, expected); +}); + +function test(scheduleMicrotask) { + let nextTickCalled = false; + expected++; + + scheduleMicrotask(function() { + process.nextTick(function() { + nextTickCalled = true; + }); + + setTimeout(function() { + assert(nextTickCalled); + done++; + }, 0); + }); +} + +// first tick case +implementations.forEach(test); + +// tick callback case +setTimeout(function() { + implementations.forEach(function(impl) { + process.nextTick(test.bind(null, impl)); + }); +}, 0); diff --git a/test/js/node/test/parallel/test-microtask-queue-run-immediate.js b/test/js/node/test/parallel/test-microtask-queue-run-immediate.js new file mode 100644 index 0000000000..577391993b --- /dev/null +++ b/test/js/node/test/parallel/test-microtask-queue-run-immediate.js @@ -0,0 +1,59 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +function enqueueMicrotask(fn) { + Promise.resolve().then(fn); +} + +let done = 0; + +process.on('exit', function() { + assert.strictEqual(done, 2); +}); + +// No nextTick, microtask +setImmediate(function() { + enqueueMicrotask(function() { + done++; + }); +}); + + +// No nextTick, microtask with nextTick +setImmediate(function() { + let called = false; + + enqueueMicrotask(function() { + process.nextTick(function() { + called = true; + }); + }); + + setImmediate(function() { + if (called) + done++; + }); + +}); diff --git a/test/js/node/test/parallel/test-microtask-queue-run.js b/test/js/node/test/parallel/test-microtask-queue-run.js new file mode 100644 index 0000000000..5281cb4f3c --- /dev/null +++ b/test/js/node/test/parallel/test-microtask-queue-run.js @@ -0,0 +1,59 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +function enqueueMicrotask(fn) { + Promise.resolve().then(fn); +} + +let done = 0; + +process.on('exit', function() { + assert.strictEqual(done, 2); +}); + +// No nextTick, microtask +setTimeout(function() { + enqueueMicrotask(function() { + done++; + }); +}, 0); + + +// No nextTick, microtask with nextTick +setTimeout(function() { + let called = false; + + enqueueMicrotask(function() { + process.nextTick(function() { + called = true; + }); + }); + + setTimeout(function() { + if (called) + done++; + }, 0); + +}, 0); diff --git a/test/js/node/test/parallel/test-module-builtin.js b/test/js/node/test/parallel/test-module-builtin.js new file mode 100644 index 0000000000..3897d71ecf --- /dev/null +++ b/test/js/node/test/parallel/test-module-builtin.js @@ -0,0 +1,14 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { builtinModules } = require('module'); + +// Includes modules in lib/ (even deprecated ones) +assert(builtinModules.includes('http')); +assert(builtinModules.includes('sys')); + +// Does not include internal modules +assert.deepStrictEqual( + builtinModules.filter((mod) => mod.startsWith('internal/')), + [] +); diff --git a/test/js/node/test/parallel/test-module-cache.js b/test/js/node/test/parallel/test-module-cache.js new file mode 100644 index 0000000000..87913c72cc --- /dev/null +++ b/test/js/node/test/parallel/test-module-cache.js @@ -0,0 +1,18 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const filePath = tmpdir.resolve('test-module-cache.json'); +assert.throws( + () => require(filePath), + { code: 'MODULE_NOT_FOUND' } +); + +fs.writeFileSync(filePath, '[]'); + +const content = require(filePath); +assert.strictEqual(Array.isArray(content), true); +assert.strictEqual(content.length, 0); diff --git a/test/js/node/test/parallel/test-module-circular-symlinks.js b/test/js/node/test/parallel/test-module-circular-symlinks.js new file mode 100644 index 0000000000..e8d80640df --- /dev/null +++ b/test/js/node/test/parallel/test-module-circular-symlinks.js @@ -0,0 +1,68 @@ +'use strict'; + +// This tests to make sure that modules with symlinked circular dependencies +// do not blow out the module cache and recurse forever. See issue +// https://github.com/nodejs/node/pull/5950 for context. PR #5950 attempted +// to solve a problem with symlinked peer dependencies by caching using the +// symlink path. Unfortunately, that breaks the case tested in this module +// because each symlinked module, despite pointing to the same code on disk, +// is loaded and cached as a separate module instance, which blows up the +// cache and leads to a recursion bug. + +// This test should pass in Node.js v4 and v5. It should pass in Node.js v6 +// after https://github.com/nodejs/node/pull/5950 has been reverted. + +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +// {tmpDir} +// ├── index.js +// └── node_modules +// ├── moduleA +// │ ├── index.js +// │ └── node_modules +// │ └── moduleB -> {tmpDir}/node_modules/moduleB +// └── moduleB +// ├── index.js +// └── node_modules +// └── moduleA -> {tmpDir}/node_modules/moduleA + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); +const tmpDir = tmpdir.path; + +const node_modules = path.join(tmpDir, 'node_modules'); +const moduleA = path.join(node_modules, 'moduleA'); +const moduleB = path.join(node_modules, 'moduleB'); +const moduleA_link = path.join(moduleB, 'node_modules', 'moduleA'); +const moduleB_link = path.join(moduleA, 'node_modules', 'moduleB'); + +fs.mkdirSync(node_modules); +fs.mkdirSync(moduleA); +fs.mkdirSync(moduleB); +fs.mkdirSync(path.join(moduleA, 'node_modules')); +fs.mkdirSync(path.join(moduleB, 'node_modules')); + +try { + fs.symlinkSync(moduleA, moduleA_link); + fs.symlinkSync(moduleB, moduleB_link); +} catch (err) { + if (err.code !== 'EPERM') throw err; + common.skip('insufficient privileges for symlinks'); +} + +fs.writeFileSync(path.join(tmpDir, 'index.js'), + 'module.exports = require(\'moduleA\');', 'utf8'); +fs.writeFileSync(path.join(moduleA, 'index.js'), + 'module.exports = {b: require(\'moduleB\')};', 'utf8'); +fs.writeFileSync(path.join(moduleB, 'index.js'), + 'module.exports = {a: require(\'moduleA\')};', 'utf8'); + +// Ensure that the symlinks are not followed forever... +const obj = require(path.join(tmpDir, 'index')); +assert.ok(obj); +assert.ok(obj.b); +assert.ok(obj.b.a); +assert.ok(!obj.b.a.b); diff --git a/test/js/node/test/parallel/test-module-main-extension-lookup.js b/test/js/node/test/parallel/test-module-main-extension-lookup.js new file mode 100644 index 0000000000..58d78e09b1 --- /dev/null +++ b/test/js/node/test/parallel/test-module-main-extension-lookup.js @@ -0,0 +1,9 @@ +'use strict'; +require('../common'); +const fixtures = require('../common/fixtures'); +const { execFileSync } = require('child_process'); + +const node = process.argv[0]; + +execFileSync(node, [fixtures.path('es-modules', 'test-esm-ok.mjs')]); +execFileSync(node, [fixtures.path('es-modules', 'noext')]); diff --git a/test/js/node/test/parallel/test-module-readonly.js b/test/js/node/test/parallel/test-module-readonly.js new file mode 100644 index 0000000000..ad9fbf7d21 --- /dev/null +++ b/test/js/node/test/parallel/test-module-readonly.js @@ -0,0 +1,48 @@ +'use strict'; + +const common = require('../common'); + +if (!common.isWindows) { + // TODO: Similar checks on *nix-like systems (e.g using chmod or the like) + common.skip('test only runs on Windows'); +} + +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const cp = require('child_process'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Create readOnlyMod.js and set to read only +const readOnlyMod = tmpdir.resolve('readOnlyMod'); +const readOnlyModRelative = path.relative(__dirname, readOnlyMod); +const readOnlyModFullPath = `${readOnlyMod}.js`; + +fs.writeFileSync(readOnlyModFullPath, 'module.exports = 42;'); + +// Removed any inherited ACEs, and any explicitly granted ACEs for the +// current user +cp.execSync( + `icacls.exe "${readOnlyModFullPath}" /inheritance:r /remove "%USERNAME%"`); + +// Grant the current user read & execute only +cp.execSync(`icacls.exe "${readOnlyModFullPath}" /grant "%USERNAME%":RX`); + +let except = null; +try { + // Attempt to load the module. Will fail if write access is required + require(readOnlyModRelative); +} catch (err) { + except = err; +} + +// Remove the explicitly granted rights, and re-enable inheritance +cp.execSync( + `icacls.exe "${readOnlyModFullPath}" /remove "%USERNAME%" /inheritance:e`); + +// Delete the test module (note: tmpdir should get cleaned anyway) +fs.unlinkSync(readOnlyModFullPath); + +assert.ifError(except); diff --git a/test/js/node/test/parallel/test-module-relative-lookup.js b/test/js/node/test/parallel/test-module-relative-lookup.js new file mode 100644 index 0000000000..1bd505392c --- /dev/null +++ b/test/js/node/test/parallel/test-module-relative-lookup.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const _module = require('module'); // Avoid collision with global.module + +// Current directory gets highest priority for local modules +function testFirstInPath(moduleName, isLocalModule) { + const assertFunction = isLocalModule ? + assert.strictEqual : + assert.notStrictEqual; + + let paths = _module._resolveLookupPaths(moduleName); + + assertFunction(paths[0], '.'); + + paths = _module._resolveLookupPaths(moduleName, null); + assertFunction(paths && paths[0], '.'); +} + +testFirstInPath('./lodash', true); + +// Relative path on Windows, but a regular file name elsewhere +testFirstInPath('.\\lodash', common.isWindows); diff --git a/test/js/node/test/parallel/test-net-autoselectfamily-attempt-timeout-default-value.js b/test/js/node/test/parallel/test-net-autoselectfamily-attempt-timeout-default-value.js new file mode 100644 index 0000000000..7822769527 --- /dev/null +++ b/test/js/node/test/parallel/test-net-autoselectfamily-attempt-timeout-default-value.js @@ -0,0 +1,8 @@ +'use strict'; + +const { platformTimeout } = require('../common'); + +const assert = require('assert'); +const { getDefaultAutoSelectFamilyAttemptTimeout } = require('net'); + +assert.strictEqual(getDefaultAutoSelectFamilyAttemptTimeout(), platformTimeout(2500)); diff --git a/test/js/node/test/parallel/test-net-bind-twice.js b/test/js/node/test/parallel/test-net-bind-twice.js new file mode 100644 index 0000000000..f59818a1e8 --- /dev/null +++ b/test/js/node/test/parallel/test-net-bind-twice.js @@ -0,0 +1,36 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server1 = net.createServer(common.mustNotCall()); +server1.listen(0, '127.0.0.1', common.mustCall(function() { + const server2 = net.createServer(common.mustNotCall()); + server2.listen(this.address().port, '127.0.0.1', common.mustNotCall()); + + server2.on('error', common.mustCall(function(e) { + assert.strictEqual(e.code, 'EADDRINUSE'); + server1.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-net-buffersize.js b/test/js/node/test/parallel/test-net-buffersize.js new file mode 100644 index 0000000000..7225d70af3 --- /dev/null +++ b/test/js/node/test/parallel/test-net-buffersize.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const iter = 10; + +const server = net.createServer(function(socket) { + socket.on('readable', function() { + socket.read(); + }); + + socket.on('end', function() { + server.close(); + }); +}); + +server.listen(0, common.mustCall(function() { + const client = net.connect(this.address().port); + + client.on('finish', common.mustCall(() => { + assert.strictEqual(client.bufferSize, 0); + })); + + for (let i = 1; i < iter; i++) { + client.write('a'); + assert.strictEqual(client.bufferSize, i); + } + + client.end(); +})); diff --git a/test/js/node/test/parallel/test-net-connect-call-socket-connect.js b/test/js/node/test/parallel/test-net-connect-call-socket-connect.js new file mode 100644 index 0000000000..88551889fe --- /dev/null +++ b/test/js/node/test/parallel/test-net-connect-call-socket-connect.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../common'); + +// This test checks that calling `net.connect` internally calls +// `Socket.prototype.connect`. +// +// This is important for people who monkey-patch `Socket.prototype.connect` +// since it's not possible to monkey-patch `net.connect` directly (as the core +// `connect` function is called internally in Node instead of calling the +// `exports.connect` function). +// +// Monkey-patching of `Socket.prototype.connect` is done by - among others - +// most APM vendors, the async-listener module and the +// continuation-local-storage module. +// +// Related: +// - https://github.com/nodejs/node/pull/12342 +// - https://github.com/nodejs/node/pull/12852 + +const net = require('net'); +const Socket = net.Socket; + +// Monkey patch Socket.prototype.connect to check that it's called. +const orig = Socket.prototype.connect; +Socket.prototype.connect = common.mustCall(function() { + return orig.apply(this, arguments); +}); + +const server = net.createServer(); + +server.listen(common.mustCall(function() { + const port = server.address().port; + const client = net.connect({ port }, common.mustCall(function() { + client.end(); + })); + client.on('end', common.mustCall(function() { + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-net-connect-destroy.js b/test/js/node/test/parallel/test-net-connect-destroy.js new file mode 100644 index 0000000000..73fdb988f9 --- /dev/null +++ b/test/js/node/test/parallel/test-net-connect-destroy.js @@ -0,0 +1,7 @@ +'use strict'; +const common = require('../common'); +const net = require('net'); + +const socket = new net.Socket(); +socket.on('close', common.mustCall()); +socket.destroy(); diff --git a/test/js/node/test/parallel/test-net-connect-immediate-destroy.js b/test/js/node/test/parallel/test-net-connect-immediate-destroy.js new file mode 100644 index 0000000000..3ca58c356b --- /dev/null +++ b/test/js/node/test/parallel/test-net-connect-immediate-destroy.js @@ -0,0 +1,11 @@ +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(); +server.listen(0); +const port = server.address().port; +const socket = net.connect(port, common.localhostIPv4, common.mustNotCall()); +socket.on('error', common.mustNotCall()); +server.close(); +socket.destroy(); diff --git a/test/js/node/test/parallel/test-net-connect-options-path.js b/test/js/node/test/parallel/test-net-connect-options-path.js new file mode 100644 index 0000000000..61de8caab1 --- /dev/null +++ b/test/js/node/test/parallel/test-net-connect-options-path.js @@ -0,0 +1,59 @@ +'use strict'; +const common = require('../common'); +const net = require('net'); + +// This file tests the option handling of net.connect, +// net.createConnect, and new Socket().connect + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const CLIENT_VARIANTS = 12; + +// Test connect(path) +{ + const prefix = `${common.PIPE}-net-connect-options-path`; + const serverPath = `${prefix}-server`; + let counter = 0; + const server = net.createServer() + .on('connection', common.mustCall(function(socket) { + socket.end('ok'); + }, CLIENT_VARIANTS)) + .listen(serverPath, common.mustCall(function() { + const getConnectCb = () => common.mustCall(function() { + this.end(); + this.on('close', common.mustCall(function() { + counter++; + if (counter === CLIENT_VARIANTS) { + server.close(); + } + })); + }); + + // CLIENT_VARIANTS depends on the following code + net.connect(serverPath, getConnectCb()).resume(); + net.connect(serverPath) + .on('connect', getConnectCb()) + .resume(); + net.createConnection(serverPath, getConnectCb()).resume(); + net.createConnection(serverPath) + .on('connect', getConnectCb()) + .resume(); + new net.Socket().connect(serverPath, getConnectCb()).resume(); + new net.Socket().connect(serverPath) + .on('connect', getConnectCb()) + .resume(); + net.connect({ path: serverPath }, getConnectCb()).resume(); + net.connect({ path: serverPath }) + .on('connect', getConnectCb()) + .resume(); + net.createConnection({ path: serverPath }, getConnectCb()).resume(); + net.createConnection({ path: serverPath }) + .on('connect', getConnectCb()) + .resume(); + new net.Socket().connect({ path: serverPath }, getConnectCb()).resume(); + new net.Socket().connect({ path: serverPath }) + .on('connect', getConnectCb()) + .resume(); + })); +} diff --git a/test/js/node/test/parallel/test-net-dns-lookup-skip.js b/test/js/node/test/parallel/test-net-dns-lookup-skip.js new file mode 100644 index 0000000000..06dbd5932b --- /dev/null +++ b/test/js/node/test/parallel/test-net-dns-lookup-skip.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); +const net = require('net'); + +function check(addressType) { + const server = net.createServer(function(client) { + client.end(); + server.close(); + }); + + const address = addressType === 4 ? '127.0.0.1' : '::1'; + server.listen(0, address, function() { + net.connect(this.address().port, address) + .on('lookup', common.mustNotCall()); + }); +} + +check(4); +common.hasIPv6 && check(6); diff --git a/test/js/node/test/parallel/test-net-during-close.js b/test/js/node/test/parallel/test-net-during-close.js new file mode 100644 index 0000000000..3670ed9c27 --- /dev/null +++ b/test/js/node/test/parallel/test-net-during-close.js @@ -0,0 +1,42 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(function(socket) { + socket.end(); +}); + +server.listen(0, common.mustCall(function() { + /* eslint-disable no-unused-expressions */ + const client = net.createConnection(this.address().port); + server.close(); + // Server connection event has not yet fired client is still attempting to + // connect. Accessing properties should not throw in this case. + client.remoteAddress; + client.remoteFamily; + client.remotePort; + // Exit now, do not wait for the client error event. + process.exit(0); + /* eslint-enable no-unused-expressions */ +})); diff --git a/test/js/node/test/parallel/test-net-end-without-connect.js b/test/js/node/test/parallel/test-net-end-without-connect.js new file mode 100644 index 0000000000..45d0b5477e --- /dev/null +++ b/test/js/node/test/parallel/test-net-end-without-connect.js @@ -0,0 +1,30 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); + +const sock = new net.Socket(); +sock.end(common.mustCall(() => { + assert.strictEqual(sock.writable, false); +})); diff --git a/test/js/node/test/parallel/test-net-isip.js b/test/js/node/test/parallel/test-net-isip.js new file mode 100644 index 0000000000..840ffe76af --- /dev/null +++ b/test/js/node/test/parallel/test-net-isip.js @@ -0,0 +1,96 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +assert.strictEqual(net.isIP('127.0.0.1'), 4); +assert.strictEqual(net.isIP('x127.0.0.1'), 0); +assert.strictEqual(net.isIP('example.com'), 0); +assert.strictEqual(net.isIP('0000:0000:0000:0000:0000:0000:0000:0000'), 6); +assert.strictEqual(net.isIP('0000:0000:0000:0000:0000:0000:0000:0000::0000'), + 0); +assert.strictEqual(net.isIP('1050:0:0:0:5:600:300c:326b'), 6); +assert.strictEqual(net.isIP('2001:252:0:1::2008:6'), 6); +assert.strictEqual(net.isIP('2001:dead:beef:1::2008:6'), 6); +assert.strictEqual(net.isIP('2001::'), 6); +assert.strictEqual(net.isIP('2001:dead::'), 6); +assert.strictEqual(net.isIP('2001:dead:beef::'), 6); +assert.strictEqual(net.isIP('2001:dead:beef:1::'), 6); +assert.strictEqual(net.isIP('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), 6); +assert.strictEqual(net.isIP(':2001:252:0:1::2008:6:'), 0); +assert.strictEqual(net.isIP(':2001:252:0:1::2008:6'), 0); +assert.strictEqual(net.isIP('2001:252:0:1::2008:6:'), 0); +assert.strictEqual(net.isIP('2001:252::1::2008:6'), 0); +assert.strictEqual(net.isIP('::2001:252:1:2008:6'), 6); +assert.strictEqual(net.isIP('::2001:252:1:1.1.1.1'), 6); +assert.strictEqual(net.isIP('::2001:252:1:255.255.255.255'), 6); +assert.strictEqual(net.isIP('::2001:252:1:255.255.255.255.76'), 0); +assert.strictEqual(net.isIP('fe80::2008%eth0'), 6); +assert.strictEqual(net.isIP('fe80::2008%eth0.0'), 6); +assert.strictEqual(net.isIP('fe80::2008%eth0@1'), 0); +assert.strictEqual(net.isIP('::anything'), 0); +assert.strictEqual(net.isIP('::1'), 6); +assert.strictEqual(net.isIP('::'), 6); +assert.strictEqual(net.isIP('0000:0000:0000:0000:0000:0000:12345:0000'), 0); +assert.strictEqual(net.isIP('0'), 0); +assert.strictEqual(net.isIP(), 0); +assert.strictEqual(net.isIP(''), 0); +assert.strictEqual(net.isIP(null), 0); +assert.strictEqual(net.isIP(123), 0); +assert.strictEqual(net.isIP(true), 0); +assert.strictEqual(net.isIP({}), 0); +assert.strictEqual(net.isIP({ toString: () => '::2001:252:1:255.255.255.255' }), + 6); +assert.strictEqual(net.isIP({ toString: () => '127.0.0.1' }), 4); +assert.strictEqual(net.isIP({ toString: () => 'bla' }), 0); + +assert.strictEqual(net.isIPv4('127.0.0.1'), true); +assert.strictEqual(net.isIPv4('example.com'), false); +assert.strictEqual(net.isIPv4('2001:252:0:1::2008:6'), false); +assert.strictEqual(net.isIPv4(), false); +assert.strictEqual(net.isIPv4(''), false); +assert.strictEqual(net.isIPv4(null), false); +assert.strictEqual(net.isIPv4(123), false); +assert.strictEqual(net.isIPv4(true), false); +assert.strictEqual(net.isIPv4({}), false); +assert.strictEqual(net.isIPv4({ + toString: () => '::2001:252:1:255.255.255.255' +}), false); +assert.strictEqual(net.isIPv4({ toString: () => '127.0.0.1' }), true); +assert.strictEqual(net.isIPv4({ toString: () => 'bla' }), false); + +assert.strictEqual(net.isIPv6('127.0.0.1'), false); +assert.strictEqual(net.isIPv6('example.com'), false); +assert.strictEqual(net.isIPv6('2001:252:0:1::2008:6'), true); +assert.strictEqual(net.isIPv6(), false); +assert.strictEqual(net.isIPv6(''), false); +assert.strictEqual(net.isIPv6(null), false); +assert.strictEqual(net.isIPv6(123), false); +assert.strictEqual(net.isIPv6(true), false); +assert.strictEqual(net.isIPv6({}), false); +assert.strictEqual(net.isIPv6({ + toString: () => '::2001:252:1:255.255.255.255' +}), true); +assert.strictEqual(net.isIPv6({ toString: () => '127.0.0.1' }), false); +assert.strictEqual(net.isIPv6({ toString: () => 'bla' }), false); diff --git a/test/js/node/test/parallel/test-net-isipv4.js b/test/js/node/test/parallel/test-net-isipv4.js new file mode 100644 index 0000000000..2c478e6ac6 --- /dev/null +++ b/test/js/node/test/parallel/test-net-isipv4.js @@ -0,0 +1,46 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +const v4 = [ + '0.0.0.0', + '8.8.8.8', + '127.0.0.1', + '100.100.100.100', + '192.168.0.1', + '18.101.25.153', + '123.23.34.2', + '172.26.168.134', + '212.58.241.131', + '128.0.0.0', + '23.71.254.72', + '223.255.255.255', + '192.0.2.235', + '99.198.122.146', + '46.51.197.88', + '173.194.34.134', +]; + +const v4not = [ + '.100.100.100.100', + '100..100.100.100.', + '100.100.100.100.', + '999.999.999.999', + '256.256.256.256', + '256.100.100.100.100', + '123.123.123', + 'http://123.123.123', + '1000.2.3.4', + '999.2.3.4', + '0000000192.168.0.200', + '192.168.0.2000000000', +]; + +for (const ip of v4) { + assert.strictEqual(net.isIPv4(ip), true); +} + +for (const ip of v4not) { + assert.strictEqual(net.isIPv4(ip), false); +} diff --git a/test/js/node/test/parallel/test-net-isipv6.js b/test/js/node/test/parallel/test-net-isipv6.js new file mode 100644 index 0000000000..dbb8d80b7b --- /dev/null +++ b/test/js/node/test/parallel/test-net-isipv6.js @@ -0,0 +1,244 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +const v6 = [ + '::', + '1::', + '::1', + '1::8', + '1::7:8', + '1:2:3:4:5:6:7:8', + '1:2:3:4:5:6::8', + '1:2:3:4:5:6:7::', + '1:2:3:4:5::7:8', + '1:2:3:4:5::8', + '1:2:3::8', + '1::4:5:6:7:8', + '1::6:7:8', + '1::3:4:5:6:7:8', + '1:2:3:4::6:7:8', + '1:2::4:5:6:7:8', + '::2:3:4:5:6:7:8', + '1:2::8', + '2001:0000:1234:0000:0000:C1C0:ABCD:0876', + '3ffe:0b00:0000:0000:0001:0000:0000:000a', + 'FF02:0000:0000:0000:0000:0000:0000:0001', + '0000:0000:0000:0000:0000:0000:0000:0001', + '0000:0000:0000:0000:0000:0000:0000:0000', + '::ffff:192.168.1.26', + '2::10', + 'ff02::1', + 'fe80::', + '2002::', + '2001:db8::', + '2001:0db8:1234::', + '::ffff:0:0', + '::ffff:192.168.1.1', + '1:2:3:4::8', + '1::2:3:4:5:6:7', + '1::2:3:4:5:6', + '1::2:3:4:5', + '1::2:3:4', + '1::2:3', + '::2:3:4:5:6:7', + '::2:3:4:5:6', + '::2:3:4:5', + '::2:3:4', + '::2:3', + '::8', + '1:2:3:4:5:6::', + '1:2:3:4:5::', + '1:2:3:4::', + '1:2:3::', + '1:2::', + '1:2:3:4::7:8', + '1:2:3::7:8', + '1:2::7:8', + '1:2:3:4:5:6:1.2.3.4', + '1:2:3:4:5::1.2.3.4', + '1:2:3:4::1.2.3.4', + '1:2:3::1.2.3.4', + '1:2::1.2.3.4', + '1::1.2.3.4', + '1:2:3:4::5:1.2.3.4', + '1:2:3::5:1.2.3.4', + '1:2::5:1.2.3.4', + '1::5:1.2.3.4', + '1::5:11.22.33.44', + 'fe80::217:f2ff:254.7.237.98', + 'fe80::217:f2ff:fe07:ed62', + '2001:DB8:0:0:8:800:200C:417A', + 'FF01:0:0:0:0:0:0:101', + '0:0:0:0:0:0:0:1', + '0:0:0:0:0:0:0:0', + '2001:DB8::8:800:200C:417A', + 'FF01::101', + '0:0:0:0:0:0:13.1.68.3', + '0:0:0:0:0:FFFF:129.144.52.38', + '::13.1.68.3', + '::FFFF:129.144.52.38', + 'fe80:0000:0000:0000:0204:61ff:fe9d:f156', + 'fe80:0:0:0:204:61ff:fe9d:f156', + 'fe80::204:61ff:fe9d:f156', + 'fe80:0:0:0:204:61ff:254.157.241.86', + 'fe80::204:61ff:254.157.241.86', + 'fe80::1', + '2001:0db8:85a3:0000:0000:8a2e:0370:7334', + '2001:db8:85a3:0:0:8a2e:370:7334', + '2001:db8:85a3::8a2e:370:7334', + '2001:0db8:0000:0000:0000:0000:1428:57ab', + '2001:0db8:0000:0000:0000::1428:57ab', + '2001:0db8:0:0:0:0:1428:57ab', + '2001:0db8:0:0::1428:57ab', + '2001:0db8::1428:57ab', + '2001:db8::1428:57ab', + '::ffff:12.34.56.78', + '::ffff:0c22:384e', + '2001:0db8:1234:0000:0000:0000:0000:0000', + '2001:0db8:1234:ffff:ffff:ffff:ffff:ffff', + '2001:db8:a::123', + '::ffff:192.0.2.128', + '::ffff:c000:280', + 'a:b:c:d:e:f:f1:f2', + 'a:b:c::d:e:f:f1', + 'a:b:c::d:e:f', + 'a:b:c::d:e', + 'a:b:c::d', + '::a', + '::a:b:c', + '::a:b:c:d:e:f:f1', + 'a::', + 'a:b:c::', + 'a:b:c:d:e:f:f1::', + 'a:bb:ccc:dddd:000e:00f:0f::', + '0:a:0:a:0:0:0:a', + '0:a:0:0:a:0:0:a', + '2001:db8:1:1:1:1:0:0', + '2001:db8:1:1:1:0:0:0', + '2001:db8:1:1:0:0:0:0', + '2001:db8:1:0:0:0:0:0', + '2001:db8:0:0:0:0:0:0', + '2001:0:0:0:0:0:0:0', + 'A:BB:CCC:DDDD:000E:00F:0F::', + '0:0:0:0:0:0:0:a', + '0:0:0:0:a:0:0:0', + '0:0:0:a:0:0:0:0', + 'a:0:0:a:0:0:a:a', + 'a:0:0:a:0:0:0:a', + 'a:0:0:0:a:0:0:a', + 'a:0:0:0:a:0:0:0', + 'a:0:0:0:0:0:0:0', + 'fe80::7:8%eth0', + 'fe80::7:8%1', +]; + +const v6not = [ + '', + '1:', + ':1', + '11:36:12', + '02001:0000:1234:0000:0000:C1C0:ABCD:0876', + '2001:0000:1234:0000:00001:C1C0:ABCD:0876', + '2001:0000:1234: 0000:0000:C1C0:ABCD:0876', + '2001:1:1:1:1:1:255Z255X255Y255', + '3ffe:0b00:0000:0001:0000:0000:000a', + 'FF02:0000:0000:0000:0000:0000:0000:0000:0001', + '3ffe:b00::1::a', + '::1111:2222:3333:4444:5555:6666::', + '1:2:3::4:5::7:8', + '12345::6:7:8', + '1::5:400.2.3.4', + '1::5:260.2.3.4', + '1::5:256.2.3.4', + '1::5:1.256.3.4', + '1::5:1.2.256.4', + '1::5:1.2.3.256', + '1::5:300.2.3.4', + '1::5:1.300.3.4', + '1::5:1.2.300.4', + '1::5:1.2.3.300', + '1::5:900.2.3.4', + '1::5:1.900.3.4', + '1::5:1.2.900.4', + '1::5:1.2.3.900', + '1::5:300.300.300.300', + '1::5:3000.30.30.30', + '1::400.2.3.4', + '1::260.2.3.4', + '1::256.2.3.4', + '1::1.256.3.4', + '1::1.2.256.4', + '1::1.2.3.256', + '1::300.2.3.4', + '1::1.300.3.4', + '1::1.2.300.4', + '1::1.2.3.300', + '1::900.2.3.4', + '1::1.900.3.4', + '1::1.2.900.4', + '1::1.2.3.900', + '1::300.300.300.300', + '1::3000.30.30.30', + '::400.2.3.4', + '::260.2.3.4', + '::256.2.3.4', + '::1.256.3.4', + '::1.2.256.4', + '::1.2.3.256', + '::300.2.3.4', + '::1.300.3.4', + '::1.2.300.4', + '::1.2.3.300', + '::900.2.3.4', + '::1.900.3.4', + '::1.2.900.4', + '::1.2.3.900', + '::300.300.300.300', + '::3000.30.30.30', + '2001:DB8:0:0:8:800:200C:417A:221', + 'FF01::101::2', + '1111:2222:3333:4444::5555:', + '1111:2222:3333::5555:', + '1111:2222::5555:', + '1111::5555:', + '::5555:', + ':::', + '1111:', + ':', + ':1111:2222:3333:4444::5555', + ':1111:2222:3333::5555', + ':1111:2222::5555', + ':1111::5555', + ':::5555', + '1.2.3.4:1111:2222:3333:4444::5555', + '1.2.3.4:1111:2222:3333::5555', + '1.2.3.4:1111:2222::5555', + '1.2.3.4:1111::5555', + '1.2.3.4::5555', + '1.2.3.4::', + 'fe80:0000:0000:0000:0204:61ff:254.157.241.086', + '123', + 'ldkfj', + '2001::FFD3::57ab', + '2001:db8:85a3::8a2e:37023:7334', + '2001:db8:85a3::8a2e:370k:7334', + '1:2:3:4:5:6:7:8:9', + '1::2::3', + '1:::3:4:5', + '1:2:3::4:5:6:7:8:9', + '::ffff:2.3.4', + '::ffff:257.1.2.3', + '::ffff:12345678901234567890.1.26', + '2001:0000:1234:0000:0000:C1C0:ABCD:0876 0', + '02001:0000:1234:0000:0000:C1C0:ABCD:0876', +]; + +for (const ip of v6) { + assert.strictEqual(net.isIPv6(ip), true); +} + +for (const ip of v6not) { + assert.strictEqual(net.isIPv6(ip), false); +} diff --git a/test/js/node/test/parallel/test-net-listen-after-destroying-stdin.js b/test/js/node/test/parallel/test-net-listen-after-destroying-stdin.js new file mode 100644 index 0000000000..4ffec304be --- /dev/null +++ b/test/js/node/test/parallel/test-net-listen-after-destroying-stdin.js @@ -0,0 +1,22 @@ +'use strict'; +// Just test that destroying stdin doesn't mess up listening on a server. +// This is a regression test for +// https://github.com/nodejs/node-v0.x-archive/issues/746. + +const common = require('../common'); +const net = require('net'); + +process.stdin.destroy(); + +const server = net.createServer(common.mustCall((socket) => { + console.log('accepted...'); + socket.end(common.mustCall(() => { console.log('finished...'); })); + server.close(common.mustCall(() => { console.log('closed'); })); +})); + + +server.listen(0, common.mustCall(() => { + console.log('listening...'); + + net.createConnection(server.address().port); +})); diff --git a/test/js/node/test/parallel/test-net-listen-error.js b/test/js/node/test/parallel/test-net-listen-error.js new file mode 100644 index 0000000000..05ca799d3e --- /dev/null +++ b/test/js/node/test/parallel/test-net-listen-error.js @@ -0,0 +1,29 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(function(socket) { +}); +server.listen(1, '1.1.1.1', common.mustNotCall()); // EACCES or EADDRNOTAVAIL +server.on('error', common.mustCall()); diff --git a/test/js/node/test/parallel/test-net-listen-exclusive-random-ports.js b/test/js/node/test/parallel/test-net-listen-exclusive-random-ports.js new file mode 100644 index 0000000000..66dfb59820 --- /dev/null +++ b/test/js/node/test/parallel/test-net-listen-exclusive-random-ports.js @@ -0,0 +1,37 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); +const net = require('net'); + +if (cluster.isPrimary) { + const worker1 = cluster.fork(); + + worker1.on('message', function(port1) { + assert.strictEqual(port1, port1 | 0, + `first worker could not listen on port ${port1}`); + const worker2 = cluster.fork(); + + worker2.on('message', function(port2) { + assert.strictEqual(port2, port2 | 0, + `second worker could not listen on port ${port2}`); + assert.notStrictEqual(port1, port2, 'ports should not be equal'); + worker1.kill(); + worker2.kill(); + }); + }); +} else { + const server = net.createServer(() => {}); + + server.on('error', function(err) { + process.send(err.code); + }); + + server.listen({ + port: 0, + exclusive: true + }, function() { + process.send(server.address().port); + }); +} diff --git a/test/js/node/test/parallel/test-net-listen-handle-in-cluster-1.js b/test/js/node/test/parallel/test-net-listen-handle-in-cluster-1.js new file mode 100644 index 0000000000..07e002bf2a --- /dev/null +++ b/test/js/node/test/parallel/test-net-listen-handle-in-cluster-1.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); +const cluster = require('cluster'); + +// Test if the worker can listen with handle successfully +if (cluster.isPrimary) { + const worker = cluster.fork(); + const server = net.createServer(); + worker.on('online', common.mustCall(() => { + server.listen(common.mustCall(() => { + // Send the server to worker + worker.send(null, server); + })); + })); + worker.on('exit', common.mustCall(() => { + server.close(); + })); +} else { + // The `got` function of net.Server will create a TCP server by listen(handle) + // See lib/internal/child_process.js + process.on('message', common.mustCall((_, server) => { + assert.strictEqual(server instanceof net.Server, true); + process.exit(0); + })); +} diff --git a/test/js/node/test/parallel/test-net-listening.js b/test/js/node/test/parallel/test-net-listening.js new file mode 100644 index 0000000000..8f2880b0bf --- /dev/null +++ b/test/js/node/test/parallel/test-net-listening.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(); + +assert.strictEqual(server.listening, false); + +server.listen(0, common.mustCall(() => { + assert.strictEqual(server.listening, true); + + server.close(common.mustCall(() => { + assert.strictEqual(server.listening, false); + })); +})); diff --git a/test/js/node/test/parallel/test-net-local-address-port.js b/test/js/node/test/parallel/test-net-local-address-port.js new file mode 100644 index 0000000000..cfc6f61ef3 --- /dev/null +++ b/test/js/node/test/parallel/test-net-local-address-port.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(common.mustCall(function(socket) { + assert.strictEqual(socket.localAddress, common.localhostIPv4); + assert.strictEqual(socket.localPort, this.address().port); + assert.strictEqual(socket.localFamily, this.address().family); + socket.on('end', function() { + server.close(); + }); + socket.resume(); +})); + +server.listen(0, common.localhostIPv4, function() { + const client = net.createConnection(this.address() + .port, common.localhostIPv4); + client.on('connect', function() { + client.end(); + }); +}); diff --git a/test/js/node/test/parallel/test-net-remote-address.js b/test/js/node/test/parallel/test-net-remote-address.js new file mode 100644 index 0000000000..a116cb99d3 --- /dev/null +++ b/test/js/node/test/parallel/test-net-remote-address.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +const net = require('net'); +const { strictEqual } = require('assert'); + +const server = net.createServer(); + +server.listen(common.mustCall(function() { + const socket = net.connect({ port: server.address().port }); + + strictEqual(socket.connecting, true); + strictEqual(socket.remoteAddress, undefined); + + socket.on('connect', common.mustCall(function() { + strictEqual(socket.remoteAddress !== undefined, true); + socket.end(); + })); + + socket.on('end', common.mustCall(function() { + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-net-server-close-before-ipc-response.js b/test/js/node/test/parallel/test-net-server-close-before-ipc-response.js new file mode 100644 index 0000000000..e85bc96ee6 --- /dev/null +++ b/test/js/node/test/parallel/test-net-server-close-before-ipc-response.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); +const net = require('net'); +const cluster = require('cluster'); + +// Process should exit +if (cluster.isPrimary) { + cluster.fork(); +} else { + const send = process.send; + process.send = function(message) { + // listenOnPrimaryHandle in net.js should call handle.close() + if (message.act === 'close') { + setImmediate(() => { + process.disconnect(); + }); + } + return send.apply(this, arguments); + }; + net.createServer().listen(0, common.mustNotCall()).close(); +} diff --git a/test/js/node/test/parallel/test-net-server-close.js b/test/js/node/test/parallel/test-net-server-close.js new file mode 100644 index 0000000000..8291f70432 --- /dev/null +++ b/test/js/node/test/parallel/test-net-server-close.js @@ -0,0 +1,45 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const sockets = []; + +const server = net.createServer(function(c) { + c.on('close', common.mustCall()); + + sockets.push(c); + + if (sockets.length === 2) { + assert.strictEqual(server.close(), server); + sockets.forEach((c) => c.destroy()); + } +}); + +server.on('close', common.mustCall()); + +assert.strictEqual(server, server.listen(0, () => { + net.createConnection(server.address().port); + net.createConnection(server.address().port); +})); diff --git a/test/js/node/test/parallel/test-net-server-listen-remove-callback.js b/test/js/node/test/parallel/test-net-server-listen-remove-callback.js new file mode 100644 index 0000000000..a874099fb8 --- /dev/null +++ b/test/js/node/test/parallel/test-net-server-listen-remove-callback.js @@ -0,0 +1,44 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +// Server should only fire listen callback once +const server = net.createServer(); + +server.on('close', function() { + const listeners = server.listeners('listening'); + console.log('Closed, listeners:', listeners.length); + assert.strictEqual(listeners.length, 0); +}); + +server.listen(0, function() { + server.close(); +}); + +server.once('close', function() { + server.listen(0, function() { + server.close(); + }); +}); diff --git a/test/js/node/test/parallel/test-net-server-unref.js b/test/js/node/test/parallel/test-net-server-unref.js new file mode 100644 index 0000000000..935ba5d639 --- /dev/null +++ b/test/js/node/test/parallel/test-net-server-unref.js @@ -0,0 +1,30 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const net = require('net'); + +const s = net.createServer(); +s.listen(0); +s.unref(); + +setTimeout(common.mustNotCall(), 1000).unref(); diff --git a/test/js/node/test/parallel/test-net-socket-byteswritten.js b/test/js/node/test/parallel/test-net-socket-byteswritten.js new file mode 100644 index 0000000000..b7b7af89e2 --- /dev/null +++ b/test/js/node/test/parallel/test-net-socket-byteswritten.js @@ -0,0 +1,35 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(function(socket) { + socket.end(); +}); + +server.listen(0, common.mustCall(function() { + const socket = net.connect(server.address().port); + + // Cork the socket, then write twice; this should cause a writev, which + // previously caused an err in the bytesWritten count. + socket.cork(); + + socket.write('one'); + socket.write(Buffer.from('twø', 'utf8')); + + socket.uncork(); + + // one = 3 bytes, twø = 4 bytes + assert.strictEqual(socket.bytesWritten, 3 + 4); + + socket.on('connect', common.mustCall(function() { + assert.strictEqual(socket.bytesWritten, 3 + 4); + })); + + socket.on('end', common.mustCall(function() { + assert.strictEqual(socket.bytesWritten, 3 + 4); + + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-net-socket-close-after-end.js b/test/js/node/test/parallel/test-net-socket-close-after-end.js new file mode 100644 index 0000000000..06bf55f89d --- /dev/null +++ b/test/js/node/test/parallel/test-net-socket-close-after-end.js @@ -0,0 +1,31 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(); + +server.on('connection', (socket) => { + let endEmitted = false; + + socket.once('readable', () => { + setTimeout(() => { + socket.read(); + }, common.platformTimeout(100)); + }); + socket.on('end', () => { + endEmitted = true; + }); + socket.on('close', () => { + assert(endEmitted); + server.close(); + }); + socket.end('foo'); +}); + +server.listen(common.mustCall(() => { + const socket = net.createConnection(server.address().port, () => { + socket.end('foo'); + }); +})); diff --git a/test/js/node/test/parallel/test-net-socket-connect-without-cb.js b/test/js/node/test/parallel/test-net-socket-connect-without-cb.js new file mode 100644 index 0000000000..274083eb29 --- /dev/null +++ b/test/js/node/test/parallel/test-net-socket-connect-without-cb.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that socket.connect can be called without callback +// which is optional. + +const net = require('net'); + +const server = net.createServer(common.mustCall(function(conn) { + conn.end(); + server.close(); +})).listen(0, common.mustCall(function() { + const client = new net.Socket(); + + client.on('connect', common.mustCall(function() { + client.end(); + })); + + const address = server.address(); + if (!common.hasIPv6 && address.family === 'IPv6') { + // Necessary to pass CI running inside containers. + client.connect(address.port); + } else { + client.connect(address); + } +})); diff --git a/test/js/node/test/parallel/test-net-socket-connecting.js b/test/js/node/test/parallel/test-net-socket-connecting.js new file mode 100644 index 0000000000..21aa261192 --- /dev/null +++ b/test/js/node/test/parallel/test-net-socket-connecting.js @@ -0,0 +1,21 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer((conn) => { + conn.end(); + server.close(); +}).listen(0, () => { + const client = net.connect(server.address().port, () => { + assert.strictEqual(client.connecting, false); + + // Legacy getter + assert.strictEqual(client._connecting, false); + client.end(); + }); + assert.strictEqual(client.connecting, true); + + // Legacy getter + assert.strictEqual(client._connecting, true); +}); diff --git a/test/js/node/test/parallel/test-net-socket-end-before-connect.js b/test/js/node/test/parallel/test-net-socket-end-before-connect.js new file mode 100644 index 0000000000..d40c90620e --- /dev/null +++ b/test/js/node/test/parallel/test-net-socket-end-before-connect.js @@ -0,0 +1,13 @@ +'use strict'; + +const common = require('../common'); + +const net = require('net'); + +const server = net.createServer(); + +server.listen(common.mustCall(() => { + const socket = net.createConnection(server.address().port); + socket.on('close', common.mustCall(() => server.close())); + socket.end(); +})); diff --git a/test/js/node/test/parallel/test-net-socket-ready-without-cb.js b/test/js/node/test/parallel/test-net-socket-ready-without-cb.js new file mode 100644 index 0000000000..29da68e173 --- /dev/null +++ b/test/js/node/test/parallel/test-net-socket-ready-without-cb.js @@ -0,0 +1,20 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that socket.connect can be called without callback +// which is optional. + +const net = require('net'); + +const server = net.createServer(common.mustCall(function(conn) { + conn.end(); + server.close(); +})).listen(0, 'localhost', common.mustCall(function() { + const client = new net.Socket(); + + client.on('ready', common.mustCall(function() { + client.end(); + })); + + client.connect(server.address()); +})); diff --git a/test/js/node/test/parallel/test-net-socket-timeout-unref.js b/test/js/node/test/parallel/test-net-socket-timeout-unref.js new file mode 100644 index 0000000000..ae6bde49ab --- /dev/null +++ b/test/js/node/test/parallel/test-net-socket-timeout-unref.js @@ -0,0 +1,56 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +// Test that unref'ed sockets with timeouts do not prevent exit. + +const common = require('../common'); +const net = require('net'); + +const server = net.createServer(function(c) { + c.write('hello'); + c.unref(); +}); +server.listen(0); +server.unref(); + +let connections = 0; +const sockets = []; +const delays = [8, 5, 3, 6, 2, 4]; + +delays.forEach(function(T) { + const socket = net.createConnection(server.address().port, 'localhost'); + socket.on('connect', common.mustCall(function() { + if (++connections === delays.length) { + sockets.forEach(function(s) { + s.socket.setTimeout(s.timeout, function() { + s.socket.destroy(); + throw new Error('socket timed out unexpectedly'); + }); + + s.socket.unref(); + }); + } + })); + + sockets.push({ socket: socket, timeout: T * 1000 }); +}); diff --git a/test/js/node/test/parallel/test-net-socket-write-error.js b/test/js/node/test/parallel/test-net-socket-write-error.js new file mode 100644 index 0000000000..e68db68c0d --- /dev/null +++ b/test/js/node/test/parallel/test-net-socket-write-error.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); +const net = require('net'); +const assert = require('assert'); + +const server = net.createServer().listen(0, connectToServer); + +function connectToServer() { + const client = net.createConnection(this.address().port, () => { + client.on('error', common.mustNotCall()); + assert.throws(() => { + client.write(1337); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + + client.destroy(); + }) + .on('close', () => server.close()); +} diff --git a/test/js/node/test/parallel/test-net-sync-cork.js b/test/js/node/test/parallel/test-net-sync-cork.js new file mode 100644 index 0000000000..447f42ca91 --- /dev/null +++ b/test/js/node/test/parallel/test-net-sync-cork.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(handle); + +const N = 100; +const buf = Buffer.alloc(2, 'a'); + +server.listen(0, function() { + const conn = net.connect(this.address().port); + + conn.on('connect', () => { + let res = true; + let i = 0; + for (; i < N && res; i++) { + conn.cork(); + conn.write(buf); + res = conn.write(buf); + conn.uncork(); + } + assert.strictEqual(i, N); + conn.end(); + }); +}); + +function handle(socket) { + socket.resume(); + socket.on('error', common.mustNotCall()) + .on('close', common.mustCall(() => server.close())); +} diff --git a/test/js/node/test/parallel/test-net-writable.js b/test/js/node/test/parallel/test-net-writable.js new file mode 100644 index 0000000000..3659869efb --- /dev/null +++ b/test/js/node/test/parallel/test-net-writable.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(common.mustCall(function(s) { + server.close(); + s.end(); +})).listen(0, '127.0.0.1', common.mustCall(function() { + const socket = net.connect(this.address().port, '127.0.0.1'); + socket.on('end', common.mustCall(() => { + assert.strictEqual(socket.writable, true); + socket.write('hello world'); + })); +})); diff --git a/test/js/node/test/parallel/test-net-write-cb-on-destroy-before-connect.js b/test/js/node/test/parallel/test-net-write-cb-on-destroy-before-connect.js new file mode 100644 index 0000000000..99efb66034 --- /dev/null +++ b/test/js/node/test/parallel/test-net-write-cb-on-destroy-before-connect.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(); +server.listen(0, common.mustCall(() => { + const socket = new net.Socket(); + + socket.on('connect', common.mustNotCall()); + + socket.connect({ + port: server.address().port, + }); + + assert(socket.connecting); + + socket.write('foo', common.expectsError({ + code: 'ERR_SOCKET_CLOSED_BEFORE_CONNECTION', + name: 'Error' + })); + + socket.destroy(); + server.close(); +})); diff --git a/test/js/node/test/parallel/test-net-write-connect-write.js b/test/js/node/test/parallel/test-net-write-connect-write.js new file mode 100644 index 0000000000..1f09b04f17 --- /dev/null +++ b/test/js/node/test/parallel/test-net-write-connect-write.js @@ -0,0 +1,46 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const server = net.createServer(function(socket) { + socket.pipe(socket); +}).listen(0, common.mustCall(function() { + const conn = net.connect(this.address().port); + let received = ''; + + conn.setEncoding('utf8'); + conn.write('before'); + conn.on('connect', function() { + conn.write(' after'); + }); + conn.on('data', function(buf) { + received += buf; + conn.end(); + }); + conn.on('end', common.mustCall(function() { + server.close(); + assert.strictEqual(received, 'before after'); + })); +})); diff --git a/test/js/node/test/parallel/test-net-write-slow.js b/test/js/node/test/parallel/test-net-write-slow.js new file mode 100644 index 0000000000..cf2d5790d9 --- /dev/null +++ b/test/js/node/test/parallel/test-net-write-slow.js @@ -0,0 +1,63 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +const SIZE = 2E5; +const N = 10; +let flushed = 0; +let received = 0; +const buf = Buffer.alloc(SIZE, 'a'); + +const server = net.createServer(function(socket) { + socket.setNoDelay(); + socket.setTimeout(9999); + socket.on('timeout', function() { + assert.fail(`flushed: ${flushed}, received: ${received}/${SIZE * N}`); + }); + + for (let i = 0; i < N; ++i) { + socket.write(buf, function() { + ++flushed; + if (flushed === N) { + socket.setTimeout(0); + } + }); + } + socket.end(); + +}).listen(0, common.mustCall(function() { + const conn = net.connect(this.address().port); + conn.on('data', function(buf) { + received += buf.length; + conn.pause(); + setTimeout(function() { + conn.resume(); + }, 20); + }); + conn.on('end', common.mustCall(function() { + server.close(); + assert.strictEqual(received, SIZE * N); + })); +})); diff --git a/test/js/node/test/parallel/test-next-tick-doesnt-hang.js b/test/js/node/test/parallel/test-next-tick-doesnt-hang.js new file mode 100644 index 0000000000..36c1740bbf --- /dev/null +++ b/test/js/node/test/parallel/test-next-tick-doesnt-hang.js @@ -0,0 +1,30 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +// This test verifies that having a single nextTick statement and nothing else +// does not hang the event loop. If this test times out it has failed. + +require('../common'); +process.nextTick(function() { + // Nothing +}); diff --git a/test/js/node/test/parallel/test-next-tick-domain.js b/test/js/node/test/parallel/test-next-tick-domain.js new file mode 100644 index 0000000000..3e55ef3225 --- /dev/null +++ b/test/js/node/test/parallel/test-next-tick-domain.js @@ -0,0 +1,31 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const origNextTick = process.nextTick; + +require('domain'); + +// Requiring domain should not change nextTick. +assert.strictEqual(origNextTick, process.nextTick); diff --git a/test/js/node/test/parallel/test-next-tick-errors.js b/test/js/node/test/parallel/test-next-tick-errors.js new file mode 100644 index 0000000000..6fd079625a --- /dev/null +++ b/test/js/node/test/parallel/test-next-tick-errors.js @@ -0,0 +1,74 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const order = []; +let exceptionHandled = false; + +// This nextTick function will throw an error. It should only be called once. +// When it throws an error, it should still get removed from the queue. +process.nextTick(function() { + order.push('A'); + // cause an error + what(); // eslint-disable-line no-undef +}); + +// This nextTick function should remain in the queue when the first one +// is removed. It should be called if the error in the first one is +// caught (which we do in this test). +process.nextTick(function() { + order.push('C'); +}); + +function testNextTickWith(val) { + assert.throws(() => { + process.nextTick(val); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); +} + +testNextTickWith(false); +testNextTickWith(true); +testNextTickWith(1); +testNextTickWith('str'); +testNextTickWith({}); +testNextTickWith([]); + +process.on('uncaughtException', function(err, errorOrigin) { + assert.strictEqual(errorOrigin, 'uncaughtException'); + + if (!exceptionHandled) { + exceptionHandled = true; + order.push('B'); + } else { + // If we get here then the first process.nextTick got called twice + order.push('OOPS!'); + } +}); + +process.on('exit', function() { + assert.deepStrictEqual(order, ['A', 'B', 'C']); +}); diff --git a/test/js/node/test/parallel/test-next-tick-fixed-queue-regression.js b/test/js/node/test/parallel/test-next-tick-fixed-queue-regression.js new file mode 100644 index 0000000000..1fe82d02b1 --- /dev/null +++ b/test/js/node/test/parallel/test-next-tick-fixed-queue-regression.js @@ -0,0 +1,18 @@ +'use strict'; + +const common = require('../common'); + +// This tests a highly specific regression tied to the FixedQueue size, which +// was introduced in Node.js 9.7.0: https://github.com/nodejs/node/pull/18617 +// More specifically, a nextTick list could potentially end up not fully +// clearing in one run through if exactly 2048 ticks were added after +// microtasks were executed within the nextTick loop. + +process.nextTick(() => { + Promise.resolve(1).then(() => { + for (let i = 0; i < 2047; i++) + process.nextTick(common.mustCall()); + const immediate = setImmediate(common.mustNotCall()); + process.nextTick(common.mustCall(() => clearImmediate(immediate))); + }); +}); diff --git a/test/js/node/test/parallel/test-next-tick-intentional-starvation.js b/test/js/node/test/parallel/test-next-tick-intentional-starvation.js new file mode 100644 index 0000000000..ed357cb233 --- /dev/null +++ b/test/js/node/test/parallel/test-next-tick-intentional-starvation.js @@ -0,0 +1,61 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// This is the inverse of test-next-tick-starvation. it verifies +// that process.nextTick will *always* come before other events + +let ran = false; +let starved = false; +const start = +new Date(); +let timerRan = false; + +function spin() { + ran = true; + const now = +new Date(); + if (now - start > 100) { + console.log('The timer is starving, just as we planned.'); + starved = true; + + // now let it out. + return; + } + + process.nextTick(spin); +} + +function onTimeout() { + if (!starved) throw new Error('The timer escaped!'); + console.log('The timer ran once the ban was lifted'); + timerRan = true; +} + +spin(); +setTimeout(onTimeout, 50); + +process.on('exit', function() { + assert.ok(ran); + assert.ok(starved); + assert.ok(timerRan); +}); diff --git a/test/js/node/test/parallel/test-next-tick-ordering.js b/test/js/node/test/parallel/test-next-tick-ordering.js new file mode 100644 index 0000000000..8d3ee6488c --- /dev/null +++ b/test/js/node/test/parallel/test-next-tick-ordering.js @@ -0,0 +1,55 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +let i; + +const N = 30; +const done = []; + +function get_printer(timeout) { + return function() { + console.log(`Running from setTimeout ${timeout}`); + done.push(timeout); + }; +} + +process.nextTick(function() { + console.log('Running from nextTick'); + done.push('nextTick'); +}); + +for (i = 0; i < N; i += 1) { + setTimeout(get_printer(i), i); +} + +console.log('Running from main.'); + + +process.on('exit', function() { + assert.strictEqual(done[0], 'nextTick'); + // Disabling this test. I don't think we can ensure the order + // for (i = 0; i < N; i += 1) { + // assert.strictEqual(i, done[i + 1]); + // } +}); diff --git a/test/js/node/test/parallel/test-next-tick-ordering2.js b/test/js/node/test/parallel/test-next-tick-ordering2.js new file mode 100644 index 0000000000..6c42bd8e57 --- /dev/null +++ b/test/js/node/test/parallel/test-next-tick-ordering2.js @@ -0,0 +1,39 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const order = []; +process.nextTick(function() { + setTimeout(function() { + order.push('setTimeout'); + }, 0); + + process.nextTick(function() { + order.push('nextTick'); + }); +}); + +process.on('exit', function() { + assert.deepStrictEqual(order, ['nextTick', 'setTimeout']); +}); diff --git a/test/js/node/test/parallel/test-next-tick-when-exiting.js b/test/js/node/test/parallel/test-next-tick-when-exiting.js new file mode 100644 index 0000000000..36dc296646 --- /dev/null +++ b/test/js/node/test/parallel/test-next-tick-when-exiting.js @@ -0,0 +1,14 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +process.on('exit', () => { + assert.strictEqual(process._exiting, true); + + process.nextTick( + common.mustNotCall('process is exiting, should not be called') + ); +}); + +process.exit(); diff --git a/test/js/node/test/parallel/test-next-tick.js b/test/js/node/test/parallel/test-next-tick.js new file mode 100644 index 0000000000..47823f45bc --- /dev/null +++ b/test/js/node/test/parallel/test-next-tick.js @@ -0,0 +1,63 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +const assert = require('assert'); + +process.nextTick(common.mustCall(function() { + process.nextTick(common.mustCall(function() { + process.nextTick(common.mustCall()); + })); +})); + +setTimeout(common.mustCall(function() { + process.nextTick(common.mustCall()); +}), 50); + +process.nextTick(common.mustCall()); + +const obj = {}; + +process.nextTick(function(a, b) { + assert.strictEqual(a, 42); + assert.strictEqual(b, obj); + assert.strictEqual(this, undefined); +}, 42, obj); + +process.nextTick((a, b) => { + assert.strictEqual(a, 42); + assert.strictEqual(b, obj); + assert.deepStrictEqual(this, {}); +}, 42, obj); + +process.nextTick(function() { + assert.strictEqual(this, undefined); +}, 1, 2, 3, 4); + +process.nextTick(() => { + assert.deepStrictEqual(this, {}); +}, 1, 2, 3, 4); + +process.on('exit', function() { + process.nextTick(common.mustNotCall()); +}); diff --git a/test/js/node/test/parallel/test-no-node-snapshot.js b/test/js/node/test/parallel/test-no-node-snapshot.js new file mode 100644 index 0000000000..a636040a4c --- /dev/null +++ b/test/js/node/test/parallel/test-no-node-snapshot.js @@ -0,0 +1,5 @@ +'use strict'; + +// Flags: --no-node-snapshot + +require('../common'); diff --git a/test/js/node/test/parallel/test-outgoing-message-destroy.js b/test/js/node/test/parallel/test-outgoing-message-destroy.js new file mode 100644 index 0000000000..0ee7b5f40e --- /dev/null +++ b/test/js/node/test/parallel/test-outgoing-message-destroy.js @@ -0,0 +1,13 @@ +'use strict'; + +// Test that http.OutgoingMessage,prototype.destroy() returns `this`. +require('../common'); + +const assert = require('assert'); +const http = require('http'); +const outgoingMessage = new http.OutgoingMessage(); + +assert.strictEqual(outgoingMessage.destroyed, false); +assert.strictEqual(outgoingMessage.destroy(), outgoingMessage); +assert.strictEqual(outgoingMessage.destroyed, true); +assert.strictEqual(outgoingMessage.destroy(), outgoingMessage); diff --git a/test/js/node/test/parallel/test-path-basename.js b/test/js/node/test/parallel/test-path-basename.js new file mode 100644 index 0000000000..b16f9e5d63 --- /dev/null +++ b/test/js/node/test/parallel/test-path-basename.js @@ -0,0 +1,76 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +assert.strictEqual(path.basename(__filename), 'test-path-basename.js'); +assert.strictEqual(path.basename(__filename, '.js'), 'test-path-basename'); +assert.strictEqual(path.basename('.js', '.js'), ''); +assert.strictEqual(path.basename('js', '.js'), 'js'); +assert.strictEqual(path.basename('file.js', '.ts'), 'file.js'); +assert.strictEqual(path.basename('file', '.js'), 'file'); +assert.strictEqual(path.basename('file.js.old', '.js.old'), 'file'); +assert.strictEqual(path.basename(''), ''); +assert.strictEqual(path.basename('/dir/basename.ext'), 'basename.ext'); +assert.strictEqual(path.basename('/basename.ext'), 'basename.ext'); +assert.strictEqual(path.basename('basename.ext'), 'basename.ext'); +assert.strictEqual(path.basename('basename.ext/'), 'basename.ext'); +assert.strictEqual(path.basename('basename.ext//'), 'basename.ext'); +assert.strictEqual(path.basename('aaa/bbb', '/bbb'), 'bbb'); +assert.strictEqual(path.basename('aaa/bbb', 'a/bbb'), 'bbb'); +assert.strictEqual(path.basename('aaa/bbb', 'bbb'), 'bbb'); +assert.strictEqual(path.basename('aaa/bbb//', 'bbb'), 'bbb'); +assert.strictEqual(path.basename('aaa/bbb', 'bb'), 'b'); +assert.strictEqual(path.basename('aaa/bbb', 'b'), 'bb'); +assert.strictEqual(path.basename('/aaa/bbb', '/bbb'), 'bbb'); +assert.strictEqual(path.basename('/aaa/bbb', 'a/bbb'), 'bbb'); +assert.strictEqual(path.basename('/aaa/bbb', 'bbb'), 'bbb'); +assert.strictEqual(path.basename('/aaa/bbb//', 'bbb'), 'bbb'); +assert.strictEqual(path.basename('/aaa/bbb', 'bb'), 'b'); +assert.strictEqual(path.basename('/aaa/bbb', 'b'), 'bb'); +assert.strictEqual(path.basename('/aaa/bbb'), 'bbb'); +assert.strictEqual(path.basename('/aaa/'), 'aaa'); +assert.strictEqual(path.basename('/aaa/b'), 'b'); +assert.strictEqual(path.basename('/a/b'), 'b'); +assert.strictEqual(path.basename('//a'), 'a'); +assert.strictEqual(path.basename('a', 'a'), ''); + +// On Windows a backslash acts as a path separator. +assert.strictEqual(path.win32.basename('\\dir\\basename.ext'), 'basename.ext'); +assert.strictEqual(path.win32.basename('\\basename.ext'), 'basename.ext'); +assert.strictEqual(path.win32.basename('basename.ext'), 'basename.ext'); +assert.strictEqual(path.win32.basename('basename.ext\\'), 'basename.ext'); +assert.strictEqual(path.win32.basename('basename.ext\\\\'), 'basename.ext'); +assert.strictEqual(path.win32.basename('foo'), 'foo'); +assert.strictEqual(path.win32.basename('aaa\\bbb', '\\bbb'), 'bbb'); +assert.strictEqual(path.win32.basename('aaa\\bbb', 'a\\bbb'), 'bbb'); +assert.strictEqual(path.win32.basename('aaa\\bbb', 'bbb'), 'bbb'); +assert.strictEqual(path.win32.basename('aaa\\bbb\\\\\\\\', 'bbb'), 'bbb'); +assert.strictEqual(path.win32.basename('aaa\\bbb', 'bb'), 'b'); +assert.strictEqual(path.win32.basename('aaa\\bbb', 'b'), 'bb'); +assert.strictEqual(path.win32.basename('C:'), ''); +assert.strictEqual(path.win32.basename('C:.'), '.'); +assert.strictEqual(path.win32.basename('C:\\'), ''); +assert.strictEqual(path.win32.basename('C:\\dir\\base.ext'), 'base.ext'); +assert.strictEqual(path.win32.basename('C:\\basename.ext'), 'basename.ext'); +assert.strictEqual(path.win32.basename('C:basename.ext'), 'basename.ext'); +assert.strictEqual(path.win32.basename('C:basename.ext\\'), 'basename.ext'); +assert.strictEqual(path.win32.basename('C:basename.ext\\\\'), 'basename.ext'); +assert.strictEqual(path.win32.basename('C:foo'), 'foo'); +assert.strictEqual(path.win32.basename('file:stream'), 'file:stream'); +assert.strictEqual(path.win32.basename('a', 'a'), ''); + +// On unix a backslash is just treated as any other character. +assert.strictEqual(path.posix.basename('\\dir\\basename.ext'), + '\\dir\\basename.ext'); +assert.strictEqual(path.posix.basename('\\basename.ext'), '\\basename.ext'); +assert.strictEqual(path.posix.basename('basename.ext'), 'basename.ext'); +assert.strictEqual(path.posix.basename('basename.ext\\'), 'basename.ext\\'); +assert.strictEqual(path.posix.basename('basename.ext\\\\'), 'basename.ext\\\\'); +assert.strictEqual(path.posix.basename('foo'), 'foo'); + +// POSIX filenames may include control characters +// c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html +const controlCharFilename = `Icon${String.fromCharCode(13)}`; +assert.strictEqual(path.posix.basename(`/a/b/${controlCharFilename}`), + controlCharFilename); diff --git a/test/js/node/test/parallel/test-path-dirname.js b/test/js/node/test/parallel/test-path-dirname.js new file mode 100644 index 0000000000..0d4a182884 --- /dev/null +++ b/test/js/node/test/parallel/test-path-dirname.js @@ -0,0 +1,59 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); + +assert.strictEqual(path.dirname(__filename).slice(-13), + common.isWindows ? 'test\\parallel' : 'test/parallel'); + +assert.strictEqual(path.posix.dirname('/a/b/'), '/a'); +assert.strictEqual(path.posix.dirname('/a/b'), '/a'); +assert.strictEqual(path.posix.dirname('/a'), '/'); +assert.strictEqual(path.posix.dirname(''), '.'); +assert.strictEqual(path.posix.dirname('/'), '/'); +assert.strictEqual(path.posix.dirname('////'), '/'); +assert.strictEqual(path.posix.dirname('//a'), '//'); +assert.strictEqual(path.posix.dirname('foo'), '.'); + +assert.strictEqual(path.win32.dirname('c:\\'), 'c:\\'); +assert.strictEqual(path.win32.dirname('c:\\foo'), 'c:\\'); +assert.strictEqual(path.win32.dirname('c:\\foo\\'), 'c:\\'); +assert.strictEqual(path.win32.dirname('c:\\foo\\bar'), 'c:\\foo'); +assert.strictEqual(path.win32.dirname('c:\\foo\\bar\\'), 'c:\\foo'); +assert.strictEqual(path.win32.dirname('c:\\foo\\bar\\baz'), 'c:\\foo\\bar'); +assert.strictEqual(path.win32.dirname('c:\\foo bar\\baz'), 'c:\\foo bar'); +assert.strictEqual(path.win32.dirname('\\'), '\\'); +assert.strictEqual(path.win32.dirname('\\foo'), '\\'); +assert.strictEqual(path.win32.dirname('\\foo\\'), '\\'); +assert.strictEqual(path.win32.dirname('\\foo\\bar'), '\\foo'); +assert.strictEqual(path.win32.dirname('\\foo\\bar\\'), '\\foo'); +assert.strictEqual(path.win32.dirname('\\foo\\bar\\baz'), '\\foo\\bar'); +assert.strictEqual(path.win32.dirname('\\foo bar\\baz'), '\\foo bar'); +assert.strictEqual(path.win32.dirname('c:'), 'c:'); +assert.strictEqual(path.win32.dirname('c:foo'), 'c:'); +assert.strictEqual(path.win32.dirname('c:foo\\'), 'c:'); +assert.strictEqual(path.win32.dirname('c:foo\\bar'), 'c:foo'); +assert.strictEqual(path.win32.dirname('c:foo\\bar\\'), 'c:foo'); +assert.strictEqual(path.win32.dirname('c:foo\\bar\\baz'), 'c:foo\\bar'); +assert.strictEqual(path.win32.dirname('c:foo bar\\baz'), 'c:foo bar'); +assert.strictEqual(path.win32.dirname('file:stream'), '.'); +assert.strictEqual(path.win32.dirname('dir\\file:stream'), 'dir'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share'), + '\\\\unc\\share'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo'), + '\\\\unc\\share\\'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\'), + '\\\\unc\\share\\'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar'), + '\\\\unc\\share\\foo'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar\\'), + '\\\\unc\\share\\foo'); +assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar\\baz'), + '\\\\unc\\share\\foo\\bar'); +assert.strictEqual(path.win32.dirname('/a/b/'), '/a'); +assert.strictEqual(path.win32.dirname('/a/b'), '/a'); +assert.strictEqual(path.win32.dirname('/a'), '/'); +assert.strictEqual(path.win32.dirname(''), '.'); +assert.strictEqual(path.win32.dirname('/'), '/'); +assert.strictEqual(path.win32.dirname('////'), '/'); +assert.strictEqual(path.win32.dirname('foo'), '.'); diff --git a/test/js/node/test/parallel/test-path-extname.js b/test/js/node/test/parallel/test-path-extname.js new file mode 100644 index 0000000000..be5a6316b0 --- /dev/null +++ b/test/js/node/test/parallel/test-path-extname.js @@ -0,0 +1,100 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +const failures = []; +const slashRE = /\//g; + +const testPaths = [ + [__filename, '.js'], + ['', ''], + ['/path/to/file', ''], + ['/path/to/file.ext', '.ext'], + ['/path.to/file.ext', '.ext'], + ['/path.to/file', ''], + ['/path.to/.file', ''], + ['/path.to/.file.ext', '.ext'], + ['/path/to/f.ext', '.ext'], + ['/path/to/..ext', '.ext'], + ['/path/to/..', ''], + ['file', ''], + ['file.ext', '.ext'], + ['.file', ''], + ['.file.ext', '.ext'], + ['/file', ''], + ['/file.ext', '.ext'], + ['/.file', ''], + ['/.file.ext', '.ext'], + ['.path/file.ext', '.ext'], + ['file.ext.ext', '.ext'], + ['file.', '.'], + ['.', ''], + ['./', ''], + ['.file.ext', '.ext'], + ['.file', ''], + ['.file.', '.'], + ['.file..', '.'], + ['..', ''], + ['../', ''], + ['..file.ext', '.ext'], + ['..file', '.file'], + ['..file.', '.'], + ['..file..', '.'], + ['...', '.'], + ['...ext', '.ext'], + ['....', '.'], + ['file.ext/', '.ext'], + ['file.ext//', '.ext'], + ['file/', ''], + ['file//', ''], + ['file./', '.'], + ['file.//', '.'], +]; + +for (const testPath of testPaths) { + const expected = testPath[1]; + const extNames = [path.posix.extname, path.win32.extname]; + for (const extname of extNames) { + let input = testPath[0]; + let os; + if (extname === path.win32.extname) { + input = input.replace(slashRE, '\\'); + os = 'win32'; + } else { + os = 'posix'; + } + const actual = extname(input); + const message = `path.${os}.extname(${JSON.stringify(input)})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + if (actual !== expected) + failures.push(`\n${message}`); + } + const input = `C:${testPath[0].replace(slashRE, '\\')}`; + const actual = path.win32.extname(input); + const message = `path.win32.extname(${JSON.stringify(input)})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + if (actual !== expected) + failures.push(`\n${message}`); +} +assert.strictEqual(failures.length, 0, failures.join('')); + +// On Windows, backslash is a path separator. +assert.strictEqual(path.win32.extname('.\\'), ''); +assert.strictEqual(path.win32.extname('..\\'), ''); +assert.strictEqual(path.win32.extname('file.ext\\'), '.ext'); +assert.strictEqual(path.win32.extname('file.ext\\\\'), '.ext'); +assert.strictEqual(path.win32.extname('file\\'), ''); +assert.strictEqual(path.win32.extname('file\\\\'), ''); +assert.strictEqual(path.win32.extname('file.\\'), '.'); +assert.strictEqual(path.win32.extname('file.\\\\'), '.'); + +// On *nix, backslash is a valid name component like any other character. +assert.strictEqual(path.posix.extname('.\\'), ''); +assert.strictEqual(path.posix.extname('..\\'), '.\\'); +assert.strictEqual(path.posix.extname('file.ext\\'), '.ext\\'); +assert.strictEqual(path.posix.extname('file.ext\\\\'), '.ext\\\\'); +assert.strictEqual(path.posix.extname('file\\'), ''); +assert.strictEqual(path.posix.extname('file\\\\'), ''); +assert.strictEqual(path.posix.extname('file.\\'), '.\\'); +assert.strictEqual(path.posix.extname('file.\\\\'), '.\\\\'); diff --git a/test/js/node/test/parallel/test-path-isabsolute.js b/test/js/node/test/parallel/test-path-isabsolute.js new file mode 100644 index 0000000000..66b4f1ee51 --- /dev/null +++ b/test/js/node/test/parallel/test-path-isabsolute.js @@ -0,0 +1,28 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +assert.strictEqual(path.win32.isAbsolute('/'), true); +assert.strictEqual(path.win32.isAbsolute('//'), true); +assert.strictEqual(path.win32.isAbsolute('//server'), true); +assert.strictEqual(path.win32.isAbsolute('//server/file'), true); +assert.strictEqual(path.win32.isAbsolute('\\\\server\\file'), true); +assert.strictEqual(path.win32.isAbsolute('\\\\server'), true); +assert.strictEqual(path.win32.isAbsolute('\\\\'), true); +assert.strictEqual(path.win32.isAbsolute('c'), false); +assert.strictEqual(path.win32.isAbsolute('c:'), false); +assert.strictEqual(path.win32.isAbsolute('c:\\'), true); +assert.strictEqual(path.win32.isAbsolute('c:/'), true); +assert.strictEqual(path.win32.isAbsolute('c://'), true); +assert.strictEqual(path.win32.isAbsolute('C:/Users/'), true); +assert.strictEqual(path.win32.isAbsolute('C:\\Users\\'), true); +assert.strictEqual(path.win32.isAbsolute('C:cwd/another'), false); +assert.strictEqual(path.win32.isAbsolute('C:cwd\\another'), false); +assert.strictEqual(path.win32.isAbsolute('directory/directory'), false); +assert.strictEqual(path.win32.isAbsolute('directory\\directory'), false); + +assert.strictEqual(path.posix.isAbsolute('/home/foo'), true); +assert.strictEqual(path.posix.isAbsolute('/home/foo/..'), true); +assert.strictEqual(path.posix.isAbsolute('bar/'), false); +assert.strictEqual(path.posix.isAbsolute('./baz'), false); diff --git a/test/js/node/test/parallel/test-path-join.js b/test/js/node/test/parallel/test-path-join.js new file mode 100644 index 0000000000..d6d1839996 --- /dev/null +++ b/test/js/node/test/parallel/test-path-join.js @@ -0,0 +1,143 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +const failures = []; +const backslashRE = /\\/g; + +const joinTests = [ + [ [path.posix.join, path.win32.join], + // Arguments result + [[['.', 'x/b', '..', '/b/c.js'], 'x/b/c.js'], + [[], '.'], + [['/.', 'x/b', '..', '/b/c.js'], '/x/b/c.js'], + [['/foo', '../../../bar'], '/bar'], + [['foo', '../../../bar'], '../../bar'], + [['foo/', '../../../bar'], '../../bar'], + [['foo/x', '../../../bar'], '../bar'], + [['foo/x', './bar'], 'foo/x/bar'], + [['foo/x/', './bar'], 'foo/x/bar'], + [['foo/x/', '.', 'bar'], 'foo/x/bar'], + [['./'], './'], + [['.', './'], './'], + [['.', '.', '.'], '.'], + [['.', './', '.'], '.'], + [['.', '/./', '.'], '.'], + [['.', '/////./', '.'], '.'], + [['.'], '.'], + [['', '.'], '.'], + [['', 'foo'], 'foo'], + [['foo', '/bar'], 'foo/bar'], + [['', '/foo'], '/foo'], + [['', '', '/foo'], '/foo'], + [['', '', 'foo'], 'foo'], + [['foo', ''], 'foo'], + [['foo/', ''], 'foo/'], + [['foo', '', '/bar'], 'foo/bar'], + [['./', '..', '/foo'], '../foo'], + [['./', '..', '..', '/foo'], '../../foo'], + [['.', '..', '..', '/foo'], '../../foo'], + [['', '..', '..', '/foo'], '../../foo'], + [['/'], '/'], + [['/', '.'], '/'], + [['/', '..'], '/'], + [['/', '..', '..'], '/'], + [[''], '.'], + [['', ''], '.'], + [[' /foo'], ' /foo'], + [[' ', 'foo'], ' /foo'], + [[' ', '.'], ' '], + [[' ', '/'], ' /'], + [[' ', ''], ' '], + [['/', 'foo'], '/foo'], + [['/', '/foo'], '/foo'], + [['/', '//foo'], '/foo'], + [['/', '', '/foo'], '/foo'], + [['', '/', 'foo'], '/foo'], + [['', '/', '/foo'], '/foo'], + ], + ], +]; + +// Windows-specific join tests +joinTests.push([ + path.win32.join, + joinTests[0][1].slice(0).concat( + [// Arguments result + // UNC path expected + [['//foo/bar'], '\\\\foo\\bar\\'], + [['\\/foo/bar'], '\\\\foo\\bar\\'], + [['\\\\foo/bar'], '\\\\foo\\bar\\'], + // UNC path expected - server and share separate + [['//foo', 'bar'], '\\\\foo\\bar\\'], + [['//foo/', 'bar'], '\\\\foo\\bar\\'], + [['//foo', '/bar'], '\\\\foo\\bar\\'], + // UNC path expected - questionable + [['//foo', '', 'bar'], '\\\\foo\\bar\\'], + [['//foo/', '', 'bar'], '\\\\foo\\bar\\'], + [['//foo/', '', '/bar'], '\\\\foo\\bar\\'], + // UNC path expected - even more questionable + [['', '//foo', 'bar'], '\\\\foo\\bar\\'], + [['', '//foo/', 'bar'], '\\\\foo\\bar\\'], + [['', '//foo/', '/bar'], '\\\\foo\\bar\\'], + // No UNC path expected (no double slash in first component) + [['\\', 'foo/bar'], '\\foo\\bar'], + [['\\', '/foo/bar'], '\\foo\\bar'], + [['', '/', '/foo/bar'], '\\foo\\bar'], + // No UNC path expected (no non-slashes in first component - + // questionable) + [['//', 'foo/bar'], '\\foo\\bar'], + [['//', '/foo/bar'], '\\foo\\bar'], + [['\\\\', '/', '/foo/bar'], '\\foo\\bar'], + [['//'], '\\'], + // No UNC path expected (share name missing - questionable). + [['//foo'], '\\foo'], + [['//foo/'], '\\foo\\'], + [['//foo', '/'], '\\foo\\'], + [['//foo', '', '/'], '\\foo\\'], + // No UNC path expected (too many leading slashes - questionable) + [['///foo/bar'], '\\foo\\bar'], + [['////foo', 'bar'], '\\foo\\bar'], + [['\\\\\\/foo/bar'], '\\foo\\bar'], + // Drive-relative vs drive-absolute paths. This merely describes the + // status quo, rather than being obviously right + [['c:'], 'c:.'], + [['c:.'], 'c:.'], + [['c:', ''], 'c:.'], + [['', 'c:'], 'c:.'], + [['c:.', '/'], 'c:.\\'], + [['c:.', 'file'], 'c:file'], + [['c:', '/'], 'c:\\'], + [['c:', 'file'], 'c:\\file'], + ] + ), +]); +joinTests.forEach((test) => { + if (!Array.isArray(test[0])) + test[0] = [test[0]]; + test[0].forEach((join) => { + test[1].forEach((test) => { + const actual = join.apply(null, test[0]); + const expected = test[1]; + // For non-Windows specific tests with the Windows join(), we need to try + // replacing the slashes since the non-Windows specific tests' `expected` + // use forward slashes + let actualAlt; + let os; + if (join === path.win32.join) { + actualAlt = actual.replace(backslashRE, '/'); + os = 'win32'; + } else { + os = 'posix'; + } + if (actual !== expected && actualAlt !== expected) { + const delimiter = test[0].map(JSON.stringify).join(','); + const message = `path.${os}.join(${delimiter})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + failures.push(`\n${message}`); + } + }); + }); +}); +assert.strictEqual(failures.length, 0, failures.join('')); diff --git a/test/js/node/test/parallel/test-path-makelong.js b/test/js/node/test/parallel/test-path-makelong.js new file mode 100644 index 0000000000..7a4783953c --- /dev/null +++ b/test/js/node/test/parallel/test-path-makelong.js @@ -0,0 +1,88 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const path = require('path'); + +if (common.isWindows) { + const file = fixtures.path('a.js'); + const resolvedFile = path.resolve(file); + + assert.strictEqual(path.toNamespacedPath(file), + `\\\\?\\${resolvedFile}`); + assert.strictEqual(path.toNamespacedPath(`\\\\?\\${file}`), + `\\\\?\\${resolvedFile}`); + assert.strictEqual(path.toNamespacedPath( + '\\\\someserver\\someshare\\somefile'), + '\\\\?\\UNC\\someserver\\someshare\\somefile'); + assert.strictEqual(path.toNamespacedPath( + '\\\\?\\UNC\\someserver\\someshare\\somefile'), + '\\\\?\\UNC\\someserver\\someshare\\somefile'); + assert.strictEqual(path.toNamespacedPath('\\\\.\\pipe\\somepipe'), + '\\\\.\\pipe\\somepipe'); +} + +assert.strictEqual(path.toNamespacedPath(''), ''); +assert.strictEqual(path.toNamespacedPath(null), null); +assert.strictEqual(path.toNamespacedPath(100), 100); +assert.strictEqual(path.toNamespacedPath(path), path); +assert.strictEqual(path.toNamespacedPath(false), false); +assert.strictEqual(path.toNamespacedPath(true), true); + +const emptyObj = {}; +assert.strictEqual(path.posix.toNamespacedPath('/foo/bar'), '/foo/bar'); +assert.strictEqual(path.posix.toNamespacedPath('foo/bar'), 'foo/bar'); +assert.strictEqual(path.posix.toNamespacedPath(null), null); +assert.strictEqual(path.posix.toNamespacedPath(true), true); +assert.strictEqual(path.posix.toNamespacedPath(1), 1); +assert.strictEqual(path.posix.toNamespacedPath(), undefined); +assert.strictEqual(path.posix.toNamespacedPath(emptyObj), emptyObj); +if (common.isWindows) { + // These tests cause resolve() to insert the cwd, so we cannot test them from + // non-Windows platforms (easily) + assert.strictEqual(path.toNamespacedPath(''), ''); + assert.strictEqual(path.win32.toNamespacedPath('foo\\bar').toLowerCase(), + `\\\\?\\${process.cwd().toLowerCase()}\\foo\\bar`); + assert.strictEqual(path.win32.toNamespacedPath('foo/bar').toLowerCase(), + `\\\\?\\${process.cwd().toLowerCase()}\\foo\\bar`); + const currentDeviceLetter = path.parse(process.cwd()).root.substring(0, 2); + assert.strictEqual( + path.win32.toNamespacedPath(currentDeviceLetter).toLowerCase(), + `\\\\?\\${process.cwd().toLowerCase()}`); + assert.strictEqual(path.win32.toNamespacedPath('C').toLowerCase(), + `\\\\?\\${process.cwd().toLowerCase()}\\c`); +} +assert.strictEqual(path.win32.toNamespacedPath('C:\\foo'), '\\\\?\\C:\\foo'); +assert.strictEqual(path.win32.toNamespacedPath('C:/foo'), '\\\\?\\C:\\foo'); +assert.strictEqual(path.win32.toNamespacedPath('\\\\foo\\bar'), + '\\\\?\\UNC\\foo\\bar\\'); +assert.strictEqual(path.win32.toNamespacedPath('//foo//bar'), + '\\\\?\\UNC\\foo\\bar\\'); +assert.strictEqual(path.win32.toNamespacedPath('\\\\?\\foo'), '\\\\?\\foo\\'); +assert.strictEqual(path.win32.toNamespacedPath('\\\\?\\c:\\Windows/System'), '\\\\?\\c:\\Windows\\System'); +assert.strictEqual(path.win32.toNamespacedPath(null), null); +assert.strictEqual(path.win32.toNamespacedPath(true), true); +assert.strictEqual(path.win32.toNamespacedPath(1), 1); +assert.strictEqual(path.win32.toNamespacedPath(), undefined); +assert.strictEqual(path.win32.toNamespacedPath(emptyObj), emptyObj); diff --git a/test/js/node/test/parallel/test-path-normalize.js b/test/js/node/test/parallel/test-path-normalize.js new file mode 100644 index 0000000000..e1d3b9ce1e --- /dev/null +++ b/test/js/node/test/parallel/test-path-normalize.js @@ -0,0 +1,72 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +assert.strictEqual(path.win32.normalize('./fixtures///b/../b/c.js'), + 'fixtures\\b\\c.js'); +assert.strictEqual(path.win32.normalize('/foo/../../../bar'), '\\bar'); +assert.strictEqual(path.win32.normalize('a//b//../b'), 'a\\b'); +assert.strictEqual(path.win32.normalize('a//b//./c'), 'a\\b\\c'); +assert.strictEqual(path.win32.normalize('a//b//.'), 'a\\b'); +assert.strictEqual(path.win32.normalize('//server/share/dir/file.ext'), + '\\\\server\\share\\dir\\file.ext'); +assert.strictEqual(path.win32.normalize('/a/b/c/../../../x/y/z'), '\\x\\y\\z'); +assert.strictEqual(path.win32.normalize('C:'), 'C:.'); +assert.strictEqual(path.win32.normalize('C:..\\abc'), 'C:..\\abc'); +assert.strictEqual(path.win32.normalize('C:..\\..\\abc\\..\\def'), + 'C:..\\..\\def'); +assert.strictEqual(path.win32.normalize('C:\\.'), 'C:\\'); +assert.strictEqual(path.win32.normalize('file:stream'), 'file:stream'); +assert.strictEqual(path.win32.normalize('bar\\foo..\\..\\'), 'bar\\'); +assert.strictEqual(path.win32.normalize('bar\\foo..\\..'), 'bar'); +assert.strictEqual(path.win32.normalize('bar\\foo..\\..\\baz'), 'bar\\baz'); +assert.strictEqual(path.win32.normalize('bar\\foo..\\'), 'bar\\foo..\\'); +assert.strictEqual(path.win32.normalize('bar\\foo..'), 'bar\\foo..'); +assert.strictEqual(path.win32.normalize('..\\foo..\\..\\..\\bar'), + '..\\..\\bar'); +assert.strictEqual(path.win32.normalize('..\\...\\..\\.\\...\\..\\..\\bar'), + '..\\..\\bar'); +assert.strictEqual(path.win32.normalize('../../../foo/../../../bar'), + '..\\..\\..\\..\\..\\bar'); +assert.strictEqual(path.win32.normalize('../../../foo/../../../bar/../../'), + '..\\..\\..\\..\\..\\..\\'); +assert.strictEqual( + path.win32.normalize('../foobar/barfoo/foo/../../../bar/../../'), + '..\\..\\' +); +assert.strictEqual( + path.win32.normalize('../.../../foobar/../../../bar/../../baz'), + '..\\..\\..\\..\\baz' +); +assert.strictEqual(path.win32.normalize('foo/bar\\baz'), 'foo\\bar\\baz'); + +assert.strictEqual(path.posix.normalize('./fixtures///b/../b/c.js'), + 'fixtures/b/c.js'); +assert.strictEqual(path.posix.normalize('/foo/../../../bar'), '/bar'); +assert.strictEqual(path.posix.normalize('a//b//../b'), 'a/b'); +assert.strictEqual(path.posix.normalize('a//b//./c'), 'a/b/c'); +assert.strictEqual(path.posix.normalize('a//b//.'), 'a/b'); +assert.strictEqual(path.posix.normalize('/a/b/c/../../../x/y/z'), '/x/y/z'); +assert.strictEqual(path.posix.normalize('///..//./foo/.//bar'), '/foo/bar'); +assert.strictEqual(path.posix.normalize('bar/foo../../'), 'bar/'); +assert.strictEqual(path.posix.normalize('bar/foo../..'), 'bar'); +assert.strictEqual(path.posix.normalize('bar/foo../../baz'), 'bar/baz'); +assert.strictEqual(path.posix.normalize('bar/foo../'), 'bar/foo../'); +assert.strictEqual(path.posix.normalize('bar/foo..'), 'bar/foo..'); +assert.strictEqual(path.posix.normalize('../foo../../../bar'), '../../bar'); +assert.strictEqual(path.posix.normalize('../.../.././.../../../bar'), + '../../bar'); +assert.strictEqual(path.posix.normalize('../../../foo/../../../bar'), + '../../../../../bar'); +assert.strictEqual(path.posix.normalize('../../../foo/../../../bar/../../'), + '../../../../../../'); +assert.strictEqual( + path.posix.normalize('../foobar/barfoo/foo/../../../bar/../../'), + '../../' +); +assert.strictEqual( + path.posix.normalize('../.../../foobar/../../../bar/../../baz'), + '../../../../baz' +); +assert.strictEqual(path.posix.normalize('foo/bar\\baz'), 'foo/bar\\baz'); diff --git a/test/js/node/test/parallel/test-path-posix-exists.js b/test/js/node/test/parallel/test-path-posix-exists.js new file mode 100644 index 0000000000..dc12ed6daf --- /dev/null +++ b/test/js/node/test/parallel/test-path-posix-exists.js @@ -0,0 +1,6 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(require('path/posix'), require('path').posix); diff --git a/test/js/node/test/parallel/test-path-posix-relative-on-windows.js b/test/js/node/test/parallel/test-path-posix-relative-on-windows.js new file mode 100644 index 0000000000..bcaaca8b18 --- /dev/null +++ b/test/js/node/test/parallel/test-path-posix-relative-on-windows.js @@ -0,0 +1,10 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const path = require('path'); + +// Refs: https://github.com/nodejs/node/issues/13683 + +const relativePath = path.posix.relative('a/b/c', '../../x'); +assert.match(relativePath, /^(\.\.\/){3,5}x$/); diff --git a/test/js/node/test/parallel/test-path-relative.js b/test/js/node/test/parallel/test-path-relative.js new file mode 100644 index 0000000000..f6a9f5662a --- /dev/null +++ b/test/js/node/test/parallel/test-path-relative.js @@ -0,0 +1,69 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const path = require('path'); + +const failures = []; + +const relativeTests = [ + [ path.win32.relative, + // Arguments result + [['c:/blah\\blah', 'd:/games', 'd:\\games'], + ['c:/aaaa/bbbb', 'c:/aaaa', '..'], + ['c:/aaaa/bbbb', 'c:/cccc', '..\\..\\cccc'], + ['c:/aaaa/bbbb', 'c:/aaaa/bbbb', ''], + ['c:/aaaa/bbbb', 'c:/aaaa/cccc', '..\\cccc'], + ['c:/aaaa/', 'c:/aaaa/cccc', 'cccc'], + ['c:/', 'c:\\aaaa\\bbbb', 'aaaa\\bbbb'], + ['c:/aaaa/bbbb', 'd:\\', 'd:\\'], + ['c:/AaAa/bbbb', 'c:/aaaa/bbbb', ''], + ['c:/aaaaa/', 'c:/aaaa/cccc', '..\\aaaa\\cccc'], + ['C:\\foo\\bar\\baz\\quux', 'C:\\', '..\\..\\..\\..'], + ['C:\\foo\\test', 'C:\\foo\\test\\bar\\package.json', 'bar\\package.json'], + ['C:\\foo\\bar\\baz-quux', 'C:\\foo\\bar\\baz', '..\\baz'], + ['C:\\foo\\bar\\baz', 'C:\\foo\\bar\\baz-quux', '..\\baz-quux'], + ['\\\\foo\\bar', '\\\\foo\\bar\\baz', 'baz'], + ['\\\\foo\\bar\\baz', '\\\\foo\\bar', '..'], + ['\\\\foo\\bar\\baz-quux', '\\\\foo\\bar\\baz', '..\\baz'], + ['\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz-quux', '..\\baz-quux'], + ['C:\\baz-quux', 'C:\\baz', '..\\baz'], + ['C:\\baz', 'C:\\baz-quux', '..\\baz-quux'], + ['\\\\foo\\baz-quux', '\\\\foo\\baz', '..\\baz'], + ['\\\\foo\\baz', '\\\\foo\\baz-quux', '..\\baz-quux'], + ['C:\\baz', '\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz'], + ['\\\\foo\\bar\\baz', 'C:\\baz', 'C:\\baz'], + ], + ], + [ path.posix.relative, + // Arguments result + [['/var/lib', '/var', '..'], + ['/var/lib', '/bin', '../../bin'], + ['/var/lib', '/var/lib', ''], + ['/var/lib', '/var/apache', '../apache'], + ['/var/', '/var/lib', 'lib'], + ['/', '/var/lib', 'var/lib'], + ['/foo/test', '/foo/test/bar/package.json', 'bar/package.json'], + ['/Users/a/web/b/test/mails', '/Users/a/web/b', '../..'], + ['/foo/bar/baz-quux', '/foo/bar/baz', '../baz'], + ['/foo/bar/baz', '/foo/bar/baz-quux', '../baz-quux'], + ['/baz-quux', '/baz', '../baz'], + ['/baz', '/baz-quux', '../baz-quux'], + ['/page1/page2/foo', '/', '../../..'], + ], + ], +]; +relativeTests.forEach((test) => { + const relative = test[0]; + test[1].forEach((test) => { + const actual = relative(test[0], test[1]); + const expected = test[2]; + if (actual !== expected) { + const os = relative === path.win32.relative ? 'win32' : 'posix'; + const message = `path.${os}.relative(${ + test.slice(0, 2).map(JSON.stringify).join(',')})\n expect=${ + JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + failures.push(`\n${message}`); + } + }); +}); +assert.strictEqual(failures.length, 0, failures.join('')); diff --git a/test/js/node/test/parallel/test-path-win32-exists.js b/test/js/node/test/parallel/test-path-win32-exists.js new file mode 100644 index 0000000000..c9efa74dbd --- /dev/null +++ b/test/js/node/test/parallel/test-path-win32-exists.js @@ -0,0 +1,6 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(require('path/win32'), require('path').win32); diff --git a/test/js/node/test/parallel/test-path-zero-length-strings.js b/test/js/node/test/parallel/test-path-zero-length-strings.js new file mode 100644 index 0000000000..f6516ffff8 --- /dev/null +++ b/test/js/node/test/parallel/test-path-zero-length-strings.js @@ -0,0 +1,39 @@ +'use strict'; + +// These testcases are specific to one uncommon behavior in path module. Few +// of the functions in path module, treat '' strings as current working +// directory. This test makes sure that the behavior is intact between commits. +// See: https://github.com/nodejs/node/pull/2106 + +require('../common'); +const assert = require('assert'); +const path = require('path'); +const pwd = process.cwd(); + +// Join will internally ignore all the zero-length strings and it will return +// '.' if the joined string is a zero-length string. +assert.strictEqual(path.posix.join(''), '.'); +assert.strictEqual(path.posix.join('', ''), '.'); +assert.strictEqual(path.win32.join(''), '.'); +assert.strictEqual(path.win32.join('', ''), '.'); +assert.strictEqual(path.join(pwd), pwd); +assert.strictEqual(path.join(pwd, ''), pwd); + +// Normalize will return '.' if the input is a zero-length string +assert.strictEqual(path.posix.normalize(''), '.'); +assert.strictEqual(path.win32.normalize(''), '.'); +assert.strictEqual(path.normalize(pwd), pwd); + +// Since '' is not a valid path in any of the common environments, return false +assert.strictEqual(path.posix.isAbsolute(''), false); +assert.strictEqual(path.win32.isAbsolute(''), false); + +// Resolve, internally ignores all the zero-length strings and returns the +// current working directory +assert.strictEqual(path.resolve(''), pwd); +assert.strictEqual(path.resolve('', ''), pwd); + +// Relative, internally calls resolve. So, '' is actually the current directory +assert.strictEqual(path.relative('', pwd), ''); +assert.strictEqual(path.relative(pwd, ''), ''); +assert.strictEqual(path.relative(pwd, pwd), ''); diff --git a/test/js/node/test/parallel/test-path.js b/test/js/node/test/parallel/test-path.js new file mode 100644 index 0000000000..0cb55d42aa --- /dev/null +++ b/test/js/node/test/parallel/test-path.js @@ -0,0 +1,73 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); + +// Test thrown TypeErrors +const typeErrorTests = [true, false, 7, null, {}, undefined, [], NaN]; + +function fail(fn) { + const args = Array.from(arguments).slice(1); + + assert.throws(() => { + fn.apply(null, args); + }, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }); +} + +for (const test of typeErrorTests) { + for (const namespace of [path.posix, path.win32]) { + fail(namespace.join, test); + fail(namespace.resolve, test); + fail(namespace.normalize, test); + fail(namespace.isAbsolute, test); + fail(namespace.relative, test, 'foo'); + fail(namespace.relative, 'foo', test); + fail(namespace.parse, test); + fail(namespace.dirname, test); + fail(namespace.basename, test); + fail(namespace.extname, test); + + // Undefined is a valid value as the second argument to basename + if (test !== undefined) { + fail(namespace.basename, 'foo', test); + } + } +} + +// path.sep tests +// windows +assert.strictEqual(path.win32.sep, '\\'); +// posix +assert.strictEqual(path.posix.sep, '/'); + +// path.delimiter tests +// windows +assert.strictEqual(path.win32.delimiter, ';'); +// posix +assert.strictEqual(path.posix.delimiter, ':'); + +if (common.isWindows) + assert.strictEqual(path, path.win32); +else + assert.strictEqual(path, path.posix); diff --git a/test/js/node/test/parallel/test-perf-gc-crash.js b/test/js/node/test/parallel/test-perf-gc-crash.js new file mode 100644 index 0000000000..d980e91a2f --- /dev/null +++ b/test/js/node/test/parallel/test-perf-gc-crash.js @@ -0,0 +1,25 @@ +'use strict'; + +require('../common'); + +// Refers to https://github.com/nodejs/node/issues/39548 + +// The test fails if this crashes. If it closes normally, +// then all is good. + +const { + PerformanceObserver, +} = require('perf_hooks'); + +// We don't actually care if the observer callback is called here. +const gcObserver = new PerformanceObserver(() => {}); + +gcObserver.observe({ entryTypes: ['gc'] }); + +gcObserver.disconnect(); + +const gcObserver2 = new PerformanceObserver(() => {}); + +gcObserver2.observe({ entryTypes: ['gc'] }); + +gcObserver2.disconnect(); diff --git a/test/js/node/test/parallel/test-performance-measure.js b/test/js/node/test/parallel/test-performance-measure.js new file mode 100644 index 0000000000..949258f96e --- /dev/null +++ b/test/js/node/test/parallel/test-performance-measure.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const { PerformanceObserver, performance } = require('perf_hooks'); +const DELAY = 1000; +const ALLOWED_MARGIN = 10; + +const expected = ['Start to Now', 'A to Now', 'A to B']; +const obs = new PerformanceObserver(common.mustCall((items) => { + items.getEntries().forEach(({ name, duration }) => { + assert.ok(duration > (DELAY - ALLOWED_MARGIN)); + assert.strictEqual(expected.shift(), name); + }); +})); +obs.observe({ entryTypes: ['measure'] }); + +performance.mark('A'); +setTimeout(common.mustCall(() => { + performance.measure('Start to Now'); + performance.measure('A to Now', 'A'); + + performance.mark('B'); + performance.measure('A to B', 'A', 'B'); +}), DELAY); diff --git a/test/js/node/test/parallel/test-performanceobserver-gc.js b/test/js/node/test/parallel/test-performanceobserver-gc.js new file mode 100644 index 0000000000..fe9397631c --- /dev/null +++ b/test/js/node/test/parallel/test-performanceobserver-gc.js @@ -0,0 +1,17 @@ +'use strict'; + +require('../common'); + +// Verifies that setting up two observers to listen +// to gc performance does not crash. + +const { + PerformanceObserver, +} = require('perf_hooks'); + +// We don't actually care if the callback is ever invoked in this test +const obs = new PerformanceObserver(() => {}); +const obs2 = new PerformanceObserver(() => {}); + +obs.observe({ type: 'gc' }); +obs2.observe({ type: 'gc' }); diff --git a/test/js/node/test/parallel/test-permission-fs-windows-path.js b/test/js/node/test/parallel/test-permission-fs-windows-path.js new file mode 100644 index 0000000000..552f8e1c21 --- /dev/null +++ b/test/js/node/test/parallel/test-permission-fs-windows-path.js @@ -0,0 +1,49 @@ +// Flags: --experimental-permission --allow-fs-read=* --allow-child-process +'use strict'; + +const common = require('../common'); +common.skipIfWorker(); + +const assert = require('assert'); +const { spawnSync } = require('child_process'); + +if (!common.isWindows) { + common.skip('windows UNC path test'); +} + +{ + const { stdout, status } = spawnSync(process.execPath, [ + '--experimental-permission', '--allow-fs-write', 'C:\\\\', '-e', + 'console.log(process.permission.has("fs.write", "C:\\\\"))', + ]); + assert.strictEqual(stdout.toString(), 'true\n'); + assert.strictEqual(status, 0); +} + +{ + const { stdout, status, stderr } = spawnSync(process.execPath, [ + '--experimental-permission', '--allow-fs-write="\\\\?\\C:\\"', '-e', + 'console.log(process.permission.has("fs.write", "C:\\\\"))', + ]); + assert.strictEqual(stdout.toString(), 'false\n', stderr.toString()); + assert.strictEqual(status, 0); +} + +{ + const { stdout, status, stderr } = spawnSync(process.execPath, [ + '--experimental-permission', '--allow-fs-write', 'C:\\', '-e', + `const path = require('path'); + console.log(process.permission.has('fs.write', path.toNamespacedPath('C:\\\\')))`, + ]); + assert.strictEqual(stdout.toString(), 'true\n', stderr.toString()); + assert.strictEqual(status, 0); +} + +{ + const { stdout, status, stderr } = spawnSync(process.execPath, [ + '--experimental-permission', '--allow-fs-write', 'C:\\*', '-e', + "console.log(process.permission.has('fs.write', '\\\\\\\\A\\\\C:\\Users'))", + ]); + assert.strictEqual(stdout.toString(), 'false\n', stderr.toString()); + assert.strictEqual(status, 0); +} diff --git a/test/js/node/test/parallel/test-pipe-abstract-socket-http.js b/test/js/node/test/parallel/test-pipe-abstract-socket-http.js new file mode 100644 index 0000000000..6d3beb44d1 --- /dev/null +++ b/test/js/node/test/parallel/test-pipe-abstract-socket-http.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +if (!common.isLinux) common.skip(); + +const server = http.createServer( + common.mustCall((req, res) => { + res.end('ok'); + }) +); + +server.listen( + '\0abstract', + common.mustCall(() => { + http.get( + { + socketPath: server.address(), + }, + common.mustCall((res) => { + assert.strictEqual(res.statusCode, 200); + server.close(); + }) + ); + }) +); diff --git a/test/js/node/test/parallel/test-pipe-abstract-socket.js b/test/js/node/test/parallel/test-pipe-abstract-socket.js new file mode 100644 index 0000000000..baf76d6b82 --- /dev/null +++ b/test/js/node/test/parallel/test-pipe-abstract-socket.js @@ -0,0 +1,34 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); + +if (!common.isLinux) common.skip(); + +const path = '\0abstract'; +const message = /can not set readableAll or writableAllt to true when path is abstract unix socket/; + +assert.throws(() => { + const server = net.createServer(common.mustNotCall()); + server.listen({ + path, + readableAll: true + }); +}, message); + +assert.throws(() => { + const server = net.createServer(common.mustNotCall()); + server.listen({ + path, + writableAll: true + }); +}, message); + +assert.throws(() => { + const server = net.createServer(common.mustNotCall()); + server.listen({ + path, + readableAll: true, + writableAll: true + }); +}, message); diff --git a/test/js/node/test/parallel/test-pipe-address.js b/test/js/node/test/parallel/test-pipe-address.js new file mode 100644 index 0000000000..3550434932 --- /dev/null +++ b/test/js/node/test/parallel/test-pipe-address.js @@ -0,0 +1,13 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const net = require('net'); +const server = net.createServer(common.mustNotCall()); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +server.listen(common.PIPE, common.mustCall(function() { + assert.strictEqual(server.address(), common.PIPE); + server.close(); +})); diff --git a/test/js/node/test/parallel/test-pipe-file-to-http.js b/test/js/node/test/parallel/test-pipe-file-to-http.js new file mode 100644 index 0000000000..6c1244427d --- /dev/null +++ b/test/js/node/test/parallel/test-pipe-file-to-http.js @@ -0,0 +1,83 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const http = require('http'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const filename = tmpdir.resolve('big'); +let count = 0; + +const server = http.createServer((req, res) => { + let timeoutId; + assert.strictEqual(req.method, 'POST'); + req.pause(); + + setTimeout(() => { + req.resume(); + }, 1000); + + req.on('data', (chunk) => { + count += chunk.length; + }); + + req.on('end', () => { + if (timeoutId) { + clearTimeout(timeoutId); + } + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end(); + }); +}); +server.listen(0); + +server.on('listening', () => { + common.createZeroFilledFile(filename); + makeRequest(); +}); + +function makeRequest() { + const req = http.request({ + port: server.address().port, + path: '/', + method: 'POST' + }); + + const s = fs.ReadStream(filename); + s.pipe(req); + s.on('close', common.mustSucceed()); + + req.on('response', (res) => { + res.resume(); + res.on('end', () => { + server.close(); + }); + }); +} + +process.on('exit', () => { + assert.strictEqual(count, 1024 * 10240); +}); diff --git a/test/js/node/test/parallel/test-pipe-head.js b/test/js/node/test/parallel/test-pipe-head.js new file mode 100644 index 0000000000..1e79249c29 --- /dev/null +++ b/test/js/node/test/parallel/test-pipe-head.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); + +const exec = require('child_process').exec; + +const nodePath = process.argv[0]; +const script = fixtures.path('print-10-lines.js'); + +const cmd = `"${nodePath}" "${script}" | head -2`; + +exec(cmd, common.mustSucceed((stdout, stderr) => { + const lines = stdout.split('\n'); + assert.strictEqual(lines.length, 3); +})); diff --git a/test/js/node/test/parallel/test-pipe-return-val.js b/test/js/node/test/parallel/test-pipe-return-val.js new file mode 100644 index 0000000000..f2a7f573ec --- /dev/null +++ b/test/js/node/test/parallel/test-pipe-return-val.js @@ -0,0 +1,33 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// This test ensures SourceStream.pipe(DestStream) returns DestStream + +require('../common'); +const Stream = require('stream').Stream; +const assert = require('assert'); + +const sourceStream = new Stream(); +const destStream = new Stream(); +const result = sourceStream.pipe(destStream); + +assert.strictEqual(result, destStream); diff --git a/test/js/node/test/parallel/test-pipe-writev.js b/test/js/node/test/parallel/test-pipe-writev.js new file mode 100644 index 0000000000..5e5b42e6a7 --- /dev/null +++ b/test/js/node/test/parallel/test-pipe-writev.js @@ -0,0 +1,45 @@ +'use strict'; + +const common = require('../common'); +if (common.isWindows) + common.skip('Unix-specific test'); + +const assert = require('assert'); +const net = require('net'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const server = net.createServer((connection) => { + connection.on('error', (err) => { + throw err; + }); + + const writev = connection._writev.bind(connection); + connection._writev = common.mustCall(writev); + + connection.cork(); + connection.write('pi'); + connection.write('ng'); + connection.end(); +}); + +server.on('error', (err) => { + throw err; +}); + +server.listen(common.PIPE, () => { + const client = net.connect(common.PIPE); + + client.on('error', (err) => { + throw err; + }); + + client.on('data', common.mustCall((data) => { + assert.strictEqual(data.toString(), 'ping'); + })); + + client.on('end', () => { + server.close(); + }); +}); diff --git a/test/js/node/test/parallel/test-preload-print-process-argv.js b/test/js/node/test/parallel/test-preload-print-process-argv.js new file mode 100644 index 0000000000..9d2774f8a4 --- /dev/null +++ b/test/js/node/test/parallel/test-preload-print-process-argv.js @@ -0,0 +1,34 @@ +'use strict'; + +// This tests that process.argv is the same in the preloaded module +// and the user module. + +require('../common'); + +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const { spawnSync } = require('child_process'); +const fs = require('fs'); + +tmpdir.refresh(); + +fs.writeFileSync( + tmpdir.resolve('preload.js'), + 'console.log(JSON.stringify(process.argv));', + 'utf-8'); + +fs.writeFileSync( + tmpdir.resolve('main.js'), + 'console.log(JSON.stringify(process.argv));', + 'utf-8'); + +const child = spawnSync(process.execPath, ['-r', './preload.js', 'main.js'], + { cwd: tmpdir.path }); + +if (child.status !== 0) { + console.log(child.stderr.toString()); + assert.strictEqual(child.status, 0); +} + +const lines = child.stdout.toString().trim().split('\n'); +assert.deepStrictEqual(JSON.parse(lines[0]), JSON.parse(lines[1])); diff --git a/test/js/node/test/parallel/test-preload-self-referential.js b/test/js/node/test/parallel/test-preload-self-referential.js new file mode 100644 index 0000000000..2624527deb --- /dev/null +++ b/test/js/node/test/parallel/test-preload-self-referential.js @@ -0,0 +1,20 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { exec } = require('child_process'); + +const nodeBinary = process.argv[0]; + +if (!common.isMainThread) + common.skip('process.chdir is not available in Workers'); + +const selfRefModule = fixtures.path('self_ref_module'); +const fixtureA = fixtures.path('printA.js'); + +exec(`"${nodeBinary}" -r self_ref "${fixtureA}"`, { cwd: selfRefModule }, + (err, stdout, stderr) => { + assert.ifError(err); + assert.strictEqual(stdout, 'A\n'); + }); diff --git a/test/js/node/test/parallel/test-process-abort.js b/test/js/node/test/parallel/test-process-abort.js new file mode 100644 index 0000000000..665e1399a3 --- /dev/null +++ b/test/js/node/test/parallel/test-process-abort.js @@ -0,0 +1,12 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +if (!common.isMainThread) + common.skip('process.abort() is not available in Workers'); + +// Check that our built-in methods do not have a prototype/constructor behaviour +// if they don't need to. This could be tested for any of our C++ methods. +assert.strictEqual(process.abort.prototype, undefined); +assert.throws(() => new process.abort(), TypeError); diff --git a/test/js/node/test/parallel/test-process-argv-0.js b/test/js/node/test/parallel/test-process-argv-0.js new file mode 100644 index 0000000000..21b406873f --- /dev/null +++ b/test/js/node/test/parallel/test-process-argv-0.js @@ -0,0 +1,42 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const path = require('path'); +const assert = require('assert'); +const spawn = require('child_process').spawn; + +if (process.argv[2] !== 'child') { + const child = spawn(process.execPath, [__filename, 'child'], { + cwd: path.dirname(process.execPath) + }); + + let childArgv0 = ''; + child.stdout.on('data', function(chunk) { + childArgv0 += chunk; + }); + process.on('exit', function() { + assert.strictEqual(childArgv0, process.execPath); + }); +} else { + process.stdout.write(process.argv[0]); +} diff --git a/test/js/node/test/parallel/test-process-constants-noatime.js b/test/js/node/test/parallel/test-process-constants-noatime.js new file mode 100644 index 0000000000..bd1a848ed7 --- /dev/null +++ b/test/js/node/test/parallel/test-process-constants-noatime.js @@ -0,0 +1,12 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const constants = require('fs').constants; + +if (common.isLinux) { + assert('O_NOATIME' in constants); + assert.strictEqual(constants.O_NOATIME, 0x40000); +} else { + assert(!('O_NOATIME' in constants)); +} diff --git a/test/js/node/test/parallel/test-process-dlopen-undefined-exports.js b/test/js/node/test/parallel/test-process-dlopen-undefined-exports.js new file mode 100644 index 0000000000..3766a73a45 --- /dev/null +++ b/test/js/node/test/parallel/test-process-dlopen-undefined-exports.js @@ -0,0 +1,10 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const someBindingPath = './test/addons/hello-world/build/Release/binding.node'; + +assert.throws(() => { + process.dlopen({ exports: undefined }, someBindingPath); +}, Error); diff --git a/test/js/node/test/parallel/test-process-domain-segfault.js b/test/js/node/test/parallel/test-process-domain-segfault.js new file mode 100644 index 0000000000..78009f4687 --- /dev/null +++ b/test/js/node/test/parallel/test-process-domain-segfault.js @@ -0,0 +1,32 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); + +// This test ensures that setting `process.domain` to `null` does not result in +// node crashing with a segfault. +// https://github.com/nodejs/node-v0.x-archive/issues/4256 + +process.domain = null; +setTimeout(function() { + console.log('this console.log statement should not make node crash'); +}, 1); diff --git a/test/js/node/test/parallel/test-process-emit.js b/test/js/node/test/parallel/test-process-emit.js new file mode 100644 index 0000000000..8c2ad675cf --- /dev/null +++ b/test/js/node/test/parallel/test-process-emit.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const sym = Symbol(); + +process.on('normal', common.mustCall((data) => { + assert.strictEqual(data, 'normalData'); +})); + +process.on(sym, common.mustCall((data) => { + assert.strictEqual(data, 'symbolData'); +})); + +process.on('SIGPIPE', common.mustCall((data) => { + assert.strictEqual(data, 'signalData'); +})); + +process.emit('normal', 'normalData'); +process.emit(sym, 'symbolData'); +process.emit('SIGPIPE', 'signalData'); + +assert.strictEqual(Number.isNaN(process._eventsCount), false); diff --git a/test/js/node/test/parallel/test-process-env-windows-error-reset.js b/test/js/node/test/parallel/test-process-env-windows-error-reset.js new file mode 100644 index 0000000000..881da06d29 --- /dev/null +++ b/test/js/node/test/parallel/test-process-env-windows-error-reset.js @@ -0,0 +1,22 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +// This checks that after accessing a missing env var, a subsequent +// env read will succeed even for empty variables. + +{ + process.env.FOO = ''; + process.env.NONEXISTENT_ENV_VAR; // eslint-disable-line no-unused-expressions + const foo = process.env.FOO; + + assert.strictEqual(foo, ''); +} + +{ + process.env.FOO = ''; + process.env.NONEXISTENT_ENV_VAR; // eslint-disable-line no-unused-expressions + const hasFoo = 'FOO' in process.env; + + assert.strictEqual(hasFoo, true); +} diff --git a/test/js/node/test/parallel/test-process-exception-capture-should-abort-on-uncaught.js b/test/js/node/test/parallel/test-process-exception-capture-should-abort-on-uncaught.js new file mode 100644 index 0000000000..f9e685a86e --- /dev/null +++ b/test/js/node/test/parallel/test-process-exception-capture-should-abort-on-uncaught.js @@ -0,0 +1,12 @@ +// Flags: --abort-on-uncaught-exception +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +assert.strictEqual(process.hasUncaughtExceptionCaptureCallback(), false); + +// This should make the process not crash even though the flag was passed. +process.setUncaughtExceptionCaptureCallback(common.mustCall((err) => { + assert.strictEqual(err.message, 'foo'); +})); +throw new Error('foo'); diff --git a/test/js/node/test/parallel/test-process-exception-capture.js b/test/js/node/test/parallel/test-process-exception-capture.js new file mode 100644 index 0000000000..c84d3459e2 --- /dev/null +++ b/test/js/node/test/parallel/test-process-exception-capture.js @@ -0,0 +1,13 @@ +// Flags: --abort-on-uncaught-exception +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +assert.strictEqual(process.hasUncaughtExceptionCaptureCallback(), false); + +// This should make the process not crash even though the flag was passed. +process.setUncaughtExceptionCaptureCallback(common.mustCall((err) => { + assert.strictEqual(err.message, 'foo'); +})); +process.on('uncaughtException', common.mustNotCall()); +throw new Error('foo'); diff --git a/test/js/node/test/parallel/test-process-execpath.js b/test/js/node/test/parallel/test-process-execpath.js new file mode 100644 index 0000000000..0fce35e264 --- /dev/null +++ b/test/js/node/test/parallel/test-process-execpath.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +if (common.isWindows) + common.skip('symlinks are weird on windows'); + +const assert = require('assert'); +const child_process = require('child_process'); +const fs = require('fs'); + +assert.strictEqual(process.execPath, fs.realpathSync(process.execPath)); + +if (process.argv[2] === 'child') { + // The console.log() output is part of the test here. + console.log(process.execPath); +} else { + const tmpdir = require('../common/tmpdir'); + tmpdir.refresh(); + + const symlinkedNode = tmpdir.resolve('symlinked-node'); + fs.symlinkSync(process.execPath, symlinkedNode); + + const proc = child_process.spawnSync(symlinkedNode, [__filename, 'child']); + assert.strictEqual(proc.stderr.toString(), ''); + assert.strictEqual(proc.stdout.toString(), `${process.execPath}\n`); + assert.strictEqual(proc.status, 0); +} diff --git a/test/js/node/test/parallel/test-process-exit-from-before-exit.js b/test/js/node/test/parallel/test-process-exit-from-before-exit.js new file mode 100644 index 0000000000..7f20c22f0b --- /dev/null +++ b/test/js/node/test/parallel/test-process-exit-from-before-exit.js @@ -0,0 +1,30 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +process.on('beforeExit', common.mustCall(function() { + setTimeout(common.mustNotCall(), 5); + process.exit(0); // Should execute immediately even if we schedule new work. + assert.fail(); +})); diff --git a/test/js/node/test/parallel/test-process-exit-handler.js b/test/js/node/test/parallel/test-process-exit-handler.js new file mode 100644 index 0000000000..d74e320fe6 --- /dev/null +++ b/test/js/node/test/parallel/test-process-exit-handler.js @@ -0,0 +1,14 @@ +'use strict'; +const common = require('../common'); + +if (!common.isMainThread) + common.skip('execArgv does not affect Workers'); + +// This test ensures that no asynchronous operations are performed in the 'exit' +// handler. +// https://github.com/nodejs/node/issues/12322 + +process.on('exit', () => { + setTimeout(() => process.abort(), 0); // Should not run. + for (const start = Date.now(); Date.now() - start < 10;); +}); diff --git a/test/js/node/test/parallel/test-process-exit-recursive.js b/test/js/node/test/parallel/test-process-exit-recursive.js new file mode 100644 index 0000000000..727aa4abe7 --- /dev/null +++ b/test/js/node/test/parallel/test-process-exit-recursive.js @@ -0,0 +1,37 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// Recursively calling .exit() should not overflow the call stack +let nexits = 0; + +process.on('exit', function(code) { + assert.strictEqual(nexits++, 0); + assert.strictEqual(code, 1); + + // Now override the exit code of 1 with 0 so that the test passes + process.exit(0); +}); + +process.exit(1); diff --git a/test/js/node/test/parallel/test-process-exit.js b/test/js/node/test/parallel/test-process-exit.js new file mode 100644 index 0000000000..cd605949af --- /dev/null +++ b/test/js/node/test/parallel/test-process-exit.js @@ -0,0 +1,35 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// Calling .exit() from within "exit" should not overflow the call stack +let nexits = 0; + +process.on('exit', function(code) { + assert.strictEqual(nexits++, 0); + assert.strictEqual(code, 0); + process.exit(); +}); + +// "exit" should be emitted unprovoked diff --git a/test/js/node/test/parallel/test-process-features.js b/test/js/node/test/parallel/test-process-features.js new file mode 100644 index 0000000000..3b4677c561 --- /dev/null +++ b/test/js/node/test/parallel/test-process-features.js @@ -0,0 +1,22 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const keys = new Set(Object.keys(process.features)); + +assert.deepStrictEqual(keys, new Set([ + 'inspector', + 'debug', + 'uv', + 'ipv6', + 'tls_alpn', + 'tls_sni', + 'tls_ocsp', + 'tls', + 'cached_builtins', +])); + +for (const key of keys) { + assert.strictEqual(typeof process.features[key], 'boolean'); +} diff --git a/test/js/node/test/parallel/test-process-getgroups.js b/test/js/node/test/parallel/test-process-getgroups.js new file mode 100644 index 0000000000..28df13205f --- /dev/null +++ b/test/js/node/test/parallel/test-process-getgroups.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +// Check `id -G` and `process.getgroups()` return same groups. + +if (common.isMacOS) + common.skip('Output of `id -G` is unreliable on Darwin.'); + +const assert = require('assert'); +const exec = require('child_process').exec; + +if (typeof process.getgroups === 'function') { + const groups = unique(process.getgroups()); + assert(Array.isArray(groups)); + assert(groups.length > 0); + exec('id -G', function(err, stdout) { + assert.ifError(err); + const real_groups = unique(stdout.match(/\d+/g).map(Number)); + assert.deepStrictEqual(groups, real_groups); + check(groups, real_groups); + check(real_groups, groups); + }); +} + +function check(a, b) { + for (let i = 0; i < a.length; ++i) assert(b.includes(a[i])); +} + +function unique(groups) { + return [...new Set(groups)].sort(); +} diff --git a/test/js/node/test/parallel/test-process-hrtime-bigint.js b/test/js/node/test/parallel/test-process-hrtime-bigint.js new file mode 100644 index 0000000000..e5ce40a994 --- /dev/null +++ b/test/js/node/test/parallel/test-process-hrtime-bigint.js @@ -0,0 +1,14 @@ +'use strict'; + +// Tests that process.hrtime.bigint() works. + +require('../common'); +const assert = require('assert'); + +const start = process.hrtime.bigint(); +assert.strictEqual(typeof start, 'bigint'); + +const end = process.hrtime.bigint(); +assert.strictEqual(typeof end, 'bigint'); + +assert(end - start >= 0n); diff --git a/test/js/node/test/parallel/test-process-kill-null.js b/test/js/node/test/parallel/test-process-kill-null.js new file mode 100644 index 0000000000..88fc677454 --- /dev/null +++ b/test/js/node/test/parallel/test-process-kill-null.js @@ -0,0 +1,42 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const { mustCall } = require('../common'); +const assert = require('assert'); +const { spawn } = require('child_process'); + +const cat = spawn('cat'); + +assert.ok(process.kill(cat.pid, 0)); + +cat.on('exit', mustCall(function() { + assert.throws(function() { + process.kill(cat.pid, 0); + }, Error); +})); + +cat.stdout.on('data', mustCall(function() { + process.kill(cat.pid, 'SIGKILL'); +})); + +// EPIPE when null sig fails +cat.stdin.write('test'); diff --git a/test/js/node/test/parallel/test-process-next-tick.js b/test/js/node/test/parallel/test-process-next-tick.js new file mode 100644 index 0000000000..66913beebf --- /dev/null +++ b/test/js/node/test/parallel/test-process-next-tick.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const N = 2; + +function cb() { + throw new Error(); +} + +for (let i = 0; i < N; ++i) { + process.nextTick(common.mustCall(cb)); +} + +process.on('uncaughtException', common.mustCall(N)); + +process.on('exit', function() { + process.removeAllListeners('uncaughtException'); +}); + +[null, 1, 'test', {}, [], Infinity, true].forEach((i) => { + assert.throws( + () => process.nextTick(i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + } + ); +}); diff --git a/test/js/node/test/parallel/test-process-ppid.js b/test/js/node/test/parallel/test-process-ppid.js new file mode 100644 index 0000000000..d78ef3a2dd --- /dev/null +++ b/test/js/node/test/parallel/test-process-ppid.js @@ -0,0 +1,16 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const cp = require('child_process'); + +if (process.argv[2] === 'child') { + // The following console.log() call is part of the test's functionality. + console.log(process.ppid); +} else { + const child = cp.spawnSync(process.execPath, [__filename, 'child']); + + assert.strictEqual(child.status, 0); + assert.strictEqual(child.signal, null); + assert.strictEqual(+child.stdout.toString().trim(), process.pid); + assert.strictEqual(child.stderr.toString().trim(), ''); +} diff --git a/test/js/node/test/parallel/test-process-remove-all-signal-listeners.js b/test/js/node/test/parallel/test-process-remove-all-signal-listeners.js new file mode 100644 index 0000000000..afd574daaa --- /dev/null +++ b/test/js/node/test/parallel/test-process-remove-all-signal-listeners.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); +if (common.isWindows) + common.skip('Win32 does not support signals.'); + +const assert = require('assert'); +const spawn = require('child_process').spawn; + +if (process.argv[2] !== '--do-test') { + // We are the primary, fork a child so we can verify it exits with correct + // status. + process.env.DOTEST = 'y'; + const child = spawn(process.execPath, [__filename, '--do-test']); + + child.once('exit', common.mustCall(function(code, signal) { + assert.strictEqual(signal, 'SIGINT'); + })); + + return; +} + +process.on('SIGINT', function() { + // Remove all handlers and kill ourselves. We should terminate by SIGINT + // now that we have no handlers. + process.removeAllListeners('SIGINT'); + process.kill(process.pid, 'SIGINT'); +}); + +// Signal handlers aren't sufficient to keep node alive, so resume stdin +process.stdin.resume(); + +// Demonstrate that signals are being handled +process.kill(process.pid, 'SIGINT'); diff --git a/test/js/node/test/parallel/test-process-setuid-io-uring.js b/test/js/node/test/parallel/test-process-setuid-io-uring.js new file mode 100644 index 0000000000..93193ac2f8 --- /dev/null +++ b/test/js/node/test/parallel/test-process-setuid-io-uring.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); + +const assert = require('node:assert'); +const { execFileSync } = require('node:child_process'); + +if (!common.isLinux) { + common.skip('test is Linux specific'); +} + +if (process.arch !== 'x64' && process.arch !== 'arm64') { + common.skip('io_uring support on this architecture is uncertain'); +} + +const kv = /^(\d+)\.(\d+)\.(\d+)/.exec(execFileSync('uname', ['-r'])).slice(1).map((n) => parseInt(n, 10)); +if (((kv[0] << 16) | (kv[1] << 8) | kv[2]) < 0x050ABA) { + common.skip('io_uring is likely buggy due to old kernel'); +} + +const userIdentitySetters = [ + ['setuid', [1000]], + ['seteuid', [1000]], + ['setgid', [1000]], + ['setegid', [1000]], + ['setgroups', [[1000]]], + ['initgroups', ['nodeuser', 1000]], +]; + +for (const [fnName, args] of userIdentitySetters) { + const call = `process.${fnName}(${args.map((a) => JSON.stringify(a)).join(', ')})`; + const code = `try { ${call}; } catch (err) { console.log(err); }`; + + const stdout = execFileSync(process.execPath, ['-e', code], { + encoding: 'utf8', + env: { ...process.env, UV_USE_IO_URING: '1' }, + }); + + const msg = new RegExp(`^Error: ${fnName}\\(\\) disabled: io_uring may be enabled\\. See CVE-[X0-9]{4}-`); + assert.match(stdout, msg); + assert.match(stdout, /code: 'ERR_INVALID_STATE'/); + + console.log(call, stdout.slice(0, stdout.indexOf('\n'))); +} diff --git a/test/js/node/test/parallel/test-process-uptime.js b/test/js/node/test/parallel/test-process-uptime.js new file mode 100644 index 0000000000..eabb6cf266 --- /dev/null +++ b/test/js/node/test/parallel/test-process-uptime.js @@ -0,0 +1,37 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +console.error(process.uptime()); +// Add some wiggle room for different platforms. +// Verify that the returned value is in seconds - +// 15 seconds should be a good estimate. +assert.ok(process.uptime() <= 15); + +const original = process.uptime(); + +setTimeout(function() { + const uptime = process.uptime(); + assert.ok(original < uptime); +}, 10); diff --git a/test/js/node/test/parallel/test-promise-handled-rejection-no-warning.js b/test/js/node/test/parallel/test-promise-handled-rejection-no-warning.js new file mode 100644 index 0000000000..8878d67fab --- /dev/null +++ b/test/js/node/test/parallel/test-promise-handled-rejection-no-warning.js @@ -0,0 +1,7 @@ +'use strict'; +const common = require('../common'); + +// This test verifies that DEP0018 does not occur when rejections are handled. +process.on('warning', common.mustNotCall()); +process.on('unhandledRejection', common.mustCall()); +Promise.reject(new Error()); diff --git a/test/js/node/test/parallel/test-promise-unhandled-issue-43655.js b/test/js/node/test/parallel/test-promise-unhandled-issue-43655.js new file mode 100644 index 0000000000..4fd2c1a711 --- /dev/null +++ b/test/js/node/test/parallel/test-promise-unhandled-issue-43655.js @@ -0,0 +1,27 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +function delay(time) { + return new Promise((resolve) => { + setTimeout(resolve, time); + }); +} + +async function test() { + for (let i = 0; i < 100000; i++) { + await new Promise((resolve, reject) => { + reject('value'); + }) + .then(() => { }, () => { }); + } + + const time0 = Date.now(); + await delay(0); + + const diff = Date.now() - time0; + assert.ok(Date.now() - time0 < 500, `Expected less than 500ms, got ${diff}ms`); +} + +test(); diff --git a/test/js/node/test/parallel/test-promise-unhandled-silent.js b/test/js/node/test/parallel/test-promise-unhandled-silent.js new file mode 100644 index 0000000000..edf5111eae --- /dev/null +++ b/test/js/node/test/parallel/test-promise-unhandled-silent.js @@ -0,0 +1,21 @@ +// Flags: --unhandled-rejections=none +'use strict'; + +const common = require('../common'); + +// Verify that ignoring unhandled rejection works fine and that no warning is +// logged. + +new Promise(() => { + throw new Error('One'); +}); + +Promise.reject('test'); + +process.on('warning', common.mustNotCall('warning')); +process.on('uncaughtException', common.mustNotCall('uncaughtException')); +process.on('rejectionHandled', common.mustNotCall('rejectionHandled')); + +process.on('unhandledRejection', common.mustCall(2)); + +setTimeout(common.mustCall(), 2); diff --git a/test/js/node/test/parallel/test-promise-unhandled-throw-handler.js b/test/js/node/test/parallel/test-promise-unhandled-throw-handler.js new file mode 100644 index 0000000000..26a1d2f85c --- /dev/null +++ b/test/js/node/test/parallel/test-promise-unhandled-throw-handler.js @@ -0,0 +1,36 @@ +// Flags: --unhandled-rejections=throw +'use strict'; + +const common = require('../common'); +const Countdown = require('../common/countdown'); +const assert = require('assert'); + +// Verify that the unhandledRejection handler prevents triggering +// uncaught exceptions + +const err1 = new Error('One'); + +const errors = [err1, null]; + +const ref = new Promise(() => { + throw err1; +}); +// Explicitly reject `null`. +Promise.reject(null); + +process.on('warning', common.mustNotCall('warning')); +process.on('rejectionHandled', common.mustNotCall('rejectionHandled')); +process.on('exit', assert.strictEqual.bind(null, 0)); +process.on('uncaughtException', common.mustNotCall('uncaughtException')); + +const timer = setTimeout(() => console.log(ref), 1000); + +const counter = new Countdown(2, () => { + clearTimeout(timer); +}); + +process.on('unhandledRejection', common.mustCall((err) => { + counter.dec(); + const knownError = errors.shift(); + assert.deepStrictEqual(err, knownError); +}, 2)); diff --git a/test/js/node/test/parallel/test-querystring-maxKeys-non-finite.js b/test/js/node/test/parallel/test-querystring-maxKeys-non-finite.js new file mode 100644 index 0000000000..610c30c7a3 --- /dev/null +++ b/test/js/node/test/parallel/test-querystring-maxKeys-non-finite.js @@ -0,0 +1,58 @@ +'use strict'; +// This test was originally written to test a regression +// that was introduced by +// https://github.com/nodejs/node/pull/2288#issuecomment-179543894 +require('../common'); + +const assert = require('assert'); +const parse = require('querystring').parse; + +// Taken from express-js/body-parser +// https://github.com/expressjs/body-parser/blob/ed25264fb494cf0c8bc992b8257092cd4f694d5e/test/urlencoded.js#L636-L651 +function createManyParams(count) { + let str = ''; + + if (count === 0) { + return str; + } + + str += '0=0'; + + for (let i = 1; i < count; i++) { + const n = i.toString(36); + str += `&${n}=${n}`; + } + + return str; +} + +const count = 10000; +const originalMaxLength = 1000; +const params = createManyParams(count); + +// thealphanerd +// 27def4f introduced a change to parse that would cause Infinity +// to be passed to String.prototype.split as an argument for limit +// In this instance split will always return an empty array +// this test confirms that the output of parse is the expected length +// when passed Infinity as the argument for maxKeys +const resultInfinity = parse(params, undefined, undefined, { + maxKeys: Infinity +}); +const resultNaN = parse(params, undefined, undefined, { + maxKeys: NaN +}); +const resultInfinityString = parse(params, undefined, undefined, { + maxKeys: 'Infinity' +}); +const resultNaNString = parse(params, undefined, undefined, { + maxKeys: 'NaN' +}); + +// Non Finite maxKeys should return the length of input +assert.strictEqual(Object.keys(resultInfinity).length, count); +assert.strictEqual(Object.keys(resultNaN).length, count); +// Strings maxKeys should return the maxLength +// defined by parses internals +assert.strictEqual(Object.keys(resultInfinityString).length, originalMaxLength); +assert.strictEqual(Object.keys(resultNaNString).length, originalMaxLength); diff --git a/test/js/node/test/parallel/test-querystring-multichar-separator.js b/test/js/node/test/parallel/test-querystring-multichar-separator.js new file mode 100644 index 0000000000..720733b1e2 --- /dev/null +++ b/test/js/node/test/parallel/test-querystring-multichar-separator.js @@ -0,0 +1,25 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const qs = require('querystring'); + +function check(actual, expected) { + assert(!(actual instanceof Object)); + assert.deepStrictEqual(Object.keys(actual).sort(), + Object.keys(expected).sort()); + Object.keys(expected).forEach(function(key) { + assert.deepStrictEqual(actual[key], expected[key]); + }); +} + +check(qs.parse('foo=>bar&&bar=>baz', '&&', '=>'), + { foo: 'bar', bar: 'baz' }); + +check(qs.stringify({ foo: 'bar', bar: 'baz' }, '&&', '=>'), + 'foo=>bar&&bar=>baz'); + +check(qs.parse('foo==>bar, bar==>baz', ', ', '==>'), + { foo: 'bar', bar: 'baz' }); + +check(qs.stringify({ foo: 'bar', bar: 'baz' }, ', ', '==>'), + 'foo==>bar, bar==>baz'); diff --git a/test/js/node/test/parallel/test-queue-microtask-uncaught-asynchooks.js b/test/js/node/test/parallel/test-queue-microtask-uncaught-asynchooks.js new file mode 100644 index 0000000000..35b3d9fa30 --- /dev/null +++ b/test/js/node/test/parallel/test-queue-microtask-uncaught-asynchooks.js @@ -0,0 +1,36 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const async_hooks = require('async_hooks'); + +// Regression test for https://github.com/nodejs/node/issues/30080: +// An uncaught exception inside a queueMicrotask callback should not lead +// to multiple after() calls for it. + +let µtaskId; +const events = []; + +async_hooks.createHook({ + init(id, type, triggerId, resource) { + if (type === 'Microtask') { + µtaskId = id; + events.push('init'); + } + }, + before(id) { + if (id === µtaskId) events.push('before'); + }, + after(id) { + if (id === µtaskId) events.push('after'); + }, + destroy(id) { + if (id === µtaskId) events.push('destroy'); + } +}).enable(); + +queueMicrotask(() => { throw new Error(); }); + +process.on('uncaughtException', common.mustCall()); +process.on('exit', () => { + assert.deepStrictEqual(events, ['init', 'after', 'before', 'destroy']); +}); diff --git a/test/js/node/test/parallel/test-quic-internal-endpoint-listen-defaults.js b/test/js/node/test/parallel/test-quic-internal-endpoint-listen-defaults.js new file mode 100644 index 0000000000..9fb9f9461c --- /dev/null +++ b/test/js/node/test/parallel/test-quic-internal-endpoint-listen-defaults.js @@ -0,0 +1,76 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const { internalBinding } = require('internal/test/binding'); +const { + ok, + strictEqual, + deepStrictEqual, +} = require('node:assert'); + +const { + SocketAddress: _SocketAddress, + AF_INET, +} = internalBinding('block_list'); +const quic = internalBinding('quic'); + +quic.setCallbacks({ + onEndpointClose: common.mustCall((...args) => { + deepStrictEqual(args, [0, 0]); + }), + + // The following are unused in this test + onSessionNew() {}, + onSessionClose() {}, + onSessionDatagram() {}, + onSessionDatagramStatus() {}, + onSessionHandshake() {}, + onSessionPathValidation() {}, + onSessionTicket() {}, + onSessionVersionNegotiation() {}, + onStreamCreated() {}, + onStreamBlocked() {}, + onStreamClose() {}, + onStreamReset() {}, + onStreamHeaders() {}, + onStreamTrailers() {}, +}); + +const endpoint = new quic.Endpoint({}); + +const state = new DataView(endpoint.state); +ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_LISTENING)); +ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_RECEIVING)); +ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_BOUND)); +strictEqual(endpoint.address(), undefined); + +endpoint.listen({}); + +ok(state.getUint8(quic.IDX_STATE_ENDPOINT_LISTENING)); +ok(state.getUint8(quic.IDX_STATE_ENDPOINT_RECEIVING)); +ok(state.getUint8(quic.IDX_STATE_ENDPOINT_BOUND)); +const address = endpoint.address(); +ok(address instanceof _SocketAddress); + +const detail = address.detail({ + address: undefined, + port: undefined, + family: undefined, + flowlabel: undefined, +}); + +strictEqual(detail.address, '127.0.0.1'); +strictEqual(detail.family, AF_INET); +strictEqual(detail.flowlabel, 0); +ok(detail.port !== 0); + +endpoint.closeGracefully(); + +ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_LISTENING)); +ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_RECEIVING)); +ok(!state.getUint8(quic.IDX_STATE_ENDPOINT_BOUND)); +strictEqual(endpoint.address(), undefined); diff --git a/test/js/node/test/parallel/test-quic-internal-endpoint-options.js b/test/js/node/test/parallel/test-quic-internal-endpoint-options.js new file mode 100644 index 0000000000..672fac18b4 --- /dev/null +++ b/test/js/node/test/parallel/test-quic-internal-endpoint-options.js @@ -0,0 +1,215 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); +const { + throws, +} = require('node:assert'); + +const { internalBinding } = require('internal/test/binding'); +const quic = internalBinding('quic'); + +quic.setCallbacks({ + onEndpointClose() {}, + onSessionNew() {}, + onSessionClose() {}, + onSessionDatagram() {}, + onSessionDatagramStatus() {}, + onSessionHandshake() {}, + onSessionPathValidation() {}, + onSessionTicket() {}, + onSessionVersionNegotiation() {}, + onStreamCreated() {}, + onStreamBlocked() {}, + onStreamClose() {}, + onStreamReset() {}, + onStreamHeaders() {}, + onStreamTrailers() {}, +}); + +throws(() => new quic.Endpoint(), { + code: 'ERR_INVALID_ARG_TYPE', + message: 'options must be an object' +}); + +throws(() => new quic.Endpoint('a'), { + code: 'ERR_INVALID_ARG_TYPE', + message: 'options must be an object' +}); + +throws(() => new quic.Endpoint(null), { + code: 'ERR_INVALID_ARG_TYPE', + message: 'options must be an object' +}); + +throws(() => new quic.Endpoint(false), { + code: 'ERR_INVALID_ARG_TYPE', + message: 'options must be an object' +}); + +{ + // Just Works... using all defaults + new quic.Endpoint({}); +} + +const cases = [ + { + key: 'retryTokenExpiration', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'tokenExpiration', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'maxConnectionsPerHost', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'maxConnectionsTotal', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'maxStatelessResetsPerHost', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'addressLRUSize', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'maxRetries', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'maxPayloadSize', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'unacknowledgedPacketThreshold', + valid: [ + 1, 10, 100, 1000, 10000, 10000n, + ], + invalid: [-1, -1n, 'a', null, false, true, {}, [], () => {}] + }, + { + key: 'validateAddress', + valid: [true, false, 0, 1, 'a'], + invalid: [], + }, + { + key: 'disableStatelessReset', + valid: [true, false, 0, 1, 'a'], + invalid: [], + }, + { + key: 'ipv6Only', + valid: [true, false, 0, 1, 'a'], + invalid: [], + }, + { + key: 'cc', + valid: [ + quic.CC_ALGO_RENO, + quic.CC_ALGO_CUBIC, + quic.CC_ALGO_BBR, + quic.CC_ALGO_BBR2, + quic.CC_ALGO_RENO_STR, + quic.CC_ALGO_CUBIC_STR, + quic.CC_ALGO_BBR_STR, + quic.CC_ALGO_BBR2_STR, + ], + invalid: [-1, 4, 1n, 'a', null, false, true, {}, [], () => {}], + }, + { + key: 'udpReceiveBufferSize', + valid: [0, 1, 2, 3, 4, 1000], + invalid: [-1, 'a', null, false, true, {}, [], () => {}], + }, + { + key: 'udpSendBufferSize', + valid: [0, 1, 2, 3, 4, 1000], + invalid: [-1, 'a', null, false, true, {}, [], () => {}], + }, + { + key: 'udpTTL', + valid: [0, 1, 2, 3, 4, 255], + invalid: [-1, 256, 'a', null, false, true, {}, [], () => {}], + }, + { + key: 'resetTokenSecret', + valid: [ + new Uint8Array(16), + new Uint16Array(8), + new Uint32Array(4), + ], + invalid: [ + 'a', null, false, true, {}, [], () => {}, + new Uint8Array(15), + new Uint8Array(17), + new ArrayBuffer(16), + ], + }, + { + key: 'tokenSecret', + valid: [ + new Uint8Array(16), + new Uint16Array(8), + new Uint32Array(4), + ], + invalid: [ + 'a', null, false, true, {}, [], () => {}, + new Uint8Array(15), + new Uint8Array(17), + new ArrayBuffer(16), + ], + }, + { + // Unknown options are ignored entirely for any value type + key: 'ignored', + valid: ['a', null, false, true, {}, [], () => {}], + invalid: [], + }, +]; + +for (const { key, valid, invalid } of cases) { + for (const value of valid) { + const options = {}; + options[key] = value; + new quic.Endpoint(options); + } + + for (const value of invalid) { + const options = {}; + options[key] = value; + throws(() => new quic.Endpoint(options), { + code: 'ERR_INVALID_ARG_VALUE', + }); + } +} diff --git a/test/js/node/test/parallel/test-quic-internal-endpoint-stats-state.js b/test/js/node/test/parallel/test-quic-internal-endpoint-stats-state.js new file mode 100644 index 0000000000..566dd675d7 --- /dev/null +++ b/test/js/node/test/parallel/test-quic-internal-endpoint-stats-state.js @@ -0,0 +1,79 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); +const { + strictEqual, +} = require('node:assert'); + +const { internalBinding } = require('internal/test/binding'); +const quic = internalBinding('quic'); + +const { + IDX_STATS_ENDPOINT_CREATED_AT, + IDX_STATS_ENDPOINT_DESTROYED_AT, + IDX_STATS_ENDPOINT_BYTES_RECEIVED, + IDX_STATS_ENDPOINT_BYTES_SENT, + IDX_STATS_ENDPOINT_PACKETS_RECEIVED, + IDX_STATS_ENDPOINT_PACKETS_SENT, + IDX_STATS_ENDPOINT_SERVER_SESSIONS, + IDX_STATS_ENDPOINT_CLIENT_SESSIONS, + IDX_STATS_ENDPOINT_SERVER_BUSY_COUNT, + IDX_STATS_ENDPOINT_RETRY_COUNT, + IDX_STATS_ENDPOINT_VERSION_NEGOTIATION_COUNT, + IDX_STATS_ENDPOINT_STATELESS_RESET_COUNT, + IDX_STATS_ENDPOINT_IMMEDIATE_CLOSE_COUNT, + IDX_STATS_ENDPOINT_COUNT, + IDX_STATE_ENDPOINT_BOUND, + IDX_STATE_ENDPOINT_BOUND_SIZE, + IDX_STATE_ENDPOINT_RECEIVING, + IDX_STATE_ENDPOINT_RECEIVING_SIZE, + IDX_STATE_ENDPOINT_LISTENING, + IDX_STATE_ENDPOINT_LISTENING_SIZE, + IDX_STATE_ENDPOINT_CLOSING, + IDX_STATE_ENDPOINT_CLOSING_SIZE, + IDX_STATE_ENDPOINT_BUSY, + IDX_STATE_ENDPOINT_BUSY_SIZE, + IDX_STATE_ENDPOINT_PENDING_CALLBACKS, + IDX_STATE_ENDPOINT_PENDING_CALLBACKS_SIZE, +} = quic; + +const endpoint = new quic.Endpoint({}); + +const state = new DataView(endpoint.state); +strictEqual(IDX_STATE_ENDPOINT_BOUND_SIZE, 1); +strictEqual(IDX_STATE_ENDPOINT_RECEIVING_SIZE, 1); +strictEqual(IDX_STATE_ENDPOINT_LISTENING_SIZE, 1); +strictEqual(IDX_STATE_ENDPOINT_CLOSING_SIZE, 1); +strictEqual(IDX_STATE_ENDPOINT_BUSY_SIZE, 1); +strictEqual(IDX_STATE_ENDPOINT_PENDING_CALLBACKS_SIZE, 8); + +strictEqual(state.getUint8(IDX_STATE_ENDPOINT_BOUND), 0); +strictEqual(state.getUint8(IDX_STATE_ENDPOINT_RECEIVING), 0); +strictEqual(state.getUint8(IDX_STATE_ENDPOINT_LISTENING), 0); +strictEqual(state.getUint8(IDX_STATE_ENDPOINT_CLOSING), 0); +strictEqual(state.getUint8(IDX_STATE_ENDPOINT_BUSY), 0); +strictEqual(state.getBigUint64(IDX_STATE_ENDPOINT_PENDING_CALLBACKS), 0n); + +endpoint.markBusy(true); +strictEqual(state.getUint8(IDX_STATE_ENDPOINT_BUSY), 1); +endpoint.markBusy(false); +strictEqual(state.getUint8(IDX_STATE_ENDPOINT_BUSY), 0); + +const stats = new BigUint64Array(endpoint.stats); +strictEqual(stats[IDX_STATS_ENDPOINT_CREATED_AT], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_DESTROYED_AT], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_BYTES_RECEIVED], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_BYTES_SENT], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_PACKETS_RECEIVED], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_PACKETS_SENT], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_SERVER_SESSIONS], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_CLIENT_SESSIONS], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_SERVER_BUSY_COUNT], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_RETRY_COUNT], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_VERSION_NEGOTIATION_COUNT], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_STATELESS_RESET_COUNT], 0n); +strictEqual(stats[IDX_STATS_ENDPOINT_IMMEDIATE_CLOSE_COUNT], 0n); +strictEqual(IDX_STATS_ENDPOINT_COUNT, 13); diff --git a/test/js/node/test/parallel/test-quic-internal-setcallbacks.js b/test/js/node/test/parallel/test-quic-internal-setcallbacks.js new file mode 100644 index 0000000000..881e9161ca --- /dev/null +++ b/test/js/node/test/parallel/test-quic-internal-setcallbacks.js @@ -0,0 +1,38 @@ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); +const { internalBinding } = require('internal/test/binding'); +const quic = internalBinding('quic'); + +const { throws } = require('assert'); + +const callbacks = { + onEndpointClose() {}, + onSessionNew() {}, + onSessionClose() {}, + onSessionDatagram() {}, + onSessionDatagramStatus() {}, + onSessionHandshake() {}, + onSessionPathValidation() {}, + onSessionTicket() {}, + onSessionVersionNegotiation() {}, + onStreamCreated() {}, + onStreamBlocked() {}, + onStreamClose() {}, + onStreamReset() {}, + onStreamHeaders() {}, + onStreamTrailers() {}, +}; +// Fail if any callback is missing +for (const fn of Object.keys(callbacks)) { + // eslint-disable-next-line no-unused-vars + const { [fn]: _, ...rest } = callbacks; + throws(() => quic.setCallbacks(rest), { + code: 'ERR_MISSING_ARGS', + }); +} +// If all callbacks are present it should work +quic.setCallbacks(callbacks); diff --git a/test/js/node/test/parallel/test-readable-from-iterator-closing.js b/test/js/node/test/parallel/test-readable-from-iterator-closing.js new file mode 100644 index 0000000000..02252ffe56 --- /dev/null +++ b/test/js/node/test/parallel/test-readable-from-iterator-closing.js @@ -0,0 +1,197 @@ +'use strict'; + +const { mustCall, mustNotCall } = require('../common'); +const { Readable } = require('stream'); +const { strictEqual } = require('assert'); + +async function asyncSupport() { + const finallyMustCall = mustCall(); + const bodyMustCall = mustCall(); + + async function* infiniteGenerate() { + try { + while (true) yield 'a'; + } finally { + finallyMustCall(); + } + } + + const stream = Readable.from(infiniteGenerate()); + + for await (const chunk of stream) { + bodyMustCall(); + strictEqual(chunk, 'a'); + break; + } +} + +async function syncSupport() { + const finallyMustCall = mustCall(); + const bodyMustCall = mustCall(); + + function* infiniteGenerate() { + try { + while (true) yield 'a'; + } finally { + finallyMustCall(); + } + } + + const stream = Readable.from(infiniteGenerate()); + + for await (const chunk of stream) { + bodyMustCall(); + strictEqual(chunk, 'a'); + break; + } +} + +async function syncPromiseSupport() { + const returnMustBeAwaited = mustCall(); + const bodyMustCall = mustCall(); + + function* infiniteGenerate() { + try { + while (true) yield Promise.resolve('a'); + } finally { + // eslint-disable-next-line no-unsafe-finally + return { then(cb) { + returnMustBeAwaited(); + cb(); + } }; + } + } + + const stream = Readable.from(infiniteGenerate()); + + for await (const chunk of stream) { + bodyMustCall(); + strictEqual(chunk, 'a'); + break; + } +} + +async function syncRejectedSupport() { + const returnMustBeAwaited = mustCall(); + const bodyMustNotCall = mustNotCall(); + const catchMustCall = mustCall(); + const secondNextMustNotCall = mustNotCall(); + + function* generate() { + try { + yield Promise.reject('a'); + secondNextMustNotCall(); + } finally { + // eslint-disable-next-line no-unsafe-finally + return { then(cb) { + returnMustBeAwaited(); + cb(); + } }; + } + } + + const stream = Readable.from(generate()); + + try { + for await (const chunk of stream) { + bodyMustNotCall(chunk); + } + } catch { + catchMustCall(); + } +} + +async function noReturnAfterThrow() { + const returnMustNotCall = mustNotCall(); + const bodyMustNotCall = mustNotCall(); + const catchMustCall = mustCall(); + const nextMustCall = mustCall(); + + const stream = Readable.from({ + [Symbol.asyncIterator]() { return this; }, + async next() { + nextMustCall(); + throw new Error('a'); + }, + async return() { + returnMustNotCall(); + return { done: true }; + }, + }); + + try { + for await (const chunk of stream) { + bodyMustNotCall(chunk); + } + } catch { + catchMustCall(); + } +} + +async function closeStreamWhileNextIsPending() { + const finallyMustCall = mustCall(); + const dataMustCall = mustCall(); + + let resolveDestroy; + const destroyed = + new Promise((resolve) => { resolveDestroy = mustCall(resolve); }); + let resolveYielded; + const yielded = + new Promise((resolve) => { resolveYielded = mustCall(resolve); }); + + async function* infiniteGenerate() { + try { + while (true) { + yield 'a'; + resolveYielded(); + await destroyed; + } + } finally { + finallyMustCall(); + } + } + + const stream = Readable.from(infiniteGenerate()); + + stream.on('data', (data) => { + dataMustCall(); + strictEqual(data, 'a'); + }); + + yielded.then(() => { + stream.destroy(); + resolveDestroy(); + }); +} + +async function closeAfterNullYielded() { + const finallyMustCall = mustCall(); + const dataMustCall = mustCall(3); + + function* generate() { + try { + yield 'a'; + yield 'a'; + yield 'a'; + } finally { + finallyMustCall(); + } + } + + const stream = Readable.from(generate()); + + stream.on('data', (chunk) => { + dataMustCall(); + strictEqual(chunk, 'a'); + }); +} + +Promise.all([ + asyncSupport(), + syncSupport(), + syncPromiseSupport(), + syncRejectedSupport(), + noReturnAfterThrow(), + closeStreamWhileNextIsPending(), + closeAfterNullYielded(), +]).then(mustCall()); diff --git a/test/js/node/test/parallel/test-readable-from.js b/test/js/node/test/parallel/test-readable-from.js new file mode 100644 index 0000000000..b844574dc9 --- /dev/null +++ b/test/js/node/test/parallel/test-readable-from.js @@ -0,0 +1,223 @@ +'use strict'; + +const { mustCall } = require('../common'); +const { once } = require('events'); +const { Readable } = require('stream'); +const { strictEqual, throws } = require('assert'); +const common = require('../common'); + +{ + throws(() => { + Readable.from(null); + }, /ERR_INVALID_ARG_TYPE/); +} + +async function toReadableBasicSupport() { + async function* generate() { + yield 'a'; + yield 'b'; + yield 'c'; + } + + const stream = Readable.from(generate()); + + const expected = ['a', 'b', 'c']; + + for await (const chunk of stream) { + strictEqual(chunk, expected.shift()); + } +} + +async function toReadableSyncIterator() { + function* generate() { + yield 'a'; + yield 'b'; + yield 'c'; + } + + const stream = Readable.from(generate()); + + const expected = ['a', 'b', 'c']; + + for await (const chunk of stream) { + strictEqual(chunk, expected.shift()); + } +} + +async function toReadablePromises() { + const promises = [ + Promise.resolve('a'), + Promise.resolve('b'), + Promise.resolve('c'), + ]; + + const stream = Readable.from(promises); + + const expected = ['a', 'b', 'c']; + + for await (const chunk of stream) { + strictEqual(chunk, expected.shift()); + } +} + +async function toReadableString() { + const stream = Readable.from('abc'); + + const expected = ['abc']; + + for await (const chunk of stream) { + strictEqual(chunk, expected.shift()); + } +} + +async function toReadableBuffer() { + const stream = Readable.from(Buffer.from('abc')); + + const expected = ['abc']; + + for await (const chunk of stream) { + strictEqual(chunk.toString(), expected.shift()); + } +} + +async function toReadableOnData() { + async function* generate() { + yield 'a'; + yield 'b'; + yield 'c'; + } + + const stream = Readable.from(generate()); + + let iterations = 0; + const expected = ['a', 'b', 'c']; + + stream.on('data', (chunk) => { + iterations++; + strictEqual(chunk, expected.shift()); + }); + + await once(stream, 'end'); + + strictEqual(iterations, 3); +} + +async function toReadableOnDataNonObject() { + async function* generate() { + yield 'a'; + yield 'b'; + yield 'c'; + } + + const stream = Readable.from(generate(), { objectMode: false }); + + let iterations = 0; + const expected = ['a', 'b', 'c']; + + stream.on('data', (chunk) => { + iterations++; + strictEqual(chunk instanceof Buffer, true); + strictEqual(chunk.toString(), expected.shift()); + }); + + await once(stream, 'end'); + + strictEqual(iterations, 3); +} + +async function destroysTheStreamWhenThrowing() { + async function* generate() { // eslint-disable-line require-yield + throw new Error('kaboom'); + } + + const stream = Readable.from(generate()); + + stream.read(); + + const [err] = await once(stream, 'error'); + strictEqual(err.message, 'kaboom'); + strictEqual(stream.destroyed, true); + +} + +async function asTransformStream() { + async function* generate(stream) { + for await (const chunk of stream) { + yield chunk.toUpperCase(); + } + } + + const source = new Readable({ + objectMode: true, + read() { + this.push('a'); + this.push('b'); + this.push('c'); + this.push(null); + } + }); + + const stream = Readable.from(generate(source)); + + const expected = ['A', 'B', 'C']; + + for await (const chunk of stream) { + strictEqual(chunk, expected.shift()); + } +} + +async function endWithError() { + async function* generate() { + yield 1; + yield 2; + yield Promise.reject('Boum'); + } + + const stream = Readable.from(generate()); + + const expected = [1, 2]; + + try { + for await (const chunk of stream) { + strictEqual(chunk, expected.shift()); + } + throw new Error(); + } catch (err) { + strictEqual(expected.length, 0); + strictEqual(err, 'Boum'); + } +} + +async function destroyingStreamWithErrorThrowsInGenerator() { + const validateError = common.mustCall((e) => { + strictEqual(e, 'Boum'); + }); + async function* generate() { + try { + yield 1; + yield 2; + yield 3; + throw new Error(); + } catch (e) { + validateError(e); + } + } + const stream = Readable.from(generate()); + stream.read(); + stream.once('error', common.mustCall()); + stream.destroy('Boum'); +} + +Promise.all([ + toReadableBasicSupport(), + toReadableSyncIterator(), + toReadablePromises(), + toReadableString(), + toReadableBuffer(), + toReadableOnData(), + toReadableOnDataNonObject(), + destroysTheStreamWhenThrowing(), + asTransformStream(), + endWithError(), + destroyingStreamWithErrorThrowsInGenerator(), +]).then(mustCall()); diff --git a/test/js/node/test/parallel/test-readable-large-hwm.js b/test/js/node/test/parallel/test-readable-large-hwm.js new file mode 100644 index 0000000000..d5bf25bc0e --- /dev/null +++ b/test/js/node/test/parallel/test-readable-large-hwm.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); +const { Readable } = require('stream'); + +// Make sure that readable completes +// even when reading larger buffer. +const bufferSize = 10 * 1024 * 1024; +let n = 0; +const r = new Readable({ + read() { + // Try to fill readable buffer piece by piece. + r.push(Buffer.alloc(bufferSize / 10)); + + if (n++ > 10) { + r.push(null); + } + } +}); + +r.on('readable', () => { + while (true) { + const ret = r.read(bufferSize); + if (ret === null) + break; + } +}); +r.on('end', common.mustCall()); diff --git a/test/js/node/test/parallel/test-readable-single-end.js b/test/js/node/test/parallel/test-readable-single-end.js new file mode 100644 index 0000000000..0969d49aa4 --- /dev/null +++ b/test/js/node/test/parallel/test-readable-single-end.js @@ -0,0 +1,16 @@ +'use strict'; + +const common = require('../common'); +const { Readable } = require('stream'); + +// This test ensures that there will not be an additional empty 'readable' +// event when stream has ended (only 1 event signalling about end) + +const r = new Readable({ + read: () => {}, +}); + +r.push(null); + +r.on('readable', common.mustCall()); +r.on('end', common.mustCall()); diff --git a/test/js/node/test/parallel/test-readline-async-iterators-destroy.js b/test/js/node/test/parallel/test-readline-async-iterators-destroy.js new file mode 100644 index 0000000000..0a3fb01890 --- /dev/null +++ b/test/js/node/test/parallel/test-readline-async-iterators-destroy.js @@ -0,0 +1,89 @@ +'use strict'; + +const common = require('../common'); +const fs = require('fs'); +const { once } = require('events'); +const readline = require('readline'); +const assert = require('assert'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const filename = tmpdir.resolve('test.txt'); + +const testContents = [ + '', + '\n', + 'line 1', + 'line 1\nline 2 南越国是前203年至前111年存在于岭南地区的一个国家\nline 3\ntrailing', + 'line 1\nline 2\nline 3 ends with newline\n', +]; + +async function testSimpleDestroy() { + for (const fileContent of testContents) { + fs.writeFileSync(filename, fileContent); + + const readable = fs.createReadStream(filename); + const rli = readline.createInterface({ + input: readable, + crlfDelay: Infinity + }); + + const iteratedLines = []; + for await (const k of rli) { + iteratedLines.push(k); + break; + } + + const expectedLines = fileContent.split('\n'); + if (expectedLines[expectedLines.length - 1] === '') { + expectedLines.pop(); + } + expectedLines.splice(1); + + assert.deepStrictEqual(iteratedLines, expectedLines); + + rli.close(); + readable.destroy(); + + await once(readable, 'close'); + } +} + +async function testMutualDestroy() { + for (const fileContent of testContents) { + fs.writeFileSync(filename, fileContent); + + const readable = fs.createReadStream(filename); + const rli = readline.createInterface({ + input: readable, + crlfDelay: Infinity + }); + + const expectedLines = fileContent.split('\n'); + if (expectedLines[expectedLines.length - 1] === '') { + expectedLines.pop(); + } + expectedLines.splice(2); + + const iteratedLines = []; + for await (const k of rli) { + iteratedLines.push(k); + for await (const l of rli) { + iteratedLines.push(l); + break; + } + assert.deepStrictEqual(iteratedLines, expectedLines); + break; + } + + assert.deepStrictEqual(iteratedLines, expectedLines); + + rli.close(); + readable.destroy(); + + await once(readable, 'close'); + } +} + +testSimpleDestroy().then(testMutualDestroy).then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-readline-async-iterators.js b/test/js/node/test/parallel/test-readline-async-iterators.js new file mode 100644 index 0000000000..32fa32a128 --- /dev/null +++ b/test/js/node/test/parallel/test-readline-async-iterators.js @@ -0,0 +1,120 @@ +'use strict'; + +const common = require('../common'); +const fs = require('fs'); +const readline = require('readline'); +const { Readable } = require('stream'); +const assert = require('assert'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const filename = tmpdir.resolve('test.txt'); + +const testContents = [ + '', + '\n', + 'line 1', + 'line 1\nline 2 南越国是前203年至前111年存在于岭南地区的一个国家\nline 3\ntrailing', + 'line 1\nline 2\nline 3 ends with newline\n', +]; + +async function testSimple() { + for (const fileContent of testContents) { + fs.writeFileSync(filename, fileContent); + + const readable = fs.createReadStream(filename); + const rli = readline.createInterface({ + input: readable, + crlfDelay: Infinity + }); + + const iteratedLines = []; + for await (const k of rli) { + iteratedLines.push(k); + } + + const expectedLines = fileContent.split('\n'); + if (expectedLines[expectedLines.length - 1] === '') { + expectedLines.pop(); + } + assert.deepStrictEqual(iteratedLines, expectedLines); + assert.strictEqual(iteratedLines.join(''), fileContent.replace(/\n/g, '')); + } +} + +async function testMutual() { + for (const fileContent of testContents) { + fs.writeFileSync(filename, fileContent); + + const readable = fs.createReadStream(filename); + const rli = readline.createInterface({ + input: readable, + crlfDelay: Infinity + }); + + const expectedLines = fileContent.split('\n'); + if (expectedLines[expectedLines.length - 1] === '') { + expectedLines.pop(); + } + const iteratedLines = []; + let iterated = false; + for await (const k of rli) { + // This outer loop should only iterate once. + assert.strictEqual(iterated, false); + iterated = true; + iteratedLines.push(k); + for await (const l of rli) { + iteratedLines.push(l); + } + assert.deepStrictEqual(iteratedLines, expectedLines); + } + assert.deepStrictEqual(iteratedLines, expectedLines); + } +} + +async function testSlowStreamForLeaks() { + const message = 'a\nb\nc\n'; + const DELAY = 1; + const REPETITIONS = 100; + const warningCallback = common.mustNotCall(); + process.on('warning', warningCallback); + + function getStream() { + const readable = Readable({ + objectMode: true, + }); + readable._read = () => {}; + let i = REPETITIONS; + function schedule() { + setTimeout(() => { + i--; + if (i < 0) { + readable.push(null); + } else { + readable.push(message); + schedule(); + } + }, DELAY); + } + schedule(); + return readable; + } + const iterable = readline.createInterface({ + input: getStream(), + }); + + let lines = 0; + // eslint-disable-next-line no-unused-vars + for await (const _ of iterable) { + lines++; + } + + assert.strictEqual(lines, 3 * REPETITIONS); + process.off('warning', warningCallback); +} + +testSimple() + .then(testMutual) + .then(testSlowStreamForLeaks) + .then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-readline-emit-keypress-events.js b/test/js/node/test/parallel/test-readline-emit-keypress-events.js new file mode 100644 index 0000000000..a9ffd3276c --- /dev/null +++ b/test/js/node/test/parallel/test-readline-emit-keypress-events.js @@ -0,0 +1,72 @@ +'use strict'; +// emitKeypressEvents is thoroughly tested in test-readline-keys.js. +// However, that test calls it implicitly. This is just a quick sanity check +// to verify that it works when called explicitly. + +require('../common'); +const assert = require('assert'); +const readline = require('readline'); +const PassThrough = require('stream').PassThrough; + +const expectedSequence = ['f', 'o', 'o']; +const expectedKeys = [ + { sequence: 'f', name: 'f', ctrl: false, meta: false, shift: false }, + { sequence: 'o', name: 'o', ctrl: false, meta: false, shift: false }, + { sequence: 'o', name: 'o', ctrl: false, meta: false, shift: false }, +]; + +{ + const stream = new PassThrough(); + const sequence = []; + const keys = []; + + readline.emitKeypressEvents(stream); + stream.on('keypress', (s, k) => { + sequence.push(s); + keys.push(k); + }); + stream.write('foo'); + + assert.deepStrictEqual(sequence, expectedSequence); + assert.deepStrictEqual(keys, expectedKeys); +} + +{ + const stream = new PassThrough(); + const sequence = []; + const keys = []; + + stream.on('keypress', (s, k) => { + sequence.push(s); + keys.push(k); + }); + readline.emitKeypressEvents(stream); + stream.write('foo'); + + assert.deepStrictEqual(sequence, expectedSequence); + assert.deepStrictEqual(keys, expectedKeys); +} + +{ + const stream = new PassThrough(); + const sequence = []; + const keys = []; + const keypressListener = (s, k) => { + sequence.push(s); + keys.push(k); + }; + + stream.on('keypress', keypressListener); + readline.emitKeypressEvents(stream); + stream.removeListener('keypress', keypressListener); + stream.write('foo'); + + assert.deepStrictEqual(sequence, []); + assert.deepStrictEqual(keys, []); + + stream.on('keypress', keypressListener); + stream.write('foo'); + + assert.deepStrictEqual(sequence, expectedSequence); + assert.deepStrictEqual(keys, expectedKeys); +} diff --git a/test/js/node/test/parallel/test-readline-interface-escapecodetimeout.js b/test/js/node/test/parallel/test-readline-interface-escapecodetimeout.js new file mode 100644 index 0000000000..a0c0e77cb8 --- /dev/null +++ b/test/js/node/test/parallel/test-readline-interface-escapecodetimeout.js @@ -0,0 +1,46 @@ +'use strict'; +require('../common'); + +// This test ensures that the escapeCodeTimeout option set correctly + +const assert = require('assert'); +const readline = require('readline'); +const EventEmitter = require('events').EventEmitter; + +class FakeInput extends EventEmitter { + resume() {} + pause() {} + write() {} + end() {} +} + +{ + const fi = new FakeInput(); + const rli = new readline.Interface({ + input: fi, + output: fi, + escapeCodeTimeout: 50 + }); + assert.strictEqual(rli.escapeCodeTimeout, 50); + rli.close(); +} + +[ + null, + {}, + NaN, + '50', +].forEach((invalidInput) => { + assert.throws(() => { + const fi = new FakeInput(); + const rli = new readline.Interface({ + input: fi, + output: fi, + escapeCodeTimeout: invalidInput + }); + rli.close(); + }, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_VALUE' + }); +}); diff --git a/test/js/node/test/parallel/test-readline-position.js b/test/js/node/test/parallel/test-readline-position.js new file mode 100644 index 0000000000..3603a42ece --- /dev/null +++ b/test/js/node/test/parallel/test-readline-position.js @@ -0,0 +1,36 @@ +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +const { PassThrough } = require('stream'); +const readline = require('readline'); +const assert = require('assert'); + +const ctrlU = { ctrl: true, name: 'u' }; + +common.skipIfDumbTerminal(); + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input, + prompt: '' + }); + + const tests = [ + [1, 'a'], + [2, 'ab'], + [2, '丁'], + [0, '\u0301'], // COMBINING ACUTE ACCENT + [1, 'a\u0301'], // á + [0, '\u20DD'], // COMBINING ENCLOSING CIRCLE + [2, 'a\u20DDb'], // a⃝b + [0, '\u200E'], // LEFT-TO-RIGHT MARK + ]; + + for (const [cursor, string] of tests) { + rl.write(string); + assert.strictEqual(rl.getCursorPos().cols, cursor); + rl.write(null, ctrlU); + } +} diff --git a/test/js/node/test/parallel/test-readline-reopen.js b/test/js/node/test/parallel/test-readline-reopen.js new file mode 100644 index 0000000000..fd305fee3e --- /dev/null +++ b/test/js/node/test/parallel/test-readline-reopen.js @@ -0,0 +1,44 @@ +'use strict'; + +// Regression test for https://github.com/nodejs/node/issues/13557 +// Tests that multiple subsequent readline instances can re-use an input stream. + +const common = require('../common'); +const assert = require('assert'); +const readline = require('readline'); +const { PassThrough } = require('stream'); + +const input = new PassThrough(); +const output = new PassThrough(); + +const rl1 = readline.createInterface({ + input, + output, + terminal: true +}); + +rl1.on('line', common.mustCall(rl1OnLine)); + +// Write a line plus the first byte of a UTF-8 multibyte character to make sure +// that it doesn’t get lost when closing the readline instance. +input.write(Buffer.concat([ + Buffer.from('foo\n'), + Buffer.from([ 0xe2 ]), // Exactly one third of a ☃ snowman. +])); + +function rl1OnLine(line) { + assert.strictEqual(line, 'foo'); + rl1.close(); + const rl2 = readline.createInterface({ + input, + output, + terminal: true + }); + + rl2.on('line', common.mustCall((line) => { + assert.strictEqual(line, '☃bar'); + rl2.close(); + })); + input.write(Buffer.from([0x98, 0x83])); // The rest of the ☃ snowman. + input.write('bar\n'); +} diff --git a/test/js/node/test/parallel/test-readline-undefined-columns.js b/test/js/node/test/parallel/test-readline-undefined-columns.js new file mode 100644 index 0000000000..25bafe957f --- /dev/null +++ b/test/js/node/test/parallel/test-readline-undefined-columns.js @@ -0,0 +1,46 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const PassThrough = require('stream').PassThrough; +const readline = require('readline'); + +common.skipIfDumbTerminal(); + +// Checks that tab completion still works +// when output column size is undefined + +const iStream = new PassThrough(); +const oStream = new PassThrough(); + +readline.createInterface({ + terminal: true, + input: iStream, + output: oStream, + completer: function(line, cb) { + cb(null, [['process.stdout', 'process.stdin', 'process.stderr'], line]); + } +}); + +let output = ''; + +oStream.on('data', function(data) { + output += data; +}); + +oStream.on('end', common.mustCall(() => { + const expect = 'process.stdout\r\n' + + 'process.stdin\r\n' + + 'process.stderr'; + assert.match(output, new RegExp(expect)); +})); + +iStream.write('process.s\t'); + +// Completion works. +assert.match(output, /process\.std\b/); +// Completion doesn’t show all results yet. +assert.doesNotMatch(output, /stdout/); + +iStream.write('\t'); +oStream.end(); diff --git a/test/js/node/test/parallel/test-readline.js b/test/js/node/test/parallel/test-readline.js new file mode 100644 index 0000000000..77799fc14c --- /dev/null +++ b/test/js/node/test/parallel/test-readline.js @@ -0,0 +1,151 @@ +'use strict'; +const common = require('../common'); +const { PassThrough } = require('stream'); +const readline = require('readline'); +const assert = require('assert'); + +common.skipIfDumbTerminal(); + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + rl.on('line', common.mustCall((data) => { + assert.strictEqual(data, 'abc'); + })); + + input.end('abc'); +} + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + rl.on('line', common.mustNotCall('must not be called before newline')); + + input.write('abc'); +} + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + rl.on('line', common.mustCall((data) => { + assert.strictEqual(data, 'abc'); + })); + + input.write('abc\n'); +} + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + rl.write('foo'); + assert.strictEqual(rl.cursor, 3); + + const key = { + xterm: { + home: ['\x1b[H', { ctrl: true, name: 'a' }], + end: ['\x1b[F', { ctrl: true, name: 'e' }], + }, + gnome: { + home: ['\x1bOH', { ctrl: true, name: 'a' }], + end: ['\x1bOF', { ctrl: true, name: 'e' }] + }, + rxvt: { + home: ['\x1b[7', { ctrl: true, name: 'a' }], + end: ['\x1b[8', { ctrl: true, name: 'e' }] + }, + putty: { + home: ['\x1b[1~', { ctrl: true, name: 'a' }], + end: ['\x1b[>~', { ctrl: true, name: 'e' }] + } + }; + + [key.xterm, key.gnome, key.rxvt, key.putty].forEach(function(key) { + rl.write.apply(rl, key.home); + assert.strictEqual(rl.cursor, 0); + rl.write.apply(rl, key.end); + assert.strictEqual(rl.cursor, 3); + }); + +} + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + const key = { + xterm: { + home: ['\x1b[H', { ctrl: true, name: 'a' }], + metab: ['\x1bb', { meta: true, name: 'b' }], + metaf: ['\x1bf', { meta: true, name: 'f' }], + } + }; + + rl.write('foo bar.hop/zoo'); + rl.write.apply(rl, key.xterm.home); + [ + { cursor: 4, key: key.xterm.metaf }, + { cursor: 7, key: key.xterm.metaf }, + { cursor: 8, key: key.xterm.metaf }, + { cursor: 11, key: key.xterm.metaf }, + { cursor: 12, key: key.xterm.metaf }, + { cursor: 15, key: key.xterm.metaf }, + { cursor: 12, key: key.xterm.metab }, + { cursor: 11, key: key.xterm.metab }, + { cursor: 8, key: key.xterm.metab }, + { cursor: 7, key: key.xterm.metab }, + { cursor: 4, key: key.xterm.metab }, + { cursor: 0, key: key.xterm.metab }, + ].forEach(function(action) { + rl.write.apply(rl, action.key); + assert.strictEqual(rl.cursor, action.cursor); + }); +} + +{ + const input = new PassThrough(); + const rl = readline.createInterface({ + terminal: true, + input: input + }); + + const key = { + xterm: { + home: ['\x1b[H', { ctrl: true, name: 'a' }], + metad: ['\x1bd', { meta: true, name: 'd' }] + } + }; + + rl.write('foo bar.hop/zoo'); + rl.write.apply(rl, key.xterm.home); + [ + 'bar.hop/zoo', + '.hop/zoo', + 'hop/zoo', + '/zoo', + 'zoo', + '', + ].forEach(function(expectedLine) { + rl.write.apply(rl, key.xterm.metad); + assert.strictEqual(rl.cursor, 0); + assert.strictEqual(rl.line, expectedLine); + }); +} diff --git a/test/js/node/test/parallel/test-ref-unref-return.js b/test/js/node/test/parallel/test-ref-unref-return.js new file mode 100644 index 0000000000..aec2fff5ce --- /dev/null +++ b/test/js/node/test/parallel/test-ref-unref-return.js @@ -0,0 +1,12 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const net = require('net'); +const dgram = require('dgram'); + +assert.ok((new net.Server()).ref() instanceof net.Server); +assert.ok((new net.Server()).unref() instanceof net.Server); +assert.ok((new net.Socket()).ref() instanceof net.Socket); +assert.ok((new net.Socket()).unref() instanceof net.Socket); +assert.ok((new dgram.Socket('udp4')).ref() instanceof dgram.Socket); +assert.ok((new dgram.Socket('udp6')).unref() instanceof dgram.Socket); diff --git a/test/js/node/test/parallel/test-regression-object-prototype.js b/test/js/node/test/parallel/test-regression-object-prototype.js new file mode 100644 index 0000000000..2ea1ba858a --- /dev/null +++ b/test/js/node/test/parallel/test-regression-object-prototype.js @@ -0,0 +1,28 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-disable node-core/require-common-first, node-core/required-modules */ +'use strict'; + +Object.prototype.xadsadsdasasdxx = function() { +}; + +console.log('puts after'); diff --git a/test/js/node/test/parallel/test-repl-clear-immediate-crash.js b/test/js/node/test/parallel/test-repl-clear-immediate-crash.js new file mode 100644 index 0000000000..ce8aaf48e7 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-clear-immediate-crash.js @@ -0,0 +1,12 @@ +'use strict'; +const common = require('../common'); +const child_process = require('child_process'); +const assert = require('assert'); + +// Regression test for https://github.com/nodejs/node/issues/37806: +const proc = child_process.spawn(process.execPath, ['-i']); +proc.on('error', common.mustNotCall()); +proc.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); +})); +proc.stdin.write('clearImmediate({});\n.exit\n'); diff --git a/test/js/node/test/parallel/test-repl-dynamic-import.js b/test/js/node/test/parallel/test-repl-dynamic-import.js new file mode 100644 index 0000000000..a043e31bf5 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-dynamic-import.js @@ -0,0 +1,20 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const child_process = require('child_process'); +const child = child_process.spawn(process.execPath, [ + '--interactive', + '--expose-gc', +], { + stdio: 'pipe' +}); +child.stdin.write('\nimport("fs");\n_.then(gc);\n'); +// Wait for concurrent GC to finish +setTimeout(() => { + child.stdin.write('\nimport("fs");\n'); + child.stdin.write('\nprocess.exit(0);\n'); +}, common.platformTimeout(50)); +child.on('exit', (code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); +}); diff --git a/test/js/node/test/parallel/test-repl-preview-without-inspector.js b/test/js/node/test/parallel/test-repl-preview-without-inspector.js new file mode 100644 index 0000000000..8905d21483 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-preview-without-inspector.js @@ -0,0 +1,161 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { REPLServer } = require('repl'); +const { Stream } = require('stream'); + +if (process.features.inspector) + common.skip('test is for node compiled with --without-inspector only'); + +// Ignore terminal settings. This is so the test can be run intact if TERM=dumb. +process.env.TERM = ''; +const PROMPT = 'repl > '; + +class REPLStream extends Stream { + readable = true; + writable = true; + + constructor() { + super(); + this.lines = ['']; + } + run(data) { + for (const entry of data) { + this.emit('data', entry); + } + this.emit('data', '\n'); + } + write(chunk) { + const chunkLines = chunk.toString('utf8').split('\n'); + this.lines[this.lines.length - 1] += chunkLines[0]; + if (chunkLines.length > 1) { + this.lines.push(...chunkLines.slice(1)); + } + this.emit('line'); + return true; + } + wait() { + this.lines = ['']; + return new Promise((resolve, reject) => { + const onError = (err) => { + this.removeListener('line', onLine); + reject(err); + }; + const onLine = () => { + if (this.lines[this.lines.length - 1].includes(PROMPT)) { + this.removeListener('error', onError); + this.removeListener('line', onLine); + resolve(this.lines); + } + }; + this.once('error', onError); + this.on('line', onLine); + }); + } + pause() { } + resume() { } +} + +function runAndWait(cmds, repl) { + const promise = repl.inputStream.wait(); + for (const cmd of cmds) { + repl.inputStream.run(cmd); + } + return promise; +} + +const repl = REPLServer({ + prompt: PROMPT, + stream: new REPLStream(), + ignoreUndefined: true, + useColors: true, + terminal: true, +}); + +repl.inputStream.run([ + 'function foo(x) { return x; }', + 'function koo() { console.log("abc"); }', + 'a = undefined;', + 'const r = 5;', +]); + +const testCases = [{ + input: 'foo', + preview: [ + 'foo\r', + '\x1B[36m[Function: foo]\x1B[39m', + ] +}, { + input: 'r', + preview: [ + 'r\r', + '\x1B[33m5\x1B[39m', + ] +}, { + input: 'koo', + preview: [ + 'koo\r', + '\x1B[36m[Function: koo]\x1B[39m', + ] +}, { + input: 'a', + preview: ['a\r'] // No "undefined" preview. +}, { + input: " { b: 1 }['b'] === 1", + preview: [ + " { b: 1 }['b'] === 1\r", + '\x1B[33mtrue\x1B[39m', + ] +}, { + input: "{ b: 1 }['b'] === 1;", + preview: [ + "{ b: 1 }['b'] === 1;\r", + '\x1B[33mfalse\x1B[39m', + ] +}, { + input: '{ a: true }', + preview: [ + '{ a: true }\r', + '{ a: \x1B[33mtrue\x1B[39m }', + ] +}, { + input: '{ a: true };', + preview: [ + '{ a: true };\r', + '\x1B[33mtrue\x1B[39m', + ] +}, { + input: ' \t { a: true};', + preview: [ + ' { a: true};\r', + '\x1B[33mtrue\x1B[39m', + ] +}, { + input: '1n + 2n', + preview: [ + '1n + 2n\r', + '\x1B[33m3n\x1B[39m', + ] +}, { + input: '{};1', + preview: [ + '{};1\r', + '\x1B[33m1\x1B[39m', + ], +}]; + +async function runTest() { + for (const { input, preview } of testCases) { + const toBeRun = input.split('\n'); + let lines = await runAndWait(toBeRun, repl); + // Remove error messages. That allows the code to run in different + // engines. + // eslint-disable-next-line no-control-regex + lines = lines.map((line) => line.replace(/Error: .+?\x1B/, '')); + assert.strictEqual(lines.pop(), '\x1B[1G\x1B[0Jrepl > \x1B[8G'); + assert.deepStrictEqual(lines, preview); + } +} + +runTest().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-repl-syntax-error-handling.js b/test/js/node/test/parallel/test-repl-syntax-error-handling.js new file mode 100644 index 0000000000..91a8614d1d --- /dev/null +++ b/test/js/node/test/parallel/test-repl-syntax-error-handling.js @@ -0,0 +1,71 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +switch (process.argv[2]) { + case 'child': + return child(); + case undefined: + return parent(); + default: + throw new Error('invalid'); +} + +function parent() { + const spawn = require('child_process').spawn; + const child = spawn(process.execPath, [__filename, 'child']); + + child.stderr.setEncoding('utf8'); + child.stderr.on('data', function(c) { + console.error(`${c}`); + throw new Error('should not get stderr data'); + }); + + child.stdout.setEncoding('utf8'); + let out = ''; + child.stdout.on('data', function(c) { + out += c; + }); + child.stdout.on('end', function() { + assert.strictEqual(out, '10\n'); + console.log('ok - got expected output'); + }); + + child.on('exit', function(c) { + assert(!c); + console.log('ok - exit success'); + }); +} + +function child() { + const vm = require('vm'); + let caught; + try { + vm.runInThisContext('haf!@##&$!@$*!@', { displayErrors: false }); + } catch { + caught = true; + } + assert(caught); + vm.runInThisContext('console.log(10)', { displayErrors: false }); +} diff --git a/test/js/node/test/parallel/test-require-cache.js b/test/js/node/test/parallel/test-require-cache.js new file mode 100644 index 0000000000..7b62ab5764 --- /dev/null +++ b/test/js/node/test/parallel/test-require-cache.js @@ -0,0 +1,44 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +{ + const relativePath = '../fixtures/semicolon'; + const absolutePath = require.resolve(relativePath); + const fakeModule = {}; + + require.cache[absolutePath] = { exports: fakeModule }; + + assert.strictEqual(require(relativePath), fakeModule); +} + + +{ + const relativePath = 'fs'; + const fakeModule = {}; + + require.cache[relativePath] = { exports: fakeModule }; + + assert.strictEqual(require(relativePath), fakeModule); +} diff --git a/test/js/node/test/parallel/test-require-dot.js b/test/js/node/test/parallel/test-require-dot.js new file mode 100644 index 0000000000..7145e688d4 --- /dev/null +++ b/test/js/node/test/parallel/test-require-dot.js @@ -0,0 +1,23 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const m = require('module'); +const fixtures = require('../common/fixtures'); + +const a = require(fixtures.path('module-require', 'relative', 'dot.js')); +const b = require(fixtures.path('module-require', 'relative', 'dot-slash.js')); + +assert.strictEqual(a.value, 42); +// require(".") should resolve like require("./") +assert.strictEqual(a, b); + +process.env.NODE_PATH = fixtures.path('module-require', 'relative'); +m._initPaths(); + +assert.throws( + () => require('.'), + { + message: /Cannot find module '\.'/, + code: 'MODULE_NOT_FOUND' + } +); diff --git a/test/js/node/test/parallel/test-require-empty-main.js b/test/js/node/test/parallel/test-require-empty-main.js new file mode 100644 index 0000000000..73f141d1f9 --- /dev/null +++ b/test/js/node/test/parallel/test-require-empty-main.js @@ -0,0 +1,25 @@ +'use strict'; +require('../common'); + +// A package.json with an empty "main" property should use index.js if present. +// require.resolve() should resolve to index.js for the same reason. +// +// In fact, any "main" property that doesn't resolve to a file should result +// in index.js being used, but that's already checked for by other tests. +// This test only concerns itself with the empty string. + +const assert = require('assert'); +const path = require('path'); +const fixtures = require('../common/fixtures'); + +const where = fixtures.path('require-empty-main'); +const expected = path.join(where, 'index.js'); + +test(); +setImmediate(test); + +function test() { + assert.strictEqual(require.resolve(where), expected); + assert.strictEqual(require(where), 42); + assert.strictEqual(require.resolve(where), expected); +} diff --git a/test/js/node/test/parallel/test-require-extensions-main.js b/test/js/node/test/parallel/test-require-extensions-main.js new file mode 100644 index 0000000000..16fbad6cf7 --- /dev/null +++ b/test/js/node/test/parallel/test-require-extensions-main.js @@ -0,0 +1,13 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const fixturesRequire = require(fixtures.path('require-bin', 'bin', 'req.js')); + +assert.strictEqual( + fixturesRequire, + '', + 'test-require-extensions-main failed to import fixture requirements: ' + + fixturesRequire +); diff --git a/test/js/node/test/parallel/test-require-extensions-same-filename-as-dir-trailing-slash.js b/test/js/node/test/parallel/test-require-extensions-same-filename-as-dir-trailing-slash.js new file mode 100644 index 0000000000..2461ece860 --- /dev/null +++ b/test/js/node/test/parallel/test-require-extensions-same-filename-as-dir-trailing-slash.js @@ -0,0 +1,35 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const content = + require(fixtures.path('json-with-directory-name-module', + 'module-stub', + 'one-trailing-slash', + 'two', + 'three.js')); + +assert.notStrictEqual(content.rocko, 'artischocko'); +assert.strictEqual(content, 'hello from module-stub!'); diff --git a/test/js/node/test/parallel/test-require-long-path.js b/test/js/node/test/parallel/test-require-long-path.js new file mode 100644 index 0000000000..abc75176bc --- /dev/null +++ b/test/js/node/test/parallel/test-require-long-path.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); +if (!common.isWindows) + common.skip('this test is Windows-specific.'); + +const fs = require('fs'); +const path = require('path'); + +const tmpdir = require('../common/tmpdir'); + +// Make a path that is more than 260 chars long. +const dirNameLen = Math.max(260 - tmpdir.path.length, 1); +const dirName = tmpdir.resolve('x'.repeat(dirNameLen)); +const fullDirPath = path.resolve(dirName); + +const indexFile = path.join(fullDirPath, 'index.js'); +const otherFile = path.join(fullDirPath, 'other.js'); + +tmpdir.refresh(); + +fs.mkdirSync(fullDirPath); +fs.writeFileSync(indexFile, 'require("./other");'); +fs.writeFileSync(otherFile, ''); + +require(indexFile); +require(otherFile); + +tmpdir.refresh(); diff --git a/test/js/node/test/parallel/test-require-process.js b/test/js/node/test/parallel/test-require-process.js new file mode 100644 index 0000000000..57af1508f0 --- /dev/null +++ b/test/js/node/test/parallel/test-require-process.js @@ -0,0 +1,7 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +const nativeProcess = require('process'); +// require('process') should return global process reference +assert.strictEqual(nativeProcess, process); diff --git a/test/js/node/test/parallel/test-require-unicode.js b/test/js/node/test/parallel/test-require-unicode.js new file mode 100644 index 0000000000..362ec6487a --- /dev/null +++ b/test/js/node/test/parallel/test-require-unicode.js @@ -0,0 +1,17 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const dirname = tmpdir.resolve('\u4e2d\u6587\u76ee\u5f55'); +fs.mkdirSync(dirname); +fs.writeFileSync(path.join(dirname, 'file.js'), 'module.exports = 42;'); +fs.writeFileSync(path.join(dirname, 'package.json'), + JSON.stringify({ name: 'test', main: 'file.js' })); +assert.strictEqual(require(dirname), 42); +assert.strictEqual(require(path.join(dirname, 'file.js')), 42); diff --git a/test/js/node/test/parallel/test-sigint-infinite-loop.js b/test/js/node/test/parallel/test-sigint-infinite-loop.js new file mode 100644 index 0000000000..30eb98ecb8 --- /dev/null +++ b/test/js/node/test/parallel/test-sigint-infinite-loop.js @@ -0,0 +1,34 @@ +'use strict'; +// This test is to assert that we can SIGINT a script which loops forever. +// Ref(http): +// groups.google.com/group/nodejs-dev/browse_thread/thread/e20f2f8df0296d3f +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; + +console.log('start'); + +const c = spawn(process.execPath, ['-e', 'while(true) { console.log("hi"); }']); + +let sentKill = false; + +c.stdout.on('data', function(s) { + // Prevent race condition: + // Wait for the first bit of output from the child process + // so that we're sure that it's in the V8 event loop and not + // just in the startup phase of execution. + if (!sentKill) { + c.kill('SIGINT'); + console.log('SIGINT infinite-loop.js'); + sentKill = true; + } +}); + +c.on('exit', common.mustCall(function(code) { + assert.ok(code !== 0); + console.log('killed infinite-loop.js'); +})); + +process.on('exit', function() { + assert.ok(sentKill); +}); diff --git a/test/js/node/test/parallel/test-signal-args.js b/test/js/node/test/parallel/test-signal-args.js new file mode 100644 index 0000000000..7b72ed6dcb --- /dev/null +++ b/test/js/node/test/parallel/test-signal-args.js @@ -0,0 +1,25 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +if (common.isWindows) + common.skip('Sending signals with process.kill is not supported on Windows'); +if (!common.isMainThread) + common.skip('No signal handling available in Workers'); + +process.once('SIGINT', common.mustCall((signal) => { + assert.strictEqual(signal, 'SIGINT'); +})); + +process.kill(process.pid, 'SIGINT'); + +process.once('SIGTERM', common.mustCall((signal) => { + assert.strictEqual(signal, 'SIGTERM'); +})); + +process.kill(process.pid, 'SIGTERM'); + +// Prevent Node.js from exiting due to empty event loop before signal handlers +// are fired +setImmediate(() => {}); diff --git a/test/js/node/test/parallel/test-signal-handler-remove-on-exit.js b/test/js/node/test/parallel/test-signal-handler-remove-on-exit.js new file mode 100644 index 0000000000..1c87497172 --- /dev/null +++ b/test/js/node/test/parallel/test-signal-handler-remove-on-exit.js @@ -0,0 +1,9 @@ +'use strict'; +require('../common'); + +// Regression test for https://github.com/nodejs/node/issues/30581 +// This script should not crash. + +function dummy() {} +process.on('SIGINT', dummy); +process.on('exit', () => process.removeListener('SIGINT', dummy)); diff --git a/test/js/node/test/parallel/test-signal-handler.js b/test/js/node/test/parallel/test-signal-handler.js new file mode 100644 index 0000000000..05ec4e7f73 --- /dev/null +++ b/test/js/node/test/parallel/test-signal-handler.js @@ -0,0 +1,56 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); + +if (common.isWindows) + common.skip('SIGUSR1 and SIGHUP signals are not supported'); +if (!common.isMainThread) + common.skip('Signal handling in Workers is not supported'); + +console.log(`process.pid: ${process.pid}`); + +process.on('SIGUSR1', common.mustCall()); + +process.on('SIGUSR1', common.mustCall(function() { + setTimeout(function() { + console.log('End.'); + process.exit(0); + }, 5); +})); + +let i = 0; +setInterval(function() { + console.log(`running process...${++i}`); + + if (i === 5) { + process.kill(process.pid, 'SIGUSR1'); + } +}, 1); + +// Test on condition where a watcher for SIGNAL +// has been previously registered, and `process.listeners(SIGNAL).length === 1` +process.on('SIGHUP', common.mustNotCall()); +process.removeAllListeners('SIGHUP'); +process.on('SIGHUP', common.mustCall()); +process.kill(process.pid, 'SIGHUP'); diff --git a/test/js/node/test/parallel/test-signal-unregister.js b/test/js/node/test/parallel/test-signal-unregister.js new file mode 100644 index 0000000000..2c4d312942 --- /dev/null +++ b/test/js/node/test/parallel/test-signal-unregister.js @@ -0,0 +1,14 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; +const fixtures = require('../common/fixtures'); + +const child = spawn(process.argv[0], [fixtures.path('should_exit.js')]); +child.stdout.once('data', function() { + child.kill('SIGINT'); +}); +child.on('exit', common.mustCall(function(exitCode, signalCode) { + assert.strictEqual(exitCode, null); + assert.strictEqual(signalCode, 'SIGINT'); +})); diff --git a/test/js/node/test/parallel/test-spawn-cmd-named-pipe.js b/test/js/node/test/parallel/test-spawn-cmd-named-pipe.js new file mode 100644 index 0000000000..4e7ad185a5 --- /dev/null +++ b/test/js/node/test/parallel/test-spawn-cmd-named-pipe.js @@ -0,0 +1,50 @@ +'use strict'; +const common = require('../common'); +// This test is intended for Windows only +if (!common.isWindows) + common.skip('this test is Windows-specific.'); + +const assert = require('assert'); + +if (!process.argv[2]) { + // parent + const net = require('net'); + const spawn = require('child_process').spawn; + const path = require('path'); + + const pipeNamePrefix = `${path.basename(__filename)}.${process.pid}`; + const stdinPipeName = `\\\\.\\pipe\\${pipeNamePrefix}.stdin`; + const stdoutPipeName = `\\\\.\\pipe\\${pipeNamePrefix}.stdout`; + + const stdinPipeServer = net.createServer(function(c) { + c.on('end', common.mustCall()); + c.end('hello'); + }); + stdinPipeServer.listen(stdinPipeName); + + const output = []; + + const stdoutPipeServer = net.createServer(function(c) { + c.on('data', function(x) { + output.push(x); + }); + c.on('end', common.mustCall(function() { + assert.strictEqual(output.join(''), 'hello'); + })); + }); + stdoutPipeServer.listen(stdoutPipeName); + + const args = + [`"${__filename}"`, 'child', '<', stdinPipeName, '>', stdoutPipeName]; + + const child = spawn(`"${process.execPath}"`, args, { shell: true }); + + child.on('exit', common.mustCall(function(exitCode) { + stdinPipeServer.close(); + stdoutPipeServer.close(); + assert.strictEqual(exitCode, 0); + })); +} else { + // child + process.stdin.pipe(process.stdout); +} diff --git a/test/js/node/test/parallel/test-stdin-child-proc.js b/test/js/node/test/parallel/test-stdin-child-proc.js new file mode 100644 index 0000000000..bbb6a29cd7 --- /dev/null +++ b/test/js/node/test/parallel/test-stdin-child-proc.js @@ -0,0 +1,15 @@ +'use strict'; +// This tests that pausing and resuming stdin does not hang and timeout +// when done in a child process. See test/parallel/test-stdin-pause-resume.js +const common = require('../common'); +const assert = require('assert'); +const child_process = require('child_process'); +const path = require('path'); +const cp = child_process.spawn( + process.execPath, + [path.resolve(__dirname, 'test-stdin-pause-resume.js')] +); + +cp.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); +})); diff --git a/test/js/node/test/parallel/test-stdin-from-file-spawn.js b/test/js/node/test/parallel/test-stdin-from-file-spawn.js new file mode 100644 index 0000000000..3830ac124a --- /dev/null +++ b/test/js/node/test/parallel/test-stdin-from-file-spawn.js @@ -0,0 +1,42 @@ +'use strict'; +const common = require('../common'); +const process = require('process'); + +let defaultShell; +if (process.platform === 'linux' || process.platform === 'darwin') { + defaultShell = '/bin/sh'; +} else if (process.platform === 'win32') { + defaultShell = 'cmd.exe'; +} else { + common.skip('This is test exists only on Linux/Win32/macOS'); +} + +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); +const tmpdir = require('../common/tmpdir'); + +const tmpDir = tmpdir.path; +tmpdir.refresh(); +const tmpCmdFile = path.join(tmpDir, 'test-stdin-from-file-spawn-cmd'); +const tmpJsFile = path.join(tmpDir, 'test-stdin-from-file-spawn.js'); +fs.writeFileSync(tmpCmdFile, 'echo hello'); +fs.writeFileSync(tmpJsFile, ` +'use strict'; +const { spawn } = require('child_process'); +// Reference the object to invoke the getter +process.stdin; +setTimeout(() => { + let ok = false; + const child = spawn(process.env.SHELL || '${defaultShell}', + [], { stdio: ['inherit', 'pipe'] }); + child.stdout.on('data', () => { + ok = true; + }); + child.on('close', () => { + process.exit(ok ? 0 : -1); + }); +}, 100); +`); + +execSync(`${process.argv[0]} ${tmpJsFile} < ${tmpCmdFile}`); diff --git a/test/js/node/test/parallel/test-stdin-hang.js b/test/js/node/test/parallel/test-stdin-hang.js new file mode 100644 index 0000000000..887f31fdb7 --- /dev/null +++ b/test/js/node/test/parallel/test-stdin-hang.js @@ -0,0 +1,32 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); + +// This test *only* verifies that invoking the stdin getter does not +// cause node to hang indefinitely. +// If it does, then the test-runner will nuke it. + +// invoke the getter. +process.stdin; // eslint-disable-line no-unused-expressions + +console.error('Should exit normally now.'); diff --git a/test/js/node/test/parallel/test-stdin-pause-resume.js b/test/js/node/test/parallel/test-stdin-pause-resume.js new file mode 100644 index 0000000000..459256099e --- /dev/null +++ b/test/js/node/test/parallel/test-stdin-pause-resume.js @@ -0,0 +1,39 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +console.error('before opening stdin'); +process.stdin.resume(); +console.error('stdin opened'); +setTimeout(function() { + console.error('pausing stdin'); + process.stdin.pause(); + setTimeout(function() { + console.error('opening again'); + process.stdin.resume(); + setTimeout(function() { + console.error('pausing again'); + process.stdin.pause(); + console.error('should exit now'); + }, 1); + }, 1); +}, 1); diff --git a/test/js/node/test/parallel/test-stdin-pipe-large.js b/test/js/node/test/parallel/test-stdin-pipe-large.js new file mode 100644 index 0000000000..5f4a2f10c8 --- /dev/null +++ b/test/js/node/test/parallel/test-stdin-pipe-large.js @@ -0,0 +1,23 @@ +'use strict'; +// See https://github.com/nodejs/node/issues/5927 + +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; + +if (process.argv[2] === 'child') { + process.stdin.pipe(process.stdout); + return; +} + +const child = spawn(process.execPath, [__filename, 'child'], { stdio: 'pipe' }); + +const expectedBytes = 1024 * 1024; +let readBytes = 0; + +child.stdin.end(Buffer.alloc(expectedBytes)); + +child.stdout.on('data', (chunk) => readBytes += chunk.length); +child.stdout.on('end', common.mustCall(() => { + assert.strictEqual(readBytes, expectedBytes); +})); diff --git a/test/js/node/test/parallel/test-stdin-pipe-resume.js b/test/js/node/test/parallel/test-stdin-pipe-resume.js new file mode 100644 index 0000000000..e9000933a3 --- /dev/null +++ b/test/js/node/test/parallel/test-stdin-pipe-resume.js @@ -0,0 +1,27 @@ +'use strict'; +// This tests that piping stdin will cause it to resume() as well. +require('../common'); +const assert = require('assert'); + +if (process.argv[2] === 'child') { + process.stdin.pipe(process.stdout); +} else { + const spawn = require('child_process').spawn; + const buffers = []; + const child = spawn(process.execPath, [__filename, 'child']); + child.stdout.on('data', function(c) { + buffers.push(c); + }); + child.stdout.on('close', function() { + const b = Buffer.concat(buffers).toString(); + assert.strictEqual(b, 'Hello, world\n'); + console.log('ok'); + }); + child.stdin.write('Hel'); + child.stdin.write('lo,'); + child.stdin.write(' wo'); + setTimeout(function() { + child.stdin.write('rld\n'); + child.stdin.end(); + }, 10); +} diff --git a/test/js/node/test/parallel/test-stdin-script-child-option.js b/test/js/node/test/parallel/test-stdin-script-child-option.js new file mode 100644 index 0000000000..5526d66f3f --- /dev/null +++ b/test/js/node/test/parallel/test-stdin-script-child-option.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const expected = '--option-to-be-seen-on-child'; + +const { spawn } = require('child_process'); +const child = spawn(process.execPath, ['-', expected], { stdio: 'pipe' }); + +child.stdin.end('console.log(process.argv[2])'); + +let actual = ''; +child.stdout.setEncoding('utf8'); +child.stdout.on('data', (chunk) => actual += chunk); +child.stdout.on('end', common.mustCall(() => { + assert.strictEqual(actual.trim(), expected); +})); diff --git a/test/js/node/test/parallel/test-stdio-closed.js b/test/js/node/test/parallel/test-stdio-closed.js new file mode 100644 index 0000000000..cc9f1e86cc --- /dev/null +++ b/test/js/node/test/parallel/test-stdio-closed.js @@ -0,0 +1,37 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +if (common.isWindows) { + if (process.argv[2] === 'child') { + /* eslint-disable no-unused-expressions */ + process.stdin; + process.stdout; + process.stderr; + return; + /* eslint-enable no-unused-expressions */ + } + const python = process.env.PYTHON || 'python'; + const script = fixtures.path('spawn_closed_stdio.py'); + const proc = spawn(python, [script, process.execPath, __filename, 'child']); + proc.on('exit', common.mustCall(function(exitCode) { + assert.strictEqual(exitCode, 0); + })); + return; +} + +if (process.argv[2] === 'child') { + [0, 1, 2].forEach((i) => fs.fstatSync(i)); + return; +} + +// Run the script in a shell but close stdout and stderr. +const cmd = `"${process.execPath}" "${__filename}" child 1>&- 2>&-`; +const proc = spawn('/bin/sh', ['-c', cmd], { stdio: 'inherit' }); + +proc.on('exit', common.mustCall(function(exitCode) { + assert.strictEqual(exitCode, 0); +})); diff --git a/test/js/node/test/parallel/test-stdio-pipe-stderr.js b/test/js/node/test/parallel/test-stdio-pipe-stderr.js new file mode 100644 index 0000000000..c914877062 --- /dev/null +++ b/test/js/node/test/parallel/test-stdio-pipe-stderr.js @@ -0,0 +1,36 @@ +'use strict'; +require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const fs = require('fs'); +const { spawnSync } = require('child_process'); + +// Test that invoking node with require, and piping stderr to file, +// does not result in exception, +// see: https://github.com/nodejs/node/issues/11257 + +tmpdir.refresh(); +const fakeModulePath = tmpdir.resolve('batman.js'); +const stderrOutputPath = tmpdir.resolve('stderr-output.txt'); +// We need to redirect stderr to a file to produce #11257 +const stream = fs.createWriteStream(stderrOutputPath); + +// The error described in #11257 only happens when we require a +// non-built-in module. +fs.writeFileSync(fakeModulePath, '', 'utf8'); + +stream.on('open', () => { + spawnSync(process.execPath, { + input: `require(${JSON.stringify(fakeModulePath)})`, + stdio: ['pipe', 'pipe', stream] + }); + const stderr = fs.readFileSync(stderrOutputPath, 'utf8').trim(); + assert.strictEqual( + stderr, + '', + `piping stderr to file should not result in exception: ${stderr}` + ); + stream.end(); + fs.unlinkSync(stderrOutputPath); + fs.unlinkSync(fakeModulePath); +}); diff --git a/test/js/node/test/parallel/test-stdio-undestroy.js b/test/js/node/test/parallel/test-stdio-undestroy.js new file mode 100644 index 0000000000..b525672db2 --- /dev/null +++ b/test/js/node/test/parallel/test-stdio-undestroy.js @@ -0,0 +1,36 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; + +if (process.argv[2] === 'child') { + process.stdout.destroy(); + process.stderr.destroy(); + console.log('stdout'); + process.stdout.write('rocks\n'); + console.error('stderr'); + setTimeout(function() { + process.stderr.write('rocks too\n'); + }, 10); + return; +} + +const proc = spawn(process.execPath, [__filename, 'child'], { stdio: 'pipe' }); + +let stdout = ''; +proc.stdout.setEncoding('utf8'); +proc.stdout.on('data', common.mustCallAtLeast(function(chunk) { + stdout += chunk; +}, 1)); + +let stderr = ''; +proc.stderr.setEncoding('utf8'); +proc.stderr.on('data', common.mustCallAtLeast(function(chunk) { + stderr += chunk; +}, 1)); + +proc.on('exit', common.mustCall(function(exitCode) { + assert.strictEqual(exitCode, 0); + assert.strictEqual(stdout, 'stdout\nrocks\n'); + assert.strictEqual(stderr, 'stderr\nrocks too\n'); +})); diff --git a/test/js/node/test/parallel/test-stdout-cannot-be-closed-child-process-pipe.js b/test/js/node/test/parallel/test-stdout-cannot-be-closed-child-process-pipe.js new file mode 100644 index 0000000000..7cd4b90c00 --- /dev/null +++ b/test/js/node/test/parallel/test-stdout-cannot-be-closed-child-process-pipe.js @@ -0,0 +1,32 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +if (process.argv[2] === 'child') + process.stdout.end('foo'); +else + parent(); + +function parent() { + const spawn = require('child_process').spawn; + const child = spawn(process.execPath, [__filename, 'child']); + let out = ''; + let err = ''; + + child.stdout.setEncoding('utf8'); + child.stderr.setEncoding('utf8'); + + child.stdout.on('data', function(c) { + out += c; + }); + child.stderr.on('data', function(c) { + err += c; + }); + + child.on('close', function(code, signal) { + assert.strictEqual(code, 0); + assert.strictEqual(err, ''); + assert.strictEqual(out, 'foo'); + console.log('ok'); + }); +} diff --git a/test/js/node/test/parallel/test-stdout-pipeline-destroy.js b/test/js/node/test/parallel/test-stdout-pipeline-destroy.js new file mode 100644 index 0000000000..291579cf69 --- /dev/null +++ b/test/js/node/test/parallel/test-stdout-pipeline-destroy.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const { Transform, Readable, pipeline } = require('stream'); +const assert = require('assert'); + +const reader = new Readable({ + read(size) { this.push('foo'); } +}); + +let count = 0; + +const err = new Error('this-error-gets-hidden'); + +const transform = new Transform({ + transform(chunk, enc, cb) { + if (count++ >= 5) + this.emit('error', err); + else + cb(null, count.toString() + '\n'); + } +}); + +pipeline( + reader, + transform, + process.stdout, + common.mustCall((e) => { + assert.strictEqual(e, err); + }) +); diff --git a/test/js/node/test/parallel/test-stdout-stderr-reading.js b/test/js/node/test/parallel/test-stdout-stderr-reading.js new file mode 100644 index 0000000000..57bfffa272 --- /dev/null +++ b/test/js/node/test/parallel/test-stdout-stderr-reading.js @@ -0,0 +1,67 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// Verify that stdout is never read from. +const net = require('net'); +const read = net.Socket.prototype.read; + +net.Socket.prototype.read = function() { + if (this.fd === 1) + throw new Error('reading from stdout!'); + if (this.fd === 2) + throw new Error('reading from stderr!'); + return read.apply(this, arguments); +}; + +if (process.argv[2] === 'child') + child(); +else + parent(); + +function parent() { + const spawn = require('child_process').spawn; + const node = process.execPath; + + const c1 = spawn(node, [__filename, 'child']); + let c1out = ''; + c1.stdout.setEncoding('utf8'); + c1.stdout.on('data', function(chunk) { + c1out += chunk; + }); + c1.stderr.setEncoding('utf8'); + c1.stderr.on('data', function(chunk) { + console.error(`c1err: ${chunk.split('\n').join('\nc1err: ')}`); + }); + c1.on('close', common.mustCall(function(code, signal) { + assert(!code); + assert(!signal); + assert.strictEqual(c1out, 'ok\n'); + console.log('ok'); + })); + + const c2 = spawn(node, ['-e', 'console.log("ok")']); + let c2out = ''; + c2.stdout.setEncoding('utf8'); + c2.stdout.on('data', function(chunk) { + c2out += chunk; + }); + c1.stderr.setEncoding('utf8'); + c1.stderr.on('data', function(chunk) { + console.error(`c1err: ${chunk.split('\n').join('\nc1err: ')}`); + }); + c2.on('close', common.mustCall(function(code, signal) { + assert(!code); + assert(!signal); + assert.strictEqual(c2out, 'ok\n'); + console.log('ok'); + })); +} + +function child() { + // Should not be reading *ever* in here. + net.Socket.prototype.read = function() { + throw new Error('no reading allowed in child'); + }; + console.log('ok'); +} diff --git a/test/js/node/test/parallel/test-stdout-stderr-write.js b/test/js/node/test/parallel/test-stdout-stderr-write.js new file mode 100644 index 0000000000..803fc70536 --- /dev/null +++ b/test/js/node/test/parallel/test-stdout-stderr-write.js @@ -0,0 +1,8 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +// https://github.com/nodejs/node/pull/39246 +assert.strictEqual(process.stderr.write('asd'), true); +assert.strictEqual(process.stdout.write('asd'), true); diff --git a/test/js/node/test/parallel/test-stdout-to-file.js b/test/js/node/test/parallel/test-stdout-to-file.js new file mode 100644 index 0000000000..9114f22443 --- /dev/null +++ b/test/js/node/test/parallel/test-stdout-to-file.js @@ -0,0 +1,46 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const childProcess = require('child_process'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); +const tmpdir = require('../common/tmpdir'); + +const scriptString = fixtures.path('print-chars.js'); +const scriptBuffer = fixtures.path('print-chars-from-buffer.js'); +const tmpFile = tmpdir.resolve('stdout.txt'); + +tmpdir.refresh(); + +function test(size, useBuffer, cb) { + const cmd = `"${process.argv[0]}" "${ + useBuffer ? scriptBuffer : scriptString}" ${size} > "${tmpFile}"`; + + try { + fs.unlinkSync(tmpFile); + } catch { + // Continue regardless of error. + } + + console.log(`${size} chars to ${tmpFile}...`); + + childProcess.exec(cmd, common.mustSucceed(() => { + console.log('done!'); + + const stat = fs.statSync(tmpFile); + + console.log(`${tmpFile} has ${stat.size} bytes`); + + assert.strictEqual(size, stat.size); + fs.unlinkSync(tmpFile); + + cb(); + })); +} + +test(1024 * 1024, false, common.mustCall(function() { + console.log('Done printing with string'); + test(1024 * 1024, true, common.mustCall(function() { + console.log('Done printing with buffer'); + })); +})); diff --git a/test/js/node/test/parallel/test-strace-openat-openssl.js b/test/js/node/test/parallel/test-strace-openat-openssl.js new file mode 100644 index 0000000000..13882e67ae --- /dev/null +++ b/test/js/node/test/parallel/test-strace-openat-openssl.js @@ -0,0 +1,61 @@ +'use strict'; + +const common = require('../common'); +const { spawn, spawnSync } = require('node:child_process'); +const { createInterface } = require('node:readline'); +const assert = require('node:assert'); + +if (!common.hasCrypto) + common.skip('missing crypto'); +if (!common.isLinux) + common.skip('linux only'); +if (common.isASan) + common.skip('strace does not work well with address sanitizer builds'); +if (spawnSync('strace').error !== undefined) { + common.skip('missing strace'); +} + +{ + const allowedOpenCalls = new Set([ + '/etc/ssl/openssl.cnf', + ]); + const strace = spawn('strace', [ + '-f', '-ff', + '-e', 'trace=open,openat', + '-s', '512', + '-D', process.execPath, '-e', 'require("crypto")', + ]); + + // stderr is the default for strace + const rl = createInterface({ input: strace.stderr }); + rl.on('line', (line) => { + if (!line.startsWith('open')) { + return; + } + + const file = line.match(/"(.*?)"/)[1]; + // skip .so reading attempt + if (file.match(/.+\.so(\.?)/) !== null) { + return; + } + // skip /proc/* + if (file.match(/\/proc\/.+/) !== null) { + return; + } + + assert(allowedOpenCalls.delete(file), `${file} is not in the list of allowed openat calls`); + }); + const debugOutput = []; + strace.stderr.setEncoding('utf8'); + strace.stderr.on('data', (chunk) => { + debugOutput.push(chunk.toString()); + }); + strace.on('error', common.mustNotCall()); + strace.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0, debugOutput); + const missingKeys = Array.from(allowedOpenCalls.keys()); + if (missingKeys.length) { + assert.fail(`The following openat call are missing: ${missingKeys.join(',')}`); + } + })); +} diff --git a/test/js/node/test/parallel/test-stream-auto-destroy.js b/test/js/node/test/parallel/test-stream-auto-destroy.js new file mode 100644 index 0000000000..2a1a5190de --- /dev/null +++ b/test/js/node/test/parallel/test-stream-auto-destroy.js @@ -0,0 +1,112 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +{ + const r = new stream.Readable({ + autoDestroy: true, + read() { + this.push('hello'); + this.push('world'); + this.push(null); + }, + destroy: common.mustCall((err, cb) => cb()) + }); + + let ended = false; + + r.resume(); + + r.on('end', common.mustCall(() => { + ended = true; + })); + + r.on('close', common.mustCall(() => { + assert(ended); + })); +} + +{ + const w = new stream.Writable({ + autoDestroy: true, + write(data, enc, cb) { + cb(null); + }, + destroy: common.mustCall((err, cb) => cb()) + }); + + let finished = false; + + w.write('hello'); + w.write('world'); + w.end(); + + w.on('finish', common.mustCall(() => { + finished = true; + })); + + w.on('close', common.mustCall(() => { + assert(finished); + })); +} + +{ + const t = new stream.Transform({ + autoDestroy: true, + transform(data, enc, cb) { + cb(null, data); + }, + destroy: common.mustCall((err, cb) => cb()) + }); + + let ended = false; + let finished = false; + + t.write('hello'); + t.write('world'); + t.end(); + + t.resume(); + + t.on('end', common.mustCall(() => { + ended = true; + })); + + t.on('finish', common.mustCall(() => { + finished = true; + })); + + t.on('close', common.mustCall(() => { + assert(ended); + assert(finished); + })); +} + +{ + const r = new stream.Readable({ + read() { + r2.emit('error', new Error('fail')); + } + }); + const r2 = new stream.Readable({ + autoDestroy: true, + destroy: common.mustCall((err, cb) => cb()) + }); + + r.pipe(r2); +} + +{ + const r = new stream.Readable({ + read() { + w.emit('error', new Error('fail')); + } + }); + const w = new stream.Writable({ + autoDestroy: true, + destroy: common.mustCall((err, cb) => cb()) + }); + + r.pipe(w); +} diff --git a/test/js/node/test/parallel/test-stream-await-drain-writers-in-synchronously-recursion-write.js b/test/js/node/test/parallel/test-stream-await-drain-writers-in-synchronously-recursion-write.js new file mode 100644 index 0000000000..110d46bb9f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-await-drain-writers-in-synchronously-recursion-write.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); +const { PassThrough } = require('stream'); + +const encode = new PassThrough({ + highWaterMark: 1 +}); + +const decode = new PassThrough({ + highWaterMark: 1 +}); + +const send = common.mustCall((buf) => { + encode.write(buf); +}, 4); + +let i = 0; +const onData = common.mustCall(() => { + if (++i === 2) { + send(Buffer.from([0x3])); + send(Buffer.from([0x4])); + } +}, 4); + +encode.pipe(decode).on('data', onData); + +send(Buffer.from([0x1])); +send(Buffer.from([0x2])); diff --git a/test/js/node/test/parallel/test-stream-backpressure.js b/test/js/node/test/parallel/test-stream-backpressure.js new file mode 100644 index 0000000000..03bcc233c8 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-backpressure.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +let pushes = 0; +const total = 65500 + 40 * 1024; +const rs = new stream.Readable({ + read: common.mustCall(function() { + if (pushes++ === 10) { + this.push(null); + return; + } + + const length = this._readableState.length; + + // We are at most doing two full runs of _reads + // before stopping, because Readable is greedy + // to keep its buffer full + assert(length <= total); + + this.push(Buffer.alloc(65500)); + for (let i = 0; i < 40; i++) { + this.push(Buffer.alloc(1024)); + } + + // We will be over highWaterMark at this point + // but a new call to _read is scheduled anyway. + }, 11) +}); + +const ws = stream.Writable({ + write: common.mustCall(function(data, enc, cb) { + setImmediate(cb); + }, 41 * 10) +}); + +rs.pipe(ws); diff --git a/test/js/node/test/parallel/test-stream-big-packet.js b/test/js/node/test/parallel/test-stream-big-packet.js new file mode 100644 index 0000000000..fdbe3cd211 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-big-packet.js @@ -0,0 +1,65 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +let passed = false; + +class TestStream extends stream.Transform { + _transform(chunk, encoding, done) { + if (!passed) { + // Char 'a' only exists in the last write + passed = chunk.toString().includes('a'); + } + done(); + } +} + +const s1 = new stream.Transform({ + transform(chunk, encoding, cb) { + process.nextTick(cb, null, chunk); + } +}); +const s2 = new stream.PassThrough(); +const s3 = new TestStream(); +s1.pipe(s3); +// Don't let s2 auto close which may close s3 +s2.pipe(s3, { end: false }); + +// We must write a buffer larger than highWaterMark +const big = Buffer.alloc(s1.writableHighWaterMark + 1, 'x'); + +// Since big is larger than highWaterMark, it will be buffered internally. +assert(!s1.write(big)); +// 'tiny' is small enough to pass through internal buffer. +assert(s2.write('tiny')); + +// Write some small data in next IO loop, which will never be written to s3 +// Because 'drain' event is not emitted from s1 and s1 is still paused +setImmediate(s1.write.bind(s1), 'later'); + +// Assert after two IO loops when all operations have been done. +process.on('exit', function() { + assert(passed, 'Large buffer is not handled properly by Writable Stream'); +}); diff --git a/test/js/node/test/parallel/test-stream-big-push.js b/test/js/node/test/parallel/test-stream-big-push.js new file mode 100644 index 0000000000..f9e75edd3f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-big-push.js @@ -0,0 +1,74 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); +const str = 'asdfasdfasdfasdfasdf'; + +const r = new stream.Readable({ + highWaterMark: 5, + encoding: 'utf8' +}); + +let reads = 0; + +function _read() { + if (reads === 0) { + setTimeout(() => { + r.push(str); + }, 1); + reads++; + } else if (reads === 1) { + const ret = r.push(str); + assert.strictEqual(ret, false); + reads++; + } else { + r.push(null); + } +} + +r._read = common.mustCall(_read, 3); + +r.on('end', common.mustCall()); + +// Push some data in to start. +// We've never gotten any read event at this point. +const ret = r.push(str); +// Should be false. > hwm +assert(!ret); +let chunk = r.read(); +assert.strictEqual(chunk, str); +chunk = r.read(); +assert.strictEqual(chunk, null); + +r.once('readable', () => { + // This time, we'll get *all* the remaining data, because + // it's been added synchronously, as the read WOULD take + // us below the hwm, and so it triggered a _read() again, + // which synchronously added more, which we then return. + chunk = r.read(); + assert.strictEqual(chunk, str + str); + + chunk = r.read(); + assert.strictEqual(chunk, null); +}); diff --git a/test/js/node/test/parallel/test-stream-catch-rejections.js b/test/js/node/test/parallel/test-stream-catch-rejections.js new file mode 100644 index 0000000000..81427c3575 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-catch-rejections.js @@ -0,0 +1,51 @@ +'use strict'; + +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +{ + const r = new stream.Readable({ + captureRejections: true, + read() { + } + }); + r.push('hello'); + r.push('world'); + + const err = new Error('kaboom'); + + r.on('error', common.mustCall((_err) => { + assert.strictEqual(err, _err); + assert.strictEqual(r.destroyed, true); + })); + + r.on('data', async () => { + throw err; + }); +} + +{ + const w = new stream.Writable({ + captureRejections: true, + highWaterMark: 1, + write(chunk, enc, cb) { + process.nextTick(cb); + } + }); + + const err = new Error('kaboom'); + + w.write('hello', () => { + w.write('world'); + }); + + w.on('error', common.mustCall((_err) => { + assert.strictEqual(err, _err); + assert.strictEqual(w.destroyed, true); + })); + + w.on('drain', common.mustCall(async () => { + throw err; + }, 2)); +} diff --git a/test/js/node/test/parallel/test-stream-construct.js b/test/js/node/test/parallel/test-stream-construct.js new file mode 100644 index 0000000000..907b9aa0e3 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-construct.js @@ -0,0 +1,280 @@ +'use strict'; + +const common = require('../common'); +const { Writable, Readable, Duplex } = require('stream'); +const assert = require('assert'); + +{ + // Multiple callback. + new Writable({ + construct: common.mustCall((callback) => { + callback(); + callback(); + }) + }).on('error', common.expectsError({ + name: 'Error', + code: 'ERR_MULTIPLE_CALLBACK' + })); +} + +{ + // Multiple callback. + new Readable({ + construct: common.mustCall((callback) => { + callback(); + callback(); + }) + }).on('error', common.expectsError({ + name: 'Error', + code: 'ERR_MULTIPLE_CALLBACK' + })); +} + +{ + // Synchronous error. + + new Writable({ + construct: common.mustCall((callback) => { + callback(new Error('test')); + }) + }).on('error', common.expectsError({ + name: 'Error', + message: 'test' + })); +} + +{ + // Synchronous error. + + new Readable({ + construct: common.mustCall((callback) => { + callback(new Error('test')); + }) + }).on('error', common.expectsError({ + name: 'Error', + message: 'test' + })); +} + +{ + // Asynchronous error. + + new Writable({ + construct: common.mustCall((callback) => { + process.nextTick(callback, new Error('test')); + }) + }).on('error', common.expectsError({ + name: 'Error', + message: 'test' + })); +} + +{ + // Asynchronous error. + + new Readable({ + construct: common.mustCall((callback) => { + process.nextTick(callback, new Error('test')); + }) + }).on('error', common.expectsError({ + name: 'Error', + message: 'test' + })); +} + +function testDestroy(factory) { + { + let constructed = false; + const s = factory({ + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }) + }); + s.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.destroy(); + } + + { + let constructed = false; + const s = factory({ + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }) + }); + s.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.destroy(null, () => { + assert.strictEqual(constructed, true); + }); + } + + { + let constructed = false; + const s = factory({ + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }) + }); + s.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.destroy(); + } + + + { + let constructed = false; + const s = factory({ + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }) + }); + s.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.on('error', common.mustCall((err) => { + assert.strictEqual(err.message, 'kaboom'); + })); + s.destroy(new Error('kaboom'), (err) => { + assert.strictEqual(err.message, 'kaboom'); + assert.strictEqual(constructed, true); + }); + } + + { + let constructed = false; + const s = factory({ + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }) + }); + s.on('error', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + s.destroy(new Error()); + } +} +testDestroy((opts) => new Readable({ + read: common.mustNotCall(), + ...opts +})); +testDestroy((opts) => new Writable({ + write: common.mustNotCall(), + final: common.mustNotCall(), + ...opts +})); + +{ + let constructed = false; + const r = new Readable({ + autoDestroy: true, + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }), + read: common.mustCall(() => { + assert.strictEqual(constructed, true); + r.push(null); + }) + }); + r.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + r.on('data', common.mustNotCall()); +} + +{ + let constructed = false; + const w = new Writable({ + autoDestroy: true, + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }), + write: common.mustCall((chunk, encoding, cb) => { + assert.strictEqual(constructed, true); + process.nextTick(cb); + }), + final: common.mustCall((cb) => { + assert.strictEqual(constructed, true); + process.nextTick(cb); + }) + }); + w.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + w.end('data'); +} + +{ + let constructed = false; + const w = new Writable({ + autoDestroy: true, + construct: common.mustCall((cb) => { + constructed = true; + process.nextTick(cb); + }), + write: common.mustNotCall(), + final: common.mustCall((cb) => { + assert.strictEqual(constructed, true); + process.nextTick(cb); + }) + }); + w.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); + w.end(); +} + +{ + new Duplex({ + construct: common.mustCall() + }); +} + +{ + // https://github.com/nodejs/node/issues/34448 + + let constructed = false; + const d = new Duplex({ + readable: false, + construct: common.mustCall((callback) => { + setImmediate(common.mustCall(() => { + constructed = true; + callback(); + })); + }), + write(chunk, encoding, callback) { + callback(); + }, + read() { + this.push(null); + } + }); + d.resume(); + d.end('foo'); + d.on('close', common.mustCall(() => { + assert.strictEqual(constructed, true); + })); +} + +{ + // Construct should not cause stream to read. + new Readable({ + construct: common.mustCall((callback) => { + callback(); + }), + read: common.mustNotCall() + }); +} diff --git a/test/js/node/test/parallel/test-stream-decoder-objectmode.js b/test/js/node/test/parallel/test-stream-decoder-objectmode.js new file mode 100644 index 0000000000..4c572fed6b --- /dev/null +++ b/test/js/node/test/parallel/test-stream-decoder-objectmode.js @@ -0,0 +1,20 @@ +'use strict'; + +require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +const readable = new stream.Readable({ + read: () => {}, + encoding: 'utf16le', + objectMode: true +}); + +readable.push(Buffer.from('abc', 'utf16le')); +readable.push(Buffer.from('def', 'utf16le')); +readable.push(null); + +// Without object mode, these would be concatenated into a single chunk. +assert.strictEqual(readable.read(), 'abc'); +assert.strictEqual(readable.read(), 'def'); +assert.strictEqual(readable.read(), null); diff --git a/test/js/node/test/parallel/test-stream-destroy-event-order.js b/test/js/node/test/parallel/test-stream-destroy-event-order.js new file mode 100644 index 0000000000..a88fff820d --- /dev/null +++ b/test/js/node/test/parallel/test-stream-destroy-event-order.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +const rs = new Readable({ + read() {} +}); + +let closed = false; +let errored = false; + +rs.on('close', common.mustCall(() => { + closed = true; + assert(errored); +})); + +rs.on('error', common.mustCall((err) => { + errored = true; + assert(!closed); +})); + +rs.destroy(new Error('kaboom')); diff --git a/test/js/node/test/parallel/test-stream-duplex-end.js b/test/js/node/test/parallel/test-stream-duplex-end.js new file mode 100644 index 0000000000..2c7706146e --- /dev/null +++ b/test/js/node/test/parallel/test-stream-duplex-end.js @@ -0,0 +1,41 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const Duplex = require('stream').Duplex; + +{ + const stream = new Duplex({ + read() {} + }); + assert.strictEqual(stream.allowHalfOpen, true); + stream.on('finish', common.mustNotCall()); + assert.strictEqual(stream.listenerCount('end'), 0); + stream.resume(); + stream.push(null); +} + +{ + const stream = new Duplex({ + read() {}, + allowHalfOpen: false + }); + assert.strictEqual(stream.allowHalfOpen, false); + stream.on('finish', common.mustCall()); + assert.strictEqual(stream.listenerCount('end'), 0); + stream.resume(); + stream.push(null); +} + +{ + const stream = new Duplex({ + read() {}, + allowHalfOpen: false + }); + assert.strictEqual(stream.allowHalfOpen, false); + stream._writableState.ended = true; + stream.on('finish', common.mustNotCall()); + assert.strictEqual(stream.listenerCount('end'), 0); + stream.resume(); + stream.push(null); +} diff --git a/test/js/node/test/parallel/test-stream-duplex-props.js b/test/js/node/test/parallel/test-stream-duplex-props.js new file mode 100644 index 0000000000..aa6b23125a --- /dev/null +++ b/test/js/node/test/parallel/test-stream-duplex-props.js @@ -0,0 +1,31 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const { Duplex } = require('stream'); + +{ + const d = new Duplex({ + objectMode: true, + highWaterMark: 100 + }); + + assert.strictEqual(d.writableObjectMode, true); + assert.strictEqual(d.writableHighWaterMark, 100); + assert.strictEqual(d.readableObjectMode, true); + assert.strictEqual(d.readableHighWaterMark, 100); +} + +{ + const d = new Duplex({ + readableObjectMode: false, + readableHighWaterMark: 10, + writableObjectMode: true, + writableHighWaterMark: 100 + }); + + assert.strictEqual(d.writableObjectMode, true); + assert.strictEqual(d.writableHighWaterMark, 100); + assert.strictEqual(d.readableObjectMode, false); + assert.strictEqual(d.readableHighWaterMark, 10); +} diff --git a/test/js/node/test/parallel/test-stream-duplex-readable-end.js b/test/js/node/test/parallel/test-stream-duplex-readable-end.js new file mode 100644 index 0000000000..3b1d4d21ce --- /dev/null +++ b/test/js/node/test/parallel/test-stream-duplex-readable-end.js @@ -0,0 +1,31 @@ +'use strict'; +// https://github.com/nodejs/node/issues/35926 +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +let loops = 5; + +const src = new stream.Readable({ + highWaterMark: 16 * 1024, + read() { + if (loops--) + this.push(Buffer.alloc(20000)); + } +}); + +const dst = new stream.Transform({ + highWaterMark: 16 * 1024, + transform(chunk, output, fn) { + this.push(null); + fn(); + } +}); + +src.pipe(dst); + +dst.on('data', () => { }); +dst.on('end', common.mustCall(() => { + assert.strictEqual(loops, 3); + assert.ok(src.isPaused()); +})); diff --git a/test/js/node/test/parallel/test-stream-duplex-readable-writable.js b/test/js/node/test/parallel/test-stream-duplex-readable-writable.js new file mode 100644 index 0000000000..aec88fc120 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-duplex-readable-writable.js @@ -0,0 +1,46 @@ +'use strict'; + +const common = require('../common'); +const { Duplex } = require('stream'); +const assert = require('assert'); + +{ + const duplex = new Duplex({ + readable: false + }); + assert.strictEqual(duplex.readable, false); + duplex.push('asd'); + duplex.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_PUSH_AFTER_EOF'); + })); + duplex.on('data', common.mustNotCall()); + duplex.on('end', common.mustNotCall()); +} + +{ + const duplex = new Duplex({ + writable: false, + write: common.mustNotCall() + }); + assert.strictEqual(duplex.writable, false); + duplex.write('asd'); + duplex.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_WRITE_AFTER_END'); + })); + duplex.on('finish', common.mustNotCall()); +} + +{ + const duplex = new Duplex({ + readable: false + }); + assert.strictEqual(duplex.readable, false); + duplex.on('data', common.mustNotCall()); + duplex.on('end', common.mustNotCall()); + async function run() { + for await (const chunk of duplex) { + assert(false, chunk); + } + } + run().then(common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-stream-duplex-writable-finished.js b/test/js/node/test/parallel/test-stream-duplex-writable-finished.js new file mode 100644 index 0000000000..20c0781a22 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-duplex-writable-finished.js @@ -0,0 +1,30 @@ +'use strict'; + +const common = require('../common'); +const { Duplex } = require('stream'); +const assert = require('assert'); + +// basic +{ + // Find it on Duplex.prototype + assert(Object.hasOwn(Duplex.prototype, 'writableFinished')); +} + +// event +{ + const duplex = new Duplex(); + + duplex._write = (chunk, encoding, cb) => { + // The state finished should start in false. + assert.strictEqual(duplex.writableFinished, false); + cb(); + }; + + duplex.on('finish', common.mustCall(() => { + assert.strictEqual(duplex.writableFinished, true); + })); + + duplex.end('testing finished state', common.mustCall(() => { + assert.strictEqual(duplex.writableFinished, true); + })); +} diff --git a/test/js/node/test/parallel/test-stream-end-of-streams.js b/test/js/node/test/parallel/test-stream-end-of-streams.js new file mode 100644 index 0000000000..80a39d052b --- /dev/null +++ b/test/js/node/test/parallel/test-stream-end-of-streams.js @@ -0,0 +1,20 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +const { Duplex, finished } = require('stream'); + +assert.throws( + () => { + // Passing empty object to mock invalid stream + // should throw error + finished({}, () => {}); + }, + { code: 'ERR_INVALID_ARG_TYPE' } +); + +const streamObj = new Duplex(); +streamObj.end(); +// Below code should not throw any errors as the +// streamObj is `Stream` +finished(streamObj, () => {}); diff --git a/test/js/node/test/parallel/test-stream-end-paused.js b/test/js/node/test/parallel/test-stream-end-paused.js new file mode 100644 index 0000000000..f29c82f532 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-end-paused.js @@ -0,0 +1,50 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// Make sure we don't miss the end event for paused 0-length streams + +const Readable = require('stream').Readable; +const stream = new Readable(); +let calledRead = false; +stream._read = function() { + assert(!calledRead); + calledRead = true; + this.push(null); +}; + +stream.on('data', function() { + throw new Error('should not ever get data'); +}); +stream.pause(); + +setTimeout(common.mustCall(function() { + stream.on('end', common.mustCall()); + stream.resume(); +}), 1); + +process.on('exit', function() { + assert(calledRead); + console.log('ok'); +}); diff --git a/test/js/node/test/parallel/test-stream-err-multiple-callback-construction.js b/test/js/node/test/parallel/test-stream-err-multiple-callback-construction.js new file mode 100644 index 0000000000..829af3ffe7 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-err-multiple-callback-construction.js @@ -0,0 +1,25 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +class TestWritable extends stream.Writable { + _write(_chunk, _encoding, callback) { + callback(); + } + + _final(callback) { + process.nextTick(callback); + process.nextTick(callback); + } +} + +const writable = new TestWritable(); + +writable.on('finish', common.mustCall()); +writable.on('error', common.mustCall((error) => { + assert.strictEqual(error.message, 'Callback called multiple times'); +})); + +writable.write('some data'); +writable.end(); diff --git a/test/js/node/test/parallel/test-stream-error-once.js b/test/js/node/test/parallel/test-stream-error-once.js new file mode 100644 index 0000000000..71f268cfa4 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-error-once.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); +const { Writable, Readable } = require('stream'); + +{ + const writable = new Writable(); + writable.on('error', common.mustCall()); + writable.end(); + writable.write('h'); + writable.write('h'); +} + +{ + const readable = new Readable(); + readable.on('error', common.mustCall()); + readable.push(null); + readable.push('h'); + readable.push('h'); +} diff --git a/test/js/node/test/parallel/test-stream-events-prepend.js b/test/js/node/test/parallel/test-stream-events-prepend.js new file mode 100644 index 0000000000..80fedf8fae --- /dev/null +++ b/test/js/node/test/parallel/test-stream-events-prepend.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); + +class Writable extends stream.Writable { + constructor() { + super(); + this.prependListener = undefined; + } + + _write(chunk, end, cb) { + cb(); + } +} + +class Readable extends stream.Readable { + _read() { + this.push(null); + } +} + +const w = new Writable(); +w.on('pipe', common.mustCall()); + +const r = new Readable(); +r.pipe(w); diff --git a/test/js/node/test/parallel/test-stream-inheritance.js b/test/js/node/test/parallel/test-stream-inheritance.js new file mode 100644 index 0000000000..658bd2be33 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-inheritance.js @@ -0,0 +1,63 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { Readable, Writable, Duplex, Transform } = require('stream'); + +const readable = new Readable({ read() {} }); +const writable = new Writable({ write() {} }); +const duplex = new Duplex({ read() {}, write() {} }); +const transform = new Transform({ transform() {} }); + +assert.ok(readable instanceof Readable); +assert.ok(!(writable instanceof Readable)); +assert.ok(duplex instanceof Readable); +assert.ok(transform instanceof Readable); + +assert.ok(!(readable instanceof Writable)); +assert.ok(writable instanceof Writable); +assert.ok(duplex instanceof Writable); +assert.ok(transform instanceof Writable); + +assert.ok(!(readable instanceof Duplex)); +assert.ok(!(writable instanceof Duplex)); +assert.ok(duplex instanceof Duplex); +assert.ok(transform instanceof Duplex); + +assert.ok(!(readable instanceof Transform)); +assert.ok(!(writable instanceof Transform)); +assert.ok(!(duplex instanceof Transform)); +assert.ok(transform instanceof Transform); + +assert.ok(!(null instanceof Writable)); +assert.ok(!(undefined instanceof Writable)); + +// Simple inheritance check for `Writable` works fine in a subclass constructor. +function CustomWritable() { + assert.ok( + this instanceof CustomWritable, + `${this} does not inherit from CustomWritable` + ); + assert.ok( + this instanceof Writable, + `${this} does not inherit from Writable` + ); +} + +Object.setPrototypeOf(CustomWritable, Writable); +Object.setPrototypeOf(CustomWritable.prototype, Writable.prototype); + +new CustomWritable(); + +assert.throws( + CustomWritable, + { + code: 'ERR_ASSERTION', + constructor: assert.AssertionError, + message: 'undefined does not inherit from CustomWritable' + } +); + +class OtherCustomWritable extends Writable {} + +assert(!(new OtherCustomWritable() instanceof CustomWritable)); +assert(!(new CustomWritable() instanceof OtherCustomWritable)); diff --git a/test/js/node/test/parallel/test-stream-iterator-helpers-test262-tests.mjs b/test/js/node/test/parallel/test-stream-iterator-helpers-test262-tests.mjs new file mode 100644 index 0000000000..59bcfdc565 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-iterator-helpers-test262-tests.mjs @@ -0,0 +1,146 @@ +import { mustCall } from '../common/index.mjs'; +import { Readable } from 'stream'; +import assert from 'assert'; + +// These tests are manually ported from the draft PR for the test262 test suite +// Authored by Rick Waldron in https://github.com/tc39/test262/pull/2818/files + +// test262 license: +// The << Software identified by reference to the Ecma Standard* ("Software)">> +// is protected by copyright and is being made available under the +// "BSD License", included below. This Software may be subject to third party +// rights (rights from parties other than Ecma International), including patent +// rights, and no licenses under such third party rights are granted under this +// license even if the third party concerned is a member of Ecma International. +// SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT +// http://www.ecma-international.org/memento/codeofconduct.htm FOR INFORMATION +// REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA +// INTERNATIONAL STANDARDS* + +// Copyright (C) 2012-2013 Ecma International +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the authors nor Ecma International may be used to +// endorse or promote products derived from this software without specific +// prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS +// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN +// NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// * Ecma International Standards hereafter means Ecma International Standards +// as well as Ecma Technical Reports + + +// Note all the tests that check AsyncIterator's prototype itself and things +// that happen before stream conversion were not ported. +{ + // drop/length + assert.strictEqual(Readable.prototype.drop.length, 1); + const descriptor = Object.getOwnPropertyDescriptor( + Readable.prototype, + 'drop' + ); + assert.strictEqual(descriptor.enumerable, false); + assert.strictEqual(descriptor.configurable, true); + assert.strictEqual(descriptor.writable, true); + // drop/limit-equals-total + const iterator = Readable.from([1, 2]).drop(2); + const result = await iterator[Symbol.asyncIterator]().next(); + assert.deepStrictEqual(result, { done: true, value: undefined }); + // drop/limit-greater-than-total.js + const iterator2 = Readable.from([1, 2]).drop(3); + const result2 = await iterator2[Symbol.asyncIterator]().next(); + assert.deepStrictEqual(result2, { done: true, value: undefined }); + // drop/limit-less-than-total.js + const iterator3 = Readable.from([1, 2]).drop(1); + const result3 = await iterator3[Symbol.asyncIterator]().next(); + assert.deepStrictEqual(result3, { done: false, value: 2 }); + // drop/limit-rangeerror + assert.throws(() => Readable.from([1]).drop(-1), RangeError); + assert.throws(() => { + Readable.from([1]).drop({ + valueOf() { + throw new Error('boom'); + } + }); + }, /boom/); + // drop/limit-tointeger + const two = await Readable.from([1, 2]).drop({ valueOf: () => 1 }).toArray(); + assert.deepStrictEqual(two, [2]); + // drop/name + assert.strictEqual(Readable.prototype.drop.name, 'drop'); + // drop/non-constructible + assert.throws(() => new Readable.prototype.drop(1), TypeError); + // drop/proto + const proto = Object.getPrototypeOf(Readable.prototype.drop); + assert.strictEqual(proto, Function.prototype); +} +{ + // every/abrupt-iterator-close + const stream = Readable.from([1, 2, 3]); + const e = new Error(); + await assert.rejects(stream.every(mustCall(() => { + throw e; + }, 1)), e); +} +{ + // every/callable-fn + await assert.rejects(Readable.from([1, 2]).every({}), TypeError); +} +{ + // every/callable + Readable.prototype.every.call(Readable.from([]), () => {}); + // eslint-disable-next-line array-callback-return + Readable.from([]).every(() => {}); + assert.throws(() => { + const r = Readable.from([]); + new r.every(() => {}); + }, TypeError); +} + +{ + // every/false + const iterator = Readable.from([1, 2, 3]); + const result = await iterator.every((v) => v === 1); + assert.strictEqual(result, false); +} +{ + // every/every + const iterator = Readable.from([1, 2, 3]); + const result = await iterator.every((v) => true); + assert.strictEqual(result, true); +} + +{ + // every/is-function + assert.strictEqual(typeof Readable.prototype.every, 'function'); +} +{ + // every/length + assert.strictEqual(Readable.prototype.every.length, 1); + // every/name + assert.strictEqual(Readable.prototype.every.name, 'every'); + // every/propdesc + const descriptor = Object.getOwnPropertyDescriptor( + Readable.prototype, + 'every' + ); + assert.strictEqual(descriptor.enumerable, false); + assert.strictEqual(descriptor.configurable, true); + assert.strictEqual(descriptor.writable, true); +} diff --git a/test/js/node/test/parallel/test-stream-once-readable-pipe.js b/test/js/node/test/parallel/test-stream-once-readable-pipe.js new file mode 100644 index 0000000000..e8f4e9422d --- /dev/null +++ b/test/js/node/test/parallel/test-stream-once-readable-pipe.js @@ -0,0 +1,61 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); + +// This test ensures that if have 'readable' listener +// on Readable instance it will not disrupt the pipe. + +{ + let receivedData = ''; + const w = new Writable({ + write: (chunk, env, callback) => { + receivedData += chunk; + callback(); + }, + }); + + const data = ['foo', 'bar', 'baz']; + const r = new Readable({ + read: () => {}, + }); + + r.once('readable', common.mustCall()); + + r.pipe(w); + r.push(data[0]); + r.push(data[1]); + r.push(data[2]); + r.push(null); + + w.on('finish', common.mustCall(() => { + assert.strictEqual(receivedData, data.join('')); + })); +} + +{ + let receivedData = ''; + const w = new Writable({ + write: (chunk, env, callback) => { + receivedData += chunk; + callback(); + }, + }); + + const data = ['foo', 'bar', 'baz']; + const r = new Readable({ + read: () => {}, + }); + + r.pipe(w); + r.push(data[0]); + r.push(data[1]); + r.push(data[2]); + r.push(null); + r.once('readable', common.mustCall()); + + w.on('finish', common.mustCall(() => { + assert.strictEqual(receivedData, data.join('')); + })); +} diff --git a/test/js/node/test/parallel/test-stream-passthrough-drain.js b/test/js/node/test/parallel/test-stream-passthrough-drain.js new file mode 100644 index 0000000000..244bf87407 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-passthrough-drain.js @@ -0,0 +1,10 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { PassThrough } = require('stream'); + +const pt = new PassThrough({ highWaterMark: 0 }); +pt.on('drain', common.mustCall()); +assert(!pt.write('hello1')); +pt.read(); +pt.read(); diff --git a/test/js/node/test/parallel/test-stream-pipe-after-end.js b/test/js/node/test/parallel/test-stream-pipe-after-end.js new file mode 100644 index 0000000000..045d27e085 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-after-end.js @@ -0,0 +1,69 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); + +class TestReadable extends Readable { + constructor(opt) { + super(opt); + this._ended = false; + } + + _read() { + if (this._ended) + this.emit('error', new Error('_read called twice')); + this._ended = true; + this.push(null); + } +} + +class TestWritable extends Writable { + constructor(opt) { + super(opt); + this._written = []; + } + + _write(chunk, encoding, cb) { + this._written.push(chunk); + cb(); + } +} + +// This one should not emit 'end' until we read() from it later. +const ender = new TestReadable(); + +// What happens when you pipe() a Readable that's already ended? +const piper = new TestReadable(); +// pushes EOF null, and length=0, so this will trigger 'end' +piper.read(); + +setTimeout(common.mustCall(function() { + ender.on('end', common.mustCall()); + const c = ender.read(); + assert.strictEqual(c, null); + + const w = new TestWritable(); + w.on('finish', common.mustCall()); + piper.pipe(w); +}), 1); diff --git a/test/js/node/test/parallel/test-stream-pipe-await-drain-manual-resume.js b/test/js/node/test/parallel/test-stream-pipe-await-drain-manual-resume.js new file mode 100644 index 0000000000..a95a5e05ae --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-await-drain-manual-resume.js @@ -0,0 +1,75 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +// A consumer stream with a very low highWaterMark, which starts in a state +// where it buffers the chunk it receives rather than indicating that they +// have been consumed. +const writable = new stream.Writable({ + highWaterMark: 5 +}); + +let isCurrentlyBufferingWrites = true; +const queue = []; + +writable._write = (chunk, encoding, cb) => { + if (isCurrentlyBufferingWrites) + queue.push({ chunk, cb }); + else + cb(); +}; + +const readable = new stream.Readable({ + read() {} +}); + +readable.pipe(writable); + +readable.once('pause', common.mustCall(() => { + assert.strictEqual( + readable._readableState.awaitDrainWriters, + writable, + 'Expected awaitDrainWriters to be a Writable but instead got ' + + `${readable._readableState.awaitDrainWriters}` + ); + // First pause, resume manually. The next write() to writable will still + // return false, because chunks are still being buffered, so it will increase + // the awaitDrain counter again. + + process.nextTick(common.mustCall(() => { + readable.resume(); + })); + + readable.once('pause', common.mustCall(() => { + assert.strictEqual( + readable._readableState.awaitDrainWriters, + writable, + '.resume() should not reset the awaitDrainWriters, but instead got ' + + `${readable._readableState.awaitDrainWriters}` + ); + // Second pause, handle all chunks from now on. Once all callbacks that + // are currently queued up are handled, the awaitDrain drain counter should + // fall back to 0 and all chunks that are pending on the readable side + // should be flushed. + isCurrentlyBufferingWrites = false; + for (const queued of queue) + queued.cb(); + })); +})); + +readable.push(Buffer.alloc(100)); // Fill the writable HWM, first 'pause'. +readable.push(Buffer.alloc(100)); // Second 'pause'. +readable.push(Buffer.alloc(100)); // Should get through to the writable. +readable.push(null); + +writable.on('finish', common.mustCall(() => { + assert.strictEqual( + readable._readableState.awaitDrainWriters, + null, + `awaitDrainWriters should be reset to null + after all chunks are written but instead got + ${readable._readableState.awaitDrainWriters}` + ); + // Everything okay, all chunks were written. +})); diff --git a/test/js/node/test/parallel/test-stream-pipe-await-drain-push-while-write.js b/test/js/node/test/parallel/test-stream-pipe-await-drain-push-while-write.js new file mode 100644 index 0000000000..089767166c --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-await-drain-push-while-write.js @@ -0,0 +1,38 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +const writable = new stream.Writable({ + highWaterMark: 16 * 1024, + write: common.mustCall(function(chunk, encoding, cb) { + assert.strictEqual( + readable._readableState.awaitDrainWriters, + null, + ); + + if (chunk.length === 32 * 1024) { // first chunk + readable.push(Buffer.alloc(34 * 1024)); // above hwm + // We should check if awaitDrain counter is increased in the next + // tick, because awaitDrain is incremented after this method finished + process.nextTick(() => { + assert.strictEqual(readable._readableState.awaitDrainWriters, writable); + }); + } + + process.nextTick(cb); + }, 3) +}); + +// A readable stream which produces two buffers. +const bufs = [Buffer.alloc(32 * 1024), Buffer.alloc(33 * 1024)]; // above hwm +const readable = new stream.Readable({ + highWaterMark: 16 * 1024, + read: function() { + while (bufs.length > 0) { + this.push(bufs.shift()); + } + } +}); + +readable.pipe(writable); diff --git a/test/js/node/test/parallel/test-stream-pipe-await-drain.js b/test/js/node/test/parallel/test-stream-pipe-await-drain.js new file mode 100644 index 0000000000..35b86f67f9 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-await-drain.js @@ -0,0 +1,67 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +// This is very similar to test-stream-pipe-cleanup-pause.js. + +const reader = new stream.Readable(); +const writer1 = new stream.Writable(); +const writer2 = new stream.Writable(); +const writer3 = new stream.Writable(); + +// 560000 is chosen here because it is larger than the (default) highWaterMark +// and will cause `.write()` to return false +// See: https://github.com/nodejs/node/issues/5820 +const buffer = Buffer.allocUnsafe(560000); + +reader._read = () => {}; + +writer1._write = common.mustCall(function(chunk, encoding, cb) { + this.emit('chunk-received'); + process.nextTick(cb); +}, 1); + +writer1.once('chunk-received', () => { + assert.strictEqual( + reader._readableState.awaitDrainWriters.size, + 0, + 'awaitDrain initial value should be 0, actual is ' + + reader._readableState.awaitDrainWriters.size + ); + setImmediate(() => { + // This one should *not* get through to writer1 because writer2 is not + // "done" processing. + reader.push(buffer); + }); +}); + +// A "slow" consumer: +writer2._write = common.mustCall((chunk, encoding, cb) => { + assert.strictEqual( + reader._readableState.awaitDrainWriters.size, + 1, + 'awaitDrain should be 1 after first push, actual is ' + + reader._readableState.awaitDrainWriters.size + ); + // Not calling cb here to "simulate" slow stream. + // This should be called exactly once, since the first .write() call + // will return false. +}, 1); + +writer3._write = common.mustCall((chunk, encoding, cb) => { + assert.strictEqual( + reader._readableState.awaitDrainWriters.size, + 2, + 'awaitDrain should be 2 after second push, actual is ' + + reader._readableState.awaitDrainWriters.size + ); + // Not calling cb here to "simulate" slow stream. + // This should be called exactly once, since the first .write() call + // will return false. +}, 1); + +reader.pipe(writer1); +reader.pipe(writer2); +reader.pipe(writer3); +reader.push(buffer); diff --git a/test/js/node/test/parallel/test-stream-pipe-cleanup-pause.js b/test/js/node/test/parallel/test-stream-pipe-cleanup-pause.js new file mode 100644 index 0000000000..3cdab94648 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-cleanup-pause.js @@ -0,0 +1,37 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); + +const reader = new stream.Readable(); +const writer1 = new stream.Writable(); +const writer2 = new stream.Writable(); + +// 560000 is chosen here because it is larger than the (default) highWaterMark +// and will cause `.write()` to return false +// See: https://github.com/nodejs/node/issues/2323 +const buffer = Buffer.allocUnsafe(560000); + +reader._read = () => {}; + +writer1._write = common.mustCall(function(chunk, encoding, cb) { + this.emit('chunk-received'); + cb(); +}, 1); +writer1.once('chunk-received', function() { + reader.unpipe(writer1); + reader.pipe(writer2); + reader.push(buffer); + setImmediate(function() { + reader.push(buffer); + setImmediate(function() { + reader.push(buffer); + }); + }); +}); + +writer2._write = common.mustCall(function(chunk, encoding, cb) { + cb(); +}, 3); + +reader.pipe(writer1); +reader.push(buffer); diff --git a/test/js/node/test/parallel/test-stream-pipe-cleanup.js b/test/js/node/test/parallel/test-stream-pipe-cleanup.js new file mode 100644 index 0000000000..cdb4d503d6 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-cleanup.js @@ -0,0 +1,125 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// This test asserts that Stream.prototype.pipe does not leave listeners +// hanging on the source or dest. +require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +function Writable() { + this.writable = true; + this.endCalls = 0; + stream.Stream.call(this); +} +Object.setPrototypeOf(Writable.prototype, stream.Stream.prototype); +Object.setPrototypeOf(Writable, stream.Stream); +Writable.prototype.end = function() { + this.endCalls++; +}; + +Writable.prototype.destroy = function() { + this.endCalls++; +}; + +function Readable() { + this.readable = true; + stream.Stream.call(this); +} +Object.setPrototypeOf(Readable.prototype, stream.Stream.prototype); +Object.setPrototypeOf(Readable, stream.Stream); + +function Duplex() { + this.readable = true; + Writable.call(this); +} +Object.setPrototypeOf(Duplex.prototype, Writable.prototype); +Object.setPrototypeOf(Duplex, Writable); + +let i = 0; +const limit = 100; + +let w = new Writable(); + +let r; + +for (i = 0; i < limit; i++) { + r = new Readable(); + r.pipe(w); + r.emit('end'); +} +assert.strictEqual(r.listeners('end').length, 0); +assert.strictEqual(w.endCalls, limit); + +w.endCalls = 0; + +for (i = 0; i < limit; i++) { + r = new Readable(); + r.pipe(w); + r.emit('close'); +} +assert.strictEqual(r.listeners('close').length, 0); +assert.strictEqual(w.endCalls, limit); + +w.endCalls = 0; + +r = new Readable(); + +for (i = 0; i < limit; i++) { + w = new Writable(); + r.pipe(w); + w.emit('close'); +} +assert.strictEqual(w.listeners('close').length, 0); + +r = new Readable(); +w = new Writable(); +const d = new Duplex(); +r.pipe(d); // pipeline A +d.pipe(w); // pipeline B +assert.strictEqual(r.listeners('end').length, 2); // A.onend, A.cleanup +assert.strictEqual(r.listeners('close').length, 2); // A.onclose, A.cleanup +assert.strictEqual(d.listeners('end').length, 2); // B.onend, B.cleanup +// A.cleanup, B.onclose, B.cleanup +assert.strictEqual(d.listeners('close').length, 3); +assert.strictEqual(w.listeners('end').length, 0); +assert.strictEqual(w.listeners('close').length, 1); // B.cleanup + +r.emit('end'); +assert.strictEqual(d.endCalls, 1); +assert.strictEqual(w.endCalls, 0); +assert.strictEqual(r.listeners('end').length, 0); +assert.strictEqual(r.listeners('close').length, 0); +assert.strictEqual(d.listeners('end').length, 2); // B.onend, B.cleanup +assert.strictEqual(d.listeners('close').length, 2); // B.onclose, B.cleanup +assert.strictEqual(w.listeners('end').length, 0); +assert.strictEqual(w.listeners('close').length, 1); // B.cleanup + +d.emit('end'); +assert.strictEqual(d.endCalls, 1); +assert.strictEqual(w.endCalls, 1); +assert.strictEqual(r.listeners('end').length, 0); +assert.strictEqual(r.listeners('close').length, 0); +assert.strictEqual(d.listeners('end').length, 0); +assert.strictEqual(d.listeners('close').length, 0); +assert.strictEqual(w.listeners('end').length, 0); +assert.strictEqual(w.listeners('close').length, 0); diff --git a/test/js/node/test/parallel/test-stream-pipe-error-handling.js b/test/js/node/test/parallel/test-stream-pipe-error-handling.js new file mode 100644 index 0000000000..cf3a3699d0 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-error-handling.js @@ -0,0 +1,124 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Stream, PassThrough } = require('stream'); + +{ + const source = new Stream(); + const dest = new Stream(); + + source.pipe(dest); + + let gotErr = null; + source.on('error', function(err) { + gotErr = err; + }); + + const err = new Error('This stream turned into bacon.'); + source.emit('error', err); + assert.strictEqual(gotErr, err); +} + +{ + const source = new Stream(); + const dest = new Stream(); + + source.pipe(dest); + + const err = new Error('This stream turned into bacon.'); + + let gotErr = null; + try { + source.emit('error', err); + } catch (e) { + gotErr = e; + } + + assert.strictEqual(gotErr, err); +} + +{ + const R = Stream.Readable; + const W = Stream.Writable; + + const r = new R({ autoDestroy: false }); + const w = new W({ autoDestroy: false }); + let removed = false; + + r._read = common.mustCall(function() { + setTimeout(common.mustCall(function() { + assert(removed); + assert.throws(function() { + w.emit('error', new Error('fail')); + }, /^Error: fail$/); + }), 1); + }); + + w.on('error', myOnError); + r.pipe(w); + w.removeListener('error', myOnError); + removed = true; + + function myOnError() { + throw new Error('this should not happen'); + } +} + +{ + const R = Stream.Readable; + const W = Stream.Writable; + + const r = new R(); + const w = new W(); + let removed = false; + + r._read = common.mustCall(function() { + setTimeout(common.mustCall(function() { + assert(removed); + w.emit('error', new Error('fail')); + }), 1); + }); + + w.on('error', common.mustCall()); + w._write = () => {}; + + r.pipe(w); + // Removing some OTHER random listener should not do anything + w.removeListener('error', () => {}); + removed = true; +} + +{ + const _err = new Error('this should be handled'); + const destination = new PassThrough(); + destination.once('error', common.mustCall((err) => { + assert.strictEqual(err, _err); + })); + + const stream = new Stream(); + stream + .pipe(destination); + + destination.destroy(_err); +} diff --git a/test/js/node/test/parallel/test-stream-pipe-error-unhandled.js b/test/js/node/test/parallel/test-stream-pipe-error-unhandled.js new file mode 100644 index 0000000000..42c1ce77fe --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-error-unhandled.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); + +process.on('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err.message, 'asd'); +})); + +const r = new Readable({ + read() { + this.push('asd'); + } +}); +const w = new Writable({ + autoDestroy: true, + write() {} +}); + +r.pipe(w); +w.destroy(new Error('asd')); diff --git a/test/js/node/test/parallel/test-stream-pipe-event.js b/test/js/node/test/parallel/test-stream-pipe-event.js new file mode 100644 index 0000000000..d7772df6a1 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-event.js @@ -0,0 +1,51 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +function Writable() { + this.writable = true; + stream.Stream.call(this); +} +Object.setPrototypeOf(Writable.prototype, stream.Stream.prototype); +Object.setPrototypeOf(Writable, stream.Stream); + +function Readable() { + this.readable = true; + stream.Stream.call(this); +} +Object.setPrototypeOf(Readable.prototype, stream.Stream.prototype); +Object.setPrototypeOf(Readable, stream.Stream); + +let passed = false; + +const w = new Writable(); +w.on('pipe', function(src) { + passed = true; +}); + +const r = new Readable(); +r.pipe(w); + +assert.ok(passed); diff --git a/test/js/node/test/parallel/test-stream-pipe-flow-after-unpipe.js b/test/js/node/test/parallel/test-stream-pipe-flow-after-unpipe.js new file mode 100644 index 0000000000..048b7ea5e5 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-flow-after-unpipe.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); +const { Readable, Writable } = require('stream'); + +// Tests that calling .unpipe() un-blocks a stream that is paused because +// it is waiting on the writable side to finish a write(). + +const rs = new Readable({ + highWaterMark: 1, + // That this gets called at least 20 times is the real test here. + read: common.mustCallAtLeast(() => rs.push('foo'), 20) +}); + +const ws = new Writable({ + highWaterMark: 1, + write: common.mustCall(() => { + // Ignore the callback, this write() simply never finishes. + setImmediate(() => rs.unpipe(ws)); + }) +}); + +let chunks = 0; +rs.on('data', common.mustCallAtLeast(() => { + chunks++; + if (chunks >= 20) + rs.pause(); // Finish this test. +})); + +rs.pipe(ws); diff --git a/test/js/node/test/parallel/test-stream-pipe-flow.js b/test/js/node/test/parallel/test-stream-pipe-flow.js new file mode 100644 index 0000000000..1f2e8f54ce --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-flow.js @@ -0,0 +1,90 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable, PassThrough } = require('stream'); + +{ + let ticks = 17; + + const rs = new Readable({ + objectMode: true, + read: () => { + if (ticks-- > 0) + return process.nextTick(() => rs.push({})); + rs.push({}); + rs.push(null); + } + }); + + const ws = new Writable({ + highWaterMark: 0, + objectMode: true, + write: (data, end, cb) => setImmediate(cb) + }); + + rs.on('end', common.mustCall()); + ws.on('finish', common.mustCall()); + rs.pipe(ws); +} + +{ + let missing = 8; + + const rs = new Readable({ + objectMode: true, + read: () => { + if (missing--) rs.push({}); + else rs.push(null); + } + }); + + const pt = rs + .pipe(new PassThrough({ objectMode: true, highWaterMark: 2 })) + .pipe(new PassThrough({ objectMode: true, highWaterMark: 2 })); + + pt.on('end', () => { + wrapper.push(null); + }); + + const wrapper = new Readable({ + objectMode: true, + read: () => { + process.nextTick(() => { + let data = pt.read(); + if (data === null) { + pt.once('readable', () => { + data = pt.read(); + if (data !== null) wrapper.push(data); + }); + } else { + wrapper.push(data); + } + }); + } + }); + + wrapper.resume(); + wrapper.on('end', common.mustCall()); +} + +{ + // Only register drain if there is backpressure. + const rs = new Readable({ read() {} }); + + const pt = rs + .pipe(new PassThrough({ objectMode: true, highWaterMark: 2 })); + assert.strictEqual(pt.listenerCount('drain'), 0); + pt.on('finish', () => { + assert.strictEqual(pt.listenerCount('drain'), 0); + }); + + rs.push('asd'); + assert.strictEqual(pt.listenerCount('drain'), 0); + + process.nextTick(() => { + rs.push('asd'); + assert.strictEqual(pt.listenerCount('drain'), 0); + rs.push(null); + assert.strictEqual(pt.listenerCount('drain'), 0); + }); +} diff --git a/test/js/node/test/parallel/test-stream-pipe-manual-resume.js b/test/js/node/test/parallel/test-stream-pipe-manual-resume.js new file mode 100644 index 0000000000..08269acfd3 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-manual-resume.js @@ -0,0 +1,35 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); + +function test(throwCodeInbetween) { + // Check that a pipe does not stall if .read() is called unexpectedly + // (i.e. the stream is not resumed by the pipe). + + const n = 1000; + let counter = n; + const rs = stream.Readable({ + objectMode: true, + read: common.mustCallAtLeast(() => { + if (--counter >= 0) + rs.push({ counter }); + else + rs.push(null); + }, n) + }); + + const ws = stream.Writable({ + objectMode: true, + write: common.mustCall((data, enc, cb) => { + setImmediate(cb); + }, n) + }); + + setImmediate(() => throwCodeInbetween(rs, ws)); + + rs.pipe(ws); +} + +test((rs) => rs.read()); +test((rs) => rs.resume()); +test(() => 0); diff --git a/test/js/node/test/parallel/test-stream-pipe-multiple-pipes.js b/test/js/node/test/parallel/test-stream-pipe-multiple-pipes.js new file mode 100644 index 0000000000..890c274b9d --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-multiple-pipes.js @@ -0,0 +1,51 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +const readable = new stream.Readable({ + read: () => {} +}); + +const writables = []; + +for (let i = 0; i < 5; i++) { + const target = new stream.Writable({ + write: common.mustCall((chunk, encoding, callback) => { + target.output.push(chunk); + callback(); + }, 1) + }); + target.output = []; + + target.on('pipe', common.mustCall()); + readable.pipe(target); + + + writables.push(target); +} + +const input = Buffer.from([1, 2, 3, 4, 5]); + +readable.push(input); + +// The pipe() calls will postpone emission of the 'resume' event using nextTick, +// so no data will be available to the writable streams until then. +process.nextTick(common.mustCall(() => { + for (const target of writables) { + assert.deepStrictEqual(target.output, [input]); + + target.on('unpipe', common.mustCall()); + readable.unpipe(target); + } + + readable.push('something else'); // This does not get through. + readable.push(null); + readable.resume(); // Make sure the 'end' event gets emitted. +})); + +readable.on('end', common.mustCall(() => { + for (const target of writables) { + assert.deepStrictEqual(target.output, [input]); + } +})); diff --git a/test/js/node/test/parallel/test-stream-pipe-needDrain.js b/test/js/node/test/parallel/test-stream-pipe-needDrain.js new file mode 100644 index 0000000000..7faf45417a --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-needDrain.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); + +// Pipe should pause temporarily if writable needs drain. +{ + const w = new Writable({ + write(buf, encoding, callback) { + process.nextTick(callback); + }, + highWaterMark: 1 + }); + + while (w.write('asd')); + + assert.strictEqual(w.writableNeedDrain, true); + + const r = new Readable({ + read() { + this.push('asd'); + this.push(null); + } + }); + + r.on('pause', common.mustCall(2)); + r.on('end', common.mustCall()); + + r.pipe(w); +} diff --git a/test/js/node/test/parallel/test-stream-pipe-same-destination-twice.js b/test/js/node/test/parallel/test-stream-pipe-same-destination-twice.js new file mode 100644 index 0000000000..ff71639588 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-same-destination-twice.js @@ -0,0 +1,78 @@ +'use strict'; +const common = require('../common'); + +// Regression test for https://github.com/nodejs/node/issues/12718. +// Tests that piping a source stream twice to the same destination stream +// works, and that a subsequent unpipe() call only removes the pipe *once*. +const assert = require('assert'); +const { PassThrough, Writable } = require('stream'); + +{ + const passThrough = new PassThrough(); + const dest = new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + assert.strictEqual(`${chunk}`, 'foobar'); + cb(); + }) + }); + + passThrough.pipe(dest); + passThrough.pipe(dest); + + assert.strictEqual(passThrough._events.data.length, 2); + assert.strictEqual(passThrough._readableState.pipes.length, 2); + assert.strictEqual(passThrough._readableState.pipes[0], dest); + assert.strictEqual(passThrough._readableState.pipes[1], dest); + + passThrough.unpipe(dest); + + assert.strictEqual(passThrough._events.data.length, 1); + assert.strictEqual(passThrough._readableState.pipes.length, 1); + assert.deepStrictEqual(passThrough._readableState.pipes, [dest]); + + passThrough.write('foobar'); + passThrough.pipe(dest); +} + +{ + const passThrough = new PassThrough(); + const dest = new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + assert.strictEqual(`${chunk}`, 'foobar'); + cb(); + }, 2) + }); + + passThrough.pipe(dest); + passThrough.pipe(dest); + + assert.strictEqual(passThrough._events.data.length, 2); + assert.strictEqual(passThrough._readableState.pipes.length, 2); + assert.strictEqual(passThrough._readableState.pipes[0], dest); + assert.strictEqual(passThrough._readableState.pipes[1], dest); + + passThrough.write('foobar'); +} + +{ + const passThrough = new PassThrough(); + const dest = new Writable({ + write: common.mustNotCall() + }); + + passThrough.pipe(dest); + passThrough.pipe(dest); + + assert.strictEqual(passThrough._events.data.length, 2); + assert.strictEqual(passThrough._readableState.pipes.length, 2); + assert.strictEqual(passThrough._readableState.pipes[0], dest); + assert.strictEqual(passThrough._readableState.pipes[1], dest); + + passThrough.unpipe(dest); + passThrough.unpipe(dest); + + assert.strictEqual(passThrough._events.data, undefined); + assert.strictEqual(passThrough._readableState.pipes.length, 0); + + passThrough.write('foobar'); +} diff --git a/test/js/node/test/parallel/test-stream-pipe-unpipe-streams.js b/test/js/node/test/parallel/test-stream-pipe-unpipe-streams.js new file mode 100644 index 0000000000..74c4353993 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipe-unpipe-streams.js @@ -0,0 +1,95 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { Readable, Writable } = require('stream'); + +const source = Readable({ read: () => {} }); +const dest1 = Writable({ write: () => {} }); +const dest2 = Writable({ write: () => {} }); + +source.pipe(dest1); +source.pipe(dest2); + +dest1.on('unpipe', common.mustCall()); +dest2.on('unpipe', common.mustCall()); + +assert.strictEqual(source._readableState.pipes[0], dest1); +assert.strictEqual(source._readableState.pipes[1], dest2); +assert.strictEqual(source._readableState.pipes.length, 2); + +// Should be able to unpipe them in the reverse order that they were piped. + +source.unpipe(dest2); + +assert.deepStrictEqual(source._readableState.pipes, [dest1]); +assert.notStrictEqual(source._readableState.pipes, dest2); + +dest2.on('unpipe', common.mustNotCall()); +source.unpipe(dest2); + +source.unpipe(dest1); + +assert.strictEqual(source._readableState.pipes.length, 0); + +{ + // Test `cleanup()` if we unpipe all streams. + const source = Readable({ read: () => {} }); + const dest1 = Writable({ write: () => {} }); + const dest2 = Writable({ write: () => {} }); + + let destCount = 0; + const srcCheckEventNames = ['end', 'data']; + const destCheckEventNames = ['close', 'finish', 'drain', 'error', 'unpipe']; + + const checkSrcCleanup = common.mustCall(() => { + assert.strictEqual(source._readableState.pipes.length, 0); + assert.strictEqual(source._readableState.flowing, false); + for (const eventName of srcCheckEventNames) { + assert.strictEqual( + source.listenerCount(eventName), 0, + `source's '${eventName}' event listeners not removed` + ); + } + }); + + function checkDestCleanup(dest) { + const currentDestId = ++destCount; + source.pipe(dest); + + const unpipeChecker = common.mustCall(() => { + assert.deepStrictEqual( + dest.listeners('unpipe'), [unpipeChecker], + `destination{${currentDestId}} should have a 'unpipe' event ` + + 'listener which is `unpipeChecker`' + ); + dest.removeListener('unpipe', unpipeChecker); + for (const eventName of destCheckEventNames) { + assert.strictEqual( + dest.listenerCount(eventName), 0, + `destination{${currentDestId}}'s '${eventName}' event ` + + 'listeners not removed' + ); + } + + if (--destCount === 0) + checkSrcCleanup(); + }); + + dest.on('unpipe', unpipeChecker); + } + + checkDestCleanup(dest1); + checkDestCleanup(dest2); + source.unpipe(); +} + +{ + const src = Readable({ read: () => {} }); + const dst = Writable({ write: () => {} }); + src.pipe(dst); + src.on('resume', common.mustCall(() => { + src.on('pause', common.mustCall()); + src.unpipe(dst); + })); +} diff --git a/test/js/node/test/parallel/test-stream-pipeline-async-iterator.js b/test/js/node/test/parallel/test-stream-pipeline-async-iterator.js new file mode 100644 index 0000000000..06a2ed6ca8 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipeline-async-iterator.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +const { Readable, PassThrough, pipeline } = require('stream'); +const assert = require('assert'); + +const _err = new Error('kaboom'); + +async function run() { + const source = new Readable({ + read() { + } + }); + source.push('hello'); + source.push('world'); + + setImmediate(() => { source.destroy(_err); }); + + const iterator = pipeline( + source, + new PassThrough(), + () => {}); + + iterator.setEncoding('utf8'); + + for await (const k of iterator) { + assert.strictEqual(k, 'helloworld'); + } +} + +run().catch(common.mustCall((err) => assert.strictEqual(err, _err))); diff --git a/test/js/node/test/parallel/test-stream-pipeline-listeners.js b/test/js/node/test/parallel/test-stream-pipeline-listeners.js new file mode 100644 index 0000000000..81e287b77c --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipeline-listeners.js @@ -0,0 +1,76 @@ +'use strict'; + +const common = require('../common'); +const { pipeline, Duplex, PassThrough, Writable } = require('stream'); +const assert = require('assert'); + +process.on('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err.message, 'no way'); +}, 2)); + +// Ensure that listeners is removed if last stream is readable +// And other stream's listeners unchanged +const a = new PassThrough(); +a.end('foobar'); +const b = new Duplex({ + write(chunk, encoding, callback) { + callback(); + } +}); +pipeline(a, b, common.mustCall((error) => { + if (error) { + assert.ifError(error); + } + + assert(a.listenerCount('error') > 0); + assert.strictEqual(b.listenerCount('error'), 0); + setTimeout(() => { + assert.strictEqual(b.listenerCount('error'), 0); + b.destroy(new Error('no way')); + }, 100); +})); + +// Async generators +const c = new PassThrough(); +c.end('foobar'); +const d = pipeline( + c, + async function* (source) { + for await (const chunk of source) { + yield String(chunk).toUpperCase(); + } + }, + common.mustCall((error) => { + if (error) { + assert.ifError(error); + } + + assert(c.listenerCount('error') > 0); + assert.strictEqual(d.listenerCount('error'), 0); + setTimeout(() => { + assert.strictEqual(b.listenerCount('error'), 0); + d.destroy(new Error('no way')); + }, 100); + }) +); + +// If last stream is not readable, will not throw and remove listeners +const e = new PassThrough(); +e.end('foobar'); +const f = new Writable({ + write(chunk, encoding, callback) { + callback(); + } +}); +pipeline(e, f, common.mustCall((error) => { + if (error) { + assert.ifError(error); + } + + assert(e.listenerCount('error') > 0); + assert(f.listenerCount('error') > 0); + setTimeout(() => { + assert(f.listenerCount('error') > 0); + f.destroy(new Error('no way')); + }, 100); +})); diff --git a/test/js/node/test/parallel/test-stream-pipeline-process.js b/test/js/node/test/parallel/test-stream-pipeline-process.js new file mode 100644 index 0000000000..a535e7263e --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipeline-process.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const os = require('os'); + +if (process.argv[2] === 'child') { + const { pipeline } = require('stream'); + pipeline( + process.stdin, + process.stdout, + common.mustSucceed() + ); +} else { + const cp = require('child_process'); + cp.exec([ + 'echo', + 'hello', + '|', + `"${process.execPath}"`, + `"${__filename}"`, + 'child', + ].join(' '), common.mustSucceed((stdout) => { + assert.strictEqual(stdout.split(os.EOL).shift().trim(), 'hello'); + })); +} diff --git a/test/js/node/test/parallel/test-stream-pipeline-queued-end-in-destroy.js b/test/js/node/test/parallel/test-stream-pipeline-queued-end-in-destroy.js new file mode 100644 index 0000000000..480e5b7f72 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipeline-queued-end-in-destroy.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable, Duplex, pipeline } = require('stream'); + +// Test that the callback for pipeline() is called even when the ._destroy() +// method of the stream places an .end() request to itself that does not +// get processed before the destruction of the stream (i.e. the 'close' event). +// Refs: https://github.com/nodejs/node/issues/24456 + +const readable = new Readable({ + read: common.mustCall() +}); + +const duplex = new Duplex({ + write(chunk, enc, cb) { + // Simulate messages queueing up. + }, + read() {}, + destroy(err, cb) { + // Call end() from inside the destroy() method, like HTTP/2 streams + // do at the time of writing. + this.end(); + cb(err); + } +}); + +duplex.on('finished', common.mustNotCall()); + +pipeline(readable, duplex, common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_STREAM_PREMATURE_CLOSE'); +})); + +// Write one chunk of data, and destroy the stream later. +// That should trigger the pipeline destruction. +readable.push('foo'); +setImmediate(() => { + readable.destroy(); +}); diff --git a/test/js/node/test/parallel/test-stream-pipeline-uncaught.js b/test/js/node/test/parallel/test-stream-pipeline-uncaught.js new file mode 100644 index 0000000000..8aa1c47b7f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipeline-uncaught.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); +const { + pipeline, + PassThrough +} = require('stream'); +const assert = require('assert'); + +process.on('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err.message, 'error'); +})); + +// Ensure that pipeline that ends with Promise +// still propagates error to uncaughtException. +const s = new PassThrough(); +s.end('data'); +pipeline(s, async function(source) { + for await (const chunk of source) { } // eslint-disable-line no-unused-vars, no-empty +}, common.mustSucceed(() => { + throw new Error('error'); +})); diff --git a/test/js/node/test/parallel/test-stream-pipeline-with-empty-string.js b/test/js/node/test/parallel/test-stream-pipeline-with-empty-string.js new file mode 100644 index 0000000000..5df1ff9edf --- /dev/null +++ b/test/js/node/test/parallel/test-stream-pipeline-with-empty-string.js @@ -0,0 +1,18 @@ +'use strict'; + +const common = require('../common'); +const { + pipeline, + PassThrough +} = require('stream'); + + +async function runTest() { + await pipeline( + '', + new PassThrough({ objectMode: true }), + common.mustCall(), + ); +} + +runTest().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-stream-preprocess.js b/test/js/node/test/parallel/test-stream-preprocess.js new file mode 100644 index 0000000000..d42c2fd63e --- /dev/null +++ b/test/js/node/test/parallel/test-stream-preprocess.js @@ -0,0 +1,60 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const fs = require('fs'); +const rl = require('readline'); +const fixtures = require('../common/fixtures'); + +const BOM = '\uFEFF'; + +// Get the data using a non-stream way to compare with the streamed data. +const modelData = fixtures.readSync('file-to-read-without-bom.txt', 'utf8'); +const modelDataFirstCharacter = modelData[0]; + +// Detect the number of forthcoming 'line' events for mustCall() 'expected' arg. +const lineCount = modelData.match(/\n/g).length; + +// Ensure both without-bom and with-bom test files are textwise equal. +assert.strictEqual(fixtures.readSync('file-to-read-with-bom.txt', 'utf8'), + `${BOM}${modelData}` +); + +// An unjustified BOM stripping with a non-BOM character unshifted to a stream. +const inputWithoutBOM = + fs.createReadStream(fixtures.path('file-to-read-without-bom.txt'), 'utf8'); + +inputWithoutBOM.once('readable', common.mustCall(() => { + const maybeBOM = inputWithoutBOM.read(1); + assert.strictEqual(maybeBOM, modelDataFirstCharacter); + assert.notStrictEqual(maybeBOM, BOM); + + inputWithoutBOM.unshift(maybeBOM); + + let streamedData = ''; + rl.createInterface({ + input: inputWithoutBOM, + }).on('line', common.mustCall((line) => { + streamedData += `${line}\n`; + }, lineCount)).on('close', common.mustCall(() => { + assert.strictEqual(streamedData, modelData); + })); +})); + +// A justified BOM stripping. +const inputWithBOM = + fs.createReadStream(fixtures.path('file-to-read-with-bom.txt'), 'utf8'); + +inputWithBOM.once('readable', common.mustCall(() => { + const maybeBOM = inputWithBOM.read(1); + assert.strictEqual(maybeBOM, BOM); + + let streamedData = ''; + rl.createInterface({ + input: inputWithBOM, + }).on('line', common.mustCall((line) => { + streamedData += `${line}\n`; + }, lineCount)).on('close', common.mustCall(() => { + assert.strictEqual(streamedData, modelData); + })); +})); diff --git a/test/js/node/test/parallel/test-stream-push-order.js b/test/js/node/test/parallel/test-stream-push-order.js new file mode 100644 index 0000000000..f026cb5b8a --- /dev/null +++ b/test/js/node/test/parallel/test-stream-push-order.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const Readable = require('stream').Readable; +const assert = require('assert'); + +const s = new Readable({ + highWaterMark: 20, + encoding: 'ascii' +}); + +const list = ['1', '2', '3', '4', '5', '6']; + +s._read = function(n) { + const one = list.shift(); + if (!one) { + s.push(null); + } else { + const two = list.shift(); + s.push(one); + s.push(two); + } +}; + +s.read(0); + +// ACTUALLY [1, 3, 5, 6, 4, 2] + +process.on('exit', function() { + assert.strictEqual(s.readableBuffer.join(','), '1,2,3,4,5,6'); + console.log('ok'); +}); diff --git a/test/js/node/test/parallel/test-stream-push-strings.js b/test/js/node/test/parallel/test-stream-push-strings.js new file mode 100644 index 0000000000..d582c8add0 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-push-strings.js @@ -0,0 +1,67 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const Readable = require('stream').Readable; + +class MyStream extends Readable { + constructor(options) { + super(options); + this._chunks = 3; + } + + _read(n) { + switch (this._chunks--) { + case 0: + return this.push(null); + case 1: + return setTimeout(() => { + this.push('last chunk'); + }, 100); + case 2: + return this.push('second to last chunk'); + case 3: + return process.nextTick(() => { + this.push('first chunk'); + }); + default: + throw new Error('?'); + } + } +} + +const ms = new MyStream(); +const results = []; +ms.on('readable', function() { + let chunk; + while (null !== (chunk = ms.read())) + results.push(String(chunk)); +}); + +const expect = [ 'first chunksecond to last chunk', 'last chunk' ]; +process.on('exit', function() { + assert.strictEqual(ms._chunks, -1); + assert.deepStrictEqual(results, expect); + console.log('ok'); +}); diff --git a/test/js/node/test/parallel/test-stream-readable-aborted.js b/test/js/node/test/parallel/test-stream-readable-aborted.js new file mode 100644 index 0000000000..9badffc51f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-aborted.js @@ -0,0 +1,66 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable, Duplex } = require('stream'); + +{ + const readable = new Readable({ + read() { + } + }); + assert.strictEqual(readable.readableAborted, false); + readable.destroy(); + assert.strictEqual(readable.readableAborted, true); +} + +{ + const readable = new Readable({ + read() { + } + }); + assert.strictEqual(readable.readableAborted, false); + readable.push(null); + readable.destroy(); + assert.strictEqual(readable.readableAborted, true); +} + +{ + const readable = new Readable({ + read() { + } + }); + assert.strictEqual(readable.readableAborted, false); + readable.push('asd'); + readable.destroy(); + assert.strictEqual(readable.readableAborted, true); +} + +{ + const readable = new Readable({ + read() { + } + }); + assert.strictEqual(readable.readableAborted, false); + readable.push('asd'); + readable.push(null); + assert.strictEqual(readable.readableAborted, false); + readable.on('end', common.mustCall(() => { + assert.strictEqual(readable.readableAborted, false); + readable.destroy(); + assert.strictEqual(readable.readableAborted, false); + queueMicrotask(() => { + assert.strictEqual(readable.readableAborted, false); + }); + })); + readable.resume(); +} + +{ + const duplex = new Duplex({ + readable: false, + write() {} + }); + duplex.destroy(); + assert.strictEqual(duplex.readableAborted, false); +} diff --git a/test/js/node/test/parallel/test-stream-readable-add-chunk-during-data.js b/test/js/node/test/parallel/test-stream-readable-add-chunk-during-data.js new file mode 100644 index 0000000000..6c36359790 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-add-chunk-during-data.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +// Verify that .push() and .unshift() can be called from 'data' listeners. + +for (const method of ['push', 'unshift']) { + const r = new Readable({ read() {} }); + r.once('data', common.mustCall((chunk) => { + assert.strictEqual(r.readableLength, 0); + r[method](chunk); + assert.strictEqual(r.readableLength, chunk.length); + + r.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk.toString(), 'Hello, world'); + })); + })); + + r.push('Hello, world'); +} diff --git a/test/js/node/test/parallel/test-stream-readable-constructor-set-methods.js b/test/js/node/test/parallel/test-stream-readable-constructor-set-methods.js new file mode 100644 index 0000000000..1b9f0496b9 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-constructor-set-methods.js @@ -0,0 +1,11 @@ +'use strict'; +const common = require('../common'); + +const Readable = require('stream').Readable; + +const _read = common.mustCall(function _read(n) { + this.push(null); +}); + +const r = new Readable({ read: _read }); +r.resume(); diff --git a/test/js/node/test/parallel/test-stream-readable-data.js b/test/js/node/test/parallel/test-stream-readable-data.js new file mode 100644 index 0000000000..277adddde6 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-data.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); + +const { Readable } = require('stream'); + +const readable = new Readable({ + read() {} +}); + +function read() {} + +readable.setEncoding('utf8'); +readable.on('readable', read); +readable.removeListener('readable', read); + +process.nextTick(function() { + readable.on('data', common.mustCall()); + readable.push('hello'); +}); diff --git a/test/js/node/test/parallel/test-stream-readable-destroy.js b/test/js/node/test/parallel/test-stream-readable-destroy.js new file mode 100644 index 0000000000..fb7da632f7 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-destroy.js @@ -0,0 +1,405 @@ +'use strict'; + +const common = require('../common'); +const { Readable, addAbortSignal } = require('stream'); +const assert = require('assert'); + +{ + const read = new Readable({ + read() {} + }); + read.resume(); + + read.on('close', common.mustCall()); + + read.destroy(); + assert.strictEqual(read.errored, null); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {} + }); + read.resume(); + + const expected = new Error('kaboom'); + + read.on('end', common.mustNotCall('no end event')); + read.on('close', common.mustCall()); + read.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + read.destroy(expected); + assert.strictEqual(read.errored, expected); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {} + }); + + read._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, expected); + cb(err); + }); + + const expected = new Error('kaboom'); + + read.on('end', common.mustNotCall('no end event')); + read.on('close', common.mustCall()); + read.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + read.destroy(expected); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {}, + destroy: common.mustCall(function(err, cb) { + assert.strictEqual(err, expected); + cb(); + }) + }); + + const expected = new Error('kaboom'); + + read.on('end', common.mustNotCall('no end event')); + + // Error is swallowed by the custom _destroy + read.on('error', common.mustNotCall('no error event')); + read.on('close', common.mustCall()); + + read.destroy(expected); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {} + }); + + read._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + cb(); + }); + + read.destroy(); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {} + }); + read.resume(); + + read._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + process.nextTick(() => { + this.push(null); + cb(); + }); + }); + + const fail = common.mustNotCall('no end event'); + + read.on('end', fail); + read.on('close', common.mustCall()); + + read.destroy(); + + read.removeListener('end', fail); + read.on('end', common.mustNotCall()); + assert.strictEqual(read.destroyed, true); +} + +{ + const read = new Readable({ + read() {} + }); + + const expected = new Error('kaboom'); + + read._destroy = common.mustCall(function(err, cb) { + assert.strictEqual(err, null); + cb(expected); + }); + + let ticked = false; + read.on('end', common.mustNotCall('no end event')); + read.on('error', common.mustCall((err) => { + assert.strictEqual(ticked, true); + assert.strictEqual(read._readableState.errorEmitted, true); + assert.strictEqual(read._readableState.errored, expected); + assert.strictEqual(err, expected); + })); + + read.destroy(); + assert.strictEqual(read._readableState.errorEmitted, false); + assert.strictEqual(read._readableState.errored, expected); + assert.strictEqual(read.destroyed, true); + ticked = true; +} + +{ + const read = new Readable({ + read() {} + }); + read.resume(); + + read.destroyed = true; + assert.strictEqual(read.destroyed, true); + + // The internal destroy() mechanism should not be triggered + read.on('end', common.mustNotCall()); + read.destroy(); +} + +{ + function MyReadable() { + assert.strictEqual(this.destroyed, false); + this.destroyed = false; + Readable.call(this); + } + + Object.setPrototypeOf(MyReadable.prototype, Readable.prototype); + Object.setPrototypeOf(MyReadable, Readable); + + new MyReadable(); +} + +{ + // Destroy and destroy callback + const read = new Readable({ + read() {} + }); + read.resume(); + + const expected = new Error('kaboom'); + + let ticked = false; + read.on('close', common.mustCall(() => { + assert.strictEqual(read._readableState.errorEmitted, true); + assert.strictEqual(ticked, true); + })); + read.on('error', common.mustCall((err) => { + assert.strictEqual(err, expected); + })); + + assert.strictEqual(read._readableState.errored, null); + assert.strictEqual(read._readableState.errorEmitted, false); + + read.destroy(expected, common.mustCall(function(err) { + assert.strictEqual(read._readableState.errored, expected); + assert.strictEqual(err, expected); + })); + assert.strictEqual(read._readableState.errorEmitted, false); + assert.strictEqual(read._readableState.errored, expected); + ticked = true; +} + +{ + const readable = new Readable({ + destroy: common.mustCall(function(err, cb) { + process.nextTick(cb, new Error('kaboom 1')); + }), + read() {} + }); + + let ticked = false; + readable.on('close', common.mustCall(() => { + assert.strictEqual(ticked, true); + assert.strictEqual(readable._readableState.errorEmitted, true); + })); + readable.on('error', common.mustCall((err) => { + assert.strictEqual(ticked, true); + assert.strictEqual(err.message, 'kaboom 1'); + assert.strictEqual(readable._readableState.errorEmitted, true); + })); + + readable.destroy(); + assert.strictEqual(readable.destroyed, true); + assert.strictEqual(readable._readableState.errored, null); + assert.strictEqual(readable._readableState.errorEmitted, false); + + // Test case where `readable.destroy()` is called again with an error before + // the `_destroy()` callback is called. + readable.destroy(new Error('kaboom 2')); + assert.strictEqual(readable._readableState.errorEmitted, false); + assert.strictEqual(readable._readableState.errored, null); + + ticked = true; +} + +{ + const read = new Readable({ + read() {} + }); + + read.destroy(); + read.push('hi'); + read.on('data', common.mustNotCall()); +} + +{ + const read = new Readable({ + read: common.mustNotCall() + }); + read.destroy(); + assert.strictEqual(read.destroyed, true); + read.read(); +} + +{ + const read = new Readable({ + autoDestroy: false, + read() { + this.push(null); + this.push('asd'); + } + }); + + read.on('error', common.mustCall(() => { + assert(read._readableState.errored); + })); + read.resume(); +} + +{ + const controller = new AbortController(); + const read = addAbortSignal(controller.signal, new Readable({ + read() { + this.push('asd'); + }, + })); + + read.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); + controller.abort(); + read.on('data', common.mustNotCall()); +} + +{ + const controller = new AbortController(); + const read = new Readable({ + signal: controller.signal, + read() { + this.push('asd'); + }, + }); + + read.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); + controller.abort(); + read.on('data', common.mustNotCall()); +} + +{ + const controller = new AbortController(); + const read = addAbortSignal(controller.signal, new Readable({ + objectMode: true, + read() { + return false; + } + })); + read.push('asd'); + + read.on('error', common.mustCall((e) => { + assert.strictEqual(e.name, 'AbortError'); + })); + assert.rejects((async () => { + // eslint-disable-next-line no-unused-vars, no-empty + for await (const chunk of read) { } + })(), /AbortError/).then(common.mustCall()); + setTimeout(() => controller.abort(), 0); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.on('error', common.mustCall((e) => { + read.push('asd'); + read.read(); + })); + read.on('close', common.mustCall((e) => { + read.push('asd'); + read.read(); + })); + read.destroy(new Error('asd')); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.on('close', common.mustCall((e) => { + read.push('asd'); + read.read(); + })); + read.destroy(); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.on('close', common.mustCall((e) => { + read.push('asd'); + read.unshift('asd'); + })); + read.destroy(); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.destroy(); + read.unshift('asd'); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.resume(); + read.on('data', common.mustNotCall()); + read.on('close', common.mustCall((e) => { + read.push('asd'); + })); + read.destroy(); +} + +{ + const read = new Readable({ + read() { + }, + }); + + read.on('data', common.mustNotCall()); + read.destroy(); + read.push('asd'); +} diff --git a/test/js/node/test/parallel/test-stream-readable-didRead.js b/test/js/node/test/parallel/test-stream-readable-didRead.js new file mode 100644 index 0000000000..878340ba19 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-didRead.js @@ -0,0 +1,111 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { isDisturbed, isErrored, Readable } = require('stream'); + +function noop() {} + +function check(readable, data, fn) { + assert.strictEqual(readable.readableDidRead, false); + assert.strictEqual(isDisturbed(readable), false); + assert.strictEqual(isErrored(readable), false); + if (data === -1) { + readable.on('error', common.mustCall(() => { + assert.strictEqual(isErrored(readable), true); + })); + readable.on('data', common.mustNotCall()); + readable.on('end', common.mustNotCall()); + } else { + readable.on('error', common.mustNotCall()); + if (data === -2) { + readable.on('end', common.mustNotCall()); + } else { + readable.on('end', common.mustCall()); + } + if (data > 0) { + readable.on('data', common.mustCallAtLeast(data)); + } else { + readable.on('data', common.mustNotCall()); + } + } + readable.on('close', common.mustCall()); + fn(); + setImmediate(() => { + assert.strictEqual(readable.readableDidRead, data > 0); + if (data > 0) { + assert.strictEqual(isDisturbed(readable), true); + } + }); +} + +{ + const readable = new Readable({ + read() { + this.push(null); + } + }); + check(readable, 0, () => { + readable.read(); + }); +} + +{ + const readable = new Readable({ + read() { + this.push(null); + } + }); + check(readable, 0, () => { + readable.resume(); + }); +} + +{ + const readable = new Readable({ + read() { + this.push(null); + } + }); + check(readable, -2, () => { + readable.destroy(); + }); +} + +{ + const readable = new Readable({ + read() { + this.push(null); + } + }); + + check(readable, -1, () => { + readable.destroy(new Error()); + }); +} + +{ + const readable = new Readable({ + read() { + this.push('data'); + this.push(null); + } + }); + + check(readable, 1, () => { + readable.on('data', noop); + }); +} + +{ + const readable = new Readable({ + read() { + this.push('data'); + this.push(null); + } + }); + + check(readable, 1, () => { + readable.on('data', noop); + readable.off('data', noop); + }); +} diff --git a/test/js/node/test/parallel/test-stream-readable-emit-readable-short-stream.js b/test/js/node/test/parallel/test-stream-readable-emit-readable-short-stream.js new file mode 100644 index 0000000000..d8b84bfbe7 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-emit-readable-short-stream.js @@ -0,0 +1,146 @@ +'use strict'; + +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +{ + const r = new stream.Readable({ + read: common.mustCall(function() { + this.push('content'); + this.push(null); + }) + }); + + const t = new stream.Transform({ + transform: common.mustCall(function(chunk, encoding, callback) { + this.push(chunk); + return callback(); + }), + flush: common.mustCall(function(callback) { + return callback(); + }) + }); + + r.pipe(t); + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + + assert.strictEqual(chunk.toString(), 'content'); + } + }, 2)); +} + +{ + const t = new stream.Transform({ + transform: common.mustCall(function(chunk, encoding, callback) { + this.push(chunk); + return callback(); + }), + flush: common.mustCall(function(callback) { + return callback(); + }) + }); + + t.end('content'); + + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + assert.strictEqual(chunk.toString(), 'content'); + } + })); +} + +{ + const t = new stream.Transform({ + transform: common.mustCall(function(chunk, encoding, callback) { + this.push(chunk); + return callback(); + }), + flush: common.mustCall(function(callback) { + return callback(); + }) + }); + + t.write('content'); + t.end(); + + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + assert.strictEqual(chunk.toString(), 'content'); + } + })); +} + +{ + const t = new stream.Readable({ + read() { + } + }); + + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + assert.strictEqual(chunk.toString(), 'content'); + } + })); + + t.push('content'); + t.push(null); +} + +{ + const t = new stream.Readable({ + read() { + } + }); + + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + assert.strictEqual(chunk.toString(), 'content'); + } + }, 2)); + + process.nextTick(() => { + t.push('content'); + t.push(null); + }); +} + +{ + const t = new stream.Transform({ + transform: common.mustCall(function(chunk, encoding, callback) { + this.push(chunk); + return callback(); + }), + flush: common.mustCall(function(callback) { + return callback(); + }) + }); + + t.on('readable', common.mustCall(function() { + while (true) { + const chunk = t.read(); + if (!chunk) + break; + assert.strictEqual(chunk.toString(), 'content'); + } + }, 2)); + + t.write('content'); + t.end(); +} diff --git a/test/js/node/test/parallel/test-stream-readable-emittedReadable.js b/test/js/node/test/parallel/test-stream-readable-emittedReadable.js new file mode 100644 index 0000000000..ba613f9e9f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-emittedReadable.js @@ -0,0 +1,73 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const Readable = require('stream').Readable; + +const readable = new Readable({ + read: () => {} +}); + +// Initialized to false. +assert.strictEqual(readable._readableState.emittedReadable, false); + +const expected = [Buffer.from('foobar'), Buffer.from('quo'), null]; +readable.on('readable', common.mustCall(() => { + // emittedReadable should be true when the readable event is emitted + assert.strictEqual(readable._readableState.emittedReadable, true); + assert.deepStrictEqual(readable.read(), expected.shift()); + // emittedReadable is reset to false during read() + assert.strictEqual(readable._readableState.emittedReadable, false); +}, 3)); + +// When the first readable listener is just attached, +// emittedReadable should be false +assert.strictEqual(readable._readableState.emittedReadable, false); + +// These trigger a single 'readable', as things are batched up +process.nextTick(common.mustCall(() => { + readable.push('foo'); +})); +process.nextTick(common.mustCall(() => { + readable.push('bar'); +})); + +// These triggers two readable events +setImmediate(common.mustCall(() => { + readable.push('quo'); + process.nextTick(common.mustCall(() => { + readable.push(null); + })); +})); + +const noRead = new Readable({ + read: () => {} +}); + +noRead.on('readable', common.mustCall(() => { + // emittedReadable should be true when the readable event is emitted + assert.strictEqual(noRead._readableState.emittedReadable, true); + noRead.read(0); + // emittedReadable is not reset during read(0) + assert.strictEqual(noRead._readableState.emittedReadable, true); +})); + +noRead.push('foo'); +noRead.push(null); + +const flowing = new Readable({ + read: () => {} +}); + +flowing.on('data', common.mustCall(() => { + // When in flowing mode, emittedReadable is always false. + assert.strictEqual(flowing._readableState.emittedReadable, false); + flowing.read(); + assert.strictEqual(flowing._readableState.emittedReadable, false); +}, 3)); + +flowing.push('foooo'); +flowing.push('bar'); +flowing.push('quo'); +process.nextTick(common.mustCall(() => { + flowing.push(null); +})); diff --git a/test/js/node/test/parallel/test-stream-readable-end-destroyed.js b/test/js/node/test/parallel/test-stream-readable-end-destroyed.js new file mode 100644 index 0000000000..4b60bf4614 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-end-destroyed.js @@ -0,0 +1,17 @@ +'use strict'; + +const common = require('../common'); +const { Readable } = require('stream'); + +{ + // Don't emit 'end' after 'close'. + + const r = new Readable(); + + r.on('end', common.mustNotCall()); + r.resume(); + r.destroy(); + r.on('close', common.mustCall(() => { + r.push(null); + })); +} diff --git a/test/js/node/test/parallel/test-stream-readable-ended.js b/test/js/node/test/parallel/test-stream-readable-ended.js new file mode 100644 index 0000000000..bdd714c955 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-ended.js @@ -0,0 +1,46 @@ +'use strict'; + +const common = require('../common'); +const { Readable } = require('stream'); +const assert = require('assert'); + +// basic +{ + // Find it on Readable.prototype + assert(Object.hasOwn(Readable.prototype, 'readableEnded')); +} + +// event +{ + const readable = new Readable(); + + readable._read = () => { + // The state ended should start in false. + assert.strictEqual(readable.readableEnded, false); + readable.push('asd'); + assert.strictEqual(readable.readableEnded, false); + readable.push(null); + assert.strictEqual(readable.readableEnded, false); + }; + + readable.on('end', common.mustCall(() => { + assert.strictEqual(readable.readableEnded, true); + })); + + readable.on('data', common.mustCall(() => { + assert.strictEqual(readable.readableEnded, false); + })); +} + +// Verifies no `error` triggered on multiple .push(null) invocations +{ + const readable = new Readable(); + + readable.on('readable', () => { readable.read(); }); + readable.on('error', common.mustNotCall()); + readable.on('end', common.mustCall()); + + readable.push('a'); + readable.push(null); + readable.push(null); +} diff --git a/test/js/node/test/parallel/test-stream-readable-error-end.js b/test/js/node/test/parallel/test-stream-readable-error-end.js new file mode 100644 index 0000000000..b46fd7f32c --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-error-end.js @@ -0,0 +1,15 @@ +'use strict'; + +const common = require('../common'); +const { Readable } = require('stream'); + +{ + const r = new Readable({ read() {} }); + + r.on('end', common.mustNotCall()); + r.on('data', common.mustCall()); + r.on('error', common.mustCall()); + r.push('asd'); + r.push(null); + r.destroy(new Error('kaboom')); +} diff --git a/test/js/node/test/parallel/test-stream-readable-event.js b/test/js/node/test/parallel/test-stream-readable-event.js new file mode 100644 index 0000000000..4f2383508a --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-event.js @@ -0,0 +1,128 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const Readable = require('stream').Readable; + +{ + // First test, not reading when the readable is added. + // make sure that on('readable', ...) triggers a readable event. + const r = new Readable({ + highWaterMark: 3 + }); + + r._read = common.mustNotCall(); + + // This triggers a 'readable' event, which is lost. + r.push(Buffer.from('blerg')); + + setTimeout(function() { + // We're testing what we think we are + assert(!r._readableState.reading); + r.on('readable', common.mustCall()); + }, 1); +} + +{ + // Second test, make sure that readable is re-emitted if there's + // already a length, while it IS reading. + + const r = new Readable({ + highWaterMark: 3 + }); + + r._read = common.mustCall(); + + // This triggers a 'readable' event, which is lost. + r.push(Buffer.from('bl')); + + setTimeout(function() { + // Assert we're testing what we think we are + assert(r._readableState.reading); + r.on('readable', common.mustCall()); + }, 1); +} + +{ + // Third test, not reading when the stream has not passed + // the highWaterMark but *has* reached EOF. + const r = new Readable({ + highWaterMark: 30 + }); + + r._read = common.mustNotCall(); + + // This triggers a 'readable' event, which is lost. + r.push(Buffer.from('blerg')); + r.push(null); + + setTimeout(function() { + // Assert we're testing what we think we are + assert(!r._readableState.reading); + r.on('readable', common.mustCall()); + }, 1); +} + +{ + // Pushing an empty string in non-objectMode should + // trigger next `read()`. + const underlyingData = ['', 'x', 'y', '', 'z']; + const expected = underlyingData.filter((data) => data); + const result = []; + + const r = new Readable({ + encoding: 'utf8', + }); + r._read = function() { + process.nextTick(() => { + if (!underlyingData.length) { + this.push(null); + } else { + this.push(underlyingData.shift()); + } + }); + }; + + r.on('readable', () => { + const data = r.read(); + if (data !== null) result.push(data); + }); + + r.on('end', common.mustCall(() => { + assert.deepStrictEqual(result, expected); + })); +} + +{ + // #20923 + const r = new Readable(); + r._read = function() { + // Actually doing thing here + }; + r.on('data', function() {}); + + r.removeAllListeners(); + + assert.strictEqual(r.eventNames().length, 0); +} diff --git a/test/js/node/test/parallel/test-stream-readable-flow-recursion.js b/test/js/node/test/parallel/test-stream-readable-flow-recursion.js new file mode 100644 index 0000000000..bac6427fb0 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-flow-recursion.js @@ -0,0 +1,77 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// This test verifies that passing a huge number to read(size) +// will push up the highWaterMark, and cause the stream to read +// more data continuously, but without triggering a nextTick +// warning or RangeError. + +const Readable = require('stream').Readable; + +// Throw an error if we trigger a nextTick warning. +process.throwDeprecation = true; + +const stream = new Readable({ highWaterMark: 2 }); +let reads = 0; +let total = 5000; +stream._read = function(size) { + reads++; + size = Math.min(size, total); + total -= size; + if (size === 0) + stream.push(null); + else + stream.push(Buffer.allocUnsafe(size)); +}; + +let depth = 0; + +function flow(stream, size, callback) { + depth += 1; + const chunk = stream.read(size); + + if (!chunk) + stream.once('readable', flow.bind(null, stream, size, callback)); + else + callback(chunk); + + depth -= 1; + console.log(`flow(${depth}): exit`); +} + +flow(stream, 5000, function() { + console.log(`complete (${depth})`); +}); + +process.on('exit', function(code) { + assert.strictEqual(reads, 2); + // We pushed up the high water mark + assert.strictEqual(stream.readableHighWaterMark, 8192); + // Length is 0 right now, because we pulled it all out. + assert.strictEqual(stream.readableLength, 0); + assert(!code); + assert.strictEqual(depth, 0); + console.log('ok'); +}); diff --git a/test/js/node/test/parallel/test-stream-readable-hwm-0-async.js b/test/js/node/test/parallel/test-stream-readable-hwm-0-async.js new file mode 100644 index 0000000000..866b524893 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-hwm-0-async.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); + +// This test ensures that Readable stream will continue to call _read +// for streams with highWaterMark === 0 once the stream returns data +// by calling push() asynchronously. + +const { Readable } = require('stream'); + +let count = 5; + +const r = new Readable({ + // Called 6 times: First 5 return data, last one signals end of stream. + read: common.mustCall(() => { + process.nextTick(common.mustCall(() => { + if (count--) + r.push('a'); + else + r.push(null); + })); + }, 6), + highWaterMark: 0, +}); + +r.on('end', common.mustCall()); +r.on('data', common.mustCall(5)); diff --git a/test/js/node/test/parallel/test-stream-readable-hwm-0-no-flow-data.js b/test/js/node/test/parallel/test-stream-readable-hwm-0-no-flow-data.js new file mode 100644 index 0000000000..5f0186d720 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-hwm-0-no-flow-data.js @@ -0,0 +1,104 @@ +'use strict'; + +const common = require('../common'); + +// Ensure that subscribing the 'data' event will not make the stream flow. +// The 'data' event will require calling read() by hand. +// +// The test is written for the (somewhat rare) highWaterMark: 0 streams to +// specifically catch any regressions that might occur with these streams. + +const assert = require('assert'); +const { Readable } = require('stream'); + +const streamData = [ 'a', null ]; + +// Track the calls so we can assert their order later. +const calls = []; +const r = new Readable({ + read: common.mustCall(() => { + calls.push('_read:' + streamData[0]); + process.nextTick(() => { + calls.push('push:' + streamData[0]); + r.push(streamData.shift()); + }); + }, streamData.length), + highWaterMark: 0, + + // Object mode is used here just for testing convenience. It really + // shouldn't affect the order of events. Just the data and its format. + objectMode: true, +}); + +assert.strictEqual(r.readableFlowing, null); +r.on('readable', common.mustCall(() => { + calls.push('readable'); +}, 2)); +assert.strictEqual(r.readableFlowing, false); +r.on('data', common.mustCall((data) => { + calls.push('data:' + data); +}, 1)); +r.on('end', common.mustCall(() => { + calls.push('end'); +})); +assert.strictEqual(r.readableFlowing, false); + +// The stream emits the events asynchronously but that's not guaranteed to +// happen on the next tick (especially since the _read implementation above +// uses process.nextTick). +// +// We use setImmediate here to give the stream enough time to emit all the +// events it's about to emit. +setImmediate(() => { + + // Only the _read, push, readable calls have happened. No data must be + // emitted yet. + assert.deepStrictEqual(calls, ['_read:a', 'push:a', 'readable']); + + // Calling 'r.read()' should trigger the data event. + assert.strictEqual(r.read(), 'a'); + assert.deepStrictEqual( + calls, + ['_read:a', 'push:a', 'readable', 'data:a']); + + // The next 'read()' will return null because hwm: 0 does not buffer any + // data and the _read implementation above does the push() asynchronously. + // + // Note: This 'null' signals "no data available". It isn't the end-of-stream + // null value as the stream doesn't know yet that it is about to reach the + // end. + // + // Using setImmediate again to give the stream enough time to emit all the + // events it wants to emit. + assert.strictEqual(r.read(), null); + setImmediate(() => { + + // There's a new 'readable' event after the data has been pushed. + // The 'end' event will be emitted only after a 'read()'. + // + // This is somewhat special for the case where the '_read' implementation + // calls 'push' asynchronously. If 'push' was synchronous, the 'end' event + // would be emitted here _before_ we call read(). + assert.deepStrictEqual( + calls, + ['_read:a', 'push:a', 'readable', 'data:a', + '_read:null', 'push:null', 'readable']); + + assert.strictEqual(r.read(), null); + + // While it isn't really specified whether the 'end' event should happen + // synchronously with read() or not, we'll assert the current behavior + // ('end' event happening on the next tick after read()) so any changes + // to it are noted and acknowledged in the future. + assert.deepStrictEqual( + calls, + ['_read:a', 'push:a', 'readable', 'data:a', + '_read:null', 'push:null', 'readable']); + process.nextTick(() => { + assert.deepStrictEqual( + calls, + ['_read:a', 'push:a', 'readable', 'data:a', + '_read:null', 'push:null', 'readable', 'end']); + }); + }); +}); diff --git a/test/js/node/test/parallel/test-stream-readable-hwm-0.js b/test/js/node/test/parallel/test-stream-readable-hwm-0.js new file mode 100644 index 0000000000..5bb17882db --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-hwm-0.js @@ -0,0 +1,30 @@ +'use strict'; + +const common = require('../common'); + +// This test ensures that Readable stream will call _read() for streams +// with highWaterMark === 0 upon .read(0) instead of just trying to +// emit 'readable' event. + +const assert = require('assert'); +const { Readable } = require('stream'); + +const r = new Readable({ + // Must be called only once upon setting 'readable' listener + read: common.mustCall(), + highWaterMark: 0, +}); + +let pushedNull = false; +// This will trigger read(0) but must only be called after push(null) +// because the we haven't pushed any data +r.on('readable', common.mustCall(() => { + assert.strictEqual(r.read(), null); + assert.strictEqual(pushedNull, true); +})); +r.on('end', common.mustCall()); +process.nextTick(() => { + assert.strictEqual(r.read(), null); + pushedNull = true; + r.push(null); +}); diff --git a/test/js/node/test/parallel/test-stream-readable-infinite-read.js b/test/js/node/test/parallel/test-stream-readable-infinite-read.js new file mode 100644 index 0000000000..df88d78b74 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-infinite-read.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +const buf = Buffer.alloc(8192); + +const readable = new Readable({ + highWaterMark: 16 * 1024, + read: common.mustCall(function() { + this.push(buf); + }, 31) +}); + +let i = 0; + +readable.on('readable', common.mustCall(function() { + if (i++ === 10) { + // We will just terminate now. + process.removeAllListeners('readable'); + return; + } + + const data = readable.read(); + // TODO(mcollina): there is something odd in the highWaterMark logic + // investigate. + if (i === 1) { + assert.strictEqual(data.length, 8192 * 2); + } else { + assert.strictEqual(data.length, 8192 * 3); + } +}, 11)); diff --git a/test/js/node/test/parallel/test-stream-readable-invalid-chunk.js b/test/js/node/test/parallel/test-stream-readable-invalid-chunk.js new file mode 100644 index 0000000000..0fcc76ae73 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-invalid-chunk.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); +const stream = require('stream'); + +function testPushArg(val) { + const readable = new stream.Readable({ + read: () => {} + }); + readable.on('error', common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + })); + readable.push(val); +} + +testPushArg([]); +testPushArg({}); +testPushArg(0); + +function testUnshiftArg(val) { + const readable = new stream.Readable({ + read: () => {} + }); + readable.on('error', common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + })); + readable.unshift(val); +} + +testUnshiftArg([]); +testUnshiftArg({}); +testUnshiftArg(0); diff --git a/test/js/node/test/parallel/test-stream-readable-needReadable.js b/test/js/node/test/parallel/test-stream-readable-needReadable.js new file mode 100644 index 0000000000..c4bc90bb19 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-needReadable.js @@ -0,0 +1,99 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const Readable = require('stream').Readable; + +const readable = new Readable({ + read: () => {} +}); + +// Initialized to false. +assert.strictEqual(readable._readableState.needReadable, false); + +readable.on('readable', common.mustCall(() => { + // When the readable event fires, needReadable is reset. + assert.strictEqual(readable._readableState.needReadable, false); + readable.read(); +})); + +// If a readable listener is attached, then a readable event is needed. +assert.strictEqual(readable._readableState.needReadable, true); + +readable.push('foo'); +readable.push(null); + +readable.on('end', common.mustCall(() => { + // No need to emit readable anymore when the stream ends. + assert.strictEqual(readable._readableState.needReadable, false); +})); + +const asyncReadable = new Readable({ + read: () => {} +}); + +asyncReadable.on('readable', common.mustCall(() => { + if (asyncReadable.read() !== null) { + // After each read(), the buffer is empty. + // If the stream doesn't end now, + // then we need to notify the reader on future changes. + assert.strictEqual(asyncReadable._readableState.needReadable, true); + } +}, 2)); + +process.nextTick(common.mustCall(() => { + asyncReadable.push('foooo'); +})); +process.nextTick(common.mustCall(() => { + asyncReadable.push('bar'); +})); +setImmediate(common.mustCall(() => { + asyncReadable.push(null); + assert.strictEqual(asyncReadable._readableState.needReadable, false); +})); + +const flowing = new Readable({ + read: () => {} +}); + +// Notice this must be above the on('data') call. +flowing.push('foooo'); +flowing.push('bar'); +flowing.push('quo'); +process.nextTick(common.mustCall(() => { + flowing.push(null); +})); + +// When the buffer already has enough data, and the stream is +// in flowing mode, there is no need for the readable event. +flowing.on('data', common.mustCall(function(data) { + assert.strictEqual(flowing._readableState.needReadable, false); +}, 3)); + +const slowProducer = new Readable({ + read: () => {} +}); + +slowProducer.on('readable', common.mustCall(() => { + const chunk = slowProducer.read(8); + const state = slowProducer._readableState; + if (chunk === null) { + // The buffer doesn't have enough data, and the stream is not need, + // we need to notify the reader when data arrives. + assert.strictEqual(state.needReadable, true); + } else { + assert.strictEqual(state.needReadable, false); + } +}, 4)); + +process.nextTick(common.mustCall(() => { + slowProducer.push('foo'); + process.nextTick(common.mustCall(() => { + slowProducer.push('foo'); + process.nextTick(common.mustCall(() => { + slowProducer.push('foo'); + process.nextTick(common.mustCall(() => { + slowProducer.push(null); + })); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-stream-readable-next-no-null.js b/test/js/node/test/parallel/test-stream-readable-next-no-null.js new file mode 100644 index 0000000000..7599e386ca --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-next-no-null.js @@ -0,0 +1,19 @@ +'use strict'; +const { mustNotCall, expectsError } = require('../common'); +const { Readable } = require('stream'); + +async function* generate() { + yield null; +} + +const stream = Readable.from(generate()); + +stream.on('error', expectsError({ + code: 'ERR_STREAM_NULL_VALUES', + name: 'TypeError', + message: 'May not write null values to stream' +})); + +stream.on('data', mustNotCall()); + +stream.on('end', mustNotCall()); diff --git a/test/js/node/test/parallel/test-stream-readable-no-unneeded-readable.js b/test/js/node/test/parallel/test-stream-readable-no-unneeded-readable.js new file mode 100644 index 0000000000..20092b57d9 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-no-unneeded-readable.js @@ -0,0 +1,62 @@ +'use strict'; +const common = require('../common'); +const { Readable, PassThrough } = require('stream'); + +function test(r) { + const wrapper = new Readable({ + read: () => { + let data = r.read(); + + if (data) { + wrapper.push(data); + return; + } + + r.once('readable', function() { + data = r.read(); + if (data) { + wrapper.push(data); + } + // else: the end event should fire + }); + }, + }); + + r.once('end', function() { + wrapper.push(null); + }); + + wrapper.resume(); + wrapper.once('end', common.mustCall()); +} + +{ + const source = new Readable({ + read: () => {} + }); + source.push('foo'); + source.push('bar'); + source.push(null); + + const pt = source.pipe(new PassThrough()); + test(pt); +} + +{ + // This is the underlying cause of the above test case. + const pushChunks = ['foo', 'bar']; + const r = new Readable({ + read: () => { + const chunk = pushChunks.shift(); + if (chunk) { + // synchronous call + r.push(chunk); + } else { + // asynchronous call + process.nextTick(() => r.push(null)); + } + }, + }); + + test(r); +} diff --git a/test/js/node/test/parallel/test-stream-readable-object-multi-push-async.js b/test/js/node/test/parallel/test-stream-readable-object-multi-push-async.js new file mode 100644 index 0000000000..3bdbe4d351 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-object-multi-push-async.js @@ -0,0 +1,183 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +const MAX = 42; +const BATCH = 10; + +{ + const readable = new Readable({ + objectMode: true, + read: common.mustCall(function() { + console.log('>> READ'); + fetchData((err, data) => { + if (err) { + this.destroy(err); + return; + } + + if (data.length === 0) { + console.log('pushing null'); + this.push(null); + return; + } + + console.log('pushing'); + data.forEach((d) => this.push(d)); + }); + }, Math.floor(MAX / BATCH) + 2) + }); + + let i = 0; + function fetchData(cb) { + if (i > MAX) { + setTimeout(cb, 10, null, []); + } else { + const array = []; + const max = i + BATCH; + for (; i < max; i++) { + array.push(i); + } + setTimeout(cb, 10, null, array); + } + } + + readable.on('readable', () => { + let data; + console.log('readable emitted'); + while ((data = readable.read()) !== null) { + console.log(data); + } + }); + + readable.on('end', common.mustCall(() => { + assert.strictEqual(i, (Math.floor(MAX / BATCH) + 1) * BATCH); + })); +} + +{ + const readable = new Readable({ + objectMode: true, + read: common.mustCall(function() { + console.log('>> READ'); + fetchData((err, data) => { + if (err) { + this.destroy(err); + return; + } + + if (data.length === 0) { + console.log('pushing null'); + this.push(null); + return; + } + + console.log('pushing'); + data.forEach((d) => this.push(d)); + }); + }, Math.floor(MAX / BATCH) + 2) + }); + + let i = 0; + function fetchData(cb) { + if (i > MAX) { + setTimeout(cb, 10, null, []); + } else { + const array = []; + const max = i + BATCH; + for (; i < max; i++) { + array.push(i); + } + setTimeout(cb, 10, null, array); + } + } + + readable.on('data', (data) => { + console.log('data emitted', data); + }); + + readable.on('end', common.mustCall(() => { + assert.strictEqual(i, (Math.floor(MAX / BATCH) + 1) * BATCH); + })); +} + +{ + const readable = new Readable({ + objectMode: true, + read: common.mustCall(function() { + console.log('>> READ'); + fetchData((err, data) => { + if (err) { + this.destroy(err); + return; + } + + console.log('pushing'); + data.forEach((d) => this.push(d)); + + if (data[BATCH - 1] >= MAX) { + console.log('pushing null'); + this.push(null); + } + }); + }, Math.floor(MAX / BATCH) + 1) + }); + + let i = 0; + function fetchData(cb) { + const array = []; + const max = i + BATCH; + for (; i < max; i++) { + array.push(i); + } + setTimeout(cb, 10, null, array); + } + + readable.on('data', (data) => { + console.log('data emitted', data); + }); + + readable.on('end', common.mustCall(() => { + assert.strictEqual(i, (Math.floor(MAX / BATCH) + 1) * BATCH); + })); +} + +{ + const readable = new Readable({ + objectMode: true, + read: common.mustNotCall() + }); + + readable.on('data', common.mustNotCall()); + + readable.push(null); + + let nextTickPassed = false; + process.nextTick(() => { + nextTickPassed = true; + }); + + readable.on('end', common.mustCall(() => { + assert.strictEqual(nextTickPassed, true); + })); +} + +{ + const readable = new Readable({ + objectMode: true, + read: common.mustCall() + }); + + readable.on('data', (data) => { + console.log('data emitted', data); + }); + + readable.on('end', common.mustCall()); + + setImmediate(() => { + readable.push('aaa'); + readable.push(null); + }); +} diff --git a/test/js/node/test/parallel/test-stream-readable-readable.js b/test/js/node/test/parallel/test-stream-readable-readable.js new file mode 100644 index 0000000000..6e1a7fb32e --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-readable.js @@ -0,0 +1,45 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { Readable } = require('stream'); + +{ + const r = new Readable({ + read() {} + }); + assert.strictEqual(r.readable, true); + r.destroy(); + assert.strictEqual(r.readable, false); +} + +{ + const mustNotCall = common.mustNotCall(); + const r = new Readable({ + read() {} + }); + assert.strictEqual(r.readable, true); + r.on('end', mustNotCall); + r.resume(); + r.push(null); + assert.strictEqual(r.readable, true); + r.off('end', mustNotCall); + r.on('end', common.mustCall(() => { + assert.strictEqual(r.readable, false); + })); +} + +{ + const r = new Readable({ + read: common.mustCall(() => { + process.nextTick(() => { + r.destroy(new Error()); + assert.strictEqual(r.readable, false); + }); + }) + }); + r.resume(); + r.on('error', common.mustCall(() => { + assert.strictEqual(r.readable, false); + })); +} diff --git a/test/js/node/test/parallel/test-stream-readable-reading-readingMore.js b/test/js/node/test/parallel/test-stream-readable-reading-readingMore.js new file mode 100644 index 0000000000..5e39c86dcb --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-reading-readingMore.js @@ -0,0 +1,171 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const Readable = require('stream').Readable; + +{ + const readable = new Readable({ + read(size) {} + }); + + const state = readable._readableState; + + // Starting off with false initially. + assert.strictEqual(state.reading, false); + assert.strictEqual(state.readingMore, false); + + readable.on('data', common.mustCall((data) => { + // While in a flowing state with a 'readable' listener + // we should not be reading more + if (readable.readableFlowing) + assert.strictEqual(state.readingMore, true); + + // Reading as long as we've not ended + assert.strictEqual(state.reading, !state.ended); + }, 2)); + + function onStreamEnd() { + // End of stream; state.reading is false + // And so should be readingMore. + assert.strictEqual(state.readingMore, false); + assert.strictEqual(state.reading, false); + } + + const expectedReadingMore = [true, true, false]; + readable.on('readable', common.mustCall(() => { + // There is only one readingMore scheduled from on('data'), + // after which everything is governed by the .read() call + assert.strictEqual(state.readingMore, expectedReadingMore.shift()); + + // If the stream has ended, we shouldn't be reading + assert.strictEqual(state.ended, !state.reading); + + // Consume all the data + while (readable.read() !== null); + + if (expectedReadingMore.length === 0) // Reached end of stream + process.nextTick(common.mustCall(onStreamEnd, 1)); + }, 3)); + + readable.on('end', common.mustCall(onStreamEnd)); + readable.push('pushed'); + + readable.read(6); + + // reading + assert.strictEqual(state.reading, true); + assert.strictEqual(state.readingMore, true); + + // add chunk to front + readable.unshift('unshifted'); + + // end + readable.push(null); +} + +{ + const readable = new Readable({ + read(size) {} + }); + + const state = readable._readableState; + + // Starting off with false initially. + assert.strictEqual(state.reading, false); + assert.strictEqual(state.readingMore, false); + + readable.on('data', common.mustCall((data) => { + // While in a flowing state without a 'readable' listener + // we should be reading more + if (readable.readableFlowing) + assert.strictEqual(state.readingMore, true); + + // Reading as long as we've not ended + assert.strictEqual(state.reading, !state.ended); + }, 2)); + + function onStreamEnd() { + // End of stream; state.reading is false + // And so should be readingMore. + assert.strictEqual(state.readingMore, false); + assert.strictEqual(state.reading, false); + } + + readable.on('end', common.mustCall(onStreamEnd)); + readable.push('pushed'); + + // Stop emitting 'data' events + assert.strictEqual(state.flowing, true); + readable.pause(); + + // paused + assert.strictEqual(state.reading, false); + assert.strictEqual(state.flowing, false); + + readable.resume(); + assert.strictEqual(state.reading, false); + assert.strictEqual(state.flowing, true); + + // add chunk to front + readable.unshift('unshifted'); + + // end + readable.push(null); +} + +{ + const readable = new Readable({ + read(size) {} + }); + + const state = readable._readableState; + + // Starting off with false initially. + assert.strictEqual(state.reading, false); + assert.strictEqual(state.readingMore, false); + + const onReadable = common.mustNotCall(); + + readable.on('readable', onReadable); + + readable.on('data', common.mustCall((data) => { + // Reading as long as we've not ended + assert.strictEqual(state.reading, !state.ended); + }, 2)); + + readable.removeListener('readable', onReadable); + + function onStreamEnd() { + // End of stream; state.reading is false + // And so should be readingMore. + assert.strictEqual(state.readingMore, false); + assert.strictEqual(state.reading, false); + } + + readable.on('end', common.mustCall(onStreamEnd)); + readable.push('pushed'); + + // We are still not flowing, we will be resuming in the next tick + assert.strictEqual(state.flowing, false); + + // Wait for nextTick, so the readableListener flag resets + process.nextTick(function() { + readable.resume(); + + // Stop emitting 'data' events + assert.strictEqual(state.flowing, true); + readable.pause(); + + // paused + assert.strictEqual(state.flowing, false); + + readable.resume(); + assert.strictEqual(state.flowing, true); + + // add chunk to front + readable.unshift('unshifted'); + + // end + readable.push(null); + }); +} diff --git a/test/js/node/test/parallel/test-stream-readable-resume-hwm.js b/test/js/node/test/parallel/test-stream-readable-resume-hwm.js new file mode 100644 index 0000000000..3f0bbad243 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-resume-hwm.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); +const { Readable } = require('stream'); + +// readable.resume() should not lead to a ._read() call being scheduled +// when we exceed the high water mark already. + +const readable = new Readable({ + read: common.mustNotCall(), + highWaterMark: 100 +}); + +// Fill up the internal buffer so that we definitely exceed the HWM: +for (let i = 0; i < 10; i++) + readable.push('a'.repeat(200)); + +// Call resume, and pause after one chunk. +// The .pause() is just so that we don’t empty the buffer fully, which would +// be a valid reason to call ._read(). +readable.resume(); +readable.once('data', common.mustCall(() => readable.pause())); diff --git a/test/js/node/test/parallel/test-stream-readable-resumeScheduled.js b/test/js/node/test/parallel/test-stream-readable-resumeScheduled.js new file mode 100644 index 0000000000..aa521629b6 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-resumeScheduled.js @@ -0,0 +1,65 @@ +'use strict'; +const common = require('../common'); + +// Testing Readable Stream resumeScheduled state + +const assert = require('assert'); +const { Readable, Writable } = require('stream'); + +{ + // pipe() test case + const r = new Readable({ read() {} }); + const w = new Writable(); + + // resumeScheduled should start = `false`. + assert.strictEqual(r._readableState.resumeScheduled, false); + + // Calling pipe() should change the state value = true. + r.pipe(w); + assert.strictEqual(r._readableState.resumeScheduled, true); + + process.nextTick(common.mustCall(() => { + assert.strictEqual(r._readableState.resumeScheduled, false); + })); +} + +{ + // 'data' listener test case + const r = new Readable({ read() {} }); + + // resumeScheduled should start = `false`. + assert.strictEqual(r._readableState.resumeScheduled, false); + + r.push(Buffer.from([1, 2, 3])); + + // Adding 'data' listener should change the state value + r.on('data', common.mustCall(() => { + assert.strictEqual(r._readableState.resumeScheduled, false); + })); + assert.strictEqual(r._readableState.resumeScheduled, true); + + process.nextTick(common.mustCall(() => { + assert.strictEqual(r._readableState.resumeScheduled, false); + })); +} + +{ + // resume() test case + const r = new Readable({ read() {} }); + + // resumeScheduled should start = `false`. + assert.strictEqual(r._readableState.resumeScheduled, false); + + // Calling resume() should change the state value. + r.resume(); + assert.strictEqual(r._readableState.resumeScheduled, true); + + r.on('resume', common.mustCall(() => { + // The state value should be `false` again + assert.strictEqual(r._readableState.resumeScheduled, false); + })); + + process.nextTick(common.mustCall(() => { + assert.strictEqual(r._readableState.resumeScheduled, false); + })); +} diff --git a/test/js/node/test/parallel/test-stream-readable-setEncoding-existing-buffers.js b/test/js/node/test/parallel/test-stream-readable-setEncoding-existing-buffers.js new file mode 100644 index 0000000000..eb75260bac --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-setEncoding-existing-buffers.js @@ -0,0 +1,60 @@ +'use strict'; +require('../common'); +const { Readable } = require('stream'); +const assert = require('assert'); + +{ + // Call .setEncoding() while there are bytes already in the buffer. + const r = new Readable({ read() {} }); + + r.push(Buffer.from('a')); + r.push(Buffer.from('b')); + + r.setEncoding('utf8'); + const chunks = []; + r.on('data', (chunk) => chunks.push(chunk)); + + process.nextTick(() => { + assert.deepStrictEqual(chunks, ['ab']); + }); +} + +{ + // Call .setEncoding() while the buffer contains a complete, + // but chunked character. + const r = new Readable({ read() {} }); + + r.push(Buffer.from([0xf0])); + r.push(Buffer.from([0x9f])); + r.push(Buffer.from([0x8e])); + r.push(Buffer.from([0x89])); + + r.setEncoding('utf8'); + const chunks = []; + r.on('data', (chunk) => chunks.push(chunk)); + + process.nextTick(() => { + assert.deepStrictEqual(chunks, ['🎉']); + }); +} + +{ + // Call .setEncoding() while the buffer contains an incomplete character, + // and finish the character later. + const r = new Readable({ read() {} }); + + r.push(Buffer.from([0xf0])); + r.push(Buffer.from([0x9f])); + + r.setEncoding('utf8'); + + r.push(Buffer.from([0x8e])); + r.push(Buffer.from([0x89])); + + const chunks = []; + r.on('data', (chunk) => chunks.push(chunk)); + + process.nextTick(() => { + assert.deepStrictEqual(chunks, ['🎉']); + }); +} diff --git a/test/js/node/test/parallel/test-stream-readable-strategy-option.js b/test/js/node/test/parallel/test-stream-readable-strategy-option.js new file mode 100644 index 0000000000..a32e70ef21 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-strategy-option.js @@ -0,0 +1,75 @@ +'use strict'; +const common = require('../common'); +const { Readable } = require('stream'); +const assert = require('assert'); +const { strictEqual } = require('assert'); + +{ + // Strategy 2 + const streamData = ['a', 'b', 'c', null]; + + // Fulfill a Readable object + const readable = new Readable({ + read: common.mustCall(() => { + process.nextTick(() => { + readable.push(streamData.shift()); + }); + }, streamData.length), + }); + + // Use helper to convert it to a Web ReadableStream using ByteLength strategy + const readableStream = Readable.toWeb(readable, { + strategy: new ByteLengthQueuingStrategy({ highWaterMark: 1 }), + }); + + assert(!readableStream.locked); + readableStream.getReader().read().then(common.mustCall()); +} + +{ + // Strategy 2 + const streamData = ['a', 'b', 'c', null]; + + // Fulfill a Readable object + const readable = new Readable({ + read: common.mustCall(() => { + process.nextTick(() => { + readable.push(streamData.shift()); + }); + }, streamData.length), + }); + + // Use helper to convert it to a Web ReadableStream using Count strategy + const readableStream = Readable.toWeb(readable, { + strategy: new CountQueuingStrategy({ highWaterMark: 1 }), + }); + + assert(!readableStream.locked); + readableStream.getReader().read().then(common.mustCall()); +} + +{ + const desireSizeExpected = 2; + + const stringStream = new ReadableStream( + { + start(controller) { + // Check if the strategy is being assigned on the init of the ReadableStream + strictEqual(controller.desiredSize, desireSizeExpected); + controller.enqueue('a'); + controller.enqueue('b'); + controller.close(); + }, + }, + new CountQueuingStrategy({ highWaterMark: desireSizeExpected }) + ); + + const reader = stringStream.getReader(); + + reader.read().then(common.mustCall()); + reader.read().then(common.mustCall()); + reader.read().then(({ value, done }) => { + strictEqual(value, undefined); + strictEqual(done, true); + }); +} diff --git a/test/js/node/test/parallel/test-stream-readable-with-unimplemented-_read.js b/test/js/node/test/parallel/test-stream-readable-with-unimplemented-_read.js new file mode 100644 index 0000000000..85e83aa3b6 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readable-with-unimplemented-_read.js @@ -0,0 +1,13 @@ +'use strict'; +const common = require('../common'); +const { Readable } = require('stream'); + +const readable = new Readable(); + +readable.read(); +readable.on('error', common.expectsError({ + code: 'ERR_METHOD_NOT_IMPLEMENTED', + name: 'Error', + message: 'The _read() method is not implemented' +})); +readable.on('close', common.mustCall()); diff --git a/test/js/node/test/parallel/test-stream-readableListening-state.js b/test/js/node/test/parallel/test-stream-readableListening-state.js new file mode 100644 index 0000000000..5e3071faf3 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-readableListening-state.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +const r = new stream.Readable({ + read: () => {} +}); + +// readableListening state should start in `false`. +assert.strictEqual(r._readableState.readableListening, false); + +r.on('readable', common.mustCall(() => { + // Inside the readable event this state should be true. + assert.strictEqual(r._readableState.readableListening, true); +})); + +r.push(Buffer.from('Testing readableListening state')); + +const r2 = new stream.Readable({ + read: () => {} +}); + +// readableListening state should start in `false`. +assert.strictEqual(r2._readableState.readableListening, false); + +r2.on('data', common.mustCall((chunk) => { + // readableListening should be false because we don't have + // a `readable` listener + assert.strictEqual(r2._readableState.readableListening, false); +})); + +r2.push(Buffer.from('Testing readableListening state')); diff --git a/test/js/node/test/parallel/test-stream-set-default-hwm.js b/test/js/node/test/parallel/test-stream-set-default-hwm.js new file mode 100644 index 0000000000..3d78907b74 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-set-default-hwm.js @@ -0,0 +1,36 @@ +'use strict'; + +require('../common'); + +const assert = require('node:assert'); +const { + setDefaultHighWaterMark, + getDefaultHighWaterMark, + Writable, + Readable, + Transform +} = require('stream'); + +assert.notStrictEqual(getDefaultHighWaterMark(false), 32 * 1000); +setDefaultHighWaterMark(false, 32 * 1000); +assert.strictEqual(getDefaultHighWaterMark(false), 32 * 1000); + +assert.notStrictEqual(getDefaultHighWaterMark(true), 32); +setDefaultHighWaterMark(true, 32); +assert.strictEqual(getDefaultHighWaterMark(true), 32); + +const w = new Writable({ + write() {} +}); +assert.strictEqual(w.writableHighWaterMark, 32 * 1000); + +const r = new Readable({ + read() {} +}); +assert.strictEqual(r.readableHighWaterMark, 32 * 1000); + +const t = new Transform({ + transform() {} +}); +assert.strictEqual(t.writableHighWaterMark, 32 * 1000); +assert.strictEqual(t.readableHighWaterMark, 32 * 1000); diff --git a/test/js/node/test/parallel/test-stream-transform-callback-twice.js b/test/js/node/test/parallel/test-stream-transform-callback-twice.js new file mode 100644 index 0000000000..bf2ccdcde4 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-transform-callback-twice.js @@ -0,0 +1,14 @@ +'use strict'; +const common = require('../common'); +const { Transform } = require('stream'); +const stream = new Transform({ + transform(chunk, enc, cb) { cb(); cb(); } +}); + +stream.on('error', common.expectsError({ + name: 'Error', + message: 'Callback called multiple times', + code: 'ERR_MULTIPLE_CALLBACK' +})); + +stream.write('foo'); diff --git a/test/js/node/test/parallel/test-stream-transform-constructor-set-methods.js b/test/js/node/test/parallel/test-stream-transform-constructor-set-methods.js new file mode 100644 index 0000000000..a20a1a07cf --- /dev/null +++ b/test/js/node/test/parallel/test-stream-transform-constructor-set-methods.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const { Transform } = require('stream'); + +const t = new Transform(); + +assert.throws( + () => { + t.end(Buffer.from('blerg')); + }, + { + name: 'Error', + code: 'ERR_METHOD_NOT_IMPLEMENTED', + message: 'The _transform() method is not implemented' + } +); + +const _transform = common.mustCall((chunk, _, next) => { + next(); +}); + +const _final = common.mustCall((next) => { + next(); +}); + +const _flush = common.mustCall((next) => { + next(); +}); + +const t2 = new Transform({ + transform: _transform, + flush: _flush, + final: _final +}); + +assert.strictEqual(t2._transform, _transform); +assert.strictEqual(t2._flush, _flush); +assert.strictEqual(t2._final, _final); + +t2.end(Buffer.from('blerg')); +t2.resume(); diff --git a/test/js/node/test/parallel/test-stream-transform-final-sync.js b/test/js/node/test/parallel/test-stream-transform-final-sync.js new file mode 100644 index 0000000000..5cc80703ee --- /dev/null +++ b/test/js/node/test/parallel/test-stream-transform-final-sync.js @@ -0,0 +1,110 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); +let state = 0; + + +// What you do +// +// const stream = new stream.Transform({ +// transform: function transformCallback(chunk, _, next) { +// // part 1 +// this.push(chunk); +// //part 2 +// next(); +// }, +// final: function endCallback(done) { +// // part 1 +// process.nextTick(function () { +// // part 2 +// done(); +// }); +// }, +// flush: function flushCallback(done) { +// // part 1 +// process.nextTick(function () { +// // part 2 +// done(); +// }); +// } +// }); +// t.on('data', dataListener); +// t.on('end', endListener); +// t.on('finish', finishListener); +// t.write(1); +// t.write(4); +// t.end(7, endMethodCallback); +// +// The order things are called +// +// 1. transformCallback part 1 +// 2. dataListener +// 3. transformCallback part 2 +// 4. transformCallback part 1 +// 5. dataListener +// 6. transformCallback part 2 +// 7. transformCallback part 1 +// 8. dataListener +// 9. transformCallback part 2 +// 10. finalCallback part 1 +// 11. finalCallback part 2 +// 12. flushCallback part 1 +// 13. finishListener +// 14. endMethodCallback +// 15. flushCallback part 2 +// 16. endListener + +const t = new stream.Transform({ + objectMode: true, + transform: common.mustCall(function(chunk, _, next) { + // transformCallback part 1 + assert.strictEqual(++state, chunk); + this.push(state); + // transformCallback part 2 + assert.strictEqual(++state, chunk + 2); + process.nextTick(next); + }, 3), + final: common.mustCall(function(done) { + state++; + // finalCallback part 1 + assert.strictEqual(state, 10); + state++; + // finalCallback part 2 + assert.strictEqual(state, 11); + done(); + }, 1), + flush: common.mustCall(function(done) { + state++; + // fluchCallback part 1 + assert.strictEqual(state, 12); + process.nextTick(function() { + state++; + // fluchCallback part 2 + assert.strictEqual(state, 13); + done(); + }); + }, 1) +}); +t.on('finish', common.mustCall(function() { + state++; + // finishListener + assert.strictEqual(state, 15); +}, 1)); +t.on('end', common.mustCall(function() { + state++; + // endEvent + assert.strictEqual(state, 16); +}, 1)); +t.on('data', common.mustCall(function(d) { + // dataListener + assert.strictEqual(++state, d + 1); +}, 3)); +t.write(1); +t.write(4); +t.end(7, common.mustCall(function() { + state++; + // endMethodCallback + assert.strictEqual(state, 14); +}, 1)); diff --git a/test/js/node/test/parallel/test-stream-transform-final.js b/test/js/node/test/parallel/test-stream-transform-final.js new file mode 100644 index 0000000000..e0b2b7e40f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-transform-final.js @@ -0,0 +1,112 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); +let state = 0; + + +// What you do: +// +// const stream = new stream.Transform({ +// transform: function transformCallback(chunk, _, next) { +// // part 1 +// this.push(chunk); +// //part 2 +// next(); +// }, +// final: function endCallback(done) { +// // part 1 +// process.nextTick(function () { +// // part 2 +// done(); +// }); +// }, +// flush: function flushCallback(done) { +// // part 1 +// process.nextTick(function () { +// // part 2 +// done(); +// }); +// } +// }); +// t.on('data', dataListener); +// t.on('end', endListener); +// t.on('finish', finishListener); +// t.write(1); +// t.write(4); +// t.end(7, endMethodCallback); +// +// The order things are called + +// 1. transformCallback part 1 +// 2. dataListener +// 3. transformCallback part 2 +// 4. transformCallback part 1 +// 5. dataListener +// 6. transformCallback part 2 +// 7. transformCallback part 1 +// 8. dataListener +// 9. transformCallback part 2 +// 10. finalCallback part 1 +// 11. finalCallback part 2 +// 12. flushCallback part 1 +// 13. finishListener +// 14. endMethodCallback +// 15. flushCallback part 2 +// 16. endListener + +const t = new stream.Transform({ + objectMode: true, + transform: common.mustCall(function(chunk, _, next) { + // transformCallback part 1 + assert.strictEqual(++state, chunk); + this.push(state); + // transformCallback part 2 + assert.strictEqual(++state, chunk + 2); + process.nextTick(next); + }, 3), + final: common.mustCall(function(done) { + state++; + // finalCallback part 1 + assert.strictEqual(state, 10); + setTimeout(function() { + state++; + // finalCallback part 2 + assert.strictEqual(state, 11); + done(); + }, 100); + }, 1), + flush: common.mustCall(function(done) { + state++; + // flushCallback part 1 + assert.strictEqual(state, 12); + process.nextTick(function() { + state++; + // flushCallback part 2 + assert.strictEqual(state, 13); + done(); + }); + }, 1) +}); +t.on('finish', common.mustCall(function() { + state++; + // finishListener + assert.strictEqual(state, 15); +}, 1)); +t.on('end', common.mustCall(function() { + state++; + // end event + assert.strictEqual(state, 16); +}, 1)); +t.on('data', common.mustCall(function(d) { + // dataListener + assert.strictEqual(++state, d + 1); +}, 3)); +t.write(1); +t.write(4); +t.end(7, common.mustCall(function() { + state++; + // endMethodCallback + assert.strictEqual(state, 14); +}, 1)); diff --git a/test/js/node/test/parallel/test-stream-transform-flush-data.js b/test/js/node/test/parallel/test-stream-transform-flush-data.js new file mode 100644 index 0000000000..51e2c8bc52 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-transform-flush-data.js @@ -0,0 +1,28 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); +const Transform = require('stream').Transform; + + +const expected = 'asdf'; + + +function _transform(d, e, n) { + n(); +} + +function _flush(n) { + n(null, expected); +} + +const t = new Transform({ + transform: _transform, + flush: _flush +}); + +t.end(Buffer.from('blerg')); +t.on('data', (data) => { + assert.strictEqual(data.toString(), expected); +}); diff --git a/test/js/node/test/parallel/test-stream-transform-objectmode-falsey-value.js b/test/js/node/test/parallel/test-stream-transform-objectmode-falsey-value.js new file mode 100644 index 0000000000..78ede5d100 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-transform-objectmode-falsey-value.js @@ -0,0 +1,51 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); +const PassThrough = stream.PassThrough; + +const src = new PassThrough({ objectMode: true }); +const tx = new PassThrough({ objectMode: true }); +const dest = new PassThrough({ objectMode: true }); + +const expect = [ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; +const results = []; + +dest.on('data', common.mustCall(function(x) { + results.push(x); +}, expect.length)); + +src.pipe(tx).pipe(dest); + +let i = -1; +const int = setInterval(common.mustCall(function() { + if (results.length === expect.length) { + src.end(); + clearInterval(int); + assert.deepStrictEqual(results, expect); + } else { + src.write(i++); + } +}, expect.length + 1), 1); diff --git a/test/js/node/test/parallel/test-stream-unpipe-event.js b/test/js/node/test/parallel/test-stream-unpipe-event.js new file mode 100644 index 0000000000..46cc8e8cb0 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-unpipe-event.js @@ -0,0 +1,85 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Writable, Readable } = require('stream'); +class NullWriteable extends Writable { + _write(chunk, encoding, callback) { + return callback(); + } +} +class QuickEndReadable extends Readable { + _read() { + this.push(null); + } +} +class NeverEndReadable extends Readable { + _read() {} +} + +{ + const dest = new NullWriteable(); + const src = new QuickEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustCall()); + src.pipe(dest); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 0); + }); +} + +{ + const dest = new NullWriteable(); + const src = new NeverEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustNotCall('unpipe should not have been emitted')); + src.pipe(dest); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 1); + }); +} + +{ + const dest = new NullWriteable(); + const src = new NeverEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustCall()); + src.pipe(dest); + src.unpipe(dest); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 0); + }); +} + +{ + const dest = new NullWriteable(); + const src = new QuickEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustCall()); + src.pipe(dest, { end: false }); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 0); + }); +} + +{ + const dest = new NullWriteable(); + const src = new NeverEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustNotCall('unpipe should not have been emitted')); + src.pipe(dest, { end: false }); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 1); + }); +} + +{ + const dest = new NullWriteable(); + const src = new NeverEndReadable(); + dest.on('pipe', common.mustCall()); + dest.on('unpipe', common.mustCall()); + src.pipe(dest, { end: false }); + src.unpipe(dest); + setImmediate(() => { + assert.strictEqual(src._readableState.pipes.length, 0); + }); +} diff --git a/test/js/node/test/parallel/test-stream-unshift-empty-chunk.js b/test/js/node/test/parallel/test-stream-unshift-empty-chunk.js new file mode 100644 index 0000000000..e8136a68e9 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-unshift-empty-chunk.js @@ -0,0 +1,80 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// This test verifies that stream.unshift(Buffer.alloc(0)) or +// stream.unshift('') does not set state.reading=false. +const Readable = require('stream').Readable; + +const r = new Readable(); +let nChunks = 10; +const chunk = Buffer.alloc(10, 'x'); + +r._read = function(n) { + setImmediate(() => { + r.push(--nChunks === 0 ? null : chunk); + }); +}; + +let readAll = false; +const seen = []; +r.on('readable', () => { + let chunk; + while ((chunk = r.read()) !== null) { + seen.push(chunk.toString()); + // Simulate only reading a certain amount of the data, + // and then putting the rest of the chunk back into the + // stream, like a parser might do. We just fill it with + // 'y' so that it's easy to see which bits were touched, + // and which were not. + const putBack = Buffer.alloc(readAll ? 0 : 5, 'y'); + readAll = !readAll; + r.unshift(putBack); + } +}); + +const expect = + [ 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy' ]; + +r.on('end', () => { + assert.deepStrictEqual(seen, expect); + console.log('ok'); +}); diff --git a/test/js/node/test/parallel/test-stream-unshift-read-race.js b/test/js/node/test/parallel/test-stream-unshift-read-race.js new file mode 100644 index 0000000000..fe110ea285 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-unshift-read-race.js @@ -0,0 +1,128 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// This test verifies that: +// 1. unshift() does not cause colliding _read() calls. +// 2. unshift() after the 'end' event is an error, but after the EOF +// signalling null, it is ok, and just creates a new readable chunk. +// 3. push() after the EOF signaling null is an error. +// 4. _read() is not called after pushing the EOF null chunk. + +const stream = require('stream'); +const hwm = 10; +const r = stream.Readable({ highWaterMark: hwm, autoDestroy: false }); +const chunks = 10; + +const data = Buffer.allocUnsafe(chunks * hwm + Math.ceil(hwm / 2)); +for (let i = 0; i < data.length; i++) { + const c = 'asdf'.charCodeAt(i % 4); + data[i] = c; +} + +let pos = 0; +let pushedNull = false; +r._read = function(n) { + assert(!pushedNull, '_read after null push'); + + // Every third chunk is fast + push(!(chunks % 3)); + + function push(fast) { + assert(!pushedNull, 'push() after null push'); + const c = pos >= data.length ? null : data.slice(pos, pos + n); + pushedNull = c === null; + if (fast) { + pos += n; + r.push(c); + if (c === null) pushError(); + } else { + setTimeout(function() { + pos += n; + r.push(c); + if (c === null) pushError(); + }, 1); + } + } +}; + +function pushError() { + r.unshift(Buffer.allocUnsafe(1)); + w.end(); + + assert.throws(() => { + r.push(Buffer.allocUnsafe(1)); + }, { + code: 'ERR_STREAM_PUSH_AFTER_EOF', + name: 'Error', + message: 'stream.push() after EOF' + }); +} + + +const w = stream.Writable(); +const written = []; +w._write = function(chunk, encoding, cb) { + written.push(chunk.toString()); + cb(); +}; + +r.on('end', common.mustNotCall()); + +r.on('readable', function() { + let chunk; + while (null !== (chunk = r.read(10))) { + w.write(chunk); + if (chunk.length > 4) + r.unshift(Buffer.from('1234')); + } +}); + +w.on('finish', common.mustCall(function() { + // Each chunk should start with 1234, and then be asfdasdfasdf... + // The first got pulled out before the first unshift('1234'), so it's + // lacking that piece. + assert.strictEqual(written[0], 'asdfasdfas'); + let asdf = 'd'; + console.error(`0: ${written[0]}`); + for (let i = 1; i < written.length; i++) { + console.error(`${i.toString(32)}: ${written[i]}`); + assert.strictEqual(written[i].slice(0, 4), '1234'); + for (let j = 4; j < written[i].length; j++) { + const c = written[i].charAt(j); + assert.strictEqual(c, asdf); + switch (asdf) { + case 'a': asdf = 's'; break; + case 's': asdf = 'd'; break; + case 'd': asdf = 'f'; break; + case 'f': asdf = 'a'; break; + } + } + } +})); + +process.on('exit', function() { + assert.strictEqual(written.length, 18); + console.log('ok'); +}); diff --git a/test/js/node/test/parallel/test-stream-writable-aborted.js b/test/js/node/test/parallel/test-stream-writable-aborted.js new file mode 100644 index 0000000000..01d638115b --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-aborted.js @@ -0,0 +1,26 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const { Writable } = require('stream'); + +{ + const writable = new Writable({ + write() { + } + }); + assert.strictEqual(writable.writableAborted, false); + writable.destroy(); + assert.strictEqual(writable.writableAborted, true); +} + +{ + const writable = new Writable({ + write() { + } + }); + assert.strictEqual(writable.writableAborted, false); + writable.end(); + writable.destroy(); + assert.strictEqual(writable.writableAborted, true); +} diff --git a/test/js/node/test/parallel/test-stream-writable-change-default-encoding.js b/test/js/node/test/parallel/test-stream-writable-change-default-encoding.js new file mode 100644 index 0000000000..94a892567c --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-change-default-encoding.js @@ -0,0 +1,78 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const stream = require('stream'); + +class MyWritable extends stream.Writable { + constructor(fn, options) { + super(options); + this.fn = fn; + } + + _write(chunk, encoding, callback) { + this.fn(Buffer.isBuffer(chunk), typeof chunk, encoding); + callback(); + } +} + +(function defaultCondingIsUtf8() { + const m = new MyWritable(function(isBuffer, type, enc) { + assert.strictEqual(enc, 'utf8'); + }, { decodeStrings: false }); + m.write('foo'); + m.end(); +}()); + +(function changeDefaultEncodingToAscii() { + const m = new MyWritable(function(isBuffer, type, enc) { + assert.strictEqual(enc, 'ascii'); + }, { decodeStrings: false }); + m.setDefaultEncoding('ascii'); + m.write('bar'); + m.end(); +}()); + +// Change default encoding to invalid value. +assert.throws(() => { + const m = new MyWritable( + (isBuffer, type, enc) => {}, + { decodeStrings: false }); + m.setDefaultEncoding({}); + m.write('bar'); + m.end(); +}, { + name: 'TypeError', + code: 'ERR_UNKNOWN_ENCODING', + message: 'Unknown encoding: {}' +}); + +(function checkVariableCaseEncoding() { + const m = new MyWritable(function(isBuffer, type, enc) { + assert.strictEqual(enc, 'ascii'); + }, { decodeStrings: false }); + m.setDefaultEncoding('AsCii'); + m.write('bar'); + m.end(); +}()); diff --git a/test/js/node/test/parallel/test-stream-writable-clear-buffer.js b/test/js/node/test/parallel/test-stream-writable-clear-buffer.js new file mode 100644 index 0000000000..c4d7ae151a --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-clear-buffer.js @@ -0,0 +1,35 @@ +'use strict'; + +// This test ensures that the _writeableState.bufferedRequestCount and +// the actual buffered request count are the same. + +const common = require('../common'); +const Stream = require('stream'); +const assert = require('assert'); + +class StreamWritable extends Stream.Writable { + constructor() { + super({ objectMode: true }); + } + + // Refs: https://github.com/nodejs/node/issues/6758 + // We need a timer like on the original issue thread. + // Otherwise the code will never reach our test case. + _write(chunk, encoding, cb) { + setImmediate(cb); + } +} + +const testStream = new StreamWritable(); +testStream.cork(); + +for (let i = 1; i <= 5; i++) { + testStream.write(i, common.mustCall(() => { + assert.strictEqual( + testStream._writableState.bufferedRequestCount, + testStream._writableState.getBuffer().length + ); + })); +} + +testStream.end(); diff --git a/test/js/node/test/parallel/test-stream-writable-constructor-set-methods.js b/test/js/node/test/parallel/test-stream-writable-constructor-set-methods.js new file mode 100644 index 0000000000..34fda8edda --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-constructor-set-methods.js @@ -0,0 +1,41 @@ +'use strict'; +const common = require('../common'); + +const assert = require('assert'); +const { Writable } = require('stream'); + +const bufferBlerg = Buffer.from('blerg'); +const w = new Writable(); + +assert.throws( + () => { + w.end(bufferBlerg); + }, + { + name: 'Error', + code: 'ERR_METHOD_NOT_IMPLEMENTED', + message: 'The _write() method is not implemented' + } +); + +const _write = common.mustCall((chunk, _, next) => { + next(); +}); + +const _writev = common.mustCall((chunks, next) => { + assert.strictEqual(chunks.length, 2); + next(); +}); + +const w2 = new Writable({ write: _write, writev: _writev }); + +assert.strictEqual(w2._write, _write); +assert.strictEqual(w2._writev, _writev); + +w2.write(bufferBlerg); + +w2.cork(); +w2.write(bufferBlerg); +w2.write(bufferBlerg); + +w2.end(); diff --git a/test/js/node/test/parallel/test-stream-writable-end-cb-uncaught.js b/test/js/node/test/parallel/test-stream-writable-end-cb-uncaught.js new file mode 100644 index 0000000000..02586b45d9 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-end-cb-uncaught.js @@ -0,0 +1,24 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +process.on('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err.message, 'kaboom'); +})); + +const writable = new stream.Writable(); +const _err = new Error('kaboom'); + +writable._write = (chunk, encoding, cb) => { + cb(); +}; +writable._final = (cb) => { + cb(_err); +}; + +writable.write('asd'); +writable.end(common.mustCall((err) => { + assert.strictEqual(err, _err); +})); diff --git a/test/js/node/test/parallel/test-stream-writable-end-multiple.js b/test/js/node/test/parallel/test-stream-writable-end-multiple.js new file mode 100644 index 0000000000..000f5b07f5 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-end-multiple.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const stream = require('stream'); + +const writable = new stream.Writable(); +writable._write = (chunk, encoding, cb) => { + setTimeout(() => cb(), 10); +}; + +writable.end('testing ended state', common.mustCall()); +writable.end(common.mustCall()); +writable.on('finish', common.mustCall(() => { + let ticked = false; + writable.end(common.mustCall((err) => { + assert.strictEqual(ticked, true); + assert.strictEqual(err.code, 'ERR_STREAM_ALREADY_FINISHED'); + })); + ticked = true; +})); diff --git a/test/js/node/test/parallel/test-stream-writable-ended-state.js b/test/js/node/test/parallel/test-stream-writable-ended-state.js new file mode 100644 index 0000000000..2c40c62a9e --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-ended-state.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const stream = require('stream'); + +const writable = new stream.Writable(); + +writable._write = (chunk, encoding, cb) => { + assert.strictEqual(writable._writableState.ended, false); + assert.strictEqual(writable._writableState.writable, undefined); + assert.strictEqual(writable.writableEnded, false); + cb(); +}; + +assert.strictEqual(writable._writableState.ended, false); +assert.strictEqual(writable._writableState.writable, undefined); +assert.strictEqual(writable.writable, true); +assert.strictEqual(writable.writableEnded, false); + +writable.end('testing ended state', common.mustCall(() => { + assert.strictEqual(writable._writableState.ended, true); + assert.strictEqual(writable._writableState.writable, undefined); + assert.strictEqual(writable.writable, false); + assert.strictEqual(writable.writableEnded, true); +})); + +assert.strictEqual(writable._writableState.ended, true); +assert.strictEqual(writable._writableState.writable, undefined); +assert.strictEqual(writable.writable, false); +assert.strictEqual(writable.writableEnded, true); diff --git a/test/js/node/test/parallel/test-stream-writable-final-async.js b/test/js/node/test/parallel/test-stream-writable-final-async.js new file mode 100644 index 0000000000..c17b843322 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-final-async.js @@ -0,0 +1,25 @@ +'use strict'; + +const common = require('../common'); +const { + Duplex, +} = require('stream'); +const { setTimeout } = require('timers/promises'); + +{ + class Foo extends Duplex { + async _final(callback) { + await setTimeout(common.platformTimeout(1)); + callback(); + } + + _read() {} + } + + const foo = new Foo(); + foo._write = common.mustCall((chunk, encoding, cb) => { + cb(); + }); + foo.end('test', common.mustCall()); + foo.on('error', common.mustNotCall()); +} diff --git a/test/js/node/test/parallel/test-stream-writable-final-destroy.js b/test/js/node/test/parallel/test-stream-writable-final-destroy.js new file mode 100644 index 0000000000..8d3bf72c89 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-final-destroy.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); + +const { Writable } = require('stream'); + +{ + const w = new Writable({ + write(chunk, encoding, callback) { + callback(null); + }, + final(callback) { + queueMicrotask(callback); + } + }); + w.end(); + w.destroy(); + + w.on('prefinish', common.mustNotCall()); + w.on('finish', common.mustNotCall()); + w.on('close', common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-stream-writable-final-throw.js b/test/js/node/test/parallel/test-stream-writable-final-throw.js new file mode 100644 index 0000000000..e7dd21abc3 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-final-throw.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +const { + Duplex, +} = require('stream'); + +{ + class Foo extends Duplex { + _final(callback) { + throw new Error('fhqwhgads'); + } + + _read() {} + } + + const foo = new Foo(); + foo._write = common.mustCall((chunk, encoding, cb) => { + cb(); + }); + foo.end('test', common.expectsError({ message: 'fhqwhgads' })); + foo.on('error', common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-stream-writable-finish-destroyed.js b/test/js/node/test/parallel/test-stream-writable-finish-destroyed.js new file mode 100644 index 0000000000..22657a170f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-finish-destroyed.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +const { Writable } = require('stream'); + +{ + const w = new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + w.on('close', common.mustCall(() => { + cb(); + })); + }) + }); + + w.on('finish', common.mustNotCall()); + w.end('asd'); + w.destroy(); +} + +{ + const w = new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + w.on('close', common.mustCall(() => { + cb(); + w.end(); + })); + }) + }); + + w.on('finish', common.mustNotCall()); + w.write('asd'); + w.destroy(); +} + +{ + const w = new Writable({ + write() { + } + }); + w.on('finish', common.mustNotCall()); + w.end(); + w.destroy(); +} diff --git a/test/js/node/test/parallel/test-stream-writable-finished-state.js b/test/js/node/test/parallel/test-stream-writable-finished-state.js new file mode 100644 index 0000000000..b42137ed0b --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-finished-state.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const stream = require('stream'); + +const writable = new stream.Writable(); + +writable._write = (chunk, encoding, cb) => { + // The state finished should start in false. + assert.strictEqual(writable._writableState.finished, false); + cb(); +}; + +writable.on('finish', common.mustCall(() => { + assert.strictEqual(writable._writableState.finished, true); +})); + +writable.end('testing finished state', common.mustCall(() => { + assert.strictEqual(writable._writableState.finished, true); +})); diff --git a/test/js/node/test/parallel/test-stream-writable-finished.js b/test/js/node/test/parallel/test-stream-writable-finished.js new file mode 100644 index 0000000000..933a80a2f9 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-finished.js @@ -0,0 +1,99 @@ +'use strict'; + +const common = require('../common'); +const { Writable } = require('stream'); +const assert = require('assert'); + +// basic +{ + // Find it on Writable.prototype + assert(Object.hasOwn(Writable.prototype, 'writableFinished')); +} + +// event +{ + const writable = new Writable(); + + writable._write = (chunk, encoding, cb) => { + // The state finished should start in false. + assert.strictEqual(writable.writableFinished, false); + cb(); + }; + + writable.on('finish', common.mustCall(() => { + assert.strictEqual(writable.writableFinished, true); + })); + + writable.end('testing finished state', common.mustCall(() => { + assert.strictEqual(writable.writableFinished, true); + })); +} + +{ + // Emit finish asynchronously. + + const w = new Writable({ + write(chunk, encoding, cb) { + cb(); + } + }); + + w.end(); + w.on('finish', common.mustCall()); +} + +{ + // Emit prefinish synchronously. + + const w = new Writable({ + write(chunk, encoding, cb) { + cb(); + } + }); + + let sync = true; + w.on('prefinish', common.mustCall(() => { + assert.strictEqual(sync, true); + })); + w.end(); + sync = false; +} + +{ + // Emit prefinish synchronously w/ final. + + const w = new Writable({ + write(chunk, encoding, cb) { + cb(); + }, + final(cb) { + cb(); + } + }); + + let sync = true; + w.on('prefinish', common.mustCall(() => { + assert.strictEqual(sync, true); + })); + w.end(); + sync = false; +} + + +{ + // Call _final synchronously. + + let sync = true; + const w = new Writable({ + write(chunk, encoding, cb) { + cb(); + }, + final: common.mustCall((cb) => { + assert.strictEqual(sync, true); + cb(); + }) + }); + + w.end(); + sync = false; +} diff --git a/test/js/node/test/parallel/test-stream-writable-invalid-chunk.js b/test/js/node/test/parallel/test-stream-writable-invalid-chunk.js new file mode 100644 index 0000000000..09032c07c5 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-invalid-chunk.js @@ -0,0 +1,36 @@ +'use strict'; + +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +function testWriteType(val, objectMode, code) { + const writable = new stream.Writable({ + objectMode, + write: () => {} + }); + writable.on('error', common.mustNotCall()); + if (code) { + assert.throws(() => { + writable.write(val); + }, { code }); + } else { + writable.write(val); + } +} + +testWriteType([], false, 'ERR_INVALID_ARG_TYPE'); +testWriteType({}, false, 'ERR_INVALID_ARG_TYPE'); +testWriteType(0, false, 'ERR_INVALID_ARG_TYPE'); +testWriteType(true, false, 'ERR_INVALID_ARG_TYPE'); +testWriteType(0.0, false, 'ERR_INVALID_ARG_TYPE'); +testWriteType(undefined, false, 'ERR_INVALID_ARG_TYPE'); +testWriteType(null, false, 'ERR_STREAM_NULL_VALUES'); + +testWriteType([], true); +testWriteType({}, true); +testWriteType(0, true); +testWriteType(true, true); +testWriteType(0.0, true); +testWriteType(undefined, true); +testWriteType(null, true, 'ERR_STREAM_NULL_VALUES'); diff --git a/test/js/node/test/parallel/test-stream-writable-needdrain-state.js b/test/js/node/test/parallel/test-stream-writable-needdrain-state.js new file mode 100644 index 0000000000..0e72d832bc --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-needdrain-state.js @@ -0,0 +1,25 @@ +'use strict'; + +const common = require('../common'); +const stream = require('stream'); +const assert = require('assert'); + +const transform = new stream.Transform({ + transform: _transform, + highWaterMark: 1 +}); + +function _transform(chunk, encoding, cb) { + process.nextTick(() => { + assert.strictEqual(transform._writableState.needDrain, true); + cb(); + }); +} + +assert.strictEqual(transform._writableState.needDrain, false); + +transform.write('asdasd', common.mustCall(() => { + assert.strictEqual(transform._writableState.needDrain, false); +})); + +assert.strictEqual(transform._writableState.needDrain, true); diff --git a/test/js/node/test/parallel/test-stream-writable-null.js b/test/js/node/test/parallel/test-stream-writable-null.js new file mode 100644 index 0000000000..99419f1cf9 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-null.js @@ -0,0 +1,47 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); + +class MyWritable extends stream.Writable { + constructor(options) { + super({ autoDestroy: false, ...options }); + } + _write(chunk, encoding, callback) { + assert.notStrictEqual(chunk, null); + callback(); + } +} + +{ + const m = new MyWritable({ objectMode: true }); + m.on('error', common.mustNotCall()); + assert.throws(() => { + m.write(null); + }, { + code: 'ERR_STREAM_NULL_VALUES' + }); +} + +{ + const m = new MyWritable(); + m.on('error', common.mustNotCall()); + assert.throws(() => { + m.write(false); + }, { + code: 'ERR_INVALID_ARG_TYPE' + }); +} + +{ // Should not throw. + const m = new MyWritable({ objectMode: true }); + m.write(false, assert.ifError); +} + +{ // Should not throw. + const m = new MyWritable({ objectMode: true }).on('error', (e) => { + assert.ifError(e || new Error('should not get here')); + }); + m.write(false, assert.ifError); +} diff --git a/test/js/node/test/parallel/test-stream-writable-properties.js b/test/js/node/test/parallel/test-stream-writable-properties.js new file mode 100644 index 0000000000..424bb58710 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-properties.js @@ -0,0 +1,22 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +const { Writable } = require('stream'); + +{ + const w = new Writable(); + assert.strictEqual(w.writableCorked, 0); + w.uncork(); + assert.strictEqual(w.writableCorked, 0); + w.cork(); + assert.strictEqual(w.writableCorked, 1); + w.cork(); + assert.strictEqual(w.writableCorked, 2); + w.uncork(); + assert.strictEqual(w.writableCorked, 1); + w.uncork(); + assert.strictEqual(w.writableCorked, 0); + w.uncork(); + assert.strictEqual(w.writableCorked, 0); +} diff --git a/test/js/node/test/parallel/test-stream-writable-writable.js b/test/js/node/test/parallel/test-stream-writable-writable.js new file mode 100644 index 0000000000..ef5454dc52 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-writable.js @@ -0,0 +1,48 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { Writable } = require('stream'); + +{ + const w = new Writable({ + write() {} + }); + assert.strictEqual(w.writable, true); + w.destroy(); + assert.strictEqual(w.writable, false); +} + +{ + const w = new Writable({ + write: common.mustCall((chunk, encoding, callback) => { + callback(new Error()); + }) + }); + assert.strictEqual(w.writable, true); + w.write('asd'); + assert.strictEqual(w.writable, false); + w.on('error', common.mustCall()); +} + +{ + const w = new Writable({ + write: common.mustCall((chunk, encoding, callback) => { + process.nextTick(() => { + callback(new Error()); + assert.strictEqual(w.writable, false); + }); + }) + }); + w.write('asd'); + w.on('error', common.mustCall()); +} + +{ + const w = new Writable({ + write: common.mustNotCall() + }); + assert.strictEqual(w.writable, true); + w.end(); + assert.strictEqual(w.writable, false); +} diff --git a/test/js/node/test/parallel/test-stream-writable-write-cb-error.js b/test/js/node/test/parallel/test-stream-writable-write-cb-error.js new file mode 100644 index 0000000000..72db1b7e3f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-write-cb-error.js @@ -0,0 +1,58 @@ +'use strict'; +const common = require('../common'); +const { Writable } = require('stream'); +const assert = require('assert'); + +// Ensure callback is always invoked before +// error is emitted. Regardless if error was +// sync or async. + +{ + let callbackCalled = false; + // Sync Error + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + cb(new Error()); + }) + }); + writable.on('error', common.mustCall(() => { + assert.strictEqual(callbackCalled, true); + })); + writable.write('hi', common.mustCall(() => { + callbackCalled = true; + })); +} + +{ + let callbackCalled = false; + // Async Error + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + process.nextTick(cb, new Error()); + }) + }); + writable.on('error', common.mustCall(() => { + assert.strictEqual(callbackCalled, true); + })); + writable.write('hi', common.mustCall(() => { + callbackCalled = true; + })); +} + +{ + // Sync Error + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + cb(new Error()); + }) + }); + + writable.on('error', common.mustCall()); + + let cnt = 0; + // Ensure we don't live lock on sync error + while (writable.write('a')) + cnt++; + + assert.strictEqual(cnt, 0); +} diff --git a/test/js/node/test/parallel/test-stream-writable-write-cb-twice.js b/test/js/node/test/parallel/test-stream-writable-write-cb-twice.js new file mode 100644 index 0000000000..244698c522 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-write-cb-twice.js @@ -0,0 +1,52 @@ +'use strict'; +const common = require('../common'); +const { Writable } = require('stream'); + +{ + // Sync + Sync + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + cb(); + cb(); + }) + }); + writable.write('hi'); + writable.on('error', common.expectsError({ + code: 'ERR_MULTIPLE_CALLBACK', + name: 'Error' + })); +} + +{ + // Sync + Async + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + cb(); + process.nextTick(() => { + cb(); + }); + }) + }); + writable.write('hi'); + writable.on('error', common.expectsError({ + code: 'ERR_MULTIPLE_CALLBACK', + name: 'Error' + })); +} + +{ + // Async + Async + const writable = new Writable({ + write: common.mustCall((buf, enc, cb) => { + process.nextTick(cb); + process.nextTick(() => { + cb(); + }); + }) + }); + writable.write('hi'); + writable.on('error', common.expectsError({ + code: 'ERR_MULTIPLE_CALLBACK', + name: 'Error' + })); +} diff --git a/test/js/node/test/parallel/test-stream-writable-write-error.js b/test/js/node/test/parallel/test-stream-writable-write-error.js new file mode 100644 index 0000000000..069e32e1be --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-write-error.js @@ -0,0 +1,75 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { Writable } = require('stream'); + +function expectError(w, args, code, sync) { + if (sync) { + if (code) { + assert.throws(() => w.write(...args), { code }); + } else { + w.write(...args); + } + } else { + let errorCalled = false; + let ticked = false; + w.write(...args, common.mustCall((err) => { + assert.strictEqual(ticked, true); + assert.strictEqual(errorCalled, false); + assert.strictEqual(err.code, code); + })); + ticked = true; + w.on('error', common.mustCall((err) => { + errorCalled = true; + assert.strictEqual(err.code, code); + })); + } +} + +function test(autoDestroy) { + { + const w = new Writable({ + autoDestroy, + _write() {} + }); + w.end(); + expectError(w, ['asd'], 'ERR_STREAM_WRITE_AFTER_END'); + } + + { + const w = new Writable({ + autoDestroy, + _write() {} + }); + w.destroy(); + } + + { + const w = new Writable({ + autoDestroy, + _write() {} + }); + expectError(w, [null], 'ERR_STREAM_NULL_VALUES', true); + } + + { + const w = new Writable({ + autoDestroy, + _write() {} + }); + expectError(w, [{}], 'ERR_INVALID_ARG_TYPE', true); + } + + { + const w = new Writable({ + decodeStrings: false, + autoDestroy, + _write() {} + }); + expectError(w, ['asd', 'noencoding'], 'ERR_UNKNOWN_ENCODING', true); + } +} + +test(false); +test(true); diff --git a/test/js/node/test/parallel/test-stream-writable-write-writev-finish.js b/test/js/node/test/parallel/test-stream-writable-write-writev-finish.js new file mode 100644 index 0000000000..9fce315f8b --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writable-write-writev-finish.js @@ -0,0 +1,152 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +// Ensure consistency between the finish event when using cork() +// and writev and when not using them + +{ + const writable = new stream.Writable(); + + writable._write = (chunks, encoding, cb) => { + cb(new Error('write test error')); + }; + + writable.on('finish', common.mustNotCall()); + writable.on('prefinish', common.mustNotCall()); + writable.on('error', common.mustCall((er) => { + assert.strictEqual(er.message, 'write test error'); + })); + + writable.end('test'); +} + +{ + const writable = new stream.Writable(); + + writable._write = (chunks, encoding, cb) => { + setImmediate(cb, new Error('write test error')); + }; + + writable.on('finish', common.mustNotCall()); + writable.on('prefinish', common.mustNotCall()); + writable.on('error', common.mustCall((er) => { + assert.strictEqual(er.message, 'write test error'); + })); + + writable.end('test'); +} + +{ + const writable = new stream.Writable(); + + writable._write = (chunks, encoding, cb) => { + cb(new Error('write test error')); + }; + + writable._writev = (chunks, cb) => { + cb(new Error('writev test error')); + }; + + writable.on('finish', common.mustNotCall()); + writable.on('prefinish', common.mustNotCall()); + writable.on('error', common.mustCall((er) => { + assert.strictEqual(er.message, 'writev test error'); + })); + + writable.cork(); + writable.write('test'); + + setImmediate(function() { + writable.end('test'); + }); +} + +{ + const writable = new stream.Writable(); + + writable._write = (chunks, encoding, cb) => { + setImmediate(cb, new Error('write test error')); + }; + + writable._writev = (chunks, cb) => { + setImmediate(cb, new Error('writev test error')); + }; + + writable.on('finish', common.mustNotCall()); + writable.on('prefinish', common.mustNotCall()); + writable.on('error', common.mustCall((er) => { + assert.strictEqual(er.message, 'writev test error'); + })); + + writable.cork(); + writable.write('test'); + + setImmediate(function() { + writable.end('test'); + }); +} + +// Regression test for +// https://github.com/nodejs/node/issues/13812 + +{ + const rs = new stream.Readable(); + rs.push('ok'); + rs.push(null); + rs._read = () => {}; + + const ws = new stream.Writable(); + + ws.on('finish', common.mustNotCall()); + ws.on('error', common.mustCall()); + + ws._write = (chunk, encoding, done) => { + setImmediate(done, new Error()); + }; + rs.pipe(ws); +} + +{ + const rs = new stream.Readable(); + rs.push('ok'); + rs.push(null); + rs._read = () => {}; + + const ws = new stream.Writable(); + + ws.on('finish', common.mustNotCall()); + ws.on('error', common.mustCall()); + + ws._write = (chunk, encoding, done) => { + done(new Error()); + }; + rs.pipe(ws); +} + +{ + const w = new stream.Writable(); + w._write = (chunk, encoding, cb) => { + process.nextTick(cb); + }; + w.on('error', common.mustCall()); + w.on('finish', common.mustNotCall()); + w.on('prefinish', () => { + w.write("shouldn't write in prefinish listener"); + }); + w.end(); +} + +{ + const w = new stream.Writable(); + w._write = (chunk, encoding, cb) => { + process.nextTick(cb); + }; + w.on('error', common.mustCall()); + w.on('finish', () => { + w.write("shouldn't write in finish listener"); + }); + w.end(); +} diff --git a/test/js/node/test/parallel/test-stream-writableState-ending.js b/test/js/node/test/parallel/test-stream-writableState-ending.js new file mode 100644 index 0000000000..d301d355cc --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writableState-ending.js @@ -0,0 +1,37 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); +const stream = require('stream'); + +const writable = new stream.Writable(); + +function testStates(ending, finished, ended) { + assert.strictEqual(writable._writableState.ending, ending); + assert.strictEqual(writable._writableState.finished, finished); + assert.strictEqual(writable._writableState.ended, ended); +} + +writable._write = (chunk, encoding, cb) => { + // Ending, finished, ended start in false. + testStates(false, false, false); + cb(); +}; + +writable.on('finish', () => { + // Ending, finished, ended = true. + testStates(true, true, true); +}); + +const result = writable.end('testing function end()', () => { + // Ending, finished, ended = true. + testStates(true, true, true); +}); + +// End returns the writable instance +assert.strictEqual(result, writable); + +// Ending, ended = true. +// finished = false. +testStates(true, false, true); diff --git a/test/js/node/test/parallel/test-stream-writableState-uncorked-bufferedRequestCount.js b/test/js/node/test/parallel/test-stream-writableState-uncorked-bufferedRequestCount.js new file mode 100644 index 0000000000..b7375b9fa2 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writableState-uncorked-bufferedRequestCount.js @@ -0,0 +1,57 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +const writable = new stream.Writable(); + +writable._writev = common.mustCall((chunks, cb) => { + assert.strictEqual(chunks.length, 2); + cb(); +}, 1); + +writable._write = common.mustCall((chunk, encoding, cb) => { + cb(); +}, 1); + +// first cork +writable.cork(); +assert.strictEqual(writable._writableState.corked, 1); +assert.strictEqual(writable._writableState.bufferedRequestCount, 0); + +// cork again +writable.cork(); +assert.strictEqual(writable._writableState.corked, 2); + +// The first chunk is buffered +writable.write('first chunk'); +assert.strictEqual(writable._writableState.bufferedRequestCount, 1); + +// First uncork does nothing +writable.uncork(); +assert.strictEqual(writable._writableState.corked, 1); +assert.strictEqual(writable._writableState.bufferedRequestCount, 1); + +process.nextTick(uncork); + +// The second chunk is buffered, because we uncork at the end of tick +writable.write('second chunk'); +assert.strictEqual(writable._writableState.corked, 1); +assert.strictEqual(writable._writableState.bufferedRequestCount, 2); + +function uncork() { + // Second uncork flushes the buffer + writable.uncork(); + assert.strictEqual(writable._writableState.corked, 0); + assert.strictEqual(writable._writableState.bufferedRequestCount, 0); + + // Verify that end() uncorks correctly + writable.cork(); + writable.write('third chunk'); + writable.end(); + + // End causes an uncork() as well + assert.strictEqual(writable._writableState.corked, 0); + assert.strictEqual(writable._writableState.bufferedRequestCount, 0); +} diff --git a/test/js/node/test/parallel/test-stream-write-destroy.js b/test/js/node/test/parallel/test-stream-write-destroy.js new file mode 100644 index 0000000000..d436b98f84 --- /dev/null +++ b/test/js/node/test/parallel/test-stream-write-destroy.js @@ -0,0 +1,62 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { Writable } = require('stream'); + +// Test interaction between calling .destroy() on a writable and pending +// writes. + +for (const withPendingData of [ false, true ]) { + for (const useEnd of [ false, true ]) { + const callbacks = []; + + const w = new Writable({ + write(data, enc, cb) { + callbacks.push(cb); + }, + // Effectively disable the HWM to observe 'drain' events more easily. + highWaterMark: 1 + }); + + let chunksWritten = 0; + let drains = 0; + w.on('drain', () => drains++); + + function onWrite(err) { + if (err) { + assert.strictEqual(w.destroyed, true); + assert.strictEqual(err.code, 'ERR_STREAM_DESTROYED'); + } else { + chunksWritten++; + } + } + + w.write('abc', onWrite); + assert.strictEqual(chunksWritten, 0); + assert.strictEqual(drains, 0); + callbacks.shift()(); + assert.strictEqual(chunksWritten, 1); + assert.strictEqual(drains, 1); + + if (withPendingData) { + // Test 2 cases: There either is or is not data still in the write queue. + // (The second write will never actually get executed either way.) + w.write('def', onWrite); + } + if (useEnd) { + // Again, test 2 cases: Either we indicate that we want to end the + // writable or not. + w.end('ghi', onWrite); + } else { + w.write('ghi', onWrite); + } + + assert.strictEqual(chunksWritten, 1); + w.destroy(); + assert.strictEqual(chunksWritten, 1); + callbacks.shift()(); + assert.strictEqual(chunksWritten, useEnd && !withPendingData ? 1 : 2); + assert.strictEqual(callbacks.length, 0); + assert.strictEqual(drains, 1); + } +} diff --git a/test/js/node/test/parallel/test-stream-write-drain.js b/test/js/node/test/parallel/test-stream-write-drain.js new file mode 100644 index 0000000000..bd65c1fdbb --- /dev/null +++ b/test/js/node/test/parallel/test-stream-write-drain.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('../common'); +const { Writable } = require('stream'); + +// Don't emit 'drain' if ended + +const w = new Writable({ + write(data, enc, cb) { + process.nextTick(cb); + }, + highWaterMark: 1 +}); + +w.on('drain', common.mustNotCall()); +w.write('asd'); +w.end(); diff --git a/test/js/node/test/parallel/test-stream-write-final.js b/test/js/node/test/parallel/test-stream-write-final.js new file mode 100644 index 0000000000..56537bd7fa --- /dev/null +++ b/test/js/node/test/parallel/test-stream-write-final.js @@ -0,0 +1,24 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); +let shutdown = false; + +const w = new stream.Writable({ + final: common.mustCall(function(cb) { + assert.strictEqual(this, w); + setTimeout(function() { + shutdown = true; + cb(); + }, 100); + }), + write: function(chunk, e, cb) { + process.nextTick(cb); + } +}); +w.on('finish', common.mustCall(function() { + assert(shutdown); +})); +w.write(Buffer.allocUnsafe(1)); +w.end(Buffer.allocUnsafe(0)); diff --git a/test/js/node/test/parallel/test-stream-writev.js b/test/js/node/test/parallel/test-stream-writev.js new file mode 100644 index 0000000000..5a42411c6f --- /dev/null +++ b/test/js/node/test/parallel/test-stream-writev.js @@ -0,0 +1,130 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const stream = require('stream'); + +const queue = []; +for (let decode = 0; decode < 2; decode++) { + for (let uncork = 0; uncork < 2; uncork++) { + for (let multi = 0; multi < 2; multi++) { + queue.push([!!decode, !!uncork, !!multi]); + } + } +} + +run(); + +function run() { + const t = queue.pop(); + if (t) + test(t[0], t[1], t[2], run); + else + console.log('ok'); +} + +function test(decode, uncork, multi, next) { + console.log(`# decode=${decode} uncork=${uncork} multi=${multi}`); + let counter = 0; + let expectCount = 0; + function cnt(msg) { + expectCount++; + const expect = expectCount; + return function(er) { + assert.ifError(er); + counter++; + assert.strictEqual(counter, expect); + }; + } + + const w = new stream.Writable({ decodeStrings: decode }); + w._write = common.mustNotCall('Should not call _write'); + + const expectChunks = decode ? [ + { encoding: 'buffer', + chunk: [104, 101, 108, 108, 111, 44, 32] }, + { encoding: 'buffer', + chunk: [119, 111, 114, 108, 100] }, + { encoding: 'buffer', + chunk: [33] }, + { encoding: 'buffer', + chunk: [10, 97, 110, 100, 32, 116, 104, 101, 110, 46, 46, 46] }, + { encoding: 'buffer', + chunk: [250, 206, 190, 167, 222, 173, 190, 239, 222, 202, 251, 173] }, + ] : [ + { encoding: 'ascii', chunk: 'hello, ' }, + { encoding: 'utf8', chunk: 'world' }, + { encoding: 'buffer', chunk: [33] }, + { encoding: 'latin1', chunk: '\nand then...' }, + { encoding: 'hex', chunk: 'facebea7deadbeefdecafbad' }, + ]; + + let actualChunks; + w._writev = function(chunks, cb) { + actualChunks = chunks.map(function(chunk) { + return { + encoding: chunk.encoding, + chunk: Buffer.isBuffer(chunk.chunk) ? + Array.prototype.slice.call(chunk.chunk) : chunk.chunk + }; + }); + cb(); + }; + + w.cork(); + w.write('hello, ', 'ascii', cnt('hello')); + w.write('world', 'utf8', cnt('world')); + + if (multi) + w.cork(); + + w.write(Buffer.from('!'), 'buffer', cnt('!')); + w.write('\nand then...', 'latin1', cnt('and then')); + + if (multi) + w.uncork(); + + w.write('facebea7deadbeefdecafbad', 'hex', cnt('hex')); + + if (uncork) + w.uncork(); + + w.end(cnt('end')); + + w.on('finish', function() { + // Make sure finish comes after all the write cb + cnt('finish')(); + assert.deepStrictEqual(actualChunks, expectChunks); + next(); + }); +} + +{ + const w = new stream.Writable({ + writev: common.mustCall(function(chunks, cb) { + cb(); + }) + }); + w.write('asd', common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-stream2-base64-single-char-read-end.js b/test/js/node/test/parallel/test-stream2-base64-single-char-read-end.js new file mode 100644 index 0000000000..2e1eb15f9f --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-base64-single-char-read-end.js @@ -0,0 +1,56 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const { Readable: R, Writable: W } = require('stream'); +const assert = require('assert'); + +const src = new R({ encoding: 'base64' }); +const dst = new W(); +let hasRead = false; +const accum = []; + +src._read = function(n) { + if (!hasRead) { + hasRead = true; + process.nextTick(function() { + src.push(Buffer.from('1')); + src.push(null); + }); + } +}; + +dst._write = function(chunk, enc, cb) { + accum.push(chunk); + cb(); +}; + +src.on('end', function() { + assert.strictEqual(String(Buffer.concat(accum)), 'MQ=='); + clearTimeout(timeout); +}); + +src.pipe(dst); + +const timeout = setTimeout(function() { + assert.fail('timed out waiting for _write'); +}, 100); diff --git a/test/js/node/test/parallel/test-stream2-basic.js b/test/js/node/test/parallel/test-stream2-basic.js new file mode 100644 index 0000000000..2670deda53 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-basic.js @@ -0,0 +1,445 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +const { Readable: R, Writable: W } = require('stream'); +const assert = require('assert'); + +const EE = require('events').EventEmitter; + +class TestReader extends R { + constructor(n) { + super(); + this._buffer = Buffer.alloc(n || 100, 'x'); + this._pos = 0; + this._bufs = 10; + } + + _read(n) { + const max = this._buffer.length - this._pos; + n = Math.max(n, 0); + const toRead = Math.min(n, max); + if (toRead === 0) { + // Simulate the read buffer filling up with some more bytes some time + // in the future. + setTimeout(() => { + this._pos = 0; + this._bufs -= 1; + if (this._bufs <= 0) { + // read them all! + if (!this.ended) + this.push(null); + } else { + // now we have more. + // kinda cheating by calling _read, but whatever, + // it's just fake anyway. + this._read(n); + } + }, 10); + return; + } + + const ret = this._buffer.slice(this._pos, this._pos + toRead); + this._pos += toRead; + this.push(ret); + } +} + +class TestWriter extends EE { + constructor() { + super(); + this.received = []; + this.flush = false; + } + + write(c) { + this.received.push(c.toString()); + this.emit('write', c); + return true; + } + + end(c) { + if (c) this.write(c); + this.emit('end', this.received); + } +} + +{ + // Test basic functionality + const r = new TestReader(20); + + const reads = []; + const expect = [ 'x', + 'xx', + 'xxx', + 'xxxx', + 'xxxxx', + 'xxxxxxxxx', + 'xxxxxxxxxx', + 'xxxxxxxxxxxx', + 'xxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxxxx' ]; + + r.on('end', common.mustCall(function() { + assert.deepStrictEqual(reads, expect); + })); + + let readSize = 1; + function flow() { + let res; + while (null !== (res = r.read(readSize++))) { + reads.push(res.toString()); + } + r.once('readable', flow); + } + + flow(); +} + +{ + // Verify pipe + const r = new TestReader(5); + + const expect = [ 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx' ]; + + const w = new TestWriter(); + + w.on('end', common.mustCall(function(received) { + assert.deepStrictEqual(received, expect); + })); + + r.pipe(w); +} + + +[1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(function(SPLIT) { + // Verify unpipe + const r = new TestReader(5); + + // Unpipe after 3 writes, then write to another stream instead. + let expect = [ 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx' ]; + expect = [ expect.slice(0, SPLIT), expect.slice(SPLIT) ]; + + const w = [ new TestWriter(), new TestWriter() ]; + + let writes = SPLIT; + w[0].on('write', function() { + if (--writes === 0) { + r.unpipe(); + assert.deepStrictEqual(r._readableState.pipes, []); + w[0].end(); + r.pipe(w[1]); + assert.deepStrictEqual(r._readableState.pipes, [w[1]]); + } + }); + + let ended = 0; + + w[0].on('end', common.mustCall(function(results) { + ended++; + assert.strictEqual(ended, 1); + assert.deepStrictEqual(results, expect[0]); + })); + + w[1].on('end', common.mustCall(function(results) { + ended++; + assert.strictEqual(ended, 2); + assert.deepStrictEqual(results, expect[1]); + })); + + r.pipe(w[0]); +}); + + +{ + // Verify both writers get the same data when piping to destinations + const r = new TestReader(5); + const w = [ new TestWriter(), new TestWriter() ]; + + const expect = [ 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx' ]; + + w[0].on('end', common.mustCall(function(received) { + assert.deepStrictEqual(received, expect); + })); + w[1].on('end', common.mustCall(function(received) { + assert.deepStrictEqual(received, expect); + })); + + r.pipe(w[0]); + r.pipe(w[1]); +} + + +[1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(function(SPLIT) { + // Verify multi-unpipe + const r = new TestReader(5); + + // Unpipe after 3 writes, then write to another stream instead. + let expect = [ 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx' ]; + expect = [ expect.slice(0, SPLIT), expect.slice(SPLIT) ]; + + const w = [ new TestWriter(), new TestWriter(), new TestWriter() ]; + + let writes = SPLIT; + w[0].on('write', function() { + if (--writes === 0) { + r.unpipe(); + w[0].end(); + r.pipe(w[1]); + } + }); + + let ended = 0; + + w[0].on('end', common.mustCall(function(results) { + ended++; + assert.strictEqual(ended, 1); + assert.deepStrictEqual(results, expect[0]); + })); + + w[1].on('end', common.mustCall(function(results) { + ended++; + assert.strictEqual(ended, 2); + assert.deepStrictEqual(results, expect[1]); + })); + + r.pipe(w[0]); + r.pipe(w[2]); +}); + +{ + // Verify that back pressure is respected + const r = new R({ objectMode: true }); + r._read = common.mustNotCall(); + let counter = 0; + r.push(['one']); + r.push(['two']); + r.push(['three']); + r.push(['four']); + r.push(null); + + const w1 = new R(); + w1.write = function(chunk) { + assert.strictEqual(chunk[0], 'one'); + w1.emit('close'); + process.nextTick(function() { + r.pipe(w2); + r.pipe(w3); + }); + }; + w1.end = common.mustNotCall(); + + r.pipe(w1); + + const expected = ['two', 'two', 'three', 'three', 'four', 'four']; + + const w2 = new R(); + w2.write = function(chunk) { + assert.strictEqual(chunk[0], expected.shift()); + assert.strictEqual(counter, 0); + + counter++; + + if (chunk[0] === 'four') { + return true; + } + + setTimeout(function() { + counter--; + w2.emit('drain'); + }, 10); + + return false; + }; + w2.end = common.mustCall(); + + const w3 = new R(); + w3.write = function(chunk) { + assert.strictEqual(chunk[0], expected.shift()); + assert.strictEqual(counter, 1); + + counter++; + + if (chunk[0] === 'four') { + return true; + } + + setTimeout(function() { + counter--; + w3.emit('drain'); + }, 50); + + return false; + }; + w3.end = common.mustCall(function() { + assert.strictEqual(counter, 2); + assert.strictEqual(expected.length, 0); + }); +} + +{ + // Verify read(0) behavior for ended streams + const r = new R(); + let written = false; + let ended = false; + r._read = common.mustNotCall(); + + r.push(Buffer.from('foo')); + r.push(null); + + const v = r.read(0); + + assert.strictEqual(v, null); + + const w = new R(); + w.write = function(buffer) { + written = true; + assert.strictEqual(ended, false); + assert.strictEqual(buffer.toString(), 'foo'); + }; + + w.end = common.mustCall(function() { + ended = true; + assert.strictEqual(written, true); + }); + + r.pipe(w); +} + +{ + // Verify synchronous _read ending + const r = new R(); + let called = false; + r._read = function(n) { + r.push(null); + }; + + r.once('end', function() { + // Verify that this is called before the next tick + called = true; + }); + + r.read(); + + process.nextTick(function() { + assert.strictEqual(called, true); + }); +} + +{ + // Verify that adding readable listeners trigger data flow + const r = new R({ highWaterMark: 5 }); + let onReadable = false; + let readCalled = 0; + + r._read = function(n) { + if (readCalled++ === 2) + r.push(null); + else + r.push(Buffer.from('asdf')); + }; + + r.on('readable', function() { + onReadable = true; + r.read(); + }); + + r.on('end', common.mustCall(function() { + assert.strictEqual(readCalled, 3); + assert.ok(onReadable); + })); +} + +{ + // Verify that streams are chainable + const r = new R(); + r._read = common.mustCall(); + const r2 = r.setEncoding('utf8').pause().resume().pause(); + assert.strictEqual(r, r2); +} + +{ + // Verify readableEncoding property + assert(Object.hasOwn(R.prototype, 'readableEncoding')); + + const r = new R({ encoding: 'utf8' }); + assert.strictEqual(r.readableEncoding, 'utf8'); +} + +{ + // Verify readableObjectMode property + assert(Object.hasOwn(R.prototype, 'readableObjectMode')); + + const r = new R({ objectMode: true }); + assert.strictEqual(r.readableObjectMode, true); +} + +{ + // Verify writableObjectMode property + assert(Object.hasOwn(W.prototype, 'writableObjectMode')); + + const w = new W({ objectMode: true }); + assert.strictEqual(w.writableObjectMode, true); +} diff --git a/test/js/node/test/parallel/test-stream2-compatibility.js b/test/js/node/test/parallel/test-stream2-compatibility.js new file mode 100644 index 0000000000..d760db8b32 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-compatibility.js @@ -0,0 +1,70 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const { Readable: R, Writable: W } = require('stream'); +const assert = require('assert'); + +let ondataCalled = 0; + +class TestReader extends R { + constructor() { + super(); + this._buffer = Buffer.alloc(100, 'x'); + + this.on('data', () => { + ondataCalled++; + }); + } + + _read(n) { + this.push(this._buffer); + this._buffer = Buffer.alloc(0); + } +} + +const reader = new TestReader(); +setImmediate(function() { + assert.strictEqual(ondataCalled, 1); + console.log('ok'); + reader.push(null); +}); + +class TestWriter extends W { + constructor() { + super(); + this.write('foo'); + this.end(); + } + + _write(chunk, enc, cb) { + cb(); + } +} + +const writer = new TestWriter(); + +process.on('exit', function() { + assert.strictEqual(reader.readable, false); + assert.strictEqual(writer.writable, false); + console.log('ok'); +}); diff --git a/test/js/node/test/parallel/test-stream2-decode-partial.js b/test/js/node/test/parallel/test-stream2-decode-partial.js new file mode 100644 index 0000000000..9d9ae21bfe --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-decode-partial.js @@ -0,0 +1,23 @@ +'use strict'; +require('../common'); +const { Readable } = require('stream'); +const assert = require('assert'); + +let buf = ''; +const euro = Buffer.from([0xE2, 0x82, 0xAC]); +const cent = Buffer.from([0xC2, 0xA2]); +const source = Buffer.concat([euro, cent]); + +const readable = Readable({ encoding: 'utf8' }); +readable.push(source.slice(0, 2)); +readable.push(source.slice(2, 4)); +readable.push(source.slice(4, 6)); +readable.push(null); + +readable.on('data', function(data) { + buf += data; +}); + +process.on('exit', function() { + assert.strictEqual(buf, '€¢'); +}); diff --git a/test/js/node/test/parallel/test-stream2-finish-pipe-error.js b/test/js/node/test/parallel/test-stream2-finish-pipe-error.js new file mode 100644 index 0000000000..a603e154b9 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-finish-pipe-error.js @@ -0,0 +1,20 @@ +'use strict'; +const common = require('../common'); +const stream = require('stream'); + +process.on('uncaughtException', common.mustCall()); + +const r = new stream.Readable(); +r._read = function(size) { + r.push(Buffer.allocUnsafe(size)); +}; + +const w = new stream.Writable(); +w._write = function(data, encoding, cb) { + cb(null); +}; + +r.pipe(w); + +// end() after pipe should cause unhandled exception +w.end(); diff --git a/test/js/node/test/parallel/test-stream2-finish-pipe.js b/test/js/node/test/parallel/test-stream2-finish-pipe.js new file mode 100644 index 0000000000..5e2969aad4 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-finish-pipe.js @@ -0,0 +1,44 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const stream = require('stream'); + +const r = new stream.Readable(); +r._read = function(size) { + r.push(Buffer.allocUnsafe(size)); +}; + +const w = new stream.Writable(); +w._write = function(data, encoding, cb) { + process.nextTick(cb, null); +}; + +r.pipe(w); + +// end() must be called in nextTick or a WRITE_AFTER_END error occurs. +process.nextTick(() => { + // This might sound unrealistic, but it happens in net.js. When + // socket.allowHalfOpen === false, EOF will cause .destroySoon() call which + // ends the writable side of net.Socket. + w.end(); +}); diff --git a/test/js/node/test/parallel/test-stream2-large-read-stall.js b/test/js/node/test/parallel/test-stream2-large-read-stall.js new file mode 100644 index 0000000000..2d44bb7f78 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-large-read-stall.js @@ -0,0 +1,74 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// If everything aligns so that you do a read(n) of exactly the +// remaining buffer, then make sure that 'end' still emits. + +const READSIZE = 100; +const PUSHSIZE = 20; +const PUSHCOUNT = 1000; +const HWM = 50; + +const Readable = require('stream').Readable; +const r = new Readable({ + highWaterMark: HWM +}); +const rs = r._readableState; + +r._read = push; + +r.on('readable', function() { + console.error('>> readable'); + let ret; + do { + console.error(` > read(${READSIZE})`); + ret = r.read(READSIZE); + console.error(` < ${ret && ret.length} (${rs.length} remain)`); + } while (ret && ret.length === READSIZE); + + console.error('<< after read()', + ret && ret.length, + rs.needReadable, + rs.length); +}); + +r.on('end', common.mustCall(function() { + assert.strictEqual(pushes, PUSHCOUNT + 1); +})); + +let pushes = 0; +function push() { + if (pushes > PUSHCOUNT) + return; + + if (pushes++ === PUSHCOUNT) { + console.error(' push(EOF)'); + return r.push(null); + } + + console.error(` push #${pushes}`); + if (r.push(Buffer.allocUnsafe(PUSHSIZE))) + setTimeout(push, 1); +} diff --git a/test/js/node/test/parallel/test-stream2-objects.js b/test/js/node/test/parallel/test-stream2-objects.js new file mode 100644 index 0000000000..b7ad074628 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-objects.js @@ -0,0 +1,297 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const common = require('../common'); +const { Readable, Writable } = require('stream'); +const assert = require('assert'); + +function toArray(callback) { + const stream = new Writable({ objectMode: true }); + const list = []; + stream.write = function(chunk) { + list.push(chunk); + }; + + stream.end = common.mustCall(function() { + callback(list); + }); + + return stream; +} + +function fromArray(list) { + const r = new Readable({ objectMode: true }); + r._read = common.mustNotCall(); + list.forEach(function(chunk) { + r.push(chunk); + }); + r.push(null); + + return r; +} + +{ + // Verify that objects can be read from the stream + const r = fromArray([{ one: '1' }, { two: '2' }]); + + const v1 = r.read(); + const v2 = r.read(); + const v3 = r.read(); + + assert.deepStrictEqual(v1, { one: '1' }); + assert.deepStrictEqual(v2, { two: '2' }); + assert.strictEqual(v3, null); +} + +{ + // Verify that objects can be piped into the stream + const r = fromArray([{ one: '1' }, { two: '2' }]); + + r.pipe(toArray(common.mustCall(function(list) { + assert.deepStrictEqual(list, [ + { one: '1' }, + { two: '2' }, + ]); + }))); +} + +{ + // Verify that read(n) is ignored + const r = fromArray([{ one: '1' }, { two: '2' }]); + const value = r.read(2); + + assert.deepStrictEqual(value, { one: '1' }); +} + +{ + // Verify that objects can be synchronously read + const r = new Readable({ objectMode: true }); + const list = [{ one: '1' }, { two: '2' }]; + r._read = function(n) { + const item = list.shift(); + r.push(item || null); + }; + + r.pipe(toArray(common.mustCall(function(list) { + assert.deepStrictEqual(list, [ + { one: '1' }, + { two: '2' }, + ]); + }))); +} + +{ + // Verify that objects can be asynchronously read + const r = new Readable({ objectMode: true }); + const list = [{ one: '1' }, { two: '2' }]; + r._read = function(n) { + const item = list.shift(); + process.nextTick(function() { + r.push(item || null); + }); + }; + + r.pipe(toArray(common.mustCall(function(list) { + assert.deepStrictEqual(list, [ + { one: '1' }, + { two: '2' }, + ]); + }))); +} + +{ + // Verify that strings can be read as objects + const r = new Readable({ + objectMode: true + }); + r._read = common.mustNotCall(); + const list = ['one', 'two', 'three']; + list.forEach(function(str) { + r.push(str); + }); + r.push(null); + + r.pipe(toArray(common.mustCall(function(array) { + assert.deepStrictEqual(array, list); + }))); +} + +{ + // Verify read(0) behavior for object streams + const r = new Readable({ + objectMode: true + }); + r._read = common.mustNotCall(); + + r.push('foobar'); + r.push(null); + + r.pipe(toArray(common.mustCall(function(array) { + assert.deepStrictEqual(array, ['foobar']); + }))); +} + +{ + // Verify the behavior of pushing falsey values + const r = new Readable({ + objectMode: true + }); + r._read = common.mustNotCall(); + + r.push(false); + r.push(0); + r.push(''); + r.push(null); + + r.pipe(toArray(common.mustCall(function(array) { + assert.deepStrictEqual(array, [false, 0, '']); + }))); +} + +{ + // Verify high watermark _read() behavior + const r = new Readable({ + highWaterMark: 6, + objectMode: true + }); + let calls = 0; + const list = ['1', '2', '3', '4', '5', '6', '7', '8']; + + r._read = function(n) { + calls++; + }; + + list.forEach(function(c) { + r.push(c); + }); + + const v = r.read(); + + assert.strictEqual(calls, 0); + assert.strictEqual(v, '1'); + + const v2 = r.read(); + assert.strictEqual(v2, '2'); + + const v3 = r.read(); + assert.strictEqual(v3, '3'); + + assert.strictEqual(calls, 1); +} + +{ + // Verify high watermark push behavior + const r = new Readable({ + highWaterMark: 6, + objectMode: true + }); + r._read = common.mustNotCall(); + for (let i = 0; i < 6; i++) { + const bool = r.push(i); + assert.strictEqual(bool, i !== 5); + } +} + +{ + // Verify that objects can be written to stream + const w = new Writable({ objectMode: true }); + + w._write = function(chunk, encoding, cb) { + assert.deepStrictEqual(chunk, { foo: 'bar' }); + cb(); + }; + + w.on('finish', common.mustCall()); + w.write({ foo: 'bar' }); + w.end(); +} + +{ + // Verify that multiple objects can be written to stream + const w = new Writable({ objectMode: true }); + const list = []; + + w._write = function(chunk, encoding, cb) { + list.push(chunk); + cb(); + }; + + w.on('finish', common.mustCall(function() { + assert.deepStrictEqual(list, [0, 1, 2, 3, 4]); + })); + + w.write(0); + w.write(1); + w.write(2); + w.write(3); + w.write(4); + w.end(); +} + +{ + // Verify that strings can be written as objects + const w = new Writable({ + objectMode: true + }); + const list = []; + + w._write = function(chunk, encoding, cb) { + list.push(chunk); + process.nextTick(cb); + }; + + w.on('finish', common.mustCall(function() { + assert.deepStrictEqual(list, ['0', '1', '2', '3', '4']); + })); + + w.write('0'); + w.write('1'); + w.write('2'); + w.write('3'); + w.write('4'); + w.end(); +} + +{ + // Verify that stream buffers finish until callback is called + const w = new Writable({ + objectMode: true + }); + let called = false; + + w._write = function(chunk, encoding, cb) { + assert.strictEqual(chunk, 'foo'); + + process.nextTick(function() { + called = true; + cb(); + }); + }; + + w.on('finish', common.mustCall(function() { + assert.strictEqual(called, true); + })); + + w.write('foo'); + w.end(); +} diff --git a/test/js/node/test/parallel/test-stream2-pipe-error-handling.js b/test/js/node/test/parallel/test-stream2-pipe-error-handling.js new file mode 100644 index 0000000000..d3f4838105 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-pipe-error-handling.js @@ -0,0 +1,106 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const stream = require('stream'); + +{ + let count = 1000; + + const source = new stream.Readable(); + source._read = function(n) { + n = Math.min(count, n); + count -= n; + source.push(Buffer.allocUnsafe(n)); + }; + + let unpipedDest; + source.unpipe = function(dest) { + unpipedDest = dest; + stream.Readable.prototype.unpipe.call(this, dest); + }; + + const dest = new stream.Writable(); + dest._write = function(chunk, encoding, cb) { + cb(); + }; + + source.pipe(dest); + + let gotErr = null; + dest.on('error', function(err) { + gotErr = err; + }); + + let unpipedSource; + dest.on('unpipe', function(src) { + unpipedSource = src; + }); + + const err = new Error('This stream turned into bacon.'); + dest.emit('error', err); + assert.strictEqual(gotErr, err); + assert.strictEqual(unpipedSource, source); + assert.strictEqual(unpipedDest, dest); +} + +{ + let count = 1000; + + const source = new stream.Readable(); + source._read = function(n) { + n = Math.min(count, n); + count -= n; + source.push(Buffer.allocUnsafe(n)); + }; + + let unpipedDest; + source.unpipe = function(dest) { + unpipedDest = dest; + stream.Readable.prototype.unpipe.call(this, dest); + }; + + const dest = new stream.Writable({ autoDestroy: false }); + dest._write = function(chunk, encoding, cb) { + cb(); + }; + + source.pipe(dest); + + let unpipedSource; + dest.on('unpipe', function(src) { + unpipedSource = src; + }); + + const err = new Error('This stream turned into bacon.'); + + let gotErr = null; + try { + dest.emit('error', err); + } catch (e) { + gotErr = e; + } + assert.strictEqual(gotErr, err); + assert.strictEqual(unpipedSource, source); + assert.strictEqual(unpipedDest, dest); +} diff --git a/test/js/node/test/parallel/test-stream2-pipe-error-once-listener.js b/test/js/node/test/parallel/test-stream2-pipe-error-once-listener.js new file mode 100644 index 0000000000..003e78e64f --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-pipe-error-once-listener.js @@ -0,0 +1,53 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +require('../common'); +const stream = require('stream'); + +class Read extends stream.Readable { + _read(size) { + this.push('x'); + this.push(null); + } +} + +class Write extends stream.Writable { + _write(buffer, encoding, cb) { + this.emit('error', new Error('boom')); + this.emit('alldone'); + } +} + +const read = new Read(); +const write = new Write(); + +write.once('error', () => {}); +write.once('alldone', function(err) { + console.log('ok'); +}); + +process.on('exit', function(c) { + console.error('error thrown even with listener'); +}); + +read.pipe(write); diff --git a/test/js/node/test/parallel/test-stream2-push.js b/test/js/node/test/parallel/test-stream2-push.js new file mode 100644 index 0000000000..748a77b9c4 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-push.js @@ -0,0 +1,136 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); + +const EE = require('events').EventEmitter; + + +// A mock thing a bit like the net.Socket/tcp_wrap.handle interaction + +const stream = new Readable({ + highWaterMark: 16, + encoding: 'utf8' +}); + +const source = new EE(); + +stream._read = function() { + console.error('stream._read'); + readStart(); +}; + +let ended = false; +stream.on('end', function() { + ended = true; +}); + +source.on('data', function(chunk) { + const ret = stream.push(chunk); + console.error('data', stream.readableLength); + if (!ret) + readStop(); +}); + +source.on('end', function() { + stream.push(null); +}); + +let reading = false; + +function readStart() { + console.error('readStart'); + reading = true; +} + +function readStop() { + console.error('readStop'); + reading = false; + process.nextTick(function() { + const r = stream.read(); + if (r !== null) + writer.write(r); + }); +} + +const writer = new Writable({ + decodeStrings: false +}); + +const written = []; + +const expectWritten = + [ 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg' ]; + +writer._write = function(chunk, encoding, cb) { + console.error(`WRITE ${chunk}`); + written.push(chunk); + process.nextTick(cb); +}; + +writer.on('finish', finish); + + +// Now emit some chunks. + +const chunk = 'asdfg'; + +let set = 0; +readStart(); +data(); +function data() { + assert(reading); + source.emit('data', chunk); + assert(reading); + source.emit('data', chunk); + assert(reading); + source.emit('data', chunk); + assert(reading); + source.emit('data', chunk); + assert(!reading); + if (set++ < 5) + setTimeout(data, 10); + else + end(); +} + +function finish() { + console.error('finish'); + assert.deepStrictEqual(written, expectWritten); + console.log('ok'); +} + +function end() { + source.emit('end'); + assert(!reading); + writer.end(stream.read()); + setImmediate(function() { + assert(ended); + }); +} diff --git a/test/js/node/test/parallel/test-stream2-read-sync-stack.js b/test/js/node/test/parallel/test-stream2-read-sync-stack.js new file mode 100644 index 0000000000..e6a5ea7e52 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-read-sync-stack.js @@ -0,0 +1,46 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const Readable = require('stream').Readable; + +// This tests synchronous read callbacks and verifies that even if they nest +// heavily the process handles it without an error + +const r = new Readable(); +const N = 256 * 1024; + +let reads = 0; +r._read = function(n) { + const chunk = reads++ === N ? null : Buffer.allocUnsafe(1); + r.push(chunk); +}; + +r.on('readable', function onReadable() { + if (!(r.readableLength % 256)) + console.error('readable', r.readableLength); + r.read(N * 2); +}); + +r.on('end', common.mustCall()); + +r.read(0); diff --git a/test/js/node/test/parallel/test-stream2-readable-empty-buffer-no-eof.js b/test/js/node/test/parallel/test-stream2-readable-empty-buffer-no-eof.js new file mode 100644 index 0000000000..7be2c358ee --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-readable-empty-buffer-no-eof.js @@ -0,0 +1,117 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const Readable = require('stream').Readable; + +test1(); +test2(); + +function test1() { + const r = new Readable(); + + // Should not end when we get a Buffer.alloc(0) or '' as the _read + // result that just means that there is *temporarily* no data, but to + // go ahead and try again later. + // + // note that this is very unusual. it only works for crypto streams + // because the other side of the stream will call read(0) to cycle + // data through openssl. that's why setImmediate() is used to call + // r.read(0) again later, otherwise there is no more work being done + // and the process just exits. + + const buf = Buffer.alloc(5, 'x'); + let reads = 5; + r._read = function(n) { + switch (reads--) { + case 5: + return setImmediate(() => { + return r.push(buf); + }); + case 4: + setImmediate(() => { + return r.push(Buffer.alloc(0)); + }); + return setImmediate(r.read.bind(r, 0)); + case 3: + setImmediate(r.read.bind(r, 0)); + return process.nextTick(() => { + return r.push(Buffer.alloc(0)); + }); + case 2: + setImmediate(r.read.bind(r, 0)); + return r.push(Buffer.alloc(0)); // Not-EOF! + case 1: + return r.push(buf); + case 0: + return r.push(null); // EOF + default: + throw new Error('unreachable'); + } + }; + + const results = []; + function flow() { + let chunk; + while (null !== (chunk = r.read())) + results.push(String(chunk)); + } + r.on('readable', flow); + r.on('end', () => { + results.push('EOF'); + }); + flow(); + + process.on('exit', () => { + assert.deepStrictEqual(results, [ 'xxxxx', 'xxxxx', 'EOF' ]); + console.log('ok'); + }); +} + +function test2() { + const r = new Readable({ encoding: 'base64' }); + let reads = 5; + r._read = function(n) { + if (!reads--) + return r.push(null); // EOF + return r.push(Buffer.from('x')); + }; + + const results = []; + function flow() { + let chunk; + while (null !== (chunk = r.read())) + results.push(String(chunk)); + } + r.on('readable', flow); + r.on('end', () => { + results.push('EOF'); + }); + flow(); + + process.on('exit', () => { + assert.deepStrictEqual(results, [ 'eHh4', 'eHg=', 'EOF' ]); + console.log('ok'); + }); +} diff --git a/test/js/node/test/parallel/test-stream2-readable-legacy-drain.js b/test/js/node/test/parallel/test-stream2-readable-legacy-drain.js new file mode 100644 index 0000000000..beb3657776 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-readable-legacy-drain.js @@ -0,0 +1,55 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const Stream = require('stream'); +const Readable = Stream.Readable; + +const r = new Readable(); +const N = 256; +let reads = 0; +r._read = function(n) { + return r.push(++reads === N ? null : Buffer.allocUnsafe(1)); +}; + +r.on('end', common.mustCall()); + +const w = new Stream(); +w.writable = true; +let buffered = 0; +w.write = function(c) { + buffered += c.length; + process.nextTick(drain); + return false; +}; + +function drain() { + assert(buffered <= 3); + buffered = 0; + w.emit('drain'); +} + +w.end = common.mustCall(); + +r.pipe(w); diff --git a/test/js/node/test/parallel/test-stream2-readable-non-empty-end.js b/test/js/node/test/parallel/test-stream2-readable-non-empty-end.js new file mode 100644 index 0000000000..417f2c3b0e --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-readable-non-empty-end.js @@ -0,0 +1,72 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable } = require('stream'); + +let len = 0; +const chunks = new Array(10); +for (let i = 1; i <= 10; i++) { + chunks[i - 1] = Buffer.allocUnsafe(i); + len += i; +} + +const test = new Readable(); +let n = 0; +test._read = function(size) { + const chunk = chunks[n++]; + setTimeout(function() { + test.push(chunk === undefined ? null : chunk); + }, 1); +}; + +test.on('end', thrower); +function thrower() { + throw new Error('this should not happen!'); +} + +let bytesread = 0; +test.on('readable', function() { + const b = len - bytesread - 1; + const res = test.read(b); + if (res) { + bytesread += res.length; + console.error(`br=${bytesread} len=${len}`); + setTimeout(next, 1); + } + test.read(0); +}); +test.read(0); + +function next() { + // Now let's make 'end' happen + test.removeListener('end', thrower); + test.on('end', common.mustCall()); + + // One to get the last byte + let r = test.read(); + assert(r); + assert.strictEqual(r.length, 1); + r = test.read(); + assert.strictEqual(r, null); +} diff --git a/test/js/node/test/parallel/test-stream2-readable-wrap-destroy.js b/test/js/node/test/parallel/test-stream2-readable-wrap-destroy.js new file mode 100644 index 0000000000..e310ae09e6 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-readable-wrap-destroy.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); + +const { Readable } = require('stream'); +const EE = require('events').EventEmitter; + +const oldStream = new EE(); +oldStream.pause = () => {}; +oldStream.resume = () => {}; + +{ + new Readable({ + autoDestroy: false, + destroy: common.mustCall() + }) + .wrap(oldStream); + oldStream.emit('destroy'); +} + +{ + new Readable({ + autoDestroy: false, + destroy: common.mustCall() + }) + .wrap(oldStream); + oldStream.emit('close'); +} diff --git a/test/js/node/test/parallel/test-stream2-readable-wrap-empty.js b/test/js/node/test/parallel/test-stream2-readable-wrap-empty.js new file mode 100644 index 0000000000..3dbbdaa9b5 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-readable-wrap-empty.js @@ -0,0 +1,38 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +const { Readable } = require('stream'); +const EE = require('events').EventEmitter; + +const oldStream = new EE(); +oldStream.pause = () => {}; +oldStream.resume = () => {}; + +const newStream = new Readable().wrap(oldStream); + +newStream + .on('readable', () => {}) + .on('end', common.mustCall()); + +oldStream.emit('end'); diff --git a/test/js/node/test/parallel/test-stream2-readable-wrap-error.js b/test/js/node/test/parallel/test-stream2-readable-wrap-error.js new file mode 100644 index 0000000000..2d2c26e2ca --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-readable-wrap-error.js @@ -0,0 +1,37 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { Readable } = require('stream'); +const EE = require('events').EventEmitter; + +class LegacyStream extends EE { + pause() {} + resume() {} +} + +{ + const err = new Error(); + const oldStream = new LegacyStream(); + const r = new Readable({ autoDestroy: true }) + .wrap(oldStream) + .on('error', common.mustCall(() => { + assert.strictEqual(r._readableState.errorEmitted, true); + assert.strictEqual(r._readableState.errored, err); + assert.strictEqual(r.destroyed, true); + })); + oldStream.emit('error', err); +} + +{ + const err = new Error(); + const oldStream = new LegacyStream(); + const r = new Readable({ autoDestroy: false }) + .wrap(oldStream) + .on('error', common.mustCall(() => { + assert.strictEqual(r._readableState.errorEmitted, true); + assert.strictEqual(r._readableState.errored, err); + assert.strictEqual(r.destroyed, false); + })); + oldStream.emit('error', err); +} diff --git a/test/js/node/test/parallel/test-stream2-readable-wrap.js b/test/js/node/test/parallel/test-stream2-readable-wrap.js new file mode 100644 index 0000000000..eebe72bc0d --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-readable-wrap.js @@ -0,0 +1,100 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable, Writable } = require('stream'); +const EE = require('events').EventEmitter; + +function runTest(highWaterMark, objectMode, produce) { + + const old = new EE(); + const r = new Readable({ highWaterMark, objectMode }); + assert.strictEqual(r, r.wrap(old)); + + r.on('end', common.mustCall()); + + old.pause = function() { + old.emit('pause'); + flowing = false; + }; + + old.resume = function() { + old.emit('resume'); + flow(); + }; + + // Make sure pause is only emitted once. + let pausing = false; + r.on('pause', () => { + assert.strictEqual(pausing, false); + pausing = true; + process.nextTick(() => { + pausing = false; + }); + }); + + let flowing; + let chunks = 10; + let oldEnded = false; + const expected = []; + function flow() { + flowing = true; + while (flowing && chunks-- > 0) { + const item = produce(); + expected.push(item); + old.emit('data', item); + } + if (chunks <= 0) { + oldEnded = true; + old.emit('end'); + } + } + + const w = new Writable({ highWaterMark: highWaterMark * 2, + objectMode }); + const written = []; + w._write = function(chunk, encoding, cb) { + written.push(chunk); + setTimeout(cb, 1); + }; + + w.on('finish', common.mustCall(function() { + performAsserts(); + })); + + r.pipe(w); + + flow(); + + function performAsserts() { + assert(oldEnded); + assert.deepStrictEqual(written, expected); + } +} + +runTest(100, false, function() { return Buffer.allocUnsafe(100); }); +runTest(10, false, function() { return Buffer.from('xxxxxxxxxx'); }); +runTest(1, true, function() { return { foo: 'bar' }; }); + +const objectChunks = [ 5, 'a', false, 0, '', 'xyz', { x: 4 }, 7, [], 555 ]; +runTest(1, true, function() { return objectChunks.shift(); }); diff --git a/test/js/node/test/parallel/test-stream2-set-encoding.js b/test/js/node/test/parallel/test-stream2-set-encoding.js new file mode 100644 index 0000000000..2d35b161bf --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-set-encoding.js @@ -0,0 +1,323 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Readable: R } = require('stream'); + +class TestReader extends R { + constructor(n, opts) { + super(opts); + this.pos = 0; + this.len = n || 100; + } + + _read(n) { + setTimeout(() => { + if (this.pos >= this.len) { + // Double push(null) to test eos handling + this.push(null); + return this.push(null); + } + + n = Math.min(n, this.len - this.pos); + if (n <= 0) { + // Double push(null) to test eos handling + this.push(null); + return this.push(null); + } + + this.pos += n; + const ret = Buffer.alloc(n, 'a'); + + return this.push(ret); + }, 1); + } +} + +{ + // Verify utf8 encoding + const tr = new TestReader(100); + tr.setEncoding('utf8'); + const out = []; + const expect = + [ 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + + +{ + // Verify hex encoding + const tr = new TestReader(100); + tr.setEncoding('hex'); + const out = []; + const expect = + [ '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify hex encoding with read(13) + const tr = new TestReader(100); + tr.setEncoding('hex'); + const out = []; + const expect = + [ '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '16161' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(13))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify base64 encoding + const tr = new TestReader(100); + tr.setEncoding('base64'); + const out = []; + const expect = + [ 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYQ==' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify utf8 encoding + const tr = new TestReader(100, { encoding: 'utf8' }); + const out = []; + const expect = + [ 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + + +{ + // Verify hex encoding + const tr = new TestReader(100, { encoding: 'hex' }); + const out = []; + const expect = + [ '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify hex encoding with read(13) + const tr = new TestReader(100, { encoding: 'hex' }); + const out = []; + const expect = + [ '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '1616161616161', + '6161616161616', + '16161' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(13))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify base64 encoding + const tr = new TestReader(100, { encoding: 'base64' }); + const out = []; + const expect = + [ 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYQ==' ]; + + tr.on('readable', function flow() { + let chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', common.mustCall(function() { + assert.deepStrictEqual(out, expect); + })); +} + +{ + // Verify chaining behavior + const tr = new TestReader(100); + assert.deepStrictEqual(tr.setEncoding('utf8'), tr); +} diff --git a/test/js/node/test/parallel/test-stream2-unpipe-drain.js b/test/js/node/test/parallel/test-stream2-unpipe-drain.js new file mode 100644 index 0000000000..4c283df680 --- /dev/null +++ b/test/js/node/test/parallel/test-stream2-unpipe-drain.js @@ -0,0 +1,72 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const stream = require('stream'); + +class TestWriter extends stream.Writable { + _write(buffer, encoding, callback) { + console.log('write called'); + // Super slow write stream (callback never called) + } +} + +const dest = new TestWriter(); + +class TestReader extends stream.Readable { + constructor() { + super(); + this.reads = 0; + } + + _read(size) { + this.reads += 1; + this.push(Buffer.alloc(size)); + } +} + +const src1 = new TestReader(); +const src2 = new TestReader(); + +src1.pipe(dest); + +src1.once('readable', () => { + process.nextTick(() => { + + src2.pipe(dest); + + src2.once('readable', () => { + process.nextTick(() => { + + src1.unpipe(dest); + }); + }); + }); +}); + + +process.on('exit', () => { + assert.strictEqual(src1.reads, 2); + assert.strictEqual(src2.reads, 2); +}); diff --git a/test/js/node/test/parallel/test-stream3-cork-end.js b/test/js/node/test/parallel/test-stream3-cork-end.js new file mode 100644 index 0000000000..0cbc033a2e --- /dev/null +++ b/test/js/node/test/parallel/test-stream3-cork-end.js @@ -0,0 +1,91 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const stream = require('stream'); +const Writable = stream.Writable; + +// Test the buffering behavior of Writable streams. +// +// The call to cork() triggers storing chunks which are flushed +// on calling end() and the stream subsequently ended. +// +// node version target: 0.12 + +const expectedChunks = ['please', 'buffer', 'me', 'kindly']; +const inputChunks = expectedChunks.slice(0); +let seenChunks = []; +let seenEnd = false; + +const w = new Writable(); +// Let's arrange to store the chunks. +w._write = function(chunk, encoding, cb) { + // Stream end event is not seen before the last write. + assert.ok(!seenEnd); + // Default encoding given none was specified. + assert.strictEqual(encoding, 'buffer'); + + seenChunks.push(chunk); + cb(); +}; +// Let's record the stream end event. +w.on('finish', () => { + seenEnd = true; +}); + +function writeChunks(remainingChunks, callback) { + const writeChunk = remainingChunks.shift(); + let writeState; + + if (writeChunk) { + setImmediate(() => { + writeState = w.write(writeChunk); + // We were not told to stop writing. + assert.ok(writeState); + + writeChunks(remainingChunks, callback); + }); + } else { + callback(); + } +} + +// Do an initial write. +w.write('stuff'); +// The write was immediate. +assert.strictEqual(seenChunks.length, 1); +// Reset the seen chunks. +seenChunks = []; + +// Trigger stream buffering. +w.cork(); + +// Write the bufferedChunks. +writeChunks(inputChunks, () => { + // Should not have seen anything yet. + assert.strictEqual(seenChunks.length, 0); + + // Trigger flush and ending the stream. + w.end(); + + // Stream should not ended in current tick. + assert.ok(!seenEnd); + + // Buffered bytes should be seen in current tick. + assert.strictEqual(seenChunks.length, 4); + + // Did the chunks match. + for (let i = 0, l = expectedChunks.length; i < l; i++) { + const seen = seenChunks[i]; + // There was a chunk. + assert.ok(seen); + + const expected = Buffer.from(expectedChunks[i]); + // It was what we expected. + assert.ok(seen.equals(expected)); + } + + setImmediate(() => { + // Stream should have ended in next tick. + assert.ok(seenEnd); + }); +}); diff --git a/test/js/node/test/parallel/test-stream3-cork-uncork.js b/test/js/node/test/parallel/test-stream3-cork-uncork.js new file mode 100644 index 0000000000..dfb901af41 --- /dev/null +++ b/test/js/node/test/parallel/test-stream3-cork-uncork.js @@ -0,0 +1,86 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const stream = require('stream'); +const Writable = stream.Writable; + +// Test the buffering behavior of Writable streams. +// +// The call to cork() triggers storing chunks which are flushed +// on calling uncork() in the same tick. +// +// node version target: 0.12 + +const expectedChunks = ['please', 'buffer', 'me', 'kindly']; +const inputChunks = expectedChunks.slice(0); +let seenChunks = []; +let seenEnd = false; + +const w = new Writable(); +// Let's arrange to store the chunks. +w._write = function(chunk, encoding, cb) { + // Default encoding given none was specified. + assert.strictEqual(encoding, 'buffer'); + + seenChunks.push(chunk); + cb(); +}; +// Let's record the stream end event. +w.on('finish', () => { + seenEnd = true; +}); + +function writeChunks(remainingChunks, callback) { + const writeChunk = remainingChunks.shift(); + let writeState; + + if (writeChunk) { + setImmediate(() => { + writeState = w.write(writeChunk); + // We were not told to stop writing. + assert.ok(writeState); + + writeChunks(remainingChunks, callback); + }); + } else { + callback(); + } +} + +// Do an initial write. +w.write('stuff'); +// The write was immediate. +assert.strictEqual(seenChunks.length, 1); +// Reset the chunks seen so far. +seenChunks = []; + +// Trigger stream buffering. +w.cork(); + +// Write the bufferedChunks. +writeChunks(inputChunks, () => { + // Should not have seen anything yet. + assert.strictEqual(seenChunks.length, 0); + + // Trigger writing out the buffer. + w.uncork(); + + // Buffered bytes should be seen in current tick. + assert.strictEqual(seenChunks.length, 4); + + // Did the chunks match. + for (let i = 0, l = expectedChunks.length; i < l; i++) { + const seen = seenChunks[i]; + // There was a chunk. + assert.ok(seen); + + const expected = Buffer.from(expectedChunks[i]); + // It was what we expected. + assert.ok(seen.equals(expected)); + } + + setImmediate(() => { + // The stream should not have been ended. + assert.ok(!seenEnd); + }); +}); diff --git a/test/js/node/test/parallel/test-stream3-pause-then-read.js b/test/js/node/test/parallel/test-stream3-pause-then-read.js new file mode 100644 index 0000000000..1a38547220 --- /dev/null +++ b/test/js/node/test/parallel/test-stream3-pause-then-read.js @@ -0,0 +1,170 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const stream = require('stream'); +const Readable = stream.Readable; +const Writable = stream.Writable; + +const totalChunks = 100; +const chunkSize = 99; +const expectTotalData = totalChunks * chunkSize; +let expectEndingData = expectTotalData; + +const r = new Readable({ highWaterMark: 1000 }); +let chunks = totalChunks; +r._read = function(n) { + console.log('_read called', chunks); + if (!(chunks % 2)) + setImmediate(push); + else if (!(chunks % 3)) + process.nextTick(push); + else + push(); +}; + +let totalPushed = 0; +function push() { + const chunk = chunks-- > 0 ? Buffer.alloc(chunkSize, 'x') : null; + if (chunk) { + totalPushed += chunk.length; + } + console.log('chunks', chunks); + r.push(chunk); +} + +read100(); + +// First we read 100 bytes. +function read100() { + readn(100, onData); +} + +function readn(n, then) { + console.error(`read ${n}`); + expectEndingData -= n; + (function read() { + const c = r.read(n); + console.error('c', c); + if (!c) + r.once('readable', read); + else { + assert.strictEqual(c.length, n); + assert(!r.readableFlowing); + then(); + } + })(); +} + +// Then we listen to some data events. +function onData() { + expectEndingData -= 100; + console.error('onData'); + let seen = 0; + r.on('data', function od(c) { + seen += c.length; + if (seen >= 100) { + // Seen enough + r.removeListener('data', od); + r.pause(); + if (seen > 100) { + // Oh no, seen too much! + // Put the extra back. + const diff = seen - 100; + r.unshift(c.slice(c.length - diff)); + console.error('seen too much', seen, diff); + } + + // Nothing should be lost in-between. + setImmediate(pipeLittle); + } + }); +} + +// Just pipe 200 bytes, then unshift the extra and unpipe. +function pipeLittle() { + expectEndingData -= 200; + console.error('pipe a little'); + const w = new Writable(); + let written = 0; + w.on('finish', () => { + assert.strictEqual(written, 200); + setImmediate(read1234); + }); + w._write = function(chunk, encoding, cb) { + written += chunk.length; + if (written >= 200) { + r.unpipe(w); + w.end(); + cb(); + if (written > 200) { + const diff = written - 200; + written -= diff; + r.unshift(chunk.slice(chunk.length - diff)); + } + } else { + setImmediate(cb); + } + }; + r.pipe(w); +} + +// Now read 1234 more bytes. +function read1234() { + readn(1234, resumePause); +} + +function resumePause() { + console.error('resumePause'); + // Don't read anything, just resume and re-pause a whole bunch. + r.resume(); + r.pause(); + r.resume(); + r.pause(); + r.resume(); + r.pause(); + r.resume(); + r.pause(); + r.resume(); + r.pause(); + setImmediate(pipe); +} + + +function pipe() { + console.error('pipe the rest'); + const w = new Writable(); + let written = 0; + w._write = function(chunk, encoding, cb) { + written += chunk.length; + cb(); + }; + w.on('finish', () => { + console.error('written', written, totalPushed); + assert.strictEqual(written, expectEndingData); + assert.strictEqual(totalPushed, expectTotalData); + console.log('ok'); + }); + r.pipe(w); +} diff --git a/test/js/node/test/parallel/test-stream3-pipeline-async-iterator.js b/test/js/node/test/parallel/test-stream3-pipeline-async-iterator.js new file mode 100644 index 0000000000..ad1e464777 --- /dev/null +++ b/test/js/node/test/parallel/test-stream3-pipeline-async-iterator.js @@ -0,0 +1,27 @@ +/* eslint-disable node-core/require-common-first, require-yield */ +'use strict'; +const { pipeline } = require('node:stream/promises'); +{ + // Ensure that async iterators can act as readable and writable streams + async function* myCustomReadable() { + yield 'Hello'; + yield 'World'; + } + + const messages = []; + async function* myCustomWritable(stream) { + for await (const chunk of stream) { + messages.push(chunk); + } + } + + (async () => { + await pipeline( + myCustomReadable, + myCustomWritable, + ); + // Importing here to avoid initializing streams + require('assert').deepStrictEqual(messages, ['Hello', 'World']); + })() + .then(require('../common').mustCall()); +} diff --git a/test/js/node/test/parallel/test-string-decoder-end.js b/test/js/node/test/parallel/test-string-decoder-end.js new file mode 100644 index 0000000000..5a3c5cc720 --- /dev/null +++ b/test/js/node/test/parallel/test-string-decoder-end.js @@ -0,0 +1,128 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Verify that the string decoder works getting 1 byte at a time, +// the whole buffer at once, and that both match the .toString(enc) +// result of the entire buffer. + +require('../common'); +const assert = require('assert'); +const SD = require('string_decoder').StringDecoder; +const encodings = ['base64', 'base64url', 'hex', 'utf8', 'utf16le', 'ucs2']; + +const bufs = [ '☃💩', 'asdf' ].map((b) => Buffer.from(b)); + +// Also test just arbitrary bytes from 0-15. +for (let i = 1; i <= 16; i++) { + const bytes = '.'.repeat(i - 1).split('.').map((_, j) => j + 0x78); + bufs.push(Buffer.from(bytes)); +} + +encodings.forEach(testEncoding); + +testEnd('utf8', Buffer.of(0xE2), Buffer.of(0x61), '\uFFFDa'); +testEnd('utf8', Buffer.of(0xE2), Buffer.of(0x82), '\uFFFD\uFFFD'); +testEnd('utf8', Buffer.of(0xE2), Buffer.of(0xE2), '\uFFFD\uFFFD'); +testEnd('utf8', Buffer.of(0xE2, 0x82), Buffer.of(0x61), '\uFFFDa'); +testEnd('utf8', Buffer.of(0xE2, 0x82), Buffer.of(0xAC), '\uFFFD\uFFFD'); +testEnd('utf8', Buffer.of(0xE2, 0x82), Buffer.of(0xE2), '\uFFFD\uFFFD'); +testEnd('utf8', Buffer.of(0xE2, 0x82, 0xAC), Buffer.of(0x61), '€a'); + +testEnd('utf16le', Buffer.of(0x3D), Buffer.of(0x61, 0x00), 'a'); +testEnd('utf16le', Buffer.of(0x3D), Buffer.of(0xD8, 0x4D, 0xDC), '\u4DD8'); +testEnd('utf16le', Buffer.of(0x3D, 0xD8), Buffer.of(), '\uD83D'); +testEnd('utf16le', Buffer.of(0x3D, 0xD8), Buffer.of(0x61, 0x00), '\uD83Da'); +testEnd( + 'utf16le', + Buffer.of(0x3D, 0xD8), + Buffer.of(0x4D, 0xDC), + '\uD83D\uDC4D' +); +testEnd('utf16le', Buffer.of(0x3D, 0xD8, 0x4D), Buffer.of(), '\uD83D'); +testEnd( + 'utf16le', + Buffer.of(0x3D, 0xD8, 0x4D), + Buffer.of(0x61, 0x00), + '\uD83Da' +); +testEnd('utf16le', Buffer.of(0x3D, 0xD8, 0x4D), Buffer.of(0xDC), '\uD83D'); +testEnd( + 'utf16le', + Buffer.of(0x3D, 0xD8, 0x4D, 0xDC), + Buffer.of(0x61, 0x00), + '👍a' +); + +testEnd('base64', Buffer.of(0x61), Buffer.of(), 'YQ=='); +testEnd('base64', Buffer.of(0x61), Buffer.of(0x61), 'YQ==YQ=='); +testEnd('base64', Buffer.of(0x61, 0x61), Buffer.of(), 'YWE='); +testEnd('base64', Buffer.of(0x61, 0x61), Buffer.of(0x61), 'YWE=YQ=='); +testEnd('base64', Buffer.of(0x61, 0x61, 0x61), Buffer.of(), 'YWFh'); +testEnd('base64', Buffer.of(0x61, 0x61, 0x61), Buffer.of(0x61), 'YWFhYQ=='); + +testEnd('base64url', Buffer.of(0x61), Buffer.of(), 'YQ'); +testEnd('base64url', Buffer.of(0x61), Buffer.of(0x61), 'YQYQ'); +testEnd('base64url', Buffer.of(0x61, 0x61), Buffer.of(), 'YWE'); +testEnd('base64url', Buffer.of(0x61, 0x61), Buffer.of(0x61), 'YWEYQ'); +testEnd('base64url', Buffer.of(0x61, 0x61, 0x61), Buffer.of(), 'YWFh'); +testEnd('base64url', Buffer.of(0x61, 0x61, 0x61), Buffer.of(0x61), 'YWFhYQ'); + +function testEncoding(encoding) { + bufs.forEach((buf) => { + testBuf(encoding, buf); + }); +} + +function testBuf(encoding, buf) { + // Write one byte at a time. + let s = new SD(encoding); + let res1 = ''; + for (let i = 0; i < buf.length; i++) { + res1 += s.write(buf.slice(i, i + 1)); + } + res1 += s.end(); + + // Write the whole buffer at once. + let res2 = ''; + s = new SD(encoding); + res2 += s.write(buf); + res2 += s.end(); + + // .toString() on the buffer + const res3 = buf.toString(encoding); + + // One byte at a time should match toString + assert.strictEqual(res1, res3); + // All bytes at once should match toString + assert.strictEqual(res2, res3); +} + +function testEnd(encoding, incomplete, next, expected) { + let res = ''; + const s = new SD(encoding); + res += s.write(incomplete); + res += s.end(); + res += s.write(next); + res += s.end(); + + assert.strictEqual(res, expected); +} diff --git a/test/js/node/test/parallel/test-stringbytes-external.js b/test/js/node/test/parallel/test-stringbytes-external.js new file mode 100644 index 0000000000..d64312f525 --- /dev/null +++ b/test/js/node/test/parallel/test-stringbytes-external.js @@ -0,0 +1,143 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +// Minimum string size to overflow into external string space +const EXTERN_APEX = 0xFBEE9; + +// Manually controlled string for checking binary output +let ucs2_control = 'a\u0000'; +let write_str = 'a'; + + +// First do basic checks +let b = Buffer.from(write_str, 'ucs2'); +// first check latin1 +let c = b.toString('latin1'); +assert.strictEqual(b[0], 0x61); +assert.strictEqual(b[1], 0); +assert.strictEqual(ucs2_control, c); +// now check binary +c = b.toString('binary'); +assert.strictEqual(b[0], 0x61); +assert.strictEqual(b[1], 0); +assert.strictEqual(ucs2_control, c); + +// Now create big strings +const size = 1 << 20; +write_str = write_str.repeat(size); +ucs2_control = ucs2_control.repeat(size); + +// Check resultant buffer and output string +b = Buffer.from(write_str, 'ucs2'); +// Check fist Buffer created from write string +for (let i = 0; i < b.length; i += 2) { + assert.strictEqual(b[i], 0x61); + assert.strictEqual(b[i + 1], 0); +} + +// Create another string to create an external string +const b_ucs = b.toString('ucs2'); + +// Check control against external binary string +const l_bin = b.toString('latin1'); +assert.strictEqual(ucs2_control, l_bin); + +// Check control against external binary string +const b_bin = b.toString('binary'); +assert.strictEqual(ucs2_control, b_bin); + +// Create buffer copy from external +const c_bin = Buffer.from(l_bin, 'latin1'); +const c_ucs = Buffer.from(b_ucs, 'ucs2'); +// Make sure they're the same length +assert.strictEqual(c_bin.length, c_ucs.length); +// Make sure Buffers from externals are the same +for (let i = 0; i < c_bin.length; i++) { + assert.strictEqual(c_bin[i], c_ucs[i]); +} +// Check resultant strings +assert.strictEqual(c_bin.toString('ucs2'), c_ucs.toString('ucs2')); +assert.strictEqual(c_bin.toString('latin1'), ucs2_control); +assert.strictEqual(c_ucs.toString('latin1'), ucs2_control); + + +// Now let's test BASE64 and HEX encoding/decoding +const RADIOS = 2; +const PRE_HALF_APEX = Math.ceil(EXTERN_APEX / 2) - RADIOS; +const PRE_3OF4_APEX = Math.ceil((EXTERN_APEX / 4) * 3) - RADIOS; + +{ + for (let j = 0; j < RADIOS * 2; j += 1) { + const datum = b; + const slice = datum.slice(0, PRE_HALF_APEX + j); + const slice2 = datum.slice(0, PRE_HALF_APEX + j + 2); + const pumped_string = slice.toString('hex'); + const pumped_string2 = slice2.toString('hex'); + const decoded = Buffer.from(pumped_string, 'hex'); + + // The string are the same? + for (let k = 0; k < pumped_string.length; ++k) { + assert.strictEqual(pumped_string[k], pumped_string2[k]); + } + + // The recoded buffer is the same? + for (let i = 0; i < decoded.length; ++i) { + assert.strictEqual(datum[i], decoded[i]); + } + } +} + +{ + for (let j = 0; j < RADIOS * 2; j += 1) { + const datum = b; + const slice = datum.slice(0, PRE_3OF4_APEX + j); + const slice2 = datum.slice(0, PRE_3OF4_APEX + j + 2); + const pumped_string = slice.toString('base64'); + const pumped_string2 = slice2.toString('base64'); + const decoded = Buffer.from(pumped_string, 'base64'); + + // The string are the same? + for (let k = 0; k < pumped_string.length - 3; ++k) { + assert.strictEqual(pumped_string[k], pumped_string2[k]); + } + + // The recoded buffer is the same? + for (let i = 0; i < decoded.length; ++i) { + assert.strictEqual(datum[i], decoded[i]); + } + } +} + +// https://github.com/nodejs/node/issues/1024 +{ + const a = 'x'.repeat(1 << 20 - 1); + const b = Buffer.from(a, 'ucs2').toString('ucs2'); + const c = Buffer.from(b, 'utf8').toString('utf8'); + + assert.strictEqual(a.length, b.length); + assert.strictEqual(b.length, c.length); + + assert.strictEqual(a, b); + assert.strictEqual(b, c); +} diff --git a/test/js/node/test/parallel/test-sync-fileread.js b/test/js/node/test/parallel/test-sync-fileread.js new file mode 100644 index 0000000000..826f62d220 --- /dev/null +++ b/test/js/node/test/parallel/test-sync-fileread.js @@ -0,0 +1,7 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +assert.strictEqual(fs.readFileSync(fixtures.path('x.txt')).toString(), 'xyz\n'); diff --git a/test/js/node/test/parallel/test-sys.js b/test/js/node/test/parallel/test-sys.js new file mode 100644 index 0000000000..a7a77b8e1c --- /dev/null +++ b/test/js/node/test/parallel/test-sys.js @@ -0,0 +1,28 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const sys = require('sys'); // eslint-disable-line no-restricted-modules +const util = require('util'); + +assert.strictEqual(sys, util); diff --git a/test/js/node/test/parallel/test-timers-api-refs.js b/test/js/node/test/parallel/test-timers-api-refs.js new file mode 100644 index 0000000000..3c55a05ac4 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-api-refs.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); +const timers = require('timers'); + +// Delete global APIs to make sure they're not relied on by the internal timers +// code +delete global.setTimeout; +delete global.clearTimeout; +delete global.setInterval; +delete global.clearInterval; +delete global.setImmediate; +delete global.clearImmediate; + +const timeoutCallback = () => { timers.clearTimeout(timeout); }; +const timeout = timers.setTimeout(common.mustCall(timeoutCallback), 1); + +const intervalCallback = () => { timers.clearInterval(interval); }; +const interval = timers.setInterval(common.mustCall(intervalCallback), 1); + +const immediateCallback = () => { timers.clearImmediate(immediate); }; +const immediate = timers.setImmediate(immediateCallback); diff --git a/test/js/node/test/parallel/test-timers-args.js b/test/js/node/test/parallel/test-timers-args.js new file mode 100644 index 0000000000..1ba44d8bcf --- /dev/null +++ b/test/js/node/test/parallel/test-timers-args.js @@ -0,0 +1,31 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +function range(n) { + return 'x'.repeat(n + 1).split('').map(function(_, i) { return i; }); +} + +function timeout(nargs) { + const args = range(nargs); + setTimeout.apply(null, [callback, 1].concat(args)); + + function callback() { + assert.deepStrictEqual([].slice.call(arguments), args); + if (nargs < 128) timeout(nargs + 1); + } +} + +function interval(nargs) { + const args = range(nargs); + const timer = setTimeout.apply(null, [callback, 1].concat(args)); + + function callback() { + clearInterval(timer); + assert.deepStrictEqual([].slice.call(arguments), args); + if (nargs < 128) interval(nargs + 1); + } +} + +timeout(0); +interval(0); diff --git a/test/js/node/test/parallel/test-timers-clear-object-does-not-throw-error.js b/test/js/node/test/parallel/test-timers-clear-object-does-not-throw-error.js new file mode 100644 index 0000000000..9752f53abd --- /dev/null +++ b/test/js/node/test/parallel/test-timers-clear-object-does-not-throw-error.js @@ -0,0 +1,8 @@ +'use strict'; +require('../common'); + +// This test makes sure clearing timers with +// objects doesn't throw +clearImmediate({}); +clearTimeout({}); +clearInterval({}); diff --git a/test/js/node/test/parallel/test-timers-clear-timeout-interval-equivalent.js b/test/js/node/test/parallel/test-timers-clear-timeout-interval-equivalent.js new file mode 100644 index 0000000000..94611b7070 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-clear-timeout-interval-equivalent.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); + +// This test makes sure that timers created with setTimeout can be disarmed by +// clearInterval and that timers created with setInterval can be disarmed by +// clearTimeout. +// +// This behavior is documented in the HTML Living Standard: +// +// * Refs: https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-setinterval + +// Disarm interval with clearTimeout. +const interval = setInterval(common.mustNotCall(), 1); +clearTimeout(interval); + +// Disarm timeout with clearInterval. +const timeout = setTimeout(common.mustNotCall(), 1); +clearInterval(timeout); diff --git a/test/js/node/test/parallel/test-timers-clearImmediate.js b/test/js/node/test/parallel/test-timers-clearImmediate.js new file mode 100644 index 0000000000..ccd9826bb0 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-clearImmediate.js @@ -0,0 +1,13 @@ +'use strict'; +const common = require('../common'); + +const N = 3; + +function next() { + const fn = common.mustCall(() => clearImmediate(immediate)); + const immediate = setImmediate(fn); +} + +for (let i = 0; i < N; i++) { + next(); +} diff --git a/test/js/node/test/parallel/test-timers-immediate-queue.js b/test/js/node/test/parallel/test-timers-immediate-queue.js new file mode 100644 index 0000000000..8b433ddedb --- /dev/null +++ b/test/js/node/test/parallel/test-timers-immediate-queue.js @@ -0,0 +1,56 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// setImmediate should run clear its queued cbs once per event loop turn +// but immediates queued while processing the current queue should happen +// on the next turn of the event loop. + +// hit should be the exact same size of QUEUE, if we're letting things +// recursively add to the immediate QUEUE hit will be > QUEUE + +let ticked = false; + +let hit = 0; +const QUEUE = 10; + +function run() { + if (hit === 0) { + setTimeout(() => { ticked = true; }, 1); + const now = Date.now(); + while (Date.now() - now < 2); + } + + if (ticked) return; + + hit += 1; + setImmediate(run); +} + +for (let i = 0; i < QUEUE; i++) + setImmediate(run); + +process.on('exit', function() { + assert.strictEqual(hit, QUEUE); +}); diff --git a/test/js/node/test/parallel/test-timers-immediate.js b/test/js/node/test/parallel/test-timers-immediate.js new file mode 100644 index 0000000000..0227e38efa --- /dev/null +++ b/test/js/node/test/parallel/test-timers-immediate.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +let mainFinished = false; + +setImmediate(common.mustCall(function() { + assert.strictEqual(mainFinished, true); + clearImmediate(immediateB); +})); + +const immediateB = setImmediate(common.mustNotCall()); + +setImmediate(common.mustCall((...args) => { + assert.deepStrictEqual(args, [1, 2, 3]); +}), 1, 2, 3); + +setImmediate(common.mustCall((...args) => { + assert.deepStrictEqual(args, [1, 2, 3, 4, 5]); +}), 1, 2, 3, 4, 5); + +mainFinished = true; diff --git a/test/js/node/test/parallel/test-timers-interval-throw.js b/test/js/node/test/parallel/test-timers-interval-throw.js new file mode 100644 index 0000000000..876f0de55a --- /dev/null +++ b/test/js/node/test/parallel/test-timers-interval-throw.js @@ -0,0 +1,17 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +// To match browser behaviour, interval should continue +// being rescheduled even if it throws. + +let count = 2; +const interval = setInterval(() => { throw new Error('IntervalError'); }, 1); + +process.on('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err.message, 'IntervalError'); + if (--count === 0) { + clearInterval(interval); + } +}, 2)); diff --git a/test/js/node/test/parallel/test-timers-non-integer-delay.js b/test/js/node/test/parallel/test-timers-non-integer-delay.js new file mode 100644 index 0000000000..089c1fee8b --- /dev/null +++ b/test/js/node/test/parallel/test-timers-non-integer-delay.js @@ -0,0 +1,81 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// This test makes sure that non-integer timer delays do not make the process +// hang. See https://github.com/joyent/node/issues/8065 and +// https://github.com/joyent/node/issues/8068 which have been fixed by +// https://github.com/joyent/node/pull/8073. +// +// If the process hangs, this test will make the tests suite timeout, +// otherwise it will exit very quickly (after 50 timers with a short delay +// fire). +// +// We have to set at least several timers with a non-integer delay to +// reproduce the issue. Sometimes, a timer with a non-integer delay will +// expire correctly. 50 timers has always been more than enough to reproduce +// it 100%. + +const TIMEOUT_DELAY = 1.1; +let N = 50; + +const interval = setInterval(common.mustCall(() => { + if (--N === 0) { + clearInterval(interval); + } +}, N), TIMEOUT_DELAY); + +// Test non-integer delay ordering +{ + const ordering = []; + + setTimeout(common.mustCall(() => { + ordering.push(1); + }), 1); + + setTimeout(common.mustCall(() => { + ordering.push(2); + }), 1.8); + + setTimeout(common.mustCall(() => { + ordering.push(3); + }), 1.1); + + setTimeout(common.mustCall(() => { + ordering.push(4); + }), 1); + + setTimeout(common.mustCall(() => { + const expected = [1, 2, 3, 4]; + + assert.deepStrictEqual( + ordering, + expected, + `Non-integer delay ordering should be ${expected}, but got ${ordering}` + ); + + // 2 should always be last of these delays due to ordering guarantees by + // the implementation. + }), 2); +} diff --git a/test/js/node/test/parallel/test-timers-process-tampering.js b/test/js/node/test/parallel/test-timers-process-tampering.js new file mode 100644 index 0000000000..766cc9f356 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-process-tampering.js @@ -0,0 +1,8 @@ +// Check that setImmediate works even if process is tampered with. +// This is a regression test for https://github.com/nodejs/node/issues/17681. + +'use strict'; +const common = require('../common'); +global.process = {}; // Boom! +common.allowGlobals(global.process); +setImmediate(common.mustCall()); diff --git a/test/js/node/test/parallel/test-timers-promises-scheduler.js b/test/js/node/test/parallel/test-timers-promises-scheduler.js new file mode 100644 index 0000000000..7caf92fdf6 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-promises-scheduler.js @@ -0,0 +1,50 @@ +'use strict'; + +const common = require('../common'); + +const { scheduler } = require('timers/promises'); +const { setTimeout } = require('timers'); +const { + strictEqual, + rejects, +} = require('assert'); + +async function testYield() { + await scheduler.yield(); + process.emit('foo'); +} +testYield().then(common.mustCall()); +queueMicrotask(common.mustCall(() => { + process.addListener('foo', common.mustCall()); +})); + +async function testWait() { + let value = 0; + setTimeout(() => value++, 10); + await scheduler.wait(15); + strictEqual(value, 1); +} + +testWait().then(common.mustCall()); + +async function testCancelableWait1() { + const ac = new AbortController(); + const wait = scheduler.wait(1e6, { signal: ac.signal }); + ac.abort(); + await rejects(wait, { + code: 'ABORT_ERR', + message: 'The operation was aborted', + }); +} + +testCancelableWait1().then(common.mustCall()); + +async function testCancelableWait2() { + const wait = scheduler.wait(10000, { signal: AbortSignal.abort() }); + await rejects(wait, { + code: 'ABORT_ERR', + message: 'The operation was aborted', + }); +} + +testCancelableWait2().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-timers-refresh-in-callback.js b/test/js/node/test/parallel/test-timers-refresh-in-callback.js new file mode 100644 index 0000000000..df62512acd --- /dev/null +++ b/test/js/node/test/parallel/test-timers-refresh-in-callback.js @@ -0,0 +1,14 @@ +'use strict'; + +const common = require('../common'); + +// This test checks whether a refresh called inside the callback will keep +// the event loop alive to run the timer again. + +let didCall = false; +const timer = setTimeout(common.mustCall(() => { + if (!didCall) { + didCall = true; + timer.refresh(); + } +}, 2), 1); diff --git a/test/js/node/test/parallel/test-timers-same-timeout-wrong-list-deleted.js b/test/js/node/test/parallel/test-timers-same-timeout-wrong-list-deleted.js new file mode 100644 index 0000000000..f02603ad88 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-same-timeout-wrong-list-deleted.js @@ -0,0 +1,34 @@ +'use strict'; + +// This is a regression test for https://github.com/nodejs/node/issues/7722. +// +// When nested timers have the same timeout, calling clearTimeout on the +// older timer after it has fired causes the list the newer timer is in +// to be deleted. Since the newer timer was not cleared, it still blocks +// the event loop completing for the duration of its timeout, however, since +// no reference exists to it in its list, it cannot be canceled and its +// callback is not called when the timeout elapses. + +const common = require('../common'); + +const TIMEOUT = common.platformTimeout(100); + +const handle1 = setTimeout(common.mustCall(function() { + // Cause the old TIMEOUT list to be deleted + clearTimeout(handle1); + + // Cause a new list with the same key (TIMEOUT) to be created for this timer + const handle2 = setTimeout(common.mustNotCall(), TIMEOUT); + + setTimeout(common.mustCall(function() { + // Attempt to cancel the second timer. Fix for this bug will keep the + // newer timer from being dereferenced by keeping its list from being + // erroneously deleted. If we are able to cancel the timer successfully, + // the bug is fixed. + clearTimeout(handle2); + }), 1); + + // When this callback completes, `listOnTimeout` should now look at the + // correct list and refrain from removing the new TIMEOUT list which + // contains the reference to the newer timer. +}), TIMEOUT); diff --git a/test/js/node/test/parallel/test-timers-setimmediate-infinite-loop.js b/test/js/node/test/parallel/test-timers-setimmediate-infinite-loop.js new file mode 100644 index 0000000000..49adc390fa --- /dev/null +++ b/test/js/node/test/parallel/test-timers-setimmediate-infinite-loop.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that if an Immediate callback clears subsequent +// immediates we don't get stuck in an infinite loop. +// +// If the process does get stuck, it will be timed out by the test +// runner. +// +// Ref: https://github.com/nodejs/node/issues/9756 + +setImmediate(common.mustCall(function() { + clearImmediate(i2); + clearImmediate(i3); +})); + +const i2 = setImmediate(common.mustNotCall()); + +const i3 = setImmediate(common.mustNotCall()); diff --git a/test/js/node/test/parallel/test-timers-this.js b/test/js/node/test/parallel/test-timers-this.js new file mode 100644 index 0000000000..a2a028fb06 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-this.js @@ -0,0 +1,50 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const immediateHandler = setImmediate(common.mustCall(function() { + assert.strictEqual(this, immediateHandler); +})); + +const immediateArgsHandler = setImmediate(common.mustCall(function() { + assert.strictEqual(this, immediateArgsHandler); +}), 'args ...'); + +const intervalHandler = setInterval(common.mustCall(function() { + clearInterval(intervalHandler); + assert.strictEqual(this, intervalHandler); +}), 1); + +const intervalArgsHandler = setInterval(common.mustCall(function() { + clearInterval(intervalArgsHandler); + assert.strictEqual(this, intervalArgsHandler); +}), 1, 'args ...'); + +const timeoutHandler = setTimeout(common.mustCall(function() { + assert.strictEqual(this, timeoutHandler); +}), 1); + +const timeoutArgsHandler = setTimeout(common.mustCall(function() { + assert.strictEqual(this, timeoutArgsHandler); +}), 1, 'args ...'); diff --git a/test/js/node/test/parallel/test-timers-timeout-with-non-integer.js b/test/js/node/test/parallel/test-timers-timeout-with-non-integer.js new file mode 100644 index 0000000000..96efc69e50 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-timeout-with-non-integer.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); + +/** + * This test is for https://github.com/nodejs/node/issues/24203 + */ +let count = 50; +const time = 1.00000000000001; +const exec = common.mustCall(() => { + if (--count === 0) { + return; + } + setTimeout(exec, time); +}, count); +exec(); diff --git a/test/js/node/test/parallel/test-timers-uncaught-exception.js b/test/js/node/test/parallel/test-timers-uncaught-exception.js new file mode 100644 index 0000000000..8bcf72e36c --- /dev/null +++ b/test/js/node/test/parallel/test-timers-uncaught-exception.js @@ -0,0 +1,39 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const errorMsg = 'BAM!'; + +// The first timer throws... +setTimeout(common.mustCall(function() { + throw new Error(errorMsg); +}), 1); + +// ...but the second one should still run +setTimeout(common.mustCall(), 1); + +function uncaughtException(err) { + assert.strictEqual(err.message, errorMsg); +} + +process.on('uncaughtException', common.mustCall(uncaughtException)); diff --git a/test/js/node/test/parallel/test-timers-unref-throw-then-ref.js b/test/js/node/test/parallel/test-timers-unref-throw-then-ref.js new file mode 100644 index 0000000000..1dd5fdd0ad --- /dev/null +++ b/test/js/node/test/parallel/test-timers-unref-throw-then-ref.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +process.once('uncaughtException', common.mustCall((err) => { + common.expectsError({ + message: 'Timeout Error' + })(err); +})); + +let called = false; +const t = setTimeout(() => { + assert(!called); + called = true; + t.ref(); + throw new Error('Timeout Error'); +}, 1).unref(); + +setTimeout(common.mustCall(), 1); diff --git a/test/js/node/test/parallel/test-timers-unrefd-interval-still-fires.js b/test/js/node/test/parallel/test-timers-unrefd-interval-still-fires.js new file mode 100644 index 0000000000..98172d18bc --- /dev/null +++ b/test/js/node/test/parallel/test-timers-unrefd-interval-still-fires.js @@ -0,0 +1,22 @@ +'use strict'; +// Regression test for https://github.com/nodejs/node-v0.x-archive/issues/8900. +const common = require('../common'); + +const TEST_DURATION = common.platformTimeout(1000); +let N = 3; + +const keepOpen = + setTimeout( + common.mustNotCall('Test timed out. keepOpen was not canceled.'), + TEST_DURATION); + +const timer = setInterval(common.mustCall(() => { + if (--N === 0) { + clearInterval(timer); + timer._onTimeout = + common.mustNotCall('Unrefd interval fired after being cleared'); + clearTimeout(keepOpen); + } +}, N), 1); + +timer.unref(); diff --git a/test/js/node/test/parallel/test-timers-unrefed-in-beforeexit.js b/test/js/node/test/parallel/test-timers-unrefed-in-beforeexit.js new file mode 100644 index 0000000000..a38b55bf45 --- /dev/null +++ b/test/js/node/test/parallel/test-timers-unrefed-in-beforeexit.js @@ -0,0 +1,7 @@ +'use strict'; + +const common = require('../common'); + +process.on('beforeExit', common.mustCall(() => { + setTimeout(common.mustNotCall(), 1).unref(); +})); diff --git a/test/js/node/test/parallel/test-timers-user-call.js b/test/js/node/test/parallel/test-timers-user-call.js new file mode 100644 index 0000000000..4ff24e688b --- /dev/null +++ b/test/js/node/test/parallel/test-timers-user-call.js @@ -0,0 +1,40 @@ +// Make sure `setTimeout()` and friends don't throw if the user-supplied +// function has .call() and .apply() monkey-patched to undesirable values. + +// Refs: https://github.com/nodejs/node/issues/12956 + +'use strict'; + +const common = require('../common'); + +{ + const fn = common.mustCall(10); + fn.call = 'not a function'; + fn.apply = 'also not a function'; + setTimeout(fn, 1); + setTimeout(fn, 1, 'oneArg'); + setTimeout(fn, 1, 'two', 'args'); + setTimeout(fn, 1, 'three', '(3)', 'args'); + setTimeout(fn, 1, 'more', 'than', 'three', 'args'); + + setImmediate(fn, 1); + setImmediate(fn, 1, 'oneArg'); + setImmediate(fn, 1, 'two', 'args'); + setImmediate(fn, 1, 'three', '(3)', 'args'); + setImmediate(fn, 1, 'more', 'than', 'three', 'args'); +} + +{ + const testInterval = (...args) => { + const fn = common.mustCall(() => { clearInterval(interval); }); + fn.call = 'not a function'; + fn.apply = 'also not a function'; + const interval = setInterval(fn, 1, ...args); + }; + + testInterval(); + testInterval('oneArg'); + testInterval('two', 'args'); + testInterval('three', '(3)', 'args'); + testInterval('more', 'than', 'three', 'args'); +} diff --git a/test/js/node/test/parallel/test-timers-zero-timeout.js b/test/js/node/test/parallel/test-timers-zero-timeout.js new file mode 100644 index 0000000000..61a5b2131b --- /dev/null +++ b/test/js/node/test/parallel/test-timers-zero-timeout.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +// https://github.com/joyent/node/issues/2079 - zero timeout drops extra args +{ + setTimeout(common.mustCall(f), 0, 'foo', 'bar', 'baz'); + setTimeout(() => {}, 0); + + function f(a, b, c) { + assert.strictEqual(a, 'foo'); + assert.strictEqual(b, 'bar'); + assert.strictEqual(c, 'baz'); + } +} + +{ + let ncalled = 3; + + const f = common.mustCall((a, b, c) => { + assert.strictEqual(a, 'foo'); + assert.strictEqual(b, 'bar'); + assert.strictEqual(c, 'baz'); + if (--ncalled === 0) clearTimeout(iv); + }, ncalled); + + const iv = setInterval(f, 0, 'foo', 'bar', 'baz'); +} diff --git a/test/js/node/test/parallel/test-tls-add-context.js b/test/js/node/test/parallel/test-tls-add-context.js new file mode 100644 index 0000000000..8d02866ce5 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-add-context.js @@ -0,0 +1,75 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const tls = require('tls'); + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + +const serverOptions = { + key: loadPEM('agent2-key'), + cert: loadPEM('agent2-cert'), + ca: [ loadPEM('ca2-cert') ], + requestCert: true, + rejectUnauthorized: false, +}; + +let connections = 0; + +const server = tls.createServer(serverOptions, (c) => { + if (++connections === 3) { + server.close(); + } + if (c.servername === 'unknowncontext') { + assert.strictEqual(c.authorized, false); + return; + } + assert.strictEqual(c.authorized, true); +}); + +const secureContext = { + key: loadPEM('agent1-key'), + cert: loadPEM('agent1-cert'), + ca: [ loadPEM('ca1-cert') ], +}; +server.addContext('context1', secureContext); +server.addContext('context2', tls.createSecureContext(secureContext)); + +const clientOptionsBase = { + key: loadPEM('agent1-key'), + cert: loadPEM('agent1-cert'), + ca: [ loadPEM('ca1-cert') ], + rejectUnauthorized: false, +}; + +server.listen(0, common.mustCall(() => { + const client1 = tls.connect({ + ...clientOptionsBase, + port: server.address().port, + servername: 'context1', + }, common.mustCall(() => { + client1.end(); + })); + + const client2 = tls.connect({ + ...clientOptionsBase, + port: server.address().port, + servername: 'context2', + }, common.mustCall(() => { + client2.end(); + })); + + const client3 = tls.connect({ + ...clientOptionsBase, + port: server.address().port, + servername: 'unknowncontext', + }, common.mustCall(() => { + client3.end(); + })); +})); diff --git a/test/js/node/test/parallel/test-tls-alert-handling.js b/test/js/node/test/parallel/test-tls-alert-handling.js new file mode 100644 index 0000000000..bd86149bc5 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-alert-handling.js @@ -0,0 +1,96 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('node compiled without OpenSSL CLI'); + +const assert = require('assert'); +const net = require('net'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +let clientClosed = false; +let errorReceived = false; +function canCloseServer() { + return clientClosed && errorReceived; +} + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`, 'utf-8'); +} + +const opts = { + key: loadPEM('agent2-key'), + cert: loadPEM('agent2-cert') +}; + +const max_iter = 20; +let iter = 0; + +const errorHandler = common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_SSL_WRONG_VERSION_NUMBER'); + assert.strictEqual(err.library, 'SSL routines'); + if (!common.hasOpenSSL3) assert.strictEqual(err.function, 'ssl3_get_record'); + assert.strictEqual(err.reason, 'wrong version number'); + errorReceived = true; + if (canCloseServer()) + server.close(); +}); +const server = tls.createServer(opts, common.mustCall(function(s) { + s.pipe(s); + s.on('error', errorHandler); +}, 2)); + +server.listen(0, common.mustCall(function() { + sendClient(); +})); + +server.on('tlsClientError', common.mustNotCall()); + +server.on('error', common.mustNotCall()); + +function sendClient() { + const client = tls.connect(server.address().port, { + rejectUnauthorized: false + }); + client.on('data', common.mustCall(function() { + if (iter++ === 2) sendBADTLSRecord(); + if (iter < max_iter) { + client.write('a'); + return; + } + client.end(); + }, max_iter)); + client.write('a', common.mustCall()); + client.on('error', common.mustNotCall()); + client.on('close', common.mustCall(function() { + clientClosed = true; + if (canCloseServer()) + server.close(); + })); +} + + +function sendBADTLSRecord() { + const BAD_RECORD = Buffer.from([0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); + const socket = net.connect(server.address().port); + const client = tls.connect({ + socket: socket, + rejectUnauthorized: false + }, common.mustCall(function() { + client.write('x'); + client.on('data', (data) => { + socket.end(BAD_RECORD); + }); + })); + client.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION'); + assert.strictEqual(err.library, 'SSL routines'); + if (!common.hasOpenSSL3) + assert.strictEqual(err.function, 'ssl3_read_bytes'); + assert.strictEqual(err.reason, 'tlsv1 alert protocol version'); + })); +} diff --git a/test/js/node/test/parallel/test-tls-alert.js b/test/js/node/test/parallel/test-tls-alert.js new file mode 100644 index 0000000000..04000771aa --- /dev/null +++ b/test/js/node/test/parallel/test-tls-alert.js @@ -0,0 +1,53 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('node compiled without OpenSSL CLI.'); + +const assert = require('assert'); +const { execFile } = require('child_process'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + +const server = tls.Server({ + secureProtocol: 'TLSv1_2_server_method', + key: loadPEM('agent2-key'), + cert: loadPEM('agent2-cert') +}, null).listen(0, common.mustCall(() => { + const args = ['s_client', '-quiet', '-tls1_1', + '-cipher', (common.hasOpenSSL31 ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), + '-connect', `127.0.0.1:${server.address().port}`]; + + execFile(common.opensslCli, args, common.mustCall((err, _, stderr) => { + assert.strictEqual(err.code, 1); + assert.match(stderr, /SSL alert number 70/); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-tls-ca-concat.js b/test/js/node/test/parallel/test-tls-ca-concat.js new file mode 100644 index 0000000000..38a6a4dfec --- /dev/null +++ b/test/js/node/test/parallel/test-tls-ca-concat.js @@ -0,0 +1,23 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +// Check ca option can contain concatenated certs by prepending an unrelated +// non-CA cert and showing that agent6's CA root is still found. + +const { + connect, keys +} = require(fixtures.path('tls-connect')); + +connect({ + client: { + checkServerIdentity: (servername, cert) => { }, + ca: `${keys.agent1.cert}\n${keys.agent6.ca}`, + }, + server: { + cert: keys.agent6.cert, + key: keys.agent6.key, + }, +}, common.mustSucceed((pair, cleanup) => { + return cleanup(); +})); diff --git a/test/js/node/test/parallel/test-tls-cert-ext-encoding.js b/test/js/node/test/parallel/test-tls-cert-ext-encoding.js new file mode 100644 index 0000000000..4556b57918 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-cert-ext-encoding.js @@ -0,0 +1,89 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (common.hasOpenSSL3) + // TODO(danbev) This test fails with the following error: + // error:0D00008F:asn1 encoding routines::no matching choice type + // + // I've not been able to figure out the reason for this but there + // is a note in https://wiki.openssl.org/index.php/OpenSSL_3.0 which + // indicates that this might not work at the moment: + // "OCSP, PEM, ASN.1 have some very limited library context support" + common.skip('when using OpenSSL 3.x'); + +// NOTE: This certificate is hand-generated, hence it is not located in +// `test/fixtures/keys` to avoid confusion. +// +// The key property of this cert is that subjectAltName contains a string with +// a type `23` which cannot be encoded into string by `X509V3_EXT_print`. +const pem = ` +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAzrmfPz5M3wTq2/CwMeSQr/N+R1FCJ+O5n+SMleKvBqaK63eJ +kL4BnySMc+ZLKCt4UQSsPFIBK63QFq8n6/vjuTDMJiBTsvzytw8zJt1Zr2HA71N3 +VIPt6NdJ/w5lgddTYxR7XudJZJ5lk3PkG8ZgrhuenPYP80UJYVzAC2YZ9KYe3r2B +rVbut1j+8h0TwVcx2Zg5PorsC/EVxHwo4dCmIHceodikr3UVqHneRcrDBytdG6Mo +IqHhZJwBeii/EES9tpWwWbzYYh+38aGGLIF2h5UlVpr0bdBVVUg+uVX3y/Qmu2Qv +4CrAO2IPV6JER9Niwl3ktzNjOMAUQG6BCRSqRQIDAQABAoIBAAmB0+cOsG5ZRYvT +5+aDgnv1EMuq2wYGnRTTZ/vErxP5OM5XcwYrFtwAzEzQPIieZywisOEdTFx74+QH +LijWLsTnj5v5RKAorejpVArnhyZfsoXPKt/CKYDZ1ddbDCQKiRU3be0RafisqDM9 +0zHLz8pyDrtdPaKMfD/0Cgj8KxlrLTmfD4otPXds8fZpQe1hR1y12XKVp47l1siW +qFGTaUPDJpQ67xybR08x5DOqmyo4cNMOuReRWrc/qRbWint9U1882eOH09gVfpJZ +Gp6FZVPSgz10MZdLSPLhXqZkY4IxIvNltjBDqkmivd12CD+GVr0qUmTJHzTpk+kG +/CWuRQkCgYEA4EFf8SJHEl0fLDJnOQFyUPY3MalMuopUkQ5CBUe3QXjQhHXsRDfj +Ci/lyzShJkHPbMDHb/rx3lYZB0xNhwnMWKS1gCFVgOCOTZLfD0K1Anxc1hOSgVxI +y5FdO9VW7oQNlsMH/WuDHps0HhJW/00lcrmdyoUM1+fE/3yPQndhUmMCgYEA6/z6 +8Gq4PHHNql+gwunAH2cZKNdmcP4Co8MvXCZwIJsLenUuLIZQ/YBKZoM/y5X/cFAG +WFJJuUe6KFetPaDm6NgZgpOmawyUwd5czDjJ6wWgsRywiTISInfJlgWLBVMOuba7 +iBL9Xuy0hmcbj0ByoRW9l3gCiBX3yJw3I6wqXTcCgYBnjei22eRF15iIeTHvQfq+ +5iNwnEQhM7V/Uj0sYQR/iEGJmUaj7ca6somDf2cW2nblOlQeIpxD1jAyjYqTW/Pv +zwc9BqeMHqW3rqWwT1Z0smbQODOD5tB6qEKMWaSN+Y6o2qC65kWjAXpclI110PME ++i+iEDRxEsaGT8d7otLfDwKBgQCs+xBaQG/x5p2SAGzP0xYALstzc4jk1FzM+5rw +mkBgtiXQyqpg+sfNOkfPIvAVZEsMYax0+0SNKrWbMsGLRjFchmMUovQ+zccQ4NT2 +4b2op8Rlbxk8R9ahK1s5u7Bu47YMjZSjJwBQn4OobVX3SI994njJ2a9JX4j0pQWK +AX5AOwKBgAfOsr8HSHTcxSW4F9gegj+hXsRYbdA+eUkFhEGrYyRJgIlQrk/HbuZC +mKd/bQ5R/vwd1cxgV6A0APzpZtbwdhvP0RWji+WnPPovgGcfK0AHFstHnga67/uu +h2LHnKQZ1qWHn+BXWo5d7hBRwWVaK66g3GDN0blZpSz1kKcpy1Pl +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIICwjCCAaqgAwIBAgIDAQABMA0GCSqGSIb3DQEBDQUAMBUxEzARBgNVBAMWCmxv +Y2FsLmhvc3QwHhcNMTkxMjA1MDQyODMzWhcNNDQxMTI5MDQyODMzWjAVMRMwEQYD +VQQDFgpsb2NhbC5ob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +zrmfPz5M3wTq2/CwMeSQr/N+R1FCJ+O5n+SMleKvBqaK63eJkL4BnySMc+ZLKCt4 +UQSsPFIBK63QFq8n6/vjuTDMJiBTsvzytw8zJt1Zr2HA71N3VIPt6NdJ/w5lgddT +YxR7XudJZJ5lk3PkG8ZgrhuenPYP80UJYVzAC2YZ9KYe3r2BrVbut1j+8h0TwVcx +2Zg5PorsC/EVxHwo4dCmIHceodikr3UVqHneRcrDBytdG6MoIqHhZJwBeii/EES9 +tpWwWbzYYh+38aGGLIF2h5UlVpr0bdBVVUg+uVX3y/Qmu2Qv4CrAO2IPV6JER9Ni +wl3ktzNjOMAUQG6BCRSqRQIDAQABoxswGTAXBgNVHREEEDAOlwwqLmxvY2FsLmhv +c3QwDQYJKoZIhvcNAQENBQADggEBAH5ThRLDLwOGuhKsifyiq7k8gbx1FqRegO7H +SIiIYYB35v5Pk0ZPN8QBJwNQzJEjUMjCpHXNdBxknBXRaA8vkbnryMfJm37gPTwA +m6r0uEG78WgcEAe8bgf9iKtQGP/iydKXpSSpDgKoHbswIxD5qtzT+o6VNnkRTSfK +/OGwakluFSoJ/Q9rLpR8lKjA01BhetXMmHbETiY8LSkxOymMldXSzUTD1WdrVn8U +L3dobxT//R/0GraKXG02mf3gZNlb0MMTvW0pVwVy39YmcPEGh8L0hWh1rpAA/VXC +f79uOowv3lLTzQ9na5EThA0tp8d837hdYrrIHh5cfTqBDxG0Tu8= +-----END CERTIFICATE----- +`; + +const tls = require('tls'); + +const options = { + key: pem, + cert: pem, +}; + +const server = tls.createServer(options, (socket) => { + socket.end(); +}); +server.listen(0, common.mustCall(function() { + const client = tls.connect({ + port: this.address().port, + rejectUnauthorized: false + }, common.mustCall(() => { + // This should not crash process: + client.getPeerCertificate(); + + server.close(); + client.end(); + })); +})); diff --git a/test/js/node/test/parallel/test-tls-cert-regression.js b/test/js/node/test/parallel/test-tls-cert-regression.js new file mode 100644 index 0000000000..478402772e --- /dev/null +++ b/test/js/node/test/parallel/test-tls-cert-regression.js @@ -0,0 +1,80 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tls = require('tls'); + +const cert = +`-----BEGIN CERTIFICATE----- +MIIDNDCCAp2gAwIBAgIJAJvXLQpGPpm7MA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV +BAYTAkdCMRAwDgYDVQQIEwdHd3luZWRkMREwDwYDVQQHEwhXYXVuZmF3cjEUMBIG +A1UEChMLQWNrbmFjayBMdGQxEjAQBgNVBAsTCVRlc3QgQ2VydDESMBAGA1UEAxMJ +bG9jYWxob3N0MB4XDTA5MTEwMjE5MzMwNVoXDTEwMTEwMjE5MzMwNVowcDELMAkG +A1UEBhMCR0IxEDAOBgNVBAgTB0d3eW5lZGQxETAPBgNVBAcTCFdhdW5mYXdyMRQw +EgYDVQQKEwtBY2tuYWNrIEx0ZDESMBAGA1UECxMJVGVzdCBDZXJ0MRIwEAYDVQQD +Ewlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANdym7nGe2yw +6LlJfJrQtC5TmKOGrSXiyolYCbGOy4xZI4KD31d3097jhlQFJyF+10gwkE62DuJe +fLvBZDUsvLe1R8bzlVhZnBVn+3QJyUIWQAL+DsRj8P3KoD7k363QN5dIaA1GOAg2 +vZcPy1HCUsvOgvDXGRUCZqNLAyt+h/cpAgMBAAGjgdUwgdIwHQYDVR0OBBYEFK4s +VBV4shKUj3UX/fvSJnFaaPBjMIGiBgNVHSMEgZowgZeAFK4sVBV4shKUj3UX/fvS +JnFaaPBjoXSkcjBwMQswCQYDVQQGEwJHQjEQMA4GA1UECBMHR3d5bmVkZDERMA8G +A1UEBxMIV2F1bmZhd3IxFDASBgNVBAoTC0Fja25hY2sgTHRkMRIwEAYDVQQLEwlU +ZXN0IENlcnQxEjAQBgNVBAMTCWxvY2FsaG9zdIIJAJvXLQpGPpm7MAwGA1UdEwQF +MAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAFxR7BA1mUlsYqPiogtxSIfLzHWh+s0bJ +SBuhNrHes4U8QxS8+x/KWjd/81gzsf9J1C2VzTlFaydAgigz3SkQYgs+TMnFkT2o +9jqoJrcdf4WpZ2DQXUALaZgwNzPumMUSx8Ac5gO+BY/RHyP6fCodYvdNwyKslnI3 +US7eCSHZsVo= +-----END CERTIFICATE-----`; + +const key = +`-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDXcpu5xntssOi5SXya0LQuU5ijhq0l4sqJWAmxjsuMWSOCg99X +d9Pe44ZUBSchftdIMJBOtg7iXny7wWQ1LLy3tUfG85VYWZwVZ/t0CclCFkAC/g7E +Y/D9yqA+5N+t0DeXSGgNRjgINr2XD8tRwlLLzoLw1xkVAmajSwMrfof3KQIDAQAB +AoGBAIBHR/tT93ce2mJAJAXV0AJpWc+7x2pwX2FpXtQujnlxNZhnRlrBCRCD7h4m +t0bVS/86kyGaesBDvAbavfx/N5keYzzmmSp5Ht8IPqKPydGWdigk4x90yWvktai7 +dWuRKF94FXr0GUuBONb/dfHdp4KBtzN7oIF9WydYGGXA9ZmBAkEA8/k01bfwQZIu +AgcdNEM94Zcug1gSspXtUu8exNQX4+PNVbadghZb1+OnUO4d3gvWfqvAnaXD3KV6 +N4OtUhQQ0QJBAOIRbKMfaymQ9yE3CQQxYfKmEhHXWARXVwuYqIFqjmhSjSXx0l/P +7mSHz1I9uDvxkJev8sQgu1TKIyTOdqPH1tkCQQDPa6H1yYoj1Un0Q2Qa2Mg1kTjk +Re6vkjPQ/KcmJEOjZjtekgFbZfLzmwLXFXqjG2FjFFaQMSxR3QYJSJQEYjbhAkEA +sy7OZcjcXnjZeEkv61Pc57/7qIp/6Aj2JGnefZ1gvI1Z9Q5kCa88rA/9Iplq8pA4 +ZBKAoDW1ZbJGAsFmxc/6mQJAdPilhci0qFN86IGmf+ZBnwsDflIwHKDaVofti4wQ +sPWhSOb9VQjMXekI4Y2l8fqAVTS2Fn6+8jkVKxXBywSVCw== +-----END RSA PRIVATE KEY-----`; + +function test(cert, key, cb) { + const server = tls.createServer({ + cert, + key + }).listen(0, function() { + server.close(cb); + }); +} + +test(cert, key, common.mustCall(function() { + test(Buffer.from(cert), Buffer.from(key), common.mustCall()); +})); diff --git a/test/js/node/test/parallel/test-tls-client-abort.js b/test/js/node/test/parallel/test-tls-client-abort.js new file mode 100644 index 0000000000..50c9a4b324 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-client-abort.js @@ -0,0 +1,35 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const cert = fixtures.readKey('rsa_cert.crt'); +const key = fixtures.readKey('rsa_private.pem'); + +const conn = tls.connect({ cert, key, port: 0 }, common.mustNotCall()); +conn.on('error', function() {}); +conn.destroy(); diff --git a/test/js/node/test/parallel/test-tls-client-destroy-soon.js b/test/js/node/test/parallel/test-tls-client-destroy-soon.js new file mode 100644 index 0000000000..1d49a6094b --- /dev/null +++ b/test/js/node/test/parallel/test-tls-client-destroy-soon.js @@ -0,0 +1,67 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Create an ssl server. First connection, validate that not resume. +// Cache session and close connection. Use session on second connection. +// ASSERT resumption. + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const options = { + key: fixtures.readKey('agent2-key.pem'), + cert: fixtures.readKey('agent2-cert.pem') +}; + +const big = Buffer.alloc(2 * 1024 * 1024, 'Y'); + +// create server +const server = tls.createServer(options, common.mustCall(function(socket) { + socket.end(big); + socket.destroySoon(); +})); + +// start listening +server.listen(0, common.mustCall(function() { + const client = tls.connect({ + port: this.address().port, + rejectUnauthorized: false + }, common.mustCall(function() { + let bytesRead = 0; + + client.on('readable', function() { + const d = client.read(); + if (d) + bytesRead += d.length; + }); + + client.on('end', common.mustCall(function() { + server.close(); + assert.strictEqual(big.length, bytesRead); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-tls-client-renegotiation-limit.js b/test/js/node/test/parallel/test-tls-client-renegotiation-limit.js new file mode 100644 index 0000000000..71d7a85bae --- /dev/null +++ b/test/js/node/test/parallel/test-tls-client-renegotiation-limit.js @@ -0,0 +1,101 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('node compiled without OpenSSL CLI.'); + +const assert = require('assert'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +// Renegotiation as a protocol feature was dropped after TLS1.2. +tls.DEFAULT_MAX_VERSION = 'TLSv1.2'; + +// Renegotiation limits to test +const LIMITS = [0, 1, 2, 3, 5, 10, 16]; + +{ + let n = 0; + function next() { + if (n >= LIMITS.length) return; + tls.CLIENT_RENEG_LIMIT = LIMITS[n++]; + test(next); + } + next(); +} + +function test(next) { + const options = { + cert: fixtures.readKey('rsa_cert.crt'), + key: fixtures.readKey('rsa_private.pem'), + }; + + const server = tls.createServer(options, (conn) => { + conn.on('error', (err) => { + console.error(`Caught exception: ${err}`); + assert.match(err.message, /TLS session renegotiation attack/); + conn.destroy(); + }); + conn.pipe(conn); + }); + + server.listen(0, () => { + const options = { + host: server.address().host, + port: server.address().port, + rejectUnauthorized: false, + }; + const client = tls.connect(options, spam); + + let renegs = 0; + + client.on('close', () => { + assert.strictEqual(renegs, tls.CLIENT_RENEG_LIMIT + 1); + server.close(); + process.nextTick(next); + }); + + client.on('error', (err) => { + console.log('CLIENT ERR', err); + throw err; + }); + + client.on('close', (hadErr) => { + assert.strictEqual(hadErr, false); + }); + + // Simulate renegotiation attack + function spam() { + client.write(''); + client.renegotiate({}, (err) => { + assert.ifError(err); + assert.ok(renegs <= tls.CLIENT_RENEG_LIMIT); + spam(); + }); + renegs++; + } + }); +} diff --git a/test/js/node/test/parallel/test-tls-client-verify.js b/test/js/node/test/parallel/test-tls-client-verify.js new file mode 100644 index 0000000000..a8de1078bf --- /dev/null +++ b/test/js/node/test/parallel/test-tls-client-verify.js @@ -0,0 +1,144 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const testCases = [ + { ca: ['ca1-cert'], + key: 'agent2-key', + cert: 'agent2-cert', + servers: [ + { ok: true, key: 'agent1-key', cert: 'agent1-cert' }, + { ok: false, key: 'agent2-key', cert: 'agent2-cert' }, + { ok: false, key: 'agent3-key', cert: 'agent3-cert' }, + ] }, + + { ca: [], + key: 'agent2-key', + cert: 'agent2-cert', + servers: [ + { ok: false, key: 'agent1-key', cert: 'agent1-cert' }, + { ok: false, key: 'agent2-key', cert: 'agent2-cert' }, + { ok: false, key: 'agent3-key', cert: 'agent3-cert' }, + ] }, + + { ca: ['ca1-cert', 'ca2-cert'], + key: 'agent2-key', + cert: 'agent2-cert', + servers: [ + { ok: true, key: 'agent1-key', cert: 'agent1-cert' }, + { ok: false, key: 'agent2-key', cert: 'agent2-cert' }, + { ok: true, key: 'agent3-key', cert: 'agent3-cert' }, + ] }, +]; + + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + +let successfulTests = 0; + +function testServers(index, servers, clientOptions, cb) { + const serverOptions = servers[index]; + if (!serverOptions) { + cb(); + return; + } + + const ok = serverOptions.ok; + + if (serverOptions.key) { + serverOptions.key = loadPEM(serverOptions.key); + } + + if (serverOptions.cert) { + serverOptions.cert = loadPEM(serverOptions.cert); + } + + const server = tls.createServer(serverOptions, common.mustCall(function(s) { + s.end('hello world\n'); + })); + + server.listen(0, common.mustCall(function() { + let b = ''; + + console.error('connecting...'); + clientOptions.port = this.address().port; + const client = tls.connect(clientOptions, common.mustCall(function() { + const authorized = client.authorized || + (client.authorizationError === 'ERR_TLS_CERT_ALTNAME_INVALID'); + + console.error(`expected: ${ok} authed: ${authorized}`); + + assert.strictEqual(authorized, ok); + server.close(); + })); + + client.on('data', function(d) { + b += d.toString(); + }); + + client.on('end', common.mustCall(function() { + assert.strictEqual(b, 'hello world\n'); + })); + + client.on('close', common.mustCall(function() { + testServers(index + 1, servers, clientOptions, cb); + })); + })); +} + + +function runTest(testIndex) { + const tcase = testCases[testIndex]; + if (!tcase) return; + + const clientOptions = { + port: undefined, + ca: tcase.ca.map(loadPEM), + key: loadPEM(tcase.key), + cert: loadPEM(tcase.cert), + rejectUnauthorized: false + }; + + + testServers(0, tcase.servers, clientOptions, common.mustCall(function() { + successfulTests++; + runTest(testIndex + 1); + })); +} + + +runTest(0); + + +process.on('exit', function() { + console.log(`successful tests: ${successfulTests}`); + assert.strictEqual(successfulTests, testCases.length); +}); diff --git a/test/js/node/test/parallel/test-tls-connect-address-family.js b/test/js/node/test/parallel/test-tls-connect-address-family.js new file mode 100644 index 0000000000..083208cc1d --- /dev/null +++ b/test/js/node/test/parallel/test-tls-connect-address-family.js @@ -0,0 +1,49 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.hasIPv6) + common.skip('no IPv6 support'); + +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const tls = require('tls'); +const dns = require('dns'); + +function runTest() { + tls.createServer({ + cert: fixtures.readKey('agent1-cert.pem'), + key: fixtures.readKey('agent1-key.pem'), + }).on('connection', common.mustCall(function() { + this.close(); + })).listen(0, '::1', common.mustCall(function() { + const options = { + host: 'localhost', + port: this.address().port, + family: 6, + rejectUnauthorized: false, + }; + // Will fail with ECONNREFUSED if the address family is not honored. + tls.connect(options).once('secureConnect', common.mustCall(function() { + assert.strictEqual(this.remoteAddress, '::1'); + this.destroy(); + })); + })); +} + +dns.lookup('localhost', { + family: 6, all: true +}, common.mustCall((err, addresses) => { + if (err) { + if (err.code === 'ENOTFOUND' || err.code === 'EAI_AGAIN') + common.skip('localhost does not resolve to ::1'); + + throw err; + } + + if (addresses.some((val) => val.address === '::1')) + runTest(); + else + common.skip('localhost does not resolve to ::1'); +})); diff --git a/test/js/node/test/parallel/test-tls-connect-no-host.js b/test/js/node/test/parallel/test-tls-connect-no-host.js new file mode 100644 index 0000000000..97b95332c4 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-connect-no-host.js @@ -0,0 +1,33 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tls = require('tls'); +const assert = require('assert'); + +const cert = fixtures.readKey('rsa_cert.crt'); +const key = fixtures.readKey('rsa_private.pem'); + +// https://github.com/nodejs/node/issues/1489 +// tls.connect(options) with no options.host should accept a cert with +// CN:'localhost' +const server = tls.createServer({ + key, + cert +}).listen(0, common.mustCall(function() { + const socket = tls.connect({ + port: this.address().port, + ca: cert, + // No host set here. 'localhost' is the default, + // but tls.checkServerIdentity() breaks before the fix with: + // Error: Hostname/IP doesn't match certificate's altnames: + // "Host: undefined. is not cert's CN: localhost" + }, common.mustCall(function() { + assert(socket.authorized); + socket.destroy(); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-tls-connect-secure-context.js b/test/js/node/test/parallel/test-tls-connect-secure-context.js new file mode 100644 index 0000000000..31941656c0 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-connect-secure-context.js @@ -0,0 +1,53 @@ +'use strict'; +require('../common'); + +// Verify connection with explicitly created client SecureContext. + +const fixtures = require('../common/fixtures'); +const { + assert, connect, keys, tls +} = require(fixtures.path('tls-connect')); + +connect({ + client: { + servername: 'agent1', + secureContext: tls.createSecureContext({ + ca: keys.agent1.ca, + }), + }, + server: { + cert: keys.agent1.cert, + key: keys.agent1.key, + }, +}, function(err, pair, cleanup) { + assert.ifError(err); + return cleanup(); +}); + +connect({ + client: { + servername: 'agent1', + secureContext: tls.createSecureContext({ + ca: keys.agent1.ca, + ciphers: null, + clientCertEngine: null, + crl: null, + dhparam: null, + passphrase: null, + pfx: null, + privateKeyIdentifier: null, + privateKeyEngine: null, + sessionIdContext: null, + sessionTimeout: null, + sigalgs: null, + ticketKeys: null, + }), + }, + server: { + cert: keys.agent1.cert, + key: keys.agent1.key, + }, +}, function(err, pair, cleanup) { + assert.ifError(err); + return cleanup(); +}); diff --git a/test/js/node/test/parallel/test-tls-dhe.js b/test/js/node/test/parallel/test-tls-dhe.js new file mode 100644 index 0000000000..46779b09ff --- /dev/null +++ b/test/js/node/test/parallel/test-tls-dhe.js @@ -0,0 +1,112 @@ +// Flags: --no-warnings +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('missing openssl-cli'); + +const assert = require('assert'); +const { X509Certificate } = require('crypto'); +const { once } = require('events'); +const tls = require('tls'); +const { execFile } = require('child_process'); +const fixtures = require('../common/fixtures'); + +const key = fixtures.readKey('agent2-key.pem'); +const cert = fixtures.readKey('agent2-cert.pem'); + +// Prefer DHE over ECDHE when possible. +const dheCipher = 'DHE-RSA-AES128-SHA256'; +const ecdheCipher = 'ECDHE-RSA-AES128-SHA256'; +const ciphers = `${dheCipher}:${ecdheCipher}`; + +// Test will emit a warning because the DH parameter size is < 2048 bits +common.expectWarning('SecurityWarning', + 'DH parameter is less than 2048 bits'); + +function loadDHParam(n) { + const keyname = `dh${n}.pem`; + return fixtures.readKey(keyname); +} + +function test(dhparam, keylen, expectedCipher) { + const options = { + key, + cert, + ciphers, + dhparam, + maxVersion: 'TLSv1.2', + }; + + const server = tls.createServer(options, (conn) => conn.end()); + + server.listen(0, '127.0.0.1', common.mustCall(() => { + const args = ['s_client', '-connect', `127.0.0.1:${server.address().port}`, + '-cipher', `${ciphers}:@SECLEVEL=1`]; + + execFile(common.opensslCli, args, common.mustSucceed((stdout) => { + assert(keylen === null || + stdout.includes(`Server Temp Key: DH, ${keylen} bits`)); + assert(stdout.includes(`Cipher : ${expectedCipher}`)); + server.close(); + })); + })); + + return once(server, 'close'); +} + +function testCustomParam(keylen, expectedCipher) { + const dhparam = loadDHParam(keylen); + if (keylen === 'error') keylen = null; + return test(dhparam, keylen, expectedCipher); +} + +(async () => { + // By default, DHE is disabled while ECDHE is enabled. + for (const dhparam of [undefined, null]) { + await test(dhparam, null, ecdheCipher); + } + + // The DHE parameters selected by OpenSSL depend on the strength of the + // certificate's key. For this test, we can assume that the modulus length + // of the certificate's key is equal to the size of the DHE parameter, but + // that is really only true for a few modulus lengths. + const { + publicKey: { asymmetricKeyDetails: { modulusLength } } + } = new X509Certificate(cert); + await test('auto', modulusLength, dheCipher); + + assert.throws(() => { + testCustomParam(512); + }, /DH parameter is less than 1024 bits/); + + // Custom DHE parameters are supported (but discouraged). + await testCustomParam(1024, dheCipher); + await testCustomParam(2048, dheCipher); + + // Invalid DHE parameters are discarded. ECDHE remains enabled. + await testCustomParam('error', ecdheCipher); +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-tls-ecdh-auto.js b/test/js/node/test/parallel/test-tls-ecdh-auto.js new file mode 100644 index 0000000000..11c588d8ac --- /dev/null +++ b/test/js/node/test/parallel/test-tls-ecdh-auto.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that the value "auto" on ecdhCurve option is +// supported to enable automatic curve selection in TLS server. + +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('missing openssl-cli'); + +const assert = require('assert'); +const tls = require('tls'); +const { execFile } = require('child_process'); +const fixtures = require('../common/fixtures'); + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + +const options = { + key: loadPEM('agent2-key'), + cert: loadPEM('agent2-cert'), + ciphers: '-ALL:ECDHE-RSA-AES128-SHA256', + ecdhCurve: 'auto', + maxVersion: 'TLSv1.2', +}; + +const reply = 'I AM THE WALRUS'; // Something recognizable + +const server = tls.createServer(options, (conn) => { + conn.end(reply); +}).listen(0, common.mustCall(() => { + const args = ['s_client', + '-cipher', `${options.ciphers}`, + '-connect', `127.0.0.1:${server.address().port}`]; + + execFile(common.opensslCli, args, common.mustSucceed((stdout) => { + assert(stdout.includes(reply)); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-tls-ecdh-multiple.js b/test/js/node/test/parallel/test-tls-ecdh-multiple.js new file mode 100644 index 0000000000..5bf119f48b --- /dev/null +++ b/test/js/node/test/parallel/test-tls-ecdh-multiple.js @@ -0,0 +1,61 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that ecdhCurve option of TLS server supports colon +// separated ECDH curve names as value. + +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('missing openssl-cli'); + +const assert = require('assert'); +const tls = require('tls'); +const { execFile } = require('child_process'); +const fixtures = require('../common/fixtures'); + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + +const options = { + key: loadPEM('agent2-key'), + cert: loadPEM('agent2-cert'), + ciphers: '-ALL:ECDHE-RSA-AES128-SHA256', + ecdhCurve: 'secp256k1:prime256v1:secp521r1', + maxVersion: 'TLSv1.2', +}; + +const reply = 'I AM THE WALRUS'; // Something recognizable + +const server = tls.createServer(options, (conn) => { + conn.end(reply); +}).listen(0, common.mustCall(() => { + const args = ['s_client', + '-cipher', `${options.ciphers}`, + '-connect', `127.0.0.1:${server.address().port}`]; + + execFile(common.opensslCli, args, common.mustSucceed((stdout) => { + assert(stdout.includes(reply)); + server.close(); + })); +})); + +{ + // Some unsupported curves. + const unsupportedCurves = [ + 'wap-wsg-idm-ecid-wtls1', + 'c2pnb163v1', + 'prime192v3', + ]; + + // Brainpool is not supported in FIPS mode. + if (common.hasFipsCrypto) + unsupportedCurves.push('brainpoolP256r1'); + + unsupportedCurves.forEach((ecdhCurve) => { + assert.throws(() => tls.createServer({ ecdhCurve }), + /Error: Failed to set ECDH curve/); + }); +} diff --git a/test/js/node/test/parallel/test-tls-ecdh.js b/test/js/node/test/parallel/test-tls-ecdh.js new file mode 100644 index 0000000000..8c879f850c --- /dev/null +++ b/test/js/node/test/parallel/test-tls-ecdh.js @@ -0,0 +1,59 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('missing openssl-cli'); + +const assert = require('assert'); +const tls = require('tls'); + +const exec = require('child_process').exec; + +const options = { + key: fixtures.readKey('agent2-key.pem'), + cert: fixtures.readKey('agent2-cert.pem'), + ciphers: '-ALL:ECDHE-RSA-AES128-SHA256', + ecdhCurve: 'prime256v1', + maxVersion: 'TLSv1.2' +}; + +const reply = 'I AM THE WALRUS'; // Something recognizable + +const server = tls.createServer(options, common.mustCall(function(conn) { + conn.end(reply); +})); + +server.listen(0, '127.0.0.1', common.mustCall(function() { + const cmd = `"${common.opensslCli}" s_client -cipher ${ + options.ciphers} -connect 127.0.0.1:${this.address().port}`; + + exec(cmd, common.mustSucceed((stdout, stderr) => { + assert(stdout.includes(reply)); + server.close(); + })); +})); diff --git a/test/js/node/test/parallel/test-tls-env-extra-ca-no-crypto.js b/test/js/node/test/parallel/test-tls-env-extra-ca-no-crypto.js new file mode 100644 index 0000000000..6f2aca505e --- /dev/null +++ b/test/js/node/test/parallel/test-tls-env-extra-ca-no-crypto.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { fork } = require('child_process'); + +// This test ensures that trying to load extra certs won't throw even when +// there is no crypto support, i.e., built with "./configure --without-ssl". +if (process.argv[2] === 'child') { + // exit +} else { + const NODE_EXTRA_CA_CERTS = fixtures.path('keys', 'ca1-cert.pem'); + + fork( + __filename, + ['child'], + { env: { ...process.env, NODE_EXTRA_CA_CERTS } }, + ).on('exit', common.mustCall(function(status) { + // Client did not succeed in connecting + assert.strictEqual(status, 0); + })); +} diff --git a/test/js/node/test/parallel/test-tls-fast-writing.js b/test/js/node/test/parallel/test-tls-fast-writing.js new file mode 100644 index 0000000000..4718acf285 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-fast-writing.js @@ -0,0 +1,76 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const tls = require('tls'); + +const options = { key: fixtures.readKey('rsa_private.pem'), + cert: fixtures.readKey('rsa_cert.crt'), + ca: [ fixtures.readKey('rsa_ca.crt') ] }; + +const server = tls.createServer(options, onconnection); +let gotChunk = false; +let gotDrain = false; + +function onconnection(conn) { + conn.on('data', function(c) { + if (!gotChunk) { + gotChunk = true; + console.log('ok - got chunk'); + } + + // Just some basic sanity checks. + assert(c.length); + assert(Buffer.isBuffer(c)); + + if (gotDrain) + process.exit(0); + }); +} + +server.listen(0, function() { + const chunk = Buffer.alloc(1024, 'x'); + const opt = { port: this.address().port, rejectUnauthorized: false }; + const conn = tls.connect(opt, function() { + conn.on('drain', ondrain); + write(); + }); + function ondrain() { + if (!gotDrain) { + gotDrain = true; + console.log('ok - got drain'); + } + if (gotChunk) + process.exit(0); + write(); + } + + function write() { + // This needs to return false eventually + while (false !== conn.write(chunk)); + } +}); diff --git a/test/js/node/test/parallel/test-tls-inception.js b/test/js/node/test/parallel/test-tls-inception.js new file mode 100644 index 0000000000..7310308e6f --- /dev/null +++ b/test/js/node/test/parallel/test-tls-inception.js @@ -0,0 +1,86 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); + +const net = require('net'); + +const options = { + key: fixtures.readKey('rsa_private.pem'), + cert: fixtures.readKey('rsa_cert.crt') +}; + +const body = 'A'.repeat(40000); + +// the "proxy" server +const a = tls.createServer(options, function(socket) { + const myOptions = { + host: '127.0.0.1', + port: b.address().port, + rejectUnauthorized: false + }; + const dest = net.connect(myOptions); + dest.pipe(socket); + socket.pipe(dest); + + dest.on('end', function() { + socket.destroy(); + }); +}); + +// the "target" server +const b = tls.createServer(options, function(socket) { + socket.end(body); +}); + +a.listen(0, function() { + b.listen(0, function() { + const myOptions = { + host: '127.0.0.1', + port: a.address().port, + rejectUnauthorized: false + }; + const socket = tls.connect(myOptions); + const ssl = tls.connect({ + socket: socket, + rejectUnauthorized: false + }); + ssl.setEncoding('utf8'); + let buf = ''; + ssl.on('data', function(data) { + buf += data; + }); + ssl.on('end', common.mustCall(function() { + assert.strictEqual(buf, body); + ssl.end(); + a.close(); + b.close(); + })); + }); +}); diff --git a/test/js/node/test/parallel/test-tls-multiple-cas-as-string.js b/test/js/node/test/parallel/test-tls-multiple-cas-as-string.js new file mode 100644 index 0000000000..679d6b6c4c --- /dev/null +++ b/test/js/node/test/parallel/test-tls-multiple-cas-as-string.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +// Verify that multiple CA certificates can be provided, and that for +// convenience that can also be in newline-separated strings. + +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const ca1 = fixtures.readKey('ca1-cert.pem', 'utf8'); +const ca2 = fixtures.readKey('ca2-cert.pem', 'utf8'); +const cert = fixtures.readKey('agent3-cert.pem', 'utf8'); +const key = fixtures.readKey('agent3-key.pem', 'utf8'); + +function test(ca) { + const server = tls.createServer({ ca, cert, key }); + + server.addContext('agent3', { ca, cert, key }); + + const host = common.localhostIPv4; + server.listen(0, host, common.mustCall(() => { + const socket = tls.connect({ + servername: 'agent3', + host, + port: server.address().port, + ca + }, common.mustCall(() => { + socket.end(); + })); + + socket.on('close', () => { + server.close(); + }); + })); +} + +// `ca1` is not actually necessary for the certificate validation -- maybe +// the fixtures should be written in a way that requires it? +test([ca1, ca2]); +test(`${ca1}\n${ca2}`); diff --git a/test/js/node/test/parallel/test-tls-net-connect-prefer-path.js b/test/js/node/test/parallel/test-tls-net-connect-prefer-path.js new file mode 100644 index 0000000000..cefeb5d471 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-net-connect-prefer-path.js @@ -0,0 +1,65 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +// This tests that both tls and net will ignore host and port if path is +// provided. + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const tls = require('tls'); +const net = require('net'); +const assert = require('assert'); + +function libName(lib) { + return lib === net ? 'net' : 'tls'; +} + +function mkServer(lib, tcp, cb) { + const handler = (socket) => { + socket.write(`${libName(lib)}:${ + server.address().port || server.address() + }`); + socket.end(); + }; + const args = [handler]; + if (lib === tls) { + args.unshift({ + cert: fixtures.readKey('rsa_cert.crt'), + key: fixtures.readKey('rsa_private.pem') + }); + } + const server = lib.createServer(...args); + server.listen(tcp ? 0 : common.PIPE, common.mustCall(() => cb(server))); +} + +function testLib(lib, cb) { + mkServer(lib, true, (tcpServer) => { + mkServer(lib, false, (unixServer) => { + const client = lib.connect({ + path: unixServer.address(), + port: tcpServer.address().port, + host: 'localhost', + rejectUnauthorized: false + }, () => { + const bufs = []; + client.on('data', common.mustCall((d) => { + bufs.push(d); + })); + client.on('end', common.mustCall(() => { + const resp = Buffer.concat(bufs).toString(); + assert.strictEqual(resp, `${libName(lib)}:${unixServer.address()}`); + tcpServer.close(); + unixServer.close(); + cb(); + })); + }); + }); + }); +} + +testLib(net, common.mustCall(() => testLib(tls, common.mustCall()))); diff --git a/test/js/node/test/parallel/test-tls-no-sslv3.js b/test/js/node/test/parallel/test-tls-no-sslv3.js new file mode 100644 index 0000000000..9282beb4bd --- /dev/null +++ b/test/js/node/test/parallel/test-tls-no-sslv3.js @@ -0,0 +1,47 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (common.opensslCli === false) + common.skip('node compiled without OpenSSL CLI.'); + +const assert = require('assert'); +const tls = require('tls'); +const spawn = require('child_process').spawn; +const fixtures = require('../common/fixtures'); + +const cert = fixtures.readKey('rsa_cert.crt'); +const key = fixtures.readKey('rsa_private.pem'); +const server = tls.createServer({ cert, key }, common.mustNotCall()); +const errors = []; +let stderr = ''; + +server.listen(0, '127.0.0.1', function() { + const address = `${this.address().address}:${this.address().port}`; + const args = ['s_client', + '-ssl3', + '-connect', address]; + + const client = spawn(common.opensslCli, args, { stdio: 'pipe' }); + client.stdout.pipe(process.stdout); + client.stderr.pipe(process.stderr); + client.stderr.setEncoding('utf8'); + client.stderr.on('data', (data) => stderr += data); + + client.once('exit', common.mustCall(function(exitCode) { + assert.strictEqual(exitCode, 1); + server.close(); + })); +}); + +server.on('tlsClientError', (err) => errors.push(err)); + +process.on('exit', function() { + if (/[Uu]nknown option:? -ssl3/.test(stderr)) { + common.printSkipMessage('`openssl s_client -ssl3` not supported.'); + } else { + assert.strictEqual(errors.length, 1); + assert(/:version too low/.test(errors[0].message)); + } +}); diff --git a/test/js/node/test/parallel/test-tls-ocsp-callback.js b/test/js/node/test/parallel/test-tls-ocsp-callback.js new file mode 100644 index 0000000000..04a60a0890 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-ocsp-callback.js @@ -0,0 +1,113 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.opensslCli) + common.skip('node compiled without OpenSSL CLI.'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const assert = require('assert'); + +const SSL_OP_NO_TICKET = require('crypto').constants.SSL_OP_NO_TICKET; + +const pfx = fixtures.readKey('agent1.pfx'); +const key = fixtures.readKey('agent1-key.pem'); +const cert = fixtures.readKey('agent1-cert.pem'); +const ca = fixtures.readKey('ca1-cert.pem'); + +function test(testOptions, cb) { + const options = { + key, + cert, + ca: [ca] + }; + const requestCount = testOptions.response ? 0 : 1; + + if (!testOptions.ocsp) + assert.strictEqual(testOptions.response, undefined); + + if (testOptions.pfx) { + delete options.key; + delete options.cert; + options.pfx = testOptions.pfx; + options.passphrase = testOptions.passphrase; + } + + const server = tls.createServer(options, common.mustCall((cleartext) => { + cleartext.on('error', function(er) { + // We're ok with getting ECONNRESET in this test, but it's + // timing-dependent, and thus unreliable. Any other errors + // are just failures, though. + if (er.code !== 'ECONNRESET') + throw er; + }); + cleartext.end(); + }, requestCount)); + + if (!testOptions.ocsp) + server.on('OCSPRequest', common.mustNotCall()); + else + server.on('OCSPRequest', common.mustCall((cert, issuer, callback) => { + assert.ok(Buffer.isBuffer(cert)); + assert.ok(Buffer.isBuffer(issuer)); + + // Callback a little later to ensure that async really works. + return setTimeout(callback, 100, null, testOptions.response ? + Buffer.from(testOptions.response) : null); + })); + + server.listen(0, function() { + const client = tls.connect({ + port: this.address().port, + requestOCSP: testOptions.ocsp, + secureOptions: testOptions.ocsp ? 0 : SSL_OP_NO_TICKET, + rejectUnauthorized: false + }, common.mustCall(requestCount)); + + client.on('OCSPResponse', common.mustCall((resp) => { + if (testOptions.response) { + assert.strictEqual(resp.toString(), testOptions.response); + client.destroy(); + } else { + assert.strictEqual(resp, null); + } + }, testOptions.ocsp === false ? 0 : 1)); + + client.on('close', common.mustCall(() => { + server.close(cb); + })); + }); +} + +test({ ocsp: true, response: false }); +test({ ocsp: true, response: 'hello world' }); +test({ ocsp: false }); + +if (!common.hasFipsCrypto) { + test({ ocsp: true, response: 'hello pfx', pfx: pfx, passphrase: 'sample' }); +} diff --git a/test/js/node/test/parallel/test-tls-on-empty-socket.js b/test/js/node/test/parallel/test-tls-on-empty-socket.js new file mode 100644 index 0000000000..87d51a81bb --- /dev/null +++ b/test/js/node/test/parallel/test-tls-on-empty-socket.js @@ -0,0 +1,40 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const net = require('net'); +const fixtures = require('../common/fixtures'); + +let out = ''; + +const server = tls.createServer({ + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}, function(c) { + c.end('hello'); +}).listen(0, function() { + const socket = new net.Socket(); + + const s = tls.connect({ + socket: socket, + rejectUnauthorized: false + }, function() { + s.on('data', function(chunk) { + out += chunk; + }); + s.on('end', function() { + s.destroy(); + server.close(); + }); + }); + + socket.connect(this.address().port); +}); + +process.on('exit', function() { + assert.strictEqual(out, 'hello'); +}); diff --git a/test/js/node/test/parallel/test-tls-peer-certificate-encoding.js b/test/js/node/test/parallel/test-tls-peer-certificate-encoding.js new file mode 100644 index 0000000000..154c31c0a1 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-peer-certificate-encoding.js @@ -0,0 +1,53 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const util = require('util'); +const fixtures = require('../common/fixtures'); + +const options = { + key: fixtures.readKey('agent5-key.pem'), + cert: fixtures.readKey('agent5-cert.pem'), + ca: [ fixtures.readKey('ca2-cert.pem') ] +}; + +const server = tls.createServer(options, (cleartext) => { + cleartext.end('World'); +}); +server.listen(0, common.mustCall(function() { + const socket = tls.connect({ + port: this.address().port, + rejectUnauthorized: false + }, common.mustCall(() => { + const peerCert = socket.getPeerCertificate(); + + console.error(util.inspect(peerCert)); + assert.strictEqual(peerCert.subject.CN, 'Ádám Lippai'); + server.close(); + })); + socket.end('Hello'); +})); diff --git a/test/js/node/test/parallel/test-tls-peer-certificate-multi-keys.js b/test/js/node/test/parallel/test-tls-peer-certificate-multi-keys.js new file mode 100644 index 0000000000..ce4a0d406f --- /dev/null +++ b/test/js/node/test/parallel/test-tls-peer-certificate-multi-keys.js @@ -0,0 +1,62 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const options = { + key: fixtures.readKey('rsa_private.pem'), + cert: fixtures.readKey('rsa_cert.crt') +}; + +const server = tls.createServer(options, function(cleartext) { + cleartext.end('World'); +}); + +server.once('secureConnection', common.mustCall(function(socket) { + const cert = socket.getCertificate(); + // The server's local cert is the client's peer cert. + assert.deepStrictEqual( + cert.subject.OU, + ['Test TLS Certificate', 'Engineering'] + ); +})); + +server.listen(0, common.mustCall(function() { + const socket = tls.connect({ + port: this.address().port, + rejectUnauthorized: false + }, common.mustCall(function() { + const peerCert = socket.getPeerCertificate(); + assert.deepStrictEqual( + peerCert.subject.OU, + ['Test TLS Certificate', 'Engineering'] + ); + server.close(); + })); + socket.end('Hello'); +})); diff --git a/test/js/node/test/parallel/test-tls-psk-server.js b/test/js/node/test/parallel/test-tls-psk-server.js new file mode 100644 index 0000000000..b926095840 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-psk-server.js @@ -0,0 +1,77 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); +if (!common.opensslCli) + common.skip('missing openssl cli'); + +const assert = require('assert'); + +const tls = require('tls'); +const spawn = require('child_process').spawn; + +const CIPHERS = 'PSK+HIGH'; +const KEY = 'd731ef57be09e5204f0b205b60627028'; +const IDENTITY = 'TestUser'; + +const server = tls.createServer({ + ciphers: CIPHERS, + pskIdentityHint: IDENTITY, + pskCallback(socket, identity) { + assert.ok(socket instanceof tls.TLSSocket); + assert.ok(typeof identity === 'string'); + if (identity === IDENTITY) + return Buffer.from(KEY, 'hex'); + } +}); + +server.on('connection', common.mustCall()); + +server.on('secureConnection', (socket) => { + socket.write('hello\r\n'); + + socket.on('data', (data) => { + socket.write(data); + }); +}); + +let gotHello = false; +let sentWorld = false; +let gotWorld = false; + +server.listen(0, () => { + const client = spawn(common.opensslCli, [ + 's_client', + '-connect', `127.0.0.1:${server.address().port}`, + '-cipher', CIPHERS, + '-psk', KEY, + '-psk_identity', IDENTITY, + ]); + + let out = ''; + + client.stdout.setEncoding('utf8'); + client.stdout.on('data', (d) => { + out += d; + + if (!gotHello && /hello/.test(out)) { + gotHello = true; + client.stdin.write('world\r\n'); + sentWorld = true; + } + + if (!gotWorld && /world/.test(out)) { + gotWorld = true; + client.stdin.end(); + } + }); + + client.on('exit', common.mustCall((code) => { + assert.ok(gotHello); + assert.ok(sentWorld); + assert.ok(gotWorld); + assert.strictEqual(code, 0); + server.close(); + })); +}); diff --git a/test/js/node/test/parallel/test-tls-reuse-host-from-socket.js b/test/js/node/test/parallel/test-tls-reuse-host-from-socket.js new file mode 100644 index 0000000000..1a7705d911 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-reuse-host-from-socket.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const net = require('net'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const server = tls.createServer({ + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}).listen(0, common.mustCall(() => { + const socket = net.connect(server.address().port, common.mustCall(() => { + const opts = { socket, rejectUnauthorized: false }; + const secureSocket = tls.connect(opts, common.mustCall(() => { + secureSocket.destroy(); + server.close(); + })); + })); +})); diff --git a/test/js/node/test/parallel/test-tls-secure-context-usage-order.js b/test/js/node/test/parallel/test-tls-secure-context-usage-order.js new file mode 100644 index 0000000000..c79a3eac77 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-secure-context-usage-order.js @@ -0,0 +1,99 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +// This test ensures that when a TLS connection is established, the server +// selects the most recently added SecureContext that matches the servername. + +const assert = require('assert'); +const tls = require('tls'); + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + +const serverOptions = { + key: loadPEM('agent2-key'), + cert: loadPEM('agent2-cert'), + requestCert: true, + rejectUnauthorized: false, +}; + +const badSecureContext = { + key: loadPEM('agent1-key'), + cert: loadPEM('agent1-cert'), + ca: [ loadPEM('ca2-cert') ] +}; + +const goodSecureContext = { + key: loadPEM('agent1-key'), + cert: loadPEM('agent1-cert'), + ca: [ loadPEM('ca1-cert') ] +}; + +const server = tls.createServer(serverOptions, (c) => { + // The 'a' and 'b' subdomains are used to distinguish between client + // connections. + // Connection to subdomain 'a' is made when the 'bad' secure context is + // the only one in use. + if ('a.example.com' === c.servername) { + assert.strictEqual(c.authorized, false); + } + // Connection to subdomain 'b' is made after the 'good' context has been + // added. + if ('b.example.com' === c.servername) { + assert.strictEqual(c.authorized, true); + } +}); + +// 1. Add the 'bad' secure context. A connection using this context will not be +// authorized. +server.addContext('*.example.com', badSecureContext); + +server.listen(0, () => { + const options = { + port: server.address().port, + key: loadPEM('agent1-key'), + cert: loadPEM('agent1-cert'), + ca: [loadPEM('ca1-cert')], + servername: 'a.example.com', + rejectUnauthorized: false, + }; + + // 2. Make a connection using servername 'a.example.com'. Since a 'bad' + // secure context is used, this connection should not be authorized. + const client = tls.connect(options, () => { + client.end(); + }); + + client.on('close', common.mustCall(() => { + // 3. Add a 'good' secure context. + server.addContext('*.example.com', goodSecureContext); + + options.servername = 'b.example.com'; + // 4. Make a connection using servername 'b.example.com'. This connection + // should be authorized because the 'good' secure context is the most + // recently added matching context. + + const other = tls.connect(options, () => { + other.end(); + }); + + other.on('close', common.mustCall(() => { + // 5. Make another connection using servername 'b.example.com' to ensure + // that the array of secure contexts is not reversed in place with each + // SNICallback call, as someone might be tempted to refactor this piece of + // code by using Array.prototype.reverse() method. + const onemore = tls.connect(options, () => { + onemore.end(); + }); + + onemore.on('close', common.mustCall(() => { + server.close(); + })); + })); + })); +}); diff --git a/test/js/node/test/parallel/test-tls-securepair-server.js b/test/js/node/test/parallel/test-tls-securepair-server.js new file mode 100644 index 0000000000..78cd9f7254 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-securepair-server.js @@ -0,0 +1,145 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('missing openssl-cli'); + +const assert = require('assert'); +const tls = require('tls'); +const net = require('net'); +const spawn = require('child_process').spawn; +const fixtures = require('../common/fixtures'); + +const key = fixtures.readKey('rsa_private.pem'); +const cert = fixtures.readKey('rsa_cert.crt'); + +function log(a) { + console.error('***server***', a); +} + +const server = net.createServer(common.mustCall(function(socket) { + log(`connection fd=${socket.fd}`); + const sslcontext = tls.createSecureContext({ key, cert }); + sslcontext.context.setCiphers('RC4-SHA:AES128-SHA:AES256-SHA'); + + const pair = tls.createSecurePair(sslcontext, true); + + assert.ok(pair.encrypted.writable); + assert.ok(pair.cleartext.writable); + + pair.encrypted.pipe(socket); + socket.pipe(pair.encrypted); + + log('i set it secure'); + + pair.on('secure', function() { + log('connected+secure!'); + pair.cleartext.write('hello\r\n'); + log(pair.cleartext.getPeerCertificate()); + log(pair.cleartext.getCipher()); + }); + + pair.cleartext.on('data', function(data) { + log(`read bytes ${data.length}`); + pair.cleartext.write(data); + }); + + socket.on('end', function() { + log('socket end'); + }); + + pair.cleartext.on('error', function(err) { + log('got error: '); + log(err); + socket.destroy(); + }); + + pair.encrypted.on('error', function(err) { + log('encrypted error: '); + log(err); + socket.destroy(); + }); + + socket.on('error', function(err) { + log('socket error: '); + log(err); + socket.destroy(); + }); + + socket.on('close', function(err) { + log('socket closed'); + }); + + pair.on('error', function(err) { + log('secure error: '); + log(err); + socket.destroy(); + }); +})); + +let gotHello = false; +let sentWorld = false; +let gotWorld = false; + +server.listen(0, common.mustCall(function() { + // To test use: openssl s_client -connect localhost:8000 + + const args = ['s_client', '-connect', `127.0.0.1:${this.address().port}`]; + + const client = spawn(common.opensslCli, args); + + + let out = ''; + + client.stdout.setEncoding('utf8'); + client.stdout.on('data', function(d) { + out += d; + + if (!gotHello && /hello/.test(out)) { + gotHello = true; + client.stdin.write('world\r\n'); + sentWorld = true; + } + + if (!gotWorld && /world/.test(out)) { + gotWorld = true; + client.stdin.end(); + } + }); + + client.stdout.pipe(process.stdout, { end: false }); + + client.on('exit', common.mustCall(function(code) { + assert.strictEqual(code, 0); + server.close(); + })); +})); + +process.on('exit', function() { + assert.ok(gotHello); + assert.ok(sentWorld); + assert.ok(gotWorld); +}); diff --git a/test/js/node/test/parallel/test-tls-server-connection-server.js b/test/js/node/test/parallel/test-tls-server-connection-server.js new file mode 100644 index 0000000000..7fb2c74996 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-server-connection-server.js @@ -0,0 +1,32 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + +const server = tls.createServer(options, function(s) { + s.end('hello'); +}).listen(0, function() { + const opts = { + port: this.address().port, + rejectUnauthorized: false + }; + + server.on('connection', common.mustCall(function(socket) { + assert.strictEqual(socket.server, server); + server.close(); + })); + + const client = tls.connect(opts, function() { + client.end(); + }); +}); diff --git a/test/js/node/test/parallel/test-tls-server-verify.js b/test/js/node/test/parallel/test-tls-server-verify.js new file mode 100644 index 0000000000..51ccd0d747 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-server-verify.js @@ -0,0 +1,348 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +if (!common.opensslCli) + common.skip('node compiled without OpenSSL CLI.'); + +// This is a rather complex test which sets up various TLS servers with node +// and connects to them using the 'openssl s_client' command line utility +// with various keys. Depending on the certificate authority and other +// parameters given to the server, the various clients are +// - rejected, +// - accepted and "unauthorized", or +// - accepted and "authorized". + +const assert = require('assert'); +const { spawn } = require('child_process'); +const { SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION } = + require('crypto').constants; +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const testCases = + [{ title: 'Do not request certs. Everyone is unauthorized.', + requestCert: false, + rejectUnauthorized: false, + renegotiate: false, + CAs: ['ca1-cert'], + clients: + [{ name: 'agent1', shouldReject: false, shouldAuth: false }, + { name: 'agent2', shouldReject: false, shouldAuth: false }, + { name: 'agent3', shouldReject: false, shouldAuth: false }, + { name: 'nocert', shouldReject: false, shouldAuth: false }, + ] }, + + { title: 'Allow both authed and unauthed connections with CA1', + requestCert: true, + rejectUnauthorized: false, + renegotiate: false, + CAs: ['ca1-cert'], + clients: + [{ name: 'agent1', shouldReject: false, shouldAuth: true }, + { name: 'agent2', shouldReject: false, shouldAuth: false }, + { name: 'agent3', shouldReject: false, shouldAuth: false }, + { name: 'nocert', shouldReject: false, shouldAuth: false }, + ] }, + + { title: 'Do not request certs at connection. Do that later', + requestCert: false, + rejectUnauthorized: false, + renegotiate: true, + CAs: ['ca1-cert'], + clients: + [{ name: 'agent1', shouldReject: false, shouldAuth: true }, + { name: 'agent2', shouldReject: false, shouldAuth: false }, + { name: 'agent3', shouldReject: false, shouldAuth: false }, + { name: 'nocert', shouldReject: false, shouldAuth: false }, + ] }, + + { title: 'Allow only authed connections with CA1', + requestCert: true, + rejectUnauthorized: true, + renegotiate: false, + CAs: ['ca1-cert'], + clients: + [{ name: 'agent1', shouldReject: false, shouldAuth: true }, + { name: 'agent2', shouldReject: true }, + { name: 'agent3', shouldReject: true }, + { name: 'nocert', shouldReject: true }, + ] }, + + { title: 'Allow only authed connections with CA1 and CA2', + requestCert: true, + rejectUnauthorized: true, + renegotiate: false, + CAs: ['ca1-cert', 'ca2-cert'], + clients: + [{ name: 'agent1', shouldReject: false, shouldAuth: true }, + { name: 'agent2', shouldReject: true }, + { name: 'agent3', shouldReject: false, shouldAuth: true }, + { name: 'nocert', shouldReject: true }, + ] }, + + + { title: 'Allow only certs signed by CA2 but not in the CRL', + requestCert: true, + rejectUnauthorized: true, + renegotiate: false, + CAs: ['ca2-cert'], + crl: 'ca2-crl', + clients: [ + { name: 'agent1', shouldReject: true, shouldAuth: false }, + { name: 'agent2', shouldReject: true, shouldAuth: false }, + { name: 'agent3', shouldReject: false, shouldAuth: true }, + // Agent4 has a cert in the CRL. + { name: 'agent4', shouldReject: true, shouldAuth: false }, + { name: 'nocert', shouldReject: true }, + ] }, + ]; + +function filenamePEM(n) { + return fixtures.path('keys', `${n}.pem`); +} + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + + +const serverKey = loadPEM('agent2-key'); +const serverCert = loadPEM('agent2-cert'); + + +function runClient(prefix, port, options, cb) { + + // Client can connect in three ways: + // - Self-signed cert + // - Certificate, but not signed by CA. + // - Certificate signed by CA. + + const args = ['s_client', '-connect', `127.0.0.1:${port}`]; + + console.log(`${prefix} connecting with`, options.name); + + switch (options.name) { + case 'agent1': + // Signed by CA1 + args.push('-key'); + args.push(filenamePEM('agent1-key')); + args.push('-cert'); + args.push(filenamePEM('agent1-cert')); + break; + + case 'agent2': + // Self-signed + // This is also the key-cert pair that the server will use. + args.push('-key'); + args.push(filenamePEM('agent2-key')); + args.push('-cert'); + args.push(filenamePEM('agent2-cert')); + break; + + case 'agent3': + // Signed by CA2 + args.push('-key'); + args.push(filenamePEM('agent3-key')); + args.push('-cert'); + args.push(filenamePEM('agent3-cert')); + break; + + case 'agent4': + // Signed by CA2 (rejected by ca2-crl) + args.push('-key'); + args.push(filenamePEM('agent4-key')); + args.push('-cert'); + args.push(filenamePEM('agent4-cert')); + break; + + case 'nocert': + // Do not send certificate + break; + + default: + throw new Error(`${prefix}Unknown agent name`); + } + + // To test use: openssl s_client -connect localhost:8000 + const client = spawn(common.opensslCli, args); + + let out = ''; + + let rejected = true; + let authed = false; + let goodbye = false; + + client.stdout.setEncoding('utf8'); + client.stdout.on('data', function(d) { + out += d; + + if (!goodbye && /_unauthed/.test(out)) { + console.error(`${prefix} * unauthed`); + goodbye = true; + client.kill(); + authed = false; + rejected = false; + } + + if (!goodbye && /_authed/.test(out)) { + console.error(`${prefix} * authed`); + goodbye = true; + client.kill(); + authed = true; + rejected = false; + } + }); + + client.on('exit', function(code) { + if (options.shouldReject) { + assert.strictEqual( + rejected, true, + `${prefix}${options.name} NOT rejected, but should have been`); + } else { + assert.strictEqual( + rejected, false, + `${prefix}${options.name} rejected, but should NOT have been`); + assert.strictEqual( + authed, options.shouldAuth, + `${prefix}${options.name} authed is ${authed} but should have been ${ + options.shouldAuth}`); + } + + cb(); + }); +} + + +// Run the tests +let successfulTests = 0; +function runTest(port, testIndex) { + const prefix = `${testIndex} `; + const tcase = testCases[testIndex]; + if (!tcase) return; + + console.error(`${prefix}Running '${tcase.title}'`); + + const cas = tcase.CAs.map(loadPEM); + + const crl = tcase.crl ? loadPEM(tcase.crl) : null; + + const serverOptions = { + key: serverKey, + cert: serverCert, + ca: cas, + crl: crl, + requestCert: tcase.requestCert, + rejectUnauthorized: tcase.rejectUnauthorized + }; + + // If renegotiating - session might be resumed and openssl won't request + // client's certificate (probably because of bug in the openssl) + if (tcase.renegotiate) { + serverOptions.secureOptions = + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; + // Renegotiation as a protocol feature was dropped after TLS1.2. + serverOptions.maxVersion = 'TLSv1.2'; + } + + let renegotiated = false; + const server = tls.Server(serverOptions, function handleConnection(c) { + c.on('error', function(e) { + // child.kill() leads ECONNRESET error in the TLS connection of + // openssl s_client via spawn(). A test result is already + // checked by the data of client.stdout before child.kill() so + // these tls errors can be ignored. + }); + if (tcase.renegotiate && !renegotiated) { + renegotiated = true; + setTimeout(function() { + console.error(`${prefix}- connected, renegotiating`); + c.write('\n_renegotiating\n'); + return c.renegotiate({ + requestCert: true, + rejectUnauthorized: false + }, function(err) { + assert.ifError(err); + c.write('\n_renegotiated\n'); + handleConnection(c); + }); + }, 200); + return; + } + + if (c.authorized) { + console.error(`${prefix}- authed connection: ${ + c.getPeerCertificate().subject.CN}`); + c.write('\n_authed\n'); + } else { + console.error(`${prefix}- unauthed connection: %s`, c.authorizationError); + c.write('\n_unauthed\n'); + } + }); + + function runNextClient(clientIndex) { + const options = tcase.clients[clientIndex]; + if (options) { + runClient(`${prefix}${clientIndex} `, port, options, function() { + runNextClient(clientIndex + 1); + }); + } else { + server.close(); + successfulTests++; + runTest(0, nextTest++); + } + } + + server.listen(port, function() { + port = server.address().port; + if (tcase.debug) { + console.error(`${prefix}TLS server running on port ${port}`); + } else if (tcase.renegotiate) { + runNextClient(0); + } else { + let clientsCompleted = 0; + for (let i = 0; i < tcase.clients.length; i++) { + runClient(`${prefix}${i} `, port, tcase.clients[i], function() { + clientsCompleted++; + if (clientsCompleted === tcase.clients.length) { + server.close(); + successfulTests++; + runTest(0, nextTest++); + } + }); + } + } + }); +} + + +let nextTest = 0; +runTest(0, nextTest++); + + +process.on('exit', function() { + assert.strictEqual(successfulTests, testCases.length); +}); diff --git a/test/js/node/test/parallel/test-tls-session-cache.js b/test/js/node/test/parallel/test-tls-session-cache.js new file mode 100644 index 0000000000..e4ecb53282 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-session-cache.js @@ -0,0 +1,164 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const tls = require('tls'); +const { spawn } = require('child_process'); + +if (!common.opensslCli) + common.skip('node compiled without OpenSSL CLI.'); + + +doTest({ tickets: false }, function() { + doTest({ tickets: true }, function() { + doTest({ tickets: false, invalidSession: true }, function() { + console.error('all done'); + }); + }); +}); + +function doTest(testOptions, callback) { + const key = fixtures.readKey('rsa_private.pem'); + const cert = fixtures.readKey('rsa_cert.crt'); + const options = { + key, + cert, + ca: [cert], + requestCert: true, + rejectUnauthorized: false, + secureProtocol: 'TLS_method', + ciphers: 'RSA@SECLEVEL=0' + }; + let requestCount = 0; + let resumeCount = 0; + let newSessionCount = 0; + let session; + + const server = tls.createServer(options, function(cleartext) { + cleartext.on('error', function(er) { + // We're ok with getting ECONNRESET in this test, but it's + // timing-dependent, and thus unreliable. Any other errors + // are just failures, though. + if (er.code !== 'ECONNRESET') + throw er; + }); + ++requestCount; + cleartext.end(''); + }); + server.on('newSession', function(id, data, cb) { + ++newSessionCount; + // Emulate asynchronous store + setImmediate(() => { + assert.ok(!session); + session = { id, data }; + cb(); + }); + }); + server.on('resumeSession', function(id, callback) { + ++resumeCount; + assert.ok(session); + assert.strictEqual(session.id.toString('hex'), id.toString('hex')); + + let data = session.data; + + // Return an invalid session to test Node does not crash. + if (testOptions.invalidSession) { + data = Buffer.from('INVALID SESSION'); + session = null; + } + + // Just to check that async really works there + setImmediate(() => { + callback(null, data); + }); + }); + + server.listen(0, function() { + const args = [ + 's_client', + '-tls1', + '-cipher', (common.hasOpenSSL31 ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), + '-connect', `localhost:${this.address().port}`, + '-servername', 'ohgod', + '-key', fixtures.path('keys/rsa_private.pem'), + '-cert', fixtures.path('keys/rsa_cert.crt'), + '-reconnect', + ].concat(testOptions.tickets ? [] : '-no_ticket'); + + function spawnClient() { + const client = spawn(common.opensslCli, args, { + stdio: [ 0, 1, 'pipe' ] + }); + let err = ''; + client.stderr.setEncoding('utf8'); + client.stderr.on('data', function(chunk) { + err += chunk; + }); + + client.on('exit', common.mustCall(function(code, signal) { + if (code !== 0) { + // If SmartOS and connection refused, then retry. See + // https://github.com/nodejs/node/issues/2663. + if (common.isSunOS && err.includes('Connection refused')) { + requestCount = 0; + spawnClient(); + return; + } + assert.fail(`code: ${code}, signal: ${signal}, output: ${err}`); + } + assert.strictEqual(code, 0); + server.close(common.mustCall(function() { + setImmediate(callback); + })); + })); + } + + spawnClient(); + }); + + process.on('exit', function() { + // Each test run connects 6 times: an initial request and 5 reconnect + // requests. + assert.strictEqual(requestCount, 6); + + if (testOptions.tickets) { + // No session cache callbacks are called. + assert.strictEqual(resumeCount, 0); + assert.strictEqual(newSessionCount, 0); + } else if (testOptions.invalidSession) { + // The resume callback was called, but each connection established a + // fresh session. + assert.strictEqual(resumeCount, 5); + assert.strictEqual(newSessionCount, 6); + } else { + // The resume callback was called, and only the initial connection + // establishes a fresh session. + assert.ok(session); + assert.strictEqual(resumeCount, 5); + assert.strictEqual(newSessionCount, 1); + } + }); +} diff --git a/test/js/node/test/parallel/test-tls-set-ciphers.js b/test/js/node/test/parallel/test-tls-set-ciphers.js new file mode 100644 index 0000000000..313c5e2389 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-set-ciphers.js @@ -0,0 +1,131 @@ +'use strict'; +const common = require('../common'); +if (!common.hasOpenSSL3) + common.skip('missing crypto, or OpenSSL version lower than 3'); + +const fixtures = require('../common/fixtures'); +const { inspect } = require('util'); + +// Test cipher: option for TLS. + +const { + assert, connect, keys +} = require(fixtures.path('tls-connect')); + + +function test(cciphers, sciphers, cipher, cerr, serr, options) { + assert(cipher || cerr || serr, 'test missing any expectations'); + const where = inspect(new Error()).split('\n')[2].replace(/[^(]*/, ''); + + const max_tls_ver = (ciphers, options) => { + if (options instanceof Object && Object.hasOwn(options, 'maxVersion')) + return options.maxVersion; + if ((typeof ciphers === 'string' || ciphers instanceof String) && ciphers.length > 0 && !ciphers.includes('TLS_')) + return 'TLSv1.2'; + + return 'TLSv1.3'; + }; + + connect({ + client: { + checkServerIdentity: (servername, cert) => { }, + ca: `${keys.agent1.cert}\n${keys.agent6.ca}`, + ciphers: cciphers, + maxVersion: max_tls_ver(cciphers, options), + }, + server: { + cert: keys.agent6.cert, + key: keys.agent6.key, + ciphers: sciphers, + maxVersion: max_tls_ver(sciphers, options), + }, + }, common.mustCall((err, pair, cleanup) => { + function u(_) { return _ === undefined ? 'U' : _; } + console.log('test:', u(cciphers), u(sciphers), + 'expect', u(cipher), u(cerr), u(serr)); + console.log(' ', where); + if (!cipher) { + console.log('client', pair.client.err ? pair.client.err.code : undefined); + console.log('server', pair.server.err ? pair.server.err.code : undefined); + if (cerr) { + assert(pair.client.err); + assert.strictEqual(pair.client.err.code, cerr); + } + if (serr) { + assert(pair.server.err); + assert.strictEqual(pair.server.err.code, serr); + } + return cleanup(); + } + + const reply = 'So long and thanks for all the fish.'; + + assert.ifError(err); + assert.ifError(pair.server.err); + assert.ifError(pair.client.err); + assert(pair.server.conn); + assert(pair.client.conn); + assert.strictEqual(pair.client.conn.getCipher().name, cipher); + assert.strictEqual(pair.server.conn.getCipher().name, cipher); + + pair.server.conn.write(reply); + + pair.client.conn.on('data', common.mustCall((data) => { + assert.strictEqual(data.toString(), reply); + return cleanup(); + })); + })); +} + +const U = undefined; + +// Have shared ciphers. +test(U, 'AES256-SHA', 'AES256-SHA'); +test('AES256-SHA', U, 'AES256-SHA'); + +test(U, 'TLS_AES_256_GCM_SHA384', 'TLS_AES_256_GCM_SHA384'); +test('TLS_AES_256_GCM_SHA384', U, 'TLS_AES_256_GCM_SHA384'); +test('TLS_AES_256_GCM_SHA384:!TLS_CHACHA20_POLY1305_SHA256', U, 'TLS_AES_256_GCM_SHA384'); + +// Do not have shared ciphers. +test('TLS_AES_256_GCM_SHA384', 'TLS_CHACHA20_POLY1305_SHA256', + U, 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE', 'ERR_SSL_NO_SHARED_CIPHER'); + +test('AES128-SHA', 'AES256-SHA', U, 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE', + 'ERR_SSL_NO_SHARED_CIPHER'); +test('AES128-SHA:TLS_AES_256_GCM_SHA384', + 'TLS_CHACHA20_POLY1305_SHA256:AES256-SHA', + U, 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE', 'ERR_SSL_NO_SHARED_CIPHER'); + +// Cipher order ignored, TLS1.3 chosen before TLS1.2. +test('AES256-SHA:TLS_AES_256_GCM_SHA384', U, 'TLS_AES_256_GCM_SHA384'); +test(U, 'AES256-SHA:TLS_AES_256_GCM_SHA384', 'TLS_AES_256_GCM_SHA384'); + +// Cipher order ignored, TLS1.3 before TLS1.2 and +// cipher suites are not disabled if TLS ciphers are set only +// TODO: maybe these tests should be reworked so maxVersion clamping +// is done explicitly and not implicitly in the test() function +test('AES256-SHA', U, 'TLS_AES_256_GCM_SHA384', U, U, { maxVersion: 'TLSv1.3' }); +test(U, 'AES256-SHA', 'TLS_AES_256_GCM_SHA384', U, U, { maxVersion: 'TLSv1.3' }); + +// TLS_AES_128_CCM_8_SHA256 & TLS_AES_128_CCM_SHA256 are not enabled by +// default, but work. +test('TLS_AES_128_CCM_8_SHA256', U, + U, 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE', 'ERR_SSL_NO_SHARED_CIPHER'); + +test('TLS_AES_128_CCM_8_SHA256', 'TLS_AES_128_CCM_8_SHA256', + 'TLS_AES_128_CCM_8_SHA256'); + +// Invalid cipher values +test(9, 'AES256-SHA', U, 'ERR_INVALID_ARG_TYPE', U); +test('AES256-SHA', 9, U, U, 'ERR_INVALID_ARG_TYPE'); +test(':', 'AES256-SHA', U, 'ERR_INVALID_ARG_VALUE', U); +test('AES256-SHA', ':', U, U, 'ERR_INVALID_ARG_VALUE'); + +// Using '' is synonymous for "use default ciphers" +test('TLS_AES_256_GCM_SHA384', '', 'TLS_AES_256_GCM_SHA384'); +test('', 'TLS_AES_256_GCM_SHA384', 'TLS_AES_256_GCM_SHA384'); + +// Using null should be treated the same as undefined. +test(null, 'AES256-SHA', 'AES256-SHA'); +test('AES256-SHA', null, 'AES256-SHA'); diff --git a/test/js/node/test/parallel/test-tls-startcom-wosign-whitelist.js b/test/js/node/test/parallel/test-tls-startcom-wosign-whitelist.js new file mode 100644 index 0000000000..56ffd73aac --- /dev/null +++ b/test/js/node/test/parallel/test-tls-startcom-wosign-whitelist.js @@ -0,0 +1,84 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +let finished = 0; + +function loadPEM(n) { + return fixtures.readKey(`${n}.pem`); +} + +const testCases = [ + { // agent8 is signed by fake-startcom-root with notBefore of + // Oct 20 23:59:59 2016 GMT. It passes StartCom/WoSign check. + serverOpts: { + key: loadPEM('agent8-key'), + cert: loadPEM('agent8-cert') + }, + clientOpts: { + ca: loadPEM('fake-startcom-root-cert'), + port: undefined, + rejectUnauthorized: true + }, + errorCode: 'CERT_REVOKED' + }, + { // agent9 is signed by fake-startcom-root with notBefore of + // Oct 21 00:00:01 2016 GMT. It fails StartCom/WoSign check. + serverOpts: { + key: loadPEM('agent9-key'), + cert: loadPEM('agent9-cert') + }, + clientOpts: { + ca: loadPEM('fake-startcom-root-cert'), + port: undefined, + rejectUnauthorized: true + }, + errorCode: 'CERT_REVOKED' + }, +]; + + +function runNextTest(server, tindex) { + server.close(function() { + finished++; + runTest(tindex + 1); + }); +} + + +function runTest(tindex) { + const tcase = testCases[tindex]; + + if (!tcase) return; + + const server = tls.createServer(tcase.serverOpts, function(s) { + s.resume(); + }).listen(0, function() { + tcase.clientOpts.port = this.address().port; + const client = tls.connect(tcase.clientOpts); + client.on('error', function(e) { + assert.strictEqual(e.code, tcase.errorCode); + runNextTest(server, tindex); + }); + + client.on('secureConnect', function() { + // agent8 can pass StartCom/WoSign check so that the secureConnect + // is established. + assert.strictEqual(tcase.errorCode, 'CERT_REVOKED'); + client.end(); + runNextTest(server, tindex); + }); + }); +} + + +runTest(0); + +process.on('exit', function() { + assert.strictEqual(finished, testCases.length); +}); diff --git a/test/js/node/test/parallel/test-tls-tlswrap-segfault-2.js b/test/js/node/test/parallel/test-tls-tlswrap-segfault-2.js new file mode 100644 index 0000000000..d4f3b12b99 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-tlswrap-segfault-2.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +// This test ensures that Node.js doesn't incur a segfault while +// adding session or keylog listeners after destroy. +// https://github.com/nodejs/node/issues/38133 +// https://github.com/nodejs/node/issues/38135 + +const tls = require('tls'); +const tlsSocketKeyLog = tls.connect('cause-error'); +tlsSocketKeyLog.on('error', common.mustCall()); +tlsSocketKeyLog.on('close', common.mustCall(() => { + tlsSocketKeyLog.on('keylog', common.mustNotCall()); +})); + +const tlsSocketSession = tls.connect('cause-error-2'); +tlsSocketSession.on('error', common.mustCall()); +tlsSocketSession.on('close', common.mustCall(() => { + tlsSocketSession.on('session', common.mustNotCall()); +})); diff --git a/test/js/node/test/parallel/test-tls-tlswrap-segfault.js b/test/js/node/test/parallel/test-tls-tlswrap-segfault.js new file mode 100644 index 0000000000..a36016efa4 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-tlswrap-segfault.js @@ -0,0 +1,42 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); + +// This test ensures that Node.js doesn't incur a segfault while accessing +// TLSWrap fields after the parent handle was destroyed. +// https://github.com/nodejs/node/issues/5108 + +const assert = require('assert'); +const tls = require('tls'); + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + +const server = tls.createServer(options, function(s) { + s.end('hello'); +}).listen(0, function() { + const opts = { + port: this.address().port, + rejectUnauthorized: false + }; + const client = tls.connect(opts, function() { + putImmediate(client); + }); + client.resume(); +}); + +function putImmediate(client) { + setImmediate(function() { + if (client.ssl) { + const fd = client.ssl.fd; + assert(!!fd); + putImmediate(client); + } else { + server.close(); + } + }); +} diff --git a/test/js/node/test/parallel/test-tls-wrap-econnreset-socket.js b/test/js/node/test/parallel/test-tls-wrap-econnreset-socket.js new file mode 100644 index 0000000000..ec305b785e --- /dev/null +++ b/test/js/node/test/parallel/test-tls-wrap-econnreset-socket.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const net = require('net'); +const tls = require('tls'); + +const server = net.createServer((c) => { + c.end(); +}).listen(common.mustCall(() => { + const port = server.address().port; + + const socket = new net.Socket(); + + let errored = false; + tls.connect({ socket }) + .once('error', common.mustCall((e) => { + assert.strictEqual(e.code, 'ECONNRESET'); + assert.strictEqual(e.path, undefined); + assert.strictEqual(e.host, undefined); + assert.strictEqual(e.port, undefined); + assert.strictEqual(e.localAddress, undefined); + errored = true; + server.close(); + })) + .on('close', common.mustCall(() => { + assert.strictEqual(errored, true); + })); + + socket.connect(port); +})); diff --git a/test/js/node/test/parallel/test-tls-zero-clear-in.js b/test/js/node/test/parallel/test-tls-zero-clear-in.js new file mode 100644 index 0000000000..f24fb6f992 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-zero-clear-in.js @@ -0,0 +1,60 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const cert = fixtures.readKey('rsa_cert.crt'); +const key = fixtures.readKey('rsa_private.pem'); + +const server = tls.createServer({ + cert, + key +}, function(c) { + // Nop + setTimeout(function() { + c.end(); + server.close(); + }, 20); +}).listen(0, common.mustCall(function() { + const conn = tls.connect({ + cert: cert, + key: key, + rejectUnauthorized: false, + port: this.address().port + }, function() { + setTimeout(function() { + conn.destroy(); + }, 20); + }); + + // SSL_write() call's return value, when called 0 bytes, should not be + // treated as error. + conn.end(''); + + conn.on('error', common.mustNotCall()); +})); diff --git a/test/js/node/test/parallel/test-trace-events-net-abstract-socket.js b/test/js/node/test/parallel/test-trace-events-net-abstract-socket.js new file mode 100644 index 0000000000..d2e1546743 --- /dev/null +++ b/test/js/node/test/parallel/test-trace-events-net-abstract-socket.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const fs = require('fs'); +const tmpdir = require('../common/tmpdir'); + +if (!common.isLinux) common.skip(); + +const CODE = ` + const net = require('net'); + net.connect('${common.PIPE}').on('error', () => {}); + net.connect('\\0${common.PIPE}').on('error', () => {}); +`; + +tmpdir.refresh(); +const FILE_NAME = tmpdir.resolve('node_trace.1.log'); + +const proc = cp.spawn(process.execPath, + [ '--trace-events-enabled', + '--trace-event-categories', 'node.net.native', + '-e', CODE ], + { cwd: tmpdir.path }); + +proc.once('exit', common.mustCall(() => { + assert(fs.existsSync(FILE_NAME)); + fs.readFile(FILE_NAME, common.mustCall((err, data) => { + const traces = JSON.parse(data.toString()).traceEvents; + assert(traces.length > 0); + let count = 0; + traces.forEach((trace) => { + if (trace.cat === 'node,node.net,node.net.native' && + trace.name === 'connect') { + count++; + if (trace.ph === 'b') { + assert.ok(!!trace.args.path_type); + assert.ok(!!trace.args.pipe_path); + } + } + }); + assert.strictEqual(count, 4); + })); +})); diff --git a/test/js/node/test/parallel/test-tty-stdin-end.js b/test/js/node/test/parallel/test-tty-stdin-end.js new file mode 100644 index 0000000000..c78f58446d --- /dev/null +++ b/test/js/node/test/parallel/test-tty-stdin-end.js @@ -0,0 +1,7 @@ +'use strict'; +require('../common'); + +// This test ensures that Node.js doesn't crash on `process.stdin.emit("end")`. +// https://github.com/nodejs/node/issues/1068 + +process.stdin.emit('end'); diff --git a/test/js/node/test/parallel/test-tz-version.js b/test/js/node/test/parallel/test-tz-version.js new file mode 100644 index 0000000000..6e4b14e1ac --- /dev/null +++ b/test/js/node/test/parallel/test-tz-version.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasIntl) { + common.skip('missing Intl'); +} + +// Refs: https://github.com/nodejs/node/blob/1af63a90ca3a59ca05b3a12ad7dbea04008db7d9/configure.py#L1694-L1711 +if (process.config.variables.icu_path !== 'deps/icu-small') { + // If Node.js is configured to use its built-in ICU, it uses a strict subset + // of ICU formed using `tools/icu/shrink-icu-src.py`, which is present in + // `deps/icu-small`. It is not the same as configuring the build with + // `./configure --with-intl=small-icu`. The latter only uses a subset of the + // locales, i.e., it uses the English locale, `root,en`, by default and other + // locales can also be specified using the `--with-icu-locales` option. + common.skip('not using the icu data file present in deps/icu-small/source/data/in/icudt##l.dat.bz2'); +} + +const fixtures = require('../common/fixtures'); + +// This test ensures the correctness of the automated timezone upgrade PRs. + +const { strictEqual } = require('assert'); +const { readFileSync } = require('fs'); + +const expectedVersion = readFileSync(fixtures.path('tz-version.txt'), 'utf8').trim(); +strictEqual(process.versions.tz, expectedVersion); diff --git a/test/js/node/test/parallel/test-unhandled-exception-with-worker-inuse.js b/test/js/node/test/parallel/test-unhandled-exception-with-worker-inuse.js new file mode 100644 index 0000000000..a3e823ca70 --- /dev/null +++ b/test/js/node/test/parallel/test-unhandled-exception-with-worker-inuse.js @@ -0,0 +1,32 @@ +'use strict'; +const common = require('../common'); + +// https://github.com/nodejs/node/issues/45421 +// +// Check that node will NOT call v8::Isolate::SetIdle() when exiting +// due to an unhandled exception, otherwise the assertion(enabled in +// debug build only) in the SetIdle(), which checks that the vm state +// is either EXTERNAL or IDLE will fail. +// +// The root cause of this issue is that before PerIsolateMessageListener() +// is invoked by v8, v8 preserves the JS vm state, although it should +// switch to EXTERNEL. https://bugs.chromium.org/p/v8/issues/detail?id=13464 +// +// Therefore, this commit can be considered as an workaround of the v8 bug, +// but we also find it not useful to call SetIdle() when terminating. + +if (process.argv[2] === 'child') { + const { Worker } = require('worker_threads'); + new Worker('', { eval: true }); + throw new Error('xxx'); +} else { + const assert = require('assert'); + const { spawnSync } = require('child_process'); + const result = spawnSync(process.execPath, [__filename, 'child']); + + const stderr = result.stderr.toString().trim(); + // Expect error message to be preserved + assert.match(stderr, /xxx/); + // Expect no crash + assert(!common.nodeProcessAborted(result.status, result.signal), stderr); +} diff --git a/test/js/node/test/parallel/test-url-canParse-whatwg.js b/test/js/node/test/parallel/test-url-canParse-whatwg.js new file mode 100644 index 0000000000..d5ffee7053 --- /dev/null +++ b/test/js/node/test/parallel/test-url-canParse-whatwg.js @@ -0,0 +1,19 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +// One argument is required +assert.throws(() => { + URL.canParse(); +}, { + code: 'ERR_MISSING_ARGS', + name: 'TypeError', +}); + +{ + // This test is to ensure that the v8 fast api works. + for (let i = 0; i < 1e5; i++) { + assert(URL.canParse('https://www.example.com/path/?query=param#hash')); + } +} diff --git a/test/js/node/test/parallel/test-url-domain-ascii-unicode.js b/test/js/node/test/parallel/test-url-domain-ascii-unicode.js new file mode 100644 index 0000000000..737294c241 --- /dev/null +++ b/test/js/node/test/parallel/test-url-domain-ascii-unicode.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasIntl) + common.skip('missing Intl'); + +const strictEqual = require('assert').strictEqual; +const url = require('url'); + +const domainToASCII = url.domainToASCII; +const domainToUnicode = url.domainToUnicode; + +const domainWithASCII = [ + ['ıíd', 'xn--d-iga7r'], + ['يٴ', 'xn--mhb8f'], + ['www.ϧƽəʐ.com', 'www.xn--cja62apfr6c.com'], + ['новини.com', 'xn--b1amarcd.com'], + ['名がドメイン.com', 'xn--v8jxj3d1dzdz08w.com'], + ['افغانستا.icom.museum', 'xn--mgbaal8b0b9b2b.icom.museum'], + ['الجزائر.icom.fake', 'xn--lgbbat1ad8j.icom.fake'], + ['भारत.org', 'xn--h2brj9c.org'], +]; + +domainWithASCII.forEach((pair) => { + const domain = pair[0]; + const ascii = pair[1]; + const domainConvertedToASCII = domainToASCII(domain); + strictEqual(domainConvertedToASCII, ascii); + const asciiConvertedToUnicode = domainToUnicode(ascii); + strictEqual(asciiConvertedToUnicode, domain); +}); diff --git a/test/js/node/test/parallel/test-url-format-whatwg.js b/test/js/node/test/parallel/test-url-format-whatwg.js new file mode 100644 index 0000000000..bf9f8eaac6 --- /dev/null +++ b/test/js/node/test/parallel/test-url-format-whatwg.js @@ -0,0 +1,147 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasIntl) + common.skip('missing Intl'); + +const assert = require('assert'); +const url = require('url'); + +const myURL = new URL('http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c'); + +assert.strictEqual( + url.format(myURL), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, {}), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +{ + [true, 1, 'test', Infinity].forEach((value) => { + assert.throws( + () => url.format(myURL, value), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "options" argument must be of type object.' + + common.invalidArgTypeHelper(value) + } + ); + }); +} + +// Any falsy value other than undefined will be treated as false. +// Any truthy value will be treated as true. + +assert.strictEqual( + url.format(myURL, { auth: false }), + 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { auth: '' }), + 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { auth: 0 }), + 'http://xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { auth: 1 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { auth: {} }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { fragment: false }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b' +); + +assert.strictEqual( + url.format(myURL, { fragment: '' }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b' +); + +assert.strictEqual( + url.format(myURL, { fragment: 0 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b' +); + +assert.strictEqual( + url.format(myURL, { fragment: 1 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { fragment: {} }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { search: false }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c' +); + +assert.strictEqual( + url.format(myURL, { search: '' }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c' +); + +assert.strictEqual( + url.format(myURL, { search: 0 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a#c' +); + +assert.strictEqual( + url.format(myURL, { search: 1 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { search: {} }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { unicode: true }), + 'http://user:pass@理容ナカムラ.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { unicode: 1 }), + 'http://user:pass@理容ナカムラ.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { unicode: {} }), + 'http://user:pass@理容ナカムラ.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { unicode: false }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(myURL, { unicode: 0 }), + 'http://user:pass@xn--lck1c3crb1723bpq4a.com/a?a=b#c' +); + +assert.strictEqual( + url.format(new URL('http://user:pass@xn--0zwm56d.com:8080/path'), { unicode: true }), + 'http://user:pass@测试.com:8080/path' +); + +assert.strictEqual( + url.format(new URL('tel:123')), + url.format(new URL('tel:123'), { unicode: true }) +); diff --git a/test/js/node/test/parallel/test-url-format.js b/test/js/node/test/parallel/test-url-format.js new file mode 100644 index 0000000000..883d060ac2 --- /dev/null +++ b/test/js/node/test/parallel/test-url-format.js @@ -0,0 +1,277 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const url = require('url'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +// Formatting tests to verify that it'll format slightly wonky content to a +// valid URL. +const formatTests = { + 'http://example.com?': { + href: 'http://example.com/?', + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + search: '?', + query: {}, + pathname: '/' + }, + 'http://example.com?foo=bar#frag': { + href: 'http://example.com/?foo=bar#frag', + protocol: 'http:', + host: 'example.com', + hostname: 'example.com', + hash: '#frag', + search: '?foo=bar', + query: 'foo=bar', + pathname: '/' + }, + 'http://example.com?foo=@bar#frag': { + href: 'http://example.com/?foo=@bar#frag', + protocol: 'http:', + host: 'example.com', + hostname: 'example.com', + hash: '#frag', + search: '?foo=@bar', + query: 'foo=@bar', + pathname: '/' + }, + 'http://example.com?foo=/bar/#frag': { + href: 'http://example.com/?foo=/bar/#frag', + protocol: 'http:', + host: 'example.com', + hostname: 'example.com', + hash: '#frag', + search: '?foo=/bar/', + query: 'foo=/bar/', + pathname: '/' + }, + 'http://example.com?foo=?bar/#frag': { + href: 'http://example.com/?foo=?bar/#frag', + protocol: 'http:', + host: 'example.com', + hostname: 'example.com', + hash: '#frag', + search: '?foo=?bar/', + query: 'foo=?bar/', + pathname: '/' + }, + 'http://example.com#frag=?bar/#frag': { + href: 'http://example.com/#frag=?bar/#frag', + protocol: 'http:', + host: 'example.com', + hostname: 'example.com', + hash: '#frag=?bar/#frag', + pathname: '/' + }, + 'http://google.com" onload="alert(42)/': { + href: 'http://google.com/%22%20onload=%22alert(42)/', + protocol: 'http:', + host: 'google.com', + pathname: '/%22%20onload=%22alert(42)/' + }, + 'http://a.com/a/b/c?s#h': { + href: 'http://a.com/a/b/c?s#h', + protocol: 'http', + host: 'a.com', + pathname: 'a/b/c', + hash: 'h', + search: 's' + }, + 'xmpp:isaacschlueter@jabber.org': { + href: 'xmpp:isaacschlueter@jabber.org', + protocol: 'xmpp:', + host: 'jabber.org', + auth: 'isaacschlueter', + hostname: 'jabber.org' + }, + 'http://atpass:foo%40bar@127.0.0.1/': { + href: 'http://atpass:foo%40bar@127.0.0.1/', + auth: 'atpass:foo@bar', + hostname: '127.0.0.1', + protocol: 'http:', + pathname: '/' + }, + 'http://atslash%2F%40:%2F%40@foo/': { + href: 'http://atslash%2F%40:%2F%40@foo/', + auth: 'atslash/@:/@', + hostname: 'foo', + protocol: 'http:', + pathname: '/' + }, + 'svn+ssh://foo/bar': { + href: 'svn+ssh://foo/bar', + hostname: 'foo', + protocol: 'svn+ssh:', + pathname: '/bar', + slashes: true + }, + 'dash-test://foo/bar': { + href: 'dash-test://foo/bar', + hostname: 'foo', + protocol: 'dash-test:', + pathname: '/bar', + slashes: true + }, + 'dash-test:foo/bar': { + href: 'dash-test:foo/bar', + hostname: 'foo', + protocol: 'dash-test:', + pathname: '/bar' + }, + 'dot.test://foo/bar': { + href: 'dot.test://foo/bar', + hostname: 'foo', + protocol: 'dot.test:', + pathname: '/bar', + slashes: true + }, + 'dot.test:foo/bar': { + href: 'dot.test:foo/bar', + hostname: 'foo', + protocol: 'dot.test:', + pathname: '/bar' + }, + // IPv6 support + 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature': { + href: 'coap:u:p@[::1]:61616/.well-known/r?n=Temperature', + protocol: 'coap:', + auth: 'u:p', + hostname: '::1', + port: '61616', + pathname: '/.well-known/r', + search: 'n=Temperature' + }, + 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton': { + href: 'coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton', + protocol: 'coap', + host: '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616', + pathname: '/s/stopButton' + }, + 'http://[::]/': { + href: 'http://[::]/', + protocol: 'http:', + hostname: '[::]', + pathname: '/' + }, + + // Encode context-specific delimiters in path and query, but do not touch + // other non-delimiter chars like `%`. + // + + // `#`,`?` in path + '/path/to/%%23%3F+=&.txt?foo=theA1#bar': { + href: '/path/to/%%23%3F+=&.txt?foo=theA1#bar', + pathname: '/path/to/%#?+=&.txt', + query: { + foo: 'theA1' + }, + hash: '#bar' + }, + + // `#`,`?` in path + `#` in query + '/path/to/%%23%3F+=&.txt?foo=the%231#bar': { + href: '/path/to/%%23%3F+=&.txt?foo=the%231#bar', + pathname: '/path/to/%#?+=&.txt', + query: { + foo: 'the#1' + }, + hash: '#bar' + }, + + // `#` in path end + `#` in query + '/path/to/%%23?foo=the%231#bar': { + href: '/path/to/%%23?foo=the%231#bar', + pathname: '/path/to/%#', + query: { + foo: 'the#1' + }, + hash: '#bar' + }, + + // `?` and `#` in path and search + 'http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag': { + href: 'http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag', + protocol: 'http:', + hostname: 'ex.com', + hash: '#frag', + search: '?abc=the#1?&foo=bar', + pathname: '/foo?100%m#r', + }, + + // `?` and `#` in search only + 'http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag': { + href: 'http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag', + protocol: 'http:', + hostname: 'ex.com', + hash: '#frag', + search: '?abc=the#1?&foo=bar', + pathname: '/fooA100%mBr', + }, + + // Multiple `#` in search + 'http://example.com/?foo=bar%231%232%233&abc=%234%23%235#frag': { + href: 'http://example.com/?foo=bar%231%232%233&abc=%234%23%235#frag', + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + hash: '#frag', + search: '?foo=bar#1#2#3&abc=#4##5', + query: {}, + pathname: '/' + }, + + // More than 255 characters in hostname which exceeds the limit + [`http://${'a'.repeat(255)}.com/node`]: { + href: 'http:///node', + protocol: 'http:', + slashes: true, + host: '', + hostname: '', + pathname: '/node', + path: '/node' + }, + + // Greater than or equal to 63 characters after `.` in hostname + [`http://www.${'z'.repeat(63)}example.com/node`]: { + href: `http://www.${'z'.repeat(63)}example.com/node`, + protocol: 'http:', + slashes: true, + host: `www.${'z'.repeat(63)}example.com`, + hostname: `www.${'z'.repeat(63)}example.com`, + pathname: '/node', + path: '/node' + }, + + // https://github.com/nodejs/node/issues/3361 + 'file:///home/user': { + href: 'file:///home/user', + protocol: 'file', + pathname: '/home/user', + path: '/home/user' + }, + + // surrogate in auth + 'http://%F0%9F%98%80@www.example.com/': { + href: 'http://%F0%9F%98%80@www.example.com/', + protocol: 'http:', + auth: '\uD83D\uDE00', + hostname: 'www.example.com', + pathname: '/' + } +}; +for (const u in formatTests) { + const expect = formatTests[u].href; + delete formatTests[u].href; + const actual = url.format(u); + const actualObj = url.format(formatTests[u]); + assert.strictEqual(actual, expect, + `wonky format(${u}) == ${expect}\nactual:${actual}`); + assert.strictEqual(actualObj, expect, + `wonky format(${JSON.stringify(formatTests[u])}) == ${ + expect}\nactual: ${actualObj}`); +} diff --git a/test/js/node/test/parallel/test-url-parse-format.js b/test/js/node/test/parallel/test-url-parse-format.js new file mode 100644 index 0000000000..f8761514a3 --- /dev/null +++ b/test/js/node/test/parallel/test-url-parse-format.js @@ -0,0 +1,1077 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +const assert = require('assert'); +const inspect = require('util').inspect; + +const url = require('url'); + +// URLs to parse, and expected data +// { url : parsed } +const parseTests = { + '//some_path': { + href: '//some_path', + pathname: '//some_path', + path: '//some_path' + }, + + 'http:\\\\evil-phisher\\foo.html#h\\a\\s\\h': { + protocol: 'http:', + slashes: true, + host: 'evil-phisher', + hostname: 'evil-phisher', + pathname: '/foo.html', + path: '/foo.html', + hash: '#h%5Ca%5Cs%5Ch', + href: 'http://evil-phisher/foo.html#h%5Ca%5Cs%5Ch' + }, + + 'http:\\\\evil-phisher\\foo.html?json="\\"foo\\""#h\\a\\s\\h': { + protocol: 'http:', + slashes: true, + host: 'evil-phisher', + hostname: 'evil-phisher', + pathname: '/foo.html', + search: '?json=%22%5C%22foo%5C%22%22', + query: 'json=%22%5C%22foo%5C%22%22', + path: '/foo.html?json=%22%5C%22foo%5C%22%22', + hash: '#h%5Ca%5Cs%5Ch', + href: 'http://evil-phisher/foo.html?json=%22%5C%22foo%5C%22%22#h%5Ca%5Cs%5Ch' + }, + + 'http:\\\\evil-phisher\\foo.html#h\\a\\s\\h?blarg': { + protocol: 'http:', + slashes: true, + host: 'evil-phisher', + hostname: 'evil-phisher', + pathname: '/foo.html', + path: '/foo.html', + hash: '#h%5Ca%5Cs%5Ch?blarg', + href: 'http://evil-phisher/foo.html#h%5Ca%5Cs%5Ch?blarg' + }, + + + 'http:\\\\evil-phisher\\foo.html': { + protocol: 'http:', + slashes: true, + host: 'evil-phisher', + hostname: 'evil-phisher', + pathname: '/foo.html', + path: '/foo.html', + href: 'http://evil-phisher/foo.html' + }, + + 'HTTP://www.example.com/': { + href: 'http://www.example.com/', + protocol: 'http:', + slashes: true, + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'HTTP://www.example.com': { + href: 'http://www.example.com/', + protocol: 'http:', + slashes: true, + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://www.ExAmPlE.com/': { + href: 'http://www.example.com/', + protocol: 'http:', + slashes: true, + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://user:pw@www.ExAmPlE.com/': { + href: 'http://user:pw@www.example.com/', + protocol: 'http:', + slashes: true, + auth: 'user:pw', + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://USER:PW@www.ExAmPlE.com/': { + href: 'http://USER:PW@www.example.com/', + protocol: 'http:', + slashes: true, + auth: 'USER:PW', + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://user@www.example.com/': { + href: 'http://user@www.example.com/', + protocol: 'http:', + slashes: true, + auth: 'user', + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://user%3Apw@www.example.com/': { + href: 'http://user:pw@www.example.com/', + protocol: 'http:', + slashes: true, + auth: 'user:pw', + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + 'http://x.com/path?that\'s#all, folks': { + href: 'http://x.com/path?that%27s#all,%20folks', + protocol: 'http:', + slashes: true, + host: 'x.com', + hostname: 'x.com', + search: '?that%27s', + query: 'that%27s', + pathname: '/path', + hash: '#all,%20folks', + path: '/path?that%27s' + }, + + 'HTTP://X.COM/Y': { + href: 'http://x.com/Y', + protocol: 'http:', + slashes: true, + host: 'x.com', + hostname: 'x.com', + pathname: '/Y', + path: '/Y' + }, + + // Whitespace in the front + ' http://www.example.com/': { + href: 'http://www.example.com/', + protocol: 'http:', + slashes: true, + host: 'www.example.com', + hostname: 'www.example.com', + pathname: '/', + path: '/' + }, + + // + not an invalid host character + // per https://url.spec.whatwg.org/#host-parsing + 'http://x.y.com+a/b/c': { + href: 'http://x.y.com+a/b/c', + protocol: 'http:', + slashes: true, + host: 'x.y.com+a', + hostname: 'x.y.com+a', + pathname: '/b/c', + path: '/b/c' + }, + + // An unexpected invalid char in the hostname. + 'HtTp://x.y.cOm;a/b/c?d=e#f gi': { + href: 'http://x.y.com/;a/b/c?d=e#f%20g%3Ch%3Ei', + protocol: 'http:', + slashes: true, + host: 'x.y.com', + hostname: 'x.y.com', + pathname: ';a/b/c', + search: '?d=e', + query: 'd=e', + hash: '#f%20g%3Ch%3Ei', + path: ';a/b/c?d=e' + }, + + // Make sure that we don't accidentally lcast the path parts. + 'HtTp://x.y.cOm;A/b/c?d=e#f gi': { + href: 'http://x.y.com/;A/b/c?d=e#f%20g%3Ch%3Ei', + protocol: 'http:', + slashes: true, + host: 'x.y.com', + hostname: 'x.y.com', + pathname: ';A/b/c', + search: '?d=e', + query: 'd=e', + hash: '#f%20g%3Ch%3Ei', + path: ';A/b/c?d=e' + }, + + 'http://x...y...#p': { + href: 'http://x...y.../#p', + protocol: 'http:', + slashes: true, + host: 'x...y...', + hostname: 'x...y...', + hash: '#p', + pathname: '/', + path: '/' + }, + + 'http://x/p/"quoted"': { + href: 'http://x/p/%22quoted%22', + protocol: 'http:', + slashes: true, + host: 'x', + hostname: 'x', + pathname: '/p/%22quoted%22', + path: '/p/%22quoted%22' + }, + + ' Is a URL!': { + href: '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!', + pathname: '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!', + path: '%3Chttp://goo.corn/bread%3E%20Is%20a%20URL!' + }, + + 'http://www.narwhaljs.org/blog/categories?id=news': { + href: 'http://www.narwhaljs.org/blog/categories?id=news', + protocol: 'http:', + slashes: true, + host: 'www.narwhaljs.org', + hostname: 'www.narwhaljs.org', + search: '?id=news', + query: 'id=news', + pathname: '/blog/categories', + path: '/blog/categories?id=news' + }, + + 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=': { + href: 'http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=', + protocol: 'http:', + slashes: true, + host: 'mt0.google.com', + hostname: 'mt0.google.com', + pathname: '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=', + path: '/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=' + }, + + 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=': { + href: 'http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api' + + '&x=2&y=2&z=3&s=', + protocol: 'http:', + slashes: true, + host: 'mt0.google.com', + hostname: 'mt0.google.com', + search: '???&hl=en&src=api&x=2&y=2&z=3&s=', + query: '??&hl=en&src=api&x=2&y=2&z=3&s=', + pathname: '/vt/lyrs=m@114', + path: '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' + }, + + 'http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=': { + href: 'http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=', + protocol: 'http:', + slashes: true, + host: 'mt0.google.com', + auth: 'user:pass', + hostname: 'mt0.google.com', + search: '???&hl=en&src=api&x=2&y=2&z=3&s=', + query: '??&hl=en&src=api&x=2&y=2&z=3&s=', + pathname: '/vt/lyrs=m@114', + path: '/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=' + }, + + 'file:///etc/passwd': { + href: 'file:///etc/passwd', + slashes: true, + protocol: 'file:', + pathname: '/etc/passwd', + hostname: '', + host: '', + path: '/etc/passwd' + }, + + 'file://localhost/etc/passwd': { + href: 'file://localhost/etc/passwd', + protocol: 'file:', + slashes: true, + pathname: '/etc/passwd', + hostname: 'localhost', + host: 'localhost', + path: '/etc/passwd' + }, + + 'file://foo/etc/passwd': { + href: 'file://foo/etc/passwd', + protocol: 'file:', + slashes: true, + pathname: '/etc/passwd', + hostname: 'foo', + host: 'foo', + path: '/etc/passwd' + }, + + 'file:///etc/node/': { + href: 'file:///etc/node/', + slashes: true, + protocol: 'file:', + pathname: '/etc/node/', + hostname: '', + host: '', + path: '/etc/node/' + }, + + 'file://localhost/etc/node/': { + href: 'file://localhost/etc/node/', + protocol: 'file:', + slashes: true, + pathname: '/etc/node/', + hostname: 'localhost', + host: 'localhost', + path: '/etc/node/' + }, + + 'file://foo/etc/node/': { + href: 'file://foo/etc/node/', + protocol: 'file:', + slashes: true, + pathname: '/etc/node/', + hostname: 'foo', + host: 'foo', + path: '/etc/node/' + }, + + 'http:/baz/../foo/bar': { + href: 'http:/baz/../foo/bar', + protocol: 'http:', + pathname: '/baz/../foo/bar', + path: '/baz/../foo/bar' + }, + + 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag': { + href: 'http://user:pass@example.com:8000/foo/bar?baz=quux#frag', + protocol: 'http:', + slashes: true, + host: 'example.com:8000', + auth: 'user:pass', + port: '8000', + hostname: 'example.com', + hash: '#frag', + search: '?baz=quux', + query: 'baz=quux', + pathname: '/foo/bar', + path: '/foo/bar?baz=quux' + }, + + '//user:pass@example.com:8000/foo/bar?baz=quux#frag': { + href: '//user:pass@example.com:8000/foo/bar?baz=quux#frag', + slashes: true, + host: 'example.com:8000', + auth: 'user:pass', + port: '8000', + hostname: 'example.com', + hash: '#frag', + search: '?baz=quux', + query: 'baz=quux', + pathname: '/foo/bar', + path: '/foo/bar?baz=quux' + }, + + '/foo/bar?baz=quux#frag': { + href: '/foo/bar?baz=quux#frag', + hash: '#frag', + search: '?baz=quux', + query: 'baz=quux', + pathname: '/foo/bar', + path: '/foo/bar?baz=quux' + }, + + 'http:/foo/bar?baz=quux#frag': { + href: 'http:/foo/bar?baz=quux#frag', + protocol: 'http:', + hash: '#frag', + search: '?baz=quux', + query: 'baz=quux', + pathname: '/foo/bar', + path: '/foo/bar?baz=quux' + }, + + 'mailto:foo@bar.com?subject=hello': { + href: 'mailto:foo@bar.com?subject=hello', + protocol: 'mailto:', + host: 'bar.com', + auth: 'foo', + hostname: 'bar.com', + search: '?subject=hello', + query: 'subject=hello', + path: '?subject=hello' + }, + + 'javascript:alert(\'hello\');': { + href: 'javascript:alert(\'hello\');', + protocol: 'javascript:', + pathname: 'alert(\'hello\');', + path: 'alert(\'hello\');' + }, + + 'xmpp:isaacschlueter@jabber.org': { + href: 'xmpp:isaacschlueter@jabber.org', + protocol: 'xmpp:', + host: 'jabber.org', + auth: 'isaacschlueter', + hostname: 'jabber.org' + }, + + 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar': { + href: 'http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar', + protocol: 'http:', + slashes: true, + host: '127.0.0.1:8080', + auth: 'atpass:foo@bar', + hostname: '127.0.0.1', + port: '8080', + pathname: '/path', + search: '?search=foo', + query: 'search=foo', + hash: '#bar', + path: '/path?search=foo' + }, + + 'svn+ssh://foo/bar': { + href: 'svn+ssh://foo/bar', + host: 'foo', + hostname: 'foo', + protocol: 'svn+ssh:', + pathname: '/bar', + path: '/bar', + slashes: true + }, + + 'dash-test://foo/bar': { + href: 'dash-test://foo/bar', + host: 'foo', + hostname: 'foo', + protocol: 'dash-test:', + pathname: '/bar', + path: '/bar', + slashes: true + }, + + 'dash-test:foo/bar': { + href: 'dash-test:foo/bar', + host: 'foo', + hostname: 'foo', + protocol: 'dash-test:', + pathname: '/bar', + path: '/bar' + }, + + 'dot.test://foo/bar': { + href: 'dot.test://foo/bar', + host: 'foo', + hostname: 'foo', + protocol: 'dot.test:', + pathname: '/bar', + path: '/bar', + slashes: true + }, + + 'dot.test:foo/bar': { + href: 'dot.test:foo/bar', + host: 'foo', + hostname: 'foo', + protocol: 'dot.test:', + pathname: '/bar', + path: '/bar' + }, + + // IDNA tests + 'http://www.日本語.com/': { + href: 'http://www.xn--wgv71a119e.com/', + protocol: 'http:', + slashes: true, + host: 'www.xn--wgv71a119e.com', + hostname: 'www.xn--wgv71a119e.com', + pathname: '/', + path: '/' + }, + + 'http://example.Bücher.com/': { + href: 'http://example.xn--bcher-kva.com/', + protocol: 'http:', + slashes: true, + host: 'example.xn--bcher-kva.com', + hostname: 'example.xn--bcher-kva.com', + pathname: '/', + path: '/' + }, + + 'http://www.Äffchen.com/': { + href: 'http://www.xn--ffchen-9ta.com/', + protocol: 'http:', + slashes: true, + host: 'www.xn--ffchen-9ta.com', + hostname: 'www.xn--ffchen-9ta.com', + pathname: '/', + path: '/' + }, + + 'http://www.Äffchen.cOm;A/b/c?d=e#f gi': { + href: 'http://www.xn--ffchen-9ta.com/;A/b/c?d=e#f%20g%3Ch%3Ei', + protocol: 'http:', + slashes: true, + host: 'www.xn--ffchen-9ta.com', + hostname: 'www.xn--ffchen-9ta.com', + pathname: ';A/b/c', + search: '?d=e', + query: 'd=e', + hash: '#f%20g%3Ch%3Ei', + path: ';A/b/c?d=e' + }, + + 'http://SÉLIER.COM/': { + href: 'http://xn--slier-bsa.com/', + protocol: 'http:', + slashes: true, + host: 'xn--slier-bsa.com', + hostname: 'xn--slier-bsa.com', + pathname: '/', + path: '/' + }, + + 'http://ليهمابتكلموشعربي؟.ي؟/': { + href: 'http://xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f/', + protocol: 'http:', + slashes: true, + host: 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f', + hostname: 'xn--egbpdaj6bu4bxfgehfvwxn.xn--egb9f', + pathname: '/', + path: '/' + }, + + 'http://➡.ws/➡': { + href: 'http://xn--hgi.ws/➡', + protocol: 'http:', + slashes: true, + host: 'xn--hgi.ws', + hostname: 'xn--hgi.ws', + pathname: '/➡', + path: '/➡' + }, + + 'http://bucket_name.s3.amazonaws.com/image.jpg': { + protocol: 'http:', + slashes: true, + host: 'bucket_name.s3.amazonaws.com', + hostname: 'bucket_name.s3.amazonaws.com', + pathname: '/image.jpg', + href: 'http://bucket_name.s3.amazonaws.com/image.jpg', + path: '/image.jpg' + }, + + 'git+http://github.com/joyent/node.git': { + protocol: 'git+http:', + slashes: true, + host: 'github.com', + hostname: 'github.com', + pathname: '/joyent/node.git', + path: '/joyent/node.git', + href: 'git+http://github.com/joyent/node.git' + }, + + // If local1@domain1 is uses as a relative URL it may + // be parse into auth@hostname, but here there is no + // way to make it work in url.parse, I add the test to be explicit + 'local1@domain1': { + pathname: 'local1@domain1', + path: 'local1@domain1', + href: 'local1@domain1' + }, + + // While this may seem counter-intuitive, a browser will parse + // as a path. + 'www.example.com': { + href: 'www.example.com', + pathname: 'www.example.com', + path: 'www.example.com' + }, + + // ipv6 support + '[fe80::1]': { + href: '[fe80::1]', + pathname: '[fe80::1]', + path: '[fe80::1]' + }, + + 'coap://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]': { + protocol: 'coap:', + slashes: true, + host: '[fedc:ba98:7654:3210:fedc:ba98:7654:3210]', + hostname: 'fedc:ba98:7654:3210:fedc:ba98:7654:3210', + href: 'coap://[fedc:ba98:7654:3210:fedc:ba98:7654:3210]/', + pathname: '/', + path: '/' + }, + + 'coap://[1080:0:0:0:8:800:200C:417A]:61616/': { + protocol: 'coap:', + slashes: true, + host: '[1080:0:0:0:8:800:200c:417a]:61616', + port: '61616', + hostname: '1080:0:0:0:8:800:200c:417a', + href: 'coap://[1080:0:0:0:8:800:200c:417a]:61616/', + pathname: '/', + path: '/' + }, + + 'http://user:password@[3ffe:2a00:100:7031::1]:8080': { + protocol: 'http:', + slashes: true, + auth: 'user:password', + host: '[3ffe:2a00:100:7031::1]:8080', + port: '8080', + hostname: '3ffe:2a00:100:7031::1', + href: 'http://user:password@[3ffe:2a00:100:7031::1]:8080/', + pathname: '/', + path: '/' + }, + + 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature': { + protocol: 'coap:', + slashes: true, + auth: 'u:p', + host: '[::192.9.5.5]:61616', + port: '61616', + hostname: '::192.9.5.5', + href: 'coap://u:p@[::192.9.5.5]:61616/.well-known/r?n=Temperature', + search: '?n=Temperature', + query: 'n=Temperature', + pathname: '/.well-known/r', + path: '/.well-known/r?n=Temperature' + }, + + // empty port + 'http://example.com:': { + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + href: 'http://example.com/', + pathname: '/', + path: '/' + }, + + 'http://example.com:/a/b.html': { + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + href: 'http://example.com/a/b.html', + pathname: '/a/b.html', + path: '/a/b.html' + }, + + 'http://example.com:?a=b': { + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + href: 'http://example.com/?a=b', + search: '?a=b', + query: 'a=b', + pathname: '/', + path: '/?a=b' + }, + + 'http://example.com:#abc': { + protocol: 'http:', + slashes: true, + host: 'example.com', + hostname: 'example.com', + href: 'http://example.com/#abc', + hash: '#abc', + pathname: '/', + path: '/' + }, + + 'http://[fe80::1]:/a/b?a=b#abc': { + protocol: 'http:', + slashes: true, + host: '[fe80::1]', + hostname: 'fe80::1', + href: 'http://[fe80::1]/a/b?a=b#abc', + search: '?a=b', + query: 'a=b', + hash: '#abc', + pathname: '/a/b', + path: '/a/b?a=b' + }, + + 'http://-lovemonsterz.tumblr.com/rss': { + protocol: 'http:', + slashes: true, + host: '-lovemonsterz.tumblr.com', + hostname: '-lovemonsterz.tumblr.com', + href: 'http://-lovemonsterz.tumblr.com/rss', + pathname: '/rss', + path: '/rss', + }, + + 'http://-lovemonsterz.tumblr.com:80/rss': { + protocol: 'http:', + slashes: true, + port: '80', + host: '-lovemonsterz.tumblr.com:80', + hostname: '-lovemonsterz.tumblr.com', + href: 'http://-lovemonsterz.tumblr.com:80/rss', + pathname: '/rss', + path: '/rss', + }, + + 'http://user:pass@-lovemonsterz.tumblr.com/rss': { + protocol: 'http:', + slashes: true, + auth: 'user:pass', + host: '-lovemonsterz.tumblr.com', + hostname: '-lovemonsterz.tumblr.com', + href: 'http://user:pass@-lovemonsterz.tumblr.com/rss', + pathname: '/rss', + path: '/rss', + }, + + 'http://user:pass@-lovemonsterz.tumblr.com:80/rss': { + protocol: 'http:', + slashes: true, + auth: 'user:pass', + port: '80', + host: '-lovemonsterz.tumblr.com:80', + hostname: '-lovemonsterz.tumblr.com', + href: 'http://user:pass@-lovemonsterz.tumblr.com:80/rss', + pathname: '/rss', + path: '/rss', + }, + + 'http://_jabber._tcp.google.com/test': { + protocol: 'http:', + slashes: true, + host: '_jabber._tcp.google.com', + hostname: '_jabber._tcp.google.com', + href: 'http://_jabber._tcp.google.com/test', + pathname: '/test', + path: '/test', + }, + + 'http://user:pass@_jabber._tcp.google.com/test': { + protocol: 'http:', + slashes: true, + auth: 'user:pass', + host: '_jabber._tcp.google.com', + hostname: '_jabber._tcp.google.com', + href: 'http://user:pass@_jabber._tcp.google.com/test', + pathname: '/test', + path: '/test', + }, + + 'http://_jabber._tcp.google.com:80/test': { + protocol: 'http:', + slashes: true, + port: '80', + host: '_jabber._tcp.google.com:80', + hostname: '_jabber._tcp.google.com', + href: 'http://_jabber._tcp.google.com:80/test', + pathname: '/test', + path: '/test', + }, + + 'http://user:pass@_jabber._tcp.google.com:80/test': { + protocol: 'http:', + slashes: true, + auth: 'user:pass', + port: '80', + host: '_jabber._tcp.google.com:80', + hostname: '_jabber._tcp.google.com', + href: 'http://user:pass@_jabber._tcp.google.com:80/test', + pathname: '/test', + path: '/test', + }, + + 'http://x:1/\' <>"`/{}|\\^~`/': { + protocol: 'http:', + slashes: true, + host: 'x:1', + port: '1', + hostname: 'x', + pathname: '/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/', + path: '/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/', + href: 'http://x:1/%27%20%3C%3E%22%60/%7B%7D%7C/%5E~%60/' + }, + + 'http://a@b@c/': { + protocol: 'http:', + slashes: true, + auth: 'a@b', + host: 'c', + hostname: 'c', + href: 'http://a%40b@c/', + path: '/', + pathname: '/' + }, + + 'http://a@b?@c': { + protocol: 'http:', + slashes: true, + auth: 'a', + host: 'b', + hostname: 'b', + href: 'http://a@b/?@c', + path: '/?@c', + pathname: '/', + search: '?@c', + query: '@c' + }, + + 'http://a.b/\tbc\ndr\ref g"hq\'j?mn\\op^q=r`99{st|uv}wz': { + protocol: 'http:', + slashes: true, + host: 'a.b', + port: null, + hostname: 'a.b', + hash: null, + pathname: '/%09bc%0Adr%0Def%20g%22hq%27j%3Ckl%3E', + path: '/%09bc%0Adr%0Def%20g%22hq%27j%3Ckl%3E?mn%5Cop%5Eq=r%6099%7Bst%7Cuv%7Dwz', + search: '?mn%5Cop%5Eq=r%6099%7Bst%7Cuv%7Dwz', + query: 'mn%5Cop%5Eq=r%6099%7Bst%7Cuv%7Dwz', + href: 'http://a.b/%09bc%0Adr%0Def%20g%22hq%27j%3Ckl%3E?mn%5Cop%5Eq=r%6099%7Bst%7Cuv%7Dwz' + }, + + 'http://a\r" \t\n<\'b:b@c\r\nd/e?f': { + protocol: 'http:', + slashes: true, + auth: 'a" <\'b:b', + host: 'cd', + port: null, + hostname: 'cd', + hash: null, + search: '?f', + query: 'f', + pathname: '/e', + path: '/e?f', + href: 'http://a%22%20%3C\'b:b@cd/e?f' + }, + + // Git urls used by npm + 'git+ssh://git@github.com:npm/npm': { + protocol: 'git+ssh:', + slashes: true, + auth: 'git', + host: 'github.com', + port: null, + hostname: 'github.com', + hash: null, + search: null, + query: null, + pathname: '/:npm/npm', + path: '/:npm/npm', + href: 'git+ssh://git@github.com/:npm/npm' + }, + + 'https://*': { + protocol: 'https:', + slashes: true, + auth: null, + host: '*', + port: null, + hostname: '*', + hash: null, + search: null, + query: null, + pathname: '/', + path: '/', + href: 'https://*/' + }, + + // The following two URLs are the same, but they differ for a capital A. + // Verify that the protocol is checked in a case-insensitive manner. + 'javascript:alert(1);a=\x27@white-listed.com\x27': { + protocol: 'javascript:', + slashes: null, + auth: null, + host: null, + port: null, + hostname: null, + hash: null, + search: null, + query: null, + pathname: "alert(1);a='@white-listed.com'", + path: "alert(1);a='@white-listed.com'", + href: "javascript:alert(1);a='@white-listed.com'" + }, + + 'javAscript:alert(1);a=\x27@white-listed.com\x27': { + protocol: 'javascript:', + slashes: null, + auth: null, + host: null, + port: null, + hostname: null, + hash: null, + search: null, + query: null, + pathname: "alert(1);a='@white-listed.com'", + path: "alert(1);a='@white-listed.com'", + href: "javascript:alert(1);a='@white-listed.com'" + }, + + 'ws://www.example.com': { + protocol: 'ws:', + slashes: true, + hostname: 'www.example.com', + host: 'www.example.com', + pathname: '/', + path: '/', + href: 'ws://www.example.com/' + }, + + 'wss://www.example.com': { + protocol: 'wss:', + slashes: true, + hostname: 'www.example.com', + host: 'www.example.com', + pathname: '/', + path: '/', + href: 'wss://www.example.com/' + }, + + '//fhqwhgads@example.com/everybody-to-the-limit': { + protocol: null, + slashes: true, + auth: 'fhqwhgads', + host: 'example.com', + port: null, + hostname: 'example.com', + hash: null, + search: null, + query: null, + pathname: '/everybody-to-the-limit', + path: '/everybody-to-the-limit', + href: '//fhqwhgads@example.com/everybody-to-the-limit' + }, + + '//fhqwhgads@example.com/everybody#to-the-limit': { + protocol: null, + slashes: true, + auth: 'fhqwhgads', + host: 'example.com', + port: null, + hostname: 'example.com', + hash: '#to-the-limit', + search: null, + query: null, + pathname: '/everybody', + path: '/everybody', + href: '//fhqwhgads@example.com/everybody#to-the-limit' + }, + + '\bhttp://example.com/\b': { + protocol: 'http:', + slashes: true, + auth: null, + host: 'example.com', + port: null, + hostname: 'example.com', + hash: null, + search: null, + query: null, + pathname: '/', + path: '/', + href: 'http://example.com/' + }, + + 'https://evil.com$.example.com': { + protocol: 'https:', + slashes: true, + auth: null, + host: 'evil.com$.example.com', + port: null, + hostname: 'evil.com$.example.com', + hash: null, + search: null, + query: null, + pathname: '/', + path: '/', + href: 'https://evil.com$.example.com/' + }, + + // Validate the output of hostname with commas. + 'x://0.0,1.1/': { + protocol: 'x:', + slashes: true, + auth: null, + host: '0.0,1.1', + port: null, + hostname: '0.0,1.1', + hash: null, + search: null, + query: null, + pathname: '/', + path: '/', + href: 'x://0.0,1.1/' + } +}; + +for (const u in parseTests) { + let actual = url.parse(u); + const spaced = url.parse(` \t ${u}\n\t`); + let expected = Object.assign(new url.Url(), parseTests[u]); + + Object.keys(actual).forEach(function(i) { + if (expected[i] === undefined && actual[i] === null) { + expected[i] = null; + } + }); + + assert.deepStrictEqual( + actual, + expected, + `parsing ${u} and expected ${inspect(expected)} but got ${inspect(actual)}` + ); + assert.deepStrictEqual( + spaced, + expected, + `expected ${inspect(expected)}, got ${inspect(spaced)}` + ); + + expected = parseTests[u].href; + actual = url.format(parseTests[u]); + + assert.strictEqual(actual, expected, + `format(${u}) == ${u}\nactual:${actual}`); +} + +{ + const parsed = url.parse('http://nodejs.org/') + .resolveObject('jAvascript:alert(1);a=\x27@white-listed.com\x27'); + + const expected = Object.assign(new url.Url(), { + protocol: 'javascript:', + slashes: null, + auth: null, + host: null, + port: null, + hostname: null, + hash: null, + search: null, + query: null, + pathname: "alert(1);a='@white-listed.com'", + path: "alert(1);a='@white-listed.com'", + href: "javascript:alert(1);a='@white-listed.com'" + }); + + assert.deepStrictEqual(parsed, expected); +} diff --git a/test/js/node/test/parallel/test-url-revokeobjecturl.js b/test/js/node/test/parallel/test-url-revokeobjecturl.js new file mode 100644 index 0000000000..dae980c4d0 --- /dev/null +++ b/test/js/node/test/parallel/test-url-revokeobjecturl.js @@ -0,0 +1,14 @@ +'use strict'; + +require('../common'); + +// Test ensures that the function receives the url argument. + +const assert = require('node:assert'); + +assert.throws(() => { + URL.revokeObjectURL(); +}, { + code: 'ERR_MISSING_ARGS', + name: 'TypeError', +}); diff --git a/test/js/node/test/parallel/test-url-urltooptions.js b/test/js/node/test/parallel/test-url-urltooptions.js new file mode 100644 index 0000000000..cc4838eeec --- /dev/null +++ b/test/js/node/test/parallel/test-url-urltooptions.js @@ -0,0 +1,37 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { urlToHttpOptions } = require('url'); + +// Test urlToHttpOptions +const urlObj = new URL('http://user:pass@foo.bar.com:21/aaa/zzz?l=24#test'); +const opts = urlToHttpOptions(urlObj); +assert.strictEqual(opts instanceof URL, false); +assert.strictEqual(opts.protocol, 'http:'); +assert.strictEqual(opts.auth, 'user:pass'); +assert.strictEqual(opts.hostname, 'foo.bar.com'); +assert.strictEqual(opts.port, 21); +assert.strictEqual(opts.path, '/aaa/zzz?l=24'); +assert.strictEqual(opts.pathname, '/aaa/zzz'); +assert.strictEqual(opts.search, '?l=24'); +assert.strictEqual(opts.hash, '#test'); + +const { hostname } = urlToHttpOptions(new URL('http://[::1]:21')); +assert.strictEqual(hostname, '::1'); + +// If a WHATWG URL object is copied, it is possible that the resulting copy +// contains the Symbols that Node uses for brand checking, but not the data +// properties, which are getters. Verify that urlToHttpOptions() can handle +// such a case. +const copiedUrlObj = { ...urlObj }; +const copiedOpts = urlToHttpOptions(copiedUrlObj); +assert.strictEqual(copiedOpts instanceof URL, false); +assert.strictEqual(copiedOpts.protocol, undefined); +assert.strictEqual(copiedOpts.auth, undefined); +assert.strictEqual(copiedOpts.hostname, undefined); +assert.strictEqual(copiedOpts.port, NaN); +assert.strictEqual(copiedOpts.path, ''); +assert.strictEqual(copiedOpts.pathname, undefined); +assert.strictEqual(copiedOpts.search, undefined); +assert.strictEqual(copiedOpts.hash, undefined); +assert.strictEqual(copiedOpts.href, undefined); diff --git a/test/js/node/test/parallel/test-utf8-scripts.js b/test/js/node/test/parallel/test-utf8-scripts.js new file mode 100644 index 0000000000..4bf5b0cd5b --- /dev/null +++ b/test/js/node/test/parallel/test-utf8-scripts.js @@ -0,0 +1,30 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +// üäö + +console.log('Σὲ γνωρίζω ἀπὸ τὴν κόψη'); + +assert.match('Hellö Wörld', /Hellö Wörld/); diff --git a/test/js/node/test/parallel/test-util-inspect-getters-accessing-this.js b/test/js/node/test/parallel/test-util-inspect-getters-accessing-this.js new file mode 100644 index 0000000000..998cd82db8 --- /dev/null +++ b/test/js/node/test/parallel/test-util-inspect-getters-accessing-this.js @@ -0,0 +1,67 @@ +'use strict'; + +require('../common'); + +// This test ensures that util.inspect logs getters +// which access this. + +const assert = require('assert'); + +const { inspect } = require('util'); + +{ + class X { + constructor() { + this._y = 123; + } + + get y() { + return this._y; + } + } + + const result = inspect(new X(), { + getters: true, + showHidden: true + }); + + assert.strictEqual( + result, + 'X { _y: 123, [y]: [Getter: 123] }' + ); +} + +// Regression test for https://github.com/nodejs/node/issues/37054 +{ + class A { + constructor(B) { + this.B = B; + } + get b() { + return this.B; + } + } + + class B { + constructor() { + this.A = new A(this); + } + get a() { + return this.A; + } + } + + const result = inspect(new B(), { + depth: 1, + getters: true, + showHidden: true + }); + + assert.strictEqual( + result, + ' B {\n' + + ' A: A { B: [Circular *1], [b]: [Getter] [Circular *1] },\n' + + ' [a]: [Getter] A { B: [Circular *1], [b]: [Getter] [Circular *1] }\n' + + '}', + ); +} diff --git a/test/js/node/test/parallel/test-util-primordial-monkeypatching.js b/test/js/node/test/parallel/test-util-primordial-monkeypatching.js new file mode 100644 index 0000000000..bf282a1212 --- /dev/null +++ b/test/js/node/test/parallel/test-util-primordial-monkeypatching.js @@ -0,0 +1,11 @@ +'use strict'; + +// Monkeypatch Object.keys() so that it throws an unexpected error. This tests +// that `util.inspect()` is unaffected by monkey-patching `Object`. + +require('../common'); +const assert = require('assert'); +const util = require('util'); + +Object.keys = () => { throw new Error('fhqwhgads'); }; +assert.strictEqual(util.inspect({}), '{}'); diff --git a/test/js/node/test/parallel/test-v8-deserialize-buffer.js b/test/js/node/test/parallel/test-v8-deserialize-buffer.js new file mode 100644 index 0000000000..f05631a72a --- /dev/null +++ b/test/js/node/test/parallel/test-v8-deserialize-buffer.js @@ -0,0 +1,7 @@ +'use strict'; + +const common = require('../common'); +const v8 = require('v8'); + +process.on('warning', common.mustNotCall()); +v8.deserialize(v8.serialize(Buffer.alloc(0))); diff --git a/test/js/node/test/parallel/test-v8-global-setter.js b/test/js/node/test/parallel/test-v8-global-setter.js new file mode 100644 index 0000000000..1cb0898e61 --- /dev/null +++ b/test/js/node/test/parallel/test-v8-global-setter.js @@ -0,0 +1,29 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); + +// This test ensures v8 correctly sets a property on the global object if it +// has a setter interceptor in strict mode. +// https://github.com/nodejs/node-v0.x-archive/issues/6235 + +require('vm').runInNewContext('"use strict"; var v = 1; v = 2'); diff --git a/test/js/node/test/parallel/test-vm-access-process-env.js b/test/js/node/test/parallel/test-vm-access-process-env.js new file mode 100644 index 0000000000..c6b18ec902 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-access-process-env.js @@ -0,0 +1,33 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Tests that node does neither crash nor throw an error when accessing +// process.env when inside a VM context. +// See https://github.com/nodejs/node-v0.x-archive/issues/7511. + +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const context = vm.createContext({ process }); +const result = vm.runInContext('process.env["PATH"]', context); +assert.notStrictEqual(undefined, result); diff --git a/test/js/node/test/parallel/test-vm-attributes-property-not-on-sandbox.js b/test/js/node/test/parallel/test-vm-attributes-property-not-on-sandbox.js new file mode 100644 index 0000000000..313dd71e47 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-attributes-property-not-on-sandbox.js @@ -0,0 +1,18 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +// Assert that accessor descriptors are not flattened on the sandbox. +// Issue: https://github.com/nodejs/node/issues/2734 +const sandbox = {}; +vm.createContext(sandbox); +const code = `Object.defineProperty( + this, + 'foo', + { get: function() {return 17} } + ); + var desc = Object.getOwnPropertyDescriptor(this, 'foo');`; + +vm.runInContext(code, sandbox); +assert.strictEqual(typeof sandbox.desc.get, 'function'); diff --git a/test/js/node/test/parallel/test-vm-context-async-script.js b/test/js/node/test/parallel/test-vm-context-async-script.js new file mode 100644 index 0000000000..879315e37b --- /dev/null +++ b/test/js/node/test/parallel/test-vm-context-async-script.js @@ -0,0 +1,35 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const sandbox = { setTimeout }; + +const ctx = vm.createContext(sandbox); + +vm.runInContext('setTimeout(function() { x = 3; }, 0);', ctx); +setTimeout(common.mustCall(() => { + assert.strictEqual(sandbox.x, 3); + assert.strictEqual(ctx.x, 3); +}), 1); diff --git a/test/js/node/test/parallel/test-vm-context-property-forwarding.js b/test/js/node/test/parallel/test-vm-context-property-forwarding.js new file mode 100644 index 0000000000..53d38c1467 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-context-property-forwarding.js @@ -0,0 +1,65 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const sandbox = { x: 3 }; + +const ctx = vm.createContext(sandbox); + +assert.strictEqual(vm.runInContext('x;', ctx), 3); +vm.runInContext('y = 4;', ctx); +assert.strictEqual(sandbox.y, 4); +assert.strictEqual(ctx.y, 4); + +// Test `IndexedPropertyGetterCallback` and `IndexedPropertyDeleterCallback` +const x = { get 1() { return 5; } }; +const pd_expected = Object.getOwnPropertyDescriptor(x, 1); +const ctx2 = vm.createContext(x); +const pd_actual = Object.getOwnPropertyDescriptor(ctx2, 1); + +assert.deepStrictEqual(pd_actual, pd_expected); +assert.strictEqual(ctx2[1], 5); +delete ctx2[1]; +assert.strictEqual(ctx2[1], undefined); + +// https://github.com/nodejs/node/issues/33806 +{ + const ctx = vm.createContext(); + + Object.defineProperty(ctx, 'prop', { + get() { + return undefined; + }, + set(val) { + throw new Error('test error'); + }, + }); + + assert.throws(() => { + vm.runInContext('prop = 42', ctx); + }, { + message: 'test error', + }); +} diff --git a/test/js/node/test/parallel/test-vm-create-context-accessors.js b/test/js/node/test/parallel/test-vm-create-context-accessors.js new file mode 100644 index 0000000000..39fc5c4eec --- /dev/null +++ b/test/js/node/test/parallel/test-vm-create-context-accessors.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +let ctx = {}; + +Object.defineProperty(ctx, 'getter', { + get: function() { + return 'ok'; + } +}); + +let val; +Object.defineProperty(ctx, 'setter', { + set: function(_val) { + val = _val; + }, + get: function() { + return `ok=${val}`; + } +}); + +ctx = vm.createContext(ctx); + +const result = vm.runInContext('setter = "test";[getter,setter]', ctx); +assert.strictEqual(result[0], 'ok'); +assert.strictEqual(result[1], 'ok=test'); diff --git a/test/js/node/test/parallel/test-vm-create-context-circular-reference.js b/test/js/node/test/parallel/test-vm-create-context-circular-reference.js new file mode 100644 index 0000000000..7ea22781bd --- /dev/null +++ b/test/js/node/test/parallel/test-vm-create-context-circular-reference.js @@ -0,0 +1,34 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +let sbx = {}; +sbx.window = sbx; + +sbx = vm.createContext(sbx); + +sbx.test = 123; + +assert.strictEqual(sbx.window.window.window.window.window.test, 123); diff --git a/test/js/node/test/parallel/test-vm-cross-context.js b/test/js/node/test/parallel/test-vm-cross-context.js new file mode 100644 index 0000000000..b7cf1309d3 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-cross-context.js @@ -0,0 +1,29 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); + +const vm = require('vm'); +const ctx = vm.createContext(global); + +// Should not throw. +vm.runInContext('!function() { var x = console.log; }()', ctx); diff --git a/test/js/node/test/parallel/test-vm-data-property-writable.js b/test/js/node/test/parallel/test-vm-data-property-writable.js new file mode 100644 index 0000000000..00937ae412 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-data-property-writable.js @@ -0,0 +1,28 @@ +'use strict'; +// Refs: https://github.com/nodejs/node/issues/10223 + +require('../common'); +const vm = require('vm'); +const assert = require('assert'); + +const context = vm.createContext({}); + +let code = ` + Object.defineProperty(this, 'foo', {value: 5}); + Object.getOwnPropertyDescriptor(this, 'foo'); +`; + +let desc = vm.runInContext(code, context); + +assert.strictEqual(desc.writable, false); + +// Check that interceptors work for symbols. +code = ` + const bar = Symbol('bar'); + Object.defineProperty(this, bar, {value: 6}); + Object.getOwnPropertyDescriptor(this, bar); +`; + +desc = vm.runInContext(code, context); + +assert.strictEqual(desc.value, 6); diff --git a/test/js/node/test/parallel/test-vm-deleting-property.js b/test/js/node/test/parallel/test-vm-deleting-property.js new file mode 100644 index 0000000000..994aa0aff9 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-deleting-property.js @@ -0,0 +1,15 @@ +'use strict'; +// Refs: https://github.com/nodejs/node/issues/6287 + +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const context = vm.createContext(); +const res = vm.runInContext(` + this.x = 'prop'; + delete this.x; + Object.getOwnPropertyDescriptor(this, 'x'); +`, context); + +assert.strictEqual(res, undefined); diff --git a/test/js/node/test/parallel/test-vm-function-declaration.js b/test/js/node/test/parallel/test-vm-function-declaration.js new file mode 100644 index 0000000000..766e5ec78b --- /dev/null +++ b/test/js/node/test/parallel/test-vm-function-declaration.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); + +const vm = require('vm'); +const o = vm.createContext({ console }); + +// Function declaration and expression should both be copied to the +// sandboxed context. +let code = 'let a = function() {};\n'; +code += 'function b(){}\n'; +code += 'var c = function() {};\n'; +code += 'var d = () => {};\n'; +code += 'let e = () => {};\n'; + +// Grab the global b function as the completion value, to ensure that +// we are getting the global function, and not some other thing +code += '(function(){return this})().b;\n'; + +const res = vm.runInContext(code, o, 'test'); +assert.strictEqual(typeof res, 'function'); +assert.strictEqual(res.name, 'b'); +assert.strictEqual(typeof o.a, 'undefined'); +assert.strictEqual(typeof o.b, 'function'); +assert.strictEqual(typeof o.c, 'function'); +assert.strictEqual(typeof o.d, 'function'); +assert.strictEqual(typeof o.e, 'undefined'); +assert.strictEqual(res, o.b); diff --git a/test/js/node/test/parallel/test-vm-function-redefinition.js b/test/js/node/test/parallel/test-vm-function-redefinition.js new file mode 100644 index 0000000000..fa1ddde389 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-function-redefinition.js @@ -0,0 +1,11 @@ +'use strict'; +// Refs: https://github.com/nodejs/node/issues/548 +require('../common'); +const assert = require('assert'); +const vm = require('vm'); +const context = vm.createContext(); + +vm.runInContext('function test() { return 0; }', context); +vm.runInContext('function test() { return 1; }', context); +const result = vm.runInContext('test()', context); +assert.strictEqual(result, 1); diff --git a/test/js/node/test/parallel/test-vm-global-assignment.js b/test/js/node/test/parallel/test-vm-global-assignment.js new file mode 100644 index 0000000000..3fb3470b4c --- /dev/null +++ b/test/js/node/test/parallel/test-vm-global-assignment.js @@ -0,0 +1,15 @@ +'use strict'; +// Regression test for https://github.com/nodejs/node/issues/10806 + +require('../common'); +const assert = require('assert'); +const vm = require('vm'); +const ctx = vm.createContext({ open() { } }); +const window = vm.runInContext('this', ctx); +const other = 123; + +assert.notStrictEqual(window.open, other); +window.open = other; +assert.strictEqual(window.open, other); +window.open = other; +assert.strictEqual(window.open, other); diff --git a/test/js/node/test/parallel/test-vm-global-configurable-properties.js b/test/js/node/test/parallel/test-vm-global-configurable-properties.js new file mode 100644 index 0000000000..4428e747ea --- /dev/null +++ b/test/js/node/test/parallel/test-vm-global-configurable-properties.js @@ -0,0 +1,15 @@ +'use strict'; +// https://github.com/nodejs/node/issues/47799 + +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const ctx = vm.createContext(); + +const window = vm.runInContext('this', ctx); + +Object.defineProperty(window, 'x', { value: '1', configurable: true }); +assert.strictEqual(window.x, '1'); +Object.defineProperty(window, 'x', { value: '2', configurable: true }); +assert.strictEqual(window.x, '2'); diff --git a/test/js/node/test/parallel/test-vm-global-get-own.js b/test/js/node/test/parallel/test-vm-global-get-own.js new file mode 100644 index 0000000000..246fcbf866 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-global-get-own.js @@ -0,0 +1,105 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +// These assertions check that we can set new keys to the global context, +// get them back and also list them via getOwnProperty* or in. +// +// Related to: +// - https://github.com/nodejs/node/issues/45983 + +const global = vm.runInContext('this', vm.createContext()); + +function runAssertions(data, property, viaDefine, value1, value2, value3) { + // Define the property for the first time + setPropertyAndAssert(data, property, viaDefine, value1); + // Update the property + setPropertyAndAssert(data, property, viaDefine, value2); + // Delete the property + deletePropertyAndAssert(data, property); + // Re-define the property + setPropertyAndAssert(data, property, viaDefine, value3); + // Delete the property again + deletePropertyAndAssert(data, property); +} + +const fun1 = () => 1; +const fun2 = () => 2; +const fun3 = () => 3; + +function runAssertionsOnSandbox(builder) { + const sandboxContext = vm.createContext({ runAssertions, fun1, fun2, fun3 }); + vm.runInContext(builder('this'), sandboxContext); + vm.runInContext(builder('{}'), sandboxContext); +} + +// Assertions on: define property +runAssertions(global, 'toto', true, 1, 2, 3); +runAssertions(global, Symbol.for('toto'), true, 1, 2, 3); +runAssertions(global, 'tutu', true, fun1, fun2, fun3); +runAssertions(global, Symbol.for('tutu'), true, fun1, fun2, fun3); +runAssertions(global, 'tyty', true, fun1, 2, 3); +runAssertions(global, Symbol.for('tyty'), true, fun1, 2, 3); + +// Assertions on: direct assignment +runAssertions(global, 'titi', false, 1, 2, 3); +runAssertions(global, Symbol.for('titi'), false, 1, 2, 3); +runAssertions(global, 'tata', false, fun1, fun2, fun3); +runAssertions(global, Symbol.for('tata'), false, fun1, fun2, fun3); +runAssertions(global, 'tztz', false, fun1, 2, 3); +runAssertions(global, Symbol.for('tztz'), false, fun1, 2, 3); + +// Assertions on: define property from sandbox +runAssertionsOnSandbox( + (variable) => ` + runAssertions(${variable}, 'toto', true, 1, 2, 3); + runAssertions(${variable}, Symbol.for('toto'), true, 1, 2, 3); + runAssertions(${variable}, 'tutu', true, fun1, fun2, fun3); + runAssertions(${variable}, Symbol.for('tutu'), true, fun1, fun2, fun3); + runAssertions(${variable}, 'tyty', true, fun1, 2, 3); + runAssertions(${variable}, Symbol.for('tyty'), true, fun1, 2, 3);` +); + +// Assertions on: direct assignment from sandbox +runAssertionsOnSandbox( + (variable) => ` + runAssertions(${variable}, 'titi', false, 1, 2, 3); + runAssertions(${variable}, Symbol.for('titi'), false, 1, 2, 3); + runAssertions(${variable}, 'tata', false, fun1, fun2, fun3); + runAssertions(${variable}, Symbol.for('tata'), false, fun1, fun2, fun3); + runAssertions(${variable}, 'tztz', false, fun1, 2, 3); + runAssertions(${variable}, Symbol.for('tztz'), false, fun1, 2, 3);` +); + +// Helpers + +// Set the property on data and assert it worked +function setPropertyAndAssert(data, property, viaDefine, value) { + if (viaDefine) { + Object.defineProperty(data, property, { + enumerable: true, + writable: true, + value: value, + configurable: true, + }); + } else { + data[property] = value; + } + assert.strictEqual(data[property], value); + assert.ok(property in data); + if (typeof property === 'string') { + assert.ok(Object.getOwnPropertyNames(data).includes(property)); + } else { + assert.ok(Object.getOwnPropertySymbols(data).includes(property)); + } +} + +// Delete the property from data and assert it worked +function deletePropertyAndAssert(data, property) { + delete data[property]; + assert.strictEqual(data[property], undefined); + assert.ok(!(property in data)); + assert.ok(!Object.getOwnPropertyNames(data).includes(property)); + assert.ok(!Object.getOwnPropertySymbols(data).includes(property)); +} diff --git a/test/js/node/test/parallel/test-vm-harmony-symbols.js b/test/js/node/test/parallel/test-vm-harmony-symbols.js new file mode 100644 index 0000000000..5936025070 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-harmony-symbols.js @@ -0,0 +1,37 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +// The sandbox should have its own Symbol constructor. +let sandbox = {}; +vm.runInNewContext('this.Symbol = Symbol', sandbox); +assert.strictEqual(typeof sandbox.Symbol, 'function'); +assert.notStrictEqual(sandbox.Symbol, Symbol); + +// Unless we copy the Symbol constructor explicitly, of course. +sandbox = { Symbol }; +vm.runInNewContext('this.Symbol = Symbol', sandbox); +assert.strictEqual(typeof sandbox.Symbol, 'function'); +assert.strictEqual(sandbox.Symbol, Symbol); diff --git a/test/js/node/test/parallel/test-vm-indexed-properties.js b/test/js/node/test/parallel/test-vm-indexed-properties.js new file mode 100644 index 0000000000..34ef8d020b --- /dev/null +++ b/test/js/node/test/parallel/test-vm-indexed-properties.js @@ -0,0 +1,17 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const code = `Object.defineProperty(this, 99, { + value: 20, + enumerable: true + });`; + + +const sandbox = {}; +const ctx = vm.createContext(sandbox); +vm.runInContext(code, ctx); + +assert.strictEqual(sandbox[99], 20); diff --git a/test/js/node/test/parallel/test-vm-low-stack-space.js b/test/js/node/test/parallel/test-vm-low-stack-space.js new file mode 100644 index 0000000000..6a49a983d5 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-low-stack-space.js @@ -0,0 +1,26 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +function a() { + try { + return a(); + } catch { + // Throw an exception as near to the recursion-based RangeError as possible. + return vm.runInThisContext('() => 42')(); + } +} + +assert.strictEqual(a(), 42); + +function b() { + try { + return b(); + } catch { + // This writes a lot of noise to stderr, but it still works. + return vm.runInNewContext('() => 42')(); + } +} + +assert.strictEqual(b(), 42); diff --git a/test/js/node/test/parallel/test-vm-new-script-this-context.js b/test/js/node/test/parallel/test-vm-new-script-this-context.js new file mode 100644 index 0000000000..18f39f9086 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-new-script-this-context.js @@ -0,0 +1,68 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const Script = require('vm').Script; + +// Run a string +let script = new Script('\'passed\';'); +const result = script.runInThisContext(script); +assert.strictEqual(result, 'passed'); + +// Thrown error +script = new Script('throw new Error(\'test\');'); +assert.throws(() => { + script.runInThisContext(script); +}, /^Error: test$/); + +global.hello = 5; +script = new Script('hello = 2'); +script.runInThisContext(script); +assert.strictEqual(global.hello, 2); + + +// Pass values +global.code = 'foo = 1;' + + 'bar = 2;' + + 'if (typeof baz !== "undefined") throw new Error("test fail");'; +global.foo = 2; +global.obj = { foo: 0, baz: 3 }; +script = new Script(global.code); +script.runInThisContext(script); +assert.strictEqual(global.obj.foo, 0); +assert.strictEqual(global.bar, 2); +assert.strictEqual(global.foo, 1); + +// Call a function +global.f = function() { global.foo = 100; }; +script = new Script('f()'); +script.runInThisContext(script); +assert.strictEqual(global.foo, 100); + +common.allowGlobals( + global.hello, + global.code, + global.foo, + global.obj, + global.f +); diff --git a/test/js/node/test/parallel/test-vm-parse-abort-on-uncaught-exception.js b/test/js/node/test/parallel/test-vm-parse-abort-on-uncaught-exception.js new file mode 100644 index 0000000000..64bbb4dd4f --- /dev/null +++ b/test/js/node/test/parallel/test-vm-parse-abort-on-uncaught-exception.js @@ -0,0 +1,18 @@ +// Flags: --abort-on-uncaught-exception +'use strict'; +require('../common'); +const vm = require('vm'); + +// Regression test for https://github.com/nodejs/node/issues/13258 + +try { + new vm.Script({ toString() { throw new Error('foo'); } }, {}); +} catch { + // Continue regardless of error. +} + +try { + new vm.Script('[', {}); +} catch { + // Continue regardless of error. +} diff --git a/test/js/node/test/parallel/test-vm-proxies.js b/test/js/node/test/parallel/test-vm-proxies.js new file mode 100644 index 0000000000..405f730577 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-proxies.js @@ -0,0 +1,18 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +// src/node_contextify.cc filters out the Proxy object from the parent +// context. Make sure that the new context has a Proxy object of its own. +let sandbox = {}; +vm.runInNewContext('this.Proxy = Proxy', sandbox); +assert.strictEqual(typeof sandbox.Proxy, 'function'); +assert.notStrictEqual(sandbox.Proxy, Proxy); + +// Unless we copy the Proxy object explicitly, of course. +sandbox = { Proxy }; +vm.runInNewContext('this.Proxy = Proxy', sandbox); +assert.strictEqual(typeof sandbox.Proxy, 'function'); +assert.strictEqual(sandbox.Proxy, Proxy); diff --git a/test/js/node/test/parallel/test-vm-proxy-failure-CP.js b/test/js/node/test/parallel/test-vm-proxy-failure-CP.js new file mode 100644 index 0000000000..93027576d8 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-proxy-failure-CP.js @@ -0,0 +1,15 @@ +'use strict'; +require('../common'); +const vm = require('vm'); + +// Check that we do not accidentally query attributes. +// Issue: https://github.com/nodejs/node/issues/11902 +const handler = { + getOwnPropertyDescriptor: (target, prop) => { + throw new Error('whoops'); + } +}; +const sandbox = new Proxy({ foo: 'bar' }, handler); +const context = vm.createContext(sandbox); + +vm.runInContext('', context); diff --git a/test/js/node/test/parallel/test-vm-script-throw-in-tostring.js b/test/js/node/test/parallel/test-vm-script-throw-in-tostring.js new file mode 100644 index 0000000000..20e7a75079 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-script-throw-in-tostring.js @@ -0,0 +1,14 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const vm = require('vm'); + +assert.throws(() => { + new vm.Script({ + toString() { + throw new Error(); + } + }); +}, Error); diff --git a/test/js/node/test/parallel/test-vm-static-this.js b/test/js/node/test/parallel/test-vm-static-this.js new file mode 100644 index 0000000000..e9382d6c3b --- /dev/null +++ b/test/js/node/test/parallel/test-vm-static-this.js @@ -0,0 +1,65 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +/* eslint-disable strict */ +const common = require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +// Run a string +const result = vm.runInThisContext('\'passed\';'); +assert.strictEqual(result, 'passed'); + +// thrown error +assert.throws(function() { + vm.runInThisContext('throw new Error(\'test\');'); +}, /test/); + +global.hello = 5; +vm.runInThisContext('hello = 2'); +assert.strictEqual(global.hello, 2); + + +// pass values +const code = 'foo = 1;' + + 'bar = 2;' + + 'if (typeof baz !== \'undefined\')' + + 'throw new Error(\'test fail\');'; +global.foo = 2; +global.obj = { foo: 0, baz: 3 }; +/* eslint-disable no-unused-vars */ +const baz = vm.runInThisContext(code); +/* eslint-enable no-unused-vars */ +assert.strictEqual(global.obj.foo, 0); +assert.strictEqual(global.bar, 2); +assert.strictEqual(global.foo, 1); + +// call a function +global.f = function() { global.foo = 100; }; +vm.runInThisContext('f()'); +assert.strictEqual(global.foo, 100); + +common.allowGlobals( + global.hello, + global.foo, + global.obj, + global.f +); diff --git a/test/js/node/test/parallel/test-vm-strict-mode.js b/test/js/node/test/parallel/test-vm-strict-mode.js new file mode 100644 index 0000000000..b1b233664d --- /dev/null +++ b/test/js/node/test/parallel/test-vm-strict-mode.js @@ -0,0 +1,14 @@ +'use strict'; +// https://github.com/nodejs/node/issues/12300 + +require('../common'); +const assert = require('assert'); +const vm = require('vm'); + +const ctx = vm.createContext({ x: 42 }); + +// This might look as if x has not been declared, but x is defined on the +// sandbox and the assignment should not throw. +vm.runInContext('"use strict"; x = 1', ctx); + +assert.strictEqual(ctx.x, 1); diff --git a/test/js/node/test/parallel/test-vm-syntax-error-message.js b/test/js/node/test/parallel/test-vm-syntax-error-message.js new file mode 100644 index 0000000000..c49ff6aeb1 --- /dev/null +++ b/test/js/node/test/parallel/test-vm-syntax-error-message.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const child_process = require('child_process'); + +const p = child_process.spawn(process.execPath, [ + '-e', + 'vm = require("vm");' + + 'context = vm.createContext({});' + + 'try { vm.runInContext("throw new Error(\'boo\')", context); } ' + + 'catch (e) { console.log(e.message); }', +]); + +p.stderr.on('data', common.mustNotCall()); + +let output = ''; + +p.stdout.on('data', (data) => output += data); + +p.stdout.on('end', common.mustCall(() => { + assert.strictEqual(output.replace(/[\r\n]+/g, ''), 'boo'); +})); diff --git a/test/js/node/test/parallel/test-webcrypto-encrypt-decrypt.js b/test/js/node/test/parallel/test-webcrypto-encrypt-decrypt.js new file mode 100644 index 0000000000..cba193b8c7 --- /dev/null +++ b/test/js/node/test/parallel/test-webcrypto-encrypt-decrypt.js @@ -0,0 +1,124 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { subtle } = globalThis.crypto; + +// This is only a partial test. The WebCrypto Web Platform Tests +// will provide much greater coverage. + +// Test Encrypt/Decrypt RSA-OAEP +{ + const buf = globalThis.crypto.getRandomValues(new Uint8Array(50)); + + async function test() { + const ec = new TextEncoder(); + const { publicKey, privateKey } = await subtle.generateKey({ + name: 'RSA-OAEP', + modulusLength: 2048, + publicExponent: new Uint8Array([1, 0, 1]), + hash: 'SHA-384', + }, true, ['encrypt', 'decrypt']); + + const ciphertext = await subtle.encrypt({ + name: 'RSA-OAEP', + label: ec.encode('a label') + }, publicKey, buf); + + const plaintext = await subtle.decrypt({ + name: 'RSA-OAEP', + label: ec.encode('a label') + }, privateKey, ciphertext); + + assert.strictEqual( + Buffer.from(plaintext).toString('hex'), + Buffer.from(buf).toString('hex')); + } + + test().then(common.mustCall()); +} + +// Test Encrypt/Decrypt AES-CTR +{ + const buf = globalThis.crypto.getRandomValues(new Uint8Array(50)); + const counter = globalThis.crypto.getRandomValues(new Uint8Array(16)); + + async function test() { + const key = await subtle.generateKey({ + name: 'AES-CTR', + length: 256 + }, true, ['encrypt', 'decrypt']); + + const ciphertext = await subtle.encrypt( + { name: 'AES-CTR', counter, length: 64 }, key, buf, + ); + + const plaintext = await subtle.decrypt( + { name: 'AES-CTR', counter, length: 64 }, key, ciphertext, + ); + + assert.strictEqual( + Buffer.from(plaintext).toString('hex'), + Buffer.from(buf).toString('hex')); + } + + test().then(common.mustCall()); +} + +// Test Encrypt/Decrypt AES-CBC +{ + const buf = globalThis.crypto.getRandomValues(new Uint8Array(50)); + const iv = globalThis.crypto.getRandomValues(new Uint8Array(16)); + + async function test() { + const key = await subtle.generateKey({ + name: 'AES-CBC', + length: 256 + }, true, ['encrypt', 'decrypt']); + + const ciphertext = await subtle.encrypt( + { name: 'AES-CBC', iv }, key, buf, + ); + + const plaintext = await subtle.decrypt( + { name: 'AES-CBC', iv }, key, ciphertext, + ); + + assert.strictEqual( + Buffer.from(plaintext).toString('hex'), + Buffer.from(buf).toString('hex')); + } + + test().then(common.mustCall()); +} + +// Test Encrypt/Decrypt AES-GCM +{ + const buf = globalThis.crypto.getRandomValues(new Uint8Array(50)); + const iv = globalThis.crypto.getRandomValues(new Uint8Array(12)); + + async function test() { + const key = await subtle.generateKey({ + name: 'AES-GCM', + length: 256 + }, true, ['encrypt', 'decrypt']); + + const ciphertext = await subtle.encrypt( + { name: 'AES-GCM', iv }, key, buf, + ); + + const plaintext = await subtle.decrypt( + { name: 'AES-GCM', iv }, key, ciphertext, + ); + + assert.strictEqual( + Buffer.from(plaintext).toString('hex'), + Buffer.from(buf).toString('hex')); + } + + test().then(common.mustCall()); +} diff --git a/test/js/node/test/parallel/test-webcrypto-getRandomValues.js b/test/js/node/test/parallel/test-webcrypto-getRandomValues.js new file mode 100644 index 0000000000..f0fbe61a20 --- /dev/null +++ b/test/js/node/test/parallel/test-webcrypto-getRandomValues.js @@ -0,0 +1,11 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const { getRandomValues } = globalThis.crypto; + +assert.throws(() => getRandomValues(new Uint8Array()), { code: 'ERR_INVALID_THIS' }); diff --git a/test/js/node/test/parallel/test-websocket.js b/test/js/node/test/parallel/test-websocket.js new file mode 100644 index 0000000000..c595ec12bf --- /dev/null +++ b/test/js/node/test/parallel/test-websocket.js @@ -0,0 +1,6 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +assert.strictEqual(typeof WebSocket, 'function'); diff --git a/test/js/node/test/parallel/test-webstream-string-tag.js b/test/js/node/test/parallel/test-webstream-string-tag.js new file mode 100644 index 0000000000..980a204a9b --- /dev/null +++ b/test/js/node/test/parallel/test-webstream-string-tag.js @@ -0,0 +1,18 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); + +const classesToBeTested = [ WritableStream, WritableStreamDefaultWriter, WritableStreamDefaultController, + ReadableStream, ReadableStreamBYOBRequest, ReadableStreamDefaultReader, + ReadableStreamBYOBReader, ReadableStreamDefaultController, ReadableByteStreamController, + ByteLengthQueuingStrategy, CountQueuingStrategy, TransformStream, + TransformStreamDefaultController]; + + +classesToBeTested.forEach((cls) => { + assert.strictEqual(cls.prototype[Symbol.toStringTag], cls.name); + assert.deepStrictEqual(Object.getOwnPropertyDescriptor(cls.prototype, Symbol.toStringTag), + { configurable: true, enumerable: false, value: cls.name, writable: false }); +}); diff --git a/test/js/node/test/parallel/test-whatwg-encoding-custom-api-basics.js b/test/js/node/test/parallel/test-whatwg-encoding-custom-api-basics.js new file mode 100644 index 0000000000..71b573a8df --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-encoding-custom-api-basics.js @@ -0,0 +1,61 @@ +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/master/encoding/api-basics.html +// This is the part that can be run without ICU + +require('../common'); + +const assert = require('assert'); + +function testDecodeSample(encoding, string, bytes) { + assert.strictEqual( + new TextDecoder(encoding).decode(new Uint8Array(bytes)), + string); + assert.strictEqual( + new TextDecoder(encoding).decode(new Uint8Array(bytes).buffer), + string); +} + +// `z` (ASCII U+007A), cent (Latin-1 U+00A2), CJK water (BMP U+6C34), +// G-Clef (non-BMP U+1D11E), PUA (BMP U+F8FF), PUA (non-BMP U+10FFFD) +// byte-swapped BOM (non-character U+FFFE) +const sample = 'z\xA2\u6C34\uD834\uDD1E\uF8FF\uDBFF\uDFFD\uFFFE'; + +{ + const encoding = 'utf-8'; + const string = sample; + const bytes = [ + 0x7A, 0xC2, 0xA2, 0xE6, 0xB0, 0xB4, + 0xF0, 0x9D, 0x84, 0x9E, 0xEF, 0xA3, + 0xBF, 0xF4, 0x8F, 0xBF, 0xBD, 0xEF, + 0xBF, 0xBE, + ]; + const encoded = new TextEncoder().encode(string); + assert.deepStrictEqual([].slice.call(encoded), bytes); + assert.strictEqual( + new TextDecoder(encoding).decode(new Uint8Array(bytes)), + string); + assert.strictEqual( + new TextDecoder(encoding).decode(new Uint8Array(bytes).buffer), + string); +} + +testDecodeSample( + 'utf-16le', + sample, + [ + 0x7A, 0x00, 0xA2, 0x00, 0x34, 0x6C, + 0x34, 0xD8, 0x1E, 0xDD, 0xFF, 0xF8, + 0xFF, 0xDB, 0xFD, 0xDF, 0xFE, 0xFF, + ] +); + +testDecodeSample( + 'utf-16', + sample, + [ + 0x7A, 0x00, 0xA2, 0x00, 0x34, 0x6C, + 0x34, 0xD8, 0x1E, 0xDD, 0xFF, 0xF8, + 0xFF, 0xDB, 0xFD, 0xDF, 0xFE, 0xFF, + ] +); diff --git a/test/js/node/test/parallel/test-whatwg-encoding-custom-fatal-streaming.js b/test/js/node/test/parallel/test-whatwg-encoding-custom-fatal-streaming.js new file mode 100644 index 0000000000..164088270c --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-encoding-custom-fatal-streaming.js @@ -0,0 +1,61 @@ +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/d74324b53c/encoding/textdecoder-fatal-streaming.html +// With the twist that we specifically test for Node.js error codes + +const common = require('../common'); +const assert = require('assert'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +{ + [ + { encoding: 'utf-8', sequence: [0xC0] }, + { encoding: 'utf-16le', sequence: [0x00] }, + { encoding: 'utf-16be', sequence: [0x00] }, + ].forEach((testCase) => { + const data = new Uint8Array([testCase.sequence]); + assert.throws( + () => { + const decoder = new TextDecoder(testCase.encoding, { fatal: true }); + decoder.decode(data); + }, { + code: 'ERR_ENCODING_INVALID_ENCODED_DATA', + name: 'TypeError', + message: + `The encoded data was not valid for encoding ${testCase.encoding}` + } + ); + }); +} + +{ + const decoder = new TextDecoder('utf-16le', { fatal: true }); + const odd = new Uint8Array([0x00]); + const even = new Uint8Array([0x00, 0x00]); + + assert.throws( + () => { + decoder.decode(even, { stream: true }); + decoder.decode(odd); + }, { + code: 'ERR_ENCODING_INVALID_ENCODED_DATA', + name: 'TypeError', + message: + 'The encoded data was not valid for encoding utf-16le' + } + ); + + assert.throws( + () => { + decoder.decode(odd, { stream: true }); + decoder.decode(even); + }, { + code: 'ERR_ENCODING_INVALID_ENCODED_DATA', + name: 'TypeError', + message: + 'The encoded data was not valid for encoding utf-16le' + } + ); +} diff --git a/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-fatal.js b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-fatal.js new file mode 100644 index 0000000000..8778fa018e --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-fatal.js @@ -0,0 +1,84 @@ +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/39a67e2fff/encoding/textdecoder-fatal.html +// With the twist that we specifically test for Node.js error codes + +const common = require('../common'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +const assert = require('assert'); + +const bad = [ + { encoding: 'utf-8', input: [0xFF], name: 'invalid code' }, + { encoding: 'utf-8', input: [0xC0], name: 'ends early' }, + { encoding: 'utf-8', input: [0xE0], name: 'ends early 2' }, + { encoding: 'utf-8', input: [0xC0, 0x00], name: 'invalid trail' }, + { encoding: 'utf-8', input: [0xC0, 0xC0], name: 'invalid trail 2' }, + { encoding: 'utf-8', input: [0xE0, 0x00], name: 'invalid trail 3' }, + { encoding: 'utf-8', input: [0xE0, 0xC0], name: 'invalid trail 4' }, + { encoding: 'utf-8', input: [0xE0, 0x80, 0x00], name: 'invalid trail 5' }, + { encoding: 'utf-8', input: [0xE0, 0x80, 0xC0], name: 'invalid trail 6' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x80, 0x80, 0x80, 0x80], + name: '> 0x10FFFF' }, + { encoding: 'utf-8', input: [0xFE, 0x80, 0x80, 0x80, 0x80, 0x80], + name: 'obsolete lead byte' }, + // Overlong encodings + { encoding: 'utf-8', input: [0xC0, 0x80], name: 'overlong U+0000 - 2 bytes' }, + { encoding: 'utf-8', input: [0xE0, 0x80, 0x80], + name: 'overlong U+0000 - 3 bytes' }, + { encoding: 'utf-8', input: [0xF0, 0x80, 0x80, 0x80], + name: 'overlong U+0000 - 4 bytes' }, + { encoding: 'utf-8', input: [0xF8, 0x80, 0x80, 0x80, 0x80], + name: 'overlong U+0000 - 5 bytes' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x80, 0x80, 0x80, 0x80], + name: 'overlong U+0000 - 6 bytes' }, + { encoding: 'utf-8', input: [0xC1, 0xBF], name: 'overlong U+007F - 2 bytes' }, + { encoding: 'utf-8', input: [0xE0, 0x81, 0xBF], + name: 'overlong U+007F - 3 bytes' }, + { encoding: 'utf-8', input: [0xF0, 0x80, 0x81, 0xBF], + name: 'overlong U+007F - 4 bytes' }, + { encoding: 'utf-8', input: [0xF8, 0x80, 0x80, 0x81, 0xBF], + name: 'overlong U+007F - 5 bytes' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x80, 0x80, 0x81, 0xBF], + name: 'overlong U+007F - 6 bytes' }, + { encoding: 'utf-8', input: [0xE0, 0x9F, 0xBF], + name: 'overlong U+07FF - 3 bytes' }, + { encoding: 'utf-8', input: [0xF0, 0x80, 0x9F, 0xBF], + name: 'overlong U+07FF - 4 bytes' }, + { encoding: 'utf-8', input: [0xF8, 0x80, 0x80, 0x9F, 0xBF], + name: 'overlong U+07FF - 5 bytes' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x80, 0x80, 0x9F, 0xBF], + name: 'overlong U+07FF - 6 bytes' }, + { encoding: 'utf-8', input: [0xF0, 0x8F, 0xBF, 0xBF], + name: 'overlong U+FFFF - 4 bytes' }, + { encoding: 'utf-8', input: [0xF8, 0x80, 0x8F, 0xBF, 0xBF], + name: 'overlong U+FFFF - 5 bytes' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x80, 0x8F, 0xBF, 0xBF], + name: 'overlong U+FFFF - 6 bytes' }, + { encoding: 'utf-8', input: [0xF8, 0x84, 0x8F, 0xBF, 0xBF], + name: 'overlong U+10FFFF - 5 bytes' }, + { encoding: 'utf-8', input: [0xFC, 0x80, 0x84, 0x8F, 0xBF, 0xBF], + name: 'overlong U+10FFFF - 6 bytes' }, + // UTF-16 surrogates encoded as code points in UTF-8 + { encoding: 'utf-8', input: [0xED, 0xA0, 0x80], name: 'lead surrogate' }, + { encoding: 'utf-8', input: [0xED, 0xB0, 0x80], name: 'trail surrogate' }, + { encoding: 'utf-8', input: [0xED, 0xA0, 0x80, 0xED, 0xB0, 0x80], + name: 'surrogate pair' }, + { encoding: 'utf-16le', input: [0x00], name: 'truncated code unit' }, + // Mismatched UTF-16 surrogates are exercised in utf16-surrogates.html + // FIXME: Add legacy encoding cases +]; + +bad.forEach((t) => { + assert.throws( + () => { + new TextDecoder(t.encoding, { fatal: true }) + .decode(new Uint8Array(t.input)); + }, { + code: 'ERR_ENCODING_INVALID_ENCODED_DATA', + name: 'TypeError' + } + ); +}); diff --git a/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-ignorebom.js b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-ignorebom.js new file mode 100644 index 0000000000..94fc3318d1 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-ignorebom.js @@ -0,0 +1,30 @@ +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/7f567fa29c/encoding/textdecoder-ignorebom.html +// This is the part that can be run without ICU + +require('../common'); + +const assert = require('assert'); + +const cases = [ + { + encoding: 'utf-8', + bytes: [0xEF, 0xBB, 0xBF, 0x61, 0x62, 0x63] + }, + { + encoding: 'utf-16le', + bytes: [0xFF, 0xFE, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00] + }, +]; + +cases.forEach((testCase) => { + const BOM = '\uFEFF'; + let decoder = new TextDecoder(testCase.encoding, { ignoreBOM: true }); + const bytes = new Uint8Array(testCase.bytes); + assert.strictEqual(decoder.decode(bytes), `${BOM}abc`); + decoder = new TextDecoder(testCase.encoding, { ignoreBOM: false }); + assert.strictEqual(decoder.decode(bytes), 'abc'); + decoder = new TextDecoder(testCase.encoding); + assert.strictEqual(decoder.decode(bytes), 'abc'); +}); diff --git a/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-invalid-arg.js b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-invalid-arg.js new file mode 100644 index 0000000000..5c8a9837f6 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-invalid-arg.js @@ -0,0 +1,19 @@ +'use strict'; + +// This tests that ERR_INVALID_ARG_TYPE are thrown when +// invalid arguments are passed to TextDecoder. + +require('../common'); +const assert = require('assert'); + +{ + const notArrayBufferViewExamples = [false, {}, 1, '', new Error()]; + notArrayBufferViewExamples.forEach((invalidInputType) => { + assert.throws(() => { + new TextDecoder(undefined, null).decode(invalidInputType); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }); + }); +} diff --git a/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-streaming.js b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-streaming.js new file mode 100644 index 0000000000..5484929326 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-streaming.js @@ -0,0 +1,38 @@ +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/fa9436d12c/encoding/textdecoder-streaming.html +// This is the part that can be run without ICU + +require('../common'); + +const assert = require('assert'); + +const string = + '\x00123ABCabc\x80\xFF\u0100\u1000\uFFFD\uD800\uDC00\uDBFF\uDFFF'; +const octets = { + 'utf-8': [ + 0x00, 0x31, 0x32, 0x33, 0x41, 0x42, 0x43, 0x61, 0x62, 0x63, 0xc2, 0x80, + 0xc3, 0xbf, 0xc4, 0x80, 0xe1, 0x80, 0x80, 0xef, 0xbf, 0xbd, 0xf0, 0x90, + 0x80, 0x80, 0xf4, 0x8f, 0xbf, 0xbf], + 'utf-16le': [ + 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x41, 0x00, 0x42, 0x00, + 0x43, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x80, 0x00, 0xFF, 0x00, + 0x00, 0x01, 0x00, 0x10, 0xFD, 0xFF, 0x00, 0xD8, 0x00, 0xDC, 0xFF, 0xDB, + 0xFF, 0xDF] +}; + +Object.keys(octets).forEach((encoding) => { + for (let len = 1; len <= 5; ++len) { + const encoded = octets[encoding]; + const decoder = new TextDecoder(encoding); + let out = ''; + for (let i = 0; i < encoded.length; i += len) { + const sub = []; + for (let j = i; j < encoded.length && j < i + len; ++j) + sub.push(encoded[j]); + out += decoder.decode(new Uint8Array(sub), { stream: true }); + } + out += decoder.decode(); + assert.strictEqual(out, string); + } +}); diff --git a/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-utf16-surrogates.js b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-utf16-surrogates.js new file mode 100644 index 0000000000..a2a31af28c --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-encoding-custom-textdecoder-utf16-surrogates.js @@ -0,0 +1,56 @@ +'use strict'; + +// From: https://github.com/w3c/web-platform-tests/blob/39a67e2fff/encoding/textdecoder-utf16-surrogates.html +// With the twist that we specifically test for Node.js error codes + +const common = require('../common'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +const assert = require('assert'); + +const bad = [ + { + encoding: 'utf-16le', + input: [0x00, 0xd8], + expected: '\uFFFD', + name: 'lone surrogate lead' + }, + { + encoding: 'utf-16le', + input: [0x00, 0xdc], + expected: '\uFFFD', + name: 'lone surrogate trail' + }, + { + encoding: 'utf-16le', + input: [0x00, 0xd8, 0x00, 0x00], + expected: '\uFFFD\u0000', + name: 'unmatched surrogate lead' + }, + { + encoding: 'utf-16le', + input: [0x00, 0xdc, 0x00, 0x00], + expected: '\uFFFD\u0000', + name: 'unmatched surrogate trail' + }, + { + encoding: 'utf-16le', + input: [0x00, 0xdc, 0x00, 0xd8], + expected: '\uFFFD\uFFFD', + name: 'swapped surrogate pair' + }, +]; + +for (const t of bad) { + assert.throws( + () => { + new TextDecoder(t.encoding, { fatal: true }) + .decode(new Uint8Array(t.input)); + }, { + code: 'ERR_ENCODING_INVALID_ENCODED_DATA', + name: 'TypeError' + } + ); +} diff --git a/test/js/node/test/parallel/test-whatwg-events-add-event-listener-options-passive.js b/test/js/node/test/parallel/test-whatwg-events-add-event-listener-options-passive.js new file mode 100644 index 0000000000..97984bd9af --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-events-add-event-listener-options-passive.js @@ -0,0 +1,65 @@ +'use strict'; + +const common = require('../common'); + +// Manually converted from https://github.com/web-platform-tests/wpt/blob/master/dom/events/AddEventListenerOptions-passive.html +// in order to define the `document` ourselves + +const { + fail, + ok, + strictEqual +} = require('assert'); + +{ + const document = new EventTarget(); + let supportsPassive = false; + const query_options = { + get passive() { + supportsPassive = true; + return false; + }, + get dummy() { + fail('dummy value getter invoked'); + return false; + } + }; + + document.addEventListener('test_event', null, query_options); + ok(supportsPassive); + + supportsPassive = false; + document.removeEventListener('test_event', null, query_options); + strictEqual(supportsPassive, false); +} +{ + function testPassiveValue(optionsValue, expectedDefaultPrevented) { + const document = new EventTarget(); + let defaultPrevented; + function handler(e) { + if (e.defaultPrevented) { + fail('Event prematurely marked defaultPrevented'); + } + e.preventDefault(); + defaultPrevented = e.defaultPrevented; + } + document.addEventListener('test', handler, optionsValue); + // TODO the WHATWG test is more extensive here and tests dispatching on + // document.body, if we ever support getParent we should amend this + const ev = new Event('test', { bubbles: true, cancelable: true }); + const uncanceled = document.dispatchEvent(ev); + + strictEqual(defaultPrevented, expectedDefaultPrevented); + strictEqual(uncanceled, !expectedDefaultPrevented); + + document.removeEventListener('test', handler, optionsValue); + } + testPassiveValue(undefined, true); + testPassiveValue({}, true); + testPassiveValue({ passive: false }, true); + + common.skip('TODO: passive listeners is still broken'); + testPassiveValue({ passive: 1 }, false); + testPassiveValue({ passive: true }, false); + testPassiveValue({ passive: 0 }, true); +} diff --git a/test/js/node/test/parallel/test-whatwg-events-add-event-listener-options-signal.js b/test/js/node/test/parallel/test-whatwg-events-add-event-listener-options-signal.js new file mode 100644 index 0000000000..460d2ee3f2 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-events-add-event-listener-options-signal.js @@ -0,0 +1,168 @@ +'use strict'; + +require('../common'); + +const { + strictEqual, + throws, +} = require('assert'); + +// Manually ported from: wpt@dom/events/AddEventListenerOptions-signal.any.js + +{ + // Passing an AbortSignal to addEventListener does not prevent + // removeEventListener + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + et.addEventListener('test', handler, { signal: controller.signal }); + et.dispatchEvent(new Event('test')); + strictEqual(count, 1, 'Adding a signal still adds a listener'); + et.dispatchEvent(new Event('test')); + strictEqual(count, 2, 'The listener was not added with the once flag'); + controller.abort(); + et.dispatchEvent(new Event('test')); + strictEqual(count, 2, 'Aborting on the controller removes the listener'); + // See: https://github.com/nodejs/node/pull/37696 , adding an event listener + // should always return undefined. + strictEqual( + et.addEventListener('test', handler, { signal: controller.signal }), + undefined); + et.dispatchEvent(new Event('test')); + strictEqual(count, 2, 'Passing an aborted signal never adds the handler'); +} + +{ + // Passing an AbortSignal to addEventListener works with the once flag + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + et.addEventListener('test', handler, { signal: controller.signal }); + et.removeEventListener('test', handler); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Removing a once listener works with a passed signal + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + const options = { signal: controller.signal, once: true }; + et.addEventListener('test', handler, options); + controller.abort(); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + const options = { signal: controller.signal, once: true }; + et.addEventListener('test', handler, options); + et.removeEventListener('test', handler); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Passing an AbortSignal to multiple listeners + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + const options = { signal: controller.signal, once: true }; + et.addEventListener('first', handler, options); + et.addEventListener('second', handler, options); + controller.abort(); + et.dispatchEvent(new Event('first')); + et.dispatchEvent(new Event('second')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Passing an AbortSignal to addEventListener works with the capture flag + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + const options = { signal: controller.signal, capture: true }; + et.addEventListener('test', handler, options); + controller.abort(); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Aborting from a listener does not call future listeners + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + const options = { signal: controller.signal }; + et.addEventListener('test', () => { + controller.abort(); + }, options); + et.addEventListener('test', handler, options); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Adding then aborting a listener in another listener does not call it + let count = 0; + function handler() { + count++; + } + const et = new EventTarget(); + const controller = new AbortController(); + et.addEventListener('test', () => { + et.addEventListener('test', handler, { signal: controller.signal }); + controller.abort(); + }, { signal: controller.signal }); + et.dispatchEvent(new Event('test')); + strictEqual(count, 0, 'The listener was still removed'); +} + +{ + // Aborting from a nested listener should remove it + const et = new EventTarget(); + const ac = new AbortController(); + let count = 0; + et.addEventListener('foo', () => { + et.addEventListener('foo', () => { + count++; + if (count > 5) ac.abort(); + et.dispatchEvent(new Event('foo')); + }, { signal: ac.signal }); + et.dispatchEvent(new Event('foo')); + }, { once: true }); + et.dispatchEvent(new Event('foo')); +} +{ + const et = new EventTarget(); + [1, 1n, {}, [], null, true, 'hi', Symbol(), () => {}].forEach((signal) => { + throws(() => et.addEventListener('foo', () => {}, { signal }), { + name: 'TypeError', + }); + }); +} diff --git a/test/js/node/test/parallel/test-whatwg-events-customevent.js b/test/js/node/test/parallel/test-whatwg-events-customevent.js new file mode 100644 index 0000000000..e21ea1783f --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-events-customevent.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); + +const { strictEqual, throws, equal } = require('assert'); + +// Manually converted from https://github.com/web-platform-tests/wpt/blob/master/dom/events/CustomEvent.html +// in order to define the `document` ourselves + +{ + const type = 'foo'; + const target = new EventTarget(); + + target.addEventListener(type, common.mustCall((evt) => { + strictEqual(evt.type, type); + })); + + target.dispatchEvent(new Event(type)); +} + +{ + throws(() => { + new Event(); + }, TypeError); +} + +{ + const event = new Event('foo'); + equal(event.type, 'foo'); + equal(event.bubbles, false); + equal(event.cancelable, false); + equal(event.detail, null); +} diff --git a/test/js/node/test/parallel/test-whatwg-events-event-constructors.js b/test/js/node/test/parallel/test-whatwg-events-event-constructors.js new file mode 100644 index 0000000000..7880b10043 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-events-event-constructors.js @@ -0,0 +1,29 @@ +'use strict'; + +require('../common'); +const { test, assert_equals, assert_array_equals } = + require('../common/wpt').harness; + +// Source: https://github.com/web-platform-tests/wpt/blob/6cef1d2087d6a07d7cc6cee8cf207eec92e27c5f/dom/events/Event-constructors.any.js#L91-L112 +test(function() { + const called = []; + const ev = new Event('Xx', { + get cancelable() { + called.push('cancelable'); + return false; + }, + get bubbles() { + called.push('bubbles'); + return true; + }, + get sweet() { + called.push('sweet'); + return 'x'; + }, + }); + assert_array_equals(called, ['bubbles', 'cancelable']); + assert_equals(ev.type, 'Xx'); + assert_equals(ev.bubbles, true); + assert_equals(ev.cancelable, false); + assert_equals(ev.sweet, undefined); +}); diff --git a/test/js/node/test/parallel/test-whatwg-events-eventtarget-this-of-listener.js b/test/js/node/test/parallel/test-whatwg-events-eventtarget-this-of-listener.js new file mode 100644 index 0000000000..16ee14feab --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-events-eventtarget-this-of-listener.js @@ -0,0 +1,119 @@ +'use strict'; + +require('../common'); +const { test, assert_equals, assert_unreached } = + require('../common/wpt').harness; + +// Manually ported from: https://github.com/web-platform-tests/wpt/blob/6cef1d2087d6a07d7cc6cee8cf207eec92e27c5f/dom/events/EventTarget-this-of-listener.html + +// Mock document +const document = { + createElement: () => new EventTarget(), + createTextNode: () => new EventTarget(), + createDocumentFragment: () => new EventTarget(), + createComment: () => new EventTarget(), + createProcessingInstruction: () => new EventTarget(), +}; + +test(() => { + const nodes = [ + document.createElement('p'), + document.createTextNode('some text'), + document.createDocumentFragment(), + document.createComment('a comment'), + document.createProcessingInstruction('target', 'data'), + ]; + + let callCount = 0; + for (const node of nodes) { + node.addEventListener('someevent', function() { + ++callCount; + assert_equals(this, node); + }); + + node.dispatchEvent(new Event('someevent')); + } + + assert_equals(callCount, nodes.length); +}, 'the this value inside the event listener callback should be the node'); + +test(() => { + const nodes = [ + document.createElement('p'), + document.createTextNode('some text'), + document.createDocumentFragment(), + document.createComment('a comment'), + document.createProcessingInstruction('target', 'data'), + ]; + + let callCount = 0; + for (const node of nodes) { + const handler = {}; + + node.addEventListener('someevent', handler); + handler.handleEvent = function() { + ++callCount; + assert_equals(this, handler); + }; + + node.dispatchEvent(new Event('someevent')); + } + + assert_equals(callCount, nodes.length); +}, 'addEventListener should not require handleEvent to be defined on object listeners'); + +test(() => { + const nodes = [ + document.createElement('p'), + document.createTextNode('some text'), + document.createDocumentFragment(), + document.createComment('a comment'), + document.createProcessingInstruction('target', 'data'), + ]; + + let callCount = 0; + for (const node of nodes) { + function handler() { + ++callCount; + assert_equals(this, node); + } + + handler.handleEvent = () => { + assert_unreached('should not call the handleEvent method on a function'); + }; + + node.addEventListener('someevent', handler); + + node.dispatchEvent(new Event('someevent')); + } + + assert_equals(callCount, nodes.length); +}, 'handleEvent properties added to a function before addEventListener are not reached'); + +test(() => { + const nodes = [ + document.createElement('p'), + document.createTextNode('some text'), + document.createDocumentFragment(), + document.createComment('a comment'), + document.createProcessingInstruction('target', 'data'), + ]; + + let callCount = 0; + for (const node of nodes) { + function handler() { + ++callCount; + assert_equals(this, node); + } + + node.addEventListener('someevent', handler); + + handler.handleEvent = () => { + assert_unreached('should not call the handleEvent method on a function'); + }; + + node.dispatchEvent(new Event('someevent')); + } + + assert_equals(callCount, nodes.length); +}, 'handleEvent properties added to a function after addEventListener are not reached'); diff --git a/test/js/node/test/parallel/test-whatwg-readablestream.mjs b/test/js/node/test/parallel/test-whatwg-readablestream.mjs new file mode 100644 index 0000000000..57ebed6045 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-readablestream.mjs @@ -0,0 +1,70 @@ +import { mustCall } from '../common/index.mjs'; +import { ReadableStream } from 'stream/web'; +import assert from 'assert'; + +{ + // Test tee() with close in the nextTick after enqueue + async function read(stream) { + const chunks = []; + for await (const chunk of stream) + chunks.push(chunk); + return Buffer.concat(chunks).toString(); + } + + const [r1, r2] = new ReadableStream({ + start(controller) { + process.nextTick(() => { + controller.enqueue(new Uint8Array([102, 111, 111, 98, 97, 114])); + + process.nextTick(() => { + controller.close(); + }); + }); + } + }).tee(); + + (async () => { + const [dataReader1, dataReader2] = await Promise.all([ + read(r1), + read(r2), + ]); + + assert.strictEqual(dataReader1, dataReader2); + assert.strictEqual(dataReader1, 'foobar'); + assert.strictEqual(dataReader2, 'foobar'); + })().then(mustCall()); +} + +{ + // Test ReadableByteStream.tee() with close in the nextTick after enqueue + async function read(stream) { + const chunks = []; + for await (const chunk of stream) + chunks.push(chunk); + return Buffer.concat(chunks).toString(); + } + + const [r1, r2] = new ReadableStream({ + type: 'bytes', + start(controller) { + process.nextTick(() => { + controller.enqueue(new Uint8Array([102, 111, 111, 98, 97, 114])); + + process.nextTick(() => { + controller.close(); + }); + }); + } + }).tee(); + + (async () => { + const [dataReader1, dataReader2] = await Promise.all([ + read(r1), + read(r2), + ]); + + assert.strictEqual(dataReader1, dataReader2); + assert.strictEqual(dataReader1, 'foobar'); + assert.strictEqual(dataReader2, 'foobar'); + })().then(mustCall()); +} diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-deepequal.js b/test/js/node/test/parallel/test-whatwg-url-custom-deepequal.js new file mode 100644 index 0000000000..9150b1561b --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-deepequal.js @@ -0,0 +1,18 @@ +'use strict'; +// This tests that the internal flags in URL objects are consistent, as manifest +// through assert libraries. +// See https://github.com/nodejs/node/issues/24211 + +// Tests below are not from WPT. + +require('../common'); +const assert = require('assert'); + +assert.deepStrictEqual( + new URL('./foo', 'https://example.com/'), + new URL('https://example.com/foo') +); +assert.deepStrictEqual( + new URL('./foo', 'https://user:pass@example.com/'), + new URL('https://user:pass@example.com/foo') +); diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-domainto.js b/test/js/node/test/parallel/test-whatwg-url-custom-domainto.js new file mode 100644 index 0000000000..b7458d7a8e --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-domainto.js @@ -0,0 +1,57 @@ +'use strict'; + +// Tests below are not from WPT. + +const common = require('../common'); + +if (!common.hasIntl) + common.skip('missing Intl'); + +const assert = require('assert'); +const { domainToASCII, domainToUnicode } = require('url'); + +const tests = require('../fixtures/url-idna'); +const fixtures = require('../common/fixtures'); +const wptToASCIITests = require( + fixtures.path('wpt', 'url', 'resources', 'toascii.json') +); + +{ + const expectedError = { code: 'ERR_MISSING_ARGS', name: 'TypeError' }; + assert.throws(() => domainToASCII(), expectedError); + assert.throws(() => domainToUnicode(), expectedError); + assert.strictEqual(domainToASCII(undefined), 'undefined'); + assert.strictEqual(domainToUnicode(undefined), 'undefined'); +} + +{ + for (const [i, { ascii, unicode }] of tests.entries()) { + assert.strictEqual(ascii, domainToASCII(unicode), + `domainToASCII(${i + 1})`); + assert.strictEqual(unicode, domainToUnicode(ascii), + `domainToUnicode(${i + 1})`); + assert.strictEqual(ascii, domainToASCII(domainToUnicode(ascii)), + `domainToASCII(domainToUnicode(${i + 1}))`); + assert.strictEqual(unicode, domainToUnicode(domainToASCII(unicode)), + `domainToUnicode(domainToASCII(${i + 1}))`); + } +} + +{ + for (const [i, test] of wptToASCIITests.entries()) { + if (typeof test === 'string') + continue; // skip comments + const { comment, input, output } = test; + let caseComment = `Case ${i + 1}`; + if (comment) + caseComment += ` (${comment})`; + if (output === null) { + assert.strictEqual(domainToASCII(input), '', caseComment); + assert.strictEqual(domainToUnicode(input), '', caseComment); + } else { + assert.strictEqual(domainToASCII(input), output, caseComment); + const roundtripped = domainToASCII(domainToUnicode(input)); + assert.strictEqual(roundtripped, output, caseComment); + } + } +} diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-href-side-effect.js b/test/js/node/test/parallel/test-whatwg-url-custom-href-side-effect.js new file mode 100644 index 0000000000..30967d9fea --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-href-side-effect.js @@ -0,0 +1,15 @@ +'use strict'; + +// Tests below are not from WPT. +require('../common'); +const assert = require('assert'); + +const ref = new URL('http://example.com/path'); +const url = new URL('http://example.com/path'); +assert.throws(() => { + url.href = ''; +}, { + name: 'TypeError' +}); + +assert.deepStrictEqual(url, ref); diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-inspect.js b/test/js/node/test/parallel/test-whatwg-url-custom-inspect.js new file mode 100644 index 0000000000..946c097eac --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-inspect.js @@ -0,0 +1,70 @@ +'use strict'; + +// Tests below are not from WPT. + +const common = require('../common'); +if (!common.hasIntl) { + // A handful of the tests fail when ICU is not included. + common.skip('missing Intl'); +} + +const util = require('util'); +const assert = require('assert'); + +const url = new URL('https://username:password@host.name:8080/path/name/?que=ry#hash'); + +assert.strictEqual( + util.inspect(url), + `URL { + href: 'https://username:password@host.name:8080/path/name/?que=ry#hash', + origin: 'https://host.name:8080', + protocol: 'https:', + username: 'username', + password: 'password', + host: 'host.name:8080', + hostname: 'host.name', + port: '8080', + pathname: '/path/name/', + search: '?que=ry', + searchParams: URLSearchParams { 'que' => 'ry' }, + hash: '#hash' +}`); + +assert.strictEqual( + util.inspect(url, { showHidden: true }), + `URL { + href: 'https://username:password@host.name:8080/path/name/?que=ry#hash', + origin: 'https://host.name:8080', + protocol: 'https:', + username: 'username', + password: 'password', + host: 'host.name:8080', + hostname: 'host.name', + port: '8080', + pathname: '/path/name/', + search: '?que=ry', + searchParams: URLSearchParams { 'que' => 'ry' }, + hash: '#hash', + [Symbol(context)]: URLContext { + href: 'https://username:password@host.name:8080/path/name/?que=ry#hash', + protocol_end: 6, + username_end: 16, + host_start: 25, + host_end: 35, + pathname_start: 40, + search_start: 51, + hash_start: 58, + port: 8080, + scheme_type: 2, + [hasPort]: [Getter], + [hasSearch]: [Getter], + [hasHash]: [Getter] + } +}`); + +assert.strictEqual( + util.inspect({ a: url }, { depth: 0 }), + '{ a: URL {} }'); + +class MyURL extends URL {} +assert(util.inspect(new MyURL(url.href)).startsWith('MyURL {')); diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-parsing.js b/test/js/node/test/parallel/test-whatwg-url-custom-parsing.js new file mode 100644 index 0000000000..cdeda59eec --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-parsing.js @@ -0,0 +1,87 @@ +'use strict'; + +// Tests below are not from WPT. + +const common = require('../common'); +if (!common.hasIntl) { + // A handful of the tests fail when ICU is not included. + common.skip('missing Intl'); +} + +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const tests = require( + fixtures.path('wpt', 'url', 'resources', 'urltestdata.json') +); + +const originalFailures = tests.filter((test) => test.failure); + +const typeFailures = [ + { input: '' }, + { input: 'test' }, + { input: undefined }, + { input: 0 }, + { input: true }, + { input: false }, + { input: null }, + { input: new Date() }, + { input: new RegExp() }, + { input: 'test', base: null }, + { input: 'http://nodejs.org', base: null }, + { input: () => {} }, +]; + +// See https://github.com/w3c/web-platform-tests/pull/10955 +// > If `failure` is true, parsing `about:blank` against `base` +// > must give failure. This tests that the logic for converting +// > base URLs into strings properly fails the whole parsing +// > algorithm if the base URL cannot be parsed. +const aboutBlankFailures = originalFailures + .map((test) => ({ + input: 'about:blank', + base: test.input, + failure: true + })); + +const failureTests = originalFailures + .concat(typeFailures) + .concat(aboutBlankFailures); + +const expectedError = { code: 'ERR_INVALID_URL', name: 'TypeError' }; + +for (const test of failureTests) { + assert.throws( + () => new URL(test.input, test.base), + (error) => { + assert.throws(() => { throw error; }, expectedError); + assert.strictEqual(`${error}`, 'TypeError: Invalid URL'); + assert.strictEqual(error.message, 'Invalid URL'); + return true; + }); +} + +const additional_tests = + require(fixtures.path('url-tests-additional.js')); + +for (const test of additional_tests) { + const url = new URL(test.url); + if (test.href) assert.strictEqual(url.href, test.href); + if (test.origin) assert.strictEqual(url.origin, test.origin); + if (test.protocol) assert.strictEqual(url.protocol, test.protocol); + if (test.username) assert.strictEqual(url.username, test.username); + if (test.password) assert.strictEqual(url.password, test.password); + if (test.hostname) assert.strictEqual(url.hostname, test.hostname); + if (test.host) assert.strictEqual(url.host, test.host); + if (test.port !== undefined) assert.strictEqual(url.port, test.port); + if (test.pathname) assert.strictEqual(url.pathname, test.pathname); + if (test.search) assert.strictEqual(url.search, test.search); + if (test.hash) assert.strictEqual(url.hash, test.hash); +} + +assert.throws(() => { + new URL(); +}, { + name: 'TypeError', + code: 'ERR_MISSING_ARGS', +}); diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-searchparams-sort.js b/test/js/node/test/parallel/test-whatwg-url-custom-searchparams-sort.js new file mode 100644 index 0000000000..e0b0c5c1ed --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-searchparams-sort.js @@ -0,0 +1,51 @@ +'use strict'; + +// Tests below are not from WPT. + +require('../common'); +const { test, assert_array_equals } = require('../common/wpt').harness; + +// TODO(joyeecheung): upstream this to WPT, if possible - even +// just as a test for large inputs. Other implementations may +// have a similar cutoff anyway. + +// Test bottom-up iterative stable merge sort because we only use that +// algorithm to sort > 100 search params. +const tests = [{ input: '', output: [] }]; +const pairs = []; +for (let i = 10; i < 100; i++) { + pairs.push([`a${i}`, 'b']); + tests[0].output.push([`a${i}`, 'b']); +} +tests[0].input = pairs.sort(() => Math.random() > 0.5) + .map((pair) => pair.join('=')).join('&'); + +tests.push( + { + 'input': 'z=a&=b&c=d', + 'output': [['', 'b'], ['c', 'd'], ['z', 'a']] + } +); + +tests.forEach((val) => { + test(() => { + const params = new URLSearchParams(val.input); + let i = 0; + params.sort(); + for (const param of params) { + assert_array_equals(param, val.output[i]); + i++; + } + }, `Parse and sort: ${val.input}`); + + test(() => { + const url = new URL(`?${val.input}`, 'https://example/'); + url.searchParams.sort(); + const params = new URLSearchParams(url.search); + let i = 0; + for (const param of params) { + assert_array_equals(param, val.output[i]); + i++; + } + }, `URL parse and sort: ${val.input}`); +}); diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-searchparams.js b/test/js/node/test/parallel/test-whatwg-url-custom-searchparams.js new file mode 100644 index 0000000000..faec86e017 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-searchparams.js @@ -0,0 +1,147 @@ +'use strict'; + +// Tests below are not from WPT. + +require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const serialized = 'a=a&a=1&a=true&a=undefined&a=null&a=%EF%BF%BD' + + '&a=%EF%BF%BD&a=%F0%9F%98%80&a=%EF%BF%BD%EF%BF%BD' + + '&a=%5Bobject+Object%5D'; +const values = ['a', 1, true, undefined, null, '\uD83D', '\uDE00', + '\uD83D\uDE00', '\uDE00\uD83D', {}]; +const normalizedValues = ['a', '1', 'true', 'undefined', 'null', '\uFFFD', + '\uFFFD', '\uD83D\uDE00', '\uFFFD\uFFFD', + '[object Object]']; + +const m = new URL('http://example.org'); +const ownSymbolsBeforeGetterAccess = Object.getOwnPropertySymbols(m); +const sp = m.searchParams; +assert.deepStrictEqual(Object.getOwnPropertySymbols(m), ownSymbolsBeforeGetterAccess); + +assert(sp); +assert.strictEqual(sp.toString(), ''); +assert.strictEqual(m.search, ''); + +assert(!sp.has('a')); +values.forEach((i) => sp.set('a', i)); +assert(sp.has('a')); +assert.strictEqual(sp.get('a'), '[object Object]'); +sp.delete('a'); +assert(!sp.has('a')); + +m.search = ''; +assert.strictEqual(sp.toString(), ''); + +values.forEach((i) => sp.append('a', i)); +assert(sp.has('a')); +assert.strictEqual(sp.getAll('a').length, values.length); +assert.strictEqual(sp.get('a'), 'a'); + +assert.strictEqual(sp.toString(), serialized); + +assert.strictEqual(m.search, `?${serialized}`); + +sp.delete('a'); +values.forEach((i) => sp.append('a', i)); +assert.strictEqual(m.href, `http://example.org/?${serialized}`); + +sp.delete('a'); +values.forEach((i) => sp.append('a', i)); +assert.strictEqual(m.toString(), `http://example.org/?${serialized}`); + +sp.delete('a'); +values.forEach((i) => sp.append('a', i)); +assert.strictEqual(m.toJSON(), `http://example.org/?${serialized}`); + +sp.delete('a'); +values.forEach((i) => sp.append('a', i)); +m.href = 'http://example.org'; +assert.strictEqual(m.href, 'http://example.org/'); +assert.strictEqual(sp.size, 0); + +sp.delete('a'); +values.forEach((i) => sp.append('a', i)); +m.search = ''; +assert.strictEqual(m.href, 'http://example.org/'); +assert.strictEqual(sp.size, 0); + +sp.delete('a'); +values.forEach((i) => sp.append('a', i)); +m.pathname = '/test'; +assert.strictEqual(m.href, `http://example.org/test?${serialized}`); +m.pathname = ''; + +sp.delete('a'); +values.forEach((i) => sp.append('a', i)); +m.hash = '#test'; +assert.strictEqual(m.href, `http://example.org/?${serialized}#test`); +m.hash = ''; + +assert.strictEqual(sp[Symbol.iterator], sp.entries); + +let key, val; +let n = 0; +for ([key, val] of sp) { + assert.strictEqual(key, 'a', n); + assert.strictEqual(val, normalizedValues[n], n); + n++; +} +n = 0; +for (key of sp.keys()) { + assert.strictEqual(key, 'a', n); + n++; +} +n = 0; +for (val of sp.values()) { + assert.strictEqual(val, normalizedValues[n], n); + n++; +} +n = 0; +sp.forEach(function(val, key, obj) { + assert.strictEqual(this, undefined, n); + assert.strictEqual(key, 'a', n); + assert.strictEqual(val, normalizedValues[n], n); + assert.strictEqual(obj, sp, n); + n++; +}); +sp.forEach(function() { + assert.strictEqual(this, m); +}, m); + +{ + const callbackErr = { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + }; + assert.throws(() => sp.forEach(), callbackErr); + assert.throws(() => sp.forEach(1), callbackErr); +} + +m.search = '?a=a&b=b'; +assert.strictEqual(sp.toString(), 'a=a&b=b'); + +const tests = require(fixtures.path('url-searchparams.js')); + +for (const [input, expected, parsed] of tests) { + if (input[0] !== '?') { + const sp = new URLSearchParams(input); + assert.strictEqual(String(sp), expected); + assert.deepStrictEqual(Array.from(sp), parsed); + + m.search = input; + assert.strictEqual(String(m.searchParams), expected); + assert.deepStrictEqual(Array.from(m.searchParams), parsed); + } + + { + const sp = new URLSearchParams(`?${input}`); + assert.strictEqual(String(sp), expected); + assert.deepStrictEqual(Array.from(sp), parsed); + + m.search = `?${input}`; + assert.strictEqual(String(m.searchParams), expected); + assert.deepStrictEqual(Array.from(m.searchParams), parsed); + } +} diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-setters.js b/test/js/node/test/parallel/test-whatwg-url-custom-setters.js new file mode 100644 index 0000000000..b98bf5d8d3 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-setters.js @@ -0,0 +1,60 @@ +'use strict'; + +// Tests below are not from WPT. + +const common = require('../common'); +if (!common.hasIntl) { + // A handful of the tests fail when ICU is not included. + common.skip('missing Intl'); +} + +const assert = require('assert'); +const { test, assert_equals } = require('../common/wpt').harness; +const fixtures = require('../common/fixtures'); + +// TODO(joyeecheung): we should submit these to the upstream +const additionalTestCases = + require(fixtures.path('url-setter-tests-additional.js')); + +{ + for (const attributeToBeSet in additionalTestCases) { + if (attributeToBeSet === 'comment') { + continue; + } + const testCases = additionalTestCases[attributeToBeSet]; + for (const testCase of testCases) { + let name = `Setting <${testCase.href}>.${attributeToBeSet}` + + ` = "${testCase.new_value}"`; + if ('comment' in testCase) { + name += ` ${testCase.comment}`; + } + test(function() { + const url = new URL(testCase.href); + url[attributeToBeSet] = testCase.new_value; + for (const attribute in testCase.expected) { + assert_equals(url[attribute], testCase.expected[attribute]); + } + }, `URL: ${name}`); + } + } +} + +{ + const url = new URL('http://example.com/'); + const obj = { + toString() { throw new Error('toString'); }, + valueOf() { throw new Error('valueOf'); } + }; + const sym = Symbol(); + const props = Object.getOwnPropertyDescriptors(Object.getPrototypeOf(url)); + for (const [name, { set }] of Object.entries(props)) { + if (set) { + assert.throws(() => url[name] = obj, + /^Error: toString$/, + `url.${name} = { toString() { throw ... } }`); + assert.throws(() => url[name] = sym, + /^TypeError: Cannot convert a Symbol value to a string$/, + `url.${name} = ${String(sym)}`); + } + } +} diff --git a/test/js/node/test/parallel/test-whatwg-url-custom-tostringtag.js b/test/js/node/test/parallel/test-whatwg-url-custom-tostringtag.js new file mode 100644 index 0000000000..54e5850a8f --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-custom-tostringtag.js @@ -0,0 +1,32 @@ +'use strict'; + +// Tests below are not from WPT. + +require('../common'); +const assert = require('assert'); + +const toString = Object.prototype.toString; + +const url = new URL('http://example.org'); +const sp = url.searchParams; +const spIterator = sp.entries(); + +const test = [ + [url, 'URL'], + [sp, 'URLSearchParams'], + [spIterator, 'URLSearchParams Iterator'], + // Web IDL spec says we have to return 'URLPrototype', but it is too + // expensive to implement; therefore, use Chrome's behavior for now, until + // spec is changed. + [Object.getPrototypeOf(url), 'URL'], + [Object.getPrototypeOf(sp), 'URLSearchParams'], + [Object.getPrototypeOf(spIterator), 'URLSearchParams Iterator'], +]; + +test.forEach(([obj, expected]) => { + assert.strictEqual(obj[Symbol.toStringTag], expected, + `${obj[Symbol.toStringTag]} !== ${expected}`); + const str = toString.call(obj); + assert.strictEqual(str, `[object ${expected}]`, + `${str} !== [object ${expected}]`); +}); diff --git a/test/js/node/test/parallel/test-whatwg-url-override-hostname.js b/test/js/node/test/parallel/test-whatwg-url-override-hostname.js new file mode 100644 index 0000000000..61e3412c6b --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-override-hostname.js @@ -0,0 +1,20 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +{ + const url = new (class extends URL { get hostname() { return 'bar.com'; } })('http://foo.com/'); + assert.strictEqual(url.href, 'http://foo.com/'); + assert.strictEqual(url.toString(), 'http://foo.com/'); + assert.strictEqual(url.toJSON(), 'http://foo.com/'); + assert.strictEqual(url.hash, ''); + assert.strictEqual(url.host, 'foo.com'); + assert.strictEqual(url.hostname, 'bar.com'); + assert.strictEqual(url.origin, 'http://foo.com'); + assert.strictEqual(url.password, ''); + assert.strictEqual(url.protocol, 'http:'); + assert.strictEqual(url.username, ''); + assert.strictEqual(url.search, ''); + assert.strictEqual(url.searchParams.toString(), ''); +} diff --git a/test/js/node/test/parallel/test-whatwg-url-toascii.js b/test/js/node/test/parallel/test-whatwg-url-toascii.js new file mode 100644 index 0000000000..e5180bfb34 --- /dev/null +++ b/test/js/node/test/parallel/test-whatwg-url-toascii.js @@ -0,0 +1,86 @@ +'use strict'; +const common = require('../common'); +if (!common.hasIntl) { + // A handful of the tests fail when ICU is not included. + common.skip('missing Intl'); +} + +const fixtures = require('../common/fixtures'); +const { test, assert_equals, assert_throws } = require('../common/wpt').harness; + +const request = { + response: require( + fixtures.path('wpt', 'url', 'resources', 'toascii.json') + ) +}; + +// The following tests are copied from WPT. Modifications to them should be +// upstreamed first. +// Refs: https://github.com/w3c/web-platform-tests/blob/4839a0a804/url/toascii.window.js +// License: http://www.w3.org/Consortium/Legal/2008/04-testsuite-copyright.html + +/* eslint-disable */ +// async_test(t => { +// const request = new XMLHttpRequest() +// request.open("GET", "toascii.json") +// request.send() +// request.responseType = "json" +// request.onload = t.step_func_done(() => { + runTests(request.response) +// }) +// }, "Loading data…") + +function makeURL(type, input) { + input = "https://" + input + "/x" + if(type === "url") { + return new URL(input) + } else { + const url = document.createElement(type) + url.href = input + return url + } +} + +function runTests(tests) { + for(var i = 0, l = tests.length; i < l; i++) { + let hostTest = tests[i] + if (typeof hostTest === "string") { + continue // skip comments + } + const typeName = { "url": "URL", "a": "", "area": "" } + // ;["url", "a", "area"].forEach((type) => { + ;["url"].forEach((type) => { + test(() => { + if(hostTest.output !== null) { + const url = makeURL("url", hostTest.input) + assert_equals(url.host, hostTest.output) + assert_equals(url.hostname, hostTest.output) + assert_equals(url.pathname, "/x") + assert_equals(url.href, "https://" + hostTest.output + "/x") + } else { + if(type === "url") { + assert_throws(new TypeError, () => makeURL("url", hostTest.input)) + } else { + const url = makeURL(type, hostTest.input) + assert_equals(url.host, "") + assert_equals(url.hostname, "") + assert_equals(url.pathname, "") + assert_equals(url.href, "https://" + hostTest.input + "/x") + } + } + }, hostTest.input + " (using " + typeName[type] + ")") + ;["host", "hostname"].forEach((val) => { + test(() => { + const url = makeURL(type, "x") + url[val] = hostTest.input + if(hostTest.output !== null) { + assert_equals(url[val], hostTest.output) + } else { + assert_equals(url[val], "x") + } + }, hostTest.input + " (using " + typeName[type] + "." + val + ")") + }) + }) + } +} +/* eslint-enable */ diff --git a/test/js/node/test/parallel/test-windows-abort-exitcode.js b/test/js/node/test/parallel/test-windows-abort-exitcode.js new file mode 100644 index 0000000000..e2e6570c1c --- /dev/null +++ b/test/js/node/test/parallel/test-windows-abort-exitcode.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +if (!common.isWindows) + common.skip('test is windows specific'); + +const assert = require('assert'); +const spawn = require('child_process').spawn; + +// This test makes sure that an aborted node process +// exits with code 3 on Windows. +// Spawn a child, force an abort, and then check the +// exit code in the parent. + +if (process.argv[2] === 'child') { + process.abort(); +} else { + const child = spawn(process.execPath, [__filename, 'child']); + child.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, 134); + assert.strictEqual(signal, null); + })); +} diff --git a/test/js/node/test/parallel/test-windows-failed-heap-allocation.js b/test/js/node/test/parallel/test-windows-failed-heap-allocation.js new file mode 100644 index 0000000000..be901b7dc2 --- /dev/null +++ b/test/js/node/test/parallel/test-windows-failed-heap-allocation.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that an out of memory error exits with code 134 on Windows + +if (!common.isWindows) return common.skip('Windows-only'); + +const assert = require('assert'); +const { exec } = require('child_process'); + +if (process.argv[2] === 'heapBomb') { + // Heap bomb, imitates a memory leak quickly + const fn = (nM) => [...Array(nM)].map((i) => fn(nM * 2)); + fn(2); +} + +// Run child in tmpdir to avoid report files in repo +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// --max-old-space-size=3 is the min 'old space' in V8, explodes fast +const cmd = `"${process.execPath}" --max-old-space-size=30 "${__filename}"`; +exec(`${cmd} heapBomb`, { cwd: tmpdir.path }, common.mustCall((err, stdout, stderr) => { + const msg = `Wrong exit code of ${err.code}! Expected 134 for abort`; + // Note: common.nodeProcessAborted() is not asserted here because it + // returns true on 134 as well as 0x80000003 (V8's base::OS::Abort) + assert.strictEqual(err.code, 134, msg); +})); diff --git a/test/js/node/test/parallel/test-worker-arraybuffer-zerofill.js b/test/js/node/test/parallel/test-worker-arraybuffer-zerofill.js new file mode 100644 index 0000000000..3dcf4c006e --- /dev/null +++ b/test/js/node/test/parallel/test-worker-arraybuffer-zerofill.js @@ -0,0 +1,33 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// Make sure that allocating uninitialized ArrayBuffers in one thread does not +// affect the zero-initialization in other threads. + +const w = new Worker(` +const { parentPort } = require('worker_threads'); + +function post() { + const uint32array = new Uint32Array(64); + parentPort.postMessage(uint32array.reduce((a, b) => a + b)); +} + +setInterval(post, 0); +`, { eval: true }); + +function allocBuffers() { + Buffer.allocUnsafe(32 * 1024 * 1024); +} + +const interval = setInterval(allocBuffers, 0); + +let messages = 0; +w.on('message', (sum) => { + assert.strictEqual(sum, 0); + if (messages++ === 100) { + clearInterval(interval); + w.terminate(); + } +}); diff --git a/test/js/node/test/parallel/test-worker-cjs-workerdata.js b/test/js/node/test/parallel/test-worker-cjs-workerdata.js new file mode 100644 index 0000000000..949011fee8 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-cjs-workerdata.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +const workerData = 'Hello from main thread'; + +const worker = new Worker(fixtures.path('worker-data.cjs'), { + workerData +}); + +worker.on('message', common.mustCall((message) => { + assert.strictEqual(message, workerData); +})); diff --git a/test/js/node/test/parallel/test-worker-cleanexit-with-js.js b/test/js/node/test/parallel/test-worker-cleanexit-with-js.js new file mode 100644 index 0000000000..b4725e297a --- /dev/null +++ b/test/js/node/test/parallel/test-worker-cleanexit-with-js.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); + +// Harden the thread interactions on the exit path. +// Ensure workers are able to bail out safe at +// arbitrary execution points. By running a lot of +// JS code in a tight loop, the expectation +// is that those will be at various control flow points +// preferably in the JS land. + +const { Worker } = require('worker_threads'); +const code = 'setInterval(() => {' + + "require('v8').deserialize(require('v8').serialize({ foo: 'bar' }));" + + "require('vm').runInThisContext('x = \"foo\";');" + + "eval('const y = \"vm\";');}, 10);"; +for (let i = 0; i < 9; i++) { + new Worker(code, { eval: true }); +} +new Worker(code, { eval: true }).on('online', common.mustCall((msg) => { + process.exit(0); +})); diff --git a/test/js/node/test/parallel/test-worker-cleanexit-with-moduleload.js b/test/js/node/test/parallel/test-worker-cleanexit-with-moduleload.js new file mode 100644 index 0000000000..f2e8ad786f --- /dev/null +++ b/test/js/node/test/parallel/test-worker-cleanexit-with-moduleload.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); + +// Harden the thread interactions on the exit path. +// Ensure workers are able to bail out safe at +// arbitrary execution points. By using a number of +// internal modules as load candidates, the expectation +// is that those will be at various control flow points +// preferably in the C++ land. + +const { Worker } = require('worker_threads'); +const modules = [ 'fs', 'assert', 'async_hooks', 'buffer', 'child_process', + 'net', 'http', 'os', 'path', 'v8', 'vm', +]; +if (common.hasCrypto) { + modules.push('https'); +} + +for (let i = 0; i < 10; i++) { + new Worker(`const modules = [${modules.map((m) => `'${m}'`)}];` + + 'modules.forEach((module) => {' + + 'const m = require(module);' + + '});', { eval: true }); +} + +// Allow workers to go live. +setTimeout(() => { + process.exit(0); +}, 200); diff --git a/test/js/node/test/parallel/test-worker-esm-exit.js b/test/js/node/test/parallel/test-worker-esm-exit.js new file mode 100644 index 0000000000..8c38faf3b7 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-esm-exit.js @@ -0,0 +1,11 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +const w = new Worker(fixtures.path('es-modules/import-process-exit.mjs')); +w.on('error', common.mustNotCall()); +w.on('exit', + common.mustCall((code) => assert.strictEqual(code, 42)) +); diff --git a/test/js/node/test/parallel/test-worker-esmodule.js b/test/js/node/test/parallel/test-worker-esmodule.js new file mode 100644 index 0000000000..e7f9bd7aea --- /dev/null +++ b/test/js/node/test/parallel/test-worker-esmodule.js @@ -0,0 +1,10 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +const w = new Worker(fixtures.path('worker-script.mjs')); +w.on('message', common.mustCall((message) => { + assert.strictEqual(message, 'Hello, world!'); +})); diff --git a/test/js/node/test/parallel/test-worker-exit-event-error.js b/test/js/node/test/parallel/test-worker-exit-event-error.js new file mode 100644 index 0000000000..e2427c7dff --- /dev/null +++ b/test/js/node/test/parallel/test-worker-exit-event-error.js @@ -0,0 +1,8 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +process.on('uncaughtException', common.mustCall()); + +new Worker('', { eval: true }) + .on('exit', common.mustCall(() => { throw new Error('foo'); })); diff --git a/test/js/node/test/parallel/test-worker-exit-from-uncaught-exception.js b/test/js/node/test/parallel/test-worker-exit-from-uncaught-exception.js new file mode 100644 index 0000000000..b9f8aeee97 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-exit-from-uncaught-exception.js @@ -0,0 +1,23 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// Check that `process.exit()` can be called inside a Worker from an uncaught +// exception handler. + +// Do not use isMainThread so that this test itself can be run inside a Worker. +if (!process.env.HAS_STARTED_WORKER) { + process.env.HAS_STARTED_WORKER = 1; + const w = new Worker(__filename); + w.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 42); + })); + return; +} + +process.on('uncaughtException', () => { + process.exit(42); +}); + +throw new Error(); diff --git a/test/js/node/test/parallel/test-worker-fs-stat-watcher.js b/test/js/node/test/parallel/test-worker-fs-stat-watcher.js new file mode 100644 index 0000000000..c648792af7 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-fs-stat-watcher.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../common'); +const { Worker, parentPort } = require('worker_threads'); +const fs = require('fs'); + +// Checks that terminating Workers does not crash the process if fs.watchFile() +// has active handles. + +// Do not use isMainThread so that this test itself can be run inside a Worker. +if (!process.env.HAS_STARTED_WORKER) { + process.env.HAS_STARTED_WORKER = 1; + const worker = new Worker(__filename); + worker.on('message', common.mustCall(() => worker.terminate())); +} else { + fs.watchFile(__filename, () => {}); + parentPort.postMessage('running'); +} diff --git a/test/js/node/test/parallel/test-worker-http2-generic-streams-terminate.js b/test/js/node/test/parallel/test-worker-http2-generic-streams-terminate.js new file mode 100644 index 0000000000..234697fb4d --- /dev/null +++ b/test/js/node/test/parallel/test-worker-http2-generic-streams-terminate.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const http2 = require('http2'); +const { Duplex } = require('stream'); +const { Worker, workerData } = require('worker_threads'); + +// Tests the interaction between terminating a Worker thread and running +// the native SetImmediate queue, which may attempt to perform multiple +// calls into JS even though one already terminates the Worker. + +if (!workerData) { + const counter = new Int32Array(new SharedArrayBuffer(4)); + const worker = new Worker(__filename, { workerData: { counter } }); + worker.on('exit', common.mustCall(() => { + assert.strictEqual(counter[0], 1); + })); +} else { + const { counter } = workerData; + + // Start two HTTP/2 connections. This will trigger write()s call from inside + // the SetImmediate queue. + for (let i = 0; i < 2; i++) { + http2.connect('http://localhost', { + createConnection() { + return new Duplex({ + write(chunk, enc, cb) { + Atomics.add(counter, 0, 1); + process.exit(); + }, + read() { } + }); + } + }); + } +} diff --git a/test/js/node/test/parallel/test-worker-invalid-workerdata.js b/test/js/node/test/parallel/test-worker-invalid-workerdata.js new file mode 100644 index 0000000000..08fd78bdc0 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-invalid-workerdata.js @@ -0,0 +1,14 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// This tests verifies that failing to serialize workerData does not keep +// the process alive. +// Refs: https://github.com/nodejs/node/issues/22736 + +assert.throws(() => { + new Worker('./worker.js', { + workerData: { fn: () => {} } + }); +}, /DataCloneError/); diff --git a/test/js/node/test/parallel/test-worker-load-file-with-extension-other-than-js.js b/test/js/node/test/parallel/test-worker-load-file-with-extension-other-than-js.js new file mode 100644 index 0000000000..5dca297576 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-load-file-with-extension-other-than-js.js @@ -0,0 +1,9 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +const { Worker } = require('worker_threads'); + +(common.mustCall(() => { + new Worker(fixtures.path('worker-script.ts')); +}))(); diff --git a/test/js/node/test/parallel/test-worker-message-port-infinite-message-loop.js b/test/js/node/test/parallel/test-worker-message-port-infinite-message-loop.js new file mode 100644 index 0000000000..0cd1cc0680 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-message-port-infinite-message-loop.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const { MessageChannel } = require('worker_threads'); + +// Make sure that an infinite asynchronous .on('message')/postMessage loop +// does not lead to a stack overflow and does not starve the event loop. +// We schedule timeouts both from before the .on('message') handler and +// inside of it, which both should run. + +const { port1, port2 } = new MessageChannel(); +let count = 0; +port1.on('message', () => { + if (count === 0) { + setTimeout(common.mustCall(() => { + port1.close(); + }), 0); + } + + port2.postMessage(0); + assert(count++ < 10000, `hit ${count} loop iterations`); +}); + +port2.postMessage(0); + +// This is part of the test -- the event loop should be available and not stall +// out due to the recursive .postMessage() calls. +setTimeout(common.mustCall(), 0); diff --git a/test/js/node/test/parallel/test-worker-message-port-transfer-terminate.js b/test/js/node/test/parallel/test-worker-message-port-transfer-terminate.js new file mode 100644 index 0000000000..dddf91e3f3 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-message-port-transfer-terminate.js @@ -0,0 +1,17 @@ +'use strict'; +require('../common'); +const { Worker, MessageChannel } = require('worker_threads'); + +// Check the interaction of calling .terminate() while transferring +// MessagePort objects; in particular, that it does not crash the process. + +for (let i = 0; i < 10; ++i) { + const w = new Worker( + "require('worker_threads').parentPort.on('message', () => {})", + { eval: true }); + setImmediate(() => { + const port = new MessageChannel().port1; + w.postMessage({ port }, [ port ]); + w.terminate(); + }); +} diff --git a/test/js/node/test/parallel/test-worker-message-port-wasm-module.js b/test/js/node/test/parallel/test-worker-message-port-wasm-module.js new file mode 100644 index 0000000000..b1aa522dc4 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-message-port-wasm-module.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const { Worker } = require('worker_threads'); +const wasmModule = new WebAssembly.Module(fixtures.readSync('simple.wasm')); + +const worker = new Worker(` +const { parentPort } = require('worker_threads'); +parentPort.once('message', ({ wasmModule }) => { + const instance = new WebAssembly.Instance(wasmModule); + parentPort.postMessage(instance.exports.add(10, 20)); +}); +`, { eval: true }); + +worker.once('message', common.mustCall((num) => assert.strictEqual(num, 30))); +worker.postMessage({ wasmModule }); diff --git a/test/js/node/test/parallel/test-worker-mjs-workerdata.js b/test/js/node/test/parallel/test-worker-mjs-workerdata.js new file mode 100644 index 0000000000..b0a65e2e80 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-mjs-workerdata.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +const workerData = 'Hello from main thread'; + +const worker = new Worker(fixtures.path('worker-data.mjs'), { + workerData +}); + +worker.on('message', common.mustCall((message) => { + assert.strictEqual(message, workerData); +})); diff --git a/test/js/node/test/parallel/test-worker-nested-on-process-exit.js b/test/js/node/test/parallel/test-worker-nested-on-process-exit.js new file mode 100644 index 0000000000..aa544fa328 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-nested-on-process-exit.js @@ -0,0 +1,20 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker, workerData } = require('worker_threads'); + +// Test that 'exit' events for nested Workers are not received when a Worker +// terminates itself through process.exit(). + +if (workerData === null) { + const nestedWorkerExitCounter = new Int32Array(new SharedArrayBuffer(4)); + const w = new Worker(__filename, { workerData: nestedWorkerExitCounter }); + w.on('exit', common.mustCall(() => { + assert.strictEqual(nestedWorkerExitCounter[0], 0); + })); +} else { + const nestedWorker = new Worker('setInterval(() => {}, 100)', { eval: true }); + // The counter should never be increased here. + nestedWorker.on('exit', () => workerData[0]++); + nestedWorker.on('online', () => process.exit()); +} diff --git a/test/js/node/test/parallel/test-worker-no-sab.js b/test/js/node/test/parallel/test-worker-no-sab.js new file mode 100644 index 0000000000..e96c987484 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-no-sab.js @@ -0,0 +1,21 @@ +// Flags: --enable-sharedarraybuffer-per-context + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// Regression test for https://github.com/nodejs/node/issues/39717. + +// Do not use isMainThread so that this test itself can be run inside a Worker. +if (!process.env.HAS_STARTED_WORKER) { + process.env.HAS_STARTED_WORKER = 1; + const w = new Worker(__filename); + + w.on('exit', common.mustCall((status) => { + assert.strictEqual(status, 2); + })); +} else { + process.exit(2); +} diff --git a/test/js/node/test/parallel/test-worker-non-fatal-uncaught-exception.js b/test/js/node/test/parallel/test-worker-non-fatal-uncaught-exception.js new file mode 100644 index 0000000000..01df55eec1 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-non-fatal-uncaught-exception.js @@ -0,0 +1,25 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// Check that `process._fatalException()` returns a boolean when run inside a +// worker. + +// Do not use isMainThread so that this test itself can be run inside a Worker. +if (!process.env.HAS_STARTED_WORKER) { + process.env.HAS_STARTED_WORKER = 1; + const w = new Worker(__filename); + w.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); + })); + return; +} + +process.once('uncaughtException', () => { + process.nextTick(() => { + assert.strictEqual(res, true); + }); +}); + +const res = process._fatalException(new Error()); diff --git a/test/js/node/test/parallel/test-worker-on-process-exit.js b/test/js/node/test/parallel/test-worker-on-process-exit.js new file mode 100644 index 0000000000..ec1c4affd1 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-on-process-exit.js @@ -0,0 +1,22 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { spawnSync } = require('child_process'); +const { Worker } = require('worker_threads'); + +// Test that 'exit' events for Workers are not received when the main thread +// terminates itself through process.exit(). + +if (process.argv[2] !== 'child') { + const { + stdout, stderr, status + } = spawnSync(process.execPath, [__filename, 'child'], { encoding: 'utf8' }); + assert.strictEqual(stderr, ''); + assert.strictEqual(stdout, ''); + assert.strictEqual(status, 0); +} else { + const nestedWorker = new Worker('setInterval(() => {}, 100)', { eval: true }); + // This console.log() should never fire. + nestedWorker.on('exit', () => console.log('exit event received')); + nestedWorker.on('online', () => process.exit()); +} diff --git a/test/js/node/test/parallel/test-worker-onmessage-not-a-function.js b/test/js/node/test/parallel/test-worker-onmessage-not-a-function.js new file mode 100644 index 0000000000..df07353075 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-onmessage-not-a-function.js @@ -0,0 +1,25 @@ +// When MessagePort.onmessage is set to a value that is not a function, the +// setter should call .unref() and .stop(), clearing a previous onmessage +// listener from holding the event loop open. This test confirms that +// functionality. + +'use strict'; +const common = require('../common'); +const { Worker, parentPort } = require('worker_threads'); + +// Do not use isMainThread so that this test itself can be run inside a Worker. +if (!process.env.HAS_STARTED_WORKER) { + process.env.HAS_STARTED_WORKER = 1; + const w = new Worker(__filename); + w.postMessage(2); +} else { + // .onmessage uses a setter. Set .onmessage to a function that ultimately + // should not be called. This will call .ref() and .start() which will keep + // the event loop open (and prevent this from exiting) if the subsequent + // assignment of a value to .onmessage doesn't call .unref() and .stop(). + parentPort.onmessage = common.mustNotCall(); + // Setting `onmessage` to a value that is not a function should clear the + // previous value and also should allow the event loop to exit. (In other + // words, this test should exit rather than run indefinitely.) + parentPort.onmessage = 'fhqwhgads'; +} diff --git a/test/js/node/test/parallel/test-worker-onmessage.js b/test/js/node/test/parallel/test-worker-onmessage.js new file mode 100644 index 0000000000..3ed10755ce --- /dev/null +++ b/test/js/node/test/parallel/test-worker-onmessage.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker, parentPort } = require('worker_threads'); + +// Do not use isMainThread so that this test itself can be run inside a Worker. +if (!process.env.HAS_STARTED_WORKER) { + process.env.HAS_STARTED_WORKER = 1; + const w = new Worker(__filename); + const expectation = [ 4, undefined, null ]; + const actual = []; + w.on('message', common.mustCall((message) => { + actual.push(message); + if (actual.length === expectation.length) { + assert.deepStrictEqual(expectation, actual); + w.terminate(); + } + }, expectation.length)); + w.postMessage(2); +} else { + parentPort.onmessage = common.mustCall((message) => { + parentPort.postMessage(message.data * 2); + parentPort.postMessage(undefined); + parentPort.postMessage(null); + }); +} diff --git a/test/js/node/test/parallel/test-worker-parent-port-ref.js b/test/js/node/test/parallel/test-worker-parent-port-ref.js new file mode 100644 index 0000000000..c1e79b9faa --- /dev/null +++ b/test/js/node/test/parallel/test-worker-parent-port-ref.js @@ -0,0 +1,24 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { isMainThread, parentPort, Worker } = require('worker_threads'); + +// This test makes sure that we manipulate the references of +// `parentPort` correctly so that any worker threads will +// automatically exit when there are no any other references. +{ + if (isMainThread) { + const worker = new Worker(__filename); + + worker.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); + }), 1); + + worker.on('online', common.mustCall()); + } else { + const messageCallback = () => {}; + parentPort.on('message', messageCallback); + // The thread won't exit if we don't make the 'message' listener off. + parentPort.off('message', messageCallback); + } +} diff --git a/test/js/node/test/parallel/test-worker-process-exit-async-module.js b/test/js/node/test/parallel/test-worker-process-exit-async-module.js new file mode 100644 index 0000000000..38d4ad74c7 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-process-exit-async-module.js @@ -0,0 +1,11 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// Regression for https://github.com/nodejs/node/issues/43182. +const w = new Worker(new URL('data:text/javascript,process.exit(1);await new Promise(()=>{ process.exit(2); })')); +w.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 1); +})); diff --git a/test/js/node/test/parallel/test-worker-ref-onexit.js b/test/js/node/test/parallel/test-worker-ref-onexit.js new file mode 100644 index 0000000000..24c940f8c8 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-ref-onexit.js @@ -0,0 +1,12 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +// Check that worker.unref() makes the 'exit' event not be emitted, if it is +// the only thing we would otherwise be waiting for. + +// Use `setInterval()` to make sure the worker is alive until the end of the +// event loop turn. +const w = new Worker('setInterval(() => {}, 100);', { eval: true }); +w.unref(); +w.on('exit', common.mustNotCall()); diff --git a/test/js/node/test/parallel/test-worker-ref.js b/test/js/node/test/parallel/test-worker-ref.js new file mode 100644 index 0000000000..645fc0fbad --- /dev/null +++ b/test/js/node/test/parallel/test-worker-ref.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +// Test that calling worker.unref() leads to 'beforeExit' being emitted, and +// that we can resurrect the worker using worker.ref() from there. + +const w = new Worker(` +const { parentPort } = require('worker_threads'); +parentPort.once('message', (msg) => { + parentPort.postMessage(msg); +}); +`, { eval: true }); + +process.once('beforeExit', common.mustCall(() => { + console.log('beforeExit'); + w.ref(); + w.postMessage({ hello: 'world' }); +})); + +w.once('message', common.mustCall((msg) => { + console.log('message', msg); +})); + +w.on('exit', common.mustCall(() => { + console.log('exit'); +})); + +w.unref(); diff --git a/test/js/node/test/parallel/test-worker-relative-path-double-dot.js b/test/js/node/test/parallel/test-worker-relative-path-double-dot.js new file mode 100644 index 0000000000..86707c1590 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-relative-path-double-dot.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('../common'); +const path = require('path'); +const assert = require('assert'); +const { Worker, isMainThread, parentPort } = require('worker_threads'); + +if (isMainThread) { + const cwdName = path.relative('../', '.'); + const relativePath = path.relative('.', __filename); + const w = new Worker(path.join('..', cwdName, relativePath)); + w.on('message', common.mustCall((message) => { + assert.strictEqual(message, 'Hello, world!'); + })); +} else { + parentPort.postMessage('Hello, world!'); +} diff --git a/test/js/node/test/parallel/test-worker-relative-path.js b/test/js/node/test/parallel/test-worker-relative-path.js new file mode 100644 index 0000000000..73dc5b3637 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-relative-path.js @@ -0,0 +1,14 @@ +'use strict'; +const common = require('../common'); +const path = require('path'); +const assert = require('assert'); +const { Worker, isMainThread, parentPort } = require('worker_threads'); + +if (isMainThread) { + const w = new Worker(`./${path.relative('.', __filename)}`); + w.on('message', common.mustCall((message) => { + assert.strictEqual(message, 'Hello, world!'); + })); +} else { + parentPort.postMessage('Hello, world!'); +} diff --git a/test/js/node/test/parallel/test-worker-safe-getters.js b/test/js/node/test/parallel/test-worker-safe-getters.js new file mode 100644 index 0000000000..69856659a5 --- /dev/null +++ b/test/js/node/test/parallel/test-worker-safe-getters.js @@ -0,0 +1,34 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const { Worker, isMainThread } = require('worker_threads'); + +if (isMainThread) { + const w = new Worker(__filename, { + stdin: true, + stdout: true, + stderr: true + }); + + const { stdin, stdout, stderr } = w; + + w.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); + + // `postMessage` should not throw after termination + // (this mimics the browser behavior). + w.postMessage('foobar'); + w.ref(); + w.unref(); + + // Sanity check. + assert.strictEqual(w.threadId, -1); + assert.strictEqual(w.stdin, stdin); + assert.strictEqual(w.stdout, stdout); + assert.strictEqual(w.stderr, stderr); + })); +} else { + process.exit(0); +} diff --git a/test/js/node/test/parallel/test-worker-sharedarraybuffer-from-worker-thread.js b/test/js/node/test/parallel/test-worker-sharedarraybuffer-from-worker-thread.js new file mode 100644 index 0000000000..ce8410f6dd --- /dev/null +++ b/test/js/node/test/parallel/test-worker-sharedarraybuffer-from-worker-thread.js @@ -0,0 +1,28 @@ +// Flags: --debug-arraybuffer-allocations +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// Regression test for https://github.com/nodejs/node/issues/28777 +// Make sure that SharedArrayBuffers and transferred ArrayBuffers created in +// Worker threads are accessible after the creating thread ended. + +for (const ctor of ['ArrayBuffer', 'SharedArrayBuffer']) { + const w = new Worker(` + const { parentPort } = require('worker_threads'); + const arrayBuffer = new ${ctor}(4); + parentPort.postMessage( + arrayBuffer, + '${ctor}' === 'SharedArrayBuffer' ? [] : [arrayBuffer]); + `, { eval: true }); + + let arrayBuffer; + w.once('message', common.mustCall((message) => arrayBuffer = message)); + w.once('exit', common.mustCall(() => { + assert.strictEqual(arrayBuffer.constructor.name, ctor); + const uint8array = new Uint8Array(arrayBuffer); + uint8array[0] = 42; + assert.deepStrictEqual(uint8array, new Uint8Array([42, 0, 0, 0])); + })); +} diff --git a/test/js/node/test/parallel/test-worker-terminate-nested.js b/test/js/node/test/parallel/test-worker-terminate-nested.js new file mode 100644 index 0000000000..3924528cea --- /dev/null +++ b/test/js/node/test/parallel/test-worker-terminate-nested.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +// Check that a Worker that's running another Worker can be terminated. + +const worker = new Worker(` +const { Worker, parentPort } = require('worker_threads'); +const worker = new Worker('setInterval(() => {}, 10);', { eval: true }); +worker.on('online', () => { + parentPort.postMessage({}); +}); +`, { eval: true }); + +worker.on('message', common.mustCall(() => worker.terminate())); diff --git a/test/js/node/test/parallel/test-worker-terminate-null-handler.js b/test/js/node/test/parallel/test-worker-terminate-null-handler.js new file mode 100644 index 0000000000..9db2e38b5c --- /dev/null +++ b/test/js/node/test/parallel/test-worker-terminate-null-handler.js @@ -0,0 +1,23 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +// Test that calling worker.terminate() if kHandler is null should return an +// empty promise that resolves to undefined, even when a callback is passed + +const worker = new Worker(` +const { parentPort } = require('worker_threads'); +parentPort.postMessage({ hello: 'world' }); +`, { eval: true }); + +process.once('beforeExit', common.mustCall(() => worker.ref())); + +worker.on('exit', common.mustCall(() => { + worker.terminate().then((res) => assert.strictEqual(res, undefined)); + worker.terminate(() => null).then( + (res) => assert.strictEqual(res, undefined) + ); +})); + +worker.unref(); diff --git a/test/js/node/test/parallel/test-worker-terminate-timers.js b/test/js/node/test/parallel/test-worker-terminate-timers.js new file mode 100644 index 0000000000..62360a6cdb --- /dev/null +++ b/test/js/node/test/parallel/test-worker-terminate-timers.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +// Test that calling .terminate() during a timer callback works fine. + +for (const fn of ['setTimeout', 'setImmediate', 'setInterval']) { + const worker = new Worker(` + const { parentPort } = require('worker_threads'); + ${fn}(() => { + require('worker_threads').parentPort.postMessage({}); + while (true); + });`, { eval: true }); + + worker.on('message', common.mustCallAtLeast(() => { + worker.terminate(); + })); +} diff --git a/test/js/node/test/parallel/test-worker-unref-from-message-during-exit.js b/test/js/node/test/parallel/test-worker-unref-from-message-during-exit.js new file mode 100644 index 0000000000..3e696f369e --- /dev/null +++ b/test/js/node/test/parallel/test-worker-unref-from-message-during-exit.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('../common'); +const { Worker } = require('worker_threads'); + +// This used to crash because the `.unref()` was unexpected while the Worker +// was exiting. + +const w = new Worker(` +require('worker_threads').parentPort.postMessage({}); +`, { eval: true }); +w.on('message', common.mustCall(() => { + w.unref(); +})); + +// Wait a bit so that the 'message' event is emitted while the Worker exits. +Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 100); diff --git a/test/js/node/test/parallel/test-worker.js b/test/js/node/test/parallel/test-worker.js new file mode 100644 index 0000000000..9154aaa12b --- /dev/null +++ b/test/js/node/test/parallel/test-worker.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { Worker, isMainThread, parentPort } = require('worker_threads'); + +const kTestString = 'Hello, world!'; + +if (isMainThread) { + const w = new Worker(__filename); + w.on('message', common.mustCall((message) => { + assert.strictEqual(message, kTestString); + })); +} else { + setImmediate(() => { + process.nextTick(() => { + parentPort.postMessage(kTestString); + }); + }); +} diff --git a/test/js/node/test/parallel/test-worker.mjs b/test/js/node/test/parallel/test-worker.mjs new file mode 100644 index 0000000000..4ee3f7dc96 --- /dev/null +++ b/test/js/node/test/parallel/test-worker.mjs @@ -0,0 +1,18 @@ +import { mustCall } from '../common/index.mjs'; +import assert from 'assert'; +import { Worker, isMainThread, parentPort } from 'worker_threads'; + +const kTestString = 'Hello, world!'; + +if (isMainThread) { + const w = new Worker(new URL(import.meta.url)); + w.on('message', mustCall((message) => { + assert.strictEqual(message, kTestString); + })); +} else { + setImmediate(() => { + process.nextTick(() => { + parentPort.postMessage(kTestString); + }); + }); +} diff --git a/test/js/node/test/parallel/test-zlib-brotli-16GB.js b/test/js/node/test/parallel/test-zlib-brotli-16GB.js new file mode 100644 index 0000000000..9b894320e9 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-brotli-16GB.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +const { createBrotliDecompress } = require('node:zlib'); +const strictEqual = require('node:assert').strictEqual; +const { getDefaultHighWaterMark } = require('stream'); + +// This tiny HEX string is a 16GB file. +// This test verifies that the stream actually stops. +/* eslint-disable @stylistic/js/max-len */ +const content = 'cfffff7ff82700e2b14020f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c32200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bff3f'; + +const buf = Buffer.from(content, 'hex'); + +const decoder = createBrotliDecompress(); +decoder.end(buf); + +// We need to wait to verify that the libuv thread pool had time +// to process the data and the buffer is not empty. +setTimeout(common.mustCall(() => { + // There is only one chunk in the buffer + strictEqual(decoder._readableState.buffer.length, getDefaultHighWaterMark() / (16 * 1024)); +}), common.platformTimeout(500)); diff --git a/test/js/node/test/parallel/test-zlib-brotli-flush.js b/test/js/node/test/parallel/test-zlib-brotli-flush.js new file mode 100644 index 0000000000..fd730bfacd --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-brotli-flush.js @@ -0,0 +1,27 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const fixtures = require('../common/fixtures'); + +const file = fixtures.readSync('person.jpg'); +const chunkSize = 16; +const deflater = new zlib.BrotliCompress(); + +const chunk = file.slice(0, chunkSize); +const expectedFull = Buffer.from('iweA/9j/4AAQSkZJRgABAQEASA==', 'base64'); +let actualFull; + +deflater.write(chunk, function() { + deflater.flush(function() { + const bufs = []; + let buf; + while ((buf = deflater.read()) !== null) + bufs.push(buf); + actualFull = Buffer.concat(bufs); + }); +}); + +process.once('exit', function() { + assert.deepStrictEqual(actualFull, expectedFull); +}); diff --git a/test/js/node/test/parallel/test-zlib-brotli-from-brotli.js b/test/js/node/test/parallel/test-zlib-brotli-from-brotli.js new file mode 100644 index 0000000000..7e6bfb419e --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-brotli-from-brotli.js @@ -0,0 +1,31 @@ +'use strict'; +// Test unzipping a file that was created with a non-node brotli lib, +// piped in as fast as possible. + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const fixtures = require('../common/fixtures'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const decompress = new zlib.BrotliDecompress(); + +const fs = require('fs'); + +const fixture = fixtures.path('person.jpg.br'); +const unzippedFixture = fixtures.path('person.jpg'); +const outputFile = tmpdir.resolve('person.jpg'); +const expect = fs.readFileSync(unzippedFixture); +const inp = fs.createReadStream(fixture); +const out = fs.createWriteStream(outputFile); + +inp.pipe(decompress).pipe(out); +out.on('close', common.mustCall(() => { + const actual = fs.readFileSync(outputFile); + assert.strictEqual(actual.length, expect.length); + for (let i = 0, l = actual.length; i < l; i++) { + assert.strictEqual(actual[i], expect[i], `byte[${i}]`); + } +})); diff --git a/test/js/node/test/parallel/test-zlib-brotli-from-string.js b/test/js/node/test/parallel/test-zlib-brotli-from-string.js new file mode 100644 index 0000000000..30be44517a --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-brotli-from-string.js @@ -0,0 +1,38 @@ +'use strict'; +// Test compressing and uncompressing a string with brotli + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli' + + 't. Morbi faucibus, purus at gravida dictum, libero arcu ' + + 'convallis lacus, in commodo libero metus eu nisi. Nullam' + + ' commodo, neque nec porta placerat, nisi est fermentum a' + + 'ugue, vitae gravida tellus sapien sit amet tellus. Aenea' + + 'n non diam orci. Proin quis elit turpis. Suspendisse non' + + ' diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu' + + 'm arcu mi, sodales non suscipit id, ultrices ut massa. S' + + 'ed ac sem sit amet arcu malesuada fermentum. Nunc sed. '; +const compressedString = 'G/gBQBwHdky2aHV5KK9Snf05//1pPdmNw/7232fnIm1IB' + + 'K1AA8RsN8OB8Nb7Lpgk3UWWUlzQXZyHQeBBbXMTQXC1j7' + + 'wg3LJs9LqOGHRH2bj/a2iCTLLx8hBOyTqgoVuD1e+Qqdn' + + 'f1rkUNyrWq6LtOhWgxP3QUwdhKGdZm3rJWaDDBV7+pDk1' + + 'MIkrmjp4ma2xVi5MsgJScA3tP1I7mXeby6MELozrwoBQD' + + 'mVTnEAicZNj4lkGqntJe2qSnGyeMmcFgraK94vCg/4iLu' + + 'Tw5RhKhnVY++dZ6niUBmRqIutsjf5TzwF5iAg8a9UkjF5' + + '2eZ0tB2vo6v8SqVfNMkBmmhxr0NT9LkYF69aEjlYzj7IE' + + 'KmEUQf1HBogRYhFIt4ymRNEgHAIzOyNEsQM='; + +zlib.brotliCompress(inputString, common.mustCall((err, buffer) => { + assert(inputString.length > buffer.length); + + zlib.brotliDecompress(buffer, common.mustCall((err, buffer) => { + assert.strictEqual(buffer.toString(), inputString); + })); +})); + +const buffer = Buffer.from(compressedString, 'base64'); +zlib.brotliDecompress(buffer, common.mustCall((err, buffer) => { + assert.strictEqual(buffer.toString(), inputString); +})); diff --git a/test/js/node/test/parallel/test-zlib-brotli-kmaxlength-rangeerror.js b/test/js/node/test/parallel/test-zlib-brotli-kmaxlength-rangeerror.js new file mode 100644 index 0000000000..6a59ad34b0 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-brotli-kmaxlength-rangeerror.js @@ -0,0 +1,28 @@ +'use strict'; +require('../common'); + +// This test ensures that zlib throws a RangeError if the final buffer needs to +// be larger than kMaxLength and concatenation fails. +// https://github.com/nodejs/node/pull/1811 + +const assert = require('assert'); + +// Change kMaxLength for zlib to trigger the error without having to allocate +// large Buffers. +const buffer = require('buffer'); +const oldkMaxLength = buffer.kMaxLength; +buffer.kMaxLength = 64; +const zlib = require('zlib'); +buffer.kMaxLength = oldkMaxLength; + +const encoded = Buffer.from('G38A+CXCIrFAIAM=', 'base64'); + +// Async +zlib.brotliDecompress(encoded, function(err) { + assert.ok(err instanceof RangeError); +}); + +// Sync +assert.throws(function() { + zlib.brotliDecompressSync(encoded); +}, RangeError); diff --git a/test/js/node/test/parallel/test-zlib-brotli.js b/test/js/node/test/parallel/test-zlib-brotli.js new file mode 100644 index 0000000000..ef31db3dd6 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-brotli.js @@ -0,0 +1,94 @@ +'use strict'; +require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const zlib = require('zlib'); + +// Test some brotli-specific properties of the brotli streams that can not +// be easily covered through expanding zlib-only tests. + +const sampleBuffer = fixtures.readSync('/pss-vectors.json'); + +{ + // Test setting the quality parameter at stream creation: + const sizes = []; + for (let quality = zlib.constants.BROTLI_MIN_QUALITY; + quality <= zlib.constants.BROTLI_MAX_QUALITY; + quality++) { + const encoded = zlib.brotliCompressSync(sampleBuffer, { + params: { + [zlib.constants.BROTLI_PARAM_QUALITY]: quality + } + }); + sizes.push(encoded.length); + } + + // Increasing quality should roughly correspond to decreasing compressed size: + for (let i = 0; i < sizes.length - 1; i++) { + assert(sizes[i + 1] <= sizes[i] * 1.05, sizes); // 5 % margin of error. + } + assert(sizes[0] > sizes[sizes.length - 1], sizes); +} + +{ + // Test that setting out-of-bounds option values or keys fails. + assert.throws(() => { + zlib.createBrotliCompress({ + params: { + 10000: 0 + } + }); + }, { + code: 'ERR_BROTLI_INVALID_PARAM', + name: 'RangeError', + message: '10000 is not a valid Brotli parameter' + }); + + // Test that accidentally using duplicate keys fails. + assert.throws(() => { + zlib.createBrotliCompress({ + params: { + '0': 0, + '00': 0 + } + }); + }, { + code: 'ERR_BROTLI_INVALID_PARAM', + name: 'RangeError', + message: '00 is not a valid Brotli parameter' + }); + + assert.throws(() => { + zlib.createBrotliCompress({ + params: { + // This is a boolean flag + [zlib.constants.BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING]: 42 + } + }); + }, { + code: 'ERR_ZLIB_INITIALIZATION_FAILED', + name: 'Error', + message: 'Initialization failed' + }); +} + +{ + // Test options.flush range + assert.throws(() => { + zlib.brotliCompressSync('', { flush: zlib.constants.Z_FINISH }); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.flush" is out of range. It must be >= 0 ' + + 'and <= 3. Received 4', + }); + + assert.throws(() => { + zlib.brotliCompressSync('', { finishFlush: zlib.constants.Z_FINISH }); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.finishFlush" is out of range. It must be ' + + '>= 0 and <= 3. Received 4', + }); +} diff --git a/test/js/node/test/parallel/test-zlib-close-after-error.js b/test/js/node/test/parallel/test-zlib-close-after-error.js new file mode 100644 index 0000000000..63d418be09 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-close-after-error.js @@ -0,0 +1,16 @@ +'use strict'; +// https://github.com/nodejs/node/issues/6034 + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const decompress = zlib.createGunzip(15); + +decompress.on('error', common.mustCall((err) => { + assert.strictEqual(decompress._closed, true); + decompress.close(); +})); + +assert.strictEqual(decompress._closed, false); +decompress.write('something invalid'); diff --git a/test/js/node/test/parallel/test-zlib-close-after-write.js b/test/js/node/test/parallel/test-zlib-close-after-write.js new file mode 100644 index 0000000000..211318dc5a --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-close-after-write.js @@ -0,0 +1,30 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); + +zlib.gzip('hello', common.mustCall((err, out) => { + const unzip = zlib.createGunzip(); + unzip.write(out); + unzip.close(common.mustCall()); +})); diff --git a/test/js/node/test/parallel/test-zlib-close-in-ondata.js b/test/js/node/test/parallel/test-zlib-close-in-ondata.js new file mode 100644 index 0000000000..44d996311d --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-close-in-ondata.js @@ -0,0 +1,10 @@ +'use strict'; + +const common = require('../common'); +const zlib = require('zlib'); + +const ts = zlib.createGzip(); +const buf = Buffer.alloc(1024 * 1024 * 20); + +ts.on('data', common.mustCall(() => ts.close())); +ts.end(buf); diff --git a/test/js/node/test/parallel/test-zlib-const.js b/test/js/node/test/parallel/test-zlib-const.js new file mode 100644 index 0000000000..342c8c712a --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-const.js @@ -0,0 +1,38 @@ +/* eslint-disable strict */ +require('../common'); +const assert = require('assert'); + +const zlib = require('zlib'); + +assert.strictEqual(zlib.constants.Z_OK, 0, + [ + 'Expected Z_OK to be 0;', + `got ${zlib.constants.Z_OK}`, + ].join(' ')); +zlib.constants.Z_OK = 1; +assert.strictEqual(zlib.constants.Z_OK, 0, + [ + 'Z_OK should be immutable.', + `Expected to get 0, got ${zlib.constants.Z_OK}`, + ].join(' ')); + +assert.strictEqual(zlib.codes.Z_OK, 0, + `Expected Z_OK to be 0; got ${zlib.codes.Z_OK}`); +zlib.codes.Z_OK = 1; +assert.strictEqual(zlib.codes.Z_OK, 0, + [ + 'Z_OK should be immutable.', + `Expected to get 0, got ${zlib.codes.Z_OK}`, + ].join(' ')); +zlib.codes = { Z_OK: 1 }; +assert.strictEqual(zlib.codes.Z_OK, 0, + [ + 'Z_OK should be immutable.', + `Expected to get 0, got ${zlib.codes.Z_OK}`, + ].join(' ')); + +assert.ok(Object.isFrozen(zlib.codes), + [ + 'Expected zlib.codes to be frozen, but Object.isFrozen', + `returned ${Object.isFrozen(zlib.codes)}`, + ].join(' ')); diff --git a/test/js/node/test/parallel/test-zlib-convenience-methods.js b/test/js/node/test/parallel/test-zlib-convenience-methods.js new file mode 100644 index 0000000000..01ec7e211b --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-convenience-methods.js @@ -0,0 +1,133 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Test convenience methods with and without options supplied + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +// Must be a multiple of 4 characters in total to test all ArrayBufferView +// types. +const expectStr = 'blah'.repeat(8); +const expectBuf = Buffer.from(expectStr); + +const opts = { + level: 9, + chunkSize: 1024, +}; + +const optsInfo = { + info: true +}; + +for (const [type, expect] of [ + ['string', expectStr], + ['Buffer', expectBuf], + ...common.getBufferSources(expectBuf).map((obj) => + [obj[Symbol.toStringTag], obj] + ), +]) { + for (const method of [ + ['gzip', 'gunzip', 'Gzip', 'Gunzip'], + ['gzip', 'unzip', 'Gzip', 'Unzip'], + ['deflate', 'inflate', 'Deflate', 'Inflate'], + ['deflateRaw', 'inflateRaw', 'DeflateRaw', 'InflateRaw'], + ['brotliCompress', 'brotliDecompress', + 'BrotliCompress', 'BrotliDecompress'], + ]) { + zlib[method[0]](expect, opts, common.mustCall((err, result) => { + zlib[method[1]](result, opts, common.mustCall((err, result) => { + assert.strictEqual(result.toString(), expectStr, + `Should get original string after ${method[0]}/` + + `${method[1]} ${type} with options.`); + })); + })); + + zlib[method[0]](expect, common.mustCall((err, result) => { + zlib[method[1]](result, common.mustCall((err, result) => { + assert.strictEqual(result.toString(), expectStr, + `Should get original string after ${method[0]}/` + + `${method[1]} ${type} without options.`); + })); + })); + + zlib[method[0]](expect, optsInfo, common.mustCall((err, result) => { + assert.ok(result.engine instanceof zlib[method[2]], + `Should get engine ${method[2]} after ${method[0]} ` + + `${type} with info option.`); + + const compressed = result.buffer; + zlib[method[1]](compressed, optsInfo, common.mustCall((err, result) => { + assert.strictEqual(result.buffer.toString(), expectStr, + `Should get original string after ${method[0]}/` + + `${method[1]} ${type} with info option.`); + assert.ok(result.engine instanceof zlib[method[3]], + `Should get engine ${method[3]} after ${method[0]} ` + + `${type} with info option.`); + })); + })); + + { + const compressed = zlib[`${method[0]}Sync`](expect, opts); + const decompressed = zlib[`${method[1]}Sync`](compressed, opts); + assert.strictEqual(decompressed.toString(), expectStr, + `Should get original string after ${method[0]}Sync/` + + `${method[1]}Sync ${type} with options.`); + } + + + { + const compressed = zlib[`${method[0]}Sync`](expect); + const decompressed = zlib[`${method[1]}Sync`](compressed); + assert.strictEqual(decompressed.toString(), expectStr, + `Should get original string after ${method[0]}Sync/` + + `${method[1]}Sync ${type} without options.`); + } + + + { + const compressed = zlib[`${method[0]}Sync`](expect, optsInfo); + assert.ok(compressed.engine instanceof zlib[method[2]], + `Should get engine ${method[2]} after ${method[0]} ` + + `${type} with info option.`); + const decompressed = zlib[`${method[1]}Sync`](compressed.buffer, + optsInfo); + assert.strictEqual(decompressed.buffer.toString(), expectStr, + `Should get original string after ${method[0]}Sync/` + + `${method[1]}Sync ${type} without options.`); + assert.ok(decompressed.engine instanceof zlib[method[3]], + `Should get engine ${method[3]} after ${method[0]} ` + + `${type} with info option.`); + } + } +} + +assert.throws( + () => zlib.gzip('abc'), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "callback" argument must be of type function. ' + + 'Received undefined' + } +); diff --git a/test/js/node/test/parallel/test-zlib-crc32.js b/test/js/node/test/parallel/test-zlib-crc32.js new file mode 100644 index 0000000000..fb8d4958ec --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-crc32.js @@ -0,0 +1,211 @@ +'use strict'; + +require('../common'); +const zlib = require('zlib'); +const assert = require('assert'); +const { Buffer } = require('buffer'); + +// The following test data comes from +// https://github.com/zlib-ng/zlib-ng/blob/5401b24/test/test_crc32.cc +// test_crc32.cc -- crc32 unit test +// Copyright (C) 2019-2021 IBM Corporation +// Authors: Rogerio Alves +// Matheus Castanho +// For conditions of distribution and use, see copyright notice in zlib.h +// +const tests = [ + [0x0, 0x0, 0, 0x0], + [0xffffffff, 0x0, 0, 0x0], + [0x0, 0x0, 255, 0x0], /* BZ 174799. */ + [0x0, 0x0, 256, 0x0], + [0x0, 0x0, 257, 0x0], + [0x0, 0x0, 32767, 0x0], + [0x0, 0x0, 32768, 0x0], + [0x0, 0x0, 32769, 0x0], + [0x0, '', 0, 0x0], + [0xffffffff, '', 0, 0xffffffff], + [0x0, 'abacus', 6, 0xc3d7115b], + [0x0, 'backlog', 7, 0x269205], + [0x0, 'campfire', 8, 0x22a515f8], + [0x0, 'delta', 5, 0x9643fed9], + [0x0, 'executable', 10, 0xd68eda01], + [0x0, 'file', 4, 0x8c9f3610], + [0x0, 'greatest', 8, 0xc1abd6cd], + [0x0, 'hello', 5, 0x3610a686], + [0x0, 'inverter', 8, 0xc9e962c9], + [0x0, 'jigsaw', 6, 0xce4e3f69], + [0x0, 'karate', 6, 0x890be0e2], + [0x0, 'landscape', 9, 0xc4e0330b], + [0x0, 'machine', 7, 0x1505df84], + [0x0, 'nanometer', 9, 0xd4e19f39], + [0x0, 'oblivion', 8, 0xdae9de77], + [0x0, 'panama', 6, 0x66b8979c], + [0x0, 'quest', 5, 0x4317f817], + [0x0, 'resource', 8, 0xbc91f416], + [0x0, 'secret', 6, 0x5ca2e8e5], + [0x0, 'test', 4, 0xd87f7e0c], + [0x0, 'ultimate', 8, 0x3fc79b0b], + [0x0, 'vector', 6, 0x1b6e485b], + [0x0, 'walrus', 6, 0xbe769b97], + [0x0, 'xeno', 4, 0xe7a06444], + [0x0, 'yelling', 7, 0xfe3944e5], + [0x0, 'zlib', 4, 0x73887d3a], + [0x0, '4BJD7PocN1VqX0jXVpWB', 20, 0xd487a5a1], + [0x0, 'F1rPWI7XvDs6nAIRx41l', 20, 0x61a0132e], + [0x0, 'ldhKlsVkPFOveXgkGtC2', 20, 0xdf02f76], + [0x0, '5KKnGOOrs8BvJ35iKTOS', 20, 0x579b2b0a], + [0x0, '0l1tw7GOcem06Ddu7yn4', 20, 0xf7d16e2d], + [0x0, 'MCr47CjPIn9R1IvE1Tm5', 20, 0x731788f5], + [0x0, 'UcixbzPKTIv0SvILHVdO', 20, 0x7112bb11], + [0x0, 'dGnAyAhRQDsWw0ESou24', 20, 0xf32a0dac], + [0x0, 'di0nvmY9UYMYDh0r45XT', 20, 0x625437bb], + [0x0, '2XKDwHfAhFsV0RhbqtvH', 20, 0x896930f9], + [0x0, 'ZhrANFIiIvRnqClIVyeD', 20, 0x8579a37], + [0x0, 'v7Q9ehzioTOVeDIZioT1', 20, 0x632aa8e0], + [0x0, 'Yod5hEeKcYqyhfXbhxj2', 20, 0xc829af29], + [0x0, 'GehSWY2ay4uUKhehXYb0', 20, 0x1b08b7e8], + [0x0, 'kwytJmq6UqpflV8Y8GoE', 20, 0x4e33b192], + [0x0, '70684206568419061514', 20, 0x59a179f0], + [0x0, '42015093765128581010', 20, 0xcd1013d7], + [0x0, '88214814356148806939', 20, 0xab927546], + [0x0, '43472694284527343838', 20, 0x11f3b20c], + [0x0, '49769333513942933689', 20, 0xd562d4ca], + [0x0, '54979784887993251199', 20, 0x233395f7], + [0x0, '58360544869206793220', 20, 0x2d167fd5], + [0x0, '27347953487840714234', 20, 0x8b5108ba], + [0x0, '07650690295365319082', 20, 0xc46b3cd8], + [0x0, '42655507906821911703', 20, 0xc10b2662], + [0x0, '29977409200786225655', 20, 0xc9a0f9d2], + [0x0, '85181542907229116674', 20, 0x9341357b], + [0x0, '87963594337989416799', 20, 0xf0424937], + [0x0, '21395988329504168551', 20, 0xd7c4c31f], + [0x0, '51991013580943379423', 20, 0xf11edcc4], + [0x0, '*]+@!);({_$;}[_},?{?;(_?,=-][@', 30, 0x40795df4], + [0x0, '_@:_).&(#.[:[{[:)$++-($_;@[)}+', 30, 0xdd61a631], + [0x0, '&[!,[$_==}+.]@!;*(+},[;:)$;)-@', 30, 0xca907a99], + [0x0, ']{.[.+?+[[=;[?}_#&;[=)__$$:+=_', 30, 0xf652deac], + [0x0, '-%.)=/[@].:.(:,()$;=%@-$?]{%+%', 30, 0xaf39a5a9], + [0x0, '+]#$(@&.=:,*];/.!]%/{:){:@(;)$', 30, 0x6bebb4cf], + // eslint-disable-next-line no-template-curly-in-string + [0x0, ')-._.:?[&:.=+}(*$/=!.${;(=$@!}', 30, 0x76430bac], + [0x0, ':(_*&%/[[}+,?#$&*+#[([*-/#;%(]', 30, 0x6c80c388], + [0x0, '{[#-;:$/{)(+[}#]/{&!%(@)%:@-$:', 30, 0xd54d977d], + [0x0, '_{$*,}(&,@.)):=!/%(&(,,-?$}}}!', 30, 0xe3966ad5], + [0x0, + 'e$98KNzqaV)Y:2X?]77].{gKRD4G5{mHZk,Z)SpU%L3FSgv!Wb8MLAFdi{+fp)c,@8m6v)yXg@]HBDFk?.4&}g5_udE*JHCiH=aL', + 100, 0xe7c71db9], + [0x0, + 'r*Fd}ef+5RJQ;+W=4jTR9)R*p!B;]Ed7tkrLi;88U7g@3v!5pk2X6D)vt,.@N8c]@yyEcKi[vwUu@.Ppm@C6%Mv*3Nw}Y,58_aH)', + 100, 0xeaa52777], + [0x0, + 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&', + 100, 0xcd472048], + [0x7a30360d, 'abacus', 6, 0xf8655a84], + [0x6fd767ee, 'backlog', 7, 0x1ed834b1], + [0xefeb7589, 'campfire', 8, 0x686cfca], + [0x61cf7e6b, 'delta', 5, 0x1554e4b1], + [0xdc712e2, 'executable', 10, 0x761b4254], + [0xad23c7fd, 'file', 4, 0x7abdd09b], + [0x85cb2317, 'greatest', 8, 0x4ba91c6b], + [0x9eed31b0, 'inverter', 8, 0xd5e78ba5], + [0xb94f34ca, 'jigsaw', 6, 0x23649109], + [0xab058a2, 'karate', 6, 0xc5591f41], + [0x5bff2b7a, 'landscape', 9, 0xf10eb644], + [0x605c9a5f, 'machine', 7, 0xbaa0a636], + [0x51bdeea5, 'nanometer', 9, 0x6af89afb], + [0x85c21c79, 'oblivion', 8, 0xecae222b], + [0x97216f56, 'panama', 6, 0x47dffac4], + [0x18444af2, 'quest', 5, 0x70c2fe36], + [0xbe6ce359, 'resource', 8, 0x1471d925], + [0x843071f1, 'secret', 6, 0x50c9a0db], + [0xf2480c60, 'ultimate', 8, 0xf973daf8], + [0x2d2feb3d, 'vector', 6, 0x344ac03d], + [0x7490310a, 'walrus', 6, 0x6d1408ef], + [0x97d247d4, 'xeno', 4, 0xe62670b5], + [0x93cf7599, 'yelling', 7, 0x1b36da38], + [0x73c84278, 'zlib', 4, 0x6432d127], + [0x228a87d1, '4BJD7PocN1VqX0jXVpWB', 20, 0x997107d0], + [0xa7a048d0, 'F1rPWI7XvDs6nAIRx41l', 20, 0xdc567274], + [0x1f0ded40, 'ldhKlsVkPFOveXgkGtC2', 20, 0xdcc63870], + [0xa804a62f, '5KKnGOOrs8BvJ35iKTOS', 20, 0x6926cffd], + [0x508fae6a, '0l1tw7GOcem06Ddu7yn4', 20, 0xb52b38bc], + [0xe5adaf4f, 'MCr47CjPIn9R1IvE1Tm5', 20, 0xf83b8178], + [0x67136a40, 'UcixbzPKTIv0SvILHVdO', 20, 0xc5213070], + [0xb00c4a10, 'dGnAyAhRQDsWw0ESou24', 20, 0xbc7648b0], + [0x2e0c84b5, 'di0nvmY9UYMYDh0r45XT', 20, 0xd8123a72], + [0x81238d44, '2XKDwHfAhFsV0RhbqtvH', 20, 0xd5ac5620], + [0xf853aa92, 'ZhrANFIiIvRnqClIVyeD', 20, 0xceae099d], + [0x5a692325, 'v7Q9ehzioTOVeDIZioT1', 20, 0xb07d2b24], + [0x3275b9f, 'Yod5hEeKcYqyhfXbhxj2', 20, 0x24ce91df], + [0x38371feb, 'GehSWY2ay4uUKhehXYb0', 20, 0x707b3b30], + [0xafc8bf62, 'kwytJmq6UqpflV8Y8GoE', 20, 0x16abc6a9], + [0x9b07db73, '70684206568419061514', 20, 0xae1fb7b7], + [0xe75b214, '42015093765128581010', 20, 0xd4eecd2d], + [0x72d0fe6f, '88214814356148806939', 20, 0x4660ec7], + [0xf857a4b1, '43472694284527343838', 20, 0xfd8afdf7], + [0x54b8e14, '49769333513942933689', 20, 0xc6d1b5f2], + [0xd6aa5616, '54979784887993251199', 20, 0x32476461], + [0x11e63098, '58360544869206793220', 20, 0xd917cf1a], + [0xbe92385, '27347953487840714234', 20, 0x4ad14a12], + [0x49511de0, '07650690295365319082', 20, 0xe37b5c6c], + [0x3db13bc1, '42655507906821911703', 20, 0x7cc497f1], + [0xbb899bea, '29977409200786225655', 20, 0x99781bb2], + [0xf6cd9436, '85181542907229116674', 20, 0x132256a1], + [0x9109e6c3, '87963594337989416799', 20, 0xbfdb2c83], + [0x75770fc, '21395988329504168551', 20, 0x8d9d1e81], + [0x69b1d19b, '51991013580943379423', 20, 0x7b6d4404], + [0xc6132975, '*]+@!);({_$;}[_},?{?;(_?,=-][@', 30, 0x8619f010], + [0xd58cb00c, '_@:_).&(#.[:[{[:)$++-($_;@[)}+', 30, 0x15746ac3], + [0xb63b8caa, '&[!,[$_==}+.]@!;*(+},[;:)$;)-@', 30, 0xaccf812f], + [0x8a45a2b8, ']{.[.+?+[[=;[?}_#&;[=)__$$:+=_', 30, 0x78af45de], + [0xcbe95b78, '-%.)=/[@].:.(:,()$;=%@-$?]{%+%', 30, 0x25b06b59], + [0x4ef8a54b, '+]#$(@&.=:,*];/.!]%/{:){:@(;)$', 30, 0x4ba0d08f], + // eslint-disable-next-line no-template-curly-in-string + [0x76ad267a, ')-._.:?[&:.=+}(*$/=!.${;(=$@!}', 30, 0xe26b6aac], + [0x569e613c, ':(_*&%/[[}+,?#$&*+#[([*-/#;%(]', 30, 0x7e2b0a66], + [0x36aa61da, '{[#-;:$/{)(+[}#]/{&!%(@)%:@-$:', 30, 0xb3430dc7], + [0xf67222df, '_{$*,}(&,@.)):=!/%(&(,,-?$}}}!', 30, 0x626c17a], + [0x74b34fd3, + 'e$98KNzqaV)Y:2X?]77].{gKRD4G5{mHZk,Z)SpU%L3FSgv!Wb8MLAFdi{+fp)c,@8m6v)yXg@]HBDFk?.4&}g5_udE*JHCiH=aL', + 100, 0xccf98060], + [0x351fd770, + 'r*Fd}ef+5RJQ;+W=4jTR9)R*p!B;]Ed7tkrLi;88U7g@3v!5pk2X6D)vt,.@N8c]@yyEcKi[vwUu@.Ppm@C6%Mv*3Nw}Y,58_aH)', + 100, 0xd8b95312], + [0xc45aef77, + 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&', + 100, 0xbb1c9912], + [0xc45aef77, + 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&' + + 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&' + + 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&' + + 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&' + + 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&' + + 'h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&', + 600, 0x888AFA5B], +]; + +for (const [ crc, data, len, expected ] of tests) { + if (data === 0) { + continue; + } + const buf = Buffer.from(data, 'utf8'); + assert.strictEqual(buf.length, len); + assert.strictEqual(zlib.crc32(buf, crc), expected, + `crc32('${data}', ${crc}) in buffer is not ${expected}`); + assert.strictEqual(zlib.crc32(buf.toString(), crc), expected, + `crc32('${data}', ${crc}) in string is not ${expected}`); + if (crc === 0) { + assert.strictEqual(zlib.crc32(buf), expected, + `crc32('${data}') in buffer is not ${expected}`); + assert.strictEqual(zlib.crc32(buf.toString()), expected, + `crc32('${data}') in string is not ${expected}`); + } +} + +[undefined, null, true, 1, () => {}, {}].forEach((invalid) => { + assert.throws(() => { zlib.crc32(invalid); }, { code: 'ERR_INVALID_ARG_TYPE' }); +}); + +[null, true, () => {}, {}].forEach((invalid) => { + assert.throws(() => { zlib.crc32('test', invalid); }, { code: 'ERR_INVALID_ARG_TYPE' }); +}); diff --git a/test/js/node/test/parallel/test-zlib-create-raw.js b/test/js/node/test/parallel/test-zlib-create-raw.js new file mode 100644 index 0000000000..92e21545e4 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-create-raw.js @@ -0,0 +1,15 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +{ + const inflateRaw = zlib.createInflateRaw(); + assert(inflateRaw instanceof zlib.InflateRaw); +} + +{ + const deflateRaw = zlib.createDeflateRaw(); + assert(deflateRaw instanceof zlib.DeflateRaw); +} diff --git a/test/js/node/test/parallel/test-zlib-deflate-raw-inherits.js b/test/js/node/test/parallel/test-zlib-deflate-raw-inherits.js new file mode 100644 index 0000000000..34bf31058a --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-deflate-raw-inherits.js @@ -0,0 +1,27 @@ +'use strict'; + +require('../common'); +const { DeflateRaw } = require('zlib'); +const { Readable } = require('stream'); + +// Validates that zlib.DeflateRaw can be inherited +// with Object.setPrototypeOf + +function NotInitialized(options) { + DeflateRaw.call(this, options); + this.prop = true; +} +Object.setPrototypeOf(NotInitialized.prototype, DeflateRaw.prototype); +Object.setPrototypeOf(NotInitialized, DeflateRaw); + +const dest = new NotInitialized(); + +const read = new Readable({ + read() { + this.push(Buffer.from('a test string')); + this.push(null); + } +}); + +read.pipe(dest); +dest.resume(); diff --git a/test/js/node/test/parallel/test-zlib-destroy-pipe.js b/test/js/node/test/parallel/test-zlib-destroy-pipe.js new file mode 100644 index 0000000000..67821a21b6 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-destroy-pipe.js @@ -0,0 +1,21 @@ +'use strict'; + +const common = require('../common'); +const zlib = require('zlib'); +const { Writable } = require('stream'); + +// Verify that the zlib transform does not error in case +// it is destroyed with data still in flight + +const ts = zlib.createGzip(); + +const ws = new Writable({ + write: common.mustCall((chunk, enc, cb) => { + setImmediate(cb); + ts.destroy(); + }) +}); + +const buf = Buffer.allocUnsafe(1024 * 1024 * 20); +ts.end(buf); +ts.pipe(ws); diff --git a/test/js/node/test/parallel/test-zlib-destroy.js b/test/js/node/test/parallel/test-zlib-destroy.js new file mode 100644 index 0000000000..775b7020b4 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-destroy.js @@ -0,0 +1,31 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const zlib = require('zlib'); + +// Verify that the zlib transform does clean up +// the handle when calling destroy. + +{ + const ts = zlib.createGzip(); + ts.destroy(); + assert.strictEqual(ts._handle, null); + + ts.on('close', common.mustCall(() => { + ts.close(common.mustCall()); + })); +} + +{ + // Ensure 'error' is only emitted once. + const decompress = zlib.createGunzip(15); + + decompress.on('error', common.mustCall((err) => { + decompress.close(); + })); + + decompress.write('something invalid'); + decompress.destroy(new Error('asd')); +} diff --git a/test/js/node/test/parallel/test-zlib-dictionary-fail.js b/test/js/node/test/parallel/test-zlib-dictionary-fail.js new file mode 100644 index 0000000000..9546954841 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-dictionary-fail.js @@ -0,0 +1,60 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +// String "test" encoded with dictionary "dict". +const input = Buffer.from([0x78, 0xBB, 0x04, 0x09, 0x01, 0xA5]); + +{ + const stream = zlib.createInflate(); + + stream.on('error', common.mustCall(function(err) { + assert.match(err.message, /Missing dictionary/); + })); + + stream.write(input); +} + +{ + const stream = zlib.createInflate({ dictionary: Buffer.from('fail') }); + + stream.on('error', common.mustCall(function(err) { + assert.match(err.message, /Bad dictionary/); + })); + + stream.write(input); +} + +{ + const stream = zlib.createInflateRaw({ dictionary: Buffer.from('fail') }); + + stream.on('error', common.mustCall(function(err) { + // It's not possible to separate invalid dict and invalid data when using + // the raw format + assert.match(err.message, /(invalid|Operation-Ending-Supplemental Code is 0x12)/); + })); + + stream.write(input); +} diff --git a/test/js/node/test/parallel/test-zlib-dictionary.js b/test/js/node/test/parallel/test-zlib-dictionary.js new file mode 100644 index 0000000000..49a01d5a03 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-dictionary.js @@ -0,0 +1,175 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Test compression/decompression with dictionary + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const spdyDict = Buffer.from([ + 'optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-', + 'languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi', + 'f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser', + '-agent10010120020120220320420520630030130230330430530630740040140240340440', + '5406407408409410411412413414415416417500501502503504505accept-rangesageeta', + 'glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic', + 'ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran', + 'sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati', + 'oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo', + 'ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe', + 'pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic', + 'ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1', + '.1statusversionurl\0', +].join('')); + +const input = [ + 'HTTP/1.1 200 Ok', + 'Server: node.js', + 'Content-Length: 0', + '', +].join('\r\n'); + +function basicDictionaryTest(spdyDict) { + let output = ''; + const deflate = zlib.createDeflate({ dictionary: spdyDict }); + const inflate = zlib.createInflate({ dictionary: spdyDict }); + inflate.setEncoding('utf-8'); + + deflate.on('data', function(chunk) { + inflate.write(chunk); + }); + + inflate.on('data', function(chunk) { + output += chunk; + }); + + deflate.on('end', function() { + inflate.end(); + }); + + inflate.on('end', common.mustCall(function() { + assert.strictEqual(input, output); + })); + + deflate.write(input); + deflate.end(); +} + +function deflateResetDictionaryTest(spdyDict) { + let doneReset = false; + let output = ''; + const deflate = zlib.createDeflate({ dictionary: spdyDict }); + const inflate = zlib.createInflate({ dictionary: spdyDict }); + inflate.setEncoding('utf-8'); + + deflate.on('data', function(chunk) { + if (doneReset) + inflate.write(chunk); + }); + + inflate.on('data', function(chunk) { + output += chunk; + }); + + deflate.on('end', function() { + inflate.end(); + }); + + inflate.on('end', common.mustCall(function() { + assert.strictEqual(input, output); + })); + + deflate.write(input); + deflate.flush(function() { + deflate.reset(); + doneReset = true; + deflate.write(input); + deflate.end(); + }); +} + +function rawDictionaryTest(spdyDict) { + let output = ''; + const deflate = zlib.createDeflateRaw({ dictionary: spdyDict }); + const inflate = zlib.createInflateRaw({ dictionary: spdyDict }); + inflate.setEncoding('utf-8'); + + deflate.on('data', function(chunk) { + inflate.write(chunk); + }); + + inflate.on('data', function(chunk) { + output += chunk; + }); + + deflate.on('end', function() { + inflate.end(); + }); + + inflate.on('end', common.mustCall(function() { + assert.strictEqual(input, output); + })); + + deflate.write(input); + deflate.end(); +} + +function deflateRawResetDictionaryTest(spdyDict) { + let doneReset = false; + let output = ''; + const deflate = zlib.createDeflateRaw({ dictionary: spdyDict }); + const inflate = zlib.createInflateRaw({ dictionary: spdyDict }); + inflate.setEncoding('utf-8'); + + deflate.on('data', function(chunk) { + if (doneReset) + inflate.write(chunk); + }); + + inflate.on('data', function(chunk) { + output += chunk; + }); + + deflate.on('end', function() { + inflate.end(); + }); + + inflate.on('end', common.mustCall(function() { + assert.strictEqual(input, output); + })); + + deflate.write(input); + deflate.flush(function() { + deflate.reset(); + doneReset = true; + deflate.write(input); + deflate.end(); + }); +} + +for (const dict of [spdyDict, ...common.getBufferSources(spdyDict)]) { + basicDictionaryTest(dict); + deflateResetDictionaryTest(dict); + rawDictionaryTest(dict); + deflateRawResetDictionaryTest(dict); +} diff --git a/test/js/node/test/parallel/test-zlib-empty-buffer.js b/test/js/node/test/parallel/test-zlib-empty-buffer.js new file mode 100644 index 0000000000..27fd1340fd --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-empty-buffer.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); +const { inspect, promisify } = require('util'); +const assert = require('assert'); +const emptyBuffer = Buffer.alloc(0); + +(async function() { + for (const [ compress, decompress, method ] of [ + [ zlib.deflateRawSync, zlib.inflateRawSync, 'raw sync' ], + [ zlib.deflateSync, zlib.inflateSync, 'deflate sync' ], + [ zlib.gzipSync, zlib.gunzipSync, 'gzip sync' ], + [ zlib.brotliCompressSync, zlib.brotliDecompressSync, 'br sync' ], + [ promisify(zlib.deflateRaw), promisify(zlib.inflateRaw), 'raw' ], + [ promisify(zlib.deflate), promisify(zlib.inflate), 'deflate' ], + [ promisify(zlib.gzip), promisify(zlib.gunzip), 'gzip' ], + [ promisify(zlib.brotliCompress), promisify(zlib.brotliDecompress), 'br' ], + ]) { + const compressed = await compress(emptyBuffer); + const decompressed = await decompress(compressed); + assert.deepStrictEqual( + emptyBuffer, decompressed, + `Expected ${inspect(compressed)} to match ${inspect(decompressed)} ` + + `to match for ${method}`); + } +})().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-zlib-flush-drain-longblock.js b/test/js/node/test/parallel/test-zlib-flush-drain-longblock.js new file mode 100644 index 0000000000..e2f56ec762 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-flush-drain-longblock.js @@ -0,0 +1,27 @@ +'use strict'; + +// Regression test for https://github.com/nodejs/node/issues/14523. +// Checks that flushes interact properly with writableState.needDrain, +// even if no flush callback was passed. + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const zipper = zlib.createGzip({ highWaterMark: 16384 }); +const unzipper = zlib.createGunzip(); +zipper.pipe(unzipper); + +zipper.write('A'.repeat(17000)); +zipper.flush(); + +let received = 0; +unzipper.on('data', common.mustCallAtLeast((d) => { + received += d.length; +}, 2)); + +// Properly `.end()`ing the streams would interfere with checking that +// `.flush()` works. +process.on('exit', () => { + assert.strictEqual(received, 17000); +}); diff --git a/test/js/node/test/parallel/test-zlib-flush-drain.js b/test/js/node/test/parallel/test-zlib-flush-drain.js new file mode 100644 index 0000000000..6993d2c9fe --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-flush-drain.js @@ -0,0 +1,51 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const bigData = Buffer.alloc(10240, 'x'); + +const opts = { + level: 0, + highWaterMark: 16 +}; + +const deflater = zlib.createDeflate(opts); + +// Shim deflater.flush so we can count times executed +let flushCount = 0; +let drainCount = 0; + +const flush = deflater.flush; +deflater.flush = function(kind, callback) { + flushCount++; + flush.call(this, kind, callback); +}; + +deflater.write(bigData); + +const ws = deflater._writableState; +const beforeFlush = ws.needDrain; +let afterFlush = ws.needDrain; + +deflater.on('data', () => { +}); + +deflater.flush(function(err) { + afterFlush = ws.needDrain; +}); + +deflater.on('drain', function() { + drainCount++; +}); + +process.once('exit', function() { + assert.strictEqual( + beforeFlush, true); + assert.strictEqual( + afterFlush, false); + assert.strictEqual( + drainCount, 1); + assert.strictEqual( + flushCount, 1); +}); diff --git a/test/js/node/test/parallel/test-zlib-flush-write-sync-interleaved.js b/test/js/node/test/parallel/test-zlib-flush-write-sync-interleaved.js new file mode 100644 index 0000000000..f8387f4006 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-flush-write-sync-interleaved.js @@ -0,0 +1,57 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { createGzip, createGunzip, Z_PARTIAL_FLUSH } = require('zlib'); + +// Verify that .flush() behaves like .write() in terms of ordering, e.g. in +// a sequence like .write() + .flush() + .write() + .flush() each .flush() call +// only affects the data written before it. +// Refs: https://github.com/nodejs/node/issues/28478 + +const compress = createGzip(); +const decompress = createGunzip(); +decompress.setEncoding('utf8'); + +const events = []; +const compressedChunks = []; + +for (const chunk of ['abc', 'def', 'ghi']) { + compress.write(chunk, common.mustCall(() => events.push({ written: chunk }))); + compress.flush(Z_PARTIAL_FLUSH, common.mustCall(() => { + events.push('flushed'); + const chunk = compress.read(); + if (chunk !== null) + compressedChunks.push(chunk); + })); +} + +compress.end(common.mustCall(() => { + events.push('compress end'); + writeToDecompress(); +})); + +function writeToDecompress() { + // Write the compressed chunks to a decompressor, one by one, in order to + // verify that the flushes actually worked. + const chunk = compressedChunks.shift(); + if (chunk === undefined) return decompress.end(); + decompress.write(chunk, common.mustCall(() => { + events.push({ read: decompress.read() }); + writeToDecompress(); + })); +} + +process.on('exit', () => { + assert.deepStrictEqual(events, [ + { written: 'abc' }, + 'flushed', + { written: 'def' }, + 'flushed', + { written: 'ghi' }, + 'flushed', + 'compress end', + { read: 'abc' }, + { read: 'def' }, + { read: 'ghi' }, + ]); +}); diff --git a/test/js/node/test/parallel/test-zlib-flush.js b/test/js/node/test/parallel/test-zlib-flush.js new file mode 100644 index 0000000000..557775d509 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-flush.js @@ -0,0 +1,36 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const fixtures = require('../common/fixtures'); + +const file = fixtures.readSync('person.jpg'); +const chunkSize = 16; +const opts = { level: 0 }; +const deflater = zlib.createDeflate(opts); + +const chunk = file.slice(0, chunkSize); +const expectedNone = Buffer.from([0x78, 0x01]); +const blkhdr = Buffer.from([0x00, 0x10, 0x00, 0xef, 0xff]); +const adler32 = Buffer.from([0x00, 0x00, 0x00, 0xff, 0xff]); +const expectedFull = Buffer.concat([blkhdr, chunk, adler32]); +let actualNone; +let actualFull; + +deflater.write(chunk, function() { + deflater.flush(zlib.constants.Z_NO_FLUSH, function() { + actualNone = deflater.read(); + deflater.flush(function() { + const bufs = []; + let buf; + while ((buf = deflater.read()) !== null) + bufs.push(buf); + actualFull = Buffer.concat(bufs); + }); + }); +}); + +process.once('exit', function() { + assert.deepStrictEqual(actualNone, expectedNone); + assert.deepStrictEqual(actualFull, expectedFull); +}); diff --git a/test/js/node/test/parallel/test-zlib-from-concatenated-gzip.js b/test/js/node/test/parallel/test-zlib-from-concatenated-gzip.js new file mode 100644 index 0000000000..1de36dacf9 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-from-concatenated-gzip.js @@ -0,0 +1,83 @@ +'use strict'; +// Test unzipping a gzip file that contains multiple concatenated "members" + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +const abc = 'abc'; +const def = 'def'; + +const abcEncoded = zlib.gzipSync(abc); +const defEncoded = zlib.gzipSync(def); + +const data = Buffer.concat([ + abcEncoded, + defEncoded, +]); + +assert.strictEqual(zlib.gunzipSync(data).toString(), (abc + def)); + +zlib.gunzip(data, common.mustSucceed((result) => { + assert.strictEqual(result.toString(), (abc + def)); +})); + +zlib.unzip(data, common.mustSucceed((result) => { + assert.strictEqual(result.toString(), (abc + def)); +})); + +// Multi-member support does not apply to zlib inflate/deflate. +zlib.unzip(Buffer.concat([ + zlib.deflateSync('abc'), + zlib.deflateSync('def'), +]), common.mustSucceed((result) => { + assert.strictEqual(result.toString(), abc); +})); + +// Files that have the "right" magic bytes for starting a new gzip member +// in the middle of themselves, even if they are part of a single +// regularly compressed member +const pmmFileZlib = fixtures.path('pseudo-multimember-gzip.z'); +const pmmFileGz = fixtures.path('pseudo-multimember-gzip.gz'); + +const pmmExpected = zlib.inflateSync(fs.readFileSync(pmmFileZlib)); +const pmmResultBuffers = []; + +fs.createReadStream(pmmFileGz) + .pipe(zlib.createGunzip()) + .on('error', (err) => { + assert.ifError(err); + }) + .on('data', (data) => pmmResultBuffers.push(data)) + .on('finish', common.mustCall(() => { + // Result should match original random garbage + assert.deepStrictEqual(Buffer.concat(pmmResultBuffers), pmmExpected); + })); + +// Test that the next gzip member can wrap around the input buffer boundary +[0, 1, 2, 3, 4, defEncoded.length].forEach((offset) => { + const resultBuffers = []; + + const unzip = zlib.createGunzip() + .on('error', (err) => { + assert.ifError(err); + }) + .on('data', (data) => resultBuffers.push(data)) + .on('finish', common.mustCall(() => { + assert.strictEqual( + Buffer.concat(resultBuffers).toString(), + 'abcdef', + `result should match original input (offset = ${offset})` + ); + })); + + // First write: write "abc" + the first bytes of "def" + unzip.write(Buffer.concat([ + abcEncoded, defEncoded.slice(0, offset), + ])); + + // Write remaining bytes of "def" + unzip.end(defEncoded.slice(offset)); +}); diff --git a/test/js/node/test/parallel/test-zlib-from-gzip.js b/test/js/node/test/parallel/test-zlib-from-gzip.js new file mode 100644 index 0000000000..f8fbe16764 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-from-gzip.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Test unzipping a file that was created with a non-node gzip lib, +// piped in as fast as possible. + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const fixtures = require('../common/fixtures'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const gunzip = zlib.createGunzip(); + +const fs = require('fs'); + +const fixture = fixtures.path('person.jpg.gz'); +const unzippedFixture = fixtures.path('person.jpg'); +const outputFile = tmpdir.resolve('person.jpg'); +const expect = fs.readFileSync(unzippedFixture); +const inp = fs.createReadStream(fixture); +const out = fs.createWriteStream(outputFile); + +inp.pipe(gunzip).pipe(out); +out.on('close', common.mustCall(() => { + const actual = fs.readFileSync(outputFile); + assert.strictEqual(actual.length, expect.length); + for (let i = 0, l = actual.length; i < l; i++) { + assert.strictEqual(actual[i], expect[i], `byte[${i}]`); + } +})); diff --git a/test/js/node/test/parallel/test-zlib-from-string.js b/test/js/node/test/parallel/test-zlib-from-string.js new file mode 100644 index 0000000000..92b6f86646 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-from-string.js @@ -0,0 +1,83 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Test compressing and uncompressing a string with zlib + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli' + + 't. Morbi faucibus, purus at gravida dictum, libero arcu ' + + 'convallis lacus, in commodo libero metus eu nisi. Nullam' + + ' commodo, neque nec porta placerat, nisi est fermentum a' + + 'ugue, vitae gravida tellus sapien sit amet tellus. Aenea' + + 'n non diam orci. Proin quis elit turpis. Suspendisse non' + + ' diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu' + + 'm arcu mi, sodales non suscipit id, ultrices ut massa. S' + + 'ed ac sem sit amet arcu malesuada fermentum. Nunc sed. '; +const expectedBase64Deflate = 'eJxdUUtOQzEMvMoc4OndgT0gJCT2buJWlpI4jePeqZfpmX' + + 'AKLRKbLOzx/HK73q6vOrhCunlF1qIDJhNUeW5I2ozT5OkD' + + 'lKWLJWkncJG5403HQXAkT3Jw29B9uIEmToMukglZ0vS6oc' + + 'iBh4JG8sV4oVLEUCitK2kxq1WzPnChHDzsaGKy491LofoA' + + 'bWh8do43oeuYhB5EPCjcLjzYJo48KrfQBvnJecNFJvHT1+' + + 'RSQsGoC7dn2t/xjhduTA1NWyQIZR0pbHwMDatnD+crPqKS' + + 'qGPHp1vnlsWM/07ubf7bheF7kqSj84Bm0R1fYTfaK8vqqq' + + 'fKBtNMhe3OZh6N95CTvMX5HJJi4xOVzCgUOIMSLH7wmeOH' + + 'aFE4RdpnGavKtrB5xzfO/Ll9'; +const expectedBase64Gzip = 'H4sIAAAAAAAAA11RS05DMQy8yhzg6d2BPSAkJPZu4laWkjiN4' + + '96pl+mZcAotEpss7PH8crverq86uEK6eUXWogMmE1R5bkjajN' + + 'Pk6QOUpYslaSdwkbnjTcdBcCRPcnDb0H24gSZOgy6SCVnS9Lq' + + 'hyIGHgkbyxXihUsRQKK0raTGrVbM+cKEcPOxoYrLj3Uuh+gBt' + + 'aHx2jjeh65iEHkQ8KNwuPNgmjjwqt9AG+cl5w0Um8dPX5FJCw' + + 'agLt2fa3/GOF25MDU1bJAhlHSlsfAwNq2cP5ys+opKoY8enW+' + + 'eWxYz/Tu5t/tuF4XuSpKPzgGbRHV9hN9ory+qqp8oG00yF7c5' + + 'mHo33kJO8xfkckmLjE5XMKBQ4gxIsfvCZ44doUThF2mcZq8q2' + + 'sHnHNzRtagj5AQAA'; + +zlib.deflate(inputString, common.mustCall((err, buffer) => { + zlib.inflate(buffer, common.mustCall((err, inflated) => { + assert.strictEqual(inflated.toString(), inputString); + })); +})); + +zlib.gzip(inputString, common.mustCall((err, buffer) => { + // Can't actually guarantee that we'll get exactly the same + // deflated bytes when we compress a string, since the header + // depends on stuff other than the input string itself. + // However, decrypting it should definitely yield the same + // result that we're expecting, and this should match what we get + // from inflating the known valid deflate data. + zlib.gunzip(buffer, common.mustCall((err, gunzipped) => { + assert.strictEqual(gunzipped.toString(), inputString); + })); +})); + +let buffer = Buffer.from(expectedBase64Deflate, 'base64'); +zlib.unzip(buffer, common.mustCall((err, buffer) => { + assert.strictEqual(buffer.toString(), inputString); +})); + +buffer = Buffer.from(expectedBase64Gzip, 'base64'); +zlib.unzip(buffer, common.mustCall((err, buffer) => { + assert.strictEqual(buffer.toString(), inputString); +})); diff --git a/test/js/node/test/parallel/test-zlib-invalid-arg-value-brotli-compress.js b/test/js/node/test/parallel/test-zlib-invalid-arg-value-brotli-compress.js new file mode 100644 index 0000000000..688acddd16 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-invalid-arg-value-brotli-compress.js @@ -0,0 +1,20 @@ +'use strict'; + +require('../common'); + +// This test ensures that the BrotliCompress function throws +// ERR_INVALID_ARG_TYPE when the values of the `params` key-value object are +// neither numbers nor booleans. + +const assert = require('assert'); +const { BrotliCompress, constants } = require('zlib'); + +const opts = { + params: { + [constants.BROTLI_PARAM_MODE]: 'lol' + } +}; + +assert.throws(() => BrotliCompress(opts), { + code: 'ERR_INVALID_ARG_TYPE' +}); diff --git a/test/js/node/test/parallel/test-zlib-invalid-input.js b/test/js/node/test/parallel/test-zlib-invalid-input.js new file mode 100644 index 0000000000..7aa44dfe70 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-invalid-input.js @@ -0,0 +1,60 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +// Test uncompressing invalid input + +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const nonStringInputs = [ + 1, + true, + { a: 1 }, + ['a'], +]; + +// zlib.Unzip classes need to get valid data, or else they'll throw. +const unzips = [ + zlib.Unzip(), + zlib.Gunzip(), + zlib.Inflate(), + zlib.InflateRaw(), + zlib.BrotliDecompress(), +]; + +nonStringInputs.forEach(common.mustCall((input) => { + assert.throws(() => { + zlib.gunzip(input); + }, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE' + }); +}, nonStringInputs.length)); + +unzips.forEach(common.mustCall((uz, i) => { + uz.on('error', common.mustCall()); + uz.on('end', common.mustNotCall()); + + // This will trigger error event + uz.write('this is not valid compressed data.'); +}, unzips.length)); diff --git a/test/js/node/test/parallel/test-zlib-kmaxlength-rangeerror.js b/test/js/node/test/parallel/test-zlib-kmaxlength-rangeerror.js new file mode 100644 index 0000000000..9803630214 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-kmaxlength-rangeerror.js @@ -0,0 +1,28 @@ +'use strict'; +require('../common'); + +// This test ensures that zlib throws a RangeError if the final buffer needs to +// be larger than kMaxLength and concatenation fails. +// https://github.com/nodejs/node/pull/1811 + +const assert = require('assert'); + +// Change kMaxLength for zlib to trigger the error without having to allocate +// large Buffers. +const buffer = require('buffer'); +const oldkMaxLength = buffer.kMaxLength; +buffer.kMaxLength = 64; +const zlib = require('zlib'); +buffer.kMaxLength = oldkMaxLength; + +const encoded = Buffer.from('H4sIAAAAAAAAA0tMHFgAAIw2K/GAAAAA', 'base64'); + +// Async +zlib.gunzip(encoded, function(err) { + assert.ok(err instanceof RangeError); +}); + +// Sync +assert.throws(function() { + zlib.gunzipSync(encoded); +}, RangeError); diff --git a/test/js/node/test/parallel/test-zlib-maxOutputLength.js b/test/js/node/test/parallel/test-zlib-maxOutputLength.js new file mode 100644 index 0000000000..9af0b3736f --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-maxOutputLength.js @@ -0,0 +1,25 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const encoded = Buffer.from('G38A+CXCIrFAIAM=', 'base64'); + +// Async +zlib.brotliDecompress(encoded, { maxOutputLength: 64 }, common.expectsError({ + code: 'ERR_BUFFER_TOO_LARGE', + message: 'Cannot create a Buffer larger than 64 bytes' +})); + +// Sync +assert.throws(function() { + zlib.brotliDecompressSync(encoded, { maxOutputLength: 64 }); +}, RangeError); + +// Async +zlib.brotliDecompress(encoded, { maxOutputLength: 256 }, function(err) { + assert.strictEqual(err, null); +}); + +// Sync +zlib.brotliDecompressSync(encoded, { maxOutputLength: 256 }); diff --git a/test/js/node/test/parallel/test-zlib-no-stream.js b/test/js/node/test/parallel/test-zlib-no-stream.js new file mode 100644 index 0000000000..68da269ab8 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-no-stream.js @@ -0,0 +1,14 @@ +/* eslint-disable node-core/required-modules */ +/* eslint-disable node-core/require-common-first */ + +'use strict'; + +// We are not loading common because it will load the stream module, +// defeating the purpose of this test. + +const { gzipSync } = require('zlib'); + +// Avoid regressions such as https://github.com/nodejs/node/issues/36615 + +// This must not throw +gzipSync('fooobar'); diff --git a/test/js/node/test/parallel/test-zlib-object-write.js b/test/js/node/test/parallel/test-zlib-object-write.js new file mode 100644 index 0000000000..2be5edab89 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-object-write.js @@ -0,0 +1,14 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { Gunzip } = require('zlib'); + +const gunzip = new Gunzip({ objectMode: true }); +gunzip.on('error', common.mustNotCall()); +assert.throws(() => { + gunzip.write({}); +}, { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE' +}); diff --git a/test/js/node/test/parallel/test-zlib-params.js b/test/js/node/test/parallel/test-zlib-params.js new file mode 100644 index 0000000000..18271fe022 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-params.js @@ -0,0 +1,40 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const fixtures = require('../common/fixtures'); + +const file = fixtures.readSync('person.jpg'); +const chunkSize = 12 * 1024; +const opts = { level: 9, strategy: zlib.constants.Z_DEFAULT_STRATEGY }; +const deflater = zlib.createDeflate(opts); + +const chunk1 = file.slice(0, chunkSize); +const chunk2 = file.slice(chunkSize); +const blkhdr = Buffer.from([0x00, 0x5a, 0x82, 0xa5, 0x7d]); +const blkftr = Buffer.from('010000ffff7dac3072', 'hex'); +const expected = Buffer.concat([blkhdr, chunk2, blkftr]); +const bufs = []; + +function read() { + let buf; + while ((buf = deflater.read()) !== null) { + bufs.push(buf); + } +} + +deflater.write(chunk1, function() { + deflater.params(0, zlib.constants.Z_DEFAULT_STRATEGY, function() { + while (deflater.read()); + + deflater.on('readable', read); + + deflater.end(chunk2); + }); + while (deflater.read()); +}); + +process.once('exit', function() { + const actual = Buffer.concat(bufs); + assert.deepStrictEqual(actual, expected); +}); diff --git a/test/js/node/test/parallel/test-zlib-premature-end.js b/test/js/node/test/parallel/test-zlib-premature-end.js new file mode 100644 index 0000000000..17446c907d --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-premature-end.js @@ -0,0 +1,33 @@ +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); +const assert = require('assert'); + +const input = '0123456789'.repeat(4); + +for (const [ compress, decompressor ] of [ + [ zlib.deflateRawSync, zlib.createInflateRaw ], + [ zlib.deflateSync, zlib.createInflate ], + [ zlib.brotliCompressSync, zlib.createBrotliDecompress ], +]) { + const compressed = compress(input); + const trailingData = Buffer.from('not valid compressed data'); + + for (const variant of [ + (stream) => { stream.end(compressed); }, + (stream) => { stream.write(compressed); stream.write(trailingData); }, + (stream) => { stream.write(compressed); stream.end(trailingData); }, + (stream) => { stream.write(Buffer.concat([compressed, trailingData])); }, + (stream) => { stream.end(Buffer.concat([compressed, trailingData])); }, + ]) { + let output = ''; + const stream = decompressor(); + stream.setEncoding('utf8'); + stream.on('data', (chunk) => output += chunk); + stream.on('end', common.mustCall(() => { + assert.strictEqual(output, input); + assert.strictEqual(stream.bytesWritten, compressed.length); + })); + variant(stream); + } +} diff --git a/test/js/node/test/parallel/test-zlib-random-byte-pipes.js b/test/js/node/test/parallel/test-zlib-random-byte-pipes.js new file mode 100644 index 0000000000..d8d039a6d6 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-random-byte-pipes.js @@ -0,0 +1,158 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); +const stream = require('stream'); +const zlib = require('zlib'); + +const Stream = stream.Stream; + +// Emit random bytes, and keep a shasum +class RandomReadStream extends Stream { + constructor(opt) { + super(); + + this.readable = true; + this._paused = false; + this._processing = false; + + this._hasher = crypto.createHash('sha1'); + opt = opt || {}; + + // base block size. + opt.block = opt.block || 256 * 1024; + + // Total number of bytes to emit + opt.total = opt.total || 256 * 1024 * 1024; + this._remaining = opt.total; + + // How variable to make the block sizes + opt.jitter = opt.jitter || 1024; + + this._opt = opt; + + this._process = this._process.bind(this); + + process.nextTick(this._process); + } + + pause() { + this._paused = true; + this.emit('pause'); + } + + resume() { + // console.error("rrs resume"); + this._paused = false; + this.emit('resume'); + this._process(); + } + + _process() { + if (this._processing) return; + if (this._paused) return; + + this._processing = true; + + if (!this._remaining) { + this._hash = this._hasher.digest('hex').toLowerCase().trim(); + this._processing = false; + + this.emit('end'); + return; + } + + // Figure out how many bytes to output + // if finished, then just emit end. + let block = this._opt.block; + const jitter = this._opt.jitter; + if (jitter) { + block += Math.ceil(Math.random() * jitter - (jitter / 2)); + } + block = Math.min(block, this._remaining); + const buf = Buffer.allocUnsafe(block); + for (let i = 0; i < block; i++) { + buf[i] = Math.random() * 256; + } + + this._hasher.update(buf); + + this._remaining -= block; + + this._processing = false; + + this.emit('data', buf); + process.nextTick(this._process); + } +} + +// A filter that just verifies a shasum +class HashStream extends Stream { + constructor() { + super(); + this.readable = this.writable = true; + this._hasher = crypto.createHash('sha1'); + } + + write(c) { + // Simulate the way that an fs.ReadStream returns false + // on *every* write, only to resume a moment later. + this._hasher.update(c); + process.nextTick(() => this.resume()); + return false; + } + + resume() { + this.emit('resume'); + process.nextTick(() => this.emit('drain')); + } + + end(c) { + if (c) { + this.write(c); + } + this._hash = this._hasher.digest('hex').toLowerCase().trim(); + this.emit('data', this._hash); + this.emit('end'); + } +} + +for (const [ createCompress, createDecompress ] of [ + [ zlib.createGzip, zlib.createGunzip ], + [ zlib.createBrotliCompress, zlib.createBrotliDecompress ], +]) { + const inp = new RandomReadStream({ total: 1024, block: 256, jitter: 16 }); + const out = new HashStream(); + const gzip = createCompress(); + const gunz = createDecompress(); + + inp.pipe(gzip).pipe(gunz).pipe(out); + + out.on('data', common.mustCall((c) => { + assert.strictEqual(c, inp._hash, `Hash '${c}' equals '${inp._hash}'.`); + })); +} diff --git a/test/js/node/test/parallel/test-zlib-reset-before-write.js b/test/js/node/test/parallel/test-zlib-reset-before-write.js new file mode 100644 index 0000000000..afa207f12c --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-reset-before-write.js @@ -0,0 +1,37 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +// Tests that zlib streams support .reset() and .params() +// before the first write. That is important to ensure that +// lazy init of zlib native library handles these cases. + +for (const fn of [ + (z, cb) => { + z.reset(); + cb(); + }, + (z, cb) => z.params(0, zlib.constants.Z_DEFAULT_STRATEGY, cb), +]) { + const deflate = zlib.createDeflate(); + const inflate = zlib.createInflate(); + + deflate.pipe(inflate); + + const output = []; + inflate + .on('error', (err) => { + assert.ifError(err); + }) + .on('data', (chunk) => output.push(chunk)) + .on('end', common.mustCall( + () => assert.strictEqual(Buffer.concat(output).toString(), 'abc'))); + + fn(deflate, () => { + fn(inflate, () => { + deflate.write('abc'); + deflate.end(); + }); + }); +} diff --git a/test/js/node/test/parallel/test-zlib-sync-no-event.js b/test/js/node/test/parallel/test-zlib-sync-no-event.js new file mode 100644 index 0000000000..e7f25c8476 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-sync-no-event.js @@ -0,0 +1,19 @@ +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); +const assert = require('assert'); + +const message = 'Come on, Fhqwhgads.'; +const buffer = Buffer.from(message); + +const zipper = new zlib.Gzip(); +zipper.on('close', common.mustNotCall()); + +const zipped = zipper._processChunk(buffer, zlib.constants.Z_FINISH); + +const unzipper = new zlib.Gunzip(); +unzipper.on('close', common.mustNotCall()); + +const unzipped = unzipper._processChunk(zipped, zlib.constants.Z_FINISH); +assert.notStrictEqual(zipped.toString(), message); +assert.strictEqual(unzipped.toString(), message); diff --git a/test/js/node/test/parallel/test-zlib-truncated.js b/test/js/node/test/parallel/test-zlib-truncated.js new file mode 100644 index 0000000000..94bc0e21cb --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-truncated.js @@ -0,0 +1,64 @@ +'use strict'; +// Tests zlib streams with truncated compressed input + +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli' + + 't. Morbi faucibus, purus at gravida dictum, libero arcu ' + + 'convallis lacus, in commodo libero metus eu nisi. Nullam' + + ' commodo, neque nec porta placerat, nisi est fermentum a' + + 'ugue, vitae gravida tellus sapien sit amet tellus. Aenea' + + 'n non diam orci. Proin quis elit turpis. Suspendisse non' + + ' diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu' + + 'm arcu mi, sodales non suscipit id, ultrices ut massa. S' + + 'ed ac sem sit amet arcu malesuada fermentum. Nunc sed. '; + +const errMessage = /unexpected end of file/; + +[ + { comp: 'gzip', decomp: 'gunzip', decompSync: 'gunzipSync' }, + { comp: 'gzip', decomp: 'unzip', decompSync: 'unzipSync' }, + { comp: 'deflate', decomp: 'inflate', decompSync: 'inflateSync' }, + { comp: 'deflateRaw', decomp: 'inflateRaw', decompSync: 'inflateRawSync' }, +].forEach(function(methods) { + zlib[methods.comp](inputString, function(err, compressed) { + assert.ifError(err); + const truncated = compressed.slice(0, compressed.length / 2); + const toUTF8 = (buffer) => buffer.toString('utf-8'); + + // sync sanity + const decompressed = zlib[methods.decompSync](compressed); + assert.strictEqual(toUTF8(decompressed), inputString); + + // async sanity + zlib[methods.decomp](compressed, function(err, result) { + assert.ifError(err); + assert.strictEqual(toUTF8(result), inputString); + }); + + // Sync truncated input test + assert.throws(function() { + zlib[methods.decompSync](truncated); + }, errMessage); + + // Async truncated input test + zlib[methods.decomp](truncated, function(err, result) { + assert.match(err.message, errMessage); + }); + + const syncFlushOpt = { finishFlush: zlib.constants.Z_SYNC_FLUSH }; + + // Sync truncated input test, finishFlush = Z_SYNC_FLUSH + const result = toUTF8(zlib[methods.decompSync](truncated, syncFlushOpt)); + assert.strictEqual(result, inputString.slice(0, result.length)); + + // Async truncated input test, finishFlush = Z_SYNC_FLUSH + zlib[methods.decomp](truncated, syncFlushOpt, function(err, decompressed) { + assert.ifError(err); + const result = toUTF8(decompressed); + assert.strictEqual(result, inputString.slice(0, result.length)); + }); + }); +}); diff --git a/test/js/node/test/parallel/test-zlib-unzip-one-byte-chunks.js b/test/js/node/test/parallel/test-zlib-unzip-one-byte-chunks.js new file mode 100644 index 0000000000..51af5153a4 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-unzip-one-byte-chunks.js @@ -0,0 +1,30 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +const data = Buffer.concat([ + zlib.gzipSync('abc'), + zlib.gzipSync('def'), +]); + +const resultBuffers = []; + +const unzip = zlib.createUnzip() + .on('error', (err) => { + assert.ifError(err); + }) + .on('data', (data) => resultBuffers.push(data)) + .on('finish', common.mustCall(() => { + const unzipped = Buffer.concat(resultBuffers).toString(); + assert.strictEqual(unzipped, 'abcdef', + `'${unzipped}' should match 'abcdef' after zipping ` + + 'and unzipping'); + })); + +for (let i = 0; i < data.length; i++) { + // Write each single byte individually. + unzip.write(Buffer.from([data[i]])); +} + +unzip.end(); diff --git a/test/js/node/test/parallel/test-zlib-write-after-close.js b/test/js/node/test/parallel/test-zlib-write-after-close.js new file mode 100644 index 0000000000..eb8ff43539 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-write-after-close.js @@ -0,0 +1,34 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); + +zlib.gzip('hello', common.mustCall(function(err, out) { + const unzip = zlib.createGunzip(); + unzip.close(common.mustCall()); + unzip.write('asd', common.expectsError({ + code: 'ERR_STREAM_DESTROYED', + name: 'Error', + message: 'Cannot call write after a stream was destroyed' + })); +})); diff --git a/test/js/node/test/parallel/test-zlib-write-after-end.js b/test/js/node/test/parallel/test-zlib-write-after-end.js new file mode 100644 index 0000000000..2b31ff30dc --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-write-after-end.js @@ -0,0 +1,16 @@ +'use strict'; +const common = require('../common'); +const zlib = require('zlib'); + +// Regression test for https://github.com/nodejs/node/issues/30976 +// Writes to a stream should finish even after the readable side has been ended. + +const data = zlib.deflateRawSync('Welcome'); + +const inflate = zlib.createInflateRaw(); + +inflate.resume(); +inflate.write(data, common.mustCall()); +inflate.write(Buffer.from([0x00]), common.mustCall()); +inflate.write(Buffer.from([0x00]), common.mustCall()); +inflate.flush(common.mustCall()); diff --git a/test/js/node/test/parallel/test-zlib-write-after-flush.js b/test/js/node/test/parallel/test-zlib-write-after-flush.js new file mode 100644 index 0000000000..6edcae2e2f --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-write-after-flush.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +for (const [ createCompress, createDecompress ] of [ + [ zlib.createGzip, zlib.createGunzip ], + [ zlib.createBrotliCompress, zlib.createBrotliDecompress ], +]) { + const gzip = createCompress(); + const gunz = createDecompress(); + + gzip.pipe(gunz); + + let output = ''; + const input = 'A line of data\n'; + gunz.setEncoding('utf8'); + gunz.on('data', (c) => output += c); + gunz.on('end', common.mustCall(() => { + assert.strictEqual(output, input); + })); + + // Make sure that flush/write doesn't trigger an assert failure + gzip.flush(); + gzip.write(input); + gzip.end(); + gunz.read(0); +} diff --git a/test/js/node/test/parallel/test-zlib-zero-byte.js b/test/js/node/test/parallel/test-zlib-zero-byte.js new file mode 100644 index 0000000000..fc57960f1e --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-zero-byte.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + +for (const Compressor of [ zlib.Gzip, zlib.BrotliCompress ]) { + const gz = Compressor(); + const emptyBuffer = Buffer.alloc(0); + let received = 0; + gz.on('data', function(c) { + received += c.length; + }); + + gz.on('end', common.mustCall(function() { + const expected = Compressor === zlib.Gzip ? 20 : 1; + assert.strictEqual(received, expected, + `${received}, ${expected}, ${Compressor.name}`); + })); + gz.on('finish', common.mustCall()); + gz.write(emptyBuffer); + gz.end(); +} diff --git a/test/js/node/test/parallel/test-zlib-zero-windowBits.js b/test/js/node/test/parallel/test-zlib-zero-windowBits.js new file mode 100644 index 0000000000..a27fd6734a --- /dev/null +++ b/test/js/node/test/parallel/test-zlib-zero-windowBits.js @@ -0,0 +1,33 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); + + +// windowBits is a special case in zlib. On the compression side, 0 is invalid. +// On the decompression side, it indicates that zlib should use the value from +// the header of the compressed stream. +{ + const inflate = zlib.createInflate({ windowBits: 0 }); + assert(inflate instanceof zlib.Inflate); +} + +{ + const gunzip = zlib.createGunzip({ windowBits: 0 }); + assert(gunzip instanceof zlib.Gunzip); +} + +{ + const unzip = zlib.createUnzip({ windowBits: 0 }); + assert(unzip instanceof zlib.Unzip); +} + +{ + assert.throws(() => zlib.createGzip({ windowBits: 0 }), { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.windowBits" is out of range. ' + + 'It must be >= 9 and <= 15. Received 0' + }); +} diff --git a/test/js/node/test/parallel/test-zlib.js b/test/js/node/test/parallel/test-zlib.js new file mode 100644 index 0000000000..65050b85a0 --- /dev/null +++ b/test/js/node/test/parallel/test-zlib.js @@ -0,0 +1,232 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const stream = require('stream'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +// Should not segfault. +assert.throws(() => zlib.gzipSync(Buffer.alloc(0), { windowBits: 8 }), { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "options.windowBits" is out of range. ' + + 'It must be >= 9 and <= 15. Received 8', +}); + +let zlibPairs = [ + [zlib.Deflate, zlib.Inflate], + [zlib.Gzip, zlib.Gunzip], + [zlib.Deflate, zlib.Unzip], + [zlib.Gzip, zlib.Unzip], + [zlib.DeflateRaw, zlib.InflateRaw], + [zlib.BrotliCompress, zlib.BrotliDecompress], +]; + +// How fast to trickle through the slowstream +let trickle = [128, 1024, 1024 * 1024]; + +// Tunable options for zlib classes. + +// several different chunk sizes +let chunkSize = [128, 1024, 1024 * 16, 1024 * 1024]; + +// This is every possible value. +let level = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; +let windowBits = [8, 9, 10, 11, 12, 13, 14, 15]; +let memLevel = [1, 2, 3, 4, 5, 6, 7, 8, 9]; +let strategy = [0, 1, 2, 3, 4]; + +// It's nice in theory to test every combination, but it +// takes WAY too long. Maybe a pummel test could do this? +if (!process.env.PUMMEL) { + trickle = [1024]; + chunkSize = [1024 * 16]; + level = [6]; + memLevel = [8]; + windowBits = [15]; + strategy = [0]; +} + +let testFiles = ['person.jpg', 'elipses.txt', 'empty.txt']; + +if (process.env.FAST) { + zlibPairs = [[zlib.Gzip, zlib.Unzip]]; + testFiles = ['person.jpg']; +} + +const tests = {}; +testFiles.forEach(common.mustCall((file) => { + tests[file] = fixtures.readSync(file); +}, testFiles.length)); + + +// Stream that saves everything +class BufferStream extends stream.Stream { + constructor() { + super(); + this.chunks = []; + this.length = 0; + this.writable = true; + this.readable = true; + } + + write(c) { + this.chunks.push(c); + this.length += c.length; + return true; + } + + end(c) { + if (c) this.write(c); + // flatten + const buf = Buffer.allocUnsafe(this.length); + let i = 0; + this.chunks.forEach((c) => { + c.copy(buf, i); + i += c.length; + }); + this.emit('data', buf); + this.emit('end'); + return true; + } +} + +class SlowStream extends stream.Stream { + constructor(trickle) { + super(); + this.trickle = trickle; + this.offset = 0; + this.readable = this.writable = true; + } + + write() { + throw new Error('not implemented, just call ss.end(chunk)'); + } + + pause() { + this.paused = true; + this.emit('pause'); + } + + resume() { + const emit = () => { + if (this.paused) return; + if (this.offset >= this.length) { + this.ended = true; + return this.emit('end'); + } + const end = Math.min(this.offset + this.trickle, this.length); + const c = this.chunk.slice(this.offset, end); + this.offset += c.length; + this.emit('data', c); + process.nextTick(emit); + }; + + if (this.ended) return; + this.emit('resume'); + if (!this.chunk) return; + this.paused = false; + emit(); + } + + end(chunk) { + // Walk over the chunk in blocks. + this.chunk = chunk; + this.length = chunk.length; + this.resume(); + return this.ended; + } +} + +// windowBits: 8 shouldn't throw +zlib.createDeflateRaw({ windowBits: 8 }); + +{ + const node = fs.createReadStream(fixtures.path('person.jpg')); + const raw = []; + const reinflated = []; + node.on('data', (chunk) => raw.push(chunk)); + + // Usually, the inflate windowBits parameter needs to be at least the + // value of the matching deflate’s windowBits. However, inflate raw with + // windowBits = 8 should be able to handle compressed data from a source + // that does not know about the silent 8-to-9 upgrade of windowBits + // that most versions of zlib/Node perform, and which *still* results in + // a valid 8-bit-window zlib stream. + node.pipe(zlib.createDeflateRaw({ windowBits: 9 })) + .pipe(zlib.createInflateRaw({ windowBits: 8 })) + .on('data', (chunk) => reinflated.push(chunk)) + .on('end', common.mustCall( + () => assert(Buffer.concat(raw).equals(Buffer.concat(reinflated))))) + .on('close', common.mustCall(1)); +} + +// For each of the files, make sure that compressing and +// decompressing results in the same data, for every combination +// of the options set above. + +const testKeys = Object.keys(tests); +testKeys.forEach(common.mustCall((file) => { + const test = tests[file]; + chunkSize.forEach(common.mustCall((chunkSize) => { + trickle.forEach(common.mustCall((trickle) => { + windowBits.forEach(common.mustCall((windowBits) => { + level.forEach(common.mustCall((level) => { + memLevel.forEach(common.mustCall((memLevel) => { + strategy.forEach(common.mustCall((strategy) => { + zlibPairs.forEach(common.mustCall((pair) => { + const Def = pair[0]; + const Inf = pair[1]; + const opts = { level, windowBits, memLevel, strategy }; + + const def = new Def(opts); + const inf = new Inf(opts); + const ss = new SlowStream(trickle); + const buf = new BufferStream(); + + // Verify that the same exact buffer comes out the other end. + buf.on('data', common.mustCall((c) => { + const msg = `${file} ${chunkSize} ${ + JSON.stringify(opts)} ${Def.name} -> ${Inf.name}`; + let i; + for (i = 0; i < Math.max(c.length, test.length); i++) { + if (c[i] !== test[i]) { + assert.fail(msg); + break; + } + } + })); + + // The magic happens here. + ss.pipe(def).pipe(inf).pipe(buf); + ss.end(test); + }, zlibPairs.length)); + }, strategy.length)); + }, memLevel.length)); + }, level.length)); + }, windowBits.length)); + }, trickle.length)); + }, chunkSize.length)); +}, testKeys.length)); diff --git a/test/js/node/test/parallel/timer-immediate.test.js b/test/js/node/test/parallel/timer-immediate.test.js deleted file mode 100644 index 1f74aa8bde..0000000000 --- a/test/js/node/test/parallel/timer-immediate.test.js +++ /dev/null @@ -1,19 +0,0 @@ -//#FILE: test-timer-immediate.js -//#SHA1: 00e4e451b5feda969bd3352a194ba0ee0e5bab85 -//----------------- -"use strict"; - -// Note: We're not using the 'common' module as it's not necessary for this test - -test("setImmediate should be called", done => { - // Recreate the global.process object - global.process = {}; - - // Use setImmediate and expect it to be called - setImmediate(() => { - expect(true).toBe(true); // This assertion is just to ensure the callback is called - done(); - }); -}); - -//<#END_FILE: test-timer-immediate.js diff --git a/test/js/node/test/parallel/timers-args.test.js b/test/js/node/test/parallel/timers-args.test.js deleted file mode 100644 index ca38fd5b37..0000000000 --- a/test/js/node/test/parallel/timers-args.test.js +++ /dev/null @@ -1,52 +0,0 @@ -//#FILE: test-timers-args.js -//#SHA1: 27f971d534c9bb3a1c14ae176ee8f34b0cdbed6f -//----------------- -"use strict"; - -function range(n) { - return "x" - .repeat(n + 1) - .split("") - .map(function (_, i) { - return i; - }); -} - -test("setTimeout with increasing number of arguments", done => { - function timeout(nargs) { - const args = range(nargs); - setTimeout(callback, 1, ...args); - - function callback(...receivedArgs) { - expect(receivedArgs).toEqual(args); - if (nargs < 128) { - timeout(nargs + 1); - } else { - done(); - } - } - } - - timeout(0); -}); - -test("setInterval with increasing number of arguments", done => { - function interval(nargs) { - const args = range(nargs); - const timer = setInterval(callback, 1, ...args); - - function callback(...receivedArgs) { - clearInterval(timer); - expect(receivedArgs).toEqual(args); - if (nargs < 128) { - interval(nargs + 1); - } else { - done(); - } - } - } - - interval(0); -}); - -//<#END_FILE: test-timers-args.js diff --git a/test/js/node/test/parallel/timers-clear-timeout-interval-equivalent.test.js b/test/js/node/test/parallel/timers-clear-timeout-interval-equivalent.test.js deleted file mode 100644 index 1fc88ba7ef..0000000000 --- a/test/js/node/test/parallel/timers-clear-timeout-interval-equivalent.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-timers-clear-timeout-interval-equivalent.js -//#SHA1: 2c98894fc8abe7d6e800533325274f39f9a16ff4 -//----------------- -"use strict"; - -// This test makes sure that timers created with setTimeout can be disarmed by -// clearInterval and that timers created with setInterval can be disarmed by -// clearTimeout. -// -// This behavior is documented in the HTML Living Standard: -// -// * Refs: https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-setinterval - -test("Disarm interval with clearTimeout", () => { - const mockCallback = jest.fn(); - const interval = setInterval(mockCallback, 1); - clearTimeout(interval); - - return new Promise(resolve => { - setTimeout(() => { - expect(mockCallback).not.toHaveBeenCalled(); - resolve(); - }, 10); - }); -}); - -test("Disarm timeout with clearInterval", () => { - const mockCallback = jest.fn(); - const timeout = setTimeout(mockCallback, 1); - clearInterval(timeout); - - return new Promise(resolve => { - setTimeout(() => { - expect(mockCallback).not.toHaveBeenCalled(); - resolve(); - }, 10); - }); -}); - -//<#END_FILE: test-timers-clear-timeout-interval-equivalent.js diff --git a/test/js/node/test/parallel/timers-clearimmediate.test.js b/test/js/node/test/parallel/timers-clearimmediate.test.js deleted file mode 100644 index d29f715d38..0000000000 --- a/test/js/node/test/parallel/timers-clearimmediate.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-timers-clearImmediate.js -//#SHA1: 819914471d2e9d0a4629df9ef4b96e8e87ae7606 -//----------------- -"use strict"; - -const N = 3; - -function next() { - const fn = jest.fn(); - const immediate = setImmediate(fn); - clearImmediate(immediate); - expect(fn).not.toHaveBeenCalled(); -} - -test("clearImmediate cancels setImmediate", () => { - for (let i = 0; i < N; i++) { - next(); - } -}); - -//<#END_FILE: test-timers-clearImmediate.js diff --git a/test/js/node/test/parallel/timers-immediate-queue.test.js b/test/js/node/test/parallel/timers-immediate-queue.test.js deleted file mode 100644 index 92aefa2084..0000000000 --- a/test/js/node/test/parallel/timers-immediate-queue.test.js +++ /dev/null @@ -1,63 +0,0 @@ -//#FILE: test-timers-immediate-queue.js -//#SHA1: 04dace9bcd1a7634224efafb4fdd5a953bfc28f6 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -// setImmediate should run clear its queued cbs once per event loop turn -// but immediates queued while processing the current queue should happen -// on the next turn of the event loop. - -// hit should be the exact same size of QUEUE, if we're letting things -// recursively add to the immediate QUEUE hit will be > QUEUE - -test("setImmediate queue processing", done => { - let ticked = false; - let hit = 0; - const QUEUE = 10; - - function run() { - if (hit === 0) { - setTimeout(() => { - ticked = true; - }, 1); - const now = Date.now(); - while (Date.now() - now < 2); - } - - if (ticked) return; - - hit += 1; - setImmediate(run); - } - - for (let i = 0; i < QUEUE; i++) setImmediate(run); - - // Use setImmediate to ensure all other immediates have run - setImmediate(() => { - expect(hit).toBe(QUEUE); - done(); - }); -}); - -//<#END_FILE: test-timers-immediate-queue.js diff --git a/test/js/node/test/parallel/timers-next-tick.test.js b/test/js/node/test/parallel/timers-next-tick.test.js deleted file mode 100644 index eced9d48cc..0000000000 --- a/test/js/node/test/parallel/timers-next-tick.test.js +++ /dev/null @@ -1,50 +0,0 @@ -//#FILE: test-timers-next-tick.js -//#SHA1: c6c6f667dac048dd0aed0a244e484ae1f0127d53 -//----------------- -"use strict"; - -// This test verifies that the next tick queue runs after each -// individual Timeout, as well as each individual Immediate. - -test("next tick queue runs after each Timeout and Immediate", async () => { - const timeoutSpy1 = jest.fn(); - const timeoutSpy2 = jest.fn(); - const immediateSpy1 = jest.fn(); - const immediateSpy2 = jest.fn(); - - setTimeout(timeoutSpy1, 1); - const t2 = setTimeout(jest.fn(), 1); - const t3 = setTimeout(jest.fn(), 1); - setTimeout(timeoutSpy2, 1); - - await new Promise(resolve => setTimeout(resolve, 5)); - - setImmediate(immediateSpy1); - const i2 = setImmediate(jest.fn()); - const i3 = setImmediate(jest.fn()); - setImmediate(immediateSpy2); - - await new Promise(resolve => setImmediate(resolve)); - - expect(timeoutSpy1).toHaveBeenCalledTimes(1); - expect(timeoutSpy2).toHaveBeenCalledTimes(1); - expect(immediateSpy1).toHaveBeenCalledTimes(1); - expect(immediateSpy2).toHaveBeenCalledTimes(1); - - // Confirm that clearing Timeouts from a next tick doesn't explode. - process.nextTick(() => { - clearTimeout(t2); - clearTimeout(t3); - }); - - // Confirm that clearing Immediates from a next tick doesn't explode. - process.nextTick(() => { - clearImmediate(i2); - clearImmediate(i3); - }); - - // Wait for next tick to complete - await new Promise(process.nextTick); -}); - -//<#END_FILE: test-timers-next-tick.js diff --git a/test/js/node/test/parallel/timers-now.test.js b/test/js/node/test/parallel/timers-now.test.js deleted file mode 100644 index 1a0e58d2eb..0000000000 --- a/test/js/node/test/parallel/timers-now.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-timers-now.js -//#SHA1: 6d35fc8681a7698d480f851c3c4ba5bfca7fb0b9 -//----------------- -"use strict"; -// Flags: --expose-internals - -test("getLibuvNow() return value fits in a SMI after start-up", () => { - // We can't use internal bindings in Jest, so we'll need to mock this behavior - // For the purpose of this test, we'll create a mock function that returns a small number - const mockGetLibuvNow = jest.fn(() => 1000); - - // Simulate the binding object - const binding = { - getLibuvNow: mockGetLibuvNow, - }; - - // Call the function and check the result - const result = binding.getLibuvNow(); - - // Check if the result is less than 0x3ffffff (67108863 in decimal) - expect(result).toBeLessThan(0x3ffffff); - - // Ensure the mock function was called - expect(mockGetLibuvNow).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-timers-now.js diff --git a/test/js/node/test/parallel/timers-ordering.test.js b/test/js/node/test/parallel/timers-ordering.test.js deleted file mode 100644 index bd3769d4fc..0000000000 --- a/test/js/node/test/parallel/timers-ordering.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-timers-ordering.js -//#SHA1: b2be662b4e86125b4dcd87ced4316d84acf57912 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const N = 30; - -test("Timer ordering and timing", async () => { - let last_i = 0; - let last_ts = 0; - - function f(i) { - return new Promise(resolve => { - if (i <= N) { - // check order - expect(i).toBe(last_i + 1); - last_i = i; - - // Check that this iteration is fired at least 1ms later than the previous - const now = Date.now(); - expect(now).toBeGreaterThanOrEqual(last_ts + 1); - last_ts = now; - - // Schedule next iteration - setTimeout(() => resolve(f(i + 1)), 1); - } else { - resolve(); - } - }); - } - - await f(1); -}, 10000); // Increased timeout to ensure all iterations complete - -//<#END_FILE: test-timers-ordering.js diff --git a/test/js/node/test/parallel/timers-promises.test.js b/test/js/node/test/parallel/timers-promises.test.js deleted file mode 100644 index 2bba7b563b..0000000000 --- a/test/js/node/test/parallel/timers-promises.test.js +++ /dev/null @@ -1,13 +0,0 @@ -//#FILE: test-timers-promises.js -//#SHA1: 78e3867e4cb1a41f11f7fa930e5a7319149c9452 -//----------------- -"use strict"; - -const timer = require("node:timers"); -const timerPromises = require("node:timers/promises"); - -test("(node:timers/promises) is equal to (node:timers).promises", () => { - expect(timerPromises).toEqual(timer.promises); -}); - -//<#END_FILE: test-timers-promises.js diff --git a/test/js/node/test/parallel/timers-setimmediate-infinite-loop.test.js b/test/js/node/test/parallel/timers-setimmediate-infinite-loop.test.js deleted file mode 100644 index d034693ae2..0000000000 --- a/test/js/node/test/parallel/timers-setimmediate-infinite-loop.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-timers-setimmediate-infinite-loop.js -//#SHA1: cd4dd01a6d06097758004eaa8d36d8712977d49d -//----------------- -"use strict"; - -// This test ensures that if an Immediate callback clears subsequent -// immediates we don't get stuck in an infinite loop. -// -// If the process does get stuck, it will be timed out by the test -// runner. -// -// Ref: https://github.com/nodejs/node/issues/9756 - -test("setImmediate clears subsequent immediates without infinite loop", done => { - const firstCallback = jest.fn(() => { - clearImmediate(i2); - clearImmediate(i3); - }); - - const secondCallback = jest.fn(); - const thirdCallback = jest.fn(); - - setImmediate(firstCallback); - - const i2 = setImmediate(secondCallback); - const i3 = setImmediate(thirdCallback); - - // Use a timeout to ensure all immediates have been processed - setTimeout(() => { - expect(firstCallback).toHaveBeenCalledTimes(1); - expect(secondCallback).not.toHaveBeenCalled(); - expect(thirdCallback).not.toHaveBeenCalled(); - done(); - }, 100); -}); - -//<#END_FILE: test-timers-setimmediate-infinite-loop.js diff --git a/test/js/node/test/parallel/timers-timeout-with-non-integer.test.js b/test/js/node/test/parallel/timers-timeout-with-non-integer.test.js deleted file mode 100644 index 2207d34ec6..0000000000 --- a/test/js/node/test/parallel/timers-timeout-with-non-integer.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-timers-timeout-with-non-integer.js -//#SHA1: 371503ab8a31c6749ef77eabac3054e5a43ab231 -//----------------- -"use strict"; - -/** - * This test is for https://github.com/nodejs/node/issues/24203 - */ -test("setTimeout with non-integer time", done => { - let count = 50; - const time = 1.00000000000001; - - const exec = jest.fn(() => { - if (--count === 0) { - expect(exec).toHaveBeenCalledTimes(50); - done(); - return; - } - setTimeout(exec, time); - }); - - exec(); -}, 10000); // Increased timeout to ensure all iterations complete - -//<#END_FILE: test-timers-timeout-with-non-integer.js diff --git a/test/js/node/test/parallel/timers-to-primitive.test.js b/test/js/node/test/parallel/timers-to-primitive.test.js deleted file mode 100644 index a47b84132c..0000000000 --- a/test/js/node/test/parallel/timers-to-primitive.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-timers-to-primitive.js -//#SHA1: 87671c76fb38458f9d4f68aa747b2d95076f6bb4 -//----------------- -"use strict"; - -test("Timeout and Interval primitives", () => { - const timeouts = [setTimeout(() => {}, 1), setInterval(() => {}, 1)]; - - timeouts.forEach(timeout => { - expect(Number.isNaN(+timeout)).toBe(false); - expect(+timeout).toBe(timeout[Symbol.toPrimitive]()); - expect(`${timeout}`).toBe(timeout[Symbol.toPrimitive]().toString()); - expect(Object.keys({ [timeout]: timeout })).toEqual([`${timeout}`]); - clearTimeout(+timeout); - }); -}); - -test("clearTimeout works with number id", () => { - const timeout = setTimeout(() => {}, 1); - const id = +timeout; - expect(() => clearTimeout(id)).not.toThrow(); -}); - -test("clearTimeout works with string id", () => { - const timeout = setTimeout(() => {}, 1); - const id = `${timeout}`; - expect(() => clearTimeout(id)).not.toThrow(); -}); - -//<#END_FILE: test-timers-to-primitive.js diff --git a/test/js/node/test/parallel/timers-unrefed-in-beforeexit.test.js b/test/js/node/test/parallel/timers-unrefed-in-beforeexit.test.js deleted file mode 100644 index 79967d0376..0000000000 --- a/test/js/node/test/parallel/timers-unrefed-in-beforeexit.test.js +++ /dev/null @@ -1,22 +0,0 @@ -//#FILE: test-timers-unrefed-in-beforeexit.js -//#SHA1: c873b545965c8b223c0bc38f2acdd4bc952427ea -//----------------- -"use strict"; - -test("unrefed timer in beforeExit should not prevent exit", () => { - const beforeExitHandler = jest.fn(() => { - setTimeout(jest.fn(), 1).unref(); - }); - - process.on("beforeExit", beforeExitHandler); - - // Simulate process exit - process.emit("beforeExit"); - - expect(beforeExitHandler).toHaveBeenCalledTimes(1); - - // Clean up the event listener - process.removeListener("beforeExit", beforeExitHandler); -}); - -//<#END_FILE: test-timers-unrefed-in-beforeexit.js diff --git a/test/js/node/test/parallel/timers-zero-timeout.test.js b/test/js/node/test/parallel/timers-zero-timeout.test.js deleted file mode 100644 index 3f16eaf6ae..0000000000 --- a/test/js/node/test/parallel/timers-zero-timeout.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-timers-zero-timeout.js -//#SHA1: f6d7cfab9ecf6f2f94001a4e153baf29dd3203b1 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -// https://github.com/joyent/node/issues/2079 - zero timeout drops extra args -test("setTimeout with zero timeout and extra args", done => { - const f = jest.fn((a, b, c) => { - expect(a).toBe("foo"); - expect(b).toBe("bar"); - expect(c).toBe("baz"); - done(); - }); - - setTimeout(f, 0, "foo", "bar", "baz"); - setTimeout(() => {}, 0); -}); - -test("setInterval with zero timeout and extra args", done => { - let ncalled = 3; - - const f = jest.fn((a, b, c) => { - expect(a).toBe("foo"); - expect(b).toBe("bar"); - expect(c).toBe("baz"); - if (--ncalled === 0) { - clearTimeout(iv); - expect(f).toHaveBeenCalledTimes(3); - done(); - } - }); - - const iv = setInterval(f, 0, "foo", "bar", "baz"); -}); - -//<#END_FILE: test-timers-zero-timeout.js diff --git a/test/js/node/test/parallel/tls-add-context.test.js b/test/js/node/test/parallel/tls-add-context.test.js deleted file mode 100644 index ca3d03c578..0000000000 --- a/test/js/node/test/parallel/tls-add-context.test.js +++ /dev/null @@ -1,93 +0,0 @@ -//#FILE: test-tls-add-context.js -//#SHA1: 61f134fe8c8fb63a00278b2d70dfecf11efb5df9 -//----------------- -"use strict"; - -const crypto = require("crypto"); -const fixtures = require("../common/fixtures"); -const tls = require("tls"); - -// Skip test if crypto is not available -if (!crypto) { - test.skip("missing crypto", () => {}); -} else { - function loadPEM(n) { - return fixtures.readKey(`${n}.pem`); - } - - const serverOptions = { - key: loadPEM("agent2-key"), - cert: loadPEM("agent2-cert"), - ca: [loadPEM("ca2-cert")], - requestCert: true, - rejectUnauthorized: false, - }; - - let connections = 0; - - test("TLS add context", done => { - const server = tls.createServer(serverOptions, c => { - if (++connections === 3) { - server.close(); - } - if (c.servername === "unknowncontext") { - expect(c.authorized).toBe(false); - return; - } - expect(c.authorized).toBe(true); - }); - - const secureContext = { - key: loadPEM("agent1-key"), - cert: loadPEM("agent1-cert"), - ca: [loadPEM("ca1-cert")], - }; - server.addContext("context1", secureContext); - server.addContext("context2", tls.createSecureContext(secureContext)); - - const clientOptionsBase = { - key: loadPEM("agent1-key"), - cert: loadPEM("agent1-cert"), - ca: [loadPEM("ca1-cert")], - rejectUnauthorized: false, - }; - - server.listen(0, () => { - const client1 = tls.connect( - { - ...clientOptionsBase, - port: server.address().port, - servername: "context1", - }, - () => { - client1.end(); - }, - ); - - const client2 = tls.connect( - { - ...clientOptionsBase, - port: server.address().port, - servername: "context2", - }, - () => { - client2.end(); - }, - ); - - const client3 = tls.connect( - { - ...clientOptionsBase, - port: server.address().port, - servername: "unknowncontext", - }, - () => { - client3.end(); - done(); - }, - ); - }); - }); -} - -//<#END_FILE: test-tls-add-context.js diff --git a/test/js/node/test/parallel/tls-ca-concat.test.js b/test/js/node/test/parallel/tls-ca-concat.test.js deleted file mode 100644 index dcee3b9522..0000000000 --- a/test/js/node/test/parallel/tls-ca-concat.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-tls-ca-concat.js -//#SHA1: 23b19f45d7777ee95a3c8d8ba4a727b149ea7409 -//----------------- -"use strict"; - -const fixtures = require("../common/fixtures"); - -// Check ca option can contain concatenated certs by prepending an unrelated -// non-CA cert and showing that agent6's CA root is still found. - -const { connect, keys } = require(fixtures.path("tls-connect")); - -test("ca option can contain concatenated certs", async () => { - await new Promise((resolve, reject) => { - connect( - { - client: { - checkServerIdentity: (servername, cert) => {}, - ca: `${keys.agent1.cert}\n${keys.agent6.ca}`, - }, - server: { - cert: keys.agent6.cert, - key: keys.agent6.key, - }, - }, - (err, pair, cleanup) => { - if (err) { - cleanup(); - reject(err); - } else { - cleanup(); - resolve(); - } - }, - ); - }); -}); - -//<#END_FILE: test-tls-ca-concat.js diff --git a/test/js/node/test/parallel/tls-cert-ext-encoding.test.js b/test/js/node/test/parallel/tls-cert-ext-encoding.test.js deleted file mode 100644 index ed33875b57..0000000000 --- a/test/js/node/test/parallel/tls-cert-ext-encoding.test.js +++ /dev/null @@ -1,90 +0,0 @@ -//#FILE: test-tls-cert-ext-encoding.js -//#SHA1: 235c886145290bd46f4f40110eace31e4cbcf55f -//----------------- -"use strict"; - -const tls = require("tls"); - -// NOTE: This certificate is hand-generated, hence it is not located in -// `test/fixtures/keys` to avoid confusion. -// -// The key property of this cert is that subjectAltName contains a string with -// a type `23` which cannot be encoded into string by `X509V3_EXT_print`. -const pem = ` ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAzrmfPz5M3wTq2/CwMeSQr/N+R1FCJ+O5n+SMleKvBqaK63eJ -kL4BnySMc+ZLKCt4UQSsPFIBK63QFq8n6/vjuTDMJiBTsvzytw8zJt1Zr2HA71N3 -VIPt6NdJ/w5lgddTYxR7XudJZJ5lk3PkG8ZgrhuenPYP80UJYVzAC2YZ9KYe3r2B -rVbut1j+8h0TwVcx2Zg5PorsC/EVxHwo4dCmIHceodikr3UVqHneRcrDBytdG6Mo -IqHhZJwBeii/EES9tpWwWbzYYh+38aGGLIF2h5UlVpr0bdBVVUg+uVX3y/Qmu2Qv -4CrAO2IPV6JER9Niwl3ktzNjOMAUQG6BCRSqRQIDAQABAoIBAAmB0+cOsG5ZRYvT -5+aDgnv1EMuq2wYGnRTTZ/vErxP5OM5XcwYrFtwAzEzQPIieZywisOEdTFx74+QH -LijWLsTnj5v5RKAorejpVArnhyZfsoXPKt/CKYDZ1ddbDCQKiRU3be0RafisqDM9 -0zHLz8pyDrtdPaKMfD/0Cgj8KxlrLTmfD4otPXds8fZpQe1hR1y12XKVp47l1siW -qFGTaUPDJpQ67xybR08x5DOqmyo4cNMOuReRWrc/qRbWint9U1882eOH09gVfpJZ -Gp6FZVPSgz10MZdLSPLhXqZkY4IxIvNltjBDqkmivd12CD+GVr0qUmTJHzTpk+kG -/CWuRQkCgYEA4EFf8SJHEl0fLDJnOQFyUPY3MalMuopUkQ5CBUe3QXjQhHXsRDfj -Ci/lyzShJkHPbMDHb/rx3lYZB0xNhwnMWKS1gCFVgOCOTZLfD0K1Anxc1hOSgVxI -y5FdO9VW7oQNlsMH/WuDHps0HhJW/00lcrmdyoUM1+fE/3yPQndhUmMCgYEA6/z6 -8Gq4PHHNql+gwunAH2cZKNdmcP4Co8MvXCZwIJsLenUuLIZQ/YBKZoM/y5X/cFAG -WFJJuUe6KFetPaDm6NgZgpOmawyUwd5czDjJ6wWgsRywiTISInfJlgWLBVMOuba7 -iBL9Xuy0hmcbj0ByoRW9l3gCiBX3yJw3I6wqXTcCgYBnjei22eRF15iIeTHvQfq+ -5iNwnEQhM7V/Uj0sYQR/iEGJmUaj7ca6somDf2cW2nblOlQeIpxD1jAyjYqTW/Pv -zwc9BqeMHqW3rqWwT1Z0smbQODOD5tB6qEKMWaSN+Y6o2qC65kWjAXpclI110PME -+i+iEDRxEsaGT8d7otLfDwKBgQCs+xBaQG/x5p2SAGzP0xYALstzc4jk1FzM+5rw -mkBgtiXQyqpg+sfNOkfPIvAVZEsMYax0+0SNKrWbMsGLRjFchmMUovQ+zccQ4NT2 -4b2op8Rlbxk8R9ahK1s5u7Bu47YMjZSjJwBQn4OobVX3SI994njJ2a9JX4j0pQWK -AX5AOwKBgAfOsr8HSHTcxSW4F9gegj+hXsRYbdA+eUkFhEGrYyRJgIlQrk/HbuZC -mKd/bQ5R/vwd1cxgV6A0APzpZtbwdhvP0RWji+WnPPovgGcfK0AHFstHnga67/uu -h2LHnKQZ1qWHn+BXWo5d7hBRwWVaK66g3GDN0blZpSz1kKcpy1Pl ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIICwjCCAaqgAwIBAgIDAQABMA0GCSqGSIb3DQEBDQUAMBUxEzARBgNVBAMWCmxv -Y2FsLmhvc3QwHhcNMTkxMjA1MDQyODMzWhcNNDQxMTI5MDQyODMzWjAVMRMwEQYD -VQQDFgpsb2NhbC5ob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA -zrmfPz5M3wTq2/CwMeSQr/N+R1FCJ+O5n+SMleKvBqaK63eJkL4BnySMc+ZLKCt4 -UQSsPFIBK63QFq8n6/vjuTDMJiBTsvzytw8zJt1Zr2HA71N3VIPt6NdJ/w5lgddT -YxR7XudJZJ5lk3PkG8ZgrhuenPYP80UJYVzAC2YZ9KYe3r2BrVbut1j+8h0TwVcx -2Zg5PorsC/EVxHwo4dCmIHceodikr3UVqHneRcrDBytdG6MoIqHhZJwBeii/EES9 -tpWwWbzYYh+38aGGLIF2h5UlVpr0bdBVVUg+uVX3y/Qmu2Qv4CrAO2IPV6JER9Ni -wl3ktzNjOMAUQG6BCRSqRQIDAQABoxswGTAXBgNVHREEEDAOlwwqLmxvY2FsLmhv -c3QwDQYJKoZIhvcNAQENBQADggEBAH5ThRLDLwOGuhKsifyiq7k8gbx1FqRegO7H -SIiIYYB35v5Pk0ZPN8QBJwNQzJEjUMjCpHXNdBxknBXRaA8vkbnryMfJm37gPTwA -m6r0uEG78WgcEAe8bgf9iKtQGP/iydKXpSSpDgKoHbswIxD5qtzT+o6VNnkRTSfK -/OGwakluFSoJ/Q9rLpR8lKjA01BhetXMmHbETiY8LSkxOymMldXSzUTD1WdrVn8U -L3dobxT//R/0GraKXG02mf3gZNlb0MMTvW0pVwVy39YmcPEGh8L0hWh1rpAA/VXC -f79uOowv3lLTzQ9na5EThA0tp8d837hdYrrIHh5cfTqBDxG0Tu8= ------END CERTIFICATE----- -`; - -const options = { - key: pem, - cert: pem, -}; - -test("TLS certificate extension encoding", async () => { - const server = tls.createServer(options, socket => { - socket.end(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - const client = tls.connect( - { - port: server.address().port, - rejectUnauthorized: false, - }, - () => { - // This should not crash process: - client.getPeerCertificate(); - - server.close(); - client.end(); - resolve(); - }, - ); - }); - }); -}); - -//<#END_FILE: test-tls-cert-ext-encoding.js diff --git a/test/js/node/test/parallel/tls-client-abort.test.js b/test/js/node/test/parallel/tls-client-abort.test.js deleted file mode 100644 index 4679c1f0e4..0000000000 --- a/test/js/node/test/parallel/tls-client-abort.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-tls-client-abort.js -//#SHA1: e4f4d09f8de79ff5f4bdefcdaf1bbebb49f3cc16 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const tls = require("tls"); -const fs = require("fs"); -const path = require("path"); - -const hasCrypto = (() => { - try { - require("crypto"); - return true; - } catch { - return false; - } -})(); - -if (!hasCrypto) { - test.skip("missing crypto", () => {}); -} else { - test("TLS client abort", () => { - const cert = fs.readFileSync(path.join(__dirname, "..", "fixtures", "keys", "rsa_cert.crt")); - const key = fs.readFileSync(path.join(__dirname, "..", "fixtures", "keys", "rsa_private.pem")); - - const onConnect = jest.fn(); - const conn = tls.connect({ cert, key, port: 0 }, onConnect); - - conn.on("error", () => {}); // Expecting an error, but not testing its content - conn.destroy(); - - expect(onConnect).not.toHaveBeenCalled(); - }); -} - -//<#END_FILE: test-tls-client-abort.js diff --git a/test/js/node/test/parallel/tls-client-default-ciphers.test.js b/test/js/node/test/parallel/tls-client-default-ciphers.test.js deleted file mode 100644 index d337071777..0000000000 --- a/test/js/node/test/parallel/tls-client-default-ciphers.test.js +++ /dev/null @@ -1,52 +0,0 @@ -//#FILE: test-tls-client-default-ciphers.js -//#SHA1: 8a9af503503ffc8b8a7c27089fd7cf417e22ec16 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const tls = require("tls"); - -if (typeof Bun !== "undefined") { - test = it; -} - -if (!("crypto" in process.versions)) { - test.skip("missing crypto", () => {}); -} else { - test("tls.connect uses default ciphers", () => { - let ciphers = ""; - - class Done extends Error {} - - tls.createSecureContext = function (options) { - ciphers = options.ciphers; - throw new Done(); - }; - - expect(() => tls.connect()).toThrow(Done); - - expect(ciphers).toBe(tls.DEFAULT_CIPHERS); - }); -} - -//<#END_FILE: test-tls-client-default-ciphers.js diff --git a/test/js/node/test/parallel/tls-env-extra-ca-no-crypto.test.js b/test/js/node/test/parallel/tls-env-extra-ca-no-crypto.test.js deleted file mode 100644 index 5b48f8f775..0000000000 --- a/test/js/node/test/parallel/tls-env-extra-ca-no-crypto.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-tls-env-extra-ca-no-crypto.js -//#SHA1: da8421700b140b9bef4723eee10dbae7786423b6 -//----------------- -"use strict"; - -const { fork } = require("child_process"); -const path = require("path"); - -// This test ensures that trying to load extra certs won't throw even when -// there is no crypto support, i.e., built with "./configure --without-ssl". -if (process.argv[2] === "child") { - // exit -} else { - const NODE_EXTRA_CA_CERTS = path.join(__dirname, "..", "fixtures", "keys", "ca1-cert.pem"); - - test("Loading extra certs without crypto support", () => { - return new Promise(resolve => { - fork(__filename, ["child"], { env: { ...process.env, NODE_EXTRA_CA_CERTS } }).on("exit", status => { - // Client did not succeed in connecting - expect(status).toBe(0); - resolve(); - }); - }); - }); -} - -//<#END_FILE: test-tls-env-extra-ca-no-crypto.js diff --git a/test/js/node/test/parallel/tls-fast-writing.test.js b/test/js/node/test/parallel/tls-fast-writing.test.js deleted file mode 100644 index 7656d5eb15..0000000000 --- a/test/js/node/test/parallel/tls-fast-writing.test.js +++ /dev/null @@ -1,97 +0,0 @@ -//#FILE: test-tls-fast-writing.js -//#SHA1: 3a9ce4612ccf460fb5c0dfd4be6f8f5cad06c4a4 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const fixtures = require("../common/fixtures"); -const tls = require("tls"); - -const options = { - key: fixtures.readKey("rsa_private.pem"), - cert: fixtures.readKey("rsa_cert.crt"), - ca: [fixtures.readKey("rsa_ca.crt")], -}; - -let gotChunk = false; -let gotDrain = false; - -function onconnection(conn) { - conn.on("data", function (c) { - if (!gotChunk) { - gotChunk = true; - console.log("ok - got chunk"); - } - - // Just some basic sanity checks. - expect(c.length).toBeGreaterThan(0); - expect(Buffer.isBuffer(c)).toBe(true); - - if (gotDrain) { - process.exit(0); - } - }); -} - -test("TLS fast writing", done => { - if (!process.versions.openssl) { - console.log("1..0 # Skipped: missing crypto"); - return done(); - } - - const server = tls.createServer(options, onconnection); - - server.listen(0, function () { - const chunk = Buffer.alloc(1024, "x"); - const opt = { port: this.address().port, rejectUnauthorized: false }; - const conn = tls.connect(opt, function () { - conn.on("drain", ondrain); - write(); - }); - - function ondrain() { - if (!gotDrain) { - gotDrain = true; - console.log("ok - got drain"); - } - if (gotChunk) { - process.exit(0); - } - write(); - } - - function write() { - // This needs to return false eventually - while (false !== conn.write(chunk)); - } - }); - - // Clean up - process.on("exit", () => { - server.close(); - expect(gotChunk).toBe(true); - expect(gotDrain).toBe(true); - done(); - }); -}); - -//<#END_FILE: test-tls-fast-writing.js diff --git a/test/js/node/test/parallel/tls-inception.test.js b/test/js/node/test/parallel/tls-inception.test.js deleted file mode 100644 index 6aef54d4c3..0000000000 --- a/test/js/node/test/parallel/tls-inception.test.js +++ /dev/null @@ -1,98 +0,0 @@ -//#FILE: test-tls-inception.js -//#SHA1: 410893674973cae3603656c032a18e01a1cd759a -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const crypto = require("crypto"); -const fixtures = require("../common/fixtures"); -const tls = require("tls"); -const net = require("net"); - -if (!crypto.getCurves().includes("prime256v1")) { - test.skip("missing crypto support"); -} - -const options = { - key: fixtures.readKey("rsa_private.pem"), - cert: fixtures.readKey("rsa_cert.crt"), -}; - -const body = "A".repeat(40000); - -test("TLS inception", async () => { - // the "proxy" server - const a = tls.createServer(options, socket => { - const myOptions = { - host: "127.0.0.1", - port: b.address().port, - rejectUnauthorized: false, - }; - const dest = net.connect(myOptions); - dest.pipe(socket); - socket.pipe(dest); - - dest.on("end", () => { - socket.destroy(); - }); - }); - - // the "target" server - const b = tls.createServer(options, socket => { - socket.end(body); - }); - - await new Promise(resolve => { - a.listen(0, () => { - b.listen(0, resolve); - }); - }); - - const myOptions = { - host: "127.0.0.1", - port: a.address().port, - rejectUnauthorized: false, - }; - - return new Promise(resolve => { - const socket = tls.connect(myOptions); - const ssl = tls.connect({ - socket: socket, - rejectUnauthorized: false, - }); - ssl.setEncoding("utf8"); - let buf = ""; - ssl.on("data", data => { - buf += data; - }); - ssl.on("end", () => { - expect(buf).toBe(body); - ssl.end(); - a.close(); - b.close(); - resolve(); - }); - }); -}); - -//<#END_FILE: test-tls-inception.js diff --git a/test/js/node/test/parallel/tls-js-stream.test.js b/test/js/node/test/parallel/tls-js-stream.test.js deleted file mode 100644 index 811e6a23d3..0000000000 --- a/test/js/node/test/parallel/tls-js-stream.test.js +++ /dev/null @@ -1,78 +0,0 @@ -//#FILE: test-tls-js-stream.js -//#SHA1: 90a8d9d14bac997dbf3abb56da784bfcba8efee6 -//----------------- -"use strict"; - -const fixtures = require("../common/fixtures"); -const net = require("net"); -const stream = require("stream"); -const tls = require("tls"); - -if (!tls.createSecureContext) { - test.skip("missing crypto"); -} - -test("TLS over JavaScript stream", done => { - const server = tls.createServer( - { - key: fixtures.readKey("agent1-key.pem"), - cert: fixtures.readKey("agent1-cert.pem"), - }, - c => { - console.log("new client"); - c.resume(); - c.end("ohai"); - }, - ); - - server.listen(0, () => { - const raw = net.connect(server.address().port); - - let pending = false; - raw.on("readable", () => { - if (pending) p._read(); - }); - - raw.on("end", () => { - p.push(null); - }); - - const p = new stream.Duplex({ - read: function read() { - pending = false; - - const chunk = raw.read(); - if (chunk) { - console.log("read", chunk); - this.push(chunk); - } else { - pending = true; - } - }, - write: function write(data, enc, cb) { - console.log("write", data, enc); - raw.write(data, enc, cb); - }, - }); - - const socket = tls.connect( - { - socket: p, - rejectUnauthorized: false, - }, - () => { - console.log("client secure"); - socket.resume(); - socket.end("hello"); - }, - ); - - socket.once("close", () => { - console.log("client close"); - server.close(); - done(); - }); - }); -}); - -//<#END_FILE: test-tls-js-stream.js diff --git a/test/js/node/test/parallel/tls-net-socket-keepalive.test.js b/test/js/node/test/parallel/tls-net-socket-keepalive.test.js deleted file mode 100644 index a0378791cc..0000000000 --- a/test/js/node/test/parallel/tls-net-socket-keepalive.test.js +++ /dev/null @@ -1,80 +0,0 @@ -//#FILE: test-tls-net-socket-keepalive.js -//#SHA1: 9ae6965b63d37c0f9cb2ab7d068d4da2a68b8b1f -//----------------- -"use strict"; - -const fixtures = require("../common/fixtures"); -const tls = require("tls"); -const net = require("net"); - -// This test ensures that when tls sockets are created with `allowHalfOpen`, -// they won't hang. -const key = fixtures.readKey("agent1-key.pem"); -const cert = fixtures.readKey("agent1-cert.pem"); -const ca = fixtures.readKey("ca1-cert.pem"); -const options = { - key, - cert, - ca: [ca], -}; - -test("TLS sockets with allowHalfOpen do not hang", done => { - const server = tls.createServer(options, conn => { - conn.write("hello", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - }); - conn.on("data", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - }); - conn.on("end", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - }); - conn.on("data", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - }); - conn.on("close", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - }); - conn.end(); - }); - - server.listen(0, () => { - const netSocket = new net.Socket({ - allowHalfOpen: true, - }); - - const socket = tls.connect({ - socket: netSocket, - rejectUnauthorized: false, - }); - - const { port, address } = server.address(); - - // Doing `net.Socket.connect()` after `tls.connect()` will make tls module - // wrap the socket in StreamWrap. - netSocket.connect({ - port, - address, - }); - - socket.on("secureConnect", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - }); - socket.on("end", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - }); - socket.on("data", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - }); - socket.on("close", () => { - expect(true).toBe(true); // Equivalent to common.mustCall() - server.close(); - done(); - }); - - socket.write("hello"); - socket.end(); - }); -}); - -//<#END_FILE: test-tls-net-socket-keepalive.js diff --git a/test/js/node/test/parallel/tls-peer-certificate-multi-keys.test.js b/test/js/node/test/parallel/tls-peer-certificate-multi-keys.test.js deleted file mode 100644 index d1cfe70e15..0000000000 --- a/test/js/node/test/parallel/tls-peer-certificate-multi-keys.test.js +++ /dev/null @@ -1,87 +0,0 @@ -//#FILE: test-tls-peer-certificate-multi-keys.js -//#SHA1: d30d685d74ebea73274e19cea1a19a2b8cea5120 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const tls = require("tls"); -const fixtures = require("../common/fixtures"); - -const options = { - key: fixtures.readKey("rsa_private.pem"), - cert: fixtures.readKey("rsa_cert.crt"), -}; - -let server; - -beforeAll(() => { - if (!process.versions.openssl) { - return test.skip("missing crypto"); - } -}); - -afterAll(() => { - if (server) { - server.close(); - } -}); - -test("TLS peer certificate with multiple keys", async () => { - server = tls.createServer(options, cleartext => { - cleartext.end("World"); - }); - - const serverSecureConnectionPromise = new Promise(resolve => { - server.once("secureConnection", socket => { - const cert = socket.getCertificate(); - // The server's local cert is the client's peer cert. - expect(cert.subject.OU).toEqual(["Test TLS Certificate", "Engineering"]); - resolve(); - }); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const clientConnectionPromise = new Promise((resolve, reject) => { - const socket = tls.connect( - { - port: server.address().port, - rejectUnauthorized: false, - }, - () => { - const peerCert = socket.getPeerCertificate(); - expect(peerCert.subject.OU).toEqual(["Test TLS Certificate", "Engineering"]); - socket.end("Hello"); - resolve(); - }, - ); - - socket.on("error", reject); - }); - - await Promise.all([serverSecureConnectionPromise, clientConnectionPromise]); -}); - -//<#END_FILE: test-tls-peer-certificate-multi-keys.js diff --git a/test/js/node/test/parallel/tls-startcom-wosign-whitelist.test.js b/test/js/node/test/parallel/tls-startcom-wosign-whitelist.test.js deleted file mode 100644 index fde4c90e29..0000000000 --- a/test/js/node/test/parallel/tls-startcom-wosign-whitelist.test.js +++ /dev/null @@ -1,82 +0,0 @@ -//#FILE: test-tls-startcom-wosign-whitelist.js -//#SHA1: 6742ecdeeaa94ca1efad850b021d5308b3077358 -//----------------- -"use strict"; - -const tls = require("tls"); -const fixtures = require("../common/fixtures"); - -function loadPEM(n) { - return fixtures.readKey(`${n}.pem`); -} - -const testCases = [ - { - // agent8 is signed by fake-startcom-root with notBefore of - // Oct 20 23:59:59 2016 GMT. It passes StartCom/WoSign check. - serverOpts: { - key: loadPEM("agent8-key"), - cert: loadPEM("agent8-cert"), - }, - clientOpts: { - ca: loadPEM("fake-startcom-root-cert"), - port: undefined, - rejectUnauthorized: true, - }, - errorCode: "CERT_REVOKED", - }, - { - // agent9 is signed by fake-startcom-root with notBefore of - // Oct 21 00:00:01 2016 GMT. It fails StartCom/WoSign check. - serverOpts: { - key: loadPEM("agent9-key"), - cert: loadPEM("agent9-cert"), - }, - clientOpts: { - ca: loadPEM("fake-startcom-root-cert"), - port: undefined, - rejectUnauthorized: true, - }, - errorCode: "CERT_REVOKED", - }, -]; - -describe("TLS StartCom/WoSign Whitelist", () => { - let finishedTests = 0; - - afterAll(() => { - expect(finishedTests).toBe(testCases.length); - }); - - testCases.forEach((tcase, index) => { - it(`should handle case ${index + 1} correctly`, async () => { - const server = tls.createServer(tcase.serverOpts, s => { - s.resume(); - }); - - await new Promise(resolve => { - server.listen(0, () => { - tcase.clientOpts.port = server.address().port; - const client = tls.connect(tcase.clientOpts); - - client.on("error", e => { - expect(e.code).toBe(tcase.errorCode); - server.close(resolve); - }); - - client.on("secureConnect", () => { - // agent8 can pass StartCom/WoSign check so that the secureConnect - // is established. - expect(tcase.errorCode).toBe("CERT_REVOKED"); - client.end(); - server.close(resolve); - }); - }); - }); - - finishedTests++; - }); - }); -}); - -//<#END_FILE: test-tls-startcom-wosign-whitelist.js diff --git a/test/js/node/test/parallel/tls-ticket-cluster.test.js b/test/js/node/test/parallel/tls-ticket-cluster.test.js deleted file mode 100644 index 2def9c478c..0000000000 --- a/test/js/node/test/parallel/tls-ticket-cluster.test.js +++ /dev/null @@ -1,153 +0,0 @@ -//#FILE: test-tls-ticket-cluster.js -//#SHA1: a65d425111990ee164b458fd373b9f5bb083f6df -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const tls = require("tls"); -const cluster = require("cluster"); -const fixtures = require("../common/fixtures"); - -const workerCount = 4; -const expectedReqCount = 16; - -if (!process.env.NODE_SKIP_NO_CRYPTO_TEST) { - test("skip if missing crypto", () => { - const hasCrypto = typeof process.versions.openssl === "string"; - if (!hasCrypto) { - console.log("1..0 # Skipped: missing crypto"); - } - expect(hasCrypto).toBe(true); - }); -} - -if (cluster.isPrimary) { - let listeningCount = 0; - let reusedCount = 0; - let reqCount = 0; - let lastSession = null; - let workerPort = null; - - function shoot() { - console.error("[primary] connecting", workerPort, "session?", !!lastSession); - const c = tls - .connect( - workerPort, - { - session: lastSession, - rejectUnauthorized: false, - }, - () => { - c.on("end", c.end); - }, - ) - .on("close", () => { - // Wait for close to shoot off another connection. We don't want to shoot - // until a new session is allocated, if one will be. The new session is - // not guaranteed on secureConnect (it depends on TLS1.2 vs TLS1.3), but - // it is guaranteed to happen before the connection is closed. - if (++reqCount === expectedReqCount) { - Object.keys(cluster.workers).forEach(function (id) { - cluster.workers[id].send("die"); - }); - } else { - shoot(); - } - }) - .once("session", session => { - expect(lastSession).toBeFalsy(); - lastSession = session; - }); - - c.resume(); // See close_notify comment in server - } - - function fork() { - const worker = cluster.fork(); - worker.on("message", ({ msg, port }) => { - console.error("[primary] got %j", msg); - if (msg === "reused") { - ++reusedCount; - } else if (msg === "listening" && ++listeningCount === workerCount) { - workerPort = port; - shoot(); - } - }); - - worker.on("exit", () => { - console.error("[primary] worker died"); - }); - } - - test("cluster primary", () => { - for (let i = 0; i < workerCount; i++) { - fork(); - } - - process.on("exit", () => { - expect(reqCount).toBe(expectedReqCount); - expect(reusedCount + 1).toBe(reqCount); - }); - }); -} else { - const key = fixtures.readKey("rsa_private.pem"); - const cert = fixtures.readKey("rsa_cert.crt"); - - const options = { key, cert }; - - const server = tls.createServer(options, c => { - console.error("[worker] connection reused?", c.isSessionReused()); - if (c.isSessionReused()) { - process.send({ msg: "reused" }); - } else { - process.send({ msg: "not-reused" }); - } - // Used to just .end(), but that means client gets close_notify before - // NewSessionTicket. Send data until that problem is solved. - c.end("x"); - }); - - server.listen(0, () => { - const { port } = server.address(); - process.send({ - msg: "listening", - port, - }); - }); - - process.on("message", function listener(msg) { - console.error("[worker] got %j", msg); - if (msg === "die") { - server.close(() => { - console.error("[worker] server close"); - process.exit(); - }); - } - }); - - process.on("exit", () => { - console.error("[worker] exit"); - }); -} - -//<#END_FILE: test-tls-ticket-cluster.js diff --git a/test/js/node/test/parallel/tls-transport-destroy-after-own-gc.test.js b/test/js/node/test/parallel/tls-transport-destroy-after-own-gc.test.js deleted file mode 100644 index 1f7c20dda3..0000000000 --- a/test/js/node/test/parallel/tls-transport-destroy-after-own-gc.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-tls-transport-destroy-after-own-gc.js -//#SHA1: e2ef35ad88444196d24664e93fb9efdad050c876 -//----------------- -// Flags: --expose-gc -"use strict"; - -// Regression test for https://github.com/nodejs/node/issues/17475 -// Unfortunately, this tests only "works" reliably when checked with valgrind or -// a similar tool. - -const { TLSSocket } = require("tls"); -const makeDuplexPair = require("../common/duplexpair"); - -test("TLSSocket destruction after garbage collection", done => { - if (!process.versions.openssl) { - done(); - return; - } - - let { clientSide } = makeDuplexPair(); - - let clientTLS = new TLSSocket(clientSide, { isServer: false }); - let clientTLSHandle = clientTLS._handle; // eslint-disable-line no-unused-vars - - setImmediate(() => { - clientTLS = null; - global.gc(); - clientTLSHandle = null; - global.gc(); - setImmediate(() => { - clientSide = null; - global.gc(); - // If we've reached this point without crashing, the test has passed - done(); - }); - }); -}); - -//<#END_FILE: test-tls-transport-destroy-after-own-gc.js diff --git a/test/js/node/test/parallel/tls-wrap-econnreset.test.js b/test/js/node/test/parallel/tls-wrap-econnreset.test.js deleted file mode 100644 index 3f11b13be2..0000000000 --- a/test/js/node/test/parallel/tls-wrap-econnreset.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-tls-wrap-econnreset.js -//#SHA1: 22f57b68ee3c5d271a9235972865773da523a34e -//----------------- -"use strict"; - -const net = require("net"); -const tls = require("tls"); - -// Skip the test if crypto is not available -if (!("crypto" in process.versions)) { - test.skip("missing crypto", () => {}); -} else { - test("TLS connection reset", async () => { - const server = net.createServer(c => { - c.end(); - }); - - await new Promise(resolve => { - server.listen(0, resolve); - }); - - const port = server.address().port; - - let errored = false; - const tlsConnection = tls.connect(port, "127.0.0.1"); - - await expect( - new Promise((_, reject) => { - tlsConnection.once("error", reject); - }), - ).rejects.toMatchObject({ - code: "ECONNRESET", - path: undefined, - host: "127.0.0.1", - port: port, - localAddress: undefined, - message: expect.any(String), - }); - - errored = true; - server.close(); - - await new Promise(resolve => { - tlsConnection.on("close", resolve); - }); - - expect(errored).toBe(true); - }); -} - -//<#END_FILE: test-tls-wrap-econnreset.js diff --git a/test/js/node/test/parallel/tls-zero-clear-in.test.js b/test/js/node/test/parallel/tls-zero-clear-in.test.js deleted file mode 100644 index 26283f4311..0000000000 --- a/test/js/node/test/parallel/tls-zero-clear-in.test.js +++ /dev/null @@ -1,81 +0,0 @@ -//#FILE: test-tls-zero-clear-in.js -//#SHA1: 6014fd3aa5a294b4e8594a32f0eb8e7b3c206213 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const tls = require("tls"); -const { readKey } = require("../common/fixtures"); - -if (!process.versions.openssl) { - test.skip("missing crypto"); -} - -const cert = readKey("rsa_cert.crt"); -const key = readKey("rsa_private.pem"); - -test("SSL_write() call with 0 bytes should not be treated as error", done => { - const server = tls.createServer( - { - cert, - key, - }, - c => { - // Nop - setTimeout(() => { - c.end(); - server.close(); - }, 20); - }, - ); - - server.listen(0, () => { - const conn = tls.connect( - { - cert: cert, - key: key, - rejectUnauthorized: false, - port: server.address().port, - }, - () => { - setTimeout(() => { - conn.destroy(); - }, 20); - }, - ); - - // SSL_write() call's return value, when called 0 bytes, should not be - // treated as error. - conn.end(""); - - conn.on("error", () => { - done(new Error("Unexpected error event")); - }); - - setTimeout(() => { - done(); - }, 100); - }); -}); - -//<#END_FILE: test-tls-zero-clear-in.js diff --git a/test/js/node/test/parallel/tty-stdin-end.test.js b/test/js/node/test/parallel/tty-stdin-end.test.js deleted file mode 100644 index cb2f825045..0000000000 --- a/test/js/node/test/parallel/tty-stdin-end.test.js +++ /dev/null @@ -1,15 +0,0 @@ -//#FILE: test-tty-stdin-end.js -//#SHA1: 66243635acf852b7a108d06e289e5db2b2573bad -//----------------- -"use strict"; - -// This test ensures that Node.js doesn't crash on `process.stdin.emit("end")`. -// https://github.com/nodejs/node/issues/1068 - -test("process.stdin.emit('end') doesn't crash", () => { - expect(() => { - process.stdin.emit("end"); - }).not.toThrow(); -}); - -//<#END_FILE: test-tty-stdin-end.js diff --git a/test/js/node/test/parallel/tz-version.test.js b/test/js/node/test/parallel/tz-version.test.js deleted file mode 100644 index 04bac4dcdb..0000000000 --- a/test/js/node/test/parallel/tz-version.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-tz-version.js -//#SHA1: 7c06414dda474dff448a3998499dddffdf084126 -//----------------- -"use strict"; - -// Skip the test if Intl is not available -if (typeof Intl === "undefined") { - test.skip("missing Intl", () => {}); -} else { - // Refs: https://github.com/nodejs/node/blob/1af63a90ca3a59ca05b3a12ad7dbea04008db7d9/configure.py#L1694-L1711 - if (process.config.variables.icu_path !== "deps/icu-small") { - // If Node.js is configured to use its built-in ICU, it uses a strict subset - // of ICU formed using `tools/icu/shrink-icu-src.py`, which is present in - // `deps/icu-small`. It is not the same as configuring the build with - // `./configure --with-intl=small-icu`. The latter only uses a subset of the - // locales, i.e., it uses the English locale, `root,en`, by default and other - // locales can also be specified using the `--with-icu-locales` option. - test.skip("not using the icu data file present in deps/icu-small/source/data/in/icudt##l.dat.bz2", () => {}); - } else { - const { readFileSync } = require("fs"); - const path = require("path"); - - // This test ensures the correctness of the automated timezone upgrade PRs. - - test("timezone version matches expected version", () => { - const expectedVersion = readFileSync(path.join(__dirname, "fixtures", "tz-version.txt"), "utf8").trim(); - expect(process.versions.tz).toBe(expectedVersion); - }); - } -} - -//<#END_FILE: test-tz-version.js diff --git a/test/js/node/test/parallel/unhandled-exception-with-worker-inuse.test.js b/test/js/node/test/parallel/unhandled-exception-with-worker-inuse.test.js deleted file mode 100644 index 6acae8cf95..0000000000 --- a/test/js/node/test/parallel/unhandled-exception-with-worker-inuse.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-unhandled-exception-with-worker-inuse.js -//#SHA1: 4eb89fdb9d675dbef4f89ed61ab77225b9200b4a -//----------------- -"use strict"; - -// https://github.com/nodejs/node/issues/45421 -// -// Check that node will NOT call v8::Isolate::SetIdle() when exiting -// due to an unhandled exception, otherwise the assertion(enabled in -// debug build only) in the SetIdle(), which checks that the vm state -// is either EXTERNAL or IDLE will fail. -// -// The root cause of this issue is that before PerIsolateMessageListener() -// is invoked by v8, v8 preserves the JS vm state, although it should -// switch to EXTERNEL. https://bugs.chromium.org/p/v8/issues/detail?id=13464 -// -// Therefore, this commit can be considered as an workaround of the v8 bug, -// but we also find it not useful to call SetIdle() when terminating. - -if (process.argv[2] === "child") { - const { Worker } = require("worker_threads"); - new Worker("", { eval: true }); - throw new Error("xxx"); -} else { - const { spawnSync } = require("child_process"); - - test("unhandled exception with worker in use", () => { - const result = spawnSync(process.execPath, [__filename, "child"]); - - const stderr = result.stderr.toString().trim(); - // Expect error message to be preserved - expect(stderr).toMatch(/xxx/); - // Expect no crash - expect(result.status).not.toBe(null); - expect(result.signal).toBe(null); - }); -} - -//<#END_FILE: test-unhandled-exception-with-worker-inuse.js diff --git a/test/js/node/test/parallel/url-canparse-whatwg.test.js b/test/js/node/test/parallel/url-canparse-whatwg.test.js deleted file mode 100644 index a6c920c536..0000000000 --- a/test/js/node/test/parallel/url-canparse-whatwg.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-url-canParse-whatwg.js -//#SHA1: e1170e8b8d0057443bfb307c64dbd27b204ac2f0 -//----------------- -"use strict"; - -test("URL.canParse requires one argument", () => { - expect(() => { - URL.canParse(); - }).toThrow( - expect.objectContaining({ - code: "ERR_MISSING_ARGS", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -test("URL.canParse works with v8 fast api", () => { - // This test is to ensure that the v8 fast api works. - for (let i = 0; i < 1e5; i++) { - expect(URL.canParse("https://www.example.com/path/?query=param#hash")).toBe(true); - } -}); - -//<#END_FILE: test-url-canParse-whatwg.js diff --git a/test/js/node/test/parallel/url-domain-ascii-unicode.test.js b/test/js/node/test/parallel/url-domain-ascii-unicode.test.js deleted file mode 100644 index 0a716939a9..0000000000 --- a/test/js/node/test/parallel/url-domain-ascii-unicode.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-url-domain-ascii-unicode.js -//#SHA1: 717d40eef6d2d8f5adccf01fe09dc43f8b776e13 -//----------------- -"use strict"; - -const url = require("url"); - -const domainToASCII = url.domainToASCII; -const domainToUnicode = url.domainToUnicode; - -const domainWithASCII = [ - ["ıíd", "xn--d-iga7r"], - ["يٴ", "xn--mhb8f"], - ["www.ϧƽəʐ.com", "www.xn--cja62apfr6c.com"], - ["новини.com", "xn--b1amarcd.com"], - ["名がドメイン.com", "xn--v8jxj3d1dzdz08w.com"], - ["افغانستا.icom.museum", "xn--mgbaal8b0b9b2b.icom.museum"], - ["الجزائر.icom.fake", "xn--lgbbat1ad8j.icom.fake"], - ["भारत.org", "xn--h2brj9c.org"], -]; - -describe("URL domain ASCII and Unicode conversion", () => { - // Skip the entire test suite if Intl is not available - beforeAll(() => { - if (typeof Intl === "undefined") { - throw new Error("missing Intl"); - } - }); - - test.each(domainWithASCII)("converts %s <-> %s", (domain, ascii) => { - const domainConvertedToASCII = domainToASCII(domain); - expect(domainConvertedToASCII).toBe(ascii); - - const asciiConvertedToUnicode = domainToUnicode(ascii); - expect(asciiConvertedToUnicode).toBe(domain); - }); -}); - -//<#END_FILE: test-url-domain-ascii-unicode.js diff --git a/test/js/node/test/parallel/url-is-url.test.js b/test/js/node/test/parallel/url-is-url.test.js deleted file mode 100644 index de30ec10f0..0000000000 --- a/test/js/node/test/parallel/url-is-url.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-url-is-url.js -//#SHA1: 337a63661b82d89cf2592ee32d91780acb8e5925 -//----------------- -"use strict"; - -const { URL, parse } = require("url"); - -// Remove the internal module usage -// const { isURL } = require('internal/url'); - -// Implement a simple isURL function for testing purposes -function isURL(input) { - return input instanceof URL; -} - -test("isURL function", () => { - expect(isURL(new URL("https://www.nodejs.org"))).toBe(true); - expect(isURL(parse("https://www.nodejs.org"))).toBe(false); - expect( - isURL({ - href: "https://www.nodejs.org", - protocol: "https:", - path: "/", - }), - ).toBe(false); -}); - -//<#END_FILE: test-url-is-url.js diff --git a/test/js/node/test/parallel/url-revokeobjecturl.test.js b/test/js/node/test/parallel/url-revokeobjecturl.test.js deleted file mode 100644 index 190ed562f5..0000000000 --- a/test/js/node/test/parallel/url-revokeobjecturl.test.js +++ /dev/null @@ -1,20 +0,0 @@ -//#FILE: test-url-revokeobjecturl.js -//#SHA1: 573bfad806102976807ad71fef71b079005d8bfa -//----------------- -"use strict"; - -// Test ensures that the function receives the url argument. - -test("URL.revokeObjectURL() throws with missing argument", () => { - expect(() => { - URL.revokeObjectURL(); - }).toThrow( - expect.objectContaining({ - code: "ERR_MISSING_ARGS", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-url-revokeobjecturl.js diff --git a/test/js/node/test/parallel/url-urltooptions.test.js b/test/js/node/test/parallel/url-urltooptions.test.js deleted file mode 100644 index 3cb50f21b5..0000000000 --- a/test/js/node/test/parallel/url-urltooptions.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-url-urltooptions.js -//#SHA1: 0ba1cb976ec5888306f7c820a39afcb93b882d03 -//----------------- -"use strict"; - -const { URL } = require("url"); -const { urlToHttpOptions } = require("url"); - -describe("urlToHttpOptions", () => { - test("converts URL object to HTTP options", () => { - const urlObj = new URL("http://user:pass@foo.bar.com:21/aaa/zzz?l=24#test"); - const opts = urlToHttpOptions(urlObj); - - expect(opts).not.toBeInstanceOf(URL); - expect(opts.protocol).toBe("http:"); - expect(opts.auth).toBe("user:pass"); - expect(opts.hostname).toBe("foo.bar.com"); - expect(opts.port).toBe(21); - expect(opts.path).toBe("/aaa/zzz?l=24"); - expect(opts.pathname).toBe("/aaa/zzz"); - expect(opts.search).toBe("?l=24"); - expect(opts.hash).toBe("#test"); - }); - - test("handles IPv6 hostname correctly", () => { - const { hostname } = urlToHttpOptions(new URL("http://[::1]:21")); - expect(hostname).toBe("::1"); - }); - - test("handles copied URL object with missing data properties", () => { - const urlObj = new URL("http://user:pass@foo.bar.com:21/aaa/zzz?l=24#test"); - const copiedUrlObj = { ...urlObj }; - const copiedOpts = urlToHttpOptions(copiedUrlObj); - - expect(copiedOpts).not.toBeInstanceOf(URL); - expect(copiedOpts.protocol).toBeUndefined(); - expect(copiedOpts.auth).toBeUndefined(); - expect(copiedOpts.hostname).toBeUndefined(); - expect(copiedOpts.port).toBeNaN(); - expect(copiedOpts.path).toBe(""); - expect(copiedOpts.pathname).toBeUndefined(); - expect(copiedOpts.search).toBeUndefined(); - expect(copiedOpts.hash).toBeUndefined(); - expect(copiedOpts.href).toBeUndefined(); - }); -}); - -//<#END_FILE: test-url-urltooptions.js diff --git a/test/js/node/test/parallel/utf8-scripts.test.js b/test/js/node/test/parallel/utf8-scripts.test.js deleted file mode 100644 index e4c5a4bec4..0000000000 --- a/test/js/node/test/parallel/utf8-scripts.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-utf8-scripts.js -//#SHA1: 103e32f817ebd35d617fba00e947304390c431d5 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -// üäö - -test("UTF-8 scripts", () => { - const consoleSpy = jest.spyOn(console, "log"); - - console.log("Σὲ γνωρίζω ἀπὸ τὴν κόψη"); - - expect(consoleSpy).toHaveBeenCalledWith("Σὲ γνωρίζω ἀπὸ τὴν κόψη"); - consoleSpy.mockRestore(); - - expect("Hellö Wörld").toMatch(/Hellö Wörld/); -}); - -//<#END_FILE: test-utf8-scripts.js diff --git a/test/js/node/test/parallel/util-deprecate.test.js b/test/js/node/test/parallel/util-deprecate.test.js deleted file mode 100644 index 319c02c3b0..0000000000 --- a/test/js/node/test/parallel/util-deprecate.test.js +++ /dev/null @@ -1,79 +0,0 @@ -//#FILE: test-util-deprecate.js -//#SHA1: 43c232bacd8dcc9f39194125c071db2ab14dfb51 -//----------------- -"use strict"; - -const util = require("util"); - -// Tests basic functionality of util.deprecate(). - -// Mock process.on for warnings -const mockWarningListener = jest.fn(); -const mockExitListener = jest.fn(); -process.on = jest.fn((event, listener) => { - if (event === "warning") mockWarningListener.mockImplementation(listener); - if (event === "exit") mockExitListener.mockImplementation(listener); -}); - -const expectedWarnings = new Map(); - -test("Emits deprecation only once if same function is called", () => { - const msg = "fhqwhgads"; - const fn = util.deprecate(() => {}, msg); - expectedWarnings.set(msg, { code: undefined, count: 1 }); - fn(); - fn(); -}); - -test("Emits deprecation twice for different functions", () => { - const msg = "sterrance"; - const fn1 = util.deprecate(() => {}, msg); - const fn2 = util.deprecate(() => {}, msg); - expectedWarnings.set(msg, { code: undefined, count: 2 }); - fn1(); - fn2(); -}); - -test("Emits deprecation only once if optional code is the same, even for different functions", () => { - const msg = "cannonmouth"; - const code = "deprecatesque"; - const fn1 = util.deprecate(() => {}, msg, code); - const fn2 = util.deprecate(() => {}, msg, code); - expectedWarnings.set(msg, { code, count: 1 }); - fn1(); - fn2(); - fn1(); - fn2(); -}); - -test("Handles warnings correctly", () => { - expectedWarnings.forEach((expected, message) => { - for (let i = 0; i < expected.count; i++) { - mockWarningListener({ - name: "DeprecationWarning", - message: message, - code: expected.code, - }); - } - }); - - expect(mockWarningListener).toHaveBeenCalledTimes( - Array.from(expectedWarnings.values()).reduce((acc, curr) => acc + curr.count, 0), - ); - - mockWarningListener.mock.calls.forEach(([warning]) => { - expect(warning.name).toBe("DeprecationWarning"); - expect(expectedWarnings.has(warning.message)).toBe(true); - const expected = expectedWarnings.get(warning.message); - expect(warning.code).toBe(expected.code); - expected.count--; - if (expected.count === 0) { - expectedWarnings.delete(warning.message); - } - }); -}); - -test("All warnings are processed", () => { - mockExitListener(); - expect(expectedWarnings.size).toBe(0); -}); diff --git a/test/js/node/test/parallel/util-inspect-getters-accessing-this.test.js b/test/js/node/test/parallel/util-inspect-getters-accessing-this.test.js deleted file mode 100644 index 78cec627d2..0000000000 --- a/test/js/node/test/parallel/util-inspect-getters-accessing-this.test.js +++ /dev/null @@ -1,64 +0,0 @@ -//#FILE: test-util-inspect-getters-accessing-this.js -//#SHA1: 92c41c06f838da46cbbfcd7f695a19784af3f581 -//----------------- -"use strict"; - -const { inspect } = require("util"); - -// This test ensures that util.inspect logs getters -// which access this. - -test("util.inspect logs getters accessing this", () => { - class X { - constructor() { - this._y = 123; - } - - get y() { - return this._y; - } - } - - const result = inspect(new X(), { - getters: true, - showHidden: true, - }); - - expect(result).toBe("X { _y: 123, [y]: [Getter: 123] }"); -}); - -// Regression test for https://github.com/nodejs/node/issues/37054 -test("util.inspect handles circular references in getters", () => { - class A { - constructor(B) { - this.B = B; - } - get b() { - return this.B; - } - } - - class B { - constructor() { - this.A = new A(this); - } - get a() { - return this.A; - } - } - - const result = inspect(new B(), { - depth: 1, - getters: true, - showHidden: true, - }); - - expect(result).toBe( - " B {\n" + - " A: A { B: [Circular *1], [b]: [Getter] [Circular *1] },\n" + - " [a]: [Getter] A { B: [Circular *1], [b]: [Getter] [Circular *1] }\n" + - "}", - ); -}); - -//<#END_FILE: test-util-inspect-getters-accessing-this.js diff --git a/test/js/node/test/parallel/util-inspect-long-running.test.js b/test/js/node/test/parallel/util-inspect-long-running.test.js deleted file mode 100644 index 7fba0e08b7..0000000000 --- a/test/js/node/test/parallel/util-inspect-long-running.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-util-inspect-long-running.js -//#SHA1: 2e4cbb5743a4dfcf869e84ce6d58795f96465aeb -//----------------- -"use strict"; - -// Test that huge objects don't crash due to exceeding the maximum heap size. - -const util = require("util"); - -test("util.inspect handles huge objects without crashing", () => { - // Create a difficult to stringify object. Without the artificial limitation - // this would crash or throw an maximum string size error. - let last = {}; - const obj = last; - - for (let i = 0; i < 1000; i++) { - last.next = { circular: obj, last, obj: { a: 1, b: 2, c: true } }; - last = last.next; - obj[i] = last; - } - - // This should not throw an error or crash - expect(() => { - util.inspect(obj, { depth: Infinity }); - }).not.toThrow(); -}); - -//<#END_FILE: test-util-inspect-long-running.js diff --git a/test/js/node/test/parallel/util-primordial-monkeypatching.test.js b/test/js/node/test/parallel/util-primordial-monkeypatching.test.js deleted file mode 100644 index 8f369c1d8f..0000000000 --- a/test/js/node/test/parallel/util-primordial-monkeypatching.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-util-primordial-monkeypatching.js -//#SHA1: 72e754f1abd435e598620901f26b087e0bf9d5a7 -//----------------- -"use strict"; - -// Monkeypatch Object.keys() so that it throws an unexpected error. This tests -// that `util.inspect()` is unaffected by monkey-patching `Object`. - -const util = require("util"); - -test("util.inspect() is unaffected by monkey-patching Object.keys()", () => { - const originalObjectKeys = Object.keys; - - // Monkey-patch Object.keys - Object.keys = () => { - throw new Error("fhqwhgads"); - }; - - try { - expect(util.inspect({})).toBe("{}"); - } finally { - // Restore original Object.keys to avoid affecting other tests - Object.keys = originalObjectKeys; - } -}); diff --git a/test/js/node/test/parallel/util-sigint-watchdog.test.js b/test/js/node/test/parallel/util-sigint-watchdog.test.js deleted file mode 100644 index 8f265aa240..0000000000 --- a/test/js/node/test/parallel/util-sigint-watchdog.test.js +++ /dev/null @@ -1,83 +0,0 @@ -//#FILE: test-util-sigint-watchdog.js -//#SHA1: 8731497954a21e75af21af0adde10619d98a703f -//----------------- -"use strict"; - -// Skip test on Windows platforms -if (process.platform === "win32") { - test.skip("platform not supported", () => {}); -} else { - const binding = { - startSigintWatchdog: jest.fn(), - stopSigintWatchdog: jest.fn(), - watchdogHasPendingSigint: jest.fn(), - }; - - // Mock the process.kill function - const originalKill = process.kill; - beforeAll(() => { - process.kill = jest.fn(); - }); - afterAll(() => { - process.kill = originalKill; - }); - - function waitForPendingSignal(cb) { - if (binding.watchdogHasPendingSigint()) cb(); - else setTimeout(waitForPendingSignal, 10, cb); - } - - test("with no signal observed", () => { - binding.startSigintWatchdog(); - binding.stopSigintWatchdog.mockReturnValue(false); - const hadPendingSignals = binding.stopSigintWatchdog(); - expect(hadPendingSignals).toBe(false); - }); - - test("with one call to the watchdog, one signal", done => { - binding.startSigintWatchdog(); - process.kill(process.pid, "SIGINT"); - binding.watchdogHasPendingSigint.mockReturnValue(true); - binding.stopSigintWatchdog.mockReturnValue(true); - - waitForPendingSignal(() => { - const hadPendingSignals = binding.stopSigintWatchdog(); - expect(hadPendingSignals).toBe(true); - done(); - }); - }); - - test("nested calls are okay", done => { - binding.startSigintWatchdog(); - binding.startSigintWatchdog(); - process.kill(process.pid, "SIGINT"); - binding.watchdogHasPendingSigint.mockReturnValue(true); - binding.stopSigintWatchdog.mockReturnValueOnce(true).mockReturnValueOnce(false); - - waitForPendingSignal(() => { - const hadPendingSignals1 = binding.stopSigintWatchdog(); - const hadPendingSignals2 = binding.stopSigintWatchdog(); - expect(hadPendingSignals1).toBe(true); - expect(hadPendingSignals2).toBe(false); - done(); - }); - }); - - test("signal comes in after first call to stop", done => { - binding.startSigintWatchdog(); - binding.startSigintWatchdog(); - binding.stopSigintWatchdog.mockReturnValueOnce(false).mockReturnValueOnce(true); - const hadPendingSignals1 = binding.stopSigintWatchdog(); - process.kill(process.pid, "SIGINT"); - binding.watchdogHasPendingSigint.mockReturnValue(true); - - waitForPendingSignal(() => { - const hadPendingSignals2 = binding.stopSigintWatchdog(); - expect(hadPendingSignals1).toBe(false); - expect(hadPendingSignals2).toBe(true); - done(); - }); - }); -} - -//<#END_FILE: test-util-sigint-watchdog.js diff --git a/test/js/node/test/parallel/uv-unmapped-exception.test.js b/test/js/node/test/parallel/uv-unmapped-exception.test.js deleted file mode 100644 index c8a5147fdc..0000000000 --- a/test/js/node/test/parallel/uv-unmapped-exception.test.js +++ /dev/null @@ -1,47 +0,0 @@ -//#FILE: test-uv-unmapped-exception.js -//#SHA1: 2878fb9a4523acb34f6283c980cde15b06fdc055 -//----------------- -"use strict"; - -// We can't use internal modules, so we'll need to implement our own UVException and UVExceptionWithHostPort -class UVException extends Error { - constructor({ errno, syscall }) { - super(`UNKNOWN: unknown error, ${syscall}`); - this.errno = errno; - this.syscall = syscall; - this.code = "UNKNOWN"; - } -} - -class UVExceptionWithHostPort extends Error { - constructor(errno, syscall, address, port) { - super(`${syscall} UNKNOWN: unknown error ${address}:${port}`); - this.code = "UNKNOWN"; - this.errno = errno; - this.syscall = syscall; - this.address = address; - this.port = port; - } -} - -test("UVException", () => { - const exception = new UVException({ errno: 100, syscall: "open" }); - - expect(exception.message).toBe("UNKNOWN: unknown error, open"); - expect(exception.errno).toBe(100); - expect(exception.syscall).toBe("open"); - expect(exception.code).toBe("UNKNOWN"); -}); - -test("UVExceptionWithHostPort", () => { - const exception = new UVExceptionWithHostPort(100, "listen", "127.0.0.1", 80); - - expect(exception.message).toBe("listen UNKNOWN: unknown error 127.0.0.1:80"); - expect(exception.code).toBe("UNKNOWN"); - expect(exception.errno).toBe(100); - expect(exception.syscall).toBe("listen"); - expect(exception.address).toBe("127.0.0.1"); - expect(exception.port).toBe(80); -}); - -//<#END_FILE: test-uv-unmapped-exception.js diff --git a/test/js/node/test/parallel/v8-collect-gc-profile-in-worker.test.js b/test/js/node/test/parallel/v8-collect-gc-profile-in-worker.test.js deleted file mode 100644 index ff43b62a54..0000000000 --- a/test/js/node/test/parallel/v8-collect-gc-profile-in-worker.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-v8-collect-gc-profile-in-worker.js -//#SHA1: 212956c00ed8e788f682c6335879f6844972a424 -//----------------- -// Flags: --expose-gc -"use strict"; - -const { Worker } = require("worker_threads"); - -// Replace testGCProfiler with a Jest-compatible mock -const testGCProfiler = jest.fn(); - -if (!process.env.isWorker) { - test("Worker thread creation", () => { - process.env.isWorker = 1; - const worker = new Worker(__filename); - expect(worker).toBeDefined(); - }); -} else { - test("GC profiler in worker thread", () => { - testGCProfiler(); - for (let i = 0; i < 100; i++) { - new Array(100); - } - - // Check if global.gc is available - if (typeof global.gc === "function") { - global.gc(); - } else { - console.warn("global.gc is not available. Make sure to run with --expose-gc flag."); - } - - expect(testGCProfiler).toHaveBeenCalledTimes(1); - }); -} - -//<#END_FILE: test-v8-collect-gc-profile-in-worker.js diff --git a/test/js/node/test/parallel/v8-deserialize-buffer.test.js b/test/js/node/test/parallel/v8-deserialize-buffer.test.js deleted file mode 100644 index d84675a6c0..0000000000 --- a/test/js/node/test/parallel/v8-deserialize-buffer.test.js +++ /dev/null @@ -1,20 +0,0 @@ -//#FILE: test-v8-deserialize-buffer.js -//#SHA1: ef80f8c41f9e9b893ea639f4558713addcf82b9a -//----------------- -"use strict"; - -const v8 = require("v8"); - -test("v8.deserialize should not emit warnings for Buffer.alloc(0)", () => { - const warningListener = jest.fn(); - process.on("warning", warningListener); - - v8.deserialize(v8.serialize(Buffer.alloc(0))); - - expect(warningListener).not.toHaveBeenCalled(); - - // Clean up the listener - process.removeListener("warning", warningListener); -}); - -//<#END_FILE: test-v8-deserialize-buffer.js diff --git a/test/js/node/test/parallel/v8-flag-pool-size-0.test.js b/test/js/node/test/parallel/v8-flag-pool-size-0.test.js deleted file mode 100644 index 0b32378981..0000000000 --- a/test/js/node/test/parallel/v8-flag-pool-size-0.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-v8-flag-pool-size-0.js -//#SHA1: 32e0b30d9305b4bf5509c6061176b2f87c47c66d -//----------------- -// Flags: --v8-pool-size=0 --expose-gc - -"use strict"; - -// This test doesn't require any specific assertions or expectations. -// It's primarily checking that the process doesn't crash or hang when -// running with the specified flags. - -test("V8 tasks scheduled by GC are handled on worker threads with --v8-pool-size=0", () => { - // This verifies that V8 tasks scheduled by GC are handled on worker threads if - // `--v8-pool-size=0` is given. The worker threads are managed by Node.js' - // `v8::Platform` implementation. - - // Trigger garbage collection - globalThis.gc(); - - // If we've reached this point without crashing or hanging, the test is successful - expect(true).toBe(true); -}); - -//<#END_FILE: test-v8-flag-pool-size-0.js diff --git a/test/js/node/test/parallel/v8-global-setter.test.js b/test/js/node/test/parallel/v8-global-setter.test.js deleted file mode 100644 index eb5fe888f2..0000000000 --- a/test/js/node/test/parallel/v8-global-setter.test.js +++ /dev/null @@ -1,37 +0,0 @@ -//#FILE: test-v8-global-setter.js -//#SHA1: 8711ed3c5a3491001fcf441ed56e14fcca67c8ef -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -// This test ensures v8 correctly sets a property on the global object if it -// has a setter interceptor in strict mode. -// https://github.com/nodejs/node-v0.x-archive/issues/6235 - -test("v8 global setter in strict mode", () => { - expect(() => { - require("vm").runInNewContext('"use strict"; var v = 1; v = 2'); - }).not.toThrow(); -}); - -//<#END_FILE: test-v8-global-setter.js diff --git a/test/js/node/test/parallel/v8-serialize-leak.test.js b/test/js/node/test/parallel/v8-serialize-leak.test.js deleted file mode 100644 index a8d0bb51e0..0000000000 --- a/test/js/node/test/parallel/v8-serialize-leak.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-v8-serialize-leak.js -//#SHA1: f1b10774a48610a130cac36679953f9af1ed15e1 -//----------------- -"use strict"; -// Flags: --expose-gc - -const v8 = require("v8"); - -// On IBMi, the rss memory always returns zero -if (process.platform === "os400") { - test.skip("On IBMi, the rss memory always returns zero"); -} - -test("v8.serialize should not leak memory", async () => { - const before = process.memoryUsage.rss(); - - for (let i = 0; i < 1000000; i++) { - v8.serialize(""); - } - - async function gcUntil(message, condition) { - for (let i = 0; i < 10; i++) { - global.gc(); - await new Promise(resolve => setTimeout(resolve, 100)); - if (condition()) { - return; - } - } - throw new Error(`${message} failed to be true in time`); - } - - await gcUntil("RSS should go down", () => { - const after = process.memoryUsage.rss(); - if (process.env.ASAN_OPTIONS) { - console.log(`ASan: before=${before} after=${after}`); - return after < before * 10; - } else if (process.config.variables.node_builtin_modules_path) { - console.log(`node_builtin_modules_path: before=${before} after=${after}`); - return after < before * 10; - } - console.log(`before=${before} after=${after}`); - return after < before * 10; - }); -}); - -//<#END_FILE: test-v8-serialize-leak.js diff --git a/test/js/node/test/parallel/vm-access-process-env.test.js b/test/js/node/test/parallel/vm-access-process-env.test.js deleted file mode 100644 index 4bac2e235c..0000000000 --- a/test/js/node/test/parallel/vm-access-process-env.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-vm-access-process-env.js -//#SHA1: ffc014abc0d92ea62b7c0510d69cad5e89d632af -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Tests that node does neither crash nor throw an error when accessing -// process.env when inside a VM context. -// See https://github.com/nodejs/node-v0.x-archive/issues/7511. - -const vm = require("vm"); - -test("VM context can access process.env", () => { - const context = vm.createContext({ process }); - const result = vm.runInContext('process.env["PATH"]', context); - expect(result).not.toBeUndefined(); -}); - -//<#END_FILE: test-vm-access-process-env.js diff --git a/test/js/node/test/parallel/vm-attributes-property-not-on-sandbox.test.js b/test/js/node/test/parallel/vm-attributes-property-not-on-sandbox.test.js deleted file mode 100644 index 3cfeab76a7..0000000000 --- a/test/js/node/test/parallel/vm-attributes-property-not-on-sandbox.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-vm-attributes-property-not-on-sandbox.js -//#SHA1: c864df0cb9b3ab90c8582ad86f50a8e94be92114 -//----------------- -"use strict"; -const vm = require("vm"); - -// Assert that accessor descriptors are not flattened on the sandbox. -// Issue: https://github.com/nodejs/node/issues/2734 -test("accessor descriptors are not flattened on the sandbox", () => { - const sandbox = {}; - vm.createContext(sandbox); - const code = `Object.defineProperty( - this, - 'foo', - { get: function() {return 17} } - ); - var desc = Object.getOwnPropertyDescriptor(this, 'foo');`; - - vm.runInContext(code, sandbox); - expect(typeof sandbox.desc.get).toBe("function"); -}); - -//<#END_FILE: test-vm-attributes-property-not-on-sandbox.js diff --git a/test/js/node/test/parallel/vm-context-async-script.test.js b/test/js/node/test/parallel/vm-context-async-script.test.js deleted file mode 100644 index 52d90dde39..0000000000 --- a/test/js/node/test/parallel/vm-context-async-script.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-vm-context-async-script.js -//#SHA1: 411e3a9c4ea8c062420ae8f112c62ed7c2a1a962 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const vm = require("vm"); - -test("vm.runInContext with async script", done => { - const sandbox = { setTimeout }; - const ctx = vm.createContext(sandbox); - - vm.runInContext("setTimeout(function() { x = 3; }, 0);", ctx); - - setTimeout(() => { - expect(sandbox.x).toBe(3); - expect(ctx.x).toBe(3); - done(); - }, 1); -}); - -//<#END_FILE: test-vm-context-async-script.js diff --git a/test/js/node/test/parallel/vm-context-property-forwarding.test.js b/test/js/node/test/parallel/vm-context-property-forwarding.test.js deleted file mode 100644 index ca3e22c38a..0000000000 --- a/test/js/node/test/parallel/vm-context-property-forwarding.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-vm-context-property-forwarding.js -//#SHA1: 611b4eee260bc77dfbaece761208e11a57fe226f -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const vm = require("vm"); - -test("vm context property forwarding", () => { - const sandbox = { x: 3 }; - const ctx = vm.createContext(sandbox); - - expect(vm.runInContext("x;", ctx)).toBe(3); - vm.runInContext("y = 4;", ctx); - expect(sandbox.y).toBe(4); - expect(ctx.y).toBe(4); -}); - -test("IndexedPropertyGetterCallback and IndexedPropertyDeleterCallback", () => { - const x = { - get 1() { - return 5; - }, - }; - const pd_expected = Object.getOwnPropertyDescriptor(x, 1); - const ctx2 = vm.createContext(x); - const pd_actual = Object.getOwnPropertyDescriptor(ctx2, 1); - - expect(pd_actual).toEqual(pd_expected); - expect(ctx2[1]).toBe(5); - delete ctx2[1]; - expect(ctx2[1]).toBeUndefined(); -}); - -// https://github.com/nodejs/node/issues/33806 -test("property setter error propagation", () => { - const ctx = vm.createContext(); - - Object.defineProperty(ctx, "prop", { - get() { - return undefined; - }, - set() { - throw new Error("test error"); - }, - }); - - expect(() => { - vm.runInContext("prop = 42", ctx); - }).toThrow( - expect.objectContaining({ - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-vm-context-property-forwarding.js diff --git a/test/js/node/test/parallel/vm-create-and-run-in-context.test.js b/test/js/node/test/parallel/vm-create-and-run-in-context.test.js deleted file mode 100644 index e6e450ef9f..0000000000 --- a/test/js/node/test/parallel/vm-create-and-run-in-context.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-vm-create-and-run-in-context.js -//#SHA1: 4583962cfdb3266d8b850442cec0b4cfa49bc6d2 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Flags: --expose-gc - -const vm = require("vm"); - -describe("vm.createContext and vm.runInContext", () => { - test("Run in a new empty context", () => { - const context = vm.createContext(); - const result = vm.runInContext('"passed";', context); - expect(result).toBe("passed"); - }); - - test("Create a new pre-populated context", () => { - const context = vm.createContext({ foo: "bar", thing: "lala" }); - expect(context.foo).toBe("bar"); - expect(context.thing).toBe("lala"); - }); - - test("Test updating context", () => { - const context = vm.createContext({ foo: "bar", thing: "lala" }); - vm.runInContext("var foo = 3;", context); - expect(context.foo).toBe(3); - expect(context.thing).toBe("lala"); - }); - - test("Run in contextified sandbox without referencing the context", () => { - // https://github.com/nodejs/node/issues/5768 - const sandbox = { x: 1 }; - vm.createContext(sandbox); - global.gc(); - vm.runInContext("x = 2", sandbox); - // Should not crash. - expect(sandbox.x).toBe(2); - }); -}); - -//<#END_FILE: test-vm-create-and-run-in-context.js diff --git a/test/js/node/test/parallel/vm-create-context-accessors.test.js b/test/js/node/test/parallel/vm-create-context-accessors.test.js deleted file mode 100644 index 6f55c5f5af..0000000000 --- a/test/js/node/test/parallel/vm-create-context-accessors.test.js +++ /dev/null @@ -1,54 +0,0 @@ -//#FILE: test-vm-create-context-accessors.js -//#SHA1: af7f3fb956333bd0669014a9e3c8f3f308efc68e -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const vm = require("vm"); - -test("vm.createContext with accessors", () => { - let ctx = {}; - - Object.defineProperty(ctx, "getter", { - get: function () { - return "ok"; - }, - }); - - let val; - Object.defineProperty(ctx, "setter", { - set: function (_val) { - val = _val; - }, - get: function () { - return `ok=${val}`; - }, - }); - - ctx = vm.createContext(ctx); - - const result = vm.runInContext('setter = "test";[getter,setter]', ctx); - expect(result[0]).toBe("ok"); - expect(result[1]).toBe("ok=test"); -}); - -//<#END_FILE: test-vm-create-context-accessors.js diff --git a/test/js/node/test/parallel/vm-create-context-circular-reference.test.js b/test/js/node/test/parallel/vm-create-context-circular-reference.test.js deleted file mode 100644 index a088f700bf..0000000000 --- a/test/js/node/test/parallel/vm-create-context-circular-reference.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-vm-create-context-circular-reference.js -//#SHA1: 30740bd812c7365e1f7b72a3d98c5ca91d8adbb5 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const vm = require("vm"); - -test("vm.createContext with circular reference", () => { - let sbx = {}; - sbx.window = sbx; - - sbx = vm.createContext(sbx); - - sbx.test = 123; - - expect(sbx.window.window.window.window.window.test).toBe(123); -}); - -//<#END_FILE: test-vm-create-context-circular-reference.js diff --git a/test/js/node/test/parallel/vm-cross-context.test.js b/test/js/node/test/parallel/vm-cross-context.test.js deleted file mode 100644 index 52c716d18a..0000000000 --- a/test/js/node/test/parallel/vm-cross-context.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-vm-cross-context.js -//#SHA1: c8f367f7edfa69a5b052feab2bb6c9326f16c9d3 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const vm = require("vm"); - -test("vm.runInContext should not throw when accessing console.log", () => { - const ctx = vm.createContext(global); - - // Should not throw. - expect(() => { - vm.runInContext("!function() { var x = console.log; }()", ctx); - }).not.toThrow(); -}); - -//<#END_FILE: test-vm-cross-context.js diff --git a/test/js/node/test/parallel/vm-data-property-writable.test.js b/test/js/node/test/parallel/vm-data-property-writable.test.js deleted file mode 100644 index e85965fb83..0000000000 --- a/test/js/node/test/parallel/vm-data-property-writable.test.js +++ /dev/null @@ -1,33 +0,0 @@ -//#FILE: test-vm-data-property-writable.js -//#SHA1: fc562132c9b5c9f17d55e08df7456dea7a1f41e3 -//----------------- -"use strict"; -// Refs: https://github.com/nodejs/node/issues/10223 - -const vm = require("vm"); - -test("vm data property writable", () => { - const context = vm.createContext({}); - - let code = ` - Object.defineProperty(this, 'foo', {value: 5}); - Object.getOwnPropertyDescriptor(this, 'foo'); - `; - - let desc = vm.runInContext(code, context); - - expect(desc.writable).toBe(false); - - // Check that interceptors work for symbols. - code = ` - const bar = Symbol('bar'); - Object.defineProperty(this, bar, {value: 6}); - Object.getOwnPropertyDescriptor(this, bar); - `; - - desc = vm.runInContext(code, context); - - expect(desc.value).toBe(6); -}); - -//<#END_FILE: test-vm-data-property-writable.js diff --git a/test/js/node/test/parallel/vm-deleting-property.test.js b/test/js/node/test/parallel/vm-deleting-property.test.js deleted file mode 100644 index ac94e9a72d..0000000000 --- a/test/js/node/test/parallel/vm-deleting-property.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-vm-deleting-property.js -//#SHA1: 20a3a3752a4d1b140cd8762c1d66c8dc734ed3fa -//----------------- -"use strict"; -// Refs: https://github.com/nodejs/node/issues/6287 - -const vm = require("vm"); - -test("deleting property in vm context", () => { - const context = vm.createContext(); - const res = vm.runInContext( - ` - this.x = 'prop'; - delete this.x; - Object.getOwnPropertyDescriptor(this, 'x'); - `, - context, - ); - - expect(res).toBeUndefined(); -}); - -//<#END_FILE: test-vm-deleting-property.js diff --git a/test/js/node/test/parallel/vm-function-declaration.test.js b/test/js/node/test/parallel/vm-function-declaration.test.js deleted file mode 100644 index 2137b50b48..0000000000 --- a/test/js/node/test/parallel/vm-function-declaration.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-vm-function-declaration.js -//#SHA1: 002bfe8201b2483628b46efd3d5e081fb17fc4b4 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const vm = require("vm"); - -test("Function declaration and expression in vm context", () => { - const o = vm.createContext({ console }); - - // Function declaration and expression should both be copied to the - // sandboxed context. - let code = "let a = function() {};\n"; - code += "function b(){}\n"; - code += "var c = function() {};\n"; - code += "var d = () => {};\n"; - code += "let e = () => {};\n"; - - // Grab the global b function as the completion value, to ensure that - // we are getting the global function, and not some other thing - code += "(function(){return this})().b;\n"; - - const res = vm.runInContext(code, o, "test"); - - expect(typeof res).toBe("function"); - expect(res.name).toBe("b"); - expect(typeof o.a).toBe("undefined"); - expect(typeof o.b).toBe("function"); - expect(typeof o.c).toBe("function"); - expect(typeof o.d).toBe("function"); - expect(typeof o.e).toBe("undefined"); - expect(res).toBe(o.b); -}); - -//<#END_FILE: test-vm-function-declaration.js diff --git a/test/js/node/test/parallel/vm-function-redefinition.test.js b/test/js/node/test/parallel/vm-function-redefinition.test.js deleted file mode 100644 index 6395cbd57e..0000000000 --- a/test/js/node/test/parallel/vm-function-redefinition.test.js +++ /dev/null @@ -1,17 +0,0 @@ -//#FILE: test-vm-function-redefinition.js -//#SHA1: afca30c05276f19448e4a2e01381c8a6fb13d544 -//----------------- -"use strict"; -// Refs: https://github.com/nodejs/node/issues/548 -const vm = require("vm"); - -test("function redefinition in vm context", () => { - const context = vm.createContext(); - - vm.runInContext("function test() { return 0; }", context); - vm.runInContext("function test() { return 1; }", context); - const result = vm.runInContext("test()", context); - expect(result).toBe(1); -}); - -//<#END_FILE: test-vm-function-redefinition.js diff --git a/test/js/node/test/parallel/vm-global-assignment.test.js b/test/js/node/test/parallel/vm-global-assignment.test.js deleted file mode 100644 index bacb3cf2d7..0000000000 --- a/test/js/node/test/parallel/vm-global-assignment.test.js +++ /dev/null @@ -1,36 +0,0 @@ -//#FILE: test-vm-global-assignment.js -//#SHA1: 54d8ce4e5d93c89a8573a3230065f4e244b198db -//----------------- -"use strict"; - -// Regression test for https://github.com/nodejs/node/issues/10806 - -const vm = require("vm"); - -describe("VM global assignment", () => { - let ctx; - let window; - const other = 123; - - beforeEach(() => { - ctx = vm.createContext({ open() {} }); - window = vm.runInContext("this", ctx); - }); - - test("window.open is not equal to other initially", () => { - expect(window.open).not.toBe(other); - }); - - test("window.open can be assigned", () => { - window.open = other; - expect(window.open).toBe(other); - }); - - test("window.open can be reassigned", () => { - window.open = other; - window.open = other; - expect(window.open).toBe(other); - }); -}); - -//<#END_FILE: test-vm-global-assignment.js diff --git a/test/js/node/test/parallel/vm-global-configurable-properties.test.js b/test/js/node/test/parallel/vm-global-configurable-properties.test.js deleted file mode 100644 index 860b260159..0000000000 --- a/test/js/node/test/parallel/vm-global-configurable-properties.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-vm-global-configurable-properties.js -//#SHA1: abaa38afe456cd1fcf98be472781d25e3918d1b5 -//----------------- -"use strict"; -// https://github.com/nodejs/node/issues/47799 - -const vm = require("vm"); - -test("VM global configurable properties", () => { - const ctx = vm.createContext(); - - const window = vm.runInContext("this", ctx); - - Object.defineProperty(window, "x", { value: "1", configurable: true }); - expect(window.x).toBe("1"); - - Object.defineProperty(window, "x", { value: "2", configurable: true }); - expect(window.x).toBe("2"); -}); - -//<#END_FILE: test-vm-global-configurable-properties.js diff --git a/test/js/node/test/parallel/vm-harmony-symbols.test.js b/test/js/node/test/parallel/vm-harmony-symbols.test.js deleted file mode 100644 index 1d8b86bdc4..0000000000 --- a/test/js/node/test/parallel/vm-harmony-symbols.test.js +++ /dev/null @@ -1,42 +0,0 @@ -//#FILE: test-vm-harmony-symbols.js -//#SHA1: 36768ab105e0bcc19dccae0fea22801068fdaedd -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const vm = require("vm"); - -test("The sandbox should have its own Symbol constructor", () => { - let sandbox = {}; - vm.runInNewContext("this.Symbol = Symbol", sandbox); - expect(typeof sandbox.Symbol).toBe("function"); - expect(sandbox.Symbol).not.toBe(Symbol); -}); - -test("Explicitly copying the Symbol constructor", () => { - let sandbox = { Symbol }; - vm.runInNewContext("this.Symbol = Symbol", sandbox); - expect(typeof sandbox.Symbol).toBe("function"); - expect(sandbox.Symbol).toBe(Symbol); -}); - -//<#END_FILE: test-vm-harmony-symbols.js diff --git a/test/js/node/test/parallel/vm-indexed-properties.test.js b/test/js/node/test/parallel/vm-indexed-properties.test.js deleted file mode 100644 index 6b9999fadf..0000000000 --- a/test/js/node/test/parallel/vm-indexed-properties.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-vm-indexed-properties.js -//#SHA1: 5938ca1da86f05ceda978b0f2d9640734d6c0ab6 -//----------------- -"use strict"; - -const vm = require("vm"); - -test("vm indexed properties", () => { - const code = `Object.defineProperty(this, 99, { - value: 20, - enumerable: true - });`; - - const sandbox = {}; - const ctx = vm.createContext(sandbox); - vm.runInContext(code, ctx); - - expect(sandbox[99]).toBe(20); -}); - -//<#END_FILE: test-vm-indexed-properties.js diff --git a/test/js/node/test/parallel/vm-low-stack-space.test.js b/test/js/node/test/parallel/vm-low-stack-space.test.js deleted file mode 100644 index e4ccd2e9b2..0000000000 --- a/test/js/node/test/parallel/vm-low-stack-space.test.js +++ /dev/null @@ -1,33 +0,0 @@ -//#FILE: test-vm-low-stack-space.js -//#SHA1: fffd6c9c17b9ff755e1dd126a42d6ea176282d00 -//----------------- -"use strict"; -const vm = require("vm"); - -test("vm.runInThisContext in low stack space", () => { - function a() { - try { - return a(); - } catch { - // Throw an exception as near to the recursion-based RangeError as possible. - return vm.runInThisContext("() => 42")(); - } - } - - expect(a()).toBe(42); -}); - -test("vm.runInNewContext in low stack space", () => { - function b() { - try { - return b(); - } catch { - // This writes a lot of noise to stderr, but it still works. - return vm.runInNewContext("() => 42")(); - } - } - - expect(b()).toBe(42); -}); - -//<#END_FILE: test-vm-low-stack-space.js diff --git a/test/js/node/test/parallel/vm-new-script-new-context.test.js b/test/js/node/test/parallel/vm-new-script-new-context.test.js deleted file mode 100644 index ea14f33bc6..0000000000 --- a/test/js/node/test/parallel/vm-new-script-new-context.test.js +++ /dev/null @@ -1,127 +0,0 @@ -//#FILE: test-vm-new-script-new-context.js -//#SHA1: 444a86a44203350903ab84a30de39e24a28732ec -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const { Script } = require("vm"); - -test("Script runInNewContext returns expected result", () => { - const script = new Script("'passed';"); - const result1 = script.runInNewContext(); - const result2 = script.runInNewContext(); - expect(result1).toBe("passed"); - expect(result2).toBe("passed"); -}); - -test("Script throws error when expected", () => { - const script = new Script("throw new Error('test');"); - expect(() => { - script.runInNewContext(); - }).toThrow( - expect.objectContaining({ - name: "Error", - message: expect.any(String), - }), - ); -}); - -test("Script throws ReferenceError for undefined variable", () => { - const script = new Script("foo.bar = 5;"); - expect(() => { - script.runInNewContext(); - }).toThrow( - expect.objectContaining({ - name: "ReferenceError", - message: expect.any(String), - }), - ); -}); - -test("Script does not affect global scope", () => { - global.hello = 5; - const script = new Script("hello = 2"); - script.runInNewContext(); - expect(global.hello).toBe(5); - - // Cleanup - delete global.hello; -}); - -test("Script runs in new context with provided object", () => { - global.code = "foo = 1;" + "bar = 2;" + "if (baz !== 3) throw new Error('test fail');"; - global.foo = 2; - global.obj = { foo: 0, baz: 3 }; - const script = new Script(global.code); - script.runInNewContext(global.obj); - expect(global.obj.foo).toBe(1); - expect(global.obj.bar).toBe(2); - expect(global.foo).toBe(2); - - // cleanup - delete global.code; - delete global.foo; - delete global.obj; -}); - -test("Script can modify global scope through provided function", () => { - const script = new Script("f()"); - function changeFoo() { - global.foo = 100; - } - script.runInNewContext({ f: changeFoo }); - expect(global.foo).toBe(100); - - // cleanup - delete global.foo; -}); - -test("Script modifies provided object and throws when object is not provided", () => { - const script = new Script("f.a = 2"); - const f = { a: 1 }; - script.runInNewContext({ f }); - expect(f.a).toBe(2); - - expect(() => { - script.runInNewContext(); - }).toThrow( - expect.objectContaining({ - name: "ReferenceError", - message: expect.any(String), - }), - ); -}); - -test("Script.runInNewContext throws when called with invalid this", () => { - const script = new Script(""); - expect(() => { - script.runInNewContext.call("'hello';"); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-vm-new-script-new-context.js diff --git a/test/js/node/test/parallel/vm-proxies.test.js b/test/js/node/test/parallel/vm-proxies.test.js deleted file mode 100644 index b713c7773a..0000000000 --- a/test/js/node/test/parallel/vm-proxies.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-vm-proxies.js -//#SHA1: 3119c41e6c3cc80d380c9467c2f922b2df3e5616 -//----------------- -"use strict"; - -const vm = require("vm"); - -test("Proxy object in new context", () => { - // src/node_contextify.cc filters out the Proxy object from the parent - // context. Make sure that the new context has a Proxy object of its own. - let sandbox = {}; - vm.runInNewContext("this.Proxy = Proxy", sandbox); - expect(typeof sandbox.Proxy).toBe("function"); - expect(sandbox.Proxy).not.toBe(Proxy); -}); - -test("Explicitly copied Proxy object in new context", () => { - // Unless we copy the Proxy object explicitly, of course. - const sandbox = { Proxy }; - vm.runInNewContext("this.Proxy = Proxy", sandbox); - expect(typeof sandbox.Proxy).toBe("function"); - expect(sandbox.Proxy).toBe(Proxy); -}); - -//<#END_FILE: test-vm-proxies.js diff --git a/test/js/node/test/parallel/vm-proxy-failure-cp.test.js b/test/js/node/test/parallel/vm-proxy-failure-cp.test.js deleted file mode 100644 index 05d2c1e7b7..0000000000 --- a/test/js/node/test/parallel/vm-proxy-failure-cp.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-vm-proxy-failure-CP.js -//#SHA1: d3eb5284a94f718a6ae1e07c0b30e01dad295ea9 -//----------------- -"use strict"; -const vm = require("vm"); - -// Check that we do not accidentally query attributes. -// Issue: https://github.com/nodejs/node/issues/11902 -test("vm does not accidentally query attributes", () => { - const handler = { - getOwnPropertyDescriptor: (target, prop) => { - throw new Error("whoops"); - }, - }; - const sandbox = new Proxy({ foo: "bar" }, handler); - const context = vm.createContext(sandbox); - - expect(() => { - vm.runInContext("", context); - }).not.toThrow(); -}); - -//<#END_FILE: test-vm-proxy-failure-CP.js diff --git a/test/js/node/test/parallel/vm-script-throw-in-tostring.test.js b/test/js/node/test/parallel/vm-script-throw-in-tostring.test.js deleted file mode 100644 index acd0eeb817..0000000000 --- a/test/js/node/test/parallel/vm-script-throw-in-tostring.test.js +++ /dev/null @@ -1,22 +0,0 @@ -//#FILE: test-vm-script-throw-in-tostring.js -//#SHA1: 16675c8942dbb81a032c117fa42c9611cda082e0 -//----------------- -"use strict"; - -const vm = require("vm"); - -test("vm.Script throws when toString throws", () => { - expect(() => { - new vm.Script({ - toString() { - throw new Error(); - }, - }); - }).toThrow( - expect.objectContaining({ - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-vm-script-throw-in-tostring.js diff --git a/test/js/node/test/parallel/vm-strict-mode.test.js b/test/js/node/test/parallel/vm-strict-mode.test.js deleted file mode 100644 index e2f06f8555..0000000000 --- a/test/js/node/test/parallel/vm-strict-mode.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-vm-strict-mode.js -//#SHA1: ab6c6c72920e9bf095b41b255872b9d0604301c7 -//----------------- -"use strict"; -// https://github.com/nodejs/node/issues/12300 - -const vm = require("vm"); - -test("vm strict mode assignment", () => { - const ctx = vm.createContext({ x: 42 }); - - // This might look as if x has not been declared, but x is defined on the - // sandbox and the assignment should not throw. - expect(() => { - vm.runInContext('"use strict"; x = 1', ctx); - }).not.toThrow(); - - expect(ctx.x).toBe(1); -}); - -//<#END_FILE: test-vm-strict-mode.js diff --git a/test/js/node/test/parallel/vm-syntax-error-message.test.js b/test/js/node/test/parallel/vm-syntax-error-message.test.js deleted file mode 100644 index 49be35047c..0000000000 --- a/test/js/node/test/parallel/vm-syntax-error-message.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-vm-syntax-error-message.js -//#SHA1: dbd3683e08ad5cf574d1824108446e9c425adf1b -//----------------- -"use strict"; - -const child_process = require("child_process"); - -test("vm syntax error message", done => { - const p = child_process.spawn(process.execPath, [ - "-e", - 'vm = require("vm");' + - "context = vm.createContext({});" + - "try { vm.runInContext(\"throw new Error('boo')\", context); } " + - "catch (e) { console.log(e.message); }", - ]); - - p.stderr.on("data", () => { - throw new Error("stderr should not receive any data"); - }); - - let output = ""; - - p.stdout.on("data", data => (output += data)); - - p.stdout.on("end", () => { - expect(output.replace(/[\r\n]+/g, "")).toBe("boo"); - done(); - }); -}); - -//<#END_FILE: test-vm-syntax-error-message.js diff --git a/test/js/node/test/parallel/webcrypto-encrypt-decrypt.test.js b/test/js/node/test/parallel/webcrypto-encrypt-decrypt.test.js deleted file mode 100644 index 560edf8eb6..0000000000 --- a/test/js/node/test/parallel/webcrypto-encrypt-decrypt.test.js +++ /dev/null @@ -1,110 +0,0 @@ -//#FILE: test-webcrypto-encrypt-decrypt.js -//#SHA1: 791cad35ebee437d2a982e6101d47daa2f775a4b -//----------------- -"use strict"; - -const { subtle } = globalThis.crypto; - -// This is only a partial test. The WebCrypto Web Platform Tests -// will provide much greater coverage. - -// Test Encrypt/Decrypt RSA-OAEP -test("Encrypt/Decrypt RSA-OAEP", async () => { - const buf = globalThis.crypto.getRandomValues(new Uint8Array(50)); - const ec = new TextEncoder(); - const { publicKey, privateKey } = await subtle.generateKey( - { - name: "RSA-OAEP", - modulusLength: 2048, - publicExponent: new Uint8Array([1, 0, 1]), - hash: "SHA-384", - }, - true, - ["encrypt", "decrypt"], - ); - - const ciphertext = await subtle.encrypt( - { - name: "RSA-OAEP", - label: ec.encode("a label"), - }, - publicKey, - buf, - ); - - const plaintext = await subtle.decrypt( - { - name: "RSA-OAEP", - label: ec.encode("a label"), - }, - privateKey, - ciphertext, - ); - - expect(Buffer.from(plaintext).toString("hex")).toBe(Buffer.from(buf).toString("hex")); -}); - -// Test Encrypt/Decrypt AES-CTR -test("Encrypt/Decrypt AES-CTR", async () => { - const buf = globalThis.crypto.getRandomValues(new Uint8Array(50)); - const counter = globalThis.crypto.getRandomValues(new Uint8Array(16)); - - const key = await subtle.generateKey( - { - name: "AES-CTR", - length: 256, - }, - true, - ["encrypt", "decrypt"], - ); - - const ciphertext = await subtle.encrypt({ name: "AES-CTR", counter, length: 64 }, key, buf); - - const plaintext = await subtle.decrypt({ name: "AES-CTR", counter, length: 64 }, key, ciphertext); - - expect(Buffer.from(plaintext).toString("hex")).toBe(Buffer.from(buf).toString("hex")); -}); - -// Test Encrypt/Decrypt AES-CBC -test("Encrypt/Decrypt AES-CBC", async () => { - const buf = globalThis.crypto.getRandomValues(new Uint8Array(50)); - const iv = globalThis.crypto.getRandomValues(new Uint8Array(16)); - - const key = await subtle.generateKey( - { - name: "AES-CBC", - length: 256, - }, - true, - ["encrypt", "decrypt"], - ); - - const ciphertext = await subtle.encrypt({ name: "AES-CBC", iv }, key, buf); - - const plaintext = await subtle.decrypt({ name: "AES-CBC", iv }, key, ciphertext); - - expect(Buffer.from(plaintext).toString("hex")).toBe(Buffer.from(buf).toString("hex")); -}); - -// Test Encrypt/Decrypt AES-GCM -test("Encrypt/Decrypt AES-GCM", async () => { - const buf = globalThis.crypto.getRandomValues(new Uint8Array(50)); - const iv = globalThis.crypto.getRandomValues(new Uint8Array(12)); - - const key = await subtle.generateKey( - { - name: "AES-GCM", - length: 256, - }, - true, - ["encrypt", "decrypt"], - ); - - const ciphertext = await subtle.encrypt({ name: "AES-GCM", iv }, key, buf); - - const plaintext = await subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext); - - expect(Buffer.from(plaintext).toString("hex")).toBe(Buffer.from(buf).toString("hex")); -}); - -//<#END_FILE: test-webcrypto-encrypt-decrypt.js diff --git a/test/js/node/test/parallel/webcrypto-getrandomvalues.test.js b/test/js/node/test/parallel/webcrypto-getrandomvalues.test.js deleted file mode 100644 index 3ec9961927..0000000000 --- a/test/js/node/test/parallel/webcrypto-getrandomvalues.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-webcrypto-getRandomValues.js -//#SHA1: d5d696eb0e68968d2411efa24e6e4c9bd46a1678 -//----------------- -"use strict"; - -if (!globalThis.crypto) { - it("skips test when crypto is missing", () => { - console.log("missing crypto"); - return; - }); -} else { - describe("webcrypto getRandomValues", () => { - const { getRandomValues } = globalThis.crypto; - - it("throws ERR_INVALID_THIS when called without proper this", () => { - expect(() => getRandomValues(new Uint8Array())).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - message: expect.any(String), - }), - ); - }); - }); -} - -//<#END_FILE: test-webcrypto-getRandomValues.js diff --git a/test/js/node/test/parallel/webstream-string-tag.test.js b/test/js/node/test/parallel/webstream-string-tag.test.js deleted file mode 100644 index f8791dbe76..0000000000 --- a/test/js/node/test/parallel/webstream-string-tag.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-webstream-string-tag.js -//#SHA1: 53f13b84555ff37eeee23ca4552540d76b88c2ad -//----------------- -"use strict"; - -test("Web Stream classes have correct Symbol.toStringTag", () => { - const classesToBeTested = [ - WritableStream, - WritableStreamDefaultWriter, - WritableStreamDefaultController, - ReadableStream, - ReadableStreamBYOBRequest, - ReadableStreamDefaultReader, - ReadableStreamBYOBReader, - ReadableStreamDefaultController, - ReadableByteStreamController, - ByteLengthQueuingStrategy, - CountQueuingStrategy, - TransformStream, - TransformStreamDefaultController, - ]; - - classesToBeTested.forEach(cls => { - expect(cls.prototype[Symbol.toStringTag]).toBe(cls.name); - expect(Object.getOwnPropertyDescriptor(cls.prototype, Symbol.toStringTag)).toEqual({ - configurable: true, - enumerable: false, - value: cls.name, - writable: false, - }); - }); -}); - -//<#END_FILE: test-webstream-string-tag.js diff --git a/test/js/node/test/parallel/whatwg-encoding-custom-api-basics.test.js b/test/js/node/test/parallel/whatwg-encoding-custom-api-basics.test.js deleted file mode 100644 index b686f76961..0000000000 --- a/test/js/node/test/parallel/whatwg-encoding-custom-api-basics.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-whatwg-encoding-custom-api-basics.js -//#SHA1: 8181a892b0d1e5885b29b1165631875e587b7700 -//----------------- -"use strict"; - -// From: https://github.com/w3c/web-platform-tests/blob/master/encoding/api-basics.html -// This is the part that can be run without ICU - -function testDecodeSample(encoding, string, bytes) { - expect(new TextDecoder(encoding).decode(new Uint8Array(bytes))).toBe(string); - expect(new TextDecoder(encoding).decode(new Uint8Array(bytes).buffer)).toBe(string); -} - -// `z` (ASCII U+007A), cent (Latin-1 U+00A2), CJK water (BMP U+6C34), -// G-Clef (non-BMP U+1D11E), PUA (BMP U+F8FF), PUA (non-BMP U+10FFFD) -// byte-swapped BOM (non-character U+FFFE) -const sample = "z\xA2\u6C34\uD834\uDD1E\uF8FF\uDBFF\uDFFD\uFFFE"; - -test("utf-8 encoding and decoding", () => { - const encoding = "utf-8"; - const string = sample; - const bytes = [ - 0x7a, 0xc2, 0xa2, 0xe6, 0xb0, 0xb4, 0xf0, 0x9d, 0x84, 0x9e, 0xef, 0xa3, 0xbf, 0xf4, 0x8f, 0xbf, 0xbd, 0xef, 0xbf, - 0xbe, - ]; - const encoded = new TextEncoder().encode(string); - expect([...encoded]).toEqual(bytes); - expect(new TextDecoder(encoding).decode(new Uint8Array(bytes))).toBe(string); - expect(new TextDecoder(encoding).decode(new Uint8Array(bytes).buffer)).toBe(string); -}); - -test("utf-16le decoding", () => { - testDecodeSample( - "utf-16le", - sample, - [0x7a, 0x00, 0xa2, 0x00, 0x34, 0x6c, 0x34, 0xd8, 0x1e, 0xdd, 0xff, 0xf8, 0xff, 0xdb, 0xfd, 0xdf, 0xfe, 0xff], - ); -}); - -test("utf-16 decoding", () => { - testDecodeSample( - "utf-16", - sample, - [0x7a, 0x00, 0xa2, 0x00, 0x34, 0x6c, 0x34, 0xd8, 0x1e, 0xdd, 0xff, 0xf8, 0xff, 0xdb, 0xfd, 0xdf, 0xfe, 0xff], - ); -}); - -//<#END_FILE: test-whatwg-encoding-custom-api-basics.js diff --git a/test/js/node/test/parallel/whatwg-encoding-custom-fatal-streaming.test.js b/test/js/node/test/parallel/whatwg-encoding-custom-fatal-streaming.test.js deleted file mode 100644 index ce4cd6051f..0000000000 --- a/test/js/node/test/parallel/whatwg-encoding-custom-fatal-streaming.test.js +++ /dev/null @@ -1,61 +0,0 @@ -//#FILE: test-whatwg-encoding-custom-fatal-streaming.js -//#SHA1: 325f0a1e055a1756532d2818d4e563dfbddb928b -//----------------- -"use strict"; - -// From: https://github.com/w3c/web-platform-tests/blob/d74324b53c/encoding/textdecoder-fatal-streaming.html -// With the twist that we specifically test for Node.js error codes - -if (!globalThis.Intl) { - test.skip("missing Intl", () => {}); -} else { - test("TextDecoder with fatal option and invalid sequences", () => { - [ - { encoding: "utf-8", sequence: [0xc0] }, - { encoding: "utf-16le", sequence: [0x00] }, - { encoding: "utf-16be", sequence: [0x00] }, - ].forEach(testCase => { - const data = new Uint8Array(testCase.sequence); - expect(() => { - const decoder = new TextDecoder(testCase.encoding, { fatal: true }); - decoder.decode(data); - }).toThrow( - expect.objectContaining({ - code: "ERR_ENCODING_INVALID_ENCODED_DATA", - name: "TypeError", - message: expect.any(String), - }), - ); - }); - }); - - test("TextDecoder with utf-16le and streaming", () => { - const decoder = new TextDecoder("utf-16le", { fatal: true }); - const odd = new Uint8Array([0x00]); - const even = new Uint8Array([0x00, 0x00]); - - expect(() => { - decoder.decode(even, { stream: true }); - decoder.decode(odd); - }).toThrow( - expect.objectContaining({ - code: "ERR_ENCODING_INVALID_ENCODED_DATA", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => { - decoder.decode(odd, { stream: true }); - decoder.decode(even); - }).toThrow( - expect.objectContaining({ - code: "ERR_ENCODING_INVALID_ENCODED_DATA", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -} - -//<#END_FILE: test-whatwg-encoding-custom-fatal-streaming.js diff --git a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-fatal.test.js b/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-fatal.test.js deleted file mode 100644 index 14aa0d31c3..0000000000 --- a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-fatal.test.js +++ /dev/null @@ -1,68 +0,0 @@ -//#FILE: test-whatwg-encoding-custom-textdecoder-fatal.js -//#SHA1: e2b44e43b78b053687ab4a8dc5de7557f6637643 -//----------------- -"use strict"; - -// From: https://github.com/w3c/web-platform-tests/blob/39a67e2fff/encoding/textdecoder-fatal.html -// With the twist that we specifically test for Node.js error codes - -if (!globalThis.Intl) { - test.skip("missing Intl"); -} - -const bad = [ - { encoding: "utf-8", input: [0xff], name: "invalid code" }, - { encoding: "utf-8", input: [0xc0], name: "ends early" }, - { encoding: "utf-8", input: [0xe0], name: "ends early 2" }, - { encoding: "utf-8", input: [0xc0, 0x00], name: "invalid trail" }, - { encoding: "utf-8", input: [0xc0, 0xc0], name: "invalid trail 2" }, - { encoding: "utf-8", input: [0xe0, 0x00], name: "invalid trail 3" }, - { encoding: "utf-8", input: [0xe0, 0xc0], name: "invalid trail 4" }, - { encoding: "utf-8", input: [0xe0, 0x80, 0x00], name: "invalid trail 5" }, - { encoding: "utf-8", input: [0xe0, 0x80, 0xc0], name: "invalid trail 6" }, - { encoding: "utf-8", input: [0xfc, 0x80, 0x80, 0x80, 0x80, 0x80], name: "> 0x10FFFF" }, - { encoding: "utf-8", input: [0xfe, 0x80, 0x80, 0x80, 0x80, 0x80], name: "obsolete lead byte" }, - // Overlong encodings - { encoding: "utf-8", input: [0xc0, 0x80], name: "overlong U+0000 - 2 bytes" }, - { encoding: "utf-8", input: [0xe0, 0x80, 0x80], name: "overlong U+0000 - 3 bytes" }, - { encoding: "utf-8", input: [0xf0, 0x80, 0x80, 0x80], name: "overlong U+0000 - 4 bytes" }, - { encoding: "utf-8", input: [0xf8, 0x80, 0x80, 0x80, 0x80], name: "overlong U+0000 - 5 bytes" }, - { encoding: "utf-8", input: [0xfc, 0x80, 0x80, 0x80, 0x80, 0x80], name: "overlong U+0000 - 6 bytes" }, - { encoding: "utf-8", input: [0xc1, 0xbf], name: "overlong U+007F - 2 bytes" }, - { encoding: "utf-8", input: [0xe0, 0x81, 0xbf], name: "overlong U+007F - 3 bytes" }, - { encoding: "utf-8", input: [0xf0, 0x80, 0x81, 0xbf], name: "overlong U+007F - 4 bytes" }, - { encoding: "utf-8", input: [0xf8, 0x80, 0x80, 0x81, 0xbf], name: "overlong U+007F - 5 bytes" }, - { encoding: "utf-8", input: [0xfc, 0x80, 0x80, 0x80, 0x81, 0xbf], name: "overlong U+007F - 6 bytes" }, - { encoding: "utf-8", input: [0xe0, 0x9f, 0xbf], name: "overlong U+07FF - 3 bytes" }, - { encoding: "utf-8", input: [0xf0, 0x80, 0x9f, 0xbf], name: "overlong U+07FF - 4 bytes" }, - { encoding: "utf-8", input: [0xf8, 0x80, 0x80, 0x9f, 0xbf], name: "overlong U+07FF - 5 bytes" }, - { encoding: "utf-8", input: [0xfc, 0x80, 0x80, 0x80, 0x9f, 0xbf], name: "overlong U+07FF - 6 bytes" }, - { encoding: "utf-8", input: [0xf0, 0x8f, 0xbf, 0xbf], name: "overlong U+FFFF - 4 bytes" }, - { encoding: "utf-8", input: [0xf8, 0x80, 0x8f, 0xbf, 0xbf], name: "overlong U+FFFF - 5 bytes" }, - { encoding: "utf-8", input: [0xfc, 0x80, 0x80, 0x8f, 0xbf, 0xbf], name: "overlong U+FFFF - 6 bytes" }, - { encoding: "utf-8", input: [0xf8, 0x84, 0x8f, 0xbf, 0xbf], name: "overlong U+10FFFF - 5 bytes" }, - { encoding: "utf-8", input: [0xfc, 0x80, 0x84, 0x8f, 0xbf, 0xbf], name: "overlong U+10FFFF - 6 bytes" }, - // UTF-16 surrogates encoded as code points in UTF-8 - { encoding: "utf-8", input: [0xed, 0xa0, 0x80], name: "lead surrogate" }, - { encoding: "utf-8", input: [0xed, 0xb0, 0x80], name: "trail surrogate" }, - { encoding: "utf-8", input: [0xed, 0xa0, 0x80, 0xed, 0xb0, 0x80], name: "surrogate pair" }, - { encoding: "utf-16le", input: [0x00], name: "truncated code unit" }, - // Mismatched UTF-16 surrogates are exercised in utf16-surrogates.html - // FIXME: Add legacy encoding cases -]; - -bad.forEach(t => { - test(`TextDecoder fatal error: ${t.name}`, () => { - expect(() => { - new TextDecoder(t.encoding, { fatal: true }).decode(new Uint8Array(t.input)); - }).toThrow( - expect.objectContaining({ - code: "ERR_ENCODING_INVALID_ENCODED_DATA", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-whatwg-encoding-custom-textdecoder-fatal.js diff --git a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-ignorebom.test.js b/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-ignorebom.test.js deleted file mode 100644 index 43a7a005ce..0000000000 --- a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-ignorebom.test.js +++ /dev/null @@ -1,35 +0,0 @@ -//#FILE: test-whatwg-encoding-custom-textdecoder-ignorebom.js -//#SHA1: 8a119026559b0341b524a97eaaf87e948a502dd6 -//----------------- -"use strict"; - -// From: https://github.com/w3c/web-platform-tests/blob/7f567fa29c/encoding/textdecoder-ignorebom.html -// This is the part that can be run without ICU - -const cases = [ - { - encoding: "utf-8", - bytes: [0xef, 0xbb, 0xbf, 0x61, 0x62, 0x63], - }, - { - encoding: "utf-16le", - bytes: [0xff, 0xfe, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00], - }, -]; - -cases.forEach(testCase => { - test(`TextDecoder with ${testCase.encoding} encoding`, () => { - const BOM = "\uFEFF"; - let decoder = new TextDecoder(testCase.encoding, { ignoreBOM: true }); - const bytes = new Uint8Array(testCase.bytes); - expect(decoder.decode(bytes)).toBe(`${BOM}abc`); - - decoder = new TextDecoder(testCase.encoding, { ignoreBOM: false }); - expect(decoder.decode(bytes)).toBe("abc"); - - decoder = new TextDecoder(testCase.encoding); - expect(decoder.decode(bytes)).toBe("abc"); - }); -}); - -//<#END_FILE: test-whatwg-encoding-custom-textdecoder-ignorebom.js diff --git a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-invalid-arg.test.js b/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-invalid-arg.test.js deleted file mode 100644 index 6389f03fbd..0000000000 --- a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-invalid-arg.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-whatwg-encoding-custom-textdecoder-invalid-arg.js -//#SHA1: eaf5b5a330366828645f6a0be0dbd859cf9f1bda -//----------------- -"use strict"; - -// This tests that ERR_INVALID_ARG_TYPE are thrown when -// invalid arguments are passed to TextDecoder. - -test("TextDecoder throws ERR_INVALID_ARG_TYPE for invalid input types", () => { - const notArrayBufferViewExamples = [false, {}, 1, "", new Error()]; - notArrayBufferViewExamples.forEach(invalidInputType => { - expect(() => { - new TextDecoder(undefined, null).decode(invalidInputType); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-whatwg-encoding-custom-textdecoder-invalid-arg.js diff --git a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-streaming.test.js b/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-streaming.test.js deleted file mode 100644 index af2ab322ed..0000000000 --- a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-streaming.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-whatwg-encoding-custom-textdecoder-streaming.js -//#SHA1: d98fc14ce0348f22b3bae160b4e1e4882ce75749 -//----------------- -"use strict"; - -// From: https://github.com/w3c/web-platform-tests/blob/fa9436d12c/encoding/textdecoder-streaming.html -// This is the part that can be run without ICU - -const string = "\x00123ABCabc\x80\xFF\u0100\u1000\uFFFD\uD800\uDC00\uDBFF\uDFFF"; -const octets = { - "utf-8": [ - 0x00, 0x31, 0x32, 0x33, 0x41, 0x42, 0x43, 0x61, 0x62, 0x63, 0xc2, 0x80, 0xc3, 0xbf, 0xc4, 0x80, 0xe1, 0x80, 0x80, - 0xef, 0xbf, 0xbd, 0xf0, 0x90, 0x80, 0x80, 0xf4, 0x8f, 0xbf, 0xbf, - ], - "utf-16le": [ - 0x00, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, - 0x00, 0x80, 0x00, 0xff, 0x00, 0x00, 0x01, 0x00, 0x10, 0xfd, 0xff, 0x00, 0xd8, 0x00, 0xdc, 0xff, 0xdb, 0xff, 0xdf, - ], -}; - -Object.keys(octets).forEach(encoding => { - describe(`TextDecoder streaming for ${encoding}`, () => { - for (let len = 1; len <= 5; ++len) { - it(`should correctly decode with chunk size ${len}`, () => { - const encoded = octets[encoding]; - const decoder = new TextDecoder(encoding); - let out = ""; - for (let i = 0; i < encoded.length; i += len) { - const sub = []; - for (let j = i; j < encoded.length && j < i + len; ++j) sub.push(encoded[j]); - out += decoder.decode(new Uint8Array(sub), { stream: true }); - } - out += decoder.decode(); - expect(out).toBe(string); - }); - } - }); -}); - -//<#END_FILE: test-whatwg-encoding-custom-textdecoder-streaming.js diff --git a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-utf16-surrogates.test.js b/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-utf16-surrogates.test.js deleted file mode 100644 index 22c5f5f5da..0000000000 --- a/test/js/node/test/parallel/whatwg-encoding-custom-textdecoder-utf16-surrogates.test.js +++ /dev/null @@ -1,60 +0,0 @@ -//#FILE: test-whatwg-encoding-custom-textdecoder-utf16-surrogates.js -//#SHA1: 0b74fafedb0961a831dff991c01aba65b15efb80 -//----------------- -"use strict"; - -// From: https://github.com/w3c/web-platform-tests/blob/39a67e2fff/encoding/textdecoder-utf16-surrogates.html -// With the twist that we specifically test for Node.js error codes - -if (!globalThis.Intl) { - test.skip("missing Intl"); -} - -const bad = [ - { - encoding: "utf-16le", - input: [0x00, 0xd8], - expected: "\uFFFD", - name: "lone surrogate lead", - }, - { - encoding: "utf-16le", - input: [0x00, 0xdc], - expected: "\uFFFD", - name: "lone surrogate trail", - }, - { - encoding: "utf-16le", - input: [0x00, 0xd8, 0x00, 0x00], - expected: "\uFFFD\u0000", - name: "unmatched surrogate lead", - }, - { - encoding: "utf-16le", - input: [0x00, 0xdc, 0x00, 0x00], - expected: "\uFFFD\u0000", - name: "unmatched surrogate trail", - }, - { - encoding: "utf-16le", - input: [0x00, 0xdc, 0x00, 0xd8], - expected: "\uFFFD\uFFFD", - name: "swapped surrogate pair", - }, -]; - -bad.forEach(t => { - test(`TextDecoder with fatal option throws for ${t.name}`, () => { - expect(() => { - new TextDecoder(t.encoding, { fatal: true }).decode(new Uint8Array(t.input)); - }).toThrow( - expect.objectContaining({ - code: "ERR_ENCODING_INVALID_ENCODED_DATA", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-whatwg-encoding-custom-textdecoder-utf16-surrogates.js diff --git a/test/js/node/test/parallel/whatwg-events-add-event-listener-options-passive.test.js b/test/js/node/test/parallel/whatwg-events-add-event-listener-options-passive.test.js deleted file mode 100644 index a918709441..0000000000 --- a/test/js/node/test/parallel/whatwg-events-add-event-listener-options-passive.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-whatwg-events-add-event-listener-options-passive.js -//#SHA1: e3c00da24b307d0e8466611bee33f70db48ad39c -//----------------- -"use strict"; - -// Manually converted from https://github.com/web-platform-tests/wpt/blob/master/dom/events/AddEventListenerOptions-passive.html -// in order to define the `document` ourselves - -test("AddEventListener options passive", () => { - const document = new EventTarget(); - let supportsPassive = false; - const query_options = { - get passive() { - supportsPassive = true; - return false; - }, - get dummy() { - throw new Error("dummy value getter invoked"); - }, - }; - - document.addEventListener("test_event", null, query_options); - expect(supportsPassive).toBe(true); - - supportsPassive = false; - document.removeEventListener("test_event", null, query_options); - expect(supportsPassive).toBe(false); -}); - -test("testPassiveValue", () => { - function testPassiveValue(optionsValue, expectedDefaultPrevented) { - const document = new EventTarget(); - let defaultPrevented; - function handler(e) { - if (e.defaultPrevented) { - throw new Error("Event prematurely marked defaultPrevented"); - } - e.preventDefault(); - defaultPrevented = e.defaultPrevented; - } - document.addEventListener("test", handler, optionsValue); - // TODO the WHATWG test is more extensive here and tests dispatching on - // document.body, if we ever support getParent we should amend this - const ev = new Event("test", { bubbles: true, cancelable: true }); - const uncanceled = document.dispatchEvent(ev); - - expect(defaultPrevented).toBe(expectedDefaultPrevented); - expect(uncanceled).toBe(!expectedDefaultPrevented); - - document.removeEventListener("test", handler, optionsValue); - } - testPassiveValue(undefined, true); - testPassiveValue({}, true); - testPassiveValue({ passive: false }, true); - - // TODO: passive listeners is still broken - // testPassiveValue({ passive: 1 }, false); - // testPassiveValue({ passive: true }, false); - // testPassiveValue({ passive: 0 }, true); -}); - -//<#END_FILE: test-whatwg-events-add-event-listener-options-passive.js diff --git a/test/js/node/test/parallel/whatwg-events-add-event-listener-options-signal.test.js b/test/js/node/test/parallel/whatwg-events-add-event-listener-options-signal.test.js deleted file mode 100644 index db5c7ef849..0000000000 --- a/test/js/node/test/parallel/whatwg-events-add-event-listener-options-signal.test.js +++ /dev/null @@ -1,176 +0,0 @@ -//#FILE: test-whatwg-events-add-event-listener-options-signal.js -//#SHA1: 2282c25dbc2f2c8bec3b2b97e0a68f3073c75c91 -//----------------- -"use strict"; - -// Manually ported from: wpt@dom/events/AddEventListenerOptions-signal.any.js - -test("Passing an AbortSignal to addEventListener does not prevent removeEventListener", () => { - let count = 0; - function handler() { - count++; - } - const et = new EventTarget(); - const controller = new AbortController(); - et.addEventListener("test", handler, { signal: controller.signal }); - et.dispatchEvent(new Event("test")); - expect(count).toBe(1); - et.dispatchEvent(new Event("test")); - expect(count).toBe(2); - controller.abort(); - et.dispatchEvent(new Event("test")); - expect(count).toBe(2); - // See: https://github.com/nodejs/node/pull/37696 , adding an event listener - // should always return undefined. - expect(et.addEventListener("test", handler, { signal: controller.signal })).toBeUndefined(); - et.dispatchEvent(new Event("test")); - expect(count).toBe(2); -}); - -test("Passing an AbortSignal to addEventListener works with the once flag", () => { - let count = 0; - function handler() { - count++; - } - const et = new EventTarget(); - const controller = new AbortController(); - et.addEventListener("test", handler, { signal: controller.signal }); - et.removeEventListener("test", handler); - et.dispatchEvent(new Event("test")); - expect(count).toBe(0); -}); - -test("Removing a once listener works with a passed signal", () => { - let count = 0; - function handler() { - count++; - } - const et = new EventTarget(); - const controller = new AbortController(); - const options = { signal: controller.signal, once: true }; - et.addEventListener("test", handler, options); - controller.abort(); - et.dispatchEvent(new Event("test")); - expect(count).toBe(0); -}); - -test("Removing a once listener with options works", () => { - let count = 0; - function handler() { - count++; - } - const et = new EventTarget(); - const controller = new AbortController(); - const options = { signal: controller.signal, once: true }; - et.addEventListener("test", handler, options); - et.removeEventListener("test", handler); - et.dispatchEvent(new Event("test")); - expect(count).toBe(0); -}); - -test("Passing an AbortSignal to multiple listeners", () => { - let count = 0; - function handler() { - count++; - } - const et = new EventTarget(); - const controller = new AbortController(); - const options = { signal: controller.signal, once: true }; - et.addEventListener("first", handler, options); - et.addEventListener("second", handler, options); - controller.abort(); - et.dispatchEvent(new Event("first")); - et.dispatchEvent(new Event("second")); - expect(count).toBe(0); -}); - -test("Passing an AbortSignal to addEventListener works with the capture flag", () => { - let count = 0; - function handler() { - count++; - } - const et = new EventTarget(); - const controller = new AbortController(); - const options = { signal: controller.signal, capture: true }; - et.addEventListener("test", handler, options); - controller.abort(); - et.dispatchEvent(new Event("test")); - expect(count).toBe(0); -}); - -test("Aborting from a listener does not call future listeners", () => { - let count = 0; - function handler() { - count++; - } - const et = new EventTarget(); - const controller = new AbortController(); - const options = { signal: controller.signal }; - et.addEventListener( - "test", - () => { - controller.abort(); - }, - options, - ); - et.addEventListener("test", handler, options); - et.dispatchEvent(new Event("test")); - expect(count).toBe(0); -}); - -test("Adding then aborting a listener in another listener does not call it", () => { - let count = 0; - function handler() { - count++; - } - const et = new EventTarget(); - const controller = new AbortController(); - et.addEventListener( - "test", - () => { - et.addEventListener("test", handler, { signal: controller.signal }); - controller.abort(); - }, - { signal: controller.signal }, - ); - et.dispatchEvent(new Event("test")); - expect(count).toBe(0); -}); - -test("Aborting from a nested listener should remove it", () => { - const et = new EventTarget(); - const ac = new AbortController(); - let count = 0; - et.addEventListener( - "foo", - () => { - et.addEventListener( - "foo", - () => { - count++; - if (count > 5) ac.abort(); - et.dispatchEvent(new Event("foo")); - }, - { signal: ac.signal }, - ); - et.dispatchEvent(new Event("foo")); - }, - { once: true }, - ); - et.dispatchEvent(new Event("foo")); - expect(count).toBe(6); -}); - -test("Invalid signal values throw TypeError", () => { - const et = new EventTarget(); - [1, 1n, {}, [], null, true, "hi", Symbol(), () => {}].forEach(signal => { - expect(() => et.addEventListener("foo", () => {}, { signal })).toThrow( - expect.objectContaining({ - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-whatwg-events-add-event-listener-options-signal.js diff --git a/test/js/node/test/parallel/whatwg-events-event-constructors.test.js b/test/js/node/test/parallel/whatwg-events-event-constructors.test.js deleted file mode 100644 index 8f08b26d1b..0000000000 --- a/test/js/node/test/parallel/whatwg-events-event-constructors.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-whatwg-events-event-constructors.js -//#SHA1: cf82cf4c0bfbf8bd7cdc9e9328587c2a1266cad8 -//----------------- -"use strict"; - -// Source: https://github.com/web-platform-tests/wpt/blob/6cef1d2087d6a07d7cc6cee8cf207eec92e27c5f/dom/events/Event-constructors.any.js#L91-L112 -test("Event constructor with getter options", () => { - const called = []; - const ev = new Event("Xx", { - get cancelable() { - called.push("cancelable"); - return false; - }, - get bubbles() { - called.push("bubbles"); - return true; - }, - get sweet() { - called.push("sweet"); - return "x"; - }, - }); - - expect(called).toEqual(["bubbles", "cancelable"]); - expect(ev.type).toBe("Xx"); - expect(ev.bubbles).toBe(true); - expect(ev.cancelable).toBe(false); - expect(ev.sweet).toBeUndefined(); -}); - -//<#END_FILE: test-whatwg-events-event-constructors.js diff --git a/test/js/node/test/parallel/whatwg-events-eventtarget-this-of-listener.test.js b/test/js/node/test/parallel/whatwg-events-eventtarget-this-of-listener.test.js deleted file mode 100644 index 9de2361e15..0000000000 --- a/test/js/node/test/parallel/whatwg-events-eventtarget-this-of-listener.test.js +++ /dev/null @@ -1,120 +0,0 @@ -//#FILE: test-whatwg-events-eventtarget-this-of-listener.js -//#SHA1: 8325e99e2f04d0fbf14abd12f002da81e4a6c338 -//----------------- -"use strict"; - -// Manually ported from: https://github.com/web-platform-tests/wpt/blob/6cef1d2087d6a07d7cc6cee8cf207eec92e27c5f/dom/events/EventTarget-this-of-listener.html - -// Mock document -const document = { - createElement: () => new EventTarget(), - createTextNode: () => new EventTarget(), - createDocumentFragment: () => new EventTarget(), - createComment: () => new EventTarget(), - createProcessingInstruction: () => new EventTarget(), -}; - -test("the this value inside the event listener callback should be the node", () => { - const nodes = [ - document.createElement("p"), - document.createTextNode("some text"), - document.createDocumentFragment(), - document.createComment("a comment"), - document.createProcessingInstruction("target", "data"), - ]; - - let callCount = 0; - for (const node of nodes) { - node.addEventListener("someevent", function () { - ++callCount; - expect(this).toBe(node); - }); - - node.dispatchEvent(new Event("someevent")); - } - - expect(callCount).toBe(nodes.length); -}); - -test("addEventListener should not require handleEvent to be defined on object listeners", () => { - const nodes = [ - document.createElement("p"), - document.createTextNode("some text"), - document.createDocumentFragment(), - document.createComment("a comment"), - document.createProcessingInstruction("target", "data"), - ]; - - let callCount = 0; - for (const node of nodes) { - const handler = {}; - - node.addEventListener("someevent", handler); - handler.handleEvent = function () { - ++callCount; - expect(this).toBe(handler); - }; - - node.dispatchEvent(new Event("someevent")); - } - - expect(callCount).toBe(nodes.length); -}); - -test("handleEvent properties added to a function before addEventListener are not reached", () => { - const nodes = [ - document.createElement("p"), - document.createTextNode("some text"), - document.createDocumentFragment(), - document.createComment("a comment"), - document.createProcessingInstruction("target", "data"), - ]; - - let callCount = 0; - for (const node of nodes) { - function handler() { - ++callCount; - expect(this).toBe(node); - } - - handler.handleEvent = () => { - throw new Error("should not call the handleEvent method on a function"); - }; - - node.addEventListener("someevent", handler); - - node.dispatchEvent(new Event("someevent")); - } - - expect(callCount).toBe(nodes.length); -}); - -test("handleEvent properties added to a function after addEventListener are not reached", () => { - const nodes = [ - document.createElement("p"), - document.createTextNode("some text"), - document.createDocumentFragment(), - document.createComment("a comment"), - document.createProcessingInstruction("target", "data"), - ]; - - let callCount = 0; - for (const node of nodes) { - function handler() { - ++callCount; - expect(this).toBe(node); - } - - node.addEventListener("someevent", handler); - - handler.handleEvent = () => { - throw new Error("should not call the handleEvent method on a function"); - }; - - node.dispatchEvent(new Event("someevent")); - } - - expect(callCount).toBe(nodes.length); -}); - -//<#END_FILE: test-whatwg-events-eventtarget-this-of-listener.js diff --git a/test/js/node/test/parallel/whatwg-url-canparse.test.js b/test/js/node/test/parallel/whatwg-url-canparse.test.js deleted file mode 100644 index e8f7099fbe..0000000000 --- a/test/js/node/test/parallel/whatwg-url-canparse.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-whatwg-url-canparse.js -//#SHA1: 23d2ea01c951bea491747284dca1abaa17596ff1 -//----------------- -"use strict"; - -const { URL } = require("url"); - -// Note: We're not using internal bindings as per your instructions -// Instead, we'll mock the canParse function - -// Mock the canParse function -const canParse = jest.fn((url, base) => { - try { - new URL(url, base); - return true; - } catch { - return false; - } -}); - -describe("URL.canParse", () => { - test("should not throw when called without a base string", () => { - expect(() => URL.canParse("https://example.org")).not.toThrow(); - expect(URL.canParse("https://example.org")).toBe(true); - expect(canParse("https://example.org")).toBe(true); - }); - - test("should correctly parse URL with base", () => { - // This for-loop is used to test V8 Fast API optimizations - for (let i = 0; i < 100000; i++) { - // This example is used because only parsing the first parameter - // results in an invalid URL. They have to be used together to - // produce truthy value. - expect(URL.canParse("/", "http://n")).toBe(true); - } - }); -}); - -//<#END_FILE: test-whatwg-url-canparse.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-deepequal.test.js b/test/js/node/test/parallel/whatwg-url-custom-deepequal.test.js deleted file mode 100644 index f3bc0a35f7..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-deepequal.test.js +++ /dev/null @@ -1,17 +0,0 @@ -//#FILE: test-whatwg-url-custom-deepequal.js -//#SHA1: 57a28f56bb87a00fe2433fabebd4a85cb2da39d0 -//----------------- -"use strict"; -// This tests that the internal flags in URL objects are consistent, as manifest -// through assert libraries. -// See https://github.com/nodejs/node/issues/24211 - -// Tests below are not from WPT. - -test("URL objects are deeply equal", () => { - expect(new URL("./foo", "https://example.com/")).toEqual(new URL("https://example.com/foo")); - - expect(new URL("./foo", "https://user:pass@example.com/")).toEqual(new URL("https://user:pass@example.com/foo")); -}); - -//<#END_FILE: test-whatwg-url-custom-deepequal.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-href-side-effect.test.js b/test/js/node/test/parallel/whatwg-url-custom-href-side-effect.test.js deleted file mode 100644 index e535f59ad2..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-href-side-effect.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-whatwg-url-custom-href-side-effect.js -//#SHA1: c2abb976ed209d25f38bb1ff1e7d8c2110ee51d4 -//----------------- -"use strict"; - -// Tests below are not from WPT. - -test("URL href assignment side effect", () => { - const ref = new URL("http://example.com/path"); - const url = new URL("http://example.com/path"); - - expect(() => { - url.href = ""; - }).toThrow( - expect.objectContaining({ - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(url).toEqual(ref); -}); - -//<#END_FILE: test-whatwg-url-custom-href-side-effect.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-searchparams-entries.test.js b/test/js/node/test/parallel/whatwg-url-custom-searchparams-entries.test.js deleted file mode 100644 index 8d7bd72648..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-searchparams-entries.test.js +++ /dev/null @@ -1,62 +0,0 @@ -//#FILE: test-whatwg-url-custom-searchparams-entries.js -//#SHA1: 4ba98b18a2f44b46ac4e6e0ee5179e97083100be -//----------------- -"use strict"; - -// Tests below are not from WPT. -test("URLSearchParams entries", () => { - const params = new URLSearchParams("a=b&c=d"); - const entries = params.entries(); - - expect(typeof entries[Symbol.iterator]).toBe("function"); - expect(entries[Symbol.iterator]()).toBe(entries); - - expect(entries.next()).toEqual({ - value: ["a", "b"], - done: false, - }); - - expect(entries.next()).toEqual({ - value: ["c", "d"], - done: false, - }); - - expect(entries.next()).toEqual({ - value: undefined, - done: true, - }); - - expect(entries.next()).toEqual({ - value: undefined, - done: true, - }); -}); - -test("entries.next() throws with invalid this", () => { - const params = new URLSearchParams("a=b&c=d"); - const entries = params.entries(); - - expect(() => { - entries.next.call(undefined); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -test("params.entries() throws with invalid this", () => { - expect(() => { - URLSearchParams.prototype.entries.call(undefined); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-whatwg-url-custom-searchparams-entries.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-searchparams-foreach.test.js b/test/js/node/test/parallel/whatwg-url-custom-searchparams-foreach.test.js deleted file mode 100644 index 233a679a00..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-searchparams-foreach.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-whatwg-url-custom-searchparams-foreach.js -//#SHA1: affe74306c7fdeb688aadc771c4d7d5b769fc236 -//----------------- -"use strict"; - -// Tests below are not from WPT. - -test('URLSearchParams.forEach called with invalid "this"', () => { - const params = new URLSearchParams(); - expect(() => { - params.forEach.call(undefined); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-whatwg-url-custom-searchparams-foreach.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-searchparams-keys.test.js b/test/js/node/test/parallel/whatwg-url-custom-searchparams-keys.test.js deleted file mode 100644 index 0b4a13ea30..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-searchparams-keys.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-whatwg-url-custom-searchparams-keys.js -//#SHA1: 06abe929cfe842fcdd80b44cee8a0092358e5fdf -//----------------- -"use strict"; - -// Tests below are not from WPT. - -describe("URLSearchParams keys", () => { - let params; - let keys; - - beforeEach(() => { - params = new URLSearchParams("a=b&c=d"); - keys = params.keys(); - }); - - test("keys iterator is a function and returns self", () => { - expect(typeof keys[Symbol.iterator]).toBe("function"); - expect(keys[Symbol.iterator]()).toBe(keys); - }); - - test("keys iterator returns correct values", () => { - expect(keys.next()).toEqual({ - value: "a", - done: false, - }); - expect(keys.next()).toEqual({ - value: "c", - done: false, - }); - expect(keys.next()).toEqual({ - value: undefined, - done: true, - }); - expect(keys.next()).toEqual({ - value: undefined, - done: true, - }); - }); - - test("keys.next() throws with invalid this", () => { - expect(() => { - keys.next.call(undefined); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - name: "TypeError", - message: expect.any(String), - }), - ); - }); - - test("params.keys() throws with invalid this", () => { - expect(() => { - params.keys.call(undefined); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - name: "TypeError", - message: expect.any(String), - }), - ); - }); -}); - -//<#END_FILE: test-whatwg-url-custom-searchparams-keys.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-searchparams-sort.test.js b/test/js/node/test/parallel/whatwg-url-custom-searchparams-sort.test.js deleted file mode 100644 index 2bbaa7c6a6..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-searchparams-sort.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-whatwg-url-custom-searchparams-sort.js -//#SHA1: 9a97c952cb19488375ead5a62f11fb579fbee211 -//----------------- -"use strict"; - -// Tests below are not from WPT. - -// TODO(joyeecheung): upstream this to WPT, if possible - even -// just as a test for large inputs. Other implementations may -// have a similar cutoff anyway. - -// Test bottom-up iterative stable merge sort because we only use that -// algorithm to sort > 100 search params. -const tests = [{ input: "", output: [] }]; -const pairs = []; -for (let i = 10; i < 100; i++) { - pairs.push([`a${i}`, "b"]); - tests[0].output.push([`a${i}`, "b"]); -} -tests[0].input = pairs - .sort(() => Math.random() > 0.5) - .map(pair => pair.join("=")) - .join("&"); - -tests.push({ - input: "z=a&=b&c=d", - output: [ - ["", "b"], - ["c", "d"], - ["z", "a"], - ], -}); - -tests.forEach(val => { - test(`Parse and sort: ${val.input}`, () => { - const params = new URLSearchParams(val.input); - let i = 0; - params.sort(); - for (const param of params) { - expect(param).toEqual(val.output[i]); - i++; - } - }); - - test(`URL parse and sort: ${val.input}`, () => { - const url = new URL(`?${val.input}`, "https://example/"); - url.searchParams.sort(); - const params = new URLSearchParams(url.search); - let i = 0; - for (const param of params) { - expect(param).toEqual(val.output[i]); - i++; - } - }); -}); - -//<#END_FILE: test-whatwg-url-custom-searchparams-sort.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-searchparams-stringifier.test.js b/test/js/node/test/parallel/whatwg-url-custom-searchparams-stringifier.test.js deleted file mode 100644 index 94028430ba..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-searchparams-stringifier.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-whatwg-url-custom-searchparams-stringifier.js -//#SHA1: 588663b1cad21e26a4b8e25c0659d204a5d96542 -//----------------- -"use strict"; - -// Tests below are not from WPT. - -test("URLSearchParams toString called with invalid this", () => { - const params = new URLSearchParams(); - expect(() => { - params.toString.call(undefined); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -// The URLSearchParams stringifier mutates the base URL using -// different percent-encoding rules than the URL itself. -test("URLSearchParams stringifier mutates base URL with different percent-encoding", () => { - const myUrl = new URL("https://example.org?foo=~bar"); - expect(myUrl.search).toBe("?foo=~bar"); - myUrl.searchParams.sort(); - expect(myUrl.search).toBe("?foo=%7Ebar"); -}); - -//<#END_FILE: test-whatwg-url-custom-searchparams-stringifier.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-searchparams-values.test.js b/test/js/node/test/parallel/whatwg-url-custom-searchparams-values.test.js deleted file mode 100644 index 5b7d88801c..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-searchparams-values.test.js +++ /dev/null @@ -1,52 +0,0 @@ -//#FILE: test-whatwg-url-custom-searchparams-values.js -//#SHA1: 7df0ccf30363d589199bb3f71c68e5559e9e4f59 -//----------------- -"use strict"; - -// Tests below are not from WPT. - -test("URLSearchParams values() method", () => { - const params = new URLSearchParams("a=b&c=d"); - const values = params.values(); - - expect(typeof values[Symbol.iterator]).toBe("function"); - expect(values[Symbol.iterator]()).toBe(values); - expect(values.next()).toEqual({ - value: "b", - done: false, - }); - expect(values.next()).toEqual({ - value: "d", - done: false, - }); - expect(values.next()).toEqual({ - value: undefined, - done: true, - }); - expect(values.next()).toEqual({ - value: undefined, - done: true, - }); - - expect(() => { - values.next.call(undefined); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => { - params.values.call(undefined); - }).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_THIS", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-whatwg-url-custom-searchparams-values.js diff --git a/test/js/node/test/parallel/whatwg-url-custom-searchparams.test.js b/test/js/node/test/parallel/whatwg-url-custom-searchparams.test.js deleted file mode 100644 index 7de337086e..0000000000 --- a/test/js/node/test/parallel/whatwg-url-custom-searchparams.test.js +++ /dev/null @@ -1,191 +0,0 @@ -//#FILE: test-whatwg-url-custom-searchparams.js -//#SHA1: 8308ed9fc341a1caaadcf01653bd49b96cebf599 -//----------------- -"use strict"; - -// Tests below are not from WPT. - -const assert = require("assert"); -const fixtures = require("../common/fixtures"); - -const serialized = - "a=a&a=1&a=true&a=undefined&a=null&a=%EF%BF%BD" + - "&a=%EF%BF%BD&a=%F0%9F%98%80&a=%EF%BF%BD%EF%BF%BD" + - "&a=%5Bobject+Object%5D"; -const values = ["a", 1, true, undefined, null, "\uD83D", "\uDE00", "\uD83D\uDE00", "\uDE00\uD83D", {}]; -const normalizedValues = [ - "a", - "1", - "true", - "undefined", - "null", - "\uFFFD", - "\uFFFD", - "\uD83D\uDE00", - "\uFFFD\uFFFD", - "[object Object]", -]; - -describe("WHATWG URL Custom SearchParams", () => { - let m, sp; - - beforeEach(() => { - m = new URL("http://example.org"); - sp = m.searchParams; - }); - - it("should not modify own symbols when accessing searchParams", () => { - const ownSymbolsBeforeGetterAccess = Object.getOwnPropertySymbols(m); - expect(sp).toBeDefined(); - expect(Object.getOwnPropertySymbols(m)).toEqual(ownSymbolsBeforeGetterAccess); - }); - - it("should initialize with empty search params", () => { - expect(sp.toString()).toBe(""); - expect(m.search).toBe(""); - }); - - it("should handle setting and deleting search params", () => { - expect(sp.has("a")).toBe(false); - values.forEach(i => sp.set("a", i)); - expect(sp.has("a")).toBe(true); - expect(sp.get("a")).toBe("[object Object]"); - sp.delete("a"); - expect(sp.has("a")).toBe(false); - }); - - it("should handle appending search params", () => { - m.search = ""; - expect(sp.toString()).toBe(""); - - values.forEach(i => sp.append("a", i)); - expect(sp.has("a")).toBe(true); - expect(sp.getAll("a").length).toBe(values.length); - expect(sp.get("a")).toBe("a"); - - expect(sp.toString()).toBe(serialized); - expect(m.search).toBe(`?${serialized}`); - }); - - it("should update URL components when modifying search params", () => { - sp.delete("a"); - values.forEach(i => sp.append("a", i)); - expect(m.href).toBe(`http://example.org/?${serialized}`); - expect(m.toString()).toBe(`http://example.org/?${serialized}`); - expect(m.toJSON()).toBe(`http://example.org/?${serialized}`); - }); - - it("should clear search params when setting href or search", () => { - sp.delete("a"); - values.forEach(i => sp.append("a", i)); - m.href = "http://example.org"; - expect(m.href).toBe("http://example.org/"); - expect(sp.size).toBe(0); - - values.forEach(i => sp.append("a", i)); - m.search = ""; - expect(m.href).toBe("http://example.org/"); - expect(sp.size).toBe(0); - }); - - it("should update URL components when modifying pathname or hash", () => { - sp.delete("a"); - values.forEach(i => sp.append("a", i)); - m.pathname = "/test"; - expect(m.href).toBe(`http://example.org/test?${serialized}`); - m.pathname = ""; - - sp.delete("a"); - values.forEach(i => sp.append("a", i)); - m.hash = "#test"; - expect(m.href).toBe(`http://example.org/?${serialized}#test`); - m.hash = ""; - }); - - it("should have correct iteration behavior", () => { - expect(sp[Symbol.iterator]).toBe(sp.entries); - - sp.delete("a"); - values.forEach(i => sp.append("a", i)); - - let n = 0; - for (const [key, val] of sp) { - expect(key).toBe("a"); - expect(val).toBe(normalizedValues[n]); - n++; - } - - n = 0; - for (const key of sp.keys()) { - expect(key).toBe("a"); - n++; - } - - n = 0; - for (const val of sp.values()) { - expect(val).toBe(normalizedValues[n]); - n++; - } - - n = 0; - sp.forEach(function (val, key, obj) { - expect(this).toBeUndefined(); - expect(key).toBe("a"); - expect(val).toBe(normalizedValues[n]); - expect(obj).toBe(sp); - n++; - }); - - sp.forEach(function () { - expect(this).toBe(m); - }, m); - }); - - it("should throw for invalid forEach arguments", () => { - expect(() => sp.forEach()).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - expect(() => sp.forEach(1)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - }); - - it("should handle setting search directly", () => { - m.search = "?a=a&b=b"; - expect(sp.toString()).toBe("a=a&b=b"); - }); - - it("should pass URL search params tests", () => { - const tests = require(fixtures.path("url-searchparams.js")); - - for (const [input, expected, parsed] of tests) { - if (input[0] !== "?") { - const sp = new URLSearchParams(input); - expect(String(sp)).toBe(expected); - expect(Array.from(sp)).toEqual(parsed); - - m.search = input; - expect(String(m.searchParams)).toBe(expected); - expect(Array.from(m.searchParams)).toEqual(parsed); - } - - { - const sp = new URLSearchParams(`?${input}`); - expect(String(sp)).toBe(expected); - expect(Array.from(sp)).toEqual(parsed); - - m.search = `?${input}`; - expect(String(m.searchParams)).toBe(expected); - expect(Array.from(m.searchParams)).toEqual(parsed); - } - } - }); -}); - -//<#END_FILE: test-whatwg-url-custom-searchparams.js diff --git a/test/js/node/test/parallel/whatwg-url-override-hostname.test.js b/test/js/node/test/parallel/whatwg-url-override-hostname.test.js deleted file mode 100644 index 876de04206..0000000000 --- a/test/js/node/test/parallel/whatwg-url-override-hostname.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-whatwg-url-override-hostname.js -//#SHA1: 22f2c5e784a47cc59c7f0b3229a68f4bbcfeff3e -//----------------- -"use strict"; - -test("URL with overridden hostname getter", () => { - const url = new (class extends URL { - get hostname() { - return "bar.com"; - } - })("http://foo.com/"); - - expect(url.href).toBe("http://foo.com/"); - expect(url.toString()).toBe("http://foo.com/"); - expect(url.toJSON()).toBe("http://foo.com/"); - expect(url.hash).toBe(""); - expect(url.host).toBe("foo.com"); - expect(url.hostname).toBe("bar.com"); - expect(url.origin).toBe("http://foo.com"); - expect(url.password).toBe(""); - expect(url.protocol).toBe("http:"); - expect(url.username).toBe(""); - expect(url.search).toBe(""); - expect(url.searchParams.toString()).toBe(""); -}); - -//<#END_FILE: test-whatwg-url-override-hostname.js diff --git a/test/js/node/test/parallel/worker-arraybuffer-zerofill.test.js b/test/js/node/test/parallel/worker-arraybuffer-zerofill.test.js deleted file mode 100644 index 7f9380841c..0000000000 --- a/test/js/node/test/parallel/worker-arraybuffer-zerofill.test.js +++ /dev/null @@ -1,43 +0,0 @@ -//#FILE: test-worker-arraybuffer-zerofill.js -//#SHA1: 3fd8cc412e658cb491c61d2323ff3a45af4d6dd1 -//----------------- -"use strict"; - -const { Worker } = require("worker_threads"); - -// Make sure that allocating uninitialized ArrayBuffers in one thread does not -// affect the zero-initialization in other threads. - -test("zero-initialization in other threads", done => { - const w = new Worker( - ` - const { parentPort } = require('worker_threads'); - - function post() { - const uint32array = new Uint32Array(64); - parentPort.postMessage(uint32array.reduce((a, b) => a + b)); - } - - setInterval(post, 0); - `, - { eval: true }, - ); - - function allocBuffers() { - Buffer.allocUnsafe(32 * 1024 * 1024); - } - - const interval = setInterval(allocBuffers, 0); - - let messages = 0; - w.on("message", sum => { - expect(sum).toBe(0); - if (messages++ === 100) { - clearInterval(interval); - w.terminate(); - done(); - } - }); -}); - -//<#END_FILE: test-worker-arraybuffer-zerofill.js diff --git a/test/js/node/test/parallel/worker-cjs-workerdata.test.js b/test/js/node/test/parallel/worker-cjs-workerdata.test.js deleted file mode 100644 index 0c0158346e..0000000000 --- a/test/js/node/test/parallel/worker-cjs-workerdata.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-worker-cjs-workerdata.js -//#SHA1: 8e5d70084de66c757d227df54612de48d1048ad3 -//----------------- -"use strict"; -const fixtures = require("../common/fixtures"); -const { Worker } = require("worker_threads"); - -const workerData = "Hello from main thread"; - -test("Worker with CJS module and workerData", done => { - const worker = new Worker(fixtures.path("worker-data.cjs"), { - workerData, - }); - - worker.on("message", message => { - expect(message).toBe(workerData); - done(); - }); -}); - -//<#END_FILE: test-worker-cjs-workerdata.js diff --git a/test/js/node/test/parallel/worker-cleanexit-with-js.test.js b/test/js/node/test/parallel/worker-cleanexit-with-js.test.js deleted file mode 100644 index 9e9acb2f4b..0000000000 --- a/test/js/node/test/parallel/worker-cleanexit-with-js.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-worker-cleanexit-with-js.js -//#SHA1: f47fd2ea6e4a0adf79207268baf613eb071e493a -//----------------- -"use strict"; - -// Harden the thread interactions on the exit path. -// Ensure workers are able to bail out safe at -// arbitrary execution points. By running a lot of -// JS code in a tight loop, the expectation -// is that those will be at various control flow points -// preferably in the JS land. - -const { Worker } = require("worker_threads"); - -test("Workers can bail out safely at arbitrary execution points", done => { - const code = - "setInterval(() => {" + - "require('v8').deserialize(require('v8').serialize({ foo: 'bar' }));" + - "require('vm').runInThisContext('x = \"foo\";');" + - "eval('const y = \"vm\";');}, 10);"; - - for (let i = 0; i < 9; i++) { - new Worker(code, { eval: true }); - } - - const lastWorker = new Worker(code, { eval: true }); - lastWorker.on("online", () => { - expect(true).toBe(true); // Ensure the worker came online - process.exit(0); - done(); - }); -}); - -//<#END_FILE: test-worker-cleanexit-with-js.js diff --git a/test/js/node/test/parallel/worker-cleanexit-with-moduleload.test.js b/test/js/node/test/parallel/worker-cleanexit-with-moduleload.test.js deleted file mode 100644 index ffad2cb95e..0000000000 --- a/test/js/node/test/parallel/worker-cleanexit-with-moduleload.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-worker-cleanexit-with-moduleload.js -//#SHA1: cdaed88b3a0ebbc07619e10f35ea0c62e5134b63 -//----------------- -"use strict"; - -const { Worker } = require("worker_threads"); - -// Harden the thread interactions on the exit path. -// Ensure workers are able to bail out safe at -// arbitrary execution points. By using a number of -// internal modules as load candidates, the expectation -// is that those will be at various control flow points -// preferably in the C++ land. - -const modules = ["fs", "assert", "async_hooks", "buffer", "child_process", "net", "http", "os", "path", "v8", "vm"]; - -if (process.versions.openssl) { - modules.push("https"); -} - -test("Workers can exit cleanly while loading modules", done => { - for (let i = 0; i < 10; i++) { - new Worker( - `const modules = [${modules.map(m => `'${m}'`)}];` + - "modules.forEach((module) => {" + - "const m = require(module);" + - "});", - { eval: true }, - ); - } - - // Allow workers to go live. - setTimeout(() => { - done(); - }, 200); -}, 300); // Set timeout to 300ms to allow for the 200ms delay - -//<#END_FILE: test-worker-cleanexit-with-moduleload.js diff --git a/test/js/node/test/parallel/worker-esmodule.test.js b/test/js/node/test/parallel/worker-esmodule.test.js deleted file mode 100644 index e08d58041a..0000000000 --- a/test/js/node/test/parallel/worker-esmodule.test.js +++ /dev/null @@ -1,20 +0,0 @@ -//#FILE: test-worker-esmodule.js -//#SHA1: a40c6a55aa2fe45203bec4808e0d53efed2fa4e4 -//----------------- -"use strict"; - -const fixtures = require("../common/fixtures"); -const { Worker } = require("worker_threads"); - -test("Worker can load ES module", () => { - const w = new Worker(fixtures.path("worker-script.mjs")); - - return new Promise(resolve => { - w.on("message", message => { - expect(message).toBe("Hello, world!"); - resolve(); - }); - }); -}); - -//<#END_FILE: test-worker-esmodule.js diff --git a/test/js/node/test/parallel/worker-invalid-workerdata.test.js b/test/js/node/test/parallel/worker-invalid-workerdata.test.js deleted file mode 100644 index 1776a6d26c..0000000000 --- a/test/js/node/test/parallel/worker-invalid-workerdata.test.js +++ /dev/null @@ -1,25 +0,0 @@ -//#FILE: test-worker-invalid-workerdata.js -//#SHA1: 2e1989d95e34d3603c30290e012335261767ae90 -//----------------- -"use strict"; - -const { Worker } = require("worker_threads"); - -// This tests verifies that failing to serialize workerData does not keep -// the process alive. -// Refs: https://github.com/nodejs/node/issues/22736 - -test("Worker creation with unserializable workerData throws DataCloneError", () => { - expect(() => { - new Worker("./worker.js", { - workerData: { fn: () => {} }, - }); - }).toThrow( - expect.objectContaining({ - name: "DataCloneError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-worker-invalid-workerdata.js diff --git a/test/js/node/test/parallel/worker-memory.test.js b/test/js/node/test/parallel/worker-memory.test.js deleted file mode 100644 index d18eb5bccd..0000000000 --- a/test/js/node/test/parallel/worker-memory.test.js +++ /dev/null @@ -1,63 +0,0 @@ -//#FILE: test-worker-memory.js -//#SHA1: e425b7d8f32d04fae4a6e0c697a78aeae4de2f60 -//----------------- -"use strict"; - -const util = require("util"); -const { Worker } = require("worker_threads"); -const os = require("os"); - -if (process.platform === "os400") { - test.skip("On IBMi, the rss memory always returns zero"); -} - -let numWorkers = +process.env.JOBS || os.availableParallelism(); -if (numWorkers > 20) { - // Cap the number of workers at 20 (as an even divisor of 60 used as - // the total number of workers started) otherwise the test fails on - // machines with high core counts. - numWorkers = 20; -} - -// Verify that a Worker's memory isn't kept in memory after the thread finishes. - -function run(n, done) { - console.log(`run() called with n=${n} (numWorkers=${numWorkers})`); - if (n <= 0) return done(); - const worker = new Worker("require('worker_threads').parentPort.postMessage(2 + 2)", { eval: true }); - worker.on("message", value => { - expect(value).toBe(4); - }); - worker.on("exit", () => { - run(n - 1, done); - }); -} - -test("Worker memory is not kept after thread finishes", async () => { - const startStats = process.memoryUsage(); - let finished = 0; - - const runPromises = Array(numWorkers) - .fill() - .map( - () => - new Promise(resolve => { - run(60 / numWorkers, () => { - console.log(`done() called (finished=${finished})`); - if (++finished === numWorkers) { - const finishStats = process.memoryUsage(); - // A typical value for this ratio would be ~1.15. - // 5 as a upper limit is generous, but the main point is that we - // don't have the memory of 50 Isolates/Node.js environments just lying - // around somewhere. - expect(finishStats.rss / startStats.rss).toBeLessThan(5); - } - resolve(); - }); - }), - ); - - await Promise.all(runPromises); -}, 30000); // Increase timeout to 30 seconds - -//<#END_FILE: test-worker-memory.js diff --git a/test/js/node/test/parallel/worker-message-channel-sharedarraybuffer.test.js b/test/js/node/test/parallel/worker-message-channel-sharedarraybuffer.test.js deleted file mode 100644 index 088cb1530c..0000000000 --- a/test/js/node/test/parallel/worker-message-channel-sharedarraybuffer.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-worker-message-channel-sharedarraybuffer.js -//#SHA1: 567096de16f1ea1b7a50e53cbdbd47b531af0e2a -//----------------- -// Flags: --expose-gc -"use strict"; - -const { Worker } = require("worker_threads"); - -test("SharedArrayBuffer can be shared between main thread and worker", async () => { - const sharedArrayBuffer = new SharedArrayBuffer(12); - const local = Buffer.from(sharedArrayBuffer); - - const w = new Worker( - ` - const { parentPort } = require('worker_threads'); - parentPort.on('message', ({ sharedArrayBuffer }) => { - const local = Buffer.from(sharedArrayBuffer); - local.write('world!', 6); - parentPort.postMessage('written!'); - }); - `, - { eval: true }, - ); - - const messagePromise = new Promise(resolve => { - w.on("message", resolve); - }); - - w.postMessage({ sharedArrayBuffer }); - // This would be a race condition if the memory regions were overlapping - local.write("Hello "); - - await messagePromise; - - expect(local.toString()).toBe("Hello world!"); - - global.gc(); - await w.terminate(); -}); - -//<#END_FILE: test-worker-message-channel-sharedarraybuffer.js diff --git a/test/js/node/test/parallel/worker-message-port-transfer-terminate.test.js b/test/js/node/test/parallel/worker-message-port-transfer-terminate.test.js deleted file mode 100644 index c00d6d2251..0000000000 --- a/test/js/node/test/parallel/worker-message-port-transfer-terminate.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-worker-message-port-transfer-terminate.js -//#SHA1: ca776ac07835f8e6306ba671531b8e5a73e25f27 -//----------------- -"use strict"; - -const { Worker, MessageChannel } = require("worker_threads"); - -// Check the interaction of calling .terminate() while transferring -// MessagePort objects; in particular, that it does not crash the process. - -test("Worker termination while transferring MessagePort objects", done => { - let completedIterations = 0; - - for (let i = 0; i < 10; ++i) { - const w = new Worker("require('worker_threads').parentPort.on('message', () => {})", { eval: true }); - - setImmediate(() => { - const port = new MessageChannel().port1; - w.postMessage({ port }, [port]); - w.terminate().then(() => { - completedIterations++; - if (completedIterations === 10) { - done(); - } - }); - }); - } -}); - -//<#END_FILE: test-worker-message-port-transfer-terminate.js diff --git a/test/js/node/test/parallel/worker-mjs-workerdata.test.js b/test/js/node/test/parallel/worker-mjs-workerdata.test.js deleted file mode 100644 index d3bfe6d442..0000000000 --- a/test/js/node/test/parallel/worker-mjs-workerdata.test.js +++ /dev/null @@ -1,22 +0,0 @@ -//#FILE: test-worker-mjs-workerdata.js -//#SHA1: c4df37cd769b399dd8245d29bed7f385f1adb472 -//----------------- -"use strict"; - -const fixtures = require("../common/fixtures"); -const { Worker } = require("worker_threads"); - -const workerData = "Hello from main thread"; - -test("Worker with workerData in MJS", done => { - const worker = new Worker(fixtures.path("worker-data.mjs"), { - workerData, - }); - - worker.on("message", message => { - expect(message).toBe(workerData); - done(); - }); -}); - -//<#END_FILE: test-worker-mjs-workerdata.js diff --git a/test/js/node/test/parallel/worker-nested-on-process-exit.test.js b/test/js/node/test/parallel/worker-nested-on-process-exit.test.js deleted file mode 100644 index d198efaf1f..0000000000 --- a/test/js/node/test/parallel/worker-nested-on-process-exit.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-worker-nested-on-process-exit.js -//#SHA1: a76202f79766e7ca3f39552cc00b4f31e8f38a56 -//----------------- -"use strict"; -const { Worker, workerData } = require("worker_threads"); - -// Test that 'exit' events for nested Workers are not received when a Worker -// terminates itself through process.exit(). - -if (workerData === null) { - test("nested worker exit events", () => { - const nestedWorkerExitCounter = new Int32Array(new SharedArrayBuffer(4)); - const w = new Worker(__filename, { workerData: nestedWorkerExitCounter }); - - return new Promise(resolve => { - w.on("exit", () => { - expect(nestedWorkerExitCounter[0]).toBe(0); - resolve(); - }); - }); - }); -} else { - const nestedWorker = new Worker("setInterval(() => {}, 100)", { eval: true }); - // The counter should never be increased here. - nestedWorker.on("exit", () => workerData[0]++); - nestedWorker.on("online", () => process.exit()); -} - -//<#END_FILE: test-worker-nested-on-process-exit.js diff --git a/test/js/node/test/parallel/worker-nested-uncaught.test.js b/test/js/node/test/parallel/worker-nested-uncaught.test.js deleted file mode 100644 index 050f33a797..0000000000 --- a/test/js/node/test/parallel/worker-nested-uncaught.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-worker-nested-uncaught.js -//#SHA1: 948558ccf744615abbd0df028a83b36d36ad7aff -//----------------- -"use strict"; -const { Worker } = require("worker_threads"); - -// Regression test for https://github.com/nodejs/node/issues/34309 - -test("nested worker uncaught error", done => { - const w = new Worker( - `const { Worker } = require('worker_threads'); - new Worker("throw new Error('uncaught')", { eval:true })`, - { eval: true }, - ); - - w.on("error", error => { - expect(error).toEqual( - expect.objectContaining({ - name: "Error", - message: expect.any(String), - }), - ); - done(); - }); -}); - -//<#END_FILE: test-worker-nested-uncaught.js diff --git a/test/js/node/test/parallel/worker-ref-onexit.test.js b/test/js/node/test/parallel/worker-ref-onexit.test.js deleted file mode 100644 index fb360433d4..0000000000 --- a/test/js/node/test/parallel/worker-ref-onexit.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-worker-ref-onexit.js -//#SHA1: b34eb5ff18bd889eb47cd37e696410f428b0b11b -//----------------- -"use strict"; -const { Worker } = require("worker_threads"); - -// Check that worker.unref() makes the 'exit' event not be emitted, if it is -// the only thing we would otherwise be waiting for. - -test("worker.unref() prevents exit event emission", done => { - // Use `setInterval()` to make sure the worker is alive until the end of the - // event loop turn. - const w = new Worker("setInterval(() => {}, 100);", { eval: true }); - w.unref(); - - const exitHandler = jest.fn(); - w.on("exit", exitHandler); - - // We need to use a timeout here to allow the event loop to complete - // and ensure the exit event is not called - setTimeout(() => { - expect(exitHandler).not.toHaveBeenCalled(); - done(); - }, 500); -}); - -//<#END_FILE: test-worker-ref-onexit.js diff --git a/test/js/node/test/parallel/worker-relative-path-double-dot.test.js b/test/js/node/test/parallel/worker-relative-path-double-dot.test.js deleted file mode 100644 index daed4389b1..0000000000 --- a/test/js/node/test/parallel/worker-relative-path-double-dot.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-worker-relative-path-double-dot.js -//#SHA1: 2b605c02bfb9cfc2d42533a6a184dfdbd5a6e5b7 -//----------------- -"use strict"; -const path = require("path"); -const { Worker, isMainThread, parentPort } = require("worker_threads"); - -if (isMainThread) { - test("Worker with relative path using double dot", done => { - const cwdName = path.relative("../", "."); - const relativePath = path.relative(".", __filename); - const w = new Worker(path.join("..", cwdName, relativePath)); - - w.on("message", message => { - expect(message).toBe("Hello, world!"); - done(); - }); - }); -} else { - parentPort.postMessage("Hello, world!"); -} - -//<#END_FILE: test-worker-relative-path-double-dot.js diff --git a/test/js/node/test/parallel/worker-relative-path.test.js b/test/js/node/test/parallel/worker-relative-path.test.js deleted file mode 100644 index a6971694c1..0000000000 --- a/test/js/node/test/parallel/worker-relative-path.test.js +++ /dev/null @@ -1,20 +0,0 @@ -//#FILE: test-worker-relative-path.js -//#SHA1: 2d69899e96eee7500da11857679bf9f2ec18c259 -//----------------- -"use strict"; -const path = require("path"); -const { Worker, isMainThread, parentPort } = require("worker_threads"); - -if (isMainThread) { - test("Worker can be started with a relative path", done => { - const w = new Worker(`./${path.relative(".", __filename)}`); - w.on("message", message => { - expect(message).toBe("Hello, world!"); - done(); - }); - }); -} else { - parentPort.postMessage("Hello, world!"); -} - -//<#END_FILE: test-worker-relative-path.js diff --git a/test/js/node/test/parallel/worker-sharedarraybuffer-from-worker-thread.test.js b/test/js/node/test/parallel/worker-sharedarraybuffer-from-worker-thread.test.js deleted file mode 100644 index 0a4c29b25e..0000000000 --- a/test/js/node/test/parallel/worker-sharedarraybuffer-from-worker-thread.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-worker-sharedarraybuffer-from-worker-thread.js -//#SHA1: a33c9c03494dc79f7dd926382125f034d8ae4bbc -//----------------- -// Flags: --debug-arraybuffer-allocations -"use strict"; - -// Regression test for https://github.com/nodejs/node/issues/28777 -// Make sure that SharedArrayBuffers and transferred ArrayBuffers created in -// Worker threads are accessible after the creating thread ended. - -const { Worker } = require("worker_threads"); - -["ArrayBuffer", "SharedArrayBuffer"].forEach(ctor => { - test(`${ctor} from worker thread`, async () => { - const w = new Worker( - ` - const { parentPort } = require('worker_threads'); - const arrayBuffer = new ${ctor}(4); - parentPort.postMessage( - arrayBuffer, - '${ctor}' === 'SharedArrayBuffer' ? [] : [arrayBuffer]); - `, - { eval: true }, - ); - - let arrayBuffer; - await new Promise(resolve => { - w.once("message", message => { - arrayBuffer = message; - resolve(); - }); - }); - - await new Promise(resolve => { - w.once("exit", () => { - expect(arrayBuffer.constructor.name).toBe(ctor); - const uint8array = new Uint8Array(arrayBuffer); - uint8array[0] = 42; - expect(uint8array).toEqual(new Uint8Array([42, 0, 0, 0])); - resolve(); - }); - }); - }); -}); - -//<#END_FILE: test-worker-sharedarraybuffer-from-worker-thread.js diff --git a/test/js/node/test/parallel/worker-terminate-http2-respond-with-file.test.js b/test/js/node/test/parallel/worker-terminate-http2-respond-with-file.test.js deleted file mode 100644 index c317604d79..0000000000 --- a/test/js/node/test/parallel/worker-terminate-http2-respond-with-file.test.js +++ /dev/null @@ -1,51 +0,0 @@ -//#FILE: test-worker-terminate-http2-respond-with-file.js -//#SHA1: e8ce958ee3283a8ec8c83acc27cc3a02824ebfb7 -//----------------- -"use strict"; - -const http2 = require("http2"); -const makeDuplexPair = require("../common/duplexpair"); -const { Worker, isMainThread } = require("worker_threads"); - -// This is a variant of test-http2-generic-streams-sendfile for checking -// that Workers can be terminated during a .respondWithFile() operation. - -if (isMainThread) { - test("Worker can be terminated during respondWithFile operation", () => { - const worker = new Worker(__filename); - expect(worker).toBeDefined(); - }); -} else { - test("HTTP/2 server responds with file", done => { - const server = http2.createServer(); - server.on("stream", (stream, headers) => { - stream.respondWithFile(process.execPath); // Use a large-ish file. - }); - - const { clientSide, serverSide } = makeDuplexPair(); - server.emit("connection", serverSide); - - const client = http2.connect("http://localhost:80", { - createConnection: () => clientSide, - }); - - const req = client.request(); - - req.on("response", headers => { - expect(headers[":status"]).toBe(200); - }); - - req.on("data", () => { - process.exit(); - done(); - }); - - req.on("end", () => { - done.fail("Request should not end"); - }); - - req.end(); - }); -} - -//<#END_FILE: test-worker-terminate-http2-respond-with-file.js diff --git a/test/js/node/test/parallel/worker-unref-from-message-during-exit.test.js b/test/js/node/test/parallel/worker-unref-from-message-during-exit.test.js deleted file mode 100644 index 338155b933..0000000000 --- a/test/js/node/test/parallel/worker-unref-from-message-during-exit.test.js +++ /dev/null @@ -1,31 +0,0 @@ -//#FILE: test-worker-unref-from-message-during-exit.js -//#SHA1: 8555ad4d197dfc269beffcf6b2a3940df8a41659 -//----------------- -"use strict"; -const { Worker } = require("worker_threads"); - -// This used to crash because the `.unref()` was unexpected while the Worker -// was exiting. - -test("Worker unref from message during exit", async () => { - const w = new Worker( - ` -require('worker_threads').parentPort.postMessage({}); -`, - { eval: true }, - ); - - const messagePromise = new Promise(resolve => { - w.on("message", () => { - w.unref(); - resolve(); - }); - }); - - // Wait a bit so that the 'message' event is emitted while the Worker exits. - await Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 100); - - await expect(messagePromise).resolves.toBeUndefined(); -}); - -//<#END_FILE: test-worker-unref-from-message-during-exit.js diff --git a/test/js/node/test/parallel/worker-workerdata-sharedarraybuffer.test.js b/test/js/node/test/parallel/worker-workerdata-sharedarraybuffer.test.js deleted file mode 100644 index 4ff64a3328..0000000000 --- a/test/js/node/test/parallel/worker-workerdata-sharedarraybuffer.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-worker-workerdata-sharedarraybuffer.js -//#SHA1: 2737167494699dbfe6986e4b879ecdeafd31a8a8 -//----------------- -// Flags: --expose-gc -"use strict"; - -const { Worker } = require("worker_threads"); - -test("SharedArrayBuffer can be passed via workerData and modified", async () => { - const sharedArrayBuffer = new SharedArrayBuffer(12); - const local = Buffer.from(sharedArrayBuffer); - - const w = new Worker( - ` - const { parentPort, workerData } = require('worker_threads'); - const local = Buffer.from(workerData.sharedArrayBuffer); - - parentPort.on('message', () => { - local.write('world!', 6); - parentPort.postMessage('written!'); - }); - `, - { - eval: true, - workerData: { sharedArrayBuffer }, - }, - ); - - const messagePromise = new Promise(resolve => { - w.on("message", resolve); - }); - - w.postMessage({}); - // This would be a race condition if the memory regions were overlapping - local.write("Hello "); - - await messagePromise; - - expect(local.toString()).toBe("Hello world!"); - - global.gc(); - await w.terminate(); -}); - -//<#END_FILE: test-worker-workerdata-sharedarraybuffer.js diff --git a/test/js/node/test/parallel/worker.test.js b/test/js/node/test/parallel/worker.test.js deleted file mode 100644 index 7328a65dec..0000000000 --- a/test/js/node/test/parallel/worker.test.js +++ /dev/null @@ -1,26 +0,0 @@ -//#FILE: test-worker.js -//#SHA1: 830c4e2ce132228fe7d49fd760271deed934db23 -//----------------- -"use strict"; - -const { Worker, isMainThread, parentPort } = require("worker_threads"); - -const kTestString = "Hello, world!"; - -if (isMainThread) { - test("Worker thread communication", done => { - const w = new Worker(__filename); - w.on("message", message => { - expect(message).toBe(kTestString); - done(); - }); - }); -} else { - setImmediate(() => { - process.nextTick(() => { - parentPort.postMessage(kTestString); - }); - }); -} - -//<#END_FILE: test-worker.js diff --git a/test/js/node/test/parallel/wrap-js-stream-read-stop.test.js b/test/js/node/test/parallel/wrap-js-stream-read-stop.test.js deleted file mode 100644 index 6efe3f5df1..0000000000 --- a/test/js/node/test/parallel/wrap-js-stream-read-stop.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-wrap-js-stream-read-stop.js -//#SHA1: 53e905da53ef7a130579672b04ddd6e65b7cb8d5 -//----------------- -"use strict"; - -const Stream = require("stream"); - -class FakeStream extends Stream { - constructor() { - super(); - this._paused = false; - } - - pause() { - this._paused = true; - } - - resume() { - this._paused = false; - } - - isPaused() { - return this._paused; - } -} - -class WrapStream { - constructor(stream) { - this.stream = stream; - this.stream.resume(); - } - - readStop() { - this.stream.pause(); - return 0; - } -} - -describe("WrapStream", () => { - let fakeStreamObj; - let wrappedStream; - - beforeEach(() => { - fakeStreamObj = new FakeStream(); - wrappedStream = new WrapStream(fakeStreamObj); - }); - - test("Resume by wrapped stream upon construction", () => { - expect(fakeStreamObj.isPaused()).toBe(false); - }); - - test("Pause and resume fakeStreamObj", () => { - fakeStreamObj.pause(); - expect(fakeStreamObj.isPaused()).toBe(true); - - fakeStreamObj.resume(); - expect(fakeStreamObj.isPaused()).toBe(false); - }); - - test("readStop method", () => { - expect(wrappedStream.readStop()).toBe(0); - expect(fakeStreamObj.isPaused()).toBe(true); - }); -}); - -//<#END_FILE: test-wrap-js-stream-read-stop.js diff --git a/test/js/node/test/parallel/zlib-brotli-16gb.test.js b/test/js/node/test/parallel/zlib-brotli-16gb.test.js deleted file mode 100644 index edefbaafe9..0000000000 --- a/test/js/node/test/parallel/zlib-brotli-16gb.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-zlib-brotli-16GB.js -//#SHA1: 0c5e79f3713fdd0597dda7e531484a0ef3170578 -//----------------- -"use strict"; - -const { createBrotliDecompress } = require("node:zlib"); -const { getDefaultHighWaterMark } = require("stream"); - -// This tiny HEX string is a 16GB file. -// This test verifies that the stream actually stops. -const content = - "cfffff7ff82700e2b14020f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c32200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdf" + - "fe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8de" + - "fff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074e" + - "ffff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500ba" + - "f7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200d" + - "dfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180" + - "eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b0004" + - "0f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800" + - "a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c0" + - "0d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c16" + - "00e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0" + - "b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f011087" + - "0500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c" + - "30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4" + - "610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e" + - "2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300" + - "715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe098" + - "0382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04" + - "401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f0" + - "2200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f" + - "0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19" + - "f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff0" + - "4f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff" + - "82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3f" + - "fc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1" + - "ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bffcfffff7ff82700e2b00040f7fe9ffffffff04f00c4610180eefd3fffffffe19f0088c30200ddfb7ffeffffc33f0110870500baf7fffcffff877f02200e0b0074effff9ffff0fff04401c1600e8defff3ffff1ffe0980382c00d0bdffe7ffff3ffc1300715800a07bff3f"; - -test("the test", async () => { - const buf = Buffer.from(content, "hex"); - - const decoder = createBrotliDecompress(); - decoder.end(buf); - - const { promise, resolve, reject } = Promise.withResolvers(); - - setTimeout(() => { - try { - expect(decoder._readableState.buffer.length).toBe(getDefaultHighWaterMark() / (16 * 1024)); - resolve(); - } catch (e) { - reject(e); - } - }, 500); - await promise; -}); - -//#FILE: test-zlib-brotli-16GB.js -//----------------- diff --git a/test/js/node/test/parallel/zlib-brotli-flush.test.js b/test/js/node/test/parallel/zlib-brotli-flush.test.js deleted file mode 100644 index 77723ad78f..0000000000 --- a/test/js/node/test/parallel/zlib-brotli-flush.test.js +++ /dev/null @@ -1,33 +0,0 @@ -//#FILE: test-zlib-brotli-flush.js -//#SHA1: b0a953be98db6dd674668bfd6cffa3e283144ad1 -//----------------- -"use strict"; -const zlib = require("zlib"); -const fs = require("fs"); -const path = require("path"); - -const fixturesPath = path.join(__dirname, "..", "fixtures"); -const file = fs.readFileSync(path.join(fixturesPath, "person.jpg")); -const chunkSize = 16; - -test("BrotliCompress flush should produce expected output", done => { - const deflater = new zlib.BrotliCompress(); - const chunk = file.slice(0, chunkSize); - const expectedFull = Buffer.from("iweA/9j/4AAQSkZJRgABAQEASA==", "base64"); - let actualFull; - - deflater.write(chunk, () => { - deflater.flush(() => { - const bufs = []; - let buf; - while ((buf = deflater.read()) !== null) { - bufs.push(buf); - } - actualFull = Buffer.concat(bufs); - expect(actualFull).toEqual(expectedFull); - done(); - }); - }); -}); - -//<#END_FILE: test-zlib-brotli-flush.js diff --git a/test/js/node/test/parallel/zlib-brotli-from-string.test.js b/test/js/node/test/parallel/zlib-brotli-from-string.test.js deleted file mode 100644 index fa861d967b..0000000000 --- a/test/js/node/test/parallel/zlib-brotli-from-string.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-zlib-brotli-from-string.js -//#SHA1: bb4656c195e75f9d49e2bad9e7b1130f571fa68b -//----------------- -"use strict"; -// Test compressing and uncompressing a string with brotli - -const zlib = require("zlib"); - -const inputString = - "ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli" + - "t. Morbi faucibus, purus at gravida dictum, libero arcu " + - "convallis lacus, in commodo libero metus eu nisi. Nullam" + - " commodo, neque nec porta placerat, nisi est fermentum a" + - "ugue, vitae gravida tellus sapien sit amet tellus. Aenea" + - "n non diam orci. Proin quis elit turpis. Suspendisse non" + - " diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu" + - "m arcu mi, sodales non suscipit id, ultrices ut massa. S" + - "ed ac sem sit amet arcu malesuada fermentum. Nunc sed. "; -const compressedString = - "G/gBQBwHdky2aHV5KK9Snf05//1pPdmNw/7232fnIm1IB" + - "K1AA8RsN8OB8Nb7Lpgk3UWWUlzQXZyHQeBBbXMTQXC1j7" + - "wg3LJs9LqOGHRH2bj/a2iCTLLx8hBOyTqgoVuD1e+Qqdn" + - "f1rkUNyrWq6LtOhWgxP3QUwdhKGdZm3rJWaDDBV7+pDk1" + - "MIkrmjp4ma2xVi5MsgJScA3tP1I7mXeby6MELozrwoBQD" + - "mVTnEAicZNj4lkGqntJe2qSnGyeMmcFgraK94vCg/4iLu" + - "Tw5RhKhnVY++dZ6niUBmRqIutsjf5TzwF5iAg8a9UkjF5" + - "2eZ0tB2vo6v8SqVfNMkBmmhxr0NT9LkYF69aEjlYzj7IE" + - "KmEUQf1HBogRYhFIt4ymRNEgHAIzOyNEsQM="; - -test("brotli compress and decompress string", async () => { - const compressCallback = jest.fn(); - const decompressCallback = jest.fn(); - - await new Promise(resolve => { - zlib.brotliCompress(inputString, (err, buffer) => { - compressCallback(); - expect(inputString.length).toBeGreaterThan(buffer.length); - - zlib.brotliDecompress(buffer, (err, buffer) => { - decompressCallback(); - expect(buffer.toString()).toBe(inputString); - resolve(); - }); - }); - }); - - expect(compressCallback).toHaveBeenCalledTimes(1); - expect(decompressCallback).toHaveBeenCalledTimes(1); -}); - -test("brotli decompress base64 string", async () => { - const decompressCallback = jest.fn(); - - const buffer = Buffer.from(compressedString, "base64"); - await new Promise(resolve => { - zlib.brotliDecompress(buffer, (err, buffer) => { - decompressCallback(); - expect(buffer.toString()).toBe(inputString); - resolve(); - }); - }); - - expect(decompressCallback).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-zlib-brotli-from-string.js diff --git a/test/js/node/test/parallel/zlib-brotli-kmaxlength-rangeerror.test.js b/test/js/node/test/parallel/zlib-brotli-kmaxlength-rangeerror.test.js deleted file mode 100644 index c3cbb9d19b..0000000000 --- a/test/js/node/test/parallel/zlib-brotli-kmaxlength-rangeerror.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-zlib-brotli-kmaxlength-rangeerror.js -//#SHA1: f0d3ad25e8a844b31b7e14ab68a84182fd5f52b7 -//----------------- -"use strict"; - -const util = require("util"); - -// Change kMaxLength for zlib to trigger the error without having to allocate large Buffers. -const buffer = require("buffer"); -const oldkMaxLength = buffer.kMaxLength; -buffer.kMaxLength = 64; -const zlib = require("zlib"); -buffer.kMaxLength = oldkMaxLength; - -// Create a large input buffer -const encoded = Buffer.from("G38A+CXCIrFAIAM=", "base64"); - -test("brotliDecompress throws RangeError for large output (async)", async () => { - await expect(async () => util.promisify(zlib.brotliDecompress)(encoded)).toThrowWithCodeAsync( - RangeError, - "ERR_BUFFER_TOO_LARGE", - ); -}, 1000); - -test("brotliDecompressSync throws RangeError for large output", () => { - expect(() => zlib.brotliDecompressSync(encoded)).toThrowWithCode(RangeError, "ERR_BUFFER_TOO_LARGE"); -}); - -//<#END_FILE: test-zlib-brotli-kmaxlength-rangeerror.js diff --git a/test/js/node/test/parallel/zlib-brotli.test.js b/test/js/node/test/parallel/zlib-brotli.test.js deleted file mode 100644 index d61df3eae3..0000000000 --- a/test/js/node/test/parallel/zlib-brotli.test.js +++ /dev/null @@ -1,101 +0,0 @@ -//#FILE: test-zlib-brotli.js -//#SHA1: 53d893f351dd67279a8561e244183e38864c0c92 -//----------------- -"use strict"; - -const fixtures = require("../common/fixtures"); -const zlib = require("zlib"); - -// Test some brotli-specific properties of the brotli streams that can not -// be easily covered through expanding zlib-only tests. - -const sampleBuffer = fixtures.readSync("/pss-vectors.json"); - -test("Quality parameter at stream creation", () => { - const sizes = []; - for (let quality = zlib.constants.BROTLI_MIN_QUALITY; quality <= zlib.constants.BROTLI_MAX_QUALITY; quality++) { - const encoded = zlib.brotliCompressSync(sampleBuffer, { - params: { - [zlib.constants.BROTLI_PARAM_QUALITY]: quality, - }, - }); - sizes.push(encoded.length); - } - - // Increasing quality should roughly correspond to decreasing compressed size: - for (let i = 0; i < sizes.length - 1; i++) { - expect(sizes[i + 1]).toBeLessThanOrEqual(sizes[i] * 1.05); // 5 % margin of error. - } - expect(sizes[0]).toBeGreaterThan(sizes[sizes.length - 1]); -}); - -test("Setting out-of-bounds option values or keys", () => { - expect(() => { - zlib.createBrotliCompress({ - params: { - 10000: 0, - }, - }); - }).toThrow( - expect.objectContaining({ - code: "ERR_BROTLI_INVALID_PARAM", - name: "RangeError", - message: expect.any(String), - }), - ); - - // Test that accidentally using duplicate keys fails. - expect(() => { - zlib.createBrotliCompress({ - params: { - 0: 0, - "00": 0, - }, - }); - }).toThrow( - expect.objectContaining({ - code: "ERR_BROTLI_INVALID_PARAM", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => { - zlib.createBrotliCompress({ - params: { - // This is a boolean flag - [zlib.constants.BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING]: 42, - }, - }); - }).toThrow( - expect.objectContaining({ - code: "ERR_ZLIB_INITIALIZATION_FAILED", - name: "Error", - message: expect.any(String), - }), - ); -}); - -test("Options.flush range", () => { - expect(() => { - zlib.brotliCompressSync("", { flush: zlib.constants.Z_FINISH }); - }).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => { - zlib.brotliCompressSync("", { finishFlush: zlib.constants.Z_FINISH }); - }).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-zlib-brotli.js diff --git a/test/js/node/test/parallel/zlib-close-after-error.test.js b/test/js/node/test/parallel/zlib-close-after-error.test.js deleted file mode 100644 index f336f5a950..0000000000 --- a/test/js/node/test/parallel/zlib-close-after-error.test.js +++ /dev/null @@ -1,22 +0,0 @@ -//#FILE: test-zlib-close-after-error.js -//#SHA1: 1f561376d8af1a6b21f9f9abf6813a20cde33be6 -//----------------- -"use strict"; -// https://github.com/nodejs/node/issues/6034 - -const zlib = require("zlib"); - -test("zlib close after error", done => { - const decompress = zlib.createGunzip(15); - - decompress.on("error", err => { - expect(decompress._closed).toBe(true); - decompress.close(); - done(); - }); - - expect(decompress._closed).toBe(false); - decompress.write("something invalid"); -}); - -//<#END_FILE: test-zlib-close-after-error.js diff --git a/test/js/node/test/parallel/zlib-close-after-write.test.js b/test/js/node/test/parallel/zlib-close-after-write.test.js deleted file mode 100644 index 05a029b37f..0000000000 --- a/test/js/node/test/parallel/zlib-close-after-write.test.js +++ /dev/null @@ -1,48 +0,0 @@ -//#FILE: test-zlib-close-after-write.js -//#SHA1: 7fad593914e2a23d73598e4366e685b9aa91cc24 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const zlib = require("zlib"); - -test("zlib close after write", async () => { - const gzipPromise = new Promise((resolve, reject) => { - zlib.gzip("hello", (err, out) => { - if (err) reject(err); - else resolve(out); - }); - }); - - const out = await gzipPromise; - - const unzip = zlib.createGunzip(); - unzip.write(out); - - const closePromise = new Promise(resolve => { - unzip.close(resolve); - }); - - await closePromise; -}); - -//<#END_FILE: test-zlib-close-after-write.js diff --git a/test/js/node/test/parallel/zlib-close-in-ondata.test.js b/test/js/node/test/parallel/zlib-close-in-ondata.test.js deleted file mode 100644 index e4449d57d4..0000000000 --- a/test/js/node/test/parallel/zlib-close-in-ondata.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-zlib-close-in-ondata.js -//#SHA1: 8218c0461dd0733882aaf37688e3b32b164e3535 -//----------------- -"use strict"; - -const zlib = require("zlib"); - -test("zlib stream closes in ondata event", done => { - const ts = zlib.createGzip(); - const buf = Buffer.alloc(1024 * 1024 * 20); - - ts.on( - "data", - jest.fn(() => { - ts.close(); - done(); - }), - ); - - ts.end(buf); -}); - -//<#END_FILE: test-zlib-close-in-ondata.js diff --git a/test/js/node/test/parallel/zlib-const.test.js b/test/js/node/test/parallel/zlib-const.test.js deleted file mode 100644 index a81535e446..0000000000 --- a/test/js/node/test/parallel/zlib-const.test.js +++ /dev/null @@ -1,23 +0,0 @@ -//#FILE: test-zlib-const.js -//#SHA1: d85ad9e395d5781dbe5bf05e5514104bd9503be8 -//----------------- -const zlib = require("zlib"); - -test("zlib constants and codes are immutable", () => { - // Test Z_OK constant - expect(zlib.constants.Z_OK).toBe(0); - zlib.constants.Z_OK = 1; - expect(zlib.constants.Z_OK).toBe(0); - - // Test Z_OK code - expect(zlib.codes.Z_OK).toBe(0); - zlib.codes.Z_OK = 1; - expect(zlib.codes.Z_OK).toBe(0); - zlib.codes = { Z_OK: 1 }; - expect(zlib.codes.Z_OK).toBe(0); - - // Test if zlib.codes is frozen - expect(Object.isFrozen(zlib.codes)).toBe(true); -}); - -//<#END_FILE: test-zlib-const.js diff --git a/test/js/node/test/parallel/zlib-convenience-methods.test.js b/test/js/node/test/parallel/zlib-convenience-methods.test.js deleted file mode 100644 index f1c853be23..0000000000 --- a/test/js/node/test/parallel/zlib-convenience-methods.test.js +++ /dev/null @@ -1,96 +0,0 @@ -//#FILE: test-zlib-convenience-methods.js -//#SHA1: e215a52650eaa95999dbb7d77f5b03376cdc673b -//----------------- -"use strict"; - -const zlib = require("zlib"); -const util = require("util"); -const { getBufferSources } = require("../common"); - -// Must be a multiple of 4 characters in total to test all ArrayBufferView -// types. -const expectStr = "blah".repeat(8); -const expectBuf = Buffer.from(expectStr); - -const opts = { - level: 9, - chunkSize: 1024, -}; - -const optsInfo = { - info: true, -}; - -const methodPairs = [ - ["gzip", "gunzip", "Gzip", "Gunzip"], - ["gzip", "unzip", "Gzip", "Unzip"], - ["deflate", "inflate", "Deflate", "Inflate"], - ["deflateRaw", "inflateRaw", "DeflateRaw", "InflateRaw"], - ["brotliCompress", "brotliDecompress", "BrotliCompress", "BrotliDecompress"], -]; - -const testCases = [ - ["string", expectStr], - ["Buffer", expectBuf], - ...getBufferSources(expectBuf).map(obj => [obj[Symbol.toStringTag], obj]), -]; - -describe("zlib convenience methods", () => { - describe.each(testCases)("%s input", (type, expected) => { - for (const [compress, decompress, CompressClass, DecompressClass] of methodPairs) { - describe(`${compress}/${decompress}`, async () => { - test("Async with options", async () => { - const c = await util.promisify(zlib[compress])(expected, opts); - const d = await util.promisify(zlib[decompress])(c, opts); - expect(d.toString()).toEqual(expectStr); - }); - - test("Async without options", async () => { - const c = await util.promisify(zlib[compress])(expected); - const d = await util.promisify(zlib[decompress])(c); - expect(d.toString()).toEqual(expectStr); - }); - - test("Async with info option", async () => { - const c = await util.promisify(zlib[compress])(expected, optsInfo); - expect(c.engine).toBeInstanceOf(zlib[CompressClass]); - const d = await util.promisify(zlib[decompress])(c.buffer, optsInfo); - expect(d.engine).toBeInstanceOf(zlib[DecompressClass]); - expect(d.buffer.toString()).toEqual(expectStr); - }); - - test("Sync with options", () => { - const c = zlib[compress + "Sync"](expected, opts); - const d = zlib[decompress + "Sync"](c, opts); - expect(d.toString()).toEqual(expectStr); - }); - - test("Sync without options", async () => { - const c = zlib[compress + "Sync"](expected); - const d = zlib[decompress + "Sync"](c); - expect(d.toString()).toEqual(expectStr); - }); - - test("Sync with info option", () => { - const c = zlib[compress + "Sync"](expected, optsInfo); - expect(c.engine).toBeInstanceOf(zlib[CompressClass]); - const d = zlib[decompress + "Sync"](c.buffer, optsInfo); - expect(d.engine).toBeInstanceOf(zlib[DecompressClass]); - expect(d.buffer.toString()).toEqual(expectStr); - }); - }); - } - }); - - test("throws error when callback is not provided", () => { - expect(() => zlib.gzip("abc")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.stringContaining('The "callback" argument must be of type function'), - }), - ); - }); -}); - -//<#END_FILE: test-zlib-convenience-methods.js diff --git a/test/js/node/test/parallel/zlib-crc32.test.js b/test/js/node/test/parallel/zlib-crc32.test.js deleted file mode 100644 index 5c1e51b63a..0000000000 --- a/test/js/node/test/parallel/zlib-crc32.test.js +++ /dev/null @@ -1,249 +0,0 @@ -//#FILE: test-zlib-crc32.js -//#SHA1: a46ac98c5c568eec892b62f9da1effc368081b07 -//----------------- -"use strict"; - -const zlib = require("zlib"); -const { Buffer } = require("buffer"); - -// The following test data comes from -// https://github.com/zlib-ng/zlib-ng/blob/5401b24/test/test_crc32.cc -// test_crc32.cc -- crc32 unit test -// Copyright (C) 2019-2021 IBM Corporation -// Authors: Rogerio Alves -// Matheus Castanho -// For conditions of distribution and use, see copyright notice in zlib.h -// -const tests = [ - [0x0, 0x0, 0, 0x0], - [0xffffffff, 0x0, 0, 0x0], - [0x0, 0x0, 255, 0x0] /* BZ 174799. */, - [0x0, 0x0, 256, 0x0], - [0x0, 0x0, 257, 0x0], - [0x0, 0x0, 32767, 0x0], - [0x0, 0x0, 32768, 0x0], - [0x0, 0x0, 32769, 0x0], - [0x0, "", 0, 0x0], - [0xffffffff, "", 0, 0xffffffff], - [0x0, "abacus", 6, 0xc3d7115b], - [0x0, "backlog", 7, 0x269205], - [0x0, "campfire", 8, 0x22a515f8], - [0x0, "delta", 5, 0x9643fed9], - [0x0, "executable", 10, 0xd68eda01], - [0x0, "file", 4, 0x8c9f3610], - [0x0, "greatest", 8, 0xc1abd6cd], - [0x0, "hello", 5, 0x3610a686], - [0x0, "inverter", 8, 0xc9e962c9], - [0x0, "jigsaw", 6, 0xce4e3f69], - [0x0, "karate", 6, 0x890be0e2], - [0x0, "landscape", 9, 0xc4e0330b], - [0x0, "machine", 7, 0x1505df84], - [0x0, "nanometer", 9, 0xd4e19f39], - [0x0, "oblivion", 8, 0xdae9de77], - [0x0, "panama", 6, 0x66b8979c], - [0x0, "quest", 5, 0x4317f817], - [0x0, "resource", 8, 0xbc91f416], - [0x0, "secret", 6, 0x5ca2e8e5], - [0x0, "test", 4, 0xd87f7e0c], - [0x0, "ultimate", 8, 0x3fc79b0b], - [0x0, "vector", 6, 0x1b6e485b], - [0x0, "walrus", 6, 0xbe769b97], - [0x0, "xeno", 4, 0xe7a06444], - [0x0, "yelling", 7, 0xfe3944e5], - [0x0, "zlib", 4, 0x73887d3a], - [0x0, "4BJD7PocN1VqX0jXVpWB", 20, 0xd487a5a1], - [0x0, "F1rPWI7XvDs6nAIRx41l", 20, 0x61a0132e], - [0x0, "ldhKlsVkPFOveXgkGtC2", 20, 0xdf02f76], - [0x0, "5KKnGOOrs8BvJ35iKTOS", 20, 0x579b2b0a], - [0x0, "0l1tw7GOcem06Ddu7yn4", 20, 0xf7d16e2d], - [0x0, "MCr47CjPIn9R1IvE1Tm5", 20, 0x731788f5], - [0x0, "UcixbzPKTIv0SvILHVdO", 20, 0x7112bb11], - [0x0, "dGnAyAhRQDsWw0ESou24", 20, 0xf32a0dac], - [0x0, "di0nvmY9UYMYDh0r45XT", 20, 0x625437bb], - [0x0, "2XKDwHfAhFsV0RhbqtvH", 20, 0x896930f9], - [0x0, "ZhrANFIiIvRnqClIVyeD", 20, 0x8579a37], - [0x0, "v7Q9ehzioTOVeDIZioT1", 20, 0x632aa8e0], - [0x0, "Yod5hEeKcYqyhfXbhxj2", 20, 0xc829af29], - [0x0, "GehSWY2ay4uUKhehXYb0", 20, 0x1b08b7e8], - [0x0, "kwytJmq6UqpflV8Y8GoE", 20, 0x4e33b192], - [0x0, "70684206568419061514", 20, 0x59a179f0], - [0x0, "42015093765128581010", 20, 0xcd1013d7], - [0x0, "88214814356148806939", 20, 0xab927546], - [0x0, "43472694284527343838", 20, 0x11f3b20c], - [0x0, "49769333513942933689", 20, 0xd562d4ca], - [0x0, "54979784887993251199", 20, 0x233395f7], - [0x0, "58360544869206793220", 20, 0x2d167fd5], - [0x0, "27347953487840714234", 20, 0x8b5108ba], - [0x0, "07650690295365319082", 20, 0xc46b3cd8], - [0x0, "42655507906821911703", 20, 0xc10b2662], - [0x0, "29977409200786225655", 20, 0xc9a0f9d2], - [0x0, "85181542907229116674", 20, 0x9341357b], - [0x0, "87963594337989416799", 20, 0xf0424937], - [0x0, "21395988329504168551", 20, 0xd7c4c31f], - [0x0, "51991013580943379423", 20, 0xf11edcc4], - [0x0, "*]+@!);({_$;}[_},?{?;(_?,=-][@", 30, 0x40795df4], - [0x0, "_@:_).&(#.[:[{[:)$++-($_;@[)}+", 30, 0xdd61a631], - [0x0, "&[!,[$_==}+.]@!;*(+},[;:)$;)-@", 30, 0xca907a99], - [0x0, "]{.[.+?+[[=;[?}_#&;[=)__$$:+=_", 30, 0xf652deac], - [0x0, "-%.)=/[@].:.(:,()$;=%@-$?]{%+%", 30, 0xaf39a5a9], - [0x0, "+]#$(@&.=:,*];/.!]%/{:){:@(;)$", 30, 0x6bebb4cf], - // eslint-disable-next-line no-template-curly-in-string - [0x0, ")-._.:?[&:.=+}(*$/=!.${;(=$@!}", 30, 0x76430bac], - [0x0, ":(_*&%/[[}+,?#$&*+#[([*-/#;%(]", 30, 0x6c80c388], - [0x0, "{[#-;:$/{)(+[}#]/{&!%(@)%:@-$:", 30, 0xd54d977d], - [0x0, "_{$*,}(&,@.)):=!/%(&(,,-?$}}}!", 30, 0xe3966ad5], - [ - 0x0, - "e$98KNzqaV)Y:2X?]77].{gKRD4G5{mHZk,Z)SpU%L3FSgv!Wb8MLAFdi{+fp)c,@8m6v)yXg@]HBDFk?.4&}g5_udE*JHCiH=aL", - 100, - 0xe7c71db9, - ], - [ - 0x0, - "r*Fd}ef+5RJQ;+W=4jTR9)R*p!B;]Ed7tkrLi;88U7g@3v!5pk2X6D)vt,.@N8c]@yyEcKi[vwUu@.Ppm@C6%Mv*3Nw}Y,58_aH)", - 100, - 0xeaa52777, - ], - [ - 0x0, - "h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&", - 100, - 0xcd472048, - ], - [0x7a30360d, "abacus", 6, 0xf8655a84], - [0x6fd767ee, "backlog", 7, 0x1ed834b1], - [0xefeb7589, "campfire", 8, 0x686cfca], - [0x61cf7e6b, "delta", 5, 0x1554e4b1], - [0xdc712e2, "executable", 10, 0x761b4254], - [0xad23c7fd, "file", 4, 0x7abdd09b], - [0x85cb2317, "greatest", 8, 0x4ba91c6b], - [0x9eed31b0, "inverter", 8, 0xd5e78ba5], - [0xb94f34ca, "jigsaw", 6, 0x23649109], - [0xab058a2, "karate", 6, 0xc5591f41], - [0x5bff2b7a, "landscape", 9, 0xf10eb644], - [0x605c9a5f, "machine", 7, 0xbaa0a636], - [0x51bdeea5, "nanometer", 9, 0x6af89afb], - [0x85c21c79, "oblivion", 8, 0xecae222b], - [0x97216f56, "panama", 6, 0x47dffac4], - [0x18444af2, "quest", 5, 0x70c2fe36], - [0xbe6ce359, "resource", 8, 0x1471d925], - [0x843071f1, "secret", 6, 0x50c9a0db], - [0xf2480c60, "ultimate", 8, 0xf973daf8], - [0x2d2feb3d, "vector", 6, 0x344ac03d], - [0x7490310a, "walrus", 6, 0x6d1408ef], - [0x97d247d4, "xeno", 4, 0xe62670b5], - [0x93cf7599, "yelling", 7, 0x1b36da38], - [0x73c84278, "zlib", 4, 0x6432d127], - [0x228a87d1, "4BJD7PocN1VqX0jXVpWB", 20, 0x997107d0], - [0xa7a048d0, "F1rPWI7XvDs6nAIRx41l", 20, 0xdc567274], - [0x1f0ded40, "ldhKlsVkPFOveXgkGtC2", 20, 0xdcc63870], - [0xa804a62f, "5KKnGOOrs8BvJ35iKTOS", 20, 0x6926cffd], - [0x508fae6a, "0l1tw7GOcem06Ddu7yn4", 20, 0xb52b38bc], - [0xe5adaf4f, "MCr47CjPIn9R1IvE1Tm5", 20, 0xf83b8178], - [0x67136a40, "UcixbzPKTIv0SvILHVdO", 20, 0xc5213070], - [0xb00c4a10, "dGnAyAhRQDsWw0ESou24", 20, 0xbc7648b0], - [0x2e0c84b5, "di0nvmY9UYMYDh0r45XT", 20, 0xd8123a72], - [0x81238d44, "2XKDwHfAhFsV0RhbqtvH", 20, 0xd5ac5620], - [0xf853aa92, "ZhrANFIiIvRnqClIVyeD", 20, 0xceae099d], - [0x5a692325, "v7Q9ehzioTOVeDIZioT1", 20, 0xb07d2b24], - [0x3275b9f, "Yod5hEeKcYqyhfXbhxj2", 20, 0x24ce91df], - [0x38371feb, "GehSWY2ay4uUKhehXYb0", 20, 0x707b3b30], - [0xafc8bf62, "kwytJmq6UqpflV8Y8GoE", 20, 0x16abc6a9], - [0x9b07db73, "70684206568419061514", 20, 0xae1fb7b7], - [0xe75b214, "42015093765128581010", 20, 0xd4eecd2d], - [0x72d0fe6f, "88214814356148806939", 20, 0x4660ec7], - [0xf857a4b1, "43472694284527343838", 20, 0xfd8afdf7], - [0x54b8e14, "49769333513942933689", 20, 0xc6d1b5f2], - [0xd6aa5616, "54979784887993251199", 20, 0x32476461], - [0x11e63098, "58360544869206793220", 20, 0xd917cf1a], - [0xbe92385, "27347953487840714234", 20, 0x4ad14a12], - [0x49511de0, "07650690295365319082", 20, 0xe37b5c6c], - [0x3db13bc1, "42655507906821911703", 20, 0x7cc497f1], - [0xbb899bea, "29977409200786225655", 20, 0x99781bb2], - [0xf6cd9436, "85181542907229116674", 20, 0x132256a1], - [0x9109e6c3, "87963594337989416799", 20, 0xbfdb2c83], - [0x75770fc, "21395988329504168551", 20, 0x8d9d1e81], - [0x69b1d19b, "51991013580943379423", 20, 0x7b6d4404], - [0xc6132975, "*]+@!);({_$;}[_},?{?;(_?,=-][@", 30, 0x8619f010], - [0xd58cb00c, "_@:_).&(#.[:[{[:)$++-($_;@[)}+", 30, 0x15746ac3], - [0xb63b8caa, "&[!,[$_==}+.]@!;*(+},[;:)$;)-@", 30, 0xaccf812f], - [0x8a45a2b8, "]{.[.+?+[[=;[?}_#&;[=)__$$:+=_", 30, 0x78af45de], - [0xcbe95b78, "-%.)=/[@].:.(:,()$;=%@-$?]{%+%", 30, 0x25b06b59], - [0x4ef8a54b, "+]#$(@&.=:,*];/.!]%/{:){:@(;)$", 30, 0x4ba0d08f], - // eslint-disable-next-line no-template-curly-in-string - [0x76ad267a, ")-._.:?[&:.=+}(*$/=!.${;(=$@!}", 30, 0xe26b6aac], - [0x569e613c, ":(_*&%/[[}+,?#$&*+#[([*-/#;%(]", 30, 0x7e2b0a66], - [0x36aa61da, "{[#-;:$/{)(+[}#]/{&!%(@)%:@-$:", 30, 0xb3430dc7], - [0xf67222df, "_{$*,}(&,@.)):=!/%(&(,,-?$}}}!", 30, 0x626c17a], - [ - 0x74b34fd3, - "e$98KNzqaV)Y:2X?]77].{gKRD4G5{mHZk,Z)SpU%L3FSgv!Wb8MLAFdi{+fp)c,@8m6v)yXg@]HBDFk?.4&}g5_udE*JHCiH=aL", - 100, - 0xccf98060, - ], - [ - 0x351fd770, - "r*Fd}ef+5RJQ;+W=4jTR9)R*p!B;]Ed7tkrLi;88U7g@3v!5pk2X6D)vt,.@N8c]@yyEcKi[vwUu@.Ppm@C6%Mv*3Nw}Y,58_aH)", - 100, - 0xd8b95312, - ], - [ - 0xc45aef77, - "h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&", - 100, - 0xbb1c9912, - ], - [ - 0xc45aef77, - "h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&" + - "h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&" + - "h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&" + - "h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&" + - "h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&" + - "h{bcmdC+a;t+Cf{6Y_dFq-{X4Yu&7uNfVDh?q&_u.UWJU],-GiH7ADzb7-V.Q%4=+v!$L9W+T=bP]$_:]Vyg}A.ygD.r;h-D]m%&", - 600, - 0x888afa5b, - ], -]; - -describe("zlib.crc32", () => { - test("crc32 test case", () => { - for (const [crc, data, len, expected] of tests) { - if (data === 0) { - return; - } - const buf = Buffer.from(data, "utf8"); - expect(buf.length).toBe(len); - expect(zlib.crc32(buf, crc)).toBe(expected); - expect(zlib.crc32(buf.toString(), crc)).toBe(expected); - if (crc === 0) { - expect(zlib.crc32(buf)).toBe(expected); - expect(zlib.crc32(buf.toString())).toBe(expected); - } - } - }); - - test("invalid input types", () => { - [undefined, null, true, 1, () => {}, {}].forEach(invalid => { - expect(() => zlib.crc32(invalid)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - }); - }); - - test("invalid crc types", () => { - [null, true, () => {}, {}].forEach(invalid => { - expect(() => zlib.crc32("test", invalid)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - }); - }); -}); - -//<#END_FILE: test-zlib-crc32.js diff --git a/test/js/node/test/parallel/zlib-create-raw.test.js b/test/js/node/test/parallel/zlib-create-raw.test.js deleted file mode 100644 index 4bd60d6d8e..0000000000 --- a/test/js/node/test/parallel/zlib-create-raw.test.js +++ /dev/null @@ -1,18 +0,0 @@ -//#FILE: test-zlib-create-raw.js -//#SHA1: 187539d5696ec6b7c567dfba0d1528c4b65d1e0a -//----------------- -"use strict"; - -const zlib = require("zlib"); - -test("zlib.createInflateRaw returns an instance of InflateRaw", () => { - const inflateRaw = zlib.createInflateRaw(); - expect(inflateRaw).toBeInstanceOf(zlib.InflateRaw); -}); - -test("zlib.createDeflateRaw returns an instance of DeflateRaw", () => { - const deflateRaw = zlib.createDeflateRaw(); - expect(deflateRaw).toBeInstanceOf(zlib.DeflateRaw); -}); - -//<#END_FILE: test-zlib-create-raw.js diff --git a/test/js/node/test/parallel/zlib-deflate-constructors.test.js b/test/js/node/test/parallel/zlib-deflate-constructors.test.js deleted file mode 100644 index d9a0411de2..0000000000 --- a/test/js/node/test/parallel/zlib-deflate-constructors.test.js +++ /dev/null @@ -1,281 +0,0 @@ -//#FILE: test-zlib-deflate-constructors.js -//#SHA1: fdacb219f8fcceeeb4800473b37fca16fcf4c241 -//----------------- -"use strict"; - -const zlib = require("zlib"); - -// Work with and without `new` keyword -test("Deflate constructor works with and without new keyword", () => { - expect(zlib.Deflate()).toBeInstanceOf(zlib.Deflate); - expect(new zlib.Deflate()).toBeInstanceOf(zlib.Deflate); -}); - -test("DeflateRaw constructor works with and without new keyword", () => { - expect(zlib.DeflateRaw()).toBeInstanceOf(zlib.DeflateRaw); - expect(new zlib.DeflateRaw()).toBeInstanceOf(zlib.DeflateRaw); -}); - -// Throws if `options.chunkSize` is invalid -test("Deflate constructor throws for invalid chunkSize", () => { - expect(() => new zlib.Deflate({ chunkSize: "test" })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ chunkSize: -Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ chunkSize: 0 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -// Confirm that maximum chunk size cannot be exceeded because it is `Infinity`. -test("Z_MAX_CHUNK is Infinity", () => { - expect(zlib.constants.Z_MAX_CHUNK).toBe(Infinity); -}); - -// Throws if `options.windowBits` is invalid -test("Deflate constructor throws for invalid windowBits", () => { - expect(() => new zlib.Deflate({ windowBits: "test" })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ windowBits: -Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ windowBits: Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ windowBits: 0 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -// Throws if `options.level` is invalid -test("Deflate constructor throws for invalid level", () => { - expect(() => new zlib.Deflate({ level: "test" })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ level: -Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ level: Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ level: -2 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -// Throws if `level` invalid in `Deflate.prototype.params()` -test("Deflate.prototype.params throws for invalid level", () => { - expect(() => new zlib.Deflate().params("test")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate().params(-Infinity)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate().params(Infinity)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate().params(-2)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -// Throws if options.memLevel is invalid -test("Deflate constructor throws for invalid memLevel", () => { - expect(() => new zlib.Deflate({ memLevel: "test" })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ memLevel: -Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ memLevel: Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ memLevel: -2 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -// Does not throw if opts.strategy is valid -test("Deflate constructor does not throw for valid strategy", () => { - expect(() => new zlib.Deflate({ strategy: zlib.constants.Z_FILTERED })).not.toThrow(); - expect(() => new zlib.Deflate({ strategy: zlib.constants.Z_HUFFMAN_ONLY })).not.toThrow(); - expect(() => new zlib.Deflate({ strategy: zlib.constants.Z_RLE })).not.toThrow(); - expect(() => new zlib.Deflate({ strategy: zlib.constants.Z_FIXED })).not.toThrow(); - expect(() => new zlib.Deflate({ strategy: zlib.constants.Z_DEFAULT_STRATEGY })).not.toThrow(); -}); - -// Throws if options.strategy is invalid -test("Deflate constructor throws for invalid strategy", () => { - expect(() => new zlib.Deflate({ strategy: "test" })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ strategy: -Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ strategy: Infinity })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate({ strategy: -2 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -// Throws TypeError if `strategy` is invalid in `Deflate.prototype.params()` -test("Deflate.prototype.params throws for invalid strategy", () => { - expect(() => new zlib.Deflate().params(0, "test")).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate().params(0, -Infinity)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate().params(0, Infinity)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); - - expect(() => new zlib.Deflate().params(0, -2)).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -// Throws if opts.dictionary is not a Buffer -test("Deflate constructor throws if dictionary is not a Buffer", () => { - expect(() => new zlib.Deflate({ dictionary: "not a buffer" })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-zlib-deflate-constructors.js diff --git a/test/js/node/test/parallel/zlib-deflate-raw-inherits.test.js b/test/js/node/test/parallel/zlib-deflate-raw-inherits.test.js deleted file mode 100644 index 1eb9243059..0000000000 --- a/test/js/node/test/parallel/zlib-deflate-raw-inherits.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-zlib-deflate-raw-inherits.js -//#SHA1: 9e1873864f4af27abf3a8a36a87edd2d036805d8 -//----------------- -"use strict"; - -const { DeflateRaw } = require("zlib"); -const { Readable } = require("stream"); - -// Validates that zlib.DeflateRaw can be inherited -// with Object.setPrototypeOf - -test("DeflateRaw can be inherited with Object.setPrototypeOf", done => { - function NotInitialized(options) { - DeflateRaw.call(this, options); - this.prop = true; - } - Object.setPrototypeOf(NotInitialized.prototype, DeflateRaw.prototype); - Object.setPrototypeOf(NotInitialized, DeflateRaw); - - const dest = new NotInitialized(); - - const read = new Readable({ - read() { - this.push(Buffer.from("a test string")); - this.push(null); - }, - }); - - read.pipe(dest); - dest.resume(); - - // We need to add an event listener to ensure the test completes - dest.on("finish", () => { - expect(dest.prop).toBe(true); - expect(dest instanceof DeflateRaw).toBe(true); - done(); - }); -}); - -//<#END_FILE: test-zlib-deflate-raw-inherits.js diff --git a/test/js/node/test/parallel/zlib-destroy-pipe.test.js b/test/js/node/test/parallel/zlib-destroy-pipe.test.js deleted file mode 100644 index 90866882de..0000000000 --- a/test/js/node/test/parallel/zlib-destroy-pipe.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-zlib-destroy-pipe.js -//#SHA1: 55e5ddd18c87bc58f331f82caa482cd49f5de168 -//----------------- -"use strict"; - -const zlib = require("zlib"); -const { Writable } = require("stream"); - -test("verify that the zlib transform does not error in case it is destroyed with data still in flight", () => { - const ts = zlib.createGzip(); - - const ws = new Writable({ - write(chunk, enc, cb) { - setImmediate(cb); - ts.destroy(); - }, - }); - - const buf = Buffer.allocUnsafe(1024 * 1024 * 20); - ts.end(buf); - ts.pipe(ws); -}); - -//<#END_FILE: test-zlib-destroy-pipe.js diff --git a/test/js/node/test/parallel/zlib-destroy.test.js b/test/js/node/test/parallel/zlib-destroy.test.js deleted file mode 100644 index 0947ce2827..0000000000 --- a/test/js/node/test/parallel/zlib-destroy.test.js +++ /dev/null @@ -1,40 +0,0 @@ -//#FILE: test-zlib-destroy.js -//#SHA1: b28cfad7c9e73659c624238b74dcc38146c94203 -//----------------- -"use strict"; - -const zlib = require("zlib"); - -// Verify that the zlib transform does clean up -// the handle when calling destroy. - -test("zlib transform cleans up handle on destroy", done => { - const ts = zlib.createGzip(); - ts.destroy(); - expect(ts._handle).toBeNull(); - - ts.on("close", () => { - ts.close(() => { - done(); - }); - }); -}); - -test("error is only emitted once", done => { - const decompress = zlib.createGunzip(15); - - let errorCount = 0; - decompress.on("error", err => { - errorCount++; - decompress.close(); - - // Ensure this callback is only called once - expect(errorCount).toBe(1); - done(); - }); - - decompress.write("something invalid"); - decompress.destroy(new Error("asd")); -}); - -//<#END_FILE: test-zlib-destroy.js diff --git a/test/js/node/test/parallel/zlib-dictionary-fail.test.js b/test/js/node/test/parallel/zlib-dictionary-fail.test.js deleted file mode 100644 index 4085281359..0000000000 --- a/test/js/node/test/parallel/zlib-dictionary-fail.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-zlib-dictionary-fail.js -//#SHA1: e9c6d383f9b0a202067a125c016f1ef3cd5be558 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const zlib = require("zlib"); - -// String "test" encoded with dictionary "dict". -const input = Buffer.from([0x78, 0xbb, 0x04, 0x09, 0x01, 0xa5]); - -test("zlib.createInflate without dictionary", async () => { - const stream = zlib.createInflate(); - - await expect( - new Promise((resolve, reject) => { - stream.on("error", reject); - stream.write(input); - }), - ).rejects.toThrow( - expect.objectContaining({ - message: expect.stringMatching(/Missing dictionary/), - }), - ); -}); - -test("zlib.createInflate with wrong dictionary", async () => { - const stream = zlib.createInflate({ dictionary: Buffer.from("fail") }); - - await expect( - new Promise((resolve, reject) => { - stream.on("error", reject); - stream.write(input); - }), - ).rejects.toThrow( - expect.objectContaining({ - message: expect.stringMatching(/Bad dictionary/), - }), - ); -}); - -test("zlib.createInflateRaw with wrong dictionary", async () => { - const stream = zlib.createInflateRaw({ dictionary: Buffer.from("fail") }); - - await expect( - new Promise((resolve, reject) => { - stream.on("error", reject); - stream.write(input); - }), - ).rejects.toThrow( - expect.objectContaining({ - message: expect.stringMatching(/(invalid|Operation-Ending-Supplemental Code is 0x12)/), - }), - ); -}); - -//<#END_FILE: test-zlib-dictionary-fail.js diff --git a/test/js/node/test/parallel/zlib-dictionary.test.js b/test/js/node/test/parallel/zlib-dictionary.test.js deleted file mode 100644 index e7b9fbc82a..0000000000 --- a/test/js/node/test/parallel/zlib-dictionary.test.js +++ /dev/null @@ -1,176 +0,0 @@ -//#FILE: test-zlib-dictionary.js -//#SHA1: b5fc5a33125dfeaa9965b0564a8196b056b95918 -//----------------- -"use strict"; - -const zlib = require("zlib"); - -const spdyDict = Buffer.from( - [ - "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-", - "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi", - "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser", - "-agent10010120020120220320420520630030130230330430530630740040140240340440", - "5406407408409410411412413414415416417500501502503504505accept-rangesageeta", - "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic", - "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran", - "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati", - "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo", - "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe", - "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic", - "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1", - ".1statusversionurl\0", - ].join(""), -); - -const input = ["HTTP/1.1 200 Ok", "Server: node.js", "Content-Length: 0", ""].join("\r\n"); - -function basicDictionaryTest(spdyDict) { - return new Promise(resolve => { - let output = ""; - const deflate = zlib.createDeflate({ dictionary: spdyDict }); - const inflate = zlib.createInflate({ dictionary: spdyDict }); - inflate.setEncoding("utf-8"); - - deflate.on("data", chunk => { - inflate.write(chunk); - }); - - inflate.on("data", chunk => { - output += chunk; - }); - - deflate.on("end", () => { - inflate.end(); - }); - - inflate.on("end", () => { - expect(output).toBe(input); - resolve(); - }); - - deflate.write(input); - deflate.end(); - }); -} - -function deflateResetDictionaryTest(spdyDict) { - return new Promise(resolve => { - let doneReset = false; - let output = ""; - const deflate = zlib.createDeflate({ dictionary: spdyDict }); - const inflate = zlib.createInflate({ dictionary: spdyDict }); - inflate.setEncoding("utf-8"); - - deflate.on("data", chunk => { - if (doneReset) inflate.write(chunk); - }); - - inflate.on("data", chunk => { - output += chunk; - }); - - deflate.on("end", () => { - inflate.end(); - }); - - inflate.on("end", () => { - expect(output).toBe(input); - resolve(); - }); - - deflate.write(input); - deflate.flush(() => { - deflate.reset(); - doneReset = true; - deflate.write(input); - deflate.end(); - }); - }); -} - -function rawDictionaryTest(spdyDict) { - return new Promise(resolve => { - let output = ""; - const deflate = zlib.createDeflateRaw({ dictionary: spdyDict }); - const inflate = zlib.createInflateRaw({ dictionary: spdyDict }); - inflate.setEncoding("utf-8"); - - deflate.on("data", chunk => { - inflate.write(chunk); - }); - - inflate.on("data", chunk => { - output += chunk; - }); - - deflate.on("end", () => { - inflate.end(); - }); - - inflate.on("end", () => { - expect(output).toBe(input); - resolve(); - }); - - deflate.write(input); - deflate.end(); - }); -} - -function deflateRawResetDictionaryTest(spdyDict) { - return new Promise(resolve => { - let doneReset = false; - let output = ""; - const deflate = zlib.createDeflateRaw({ dictionary: spdyDict }); - const inflate = zlib.createInflateRaw({ dictionary: spdyDict }); - inflate.setEncoding("utf-8"); - - deflate.on("data", chunk => { - if (doneReset) inflate.write(chunk); - }); - - inflate.on("data", chunk => { - output += chunk; - }); - - deflate.on("end", () => { - inflate.end(); - }); - - inflate.on("end", () => { - expect(output).toBe(input); - resolve(); - }); - - deflate.write(input); - deflate.flush(() => { - deflate.reset(); - doneReset = true; - deflate.write(input); - deflate.end(); - }); - }); -} - -const dictionaries = [spdyDict, Buffer.from(spdyDict), new Uint8Array(spdyDict)]; - -describe("zlib dictionary tests", () => { - test.each(dictionaries)("basic dictionary test", async dict => { - await basicDictionaryTest(dict); - }); - - test.each(dictionaries)("deflate reset dictionary test", async dict => { - await deflateResetDictionaryTest(dict); - }); - - test.each(dictionaries)("raw dictionary test", async dict => { - await rawDictionaryTest(dict); - }); - - test.each(dictionaries)("deflate raw reset dictionary test", async dict => { - await deflateRawResetDictionaryTest(dict); - }); -}); - -//<#END_FILE: test-zlib-dictionary.js diff --git a/test/js/node/test/parallel/zlib-empty-buffer.test.js b/test/js/node/test/parallel/zlib-empty-buffer.test.js deleted file mode 100644 index 553415206f..0000000000 --- a/test/js/node/test/parallel/zlib-empty-buffer.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-zlib-empty-buffer.js -//#SHA1: 7a2e8687dcd6e4bd815aa22c2bbff08251d9d91a -//----------------- -"use strict"; -const zlib = require("zlib"); -const { inspect, promisify } = require("util"); -const emptyBuffer = Buffer.alloc(0); - -describe("zlib empty buffer", () => { - const testCases = [ - [zlib.deflateRawSync, zlib.inflateRawSync, "raw sync"], - [zlib.deflateSync, zlib.inflateSync, "deflate sync"], - [zlib.gzipSync, zlib.gunzipSync, "gzip sync"], - [zlib.brotliCompressSync, zlib.brotliDecompressSync, "br sync"], - [promisify(zlib.deflateRaw), promisify(zlib.inflateRaw), "raw"], - [promisify(zlib.deflate), promisify(zlib.inflate), "deflate"], - [promisify(zlib.gzip), promisify(zlib.gunzip), "gzip"], - [promisify(zlib.brotliCompress), promisify(zlib.brotliDecompress), "br"], - ]; - - test.each(testCases)("compresses and decompresses empty buffer using %s", async (compress, decompress, method) => { - const compressed = await compress(emptyBuffer); - const decompressed = await decompress(compressed); - expect(decompressed).toStrictEqual(emptyBuffer); - }); -}); - -//<#END_FILE: test-zlib-empty-buffer.js diff --git a/test/js/node/test/parallel/zlib-failed-init.test.js b/test/js/node/test/parallel/zlib-failed-init.test.js deleted file mode 100644 index 1f47ce229d..0000000000 --- a/test/js/node/test/parallel/zlib-failed-init.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-zlib-failed-init.js -//#SHA1: a1c770e81c677ebefa0e5969e3441b28e63ab75a -//----------------- -"use strict"; - -const zlib = require("zlib"); - -test("zlib createGzip with invalid chunkSize", () => { - expect(() => zlib.createGzip({ chunkSize: 0 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - }), - ); -}); - -test("zlib createGzip with invalid windowBits", () => { - expect(() => zlib.createGzip({ windowBits: 0 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - }), - ); -}); - -test("zlib createGzip with invalid memLevel", () => { - expect(() => zlib.createGzip({ memLevel: 0 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - }), - ); -}); - -test("zlib createGzip with NaN level", () => { - const stream = zlib.createGzip({ level: NaN }); - expect(stream._level).toBe(zlib.constants.Z_DEFAULT_COMPRESSION); -}); - -test("zlib createGzip with NaN strategy", () => { - const stream = zlib.createGzip({ strategy: NaN }); - expect(stream._strategy).toBe(zlib.constants.Z_DEFAULT_STRATEGY); -}); - -//<#END_FILE: test-zlib-failed-init.js diff --git a/test/js/node/test/parallel/zlib-flush-drain-longblock.test.js b/test/js/node/test/parallel/zlib-flush-drain-longblock.test.js deleted file mode 100644 index 21f8a7d720..0000000000 --- a/test/js/node/test/parallel/zlib-flush-drain-longblock.test.js +++ /dev/null @@ -1,34 +0,0 @@ -//#FILE: test-zlib-flush-drain-longblock.js -//#SHA1: 95927f13fbb59e0a8a2a32c14d0443fc110bab6e -//----------------- -"use strict"; - -const zlib = require("zlib"); - -test("zlib flush interacts properly with writableState.needDrain", done => { - const zipper = zlib.createGzip({ highWaterMark: 16384 }); - const unzipper = zlib.createGunzip(); - zipper.pipe(unzipper); - - zipper.write("A".repeat(17000)); - zipper.flush(); - - let received = 0; - let dataCallCount = 0; - - unzipper.on("data", d => { - received += d.length; - dataCallCount++; - }); - - unzipper.on("end", () => { - expect(received).toBe(17000); - expect(dataCallCount).toBeGreaterThanOrEqual(2); - done(); - }); - - // Properly end the streams to ensure all data is processed - zipper.end(); -}); - -//<#END_FILE: test-zlib-flush-drain-longblock.js diff --git a/test/js/node/test/parallel/zlib-flush-drain.test.js b/test/js/node/test/parallel/zlib-flush-drain.test.js deleted file mode 100644 index b4407e7cbd..0000000000 --- a/test/js/node/test/parallel/zlib-flush-drain.test.js +++ /dev/null @@ -1,53 +0,0 @@ -//#FILE: test-zlib-flush-drain.js -//#SHA1: 2f83bee63a56543c9824833e4fa7d8f5b33a373e -//----------------- -"use strict"; -const zlib = require("zlib"); - -const bigData = Buffer.alloc(10240, "x"); - -const opts = { - level: 0, - highWaterMark: 16, -}; - -let flushCount = 0; -let drainCount = 0; -let beforeFlush, afterFlush; - -test("zlib flush and drain behavior", done => { - const deflater = zlib.createDeflate(opts); - - // Shim deflater.flush so we can count times executed - const flush = deflater.flush; - deflater.flush = function (kind, callback) { - flushCount++; - flush.call(this, kind, callback); - }; - - deflater.write(bigData); - - const ws = deflater._writableState; - beforeFlush = ws.needDrain; - - deflater.on("data", () => {}); - - deflater.flush(function (err) { - afterFlush = ws.needDrain; - }); - - deflater.on("drain", function () { - drainCount++; - }); - - // Use setTimeout to ensure all asynchronous operations have completed - setTimeout(() => { - expect(beforeFlush).toBe(true); - expect(afterFlush).toBe(false); - expect(drainCount).toBe(1); - expect(flushCount).toBe(1); - done(); - }, 100); -}); - -//<#END_FILE: test-zlib-flush-drain.js diff --git a/test/js/node/test/parallel/zlib-flush-flags.test.js b/test/js/node/test/parallel/zlib-flush-flags.test.js deleted file mode 100644 index 306fe03791..0000000000 --- a/test/js/node/test/parallel/zlib-flush-flags.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-zlib-flush-flags.js -//#SHA1: 9fb4201163deb1a6dc1f74c5c7a2723ec1a9d342 -//----------------- -"use strict"; - -const zlib = require("zlib"); - -test("createGzip with valid flush option", () => { - expect(() => zlib.createGzip({ flush: zlib.constants.Z_SYNC_FLUSH })).not.toThrow(); -}); - -test("createGzip with invalid flush option type", () => { - expect(() => zlib.createGzip({ flush: "foobar" })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -test("createGzip with out of range flush option", () => { - expect(() => zlib.createGzip({ flush: 10000 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -test("createGzip with valid finishFlush option", () => { - expect(() => zlib.createGzip({ finishFlush: zlib.constants.Z_SYNC_FLUSH })).not.toThrow(); -}); - -test("createGzip with invalid finishFlush option type", () => { - expect(() => zlib.createGzip({ finishFlush: "foobar" })).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: expect.any(String), - }), - ); -}); - -test("createGzip with out of range finishFlush option", () => { - expect(() => zlib.createGzip({ finishFlush: 10000 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-zlib-flush-flags.js diff --git a/test/js/node/test/parallel/zlib-flush-write-sync-interleaved.test.js b/test/js/node/test/parallel/zlib-flush-write-sync-interleaved.test.js deleted file mode 100644 index 1661372b3d..0000000000 --- a/test/js/node/test/parallel/zlib-flush-write-sync-interleaved.test.js +++ /dev/null @@ -1,66 +0,0 @@ -//#FILE: test-zlib-flush-write-sync-interleaved.js -//#SHA1: 35bfe36486f112686943448a115a586035455ba7 -//----------------- -"use strict"; -const { createGzip, createGunzip, Z_PARTIAL_FLUSH } = require("zlib"); - -// Verify that .flush() behaves like .write() in terms of ordering, e.g. in -// a sequence like .write() + .flush() + .write() + .flush() each .flush() call -// only affects the data written before it. -// Refs: https://github.com/nodejs/node/issues/28478 - -test("zlib flush and write ordering", done => { - const compress = createGzip(); - const decompress = createGunzip(); - decompress.setEncoding("utf8"); - - const events = []; - const compressedChunks = []; - - for (const chunk of ["abc", "def", "ghi"]) { - compress.write(chunk, () => events.push({ written: chunk })); - compress.flush(Z_PARTIAL_FLUSH, () => { - events.push("flushed"); - const chunk = compress.read(); - if (chunk !== null) compressedChunks.push(chunk); - }); - } - - compress.end(() => { - events.push("compress end"); - writeToDecompress(); - }); - - function writeToDecompress() { - // Write the compressed chunks to a decompressor, one by one, in order to - // verify that the flushes actually worked. - const chunk = compressedChunks.shift(); - if (chunk === undefined) { - decompress.end(); - checkResults(); - return; - } - decompress.write(chunk, () => { - events.push({ read: decompress.read() }); - writeToDecompress(); - }); - } - - function checkResults() { - expect(events).toEqual([ - { written: "abc" }, - "flushed", - { written: "def" }, - "flushed", - { written: "ghi" }, - "flushed", - "compress end", - { read: "abc" }, - { read: "def" }, - { read: "ghi" }, - ]); - done(); - } -}); - -//<#END_FILE: test-zlib-flush-write-sync-interleaved.js diff --git a/test/js/node/test/parallel/zlib-flush.test.js b/test/js/node/test/parallel/zlib-flush.test.js deleted file mode 100644 index cfda6ffb6d..0000000000 --- a/test/js/node/test/parallel/zlib-flush.test.js +++ /dev/null @@ -1,38 +0,0 @@ -//#FILE: test-zlib-flush.js -//#SHA1: 61f325893c63c826c2a498bc52ef3401f9a5a542 -//----------------- -"use strict"; - -const zlib = require("node:zlib"); - -test("zlib flush", async () => { - const opts = { level: 0 }; - const deflater = zlib.createDeflate(opts); - const chunk = Buffer.from("/9j/4AAQSkZJRgABAQEASA==", "base64"); - const expectedNone = Buffer.from([0x78, 0x01]); - const blkhdr = Buffer.from([0x00, 0x10, 0x00, 0xef, 0xff]); - const adler32 = Buffer.from([0x00, 0x00, 0x00, 0xff, 0xff]); - const expectedFull = Buffer.concat([blkhdr, chunk, adler32]); - let actualNone; - let actualFull; - - await new Promise(resolve => { - deflater.write(chunk, function () { - deflater.flush(zlib.constants.Z_NO_FLUSH, function () { - actualNone = deflater.read(); - deflater.flush(function () { - const bufs = []; - let buf; - while ((buf = deflater.read()) !== null) bufs.push(buf); - actualFull = Buffer.concat(bufs); - resolve(); - }); - }); - }); - }); - - expect(actualNone).toEqual(expectedNone); - expect(actualFull).toEqual(expectedFull); -}); - -//<#END_FILE: test-zlib-flush.js diff --git a/test/js/node/test/parallel/zlib-from-concatenated-gzip.test.js b/test/js/node/test/parallel/zlib-from-concatenated-gzip.test.js deleted file mode 100644 index 1961f9e215..0000000000 --- a/test/js/node/test/parallel/zlib-from-concatenated-gzip.test.js +++ /dev/null @@ -1,99 +0,0 @@ -//#FILE: test-zlib-from-concatenated-gzip.js -//#SHA1: cf8f45097fb201dc583ee154b34585b6a0a0dc34 -//----------------- -"use strict"; -// Test unzipping a gzip file that contains multiple concatenated "members" - -const zlib = require("zlib"); -const fs = require("fs"); -const path = require("path"); - -const abc = "abc"; -const def = "def"; - -const abcEncoded = zlib.gzipSync(abc); -const defEncoded = zlib.gzipSync(def); - -const data = Buffer.concat([abcEncoded, defEncoded]); - -test("gunzipSync concatenated gzip members", () => { - expect(zlib.gunzipSync(data).toString()).toBe(abc + def); -}); - -test("gunzip concatenated gzip members", async () => { - const result = await new Promise((resolve, reject) => { - zlib.gunzip(data, (err, result) => { - if (err) reject(err); - else resolve(result); - }); - }); - expect(result.toString()).toBe(abc + def); -}); - -test("unzip concatenated gzip members", async () => { - const result = await new Promise((resolve, reject) => { - zlib.unzip(data, (err, result) => { - if (err) reject(err); - else resolve(result); - }); - }); - expect(result.toString()).toBe(abc + def); -}); - -test("unzip concatenated deflate members", async () => { - const result = await new Promise((resolve, reject) => { - zlib.unzip(Buffer.concat([zlib.deflateSync("abc"), zlib.deflateSync("def")]), (err, result) => { - if (err) reject(err); - else resolve(result); - }); - }); - expect(result.toString()).toBe(abc); -}); - -test("pseudo-multimember gzip file", async () => { - const pmmFileZlib = path.join(__dirname, "../fixtures/pseudo-multimember-gzip.z"); - const pmmFileGz = path.join(__dirname, "../fixtures/pseudo-multimember-gzip.gz"); - - const pmmExpected = zlib.inflateSync(fs.readFileSync(pmmFileZlib)); - const pmmResultBuffers = []; - - await new Promise((resolve, reject) => { - fs.createReadStream(pmmFileGz) - .pipe(zlib.createGunzip()) - .on("error", reject) - .on("data", data => pmmResultBuffers.push(data)) - .on("finish", resolve); - }); - - // Result should match original random garbage - expect(Buffer.concat(pmmResultBuffers)).toEqual(pmmExpected); -}); - -test("gzip member wrapping around input buffer boundary", async () => { - const offsets = [0, 1, 2, 3, 4, defEncoded.length]; - - for (const offset of offsets) { - const resultBuffers = []; - - await new Promise((resolve, reject) => { - const unzip = zlib - .createGunzip() - .on("error", reject) - .on("data", data => resultBuffers.push(data)) - .on("finish", resolve); - - // First write: write "abc" + the first bytes of "def" - unzip.write(Buffer.concat([abcEncoded, defEncoded.slice(0, offset)])); - - // Write remaining bytes of "def" - unzip.end(defEncoded.slice(offset)); - }); - - expect(Buffer.concat(resultBuffers).toString()).toBe( - "abcdef", - `result should match original input (offset = ${offset})`, - ); - } -}); - -//<#END_FILE: test-zlib-from-concatenated-gzip.js diff --git a/test/js/node/test/parallel/zlib-from-gzip-with-trailing-garbage.test.js b/test/js/node/test/parallel/zlib-from-gzip-with-trailing-garbage.test.js deleted file mode 100644 index 4b2371eabd..0000000000 --- a/test/js/node/test/parallel/zlib-from-gzip-with-trailing-garbage.test.js +++ /dev/null @@ -1,76 +0,0 @@ -//#FILE: test-zlib-from-gzip-with-trailing-garbage.js -//#SHA1: 8c3ebbede1912a48995aaada3c3e470d48bc01f3 -//----------------- -"use strict"; -const zlib = require("zlib"); - -describe("Unzipping a gzip file with trailing garbage", () => { - test("Should ignore trailing null-bytes", () => { - const data = Buffer.concat([zlib.gzipSync("abc"), zlib.gzipSync("def"), Buffer.alloc(10)]); - - expect(zlib.gunzipSync(data).toString()).toBe("abcdef"); - }); - - test("Should ignore trailing null-bytes (async)", async () => { - const data = Buffer.concat([zlib.gzipSync("abc"), zlib.gzipSync("def"), Buffer.alloc(10)]); - - const result = await new Promise((resolve, reject) => { - zlib.gunzip(data, (err, result) => { - if (err) reject(err); - else resolve(result); - }); - }); - - expect(result.toString()).toBe("abcdef"); - }); - - test("Should throw error if trailing garbage looks like a gzip header", () => { - const data = Buffer.concat([ - zlib.gzipSync("abc"), - zlib.gzipSync("def"), - Buffer.from([0x1f, 0x8b, 0xff, 0xff]), - Buffer.alloc(10), - ]); - - expect(() => zlib.gunzipSync(data)).toThrow("unknown compression method"); - }); - - test("Should throw error if trailing garbage looks like a gzip header (async)", async () => { - const data = Buffer.concat([ - zlib.gzipSync("abc"), - zlib.gzipSync("def"), - Buffer.from([0x1f, 0x8b, 0xff, 0xff]), - Buffer.alloc(10), - ]); - - await expect( - new Promise((resolve, reject) => { - zlib.gunzip(data, (err, result) => { - if (err) reject(err); - else resolve(result); - }); - }), - ).rejects.toThrow("unknown compression method"); - }); - - test("Should throw error if trailing junk is too short to be a gzip segment", () => { - const data = Buffer.concat([zlib.gzipSync("abc"), zlib.gzipSync("def"), Buffer.from([0x1f, 0x8b, 0xff, 0xff])]); - - expect(() => zlib.gunzipSync(data)).toThrow("unknown compression method"); - }); - - test("Should throw error if trailing junk is too short to be a gzip segment (async)", async () => { - const data = Buffer.concat([zlib.gzipSync("abc"), zlib.gzipSync("def"), Buffer.from([0x1f, 0x8b, 0xff, 0xff])]); - - await expect( - new Promise((resolve, reject) => { - zlib.gunzip(data, (err, result) => { - if (err) reject(err); - else resolve(result); - }); - }), - ).rejects.toThrow("unknown compression method"); - }); -}); - -//<#END_FILE: test-zlib-from-gzip-with-trailing-garbage.js diff --git a/test/js/node/test/parallel/zlib-from-gzip.test.js b/test/js/node/test/parallel/zlib-from-gzip.test.js deleted file mode 100644 index c1406008f2..0000000000 --- a/test/js/node/test/parallel/zlib-from-gzip.test.js +++ /dev/null @@ -1,41 +0,0 @@ -//#FILE: test-zlib-from-gzip.js -//#SHA1: 44570a611316087d64497151e9ed570aca9060d5 -//----------------- -"use strict"; - -import { tmpdirSync } from "harness"; - -const assert = require("assert"); -const path = require("path"); -const zlib = require("zlib"); -const fixtures = require("../common/fixtures"); -const fs = require("fs"); - -test("test unzipping a file that was created with a non-node gzip lib, piped in as fast as possible", async () => { - const x = tmpdirSync(); - const gunzip = zlib.createGunzip(); - const fixture = fixtures.path("person.jpg.gz"); - const unzippedFixture = fixtures.path("person.jpg"); - const outputFile = path.resolve(x, "person.jpg"); - const expected = fs.readFileSync(unzippedFixture); - const inp = fs.createReadStream(fixture); - const out = fs.createWriteStream(outputFile); - - inp.pipe(gunzip).pipe(out); - - const { promise, resolve, reject } = Promise.withResolvers(); - - out.on("close", () => { - try { - const actual = fs.readFileSync(outputFile); - expect(actual.length).toBe(expected.length); - expect(actual).toEqual(expected); - resolve(); - } catch (e) { - reject(e); - } - }); - await promise; -}); - -//<#END_FILE: test-zlib-from-gzip.js diff --git a/test/js/node/test/parallel/zlib-from-string.test.js b/test/js/node/test/parallel/zlib-from-string.test.js deleted file mode 100644 index a9b8900d7f..0000000000 --- a/test/js/node/test/parallel/zlib-from-string.test.js +++ /dev/null @@ -1,121 +0,0 @@ -//#FILE: test-zlib-from-string.js -//#SHA1: 0514669607bbf01e20f41875fa716660ebfcf28b -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Test compressing and uncompressing a string with zlib - -const zlib = require("zlib"); - -const inputString = - "ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli" + - "t. Morbi faucibus, purus at gravida dictum, libero arcu " + - "convallis lacus, in commodo libero metus eu nisi. Nullam" + - " commodo, neque nec porta placerat, nisi est fermentum a" + - "ugue, vitae gravida tellus sapien sit amet tellus. Aenea" + - "n non diam orci. Proin quis elit turpis. Suspendisse non" + - " diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu" + - "m arcu mi, sodales non suscipit id, ultrices ut massa. S" + - "ed ac sem sit amet arcu malesuada fermentum. Nunc sed. "; -const expectedBase64Deflate = - "eJxdUUtOQzEMvMoc4OndgT0gJCT2buJWlpI4jePeqZfpmX" + - "AKLRKbLOzx/HK73q6vOrhCunlF1qIDJhNUeW5I2ozT5OkD" + - "lKWLJWkncJG5403HQXAkT3Jw29B9uIEmToMukglZ0vS6oc" + - "iBh4JG8sV4oVLEUCitK2kxq1WzPnChHDzsaGKy491LofoA" + - "bWh8do43oeuYhB5EPCjcLjzYJo48KrfQBvnJecNFJvHT1+" + - "RSQsGoC7dn2t/xjhduTA1NWyQIZR0pbHwMDatnD+crPqKS" + - "qGPHp1vnlsWM/07ubf7bheF7kqSj84Bm0R1fYTfaK8vqqq" + - "fKBtNMhe3OZh6N95CTvMX5HJJi4xOVzCgUOIMSLH7wmeOH" + - "aFE4RdpnGavKtrB5xzfO/Ll9"; -const expectedBase64Gzip = - "H4sIAAAAAAAAA11RS05DMQy8yhzg6d2BPSAkJPZu4laWkjiN4" + - "96pl+mZcAotEpss7PH8crverq86uEK6eUXWogMmE1R5bkjajN" + - "Pk6QOUpYslaSdwkbnjTcdBcCRPcnDb0H24gSZOgy6SCVnS9Lq" + - "hyIGHgkbyxXihUsRQKK0raTGrVbM+cKEcPOxoYrLj3Uuh+gBt" + - "aHx2jjeh65iEHkQ8KNwuPNgmjjwqt9AG+cl5w0Um8dPX5FJCw" + - "agLt2fa3/GOF25MDU1bJAhlHSlsfAwNq2cP5ys+opKoY8enW+" + - "eWxYz/Tu5t/tuF4XuSpKPzgGbRHV9hN9ory+qqp8oG00yF7c5" + - "mHo33kJO8xfkckmLjE5XMKBQ4gxIsfvCZ44doUThF2mcZq8q2" + - "sHnHNzRtagj5AQAA"; - -test("deflate and inflate", async () => { - const buffer = await new Promise((resolve, reject) => { - zlib.deflate(inputString, (err, buffer) => { - if (err) reject(err); - else resolve(buffer); - }); - }); - - const inflated = await new Promise((resolve, reject) => { - zlib.inflate(buffer, (err, inflated) => { - if (err) reject(err); - else resolve(inflated); - }); - }); - - expect(inflated.toString()).toBe(inputString); -}); - -test("gzip and gunzip", async () => { - const buffer = await new Promise((resolve, reject) => { - zlib.gzip(inputString, (err, buffer) => { - if (err) reject(err); - else resolve(buffer); - }); - }); - - const gunzipped = await new Promise((resolve, reject) => { - zlib.gunzip(buffer, (err, gunzipped) => { - if (err) reject(err); - else resolve(gunzipped); - }); - }); - - expect(gunzipped.toString()).toBe(inputString); -}); - -test("unzip base64 deflate", async () => { - const buffer = Buffer.from(expectedBase64Deflate, "base64"); - const unzipped = await new Promise((resolve, reject) => { - zlib.unzip(buffer, (err, unzipped) => { - if (err) reject(err); - else resolve(unzipped); - }); - }); - - expect(unzipped.toString()).toBe(inputString); -}); - -test("unzip base64 gzip", async () => { - const buffer = Buffer.from(expectedBase64Gzip, "base64"); - const unzipped = await new Promise((resolve, reject) => { - zlib.unzip(buffer, (err, unzipped) => { - if (err) reject(err); - else resolve(unzipped); - }); - }); - - expect(unzipped.toString()).toBe(inputString); -}); - -//<#END_FILE: test-zlib-from-string.js diff --git a/test/js/node/test/parallel/zlib-invalid-arg-value-brotli-compress.test.js b/test/js/node/test/parallel/zlib-invalid-arg-value-brotli-compress.test.js deleted file mode 100644 index 4fbb56f1a0..0000000000 --- a/test/js/node/test/parallel/zlib-invalid-arg-value-brotli-compress.test.js +++ /dev/null @@ -1,27 +0,0 @@ -//#FILE: test-zlib-invalid-arg-value-brotli-compress.js -//#SHA1: fab88f892e21351603b5ae7227837d01149bdae6 -//----------------- -"use strict"; - -// This test ensures that the BrotliCompress function throws -// ERR_INVALID_ARG_TYPE when the values of the `params` key-value object are -// neither numbers nor booleans. - -const { BrotliCompress, constants } = require("zlib"); - -test("BrotliCompress throws ERR_INVALID_ARG_TYPE for invalid params value", () => { - const opts = { - params: { - [constants.BROTLI_PARAM_MODE]: "lol", - }, - }; - - expect(() => BrotliCompress(opts)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-zlib-invalid-arg-value-brotli-compress.js diff --git a/test/js/node/test/parallel/zlib-invalid-input-memory.test.js b/test/js/node/test/parallel/zlib-invalid-input-memory.test.js deleted file mode 100644 index 7193d0a531..0000000000 --- a/test/js/node/test/parallel/zlib-invalid-input-memory.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-zlib-invalid-input-memory.js -//#SHA1: 2607db89f2850bfbe75959ca2cd56e647b9eac78 -//----------------- -"use strict"; -const zlib = require("zlib"); -const onGC = require("../common/ongc"); -const common = require("../common"); - -const ongc = common.mustCall(); - -test.todo("zlib context with error can be garbage collected", () => { - const input = Buffer.from("foobar"); - const strm = zlib.createInflate(); - - strm.end(input); - - strm.once("error", err => { - expect(err).toBeTruthy(); - - setImmediate(() => { - global.gc(); - // Keep the event loop alive for seeing the async_hooks destroy hook we use for GC tracking... - // TODO(addaleax): This should maybe not be necessary? - setImmediate(() => {}); - }); - }); - onGC(strm, { ongc }); -}); - -//<#END_FILE: test-zlib-invalid-input-memory.js diff --git a/test/js/node/test/parallel/zlib-invalid-input.test.js b/test/js/node/test/parallel/zlib-invalid-input.test.js deleted file mode 100644 index aeb28f609d..0000000000 --- a/test/js/node/test/parallel/zlib-invalid-input.test.js +++ /dev/null @@ -1,70 +0,0 @@ -//#FILE: test-zlib-invalid-input.js -//#SHA1: b76d7dde545ea75c25000ee36864841cf73951e0 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -// Test uncompressing invalid input - -const zlib = require("zlib"); - -const nonStringInputs = [1, true, { a: 1 }, ["a"]]; - -// zlib.Unzip classes need to get valid data, or else they'll throw. -const unzips = [zlib.Unzip(), zlib.Gunzip(), zlib.Inflate(), zlib.InflateRaw(), zlib.BrotliDecompress()]; - -test("zlib.gunzip throws TypeError for non-string inputs", () => { - nonStringInputs.forEach(input => { - expect(() => { - zlib.gunzip(input); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_TYPE", - message: expect.any(String), - }), - ); - }); -}); - -test("unzip classes emit error for invalid compressed data", done => { - let completedCount = 0; - - unzips.forEach((uz, i) => { - uz.on("error", err => { - expect(err).toBeTruthy(); - completedCount++; - if (completedCount === unzips.length) { - done(); - } - }); - - uz.on("end", () => { - throw new Error("end event should not be emitted"); - }); - - // This will trigger error event - uz.write("this is not valid compressed data."); - }); -}); - -//<#END_FILE: test-zlib-invalid-input.js diff --git a/test/js/node/test/parallel/zlib-kmaxlength-rangeerror.test.js b/test/js/node/test/parallel/zlib-kmaxlength-rangeerror.test.js deleted file mode 100644 index 9d61c6ba81..0000000000 --- a/test/js/node/test/parallel/zlib-kmaxlength-rangeerror.test.js +++ /dev/null @@ -1,30 +0,0 @@ -//#FILE: test-zlib-kmaxlength-rangeerror.js -//#SHA1: b6507dfb859656a2d5194153ddf45de79e0ef0ce -//----------------- -"use strict"; - -const util = require("util"); - -// Change kMaxLength for zlib to trigger the error without having to allocate large Buffers. -const buffer = require("buffer"); -const oldkMaxLength = buffer.kMaxLength; -buffer.kMaxLength = 64; -const zlib = require("zlib"); -buffer.kMaxLength = oldkMaxLength; - -const encoded = Buffer.from("H4sIAAAAAAAAA0tMHFgAAIw2K/GAAAAA", "base64"); - -describe("ensure that zlib throws a RangeError if the final buffer needs to be larger than kMaxLength and concatenation fails", () => { - test("sync", () => { - expect(() => zlib.gunzipSync(encoded)).toThrowWithCode(RangeError, "ERR_BUFFER_TOO_LARGE"); - }); - - test("async", async () => { - await expect(async () => util.promisify(zlib.gunzip)(encoded)).toThrowWithCodeAsync( - RangeError, - "ERR_BUFFER_TOO_LARGE", - ); - }); -}); - -//<#END_FILE: test-zlib-kmaxlength-rangeerror.js diff --git a/test/js/node/test/parallel/zlib-maxoutputlength.test.js b/test/js/node/test/parallel/zlib-maxoutputlength.test.js deleted file mode 100644 index 2223043725..0000000000 --- a/test/js/node/test/parallel/zlib-maxoutputlength.test.js +++ /dev/null @@ -1,45 +0,0 @@ -//#FILE: test-zlib-maxOutputLength.js -//#SHA1: 807eb08c901d7476140bcb35b519518450bef3e6 -//----------------- -"use strict"; -const zlib = require("zlib"); -const { promisify } = require("util"); - -const brotliDecompressAsync = promisify(zlib.brotliDecompress); - -const encoded = Buffer.from("G38A+CXCIrFAIAM=", "base64"); - -describe("zlib.brotliDecompress with maxOutputLength", () => { - test("Async: should throw ERR_BUFFER_TOO_LARGE when maxOutputLength is exceeded", async () => { - await expect(brotliDecompressAsync(encoded, { maxOutputLength: 64 })).rejects.toThrow( - expect.objectContaining({ - code: "ERR_BUFFER_TOO_LARGE", - message: expect.stringContaining("Cannot create a Buffer larger than 64 bytes"), - }), - ); - }); - - test("Sync: should throw ERR_BUFFER_TOO_LARGE when maxOutputLength is exceeded", () => { - expect(() => { - zlib.brotliDecompressSync(encoded, { maxOutputLength: 64 }); - }).toThrow( - expect.objectContaining({ - name: "RangeError", - code: "ERR_BUFFER_TOO_LARGE", - message: expect.stringContaining("Cannot create a Buffer larger than 64 bytes"), - }), - ); - }); - - test("Async: should not throw error when maxOutputLength is sufficient", async () => { - await brotliDecompressAsync(encoded, { maxOutputLength: 256 }); - }); - - test("Sync: should not throw error when maxOutputLength is sufficient", () => { - expect(() => { - zlib.brotliDecompressSync(encoded, { maxOutputLength: 256 }); - }).not.toThrow(); - }); -}); - -//<#END_FILE: test-zlib-maxOutputLength.js diff --git a/test/js/node/test/parallel/zlib-no-stream.test.js b/test/js/node/test/parallel/zlib-no-stream.test.js deleted file mode 100644 index 871f4686a2..0000000000 --- a/test/js/node/test/parallel/zlib-no-stream.test.js +++ /dev/null @@ -1,21 +0,0 @@ -//#FILE: test-zlib-no-stream.js -//#SHA1: 5755924e9363a20243c326747623e8e266f81625 -//----------------- -/* eslint-disable node-core/required-modules */ -/* eslint-disable node-core/require-common-first */ - -"use strict"; - -// We are not loading common because it will load the stream module, -// defeating the purpose of this test. - -const { gzipSync } = require("zlib"); - -// Avoid regressions such as https://github.com/nodejs/node/issues/36615 - -test("gzipSync should not throw", () => { - // This must not throw - expect(() => gzipSync("fooobar")).not.toThrow(); -}); - -//<#END_FILE: test-zlib-no-stream.js diff --git a/test/js/node/test/parallel/zlib-not-string-or-buffer.test.js b/test/js/node/test/parallel/zlib-not-string-or-buffer.test.js deleted file mode 100644 index 626430ec72..0000000000 --- a/test/js/node/test/parallel/zlib-not-string-or-buffer.test.js +++ /dev/null @@ -1,24 +0,0 @@ -//#FILE: test-zlib-not-string-or-buffer.js -//#SHA1: d07db97d9393df2ab9453800ae80f2921d93b6e2 -//----------------- -"use strict"; - -// Check the error condition testing for passing something other than a string -// or buffer. - -const zlib = require("zlib"); - -test("zlib.deflateSync throws for invalid input types", () => { - const invalidInputs = [undefined, null, true, false, 0, 1, [1, 2, 3], { foo: "bar" }]; - - invalidInputs.forEach(input => { - expect(() => zlib.deflateSync(input)).toThrow( - expect.objectContaining({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }), - ); - }); -}); - -//<#END_FILE: test-zlib-not-string-or-buffer.js diff --git a/test/js/node/test/parallel/zlib-object-write.test.js b/test/js/node/test/parallel/zlib-object-write.test.js deleted file mode 100644 index 63934f60b2..0000000000 --- a/test/js/node/test/parallel/zlib-object-write.test.js +++ /dev/null @@ -1,28 +0,0 @@ -//#FILE: test-zlib-object-write.js -//#SHA1: 8866194c1a944026a655101d66189f364b414ad7 -//----------------- -"use strict"; - -const { Gunzip } = require("zlib"); - -test("Gunzip in object mode throws on non-buffer write", () => { - const gunzip = new Gunzip({ objectMode: true }); - - // We use jest.fn() to create a mock function that we expect not to be called - const errorHandler = jest.fn(); - gunzip.on("error", errorHandler); - - expect(() => { - gunzip.write({}); - }).toThrow( - expect.objectContaining({ - name: "TypeError", - code: "ERR_INVALID_ARG_TYPE", - }), - ); - - // Verify that the error handler was not called - expect(errorHandler).not.toHaveBeenCalled(); -}); - -//<#END_FILE: test-zlib-object-write.js diff --git a/test/js/node/test/parallel/zlib-params.test.js b/test/js/node/test/parallel/zlib-params.test.js deleted file mode 100644 index 2c8063d78a..0000000000 --- a/test/js/node/test/parallel/zlib-params.test.js +++ /dev/null @@ -1,49 +0,0 @@ -//#FILE: test-zlib-params.js -//#SHA1: d7d1b0c78ae9b4b5df5a1057c18fc7f2ef735526 -//----------------- -"use strict"; -const zlib = require("zlib"); -const fs = require("fs"); -const path = require("path"); - -const fixturesPath = path.join(__dirname, "..", "fixtures"); -const file = fs.readFileSync(path.join(fixturesPath, "person.jpg")); -const chunkSize = 12 * 1024; -const opts = { level: 9, strategy: zlib.constants.Z_DEFAULT_STRATEGY }; - -test("zlib params change mid-stream", done => { - const deflater = zlib.createDeflate(opts); - - const chunk1 = file.slice(0, chunkSize); - const chunk2 = file.slice(chunkSize); - const blkhdr = Buffer.from([0x00, 0x5a, 0x82, 0xa5, 0x7d]); - const blkftr = Buffer.from("010000ffff7dac3072", "hex"); - const expected = Buffer.concat([blkhdr, chunk2, blkftr]); - const bufs = []; - - function read() { - let buf; - while ((buf = deflater.read()) !== null) { - bufs.push(buf); - } - } - - deflater.write(chunk1, () => { - deflater.params(0, zlib.constants.Z_DEFAULT_STRATEGY, () => { - while (deflater.read()); - - deflater.on("readable", read); - - deflater.end(chunk2); - }); - while (deflater.read()); - }); - - deflater.on("end", () => { - const actual = Buffer.concat(bufs); - expect(actual).toEqual(expected); - done(); - }); -}); - -//<#END_FILE: test-zlib-params.js diff --git a/test/js/node/test/parallel/zlib-premature-end.test.js b/test/js/node/test/parallel/zlib-premature-end.test.js deleted file mode 100644 index cb20184ac0..0000000000 --- a/test/js/node/test/parallel/zlib-premature-end.test.js +++ /dev/null @@ -1,59 +0,0 @@ -//#FILE: test-zlib-premature-end.js -//#SHA1: f44e08e4886032467eb9cf64412c7eda2b575a16 -//----------------- -"use strict"; -const zlib = require("zlib"); - -const input = "0123456789".repeat(4); - -const testCases = [ - [zlib.deflateRawSync, zlib.createInflateRaw], - [zlib.deflateSync, zlib.createInflate], - [zlib.brotliCompressSync, zlib.createBrotliDecompress], -]; - -const variants = [ - (stream, compressed, trailingData) => { - stream.end(compressed); - }, - (stream, compressed, trailingData) => { - stream.write(compressed); - stream.write(trailingData); - }, - (stream, compressed, trailingData) => { - stream.write(compressed); - stream.end(trailingData); - }, - (stream, compressed, trailingData) => { - stream.write(Buffer.concat([compressed, trailingData])); - }, - (stream, compressed, trailingData) => { - stream.end(Buffer.concat([compressed, trailingData])); - }, -]; - -describe("zlib premature end tests", () => { - testCases.forEach(([compress, decompressor]) => { - describe(`${compress.name} and ${decompressor.name}`, () => { - const compressed = compress(input); - const trailingData = Buffer.from("not valid compressed data"); - - variants.forEach((variant, index) => { - test(`variant ${index + 1}`, done => { - let output = ""; - const stream = decompressor(); - stream.setEncoding("utf8"); - stream.on("data", chunk => (output += chunk)); - stream.on("end", () => { - expect(output).toBe(input); - expect(stream.bytesWritten).toBe(compressed.length); - done(); - }); - variant(stream, compressed, trailingData); - }); - }); - }); - }); -}); - -//<#END_FILE: test-zlib-premature-end.js diff --git a/test/js/node/test/parallel/zlib-random-byte-pipes.test.js b/test/js/node/test/parallel/zlib-random-byte-pipes.test.js deleted file mode 100644 index fb37cf53f9..0000000000 --- a/test/js/node/test/parallel/zlib-random-byte-pipes.test.js +++ /dev/null @@ -1,166 +0,0 @@ -//#FILE: test-zlib-random-byte-pipes.js -//#SHA1: ef7e7d3683660b911f9e24a6f40947a47be3dbba -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const crypto = require("crypto"); -const stream = require("stream"); -const zlib = require("zlib"); - -const Stream = stream.Stream; - -// Emit random bytes, and keep a shasum -class RandomReadStream extends Stream { - constructor(opt) { - super(); - - this.readable = true; - this._paused = false; - this._processing = false; - - this._hasher = crypto.createHash("sha1"); - opt = opt || {}; - - // base block size. - opt.block = opt.block || 256 * 1024; - - // Total number of bytes to emit - opt.total = opt.total || 256 * 1024 * 1024; - this._remaining = opt.total; - - // How variable to make the block sizes - opt.jitter = opt.jitter || 1024; - - this._opt = opt; - - this._process = this._process.bind(this); - - process.nextTick(this._process); - } - - pause() { - this._paused = true; - this.emit("pause"); - } - - resume() { - // console.error("rrs resume"); - this._paused = false; - this.emit("resume"); - this._process(); - } - - _process() { - if (this._processing) return; - if (this._paused) return; - - this._processing = true; - - if (!this._remaining) { - this._hash = this._hasher.digest("hex").toLowerCase().trim(); - this._processing = false; - - this.emit("end"); - return; - } - - // Figure out how many bytes to output - // if finished, then just emit end. - let block = this._opt.block; - const jitter = this._opt.jitter; - if (jitter) { - block += Math.ceil(Math.random() * jitter - jitter / 2); - } - block = Math.min(block, this._remaining); - const buf = Buffer.allocUnsafe(block); - for (let i = 0; i < block; i++) { - buf[i] = Math.random() * 256; - } - - this._hasher.update(buf); - - this._remaining -= block; - - this._processing = false; - - this.emit("data", buf); - process.nextTick(this._process); - } -} - -// A filter that just verifies a shasum -class HashStream extends Stream { - constructor() { - super(); - this.readable = this.writable = true; - this._hasher = crypto.createHash("sha1"); - } - - write(c) { - // Simulate the way that an fs.ReadStream returns false - // on *every* write, only to resume a moment later. - this._hasher.update(c); - process.nextTick(() => this.resume()); - return false; - } - - resume() { - this.emit("resume"); - process.nextTick(() => this.emit("drain")); - } - - end(c) { - if (c) { - this.write(c); - } - this._hash = this._hasher.digest("hex").toLowerCase().trim(); - this.emit("data", this._hash); - this.emit("end"); - } -} - -test("zlib random byte pipes", async () => { - const compressionMethods = [ - [zlib.createGzip, zlib.createGunzip], - [zlib.createBrotliCompress, zlib.createBrotliDecompress], - ]; - - for (const [createCompress, createDecompress] of compressionMethods) { - const inp = new RandomReadStream({ total: 1024, block: 256, jitter: 16 }); - const out = new HashStream(); - const gzip = createCompress(); - const gunz = createDecompress(); - - inp.pipe(gzip).pipe(gunz).pipe(out); - - await new Promise(resolve => { - out.on("data", c => { - expect(c).toBe(inp._hash); - resolve(); - }); - }); - } -}); - -//<#END_FILE: test-zlib-random-byte-pipes.js diff --git a/test/js/node/test/parallel/zlib-reset-before-write.test.js b/test/js/node/test/parallel/zlib-reset-before-write.test.js deleted file mode 100644 index fd997b8a51..0000000000 --- a/test/js/node/test/parallel/zlib-reset-before-write.test.js +++ /dev/null @@ -1,46 +0,0 @@ -//#FILE: test-zlib-reset-before-write.js -//#SHA1: 44561d35a5b7e4fc363d7dbde7ec6891af1f338a -//----------------- -"use strict"; -const zlib = require("zlib"); - -// Tests that zlib streams support .reset() and .params() -// before the first write. That is important to ensure that -// lazy init of zlib native library handles these cases. - -const testCases = [ - (z, cb) => { - z.reset(); - cb(); - }, - (z, cb) => z.params(0, zlib.constants.Z_DEFAULT_STRATEGY, cb), -]; - -testCases.forEach((fn, index) => { - test(`zlib stream supports ${index === 0 ? ".reset()" : ".params()"} before first write`, done => { - const deflate = zlib.createDeflate(); - const inflate = zlib.createInflate(); - - deflate.pipe(inflate); - - const output = []; - inflate - .on("error", err => { - expect(err).toBeFalsy(); - }) - .on("data", chunk => output.push(chunk)) - .on("end", () => { - expect(Buffer.concat(output).toString()).toBe("abc"); - done(); - }); - - fn(deflate, () => { - fn(inflate, () => { - deflate.write("abc"); - deflate.end(); - }); - }); - }); -}); - -//<#END_FILE: test-zlib-reset-before-write.js diff --git a/test/js/node/test/parallel/zlib-sync-no-event.test.js b/test/js/node/test/parallel/zlib-sync-no-event.test.js deleted file mode 100644 index 9e821acfe9..0000000000 --- a/test/js/node/test/parallel/zlib-sync-no-event.test.js +++ /dev/null @@ -1,39 +0,0 @@ -//#FILE: test-zlib-sync-no-event.js -//#SHA1: 382796f607eb25a85aa067e0dbc3d5103d321def -//----------------- -"use strict"; -const zlib = require("zlib"); - -const message = "Come on, Fhqwhgads."; -const buffer = Buffer.from(message); - -test("zlib sync compression and decompression without events", () => { - const zipper = new zlib.Gzip(); - const closeSpy = jest.fn(); - zipper.on("close", closeSpy); - - const zipped = zipper._processChunk(buffer, zlib.constants.Z_FINISH); - - const unzipper = new zlib.Gunzip(); - const unzipperCloseSpy = jest.fn(); - unzipper.on("close", unzipperCloseSpy); - - const unzipped = unzipper._processChunk(zipped, zlib.constants.Z_FINISH); - - expect(zipped).toEqual( - // prettier-ignore - Buffer.from([ 31, 139, 8, 0, 0, 0, 0, 0, 0, osbyte(), 115, 206, 207, 77, 85, 200, 207, 211, 81, 112, 203, 40, 44, 207, 72, 79, 76, 41, 214, 3, 0, 160, 120, 128, 220, 19, 0, 0, 0 ]), - ); - expect(unzipped.toString()).toEqual(message); - - expect(closeSpy).not.toHaveBeenCalled(); - expect(unzipperCloseSpy).not.toHaveBeenCalled(); -}); - -//<#END_FILE: test-zlib-sync-no-event.js - -function osbyte() { - if (process.platform === "darwin") return 19; - if (process.platform === "linux") return 3; - if (process.platform === "win32") return 10; -} diff --git a/test/js/node/test/parallel/zlib-truncated.test.js b/test/js/node/test/parallel/zlib-truncated.test.js deleted file mode 100644 index 703ce49c04..0000000000 --- a/test/js/node/test/parallel/zlib-truncated.test.js +++ /dev/null @@ -1,99 +0,0 @@ -//#FILE: test-zlib-truncated.js -//#SHA1: 79f9bcf3c52b3d0736ebe457652d579a856d1f7b -//----------------- -"use strict"; -// Tests zlib streams with truncated compressed input - -const zlib = require("zlib"); - -const inputString = - "ΩΩLorem ipsum dolor sit amet, consectetur adipiscing eli" + - "t. Morbi faucibus, purus at gravida dictum, libero arcu " + - "convallis lacus, in commodo libero metus eu nisi. Nullam" + - " commodo, neque nec porta placerat, nisi est fermentum a" + - "ugue, vitae gravida tellus sapien sit amet tellus. Aenea" + - "n non diam orci. Proin quis elit turpis. Suspendisse non" + - " diam ipsum. Suspendisse nec ullamcorper odio. Vestibulu" + - "m arcu mi, sodales non suscipit id, ultrices ut massa. S" + - "ed ac sem sit amet arcu malesuada fermentum. Nunc sed. "; - -const errMessage = /unexpected end of file/; - -const methods = [ - { comp: "gzip", decomp: "gunzip", decompSync: "gunzipSync" }, - { comp: "gzip", decomp: "unzip", decompSync: "unzipSync" }, - { comp: "deflate", decomp: "inflate", decompSync: "inflateSync" }, - { comp: "deflateRaw", decomp: "inflateRaw", decompSync: "inflateRawSync" }, -]; - -methods.forEach(({ comp, decomp, decompSync }) => { - test(`Test ${comp} and ${decomp}`, async () => { - const compressed = await new Promise((resolve, reject) => { - zlib[comp](inputString, (err, result) => { - if (err) reject(err); - else resolve(result); - }); - }); - - const truncated = compressed.slice(0, compressed.length / 2); - const toUTF8 = buffer => buffer.toString("utf-8"); - - // sync sanity - const decompressed = zlib[decompSync](compressed); - expect(toUTF8(decompressed)).toBe(inputString); - - // async sanity - await new Promise((resolve, reject) => { - zlib[decomp](compressed, (err, result) => { - if (err) reject(err); - else { - expect(toUTF8(result)).toBe(inputString); - resolve(); - } - }); - }); - - // Sync truncated input test - expect(() => { - zlib[decompSync](truncated); - }).toThrow( - expect.objectContaining({ - message: expect.stringMatching(errMessage), - }), - ); - - // Async truncated input test - await expect( - new Promise((resolve, reject) => { - zlib[decomp](truncated, err => { - if (err) reject(err); - else resolve(); - }); - }), - ).rejects.toThrow( - expect.objectContaining({ - message: expect.stringMatching(errMessage), - }), - ); - - const syncFlushOpt = { finishFlush: zlib.constants.Z_SYNC_FLUSH }; - - // Sync truncated input test, finishFlush = Z_SYNC_FLUSH - const result = toUTF8(zlib[decompSync](truncated, syncFlushOpt)); - expect(result).toBe(inputString.slice(0, result.length)); - - // Async truncated input test, finishFlush = Z_SYNC_FLUSH - await new Promise((resolve, reject) => { - zlib[decomp](truncated, syncFlushOpt, (err, decompressed) => { - if (err) reject(err); - else { - const result = toUTF8(decompressed); - expect(result).toBe(inputString.slice(0, result.length)); - resolve(); - } - }); - }); - }); -}); - -//<#END_FILE: test-zlib-truncated.js diff --git a/test/js/node/test/parallel/zlib-unzip-one-byte-chunks.test.js b/test/js/node/test/parallel/zlib-unzip-one-byte-chunks.test.js deleted file mode 100644 index f4fa2a0795..0000000000 --- a/test/js/node/test/parallel/zlib-unzip-one-byte-chunks.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-zlib-unzip-one-byte-chunks.js -//#SHA1: 3c242140501ae0e8e9277c68696c231a04070018 -//----------------- -"use strict"; -const zlib = require("zlib"); - -test("zlib unzip one byte chunks", done => { - const data = Buffer.concat([zlib.gzipSync("abc"), zlib.gzipSync("def")]); - - const resultBuffers = []; - - const unzip = zlib - .createUnzip() - .on("error", err => { - expect(err).toBeFalsy(); - }) - .on("data", data => resultBuffers.push(data)) - .on("finish", () => { - const unzipped = Buffer.concat(resultBuffers).toString(); - expect(unzipped).toBe("abcdef"); - done(); - }); - - for (let i = 0; i < data.length; i++) { - // Write each single byte individually. - unzip.write(Buffer.from([data[i]])); - } - - unzip.end(); -}); - -//<#END_FILE: test-zlib-unzip-one-byte-chunks.js diff --git a/test/js/node/test/parallel/zlib-write-after-close.test.js b/test/js/node/test/parallel/zlib-write-after-close.test.js deleted file mode 100644 index a778b7731c..0000000000 --- a/test/js/node/test/parallel/zlib-write-after-close.test.js +++ /dev/null @@ -1,52 +0,0 @@ -//#FILE: test-zlib-write-after-close.js -//#SHA1: 5e09b22ace4e546969c9d31f686446adda15fbd2 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; - -const zlib = require("node:zlib"); - -test("zlib should not allow writing after close", async () => { - const closeCallback = jest.fn(); - - await new Promise(resolve => { - zlib.gzip("hello", function () { - const unzip = zlib.createGunzip(); - unzip.close(closeCallback); - unzip.write("asd", function (err) { - expect(err).toEqual( - expect.objectContaining({ - code: "ERR_STREAM_DESTROYED", - name: "Error", - message: "Cannot call write after a stream was destroyed", - }), - ); - resolve(); - }); - }); - }); - - expect(closeCallback).toHaveBeenCalledTimes(1); -}); - -//<#END_FILE: test-zlib-write-after-close.js diff --git a/test/js/node/test/parallel/zlib-write-after-end.test.js b/test/js/node/test/parallel/zlib-write-after-end.test.js deleted file mode 100644 index c4e0586e65..0000000000 --- a/test/js/node/test/parallel/zlib-write-after-end.test.js +++ /dev/null @@ -1,29 +0,0 @@ -//#FILE: test-zlib-write-after-end.js -//#SHA1: 0d11ed6c9992b52c81a45bdb3d6fe0db4ab2681a -//----------------- -"use strict"; -const zlib = require("zlib"); - -// Regression test for https://github.com/nodejs/node/issues/30976 -// Writes to a stream should finish even after the readable side has been ended. - -test("zlib write after end", done => { - const data = zlib.deflateRawSync("Welcome"); - - const inflate = zlib.createInflateRaw(); - - inflate.resume(); - - const writeCallback = jest.fn(); - - inflate.write(data, writeCallback); - inflate.write(Buffer.from([0x00]), writeCallback); - inflate.write(Buffer.from([0x00]), writeCallback); - - inflate.flush(() => { - expect(writeCallback).toHaveBeenCalledTimes(3); - done(); - }); -}); - -//<#END_FILE: test-zlib-write-after-end.js diff --git a/test/js/node/test/parallel/zlib-write-after-flush.test.js b/test/js/node/test/parallel/zlib-write-after-flush.test.js deleted file mode 100644 index 3bdcfb8d4f..0000000000 --- a/test/js/node/test/parallel/zlib-write-after-flush.test.js +++ /dev/null @@ -1,57 +0,0 @@ -//#FILE: test-zlib-write-after-flush.js -//#SHA1: 2cd2c91ba7a105560cdf6831ece0e95174aac860 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const zlib = require("zlib"); - -const compressionMethods = [ - [zlib.createGzip, zlib.createGunzip], - [zlib.createBrotliCompress, zlib.createBrotliDecompress], -]; - -compressionMethods.forEach(([createCompress, createDecompress]) => { - test(`${createCompress.name} and ${createDecompress.name}`, done => { - const gzip = createCompress(); - const gunz = createDecompress(); - - gzip.pipe(gunz); - - let output = ""; - const input = "A line of data\n"; - gunz.setEncoding("utf8"); - gunz.on("data", c => (output += c)); - gunz.on("end", () => { - expect(output).toBe(input); - done(); - }); - - // Make sure that flush/write doesn't trigger an assert failure - gzip.flush(); - gzip.write(input); - gzip.end(); - gunz.read(0); - }); -}); - -//<#END_FILE: test-zlib-write-after-flush.js diff --git a/test/js/node/test/parallel/zlib-zero-byte.test.js b/test/js/node/test/parallel/zlib-zero-byte.test.js deleted file mode 100644 index 762ee875f2..0000000000 --- a/test/js/node/test/parallel/zlib-zero-byte.test.js +++ /dev/null @@ -1,56 +0,0 @@ -//#FILE: test-zlib-zero-byte.js -//#SHA1: 54539c28619fb98230547ba0929ddad146f15bc5 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const zlib = require("zlib"); - -for (const Compressor of [zlib.Gzip, zlib.BrotliCompress]) { - test(`${Compressor.name} compresses zero-byte input`, async () => { - const gz = Compressor(); - const emptyBuffer = Buffer.alloc(0); - let received = 0; - - gz.on("data", c => { - received += c.length; - }); - - const endPromise = new Promise(resolve => { - gz.on("end", resolve); - }); - - const finishPromise = new Promise(resolve => { - gz.on("finish", resolve); - }); - - gz.write(emptyBuffer); - gz.end(); - - await Promise.all([endPromise, finishPromise]); - - const expected = Compressor === zlib.Gzip ? 20 : 1; - expect(received).toBe(expected); - }); -} - -//<#END_FILE: test-zlib-zero-byte.js diff --git a/test/js/node/test/parallel/zlib-zero-windowbits.test.js b/test/js/node/test/parallel/zlib-zero-windowbits.test.js deleted file mode 100644 index 32f4225a30..0000000000 --- a/test/js/node/test/parallel/zlib-zero-windowbits.test.js +++ /dev/null @@ -1,32 +0,0 @@ -//#FILE: test-zlib-zero-windowBits.js -//#SHA1: 3f1f031d2f5ab37f2ea2a963d6de0e2ececa9d33 -//----------------- -"use strict"; - -const zlib = require("zlib"); - -// windowBits is a special case in zlib. On the compression side, 0 is invalid. -// On the decompression side, it indicates that zlib should use the value from -// the header of the compressed stream. -test("windowBits 0 for decompression", () => { - const inflate = zlib.createInflate({ windowBits: 0 }); - expect(inflate).toBeInstanceOf(zlib.Inflate); - - const gunzip = zlib.createGunzip({ windowBits: 0 }); - expect(gunzip).toBeInstanceOf(zlib.Gunzip); - - const unzip = zlib.createUnzip({ windowBits: 0 }); - expect(unzip).toBeInstanceOf(zlib.Unzip); -}); - -test("windowBits 0 for compression throws error", () => { - expect(() => zlib.createGzip({ windowBits: 0 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - message: expect.any(String), - }), - ); -}); - -//<#END_FILE: test-zlib-zero-windowBits.js diff --git a/test/js/node/test/parallel/zlib.test.js b/test/js/node/test/parallel/zlib.test.js deleted file mode 100644 index d6b27408a1..0000000000 --- a/test/js/node/test/parallel/zlib.test.js +++ /dev/null @@ -1,233 +0,0 @@ -//#FILE: test-zlib.js -//#SHA1: 0e67da3898d627175ffca51fdbd1042571d0c405 -//----------------- -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -"use strict"; -const zlib = require("zlib"); -const stream = require("stream"); -const fs = require("fs"); -const path = require("path"); - -const fixturesPath = path.join(__dirname, "..", "fixtures"); - -// Should not segfault. -test("gzipSync with invalid windowBits", () => { - expect(() => zlib.gzipSync(Buffer.alloc(0), { windowBits: 8 })).toThrow( - expect.objectContaining({ - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - }), - ); -}); - -let zlibPairs = [ - [zlib.Deflate, zlib.Inflate], - [zlib.Gzip, zlib.Gunzip], - [zlib.Deflate, zlib.Unzip], - [zlib.Gzip, zlib.Unzip], - [zlib.DeflateRaw, zlib.InflateRaw], - [zlib.BrotliCompress, zlib.BrotliDecompress], -]; - -// How fast to trickle through the slowstream -let trickle = [128, 1024, 1024 * 1024]; - -// Tunable options for zlib classes. - -// several different chunk sizes -let chunkSize = [128, 1024, 1024 * 16, 1024 * 1024]; - -// This is every possible value. -let level = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; -let windowBits = [8, 9, 10, 11, 12, 13, 14, 15]; -let memLevel = [1, 2, 3, 4, 5, 6, 7, 8, 9]; -let strategy = [0, 1, 2, 3, 4]; - -// It's nice in theory to test every combination, but it -// takes WAY too long. Maybe a pummel test could do this? -if (!process.env.PUMMEL) { - trickle = [1024]; - chunkSize = [1024 * 16]; - level = [6]; - memLevel = [8]; - windowBits = [15]; - strategy = [0]; -} - -let testFiles = ["person.jpg", "elipses.txt", "empty.txt"]; - -if (process.env.FAST) { - zlibPairs = [[zlib.Gzip, zlib.Unzip]]; - testFiles = ["person.jpg"]; -} - -const tests = {}; -testFiles.forEach(file => { - tests[file] = fs.readFileSync(path.join(fixturesPath, file)); -}); - -// Stream that saves everything -class BufferStream extends stream.Stream { - constructor() { - super(); - this.chunks = []; - this.length = 0; - this.writable = true; - this.readable = true; - } - - write(c) { - this.chunks.push(c); - this.length += c.length; - return true; - } - - end(c) { - if (c) this.write(c); - // flatten - const buf = Buffer.allocUnsafe(this.length); - let i = 0; - this.chunks.forEach(c => { - c.copy(buf, i); - i += c.length; - }); - this.emit("data", buf); - this.emit("end"); - return true; - } -} - -class SlowStream extends stream.Stream { - constructor(trickle) { - super(); - this.trickle = trickle; - this.offset = 0; - this.readable = this.writable = true; - } - - write() { - throw new Error("not implemented, just call ss.end(chunk)"); - } - - pause() { - this.paused = true; - this.emit("pause"); - } - - resume() { - const emit = () => { - if (this.paused) return; - if (this.offset >= this.length) { - this.ended = true; - return this.emit("end"); - } - const end = Math.min(this.offset + this.trickle, this.length); - const c = this.chunk.slice(this.offset, end); - this.offset += c.length; - this.emit("data", c); - process.nextTick(emit); - }; - - if (this.ended) return; - this.emit("resume"); - if (!this.chunk) return; - this.paused = false; - emit(); - } - - end(chunk) { - // Walk over the chunk in blocks. - this.chunk = chunk; - this.length = chunk.length; - this.resume(); - return this.ended; - } -} - -test("createDeflateRaw with windowBits 8", () => { - expect(() => zlib.createDeflateRaw({ windowBits: 8 })).not.toThrow(); -}); - -test("inflate raw with windowBits 8", async () => { - const node = fs.createReadStream(path.join(fixturesPath, "person.jpg")); - const raw = []; - const reinflated = []; - - await new Promise((resolve, reject) => { - node.on("data", chunk => raw.push(chunk)); - - node - .pipe(zlib.createDeflateRaw({ windowBits: 9 })) - .pipe(zlib.createInflateRaw({ windowBits: 8 })) - .on("data", chunk => reinflated.push(chunk)) - .on("end", () => { - expect(Buffer.concat(raw)).toEqual(Buffer.concat(reinflated)); - resolve(); - }) - .on("error", reject); - }); -}); - -// For each of the files, make sure that compressing and -// decompressing results in the same data, for every combination -// of the options set above. - -const testKeys = Object.keys(tests); -testKeys.forEach(file => { - const test = tests[file]; - chunkSize.forEach(chunkSize => { - trickle.forEach(trickle => { - windowBits.forEach(windowBits => { - level.forEach(level => { - memLevel.forEach(memLevel => { - strategy.forEach(strategy => { - zlibPairs.forEach(pair => { - const [Def, Inf] = pair; - const opts = { level, windowBits, memLevel, strategy }; - - it(`${file} ${chunkSize} ${JSON.stringify(opts)} ${Def.name} -> ${Inf.name}`, done => { - const def = new Def(opts); - const inf = new Inf(opts); - const ss = new SlowStream(trickle); - const buf = new BufferStream(); - - // Verify that the same exact buffer comes out the other end. - buf.on("data", c => { - expect(c).toEqual(test); - done(); - }); - - // The magic happens here. - ss.pipe(def).pipe(inf).pipe(buf); - ss.end(test); - }); - }); - }); - }); - }); - }); - }); - }); -}); - -//<#END_FILE: test-zlib.js diff --git a/test/js/node/tls/node-tls-connect.test.ts b/test/js/node/tls/node-tls-connect.test.ts index 8ecbb11bb6..0852df396e 100644 --- a/test/js/node/tls/node-tls-connect.test.ts +++ b/test/js/node/tls/node-tls-connect.test.ts @@ -4,8 +4,10 @@ import net from "net"; import { join } from "path"; import tls, { checkServerIdentity, connect as tlsConnect, TLSSocket } from "tls"; import stream from "stream"; +import { once } from "events"; import { Duplex } from "node:stream"; +import type { AddressInfo } from "net"; const symbolConnectOptions = Symbol.for("::buntlsconnectoptions::"); @@ -117,6 +119,22 @@ it("should have checkServerIdentity", async () => { expect(checkServerIdentity).toBeFunction(); expect(tls.checkServerIdentity).toBeFunction(); }); + +it("should thow ECONNRESET if FIN is received before handshake", async () => { + await using server = net.createServer(c => { + c.end(); + }); + await once(server.listen(0, "127.0.0.1"), "listening"); + const { promise, resolve } = Promise.withResolvers(); + tls.connect((server.address() as AddressInfo).port).on("error", resolve); + + const error = await promise; + + expect(error).toBeDefined(); + // TODO: today we are a little incompatible with node.js we need to change `UNABLE_TO_GET_ISSUER_CERT` when closed before handshake complete on the openssl.c to emit error `ECONNRESET` instead of SSL fail, + // current behavior is not wrong because is the right error but is incompatible with node.js + expect((error as Error).code as string).toBeOneOf(["ECONNRESET", "UNABLE_TO_GET_ISSUER_CERT"]); +}); it("should be able to grab the JSStreamSocket constructor", () => { // this keep http2-wrapper compatibility with node.js const socket = new tls.TLSSocket(new stream.PassThrough()); diff --git a/test/js/node/tls/node-tls-upgrade.test.ts b/test/js/node/tls/node-tls-upgrade.test.ts new file mode 100644 index 0000000000..a48f5c3e77 --- /dev/null +++ b/test/js/node/tls/node-tls-upgrade.test.ts @@ -0,0 +1,62 @@ +import net from "net"; +import tls from "tls"; +import { once } from "events"; +import { tls as certs } from "harness"; +import { test, expect } from "bun:test"; + +test("should be able to upgrade a paused socket and also have backpressure on it #15438", async () => { + // enought to trigger backpressure + const payload = Buffer.alloc(16 * 1024 * 4, "b").toString("utf8"); + + const server = tls.createServer(certs, socket => { + // echo + socket.on("data", data => { + socket.write(data); + }); + }); + + await once(server.listen(0, "127.0.0.1"), "listening"); + + const socket = net.connect({ + port: (server.address() as net.AddressInfo).port, + host: "127.0.0.1", + }); + await once(socket, "connect"); + + // pause raw socket + socket.pause(); + + const tlsSocket = tls.connect({ + ca: certs.cert, + servername: "localhost", + socket, + }); + await once(tlsSocket, "secureConnect"); + + // do http request using tls socket + async function doWrite(socket: net.Socket) { + let downloadedBody = 0; + const { promise, resolve, reject } = Promise.withResolvers(); + function onData(data: Buffer) { + downloadedBody += data.byteLength; + if (downloadedBody === payload.length * 2) { + resolve(); + } + } + socket.pause(); + socket.write(payload); + socket.write(payload, () => { + socket.on("data", onData); + socket.resume(); + }); + + await promise; + socket.off("data", onData); + } + for (let i = 0; i < 100; i++) { + // upgrade the tlsSocket + await doWrite(tlsSocket); + } + + expect().pass(); +}); diff --git a/test/js/third_party/postgres/postgres.test.ts b/test/js/third_party/postgres/postgres.test.ts index ef60a79989..3800a062bc 100644 --- a/test/js/third_party/postgres/postgres.test.ts +++ b/test/js/third_party/postgres/postgres.test.ts @@ -11,7 +11,30 @@ describe.skipIf(!databaseUrl)("postgres", () => { const [{ version }] = await sql`SELECT version()`; expect(version).toMatch(/PostgreSQL/); } finally { - sql.end(); + await sql.end(); + } + }); + + test("should be able to resume after backpressure pause on upgraded handler #15438", async () => { + const sql = postgres(databaseUrl!); + try { + const batch = []; + for (let i = 0; i < 1000; i++) { + batch.push( + (async sql => { + const [{ version }] = await sql`SELECT version()`; + expect(version).toMatch(/PostgreSQL/); + })(sql), + ); + if (batch.length === 50) { + await Promise.all(batch); + } + } + if (batch.length > 0) { + await Promise.all(batch); + } + } finally { + await sql.end(); } }); diff --git a/test/js/third_party/svelte/bun-loader-svelte.ts b/test/js/third_party/svelte/bun-loader-svelte.ts index c30a7bd11e..e90562b38f 100644 --- a/test/js/third_party/svelte/bun-loader-svelte.ts +++ b/test/js/third_party/svelte/bun-loader-svelte.ts @@ -11,7 +11,8 @@ await plugin({ readFileSync(path.substring(0, path.includes("?") ? path.indexOf("?") : path.length), "utf-8"), { filename: path, - generate: "ssr", + generate: "server", + dev: false, }, ).js.code, loader: "js", diff --git a/test/js/third_party/svelte/svelte.test.ts b/test/js/third_party/svelte/svelte.test.ts index 67167ecbe0..05a56e4c42 100644 --- a/test/js/third_party/svelte/svelte.test.ts +++ b/test/js/third_party/svelte/svelte.test.ts @@ -1,12 +1,13 @@ import { describe, expect, it } from "bun:test"; +import { render as svelteRender } from "svelte/server"; import "./bun-loader-svelte"; describe("require", () => { it("SSRs `

Hello world!

` with Svelte", () => { const { default: App } = require("./hello.svelte"); - const { html } = App.render(); + const { body } = svelteRender(App); - expect(html).toBe("

Hello world!

"); + expect(body).toBe("

Hello world!

"); }); it("works if you require it 1,000 times", () => { @@ -14,7 +15,7 @@ describe("require", () => { Bun.unsafe.gcAggressionLevel(0); for (let i = 0; i < 1000; i++) { const { default: App } = require("./hello.svelte?r" + i); - expect(App.render).toBeFunction(); + expect(App).toBeFunction(); } Bun.gc(true); Bun.unsafe.gcAggressionLevel(prev); @@ -27,7 +28,7 @@ describe("dynamic import", () => { Bun.unsafe.gcAggressionLevel(0); for (let i = 0; i < 1000; i++) { const { default: App } = await import("./hello.svelte?i" + i); - expect(App.render).toBeFunction(); + expect(App).toBeFunction(); } Bun.gc(true); Bun.unsafe.gcAggressionLevel(prev); @@ -35,8 +36,7 @@ describe("dynamic import", () => { it("SSRs `

Hello world!

` with Svelte", async () => { const { default: App }: any = await import("./hello.svelte"); - const { html } = App.render(); - - expect(html).toBe("

Hello world!

"); + const { body } = svelteRender(App); + expect(body).toBe("

Hello world!

"); }); }); diff --git a/test/js/web/fetch/fetch-leak-test-fixture-5.js b/test/js/web/fetch/fetch-leak-test-fixture-5.js index 88d0b7c1df..afcdb6e31b 100644 --- a/test/js/web/fetch/fetch-leak-test-fixture-5.js +++ b/test/js/web/fetch/fetch-leak-test-fixture-5.js @@ -66,6 +66,20 @@ function getBody() { case "urlsearchparams": body = getURLSearchParams(); break; + case "iterator": + body = async function* iter() { + yield (cachedBody ??= getString()); + }; + break; + case "stream": + body = new ReadableStream({ + async pull(c) { + await Bun.sleep(10); + c.enqueue((cachedBody ??= getBuffer())); + c.close(); + }, + }); + break; default: throw new Error(`Invalid type: ${type}`); } @@ -85,7 +99,8 @@ try { { Bun.gc(true); - await Bun.sleep(10); + await Bun.sleep(100); + Bun.gc(true); const stats = getHeapStats(); expect(stats.Response || 0).toBeLessThanOrEqual(threshold); expect(stats.Promise || 0).toBeLessThanOrEqual(threshold); diff --git a/test/js/web/fetch/fetch-leak.test.ts b/test/js/web/fetch/fetch-leak.test.ts index 04b68de276..bf90582a79 100644 --- a/test/js/web/fetch/fetch-leak.test.ts +++ b/test/js/web/fetch/fetch-leak.test.ts @@ -99,7 +99,7 @@ describe("fetch doesn't leak", () => { } }); -describe.each(["FormData", "Blob", "Buffer", "String", "URLSearchParams"])("Sending %s", type => { +describe.each(["FormData", "Blob", "Buffer", "String", "URLSearchParams", "stream", "iterator"])("Sending %s", type => { test( "does not leak", async () => { diff --git a/test/js/web/fetch/fetch.test.ts b/test/js/web/fetch/fetch.test.ts index 37818cd03c..47099852e8 100644 --- a/test/js/web/fetch/fetch.test.ts +++ b/test/js/web/fetch/fetch.test.ts @@ -6,7 +6,7 @@ import { mkfifo } from "mkfifo"; import net from "net"; import { join } from "path"; import { gzipSync } from "zlib"; - +import { Readable } from "stream"; const tmp_dir = tmpdirSync(); const fixture = readFileSync(join(import.meta.dir, "fetch.js.txt"), "utf8").replaceAll("\r\n", "\n"); @@ -2074,3 +2074,188 @@ describe("fetch Response life cycle", () => { } }); }); + +describe("fetch should allow duplex", () => { + it("should allow duplex streaming", async () => { + using server = Bun.serve({ + port: 0, + async fetch(req) { + return new Response(req.body); + }, + }); + const intervalStream = new ReadableStream({ + start(c) { + let count = 0; + const timer = setInterval(() => { + c.enqueue("Hello\n"); + if (count === 5) { + clearInterval(timer); + c.close(); + } + count++; + }, 20); + }, + }).pipeThrough(new TextEncoderStream()); + + const resp = await fetch(server.url, { + method: "POST", + body: intervalStream, + duplex: "half", + }); + + const reader = resp.body.pipeThrough(new TextDecoderStream()).getReader(); + var result = ""; + while (true) { + const { value, done } = await reader.read(); + if (done) break; + result += value; + } + expect(result).toBe("Hello\n".repeat(6)); + }); + + it("should allow duplex extending Readable (sync)", async () => { + class HelloWorldStream extends Readable { + constructor(options) { + super(options); + this.chunks = ["Hello", " ", "World!"]; + this.index = 0; + } + + _read(size) { + if (this.index < this.chunks.length) { + this.push(this.chunks[this.index]); + this.index++; + } else { + this.push(null); + } + } + } + + using server = Bun.serve({ + port: 0, + async fetch(req) { + return new Response(req.body); + }, + }); + const response = await fetch(server.url, { + body: new HelloWorldStream(), + method: "POST", + duplex: "half", + }); + + expect(await response.text()).toBe("Hello World!"); + }); + it("should allow duplex extending Readable (async)", async () => { + class HelloWorldStream extends Readable { + constructor(options) { + super(options); + this.chunks = ["Hello", " ", "World!"]; + this.index = 0; + } + + _read(size) { + setTimeout(() => { + if (this.index < this.chunks.length) { + this.push(this.chunks[this.index]); + this.index++; + } else { + this.push(null); + } + }, 20); + } + } + + using server = Bun.serve({ + port: 0, + async fetch(req) { + return new Response(req.body); + }, + }); + const response = await fetch(server.url, { + body: new HelloWorldStream(), + method: "POST", + duplex: "half", + }); + + expect(await response.text()).toBe("Hello World!"); + }); + + it("should allow duplex using async iterator (async)", async () => { + using server = Bun.serve({ + port: 0, + async fetch(req) { + return new Response(req.body); + }, + }); + const response = await fetch(server.url, { + body: async function* iter() { + yield "Hello"; + await Bun.sleep(20); + yield " "; + await Bun.sleep(20); + yield "World!"; + }, + method: "POST", + duplex: "half", + }); + + expect(await response.text()).toBe("Hello World!"); + }); + + it("should fail in redirects .follow when using duplex", async () => { + using server = Bun.serve({ + port: 0, + async fetch(req) { + if (req.url.indexOf("/redirect") === -1) { + return Response.redirect("/"); + } + return new Response(req.body); + }, + }); + + expect(async () => { + const response = await fetch(server.url, { + body: async function* iter() { + yield "Hello"; + await Bun.sleep(20); + yield " "; + await Bun.sleep(20); + yield "World!"; + }, + method: "POST", + duplex: "half", + }); + + await response.text(); + }).toThrow(); + }); + + it("should work in redirects .manual when using duplex", async () => { + using server = Bun.serve({ + port: 0, + async fetch(req) { + if (req.url.indexOf("/redirect") === -1) { + return Response.redirect("/"); + } + return new Response(req.body); + }, + }); + + expect(async () => { + const response = await fetch(server.url, { + body: async function* iter() { + yield "Hello"; + await Bun.sleep(20); + yield " "; + await Bun.sleep(20); + yield "World!"; + }, + method: "POST", + duplex: "half", + redirect: "manual", + }); + + await response.text(); + }).not.toThrow(); + }); +}); diff --git a/test/package.json b/test/package.json index 03a8717ce3..f643ef682d 100644 --- a/test/package.json +++ b/test/package.json @@ -21,6 +21,7 @@ "axios": "1.6.8", "body-parser": "1.20.2", "comlink": "4.4.1", + "devalue": "5.1.1", "es-module-lexer": "1.3.0", "esbuild": "0.18.6", "express": "4.18.2", @@ -59,7 +60,7 @@ "string-width": "7.0.0", "stripe": "15.4.0", "supertest": "6.3.3", - "svelte": "3.55.1", + "svelte": "5.4.0", "typescript": "5.0.2", "undici": "5.20.0", "verdaccio": "6.0.0", diff --git a/test/regression/issue/ctrl-c.test.ts b/test/regression/issue/ctrl-c.test.ts index 6c336a2444..1a511a6df7 100644 --- a/test/regression/issue/ctrl-c.test.ts +++ b/test/regression/issue/ctrl-c.test.ts @@ -1,4 +1,4 @@ -import { expect, test } from "bun:test"; +import { expect, it, test } from "bun:test"; import { bunEnv, bunExe, isWindows, tempDirWithFiles } from "harness"; test.skipIf(isWindows)("verify that we forward SIGINT from parent to child in bun run", () => { @@ -32,3 +32,72 @@ test.skipIf(isWindows)("verify that we forward SIGINT from parent to child in bu expect(result.exitCode).toBe(null); expect(result.signalCode).toBe("SIGKILL"); }); + +for (const mode of [ + ["vite"], + ["dev"], + ...(isWindows ? [] : [["./node_modules/.bin/vite"]]), + ["--bun", "vite"], + ["--bun", "dev"], + ...(isWindows ? [] : [["--bun", "./node_modules/.bin/vite"]]), +]) { + it("kills on SIGINT in: 'bun " + mode.join(" ") + "'", async () => { + const dir = tempDirWithFiles("ctrlc", { + "package.json": JSON.stringify({ + name: "ctrlc", + scripts: { + "dev": "vite", + }, + devDependencies: { + "vite": "^6.0.1", + }, + }), + }); + expect( + Bun.spawnSync({ + cmd: [bunExe(), "install"], + cwd: dir, + stdin: "inherit", + stdout: "inherit", + stderr: "inherit", + }).exitCode, + ).toBe(0); + const proc = Bun.spawn({ + cmd: [bunExe(), ...mode], + cwd: dir, + stdin: "inherit", + stdout: "pipe", + stderr: "inherit", + env: { ...bunEnv, PORT: "9876" }, + }); + + // wait for vite to start + const reader = proc.stdout.getReader(); + await reader.read(); // wait for first bit of stdout + reader.releaseLock(); + + expect(proc.killed).toBe(false); + + // send sigint + process.kill(proc.pid, "SIGINT"); + + // wait for exit or 200ms + await Promise.race([proc.exited, Bun.sleep(200)]); + + // wait to allow a moment to be killed + await Bun.sleep(100); // wait for kill + expect({ + killed: proc.killed, + exitCode: proc.exitCode, + signalCode: proc.signalCode, + }).toEqual(isWindows ? { + killed: true, + exitCode: 1, + signalCode: null, + } : { + killed: true, + exitCode: null, + signalCode: "SIGINT", + }); + }); +}