diff --git a/.ci/oauth2/setup.sh b/.ci/oauth2/setup.sh
index 49af95fb16..8c11cee739 100755
--- a/.ci/oauth2/setup.sh
+++ b/.ci/oauth2/setup.sh
@@ -46,9 +46,9 @@ function start_rabbitmq
--network "$docker_network" \
--publish 5672:5672 \
--publish 15672:15672 \
- --volume "$GITHUB_WORKSPACE/projects/OAuth2Test/enabled_plugins:/etc/rabbitmq/enabled_plugins" \
- --volume "$GITHUB_WORKSPACE/projects/OAuth2Test/$mode/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro" \
- --volume "$GITHUB_WORKSPACE/projects/OAuth2Test/$mode/signing-key/signing-key.pem:/etc/rabbitmq/signing-key.pem:ro" \
+ --volume "$GITHUB_WORKSPACE/projects/Test/OAuth2/enabled_plugins:/etc/rabbitmq/enabled_plugins" \
+ --volume "$GITHUB_WORKSPACE/projects/Test/OAuth2/$mode/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro" \
+ --volume "$GITHUB_WORKSPACE/projects/Test/OAuth2/$mode/signing-key/signing-key.pem:/etc/rabbitmq/signing-key.pem:ro" \
rabbitmq:3-management
}
@@ -90,7 +90,7 @@ function start_oauth_service
--publish 8080:8080 \
--env 'UAA_CONFIG_PATH=/uaa' \
--env 'JAVA_OPTS=-Djava.security.egd=file:/dev/./urandom' \
- --volume "$GITHUB_WORKSPACE/projects/OAuth2Test/uaa:/uaa" \
+ --volume "$GITHUB_WORKSPACE/projects/Test/OAuth2/uaa:/uaa" \
"cloudfoundry/uaa:$uaa_image_version"
else
readonly keycloak_docker_name="$docker_name_prefix-keycloak"
@@ -101,7 +101,7 @@ function start_oauth_service
--env 'KEYCLOAK_ADMIN=admin' \
--env 'KEYCLOAK_ADMIN_PASSWORD=admin' \
--env KC_HEALTH_ENABLED=true \
- --volume "$GITHUB_WORKSPACE/projects/OAuth2Test/keycloak/import:/opt/keycloak/data/import" \
+ --volume "$GITHUB_WORKSPACE/projects/Test/OAuth2/keycloak/import:/opt/keycloak/data/import" \
"quay.io/keycloak/keycloak:$keycloak_image_version" start-dev --metrics-enabled=true --import-realm
fi
}
diff --git a/.ci/oauth2/test.sh b/.ci/oauth2/test.sh
index e99f5449fa..eda1f159b6 100755
--- a/.ci/oauth2/test.sh
+++ b/.ci/oauth2/test.sh
@@ -12,4 +12,4 @@ source "$script_dir/common.sh"
export OAUTH2_MODE="$mode"
-dotnet test --environment OAUTH2_MODE="$mode" "$GITHUB_WORKSPACE/projects/OAuth2Test/OAuth2Test.csproj" --logger "console;verbosity=detailed" --framework "net6.0"
+dotnet test --environment OAUTH2_MODE="$mode" "$GITHUB_WORKSPACE/projects/Test/OAuth2/OAuth2.csproj" --logger "console;verbosity=detailed"
diff --git a/.ci/ubuntu/rabbitmq.conf b/.ci/ubuntu/rabbitmq.conf
index ba2a758c03..af3f1224b1 100644
--- a/.ci/ubuntu/rabbitmq.conf
+++ b/.ci/ubuntu/rabbitmq.conf
@@ -1,6 +1,9 @@
log.console = false
+log.exchange = false
log.file = /var/log/rabbitmq/rabbitmq.log
-log.file.level = debug
+log.file.level = info
+log.connection.level = warning
+log.channel.level = warning
listeners.tcp.default = 5672
listeners.ssl.default = 5671
reverse_dns_lookups = false
diff --git a/.ci/windows/gha-run-tests.ps1 b/.ci/windows/gha-run-tests.ps1
deleted file mode 100644
index d75f977e57..0000000000
--- a/.ci/windows/gha-run-tests.ps1
+++ /dev/null
@@ -1,39 +0,0 @@
-$ProgressPreference = 'Continue'
-$ErrorActionPreference = 'Stop'
-Set-StrictMode -Version 2.0
-
-$erlang_reg_path = 'HKLM:\SOFTWARE\Ericsson\Erlang'
-if (Test-Path 'HKLM:\SOFTWARE\WOW6432Node\')
-{
- $erlang_reg_path = 'HKLM:\SOFTWARE\WOW6432Node\Ericsson\Erlang'
-}
-$erlang_erts_version = Get-ChildItem -Path $erlang_reg_path -Name
-$erlang_home = (Get-ItemProperty -LiteralPath $erlang_reg_path\$erlang_erts_version).'(default)'
-
-Write-Host "[INFO] Setting ERLANG_HOME to '$erlang_home'..."
-$env:ERLANG_HOME = $erlang_home
-[Environment]::SetEnvironmentVariable('ERLANG_HOME', $erlang_home, 'Machine')
-
-$rabbitmq_base_path = (Get-ItemProperty -Name Install_Dir -Path 'HKLM:\SOFTWARE\WOW6432Node\VMware, Inc.\RabbitMQ Server').Install_Dir
-$regPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\RabbitMQ'
-if (Test-Path 'HKLM:\SOFTWARE\WOW6432Node\')
-{
- $regPath = 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\RabbitMQ'
-}
-$rabbitmq_version = (Get-ItemProperty $regPath "DisplayVersion").DisplayVersion
-$rabbitmqctl_path = Resolve-Path -LiteralPath (Join-Path -Path $rabbitmq_base_path -ChildPath "rabbitmq_server-$rabbitmq_version" | Join-Path -ChildPath 'sbin' | Join-Path -ChildPath 'rabbitmqctl.bat')
-
-Write-Host "[INFO] Setting RABBITMQ_RABBITMQCTL_PATH to '$rabbitmqctl_path'..."
-$env:RABBITMQ_RABBITMQCTL_PATH = $rabbitmqctl_path
-[Environment]::SetEnvironmentVariable('RABBITMQ_RABBITMQCTL_PATH', $rabbitmqctl_path, 'Machine')
-
-New-Variable -Name ci_dir -Option Constant -Value (Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath '.ci')
-New-Variable -Name certs_dir -Option Constant -Value (Join-Path -Path $ci_dir -ChildPath 'certs')
-
-$csproj_file = Resolve-Path -LiteralPath (Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath 'projects' | Join-Path -ChildPath 'Unit' | Join-Path -ChildPath 'Unit.csproj')
-
-dotnet test --environment "RABBITMQ_RABBITMQCTL_PATH=$rabbitmqctl_path" `
- --environment 'RABBITMQ_LONG_RUNNING_TESTS=true' `
- --environment 'PASSWORD=grapefruit' `
- --environment "SSL_CERTS_DIR=$certs_dir" `
- $csproj_file --no-restore --no-build --logger "console;verbosity=detailed"
diff --git a/.ci/windows/gha-setup.ps1 b/.ci/windows/gha-setup.ps1
index 605b851250..2f1cf929c2 100644
--- a/.ci/windows/gha-setup.ps1
+++ b/.ci/windows/gha-setup.ps1
@@ -65,6 +65,7 @@ $erlang_home = (Get-ItemProperty -LiteralPath $erlang_reg_path\$erlang_erts_vers
Write-Host "[INFO] Setting ERLANG_HOME to '$erlang_home'..."
$env:ERLANG_HOME = $erlang_home
[Environment]::SetEnvironmentVariable('ERLANG_HOME', $erlang_home, 'Machine')
+Add-Content -Verbose -LiteralPath $env:GITHUB_ENV -Value "ERLANG_HOME=$erlang_home"
Write-Host "[INFO] Setting RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS..."
$env:RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS = '-rabbitmq_stream advertised_host localhost'
@@ -189,6 +190,17 @@ Write-Host '[INFO] Enabling plugins...'
& $rabbitmq_plugins_path enable rabbitmq_management rabbitmq_stream rabbitmq_stream_management rabbitmq_amqp1_0
echo Q | openssl s_client -connect localhost:5671 -CAfile "$certs_dir/ca_certificate.pem" -cert "$certs_dir/client_localhost_certificate.pem" -key "$certs_dir/client_localhost_key.pem" -pass pass:grapefruit
-if ($LASTEXITCODE -ne 0) {
+if ($LASTEXITCODE -ne 0)
+{
throw "[ERROR] 'openssl s_client' returned error: $LASTEXITCODE"
}
+
+
+$rabbitmqctl_path = Resolve-Path -LiteralPath `
+ (Join-Path -Path $rabbitmq_base_path -ChildPath "rabbitmq_server-$rabbitmq_version" | Join-Path -ChildPath 'sbin' | Join-Path -ChildPath 'rabbitmqctl.bat')
+
+Write-Host "[INFO] Setting RABBITMQ_RABBITMQCTL_PATH to '$rabbitmqctl_path'..."
+$env:RABBITMQ_RABBITMQCTL_PATH = $rabbitmqctl_path
+[Environment]::SetEnvironmentVariable('RABBITMQ_RABBITMQCTL_PATH', $rabbitmqctl_path, 'Machine')
+Add-Content -Verbose -LiteralPath $env:GITHUB_OUTPUT -Value "path=$rabbitmqctl_path"
+Add-Content -Verbose -LiteralPath $env:GITHUB_ENV -Value "RABBITMQ_RABBITMQCTL_PATH=$rabbitmqctl_path"
diff --git a/.ci/windows/rabbitmq.conf.in b/.ci/windows/rabbitmq.conf.in
index 0923dbe6c7..38c0af233c 100644
--- a/.ci/windows/rabbitmq.conf.in
+++ b/.ci/windows/rabbitmq.conf.in
@@ -1,5 +1,8 @@
log.console = false
-log.file.level = debug
+log.exchange = false
+log.file.level = info
+log.connection.level = warning
+log.channel.level = warning
listeners.tcp.default = 5672
listeners.ssl.default = 5671
reverse_dns_lookups = false
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 0000000000..a9209d0db8
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,5 @@
+# https://github.com/rabbitmq/rabbitmq-dotnet-client/commit/1713f50eb2dc52a97184f3857f70841dd55b5bef
+1713f50eb2dc52a97184f3857f70841dd55b5bef
+67c02d79d3ae48fea7de93c758dce91a51d14988
+# Revert the above
+6b1a06bd429f395891a3230cad92e674dcbbb0d2
diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml
index a7e4d6aa5b..4d37f03d7e 100644
--- a/.github/workflows/build-test.yaml
+++ b/.github/workflows/build-test.yaml
@@ -5,7 +5,7 @@ on:
jobs:
build-win32:
- name: build/test on windows-latest
+ name: build, unit test on windows-latest
runs-on: windows-latest
# https://github.com/NuGet/Home/issues/11548
env:
@@ -15,13 +15,6 @@ jobs:
uses: actions/checkout@v4
with:
submodules: true
- - name: Cache installers
- uses: actions/cache@v3
- with:
- # Note: the cache path is relative to the workspace directory
- # https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#using-the-cache-action
- path: ~/installers
- key: ${{ runner.os }}-v0-${{ hashFiles('.ci/versions.json') }}
- name: Cache NuGet packages
uses: actions/cache@v3
with:
@@ -31,24 +24,96 @@ jobs:
key: ${{ runner.os }}-v1-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-v1-nuget-
- - name: Install and Start RabbitMQ
- run: .\.ci\windows\gha-setup.ps1
- - name: List NuGet sources
- run: dotnet nuget locals all --list
- name: Build (Debug)
run: dotnet build ${{ github.workspace }}\Build.csproj
- name: Verify
- run: dotnet format ${{ github.workspace }}\RabbitMQDotNetClient.sln --verify-no-changes --verbosity=diagnostic
- - name: Test
- run: .\.ci\windows\gha-run-tests.ps1
+ run: dotnet format ${{ github.workspace }}\RabbitMQDotNetClient.sln --no-restore --verify-no-changes --verbosity=diagnostic
+ - name: APIApproval Test
+ run: dotnet test "${{ github.workspace }}\projects\Test\Unit\Unit.csproj" --no-restore --no-build --logger 'console;verbosity=detailed' --filter='FullyQualifiedName=Test.Unit.APIApproval.Approve'
+ - name: Unit Tests
+ run: dotnet test "${{ github.workspace }}\projects\Test\Unit\Unit.csproj" --no-restore --no-build --logger 'console;verbosity=detailed'
+ - name: Upload Build (Debug)
+ uses: actions/upload-artifact@v3
+ with:
+ name: rabbitmq-dotnet-client-build-win32
+ path: |
+ projects/Test/Unit/bin
+ projects/Test/Integration/bin
+ projects/Test/SequentialIntegration/bin
+ projects/RabbitMQ.*/bin
+ integration-win32:
+ name: integration test on windows-latest
+ needs: build-win32
+ runs-on: windows-latest
+ # https://github.com/NuGet/Home/issues/11548
+ env:
+ NUGET_CERT_REVOCATION_MODE: offline
+ steps:
+ - name: Clone repository
+ uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Cache installers
+ uses: actions/cache@v3
+ with:
+ # Note: the cache path is relative to the workspace directory
+ # https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#using-the-cache-action
+ path: ~/installers
+ key: ${{ runner.os }}-v0-${{ hashFiles('.ci/versions.json') }}
+ - name: Download Build (Debug)
+ uses: actions/download-artifact@v3
+ with:
+ name: rabbitmq-dotnet-client-build-win32
+ path: projects
+ - name: Install and Start RabbitMQ
+ id: install-start-rabbitmq
+ run: .\.ci\windows\gha-setup.ps1
+ - name: Integration Tests
+ run: dotnet test --environment "RABBITMQ_RABBITMQCTL_PATH=${{ steps.install-start-rabbitmq.outputs.path }}" --environment 'RABBITMQ_LONG_RUNNING_TESTS=false' --environment 'PASSWORD=grapefruit' --environment SSL_CERTS_DIR="${{ github.workspace }}\.ci\certs" "${{ github.workspace }}\projects\Test\Integration\Integration.csproj" --no-restore --no-build --logger 'console;verbosity=detailed'
+ - name: Maybe upload RabbitMQ logs
+ if: failure()
+ uses: actions/upload-artifact@v3
+ with:
+ name: rabbitmq-logs-integration-win32
+ path: ~/AppData/Roaming/RabbitMQ/log/
+ sequential-integration-win32:
+ name: sequential integration test on windows-latest
+ needs: build-win32
+ runs-on: windows-latest
+ # https://github.com/NuGet/Home/issues/11548
+ env:
+ NUGET_CERT_REVOCATION_MODE: offline
+ steps:
+ - name: Clone repository
+ uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Cache installers
+ uses: actions/cache@v3
+ with:
+ # Note: the cache path is relative to the workspace directory
+ # https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#using-the-cache-action
+ path: ~/installers
+ key: ${{ runner.os }}-v0-${{ hashFiles('.ci/versions.json') }}
+ - name: Download Build (Debug)
+ uses: actions/download-artifact@v3
+ with:
+ name: rabbitmq-dotnet-client-build-win32
+ path: projects
+ - name: Install and Start RabbitMQ
+ id: install-start-rabbitmq
+ run: .\.ci\windows\gha-setup.ps1
+ - name: Sequential Integration Tests
+ run: dotnet test --environment "RABBITMQ_RABBITMQCTL_PATH=${{ steps.install-start-rabbitmq.outputs.path }}" "${{ github.workspace }}\projects\Test\SequentialIntegration\SequentialIntegration.csproj" --no-restore --no-build --logger 'console;verbosity=detailed'
- name: Maybe upload RabbitMQ logs
if: failure()
uses: actions/upload-artifact@v3
with:
- name: rabbitmq-logs
+ name: rabbitmq-logs-sequential-integration-win32
path: ~/AppData/Roaming/RabbitMQ/log/
- build:
- name: build/test on ubuntu-latest
+
+ build-ubuntu:
+ name: build, unit test on ubuntu-latest
runs-on: ubuntu-latest
steps:
- name: Clone repository
@@ -68,26 +133,87 @@ jobs:
key: ${{ runner.os }}-v1-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-v1-nuget-
- - name: Start RabbitMQ
- id: start-rabbitmq
- run: ${{ github.workspace }}/.ci/ubuntu/gha-setup.sh
- - name: List NuGet sources
- run: dotnet nuget locals all --list
- name: Build (Debug)
run: dotnet build ${{ github.workspace }}/Build.csproj
- name: Verify
- run: dotnet format ${{ github.workspace }}/RabbitMQDotNetClient.sln --verify-no-changes --verbosity=diagnostic
- - name: Test
+ run: dotnet format ${{ github.workspace }}/RabbitMQDotNetClient.sln --no-restore --verify-no-changes --verbosity=diagnostic
+ - name: APIApproval Test
+ run: dotnet test "${{ github.workspace }}/projects/Test/Unit/Unit.csproj" --no-restore --no-build --logger 'console;verbosity=detailed' --filter='FullyQualifiedName=Test.Unit.APIApproval.Approve'
+ - name: Unit Tests
+ run: dotnet test "${{ github.workspace }}/projects/Test/Unit/Unit.csproj" --no-restore --no-build --verbosity=diagnostic --logger 'console;verbosity=detailed'
+ - name: Upload Build (Debug)
+ uses: actions/upload-artifact@v3
+ with:
+ name: rabbitmq-dotnet-client-build-ubuntu
+ path: |
+ projects/Test/Unit/bin
+ projects/Test/Integration/bin
+ projects/Test/SequentialIntegration/bin
+ projects/RabbitMQ.*/bin
+ integration-ubuntu:
+ name: integration test on ubuntu-latest
+ needs: build-ubuntu
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone repository
+ uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: 6.x
+ - name: Download Build (Debug)
+ uses: actions/download-artifact@v3
+ with:
+ name: rabbitmq-dotnet-client-build-ubuntu
+ path: projects
+ - name: Start RabbitMQ
+ id: start-rabbitmq
+ run: ${{ github.workspace }}/.ci/ubuntu/gha-setup.sh
+ - name: Integration Tests
run: |
dotnet test \
--environment "RABBITMQ_RABBITMQCTL_PATH=DOCKER:${{ steps.start-rabbitmq.outputs.id }}" \
- --environment 'RABBITMQ_LONG_RUNNING_TESTS=true' \
+ --environment 'RABBITMQ_LONG_RUNNING_TESTS=false' \
--environment 'PASSWORD=grapefruit' \
--environment SSL_CERTS_DIR="${{ github.workspace }}/.ci/certs" \
- "${{ github.workspace }}/projects/Unit/Unit.csproj" --no-restore --no-build --logger 'console;verbosity=detailed' --framework 'net6.0'
+ "${{ github.workspace }}/projects/Test/Integration/Integration.csproj" --no-restore --no-build --logger 'console;verbosity=detailed'
+ - name: Maybe upload RabbitMQ logs
+ if: failure()
+ uses: actions/upload-artifact@v3
+ with:
+ name: rabbitmq-logs-integration-ubuntu
+ path: ${{ github.workspace }}/.ci/ubuntu/log/
+ sequential-integration-ubuntu:
+ name: sequential integration test on ubuntu-latest
+ needs: build-ubuntu
+ runs-on: ubuntu-latest
+ steps:
+ - name: Clone repository
+ uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: 6.x
+ - name: Download Build (Debug)
+ uses: actions/download-artifact@v3
+ with:
+ name: rabbitmq-dotnet-client-build-ubuntu
+ path: projects
+ - name: Start RabbitMQ
+ id: start-rabbitmq
+ run: ${{ github.workspace }}/.ci/ubuntu/gha-setup.sh
+ - name: Sequential Integration Tests
+ run: |
+ dotnet test \
+ --environment "RABBITMQ_RABBITMQCTL_PATH=DOCKER:${{ steps.start-rabbitmq.outputs.id }}" \
+ "${{ github.workspace }}/projects/Test/SequentialIntegration/SequentialIntegration.csproj" --no-restore --no-build --logger 'console;verbosity=detailed'
- name: Maybe upload RabbitMQ logs
if: failure()
uses: actions/upload-artifact@v3
with:
- name: rabbitmq-logs
+ name: rabbitmq-logs-sequential-integration-ubuntu
path: ${{ github.workspace }}/.ci/ubuntu/log/
diff --git a/.gitignore b/.gitignore
index 19a14ef6f4..ff820f3350 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,8 +52,8 @@ build/
BenchmarkDotNet.Artifacts/*
-projects/Unit/APIApproval.Approve.received.txt
-projects/Unit/APIApproval.Approve.*.received.txt
+projects/Test/Unit/APIApproval.Approve.received.txt
+projects/Test/Unit/APIApproval.Approve.*.received.txt
# Visual Studio 2015 cache/options directory
.vs/
@@ -115,7 +115,7 @@ UpgradeLog*.htm
# Unit tests
-projects/Unit*/TestResult.xml
+projects/Test/Unit*/TestResult.xml
# Development scripts
diff --git a/Build.csproj b/Build.csproj
index b4c88b63a2..84aa80559a 100644
--- a/Build.csproj
+++ b/Build.csproj
@@ -9,10 +9,13 @@
-
-
-
-
+
+
+
+
+
+
+
diff --git a/RUNNING_TESTS.md b/RUNNING_TESTS.md
index a25a008221..f8b398ede4 100644
--- a/RUNNING_TESTS.md
+++ b/RUNNING_TESTS.md
@@ -71,7 +71,7 @@ in this example, it should be `./rabbitmq-server/deps/rabbit/sbin/rabbitmqctl`.
It is possible to override the location using `RABBITMQ_RABBITMQCTL_PATH`:
```
-RABBITMQ_RABBITMQCTL_PATH=/path/to/rabbitmqctl dotnet test projects/Unit
+RABBITMQ_RABBITMQCTL_PATH=/path/to/rabbitmqctl dotnet test projects/Test/Unit.csproj
```
### Option Three: Using a Docker Container
@@ -110,9 +110,9 @@ Running individual tests and fixtures on Windows is trivial using the Visual Stu
To run a specific tests fixture on MacOS or Linux, use the NUnit filter expressions to select the tests to be run:
``` shell
-dotnet test projects/Unit --filter "Name~TestAmqpUriParseFail"
+dotnet test projects/Test/Unit.csproj --filter "Name~TestAmqpUriParseFail"
-dotnet test projects/Unit --filter "FullyQualifiedName~RabbitMQ.Client.Unit.TestHeartbeats"
+dotnet test projects/Test/Unit.csproj --filter "FullyQualifiedName~RabbitMQ.Client.Unit.TestHeartbeats"
```
## Running Tests for a Specific .NET Target
diff --git a/RabbitMQDotNetClient.sln b/RabbitMQDotNetClient.sln
index 4d22b5e523..04d32e7562 100644
--- a/RabbitMQDotNetClient.sln
+++ b/RabbitMQDotNetClient.sln
@@ -9,19 +9,33 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RabbitMQ.Client", "projects\RabbitMQ.Client\RabbitMQ.Client.csproj", "{8C554257-5ECC-45DB-873D-560BFBB74EC8}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Unit", "projects\Unit\Unit.csproj", "{B8FAC024-CC03-4067-9FFC-02846FB8AE48}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Unit", "projects\Test\Unit\Unit.csproj", "{B8FAC024-CC03-4067-9FFC-02846FB8AE48}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks", "projects\Benchmarks\Benchmarks.csproj", "{38D72C9A-68E9-4653-B0CE-C7BA9FFD91D0}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MassPublish", "projects\TestApplications\MassPublish\MassPublish.csproj", "{0E3C4FBE-9976-40A3-9F57-DC0D9B7A39A6}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MassPublish", "projects\Test\Applications\MassPublish\MassPublish.csproj", "{0E3C4FBE-9976-40A3-9F57-DC0D9B7A39A6}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestApplications", "TestApplications", "{D21B282C-49E6-4A30-887B-9626D94B8D69}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Applications", "Applications", "{D21B282C-49E6-4A30-887B-9626D94B8D69}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateChannel", "projects\TestApplications\CreateChannel\CreateChannel.csproj", "{4A589408-F3A3-40E1-A6DF-F5E620F7CA31}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateChannel", "projects\Test\Applications\CreateChannel\CreateChannel.csproj", "{4A589408-F3A3-40E1-A6DF-F5E620F7CA31}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RabbitMQ.Client.OAuth2", "projects\RabbitMQ.Client.OAuth2\RabbitMQ.Client.OAuth2.csproj", "{794C7B31-0E9A-44A4-B285-0F3CAF6209F1}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OAuth2Test", "projects\OAuth2Test\OAuth2Test.csproj", "{897D13F0-AF06-444A-9072-CF7E809A4A2C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OAuth2", "projects\Test\OAuth2\OAuth2.csproj", "{897D13F0-AF06-444A-9072-CF7E809A4A2C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{EFD4BED5-13A5-4D9C-AADF-CAB7E1573704}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Integration", "projects\Test\Integration\Integration.csproj", "{B01347D8-C327-471B-A1FE-7B86F7684A27}"
+ ProjectSection(ProjectDependencies) = postProject
+ {C11F25F4-7EA1-4874-9E25-DEB42E3A7C67} = {C11F25F4-7EA1-4874-9E25-DEB42E3A7C67}
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SequentialIntegration", "projects\Test\SequentialIntegration\SequentialIntegration.csproj", "{F25725D7-2978-45F4-B90F-25D6F8B71C9E}"
+ ProjectSection(ProjectDependencies) = postProject
+ {C11F25F4-7EA1-4874-9E25-DEB42E3A7C67} = {C11F25F4-7EA1-4874-9E25-DEB42E3A7C67}
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common", "projects\Test\Common\Common.csproj", "{C11F25F4-7EA1-4874-9E25-DEB42E3A7C67}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -57,13 +71,31 @@ Global
{897D13F0-AF06-444A-9072-CF7E809A4A2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{897D13F0-AF06-444A-9072-CF7E809A4A2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{897D13F0-AF06-444A-9072-CF7E809A4A2C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B01347D8-C327-471B-A1FE-7B86F7684A27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B01347D8-C327-471B-A1FE-7B86F7684A27}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B01347D8-C327-471B-A1FE-7B86F7684A27}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B01347D8-C327-471B-A1FE-7B86F7684A27}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F25725D7-2978-45F4-B90F-25D6F8B71C9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F25725D7-2978-45F4-B90F-25D6F8B71C9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F25725D7-2978-45F4-B90F-25D6F8B71C9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F25725D7-2978-45F4-B90F-25D6F8B71C9E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C11F25F4-7EA1-4874-9E25-DEB42E3A7C67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C11F25F4-7EA1-4874-9E25-DEB42E3A7C67}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C11F25F4-7EA1-4874-9E25-DEB42E3A7C67}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C11F25F4-7EA1-4874-9E25-DEB42E3A7C67}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
+ {B8FAC024-CC03-4067-9FFC-02846FB8AE48} = {EFD4BED5-13A5-4D9C-AADF-CAB7E1573704}
{0E3C4FBE-9976-40A3-9F57-DC0D9B7A39A6} = {D21B282C-49E6-4A30-887B-9626D94B8D69}
+ {D21B282C-49E6-4A30-887B-9626D94B8D69} = {EFD4BED5-13A5-4D9C-AADF-CAB7E1573704}
{4A589408-F3A3-40E1-A6DF-F5E620F7CA31} = {D21B282C-49E6-4A30-887B-9626D94B8D69}
+ {897D13F0-AF06-444A-9072-CF7E809A4A2C} = {EFD4BED5-13A5-4D9C-AADF-CAB7E1573704}
+ {B01347D8-C327-471B-A1FE-7B86F7684A27} = {EFD4BED5-13A5-4D9C-AADF-CAB7E1573704}
+ {F25725D7-2978-45F4-B90F-25D6F8B71C9E} = {EFD4BED5-13A5-4D9C-AADF-CAB7E1573704}
+ {C11F25F4-7EA1-4874-9E25-DEB42E3A7C67} = {EFD4BED5-13A5-4D9C-AADF-CAB7E1573704}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3C6A0C44-FA63-4101-BBF9-2598641167D1}
diff --git a/build.ps1 b/build.ps1
index 9b630e8011..caae1b570a 100644
--- a/build.ps1
+++ b/build.ps1
@@ -14,7 +14,7 @@ Write-Host "Done building." -ForegroundColor "Green"
if ($RunTests)
{
- $unit_csproj_file = Resolve-Path -LiteralPath (Join-Path -Path $PSScriptRoot -ChildPath 'projects' | Join-Path -ChildPath 'Unit' | Join-Path -ChildPath 'Unit.csproj')
+ $unit_csproj_file = Resolve-Path -LiteralPath (Join-Path -Path $PSScriptRoot -ChildPath 'projects' | Join-Path -ChildPath 'Test' | Join-Path -ChildPath 'Unit' | Join-Path -ChildPath 'Unit.csproj')
Write-Host "Running Unit / Integration tests from '$unit_csproj_file' (all frameworks)" -ForegroundColor "Magenta"
dotnet test $unit_csproj_file --no-restore --no-build --logger "console;verbosity=detailed"
if ($LastExitCode -ne 0) {
diff --git a/projects/RabbitMQ.Client/RabbitMQ.Client.csproj b/projects/RabbitMQ.Client/RabbitMQ.Client.csproj
index 68ebb42a02..d591d70b0a 100644
--- a/projects/RabbitMQ.Client/RabbitMQ.Client.csproj
+++ b/projects/RabbitMQ.Client/RabbitMQ.Client.csproj
@@ -45,9 +45,18 @@
+
+ <_Parameter1>Common, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5
+
<_Parameter1>Unit, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5
+
+ <_Parameter1>Integration, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5
+
+
+ <_Parameter1>SequentialIntegration, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5
+
<_Parameter1>Benchmarks, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5
diff --git a/projects/RabbitMQ.Client/client/api/AmqpTimestamp.cs b/projects/RabbitMQ.Client/client/api/AmqpTimestamp.cs
index 2465fd167b..b735d14be3 100644
--- a/projects/RabbitMQ.Client/client/api/AmqpTimestamp.cs
+++ b/projects/RabbitMQ.Client/client/api/AmqpTimestamp.cs
@@ -33,9 +33,6 @@
namespace RabbitMQ.Client
{
- // time representations in mainstream languages: the horror, the horror
- // see in particular the difference between .NET 1.x and .NET 2.0's versions of DateTime
-
///
/// Structure holding an AMQP timestamp, a posix 64-bit time_t.
///
diff --git a/projects/RabbitMQ.Client/client/api/IChannel.cs b/projects/RabbitMQ.Client/client/api/IChannel.cs
index 66515e4c01..5ded578ed2 100644
--- a/projects/RabbitMQ.Client/client/api/IChannel.cs
+++ b/projects/RabbitMQ.Client/client/api/IChannel.cs
@@ -58,8 +58,9 @@ public interface IChannel : IDisposable
///
ShutdownEventArgs CloseReason { get; }
- /// Signalled when an unexpected message is delivered
+ /// Signalled when an unexpected message is delivered.
///
+ ///
/// Under certain circumstances it is possible for a channel to receive a
/// message delivery which does not match any consumer which is currently
/// set up via basicConsume(). This will occur after the following sequence
@@ -79,7 +80,8 @@ public interface IChannel : IDisposable
/// such deliveries. If no default consumer is registered an
/// InvalidOperationException will be thrown when such a delivery arrives.
///
- /// Most people will not need to use this.
+ /// Most people will not need to use this.
+ ///
IBasicConsumer DefaultConsumer { get; set; }
///
@@ -89,7 +91,8 @@ public interface IChannel : IDisposable
///
/// Returns true if the channel is still in a state where it can be used.
- /// Identical to checking if equals null.
+ /// Identical to checking if equals null.
+ ///
bool IsOpen { get; }
///
@@ -115,17 +118,6 @@ public interface IChannel : IDisposable
///
event EventHandler BasicNacks;
- ///
- /// All messages received before this fires that haven't been ack'ed will be redelivered.
- /// All messages received afterwards won't be.
- ///
- ///
- /// Handlers for this event are invoked by the connection thread.
- /// It is sometimes useful to allow that thread to know that a recover-ok
- /// has been received, rather than the thread that invoked .
- ///
- event EventHandler BasicRecoverOk;
-
///
/// Signalled when a Basic.Return command arrives from the broker.
///
@@ -208,9 +200,32 @@ public interface IChannel : IDisposable
///
BasicGetResult BasicGet(string queue, bool autoAck);
- /// Reject one or more delivered message(s).
+ ///
+ /// Asynchronously retrieve an individual message, if
+ /// one is available; returns null if the server answers that
+ /// no messages are currently available. See also .
+ ///
+ /// The queue.
+ /// If set to true, automatically ack the message.
+ ///
+ ValueTask BasicGetAsync(string queue, bool autoAck);
+
+ ///
+ /// Nack one or more delivered message(s).
+ ///
+ /// The delivery tag.
+ /// If set to true, nack all messages up to the current tag.
+ /// If set to true, requeue nack'd messages.
void BasicNack(ulong deliveryTag, bool multiple, bool requeue);
+ ///
+ /// Asynchronously nack one or more delivered message(s).
+ ///
+ /// The delivery tag.
+ /// If set to true, nack all messages up to the current tag.
+ /// If set to true, requeue nack'd messages.
+ ValueTask BasicNackAsync(ulong deliveryTag, bool multiple, bool requeue);
+
#nullable enable
///
@@ -277,20 +292,6 @@ ValueTask BasicPublishAsync(CachedString exchange, CachedString rou
/// See the Consumer Prefetch documentation.
ValueTask BasicQosAsync(uint prefetchSize, ushort prefetchCount, bool global);
- ///
- /// Indicates that a consumer has recovered.
- /// Deprecated. Should not be used.
- ///
- [Obsolete]
- void BasicRecover(bool requeue);
-
- ///
- /// Indicates that a consumer has recovered.
- /// Deprecated. Should not be used.
- ///
- [Obsolete]
- void BasicRecoverAsync(bool requeue);
-
/// Reject a delivered message.
void BasicReject(ulong deliveryTag, bool requeue);
@@ -304,10 +305,19 @@ ValueTask BasicPublishAsync(CachedString exchange, CachedString rou
void Close(ushort replyCode, string replyText, bool abort);
///
- /// Enable publisher acknowledgements.
+ /// Asynchronously close this session.
///
+ /// The instance containing the close data.
+ /// Whether or not the close is an abort (ignoring certain exceptions).
+ ///
+ ValueTask CloseAsync(ShutdownEventArgs reason, bool abort);
+
+ /// Enable publisher confirmations.
void ConfirmSelect();
+ /// Asynchronously enable publisher confirmations.
+ ValueTask ConfirmSelectAsync();
+
///
/// Bind an exchange to an exchange.
///
@@ -374,14 +384,6 @@ ValueTask BasicPublishAsync(CachedString exchange, CachedString rou
///
void ExchangeDelete(string exchange, bool ifUnused);
- /*
- * TODO LRB rabbitmq/rabbitmq-dotnet-client#1347
- ///
- /// Asynchronously delete an exchange.
- ///
- ValueTask ExchangeDeleteAsync(string exchange, bool ifUnused);
- */
-
///
/// Asynchronously delete an exchange.
///
@@ -421,20 +423,24 @@ ValueTask BasicPublishAsync(CachedString exchange, CachedString rou
///
/// Bind a queue to an exchange.
///
+ /// The queue.
+ /// The exchange.
+ /// The routing key.
+ /// The arguments.
///
- ///
- /// Routing key must be shorter than 255 bytes.
- ///
+ /// Routing key must be shorter than 255 bytes.
///
void QueueBind(string queue, string exchange, string routingKey, IDictionary arguments);
///
/// Asynchronously bind a queue to an exchange.
///
+ /// The queue.
+ /// The exchange.
+ /// The routing key.
+ /// The arguments.
///
- ///
- /// Routing key must be shorter than 255 bytes.
- ///
+ /// Routing key must be shorter than 255 bytes.
///
ValueTask QueueBindAsync(string queue, string exchange, string routingKey, IDictionary arguments);
@@ -528,39 +534,58 @@ ValueTask BasicPublishAsync(CachedString exchange, CachedString rou
/// Returns the number of messages purged during deletion.
void QueueDeleteNoWait(string queue, bool ifUnused, bool ifEmpty);
- ///
- /// Purge a queue of messages.
- ///
- ///
- /// Returns the number of messages purged.
- ///
+ /// Asynchronously purge a queue of messages.
+ /// The queue.
+ /// Returns the number of messages purged.
uint QueuePurge(string queue);
+ /// Asynchronously purge a queue of messages.
+ /// The queue.
+ /// Returns the number of messages purged.
+ ValueTask QueuePurgeAsync(string queue);
+
///
/// Unbind a queue from an exchange.
///
+ /// The queue.
+ /// The exchange.
+ /// The routing key.
+ /// The arguments.
///
- ///
- /// Routing key must be shorter than 255 bytes.
- ///
+ /// Routing key must be shorter than 255 bytes.
///
void QueueUnbind(string queue, string exchange, string routingKey, IDictionary arguments);
///
- /// Commit this session's active TX transaction.
+ /// Asynchronously unbind a queue from an exchange.
///
+ /// The queue.
+ /// The exchange.
+ /// The routing key.
+ /// The arguments.
+ ///
+ /// Routing key must be shorter than 255 bytes.
+ ///
+ ValueTask QueueUnbindAsync(string queue, string exchange, string routingKey, IDictionary arguments);
+
+ /// Commit this session's active TX transaction.
void TxCommit();
- ///
- /// Roll back this session's active TX transaction.
- ///
+ /// Asynchronously commit this session's active TX transaction.
+ ValueTask TxCommitAsync();
+
+ /// Roll back this session's active TX transaction.
void TxRollback();
- ///
- /// Enable TX mode for this session.
- ///
+ /// Asynchronously roll back this session's active TX transaction.
+ ValueTask TxRollbackAsync();
+
+ /// Enable TX mode for this session.
void TxSelect();
+ /// Asynchronously enable TX mode for this session.
+ ValueTask TxSelectAsync();
+
///
/// Wait until all published messages on this channel have been confirmed.
///
diff --git a/projects/RabbitMQ.Client/client/api/IChannelExtensions.cs b/projects/RabbitMQ.Client/client/api/IChannelExtensions.cs
index 6beb1d6bb5..22b7463fe5 100644
--- a/projects/RabbitMQ.Client/client/api/IChannelExtensions.cs
+++ b/projects/RabbitMQ.Client/client/api/IChannelExtensions.cs
@@ -250,15 +250,32 @@ public static void Close(this IChannel channel)
channel.Close(Constants.ReplySuccess, "Goodbye", false);
}
- /// Close this session.
+ /// Asynchronously close this session.
+ ///
+ /// If the session is already closed (or closing), then this
+ /// method does nothing but wait for the in-progress close
+ /// operation to complete. This method will not return to the
+ /// caller until the shutdown is complete.
+ ///
+ public static ValueTask CloseAsync(this IChannel channel)
+ {
+ var reason = new ShutdownEventArgs(ShutdownInitiator.Library, Constants.ReplySuccess, "Goodbye");
+ return channel.CloseAsync(reason, false);
+ }
+
+ ///
+ /// Close this channel.
+ ///
+ /// The channel.
+ /// The reply code.
+ /// The reply text.
///
/// The method behaves in the same way as Close(), with the only
/// difference that the channel is closed with the given channel
/// close code and message.
///
/// The close code (See under "Reply Codes" in the AMQP specification)
- ///
- ///
+ ///
/// A message indicating the reason for closing the channel
///
///
diff --git a/projects/RabbitMQ.Client/client/framing/Channel.cs b/projects/RabbitMQ.Client/client/framing/Channel.cs
index d0930b7aef..4ed5678c0c 100644
--- a/projects/RabbitMQ.Client/client/framing/Channel.cs
+++ b/projects/RabbitMQ.Client/client/framing/Channel.cs
@@ -62,11 +62,6 @@ public override void _Private_BasicGet(string queue, bool autoAck)
ChannelSend(new BasicGet(queue, autoAck));
}
- public override void _Private_BasicRecover(bool requeue)
- {
- ChannelSend(new BasicRecover(requeue));
- }
-
public override void _Private_ChannelClose(ushort replyCode, string replyText, ushort classId, ushort methodId)
{
ChannelSend(new ChannelClose(replyCode, replyText, classId, methodId));
@@ -245,14 +240,15 @@ public override void BasicNack(ulong deliveryTag, bool multiple, bool requeue)
ChannelSend(new BasicNack(deliveryTag, multiple, requeue));
}
- public override void BasicQos(uint prefetchSize, ushort prefetchCount, bool global)
+ public override ValueTask BasicNackAsync(ulong deliveryTag, bool multiple, bool requeue)
{
- ChannelRpc(new BasicQos(prefetchSize, prefetchCount, global), ProtocolCommandId.BasicQosOk);
+ var method = new BasicNack(deliveryTag, multiple, requeue);
+ return ModelSendAsync(method);
}
- public override void BasicRecoverAsync(bool requeue)
+ public override void BasicQos(uint prefetchSize, ushort prefetchCount, bool global)
{
- ChannelSend(new BasicRecoverAsync(requeue));
+ ChannelRpc(new BasicQos(prefetchSize, prefetchCount, global), ProtocolCommandId.BasicQosOk);
}
public override void BasicReject(ulong deliveryTag, bool requeue)
@@ -321,20 +317,13 @@ protected override bool DispatchAsynchronous(in IncomingCommand cmd)
}
case ProtocolCommandId.BasicGetOk:
{
- HandleBasicGetOk(in cmd);
- return true;
+ return HandleBasicGetOk(in cmd);
}
case ProtocolCommandId.BasicNack:
{
HandleBasicNack(in cmd);
return true;
}
- case ProtocolCommandId.BasicRecoverOk:
- {
- cmd.ReturnMethodBuffer();
- HandleBasicRecoverOk();
- return true;
- }
case ProtocolCommandId.BasicReturn:
{
HandleBasicReturn(in cmd);
@@ -347,8 +336,7 @@ protected override bool DispatchAsynchronous(in IncomingCommand cmd)
}
case ProtocolCommandId.ChannelCloseOk:
{
- cmd.ReturnMethodBuffer();
- HandleChannelCloseOk();
+ HandleChannelCloseOk(in cmd);
return true;
}
case ProtocolCommandId.ChannelFlow:
diff --git a/projects/RabbitMQ.Client/client/impl/AsyncRpcContinuations.cs b/projects/RabbitMQ.Client/client/impl/AsyncRpcContinuations.cs
index a171515dce..62ea0778ab 100644
--- a/projects/RabbitMQ.Client/client/impl/AsyncRpcContinuations.cs
+++ b/projects/RabbitMQ.Client/client/impl/AsyncRpcContinuations.cs
@@ -68,7 +68,10 @@ public AsyncRpcContinuation(TimeSpan continuationTimeout)
// What to do if setting a result fails?
public abstract void HandleCommand(in IncomingCommand cmd);
- public void HandleChannelShutdown(ShutdownEventArgs reason) => _tcs.SetException(new OperationInterruptedException(reason));
+ public virtual void HandleChannelShutdown(ShutdownEventArgs reason)
+ {
+ _tcs.SetException(new OperationInterruptedException(reason));
+ }
protected virtual void Dispose(bool disposing)
{
@@ -198,6 +201,51 @@ public override void HandleCommand(in IncomingCommand cmd)
}
}
+ internal class BasicGetAsyncRpcContinuation : AsyncRpcContinuation
+ {
+ private readonly Func _adjustDeliveryTag;
+
+ public BasicGetAsyncRpcContinuation(Func adjustDeliveryTag, TimeSpan continuationTimeout)
+ : base(continuationTimeout)
+ {
+ _adjustDeliveryTag = adjustDeliveryTag;
+ }
+
+ public override void HandleCommand(in IncomingCommand cmd)
+ {
+ try
+ {
+ if (cmd.CommandId == ProtocolCommandId.BasicGetOk)
+ {
+ var method = new Client.Framing.Impl.BasicGetOk(cmd.MethodBytes.Span);
+ cmd.ReturnMethodBuffer();
+ var header = new ReadOnlyBasicProperties(cmd.HeaderBytes.Span);
+ cmd.ReturnHeaderBuffer();
+
+ var result = new BasicGetResult(
+ _adjustDeliveryTag(method._deliveryTag),
+ method._redelivered,
+ method._exchange,
+ method._routingKey,
+ method._messageCount,
+ header,
+ cmd.Body,
+ cmd.TakeoverBody());
+
+ _tcs.TrySetResult(result);
+ }
+ else
+ {
+ _tcs.SetException(new InvalidOperationException($"Received unexpected command of type {cmd.CommandId}!"));
+ }
+ }
+ finally
+ {
+ cmd.ReturnMethodBuffer();
+ }
+ }
+ }
+
internal class BasicQosAsyncRpcContinuation : SimpleAsyncRpcContinuation
{
public BasicQosAsyncRpcContinuation(TimeSpan continuationTimeout)
@@ -206,6 +254,32 @@ public BasicQosAsyncRpcContinuation(TimeSpan continuationTimeout)
}
}
+ internal class ChannelCloseAsyncRpcContinuation : SimpleAsyncRpcContinuation
+ {
+ public ChannelCloseAsyncRpcContinuation(TimeSpan continuationTimeout)
+ : base(ProtocolCommandId.ChannelCloseOk, continuationTimeout)
+ {
+ }
+
+ public override void HandleChannelShutdown(ShutdownEventArgs reason)
+ {
+ // Nothing to do here!
+ }
+
+ public void OnConnectionShutdown(object sender, ShutdownEventArgs reason)
+ {
+ _tcs.TrySetResult(true);
+ }
+ }
+
+ internal class ConfirmSelectAsyncRpcContinuation : SimpleAsyncRpcContinuation
+ {
+ public ConfirmSelectAsyncRpcContinuation(TimeSpan continuationTimeout)
+ : base(ProtocolCommandId.ConfirmSelectOk, continuationTimeout)
+ {
+ }
+ }
+
internal class ExchangeBindAsyncRpcContinuation : SimpleAsyncRpcContinuation
{
public ExchangeBindAsyncRpcContinuation(TimeSpan continuationTimeout)
@@ -274,6 +348,14 @@ public QueueBindAsyncRpcContinuation(TimeSpan continuationTimeout)
}
}
+ internal class QueueUnbindAsyncRpcContinuation : SimpleAsyncRpcContinuation
+ {
+ public QueueUnbindAsyncRpcContinuation(TimeSpan continuationTimeout)
+ : base(ProtocolCommandId.QueueUnbindOk, continuationTimeout)
+ {
+ }
+ }
+
internal class QueueDeleteAsyncRpcContinuation : AsyncRpcContinuation
{
public QueueDeleteAsyncRpcContinuation(TimeSpan continuationTimeout) : base(continuationTimeout)
@@ -300,4 +382,55 @@ public override void HandleCommand(in IncomingCommand cmd)
}
}
}
+
+ internal class QueuePurgeAsyncRpcContinuation : AsyncRpcContinuation
+ {
+ public QueuePurgeAsyncRpcContinuation(TimeSpan continuationTimeout) : base(continuationTimeout)
+ {
+ }
+
+ public override void HandleCommand(in IncomingCommand cmd)
+ {
+ try
+ {
+ if (cmd.CommandId == ProtocolCommandId.QueuePurgeOk)
+ {
+ var method = new Client.Framing.Impl.QueuePurgeOk(cmd.MethodBytes.Span);
+ _tcs.TrySetResult(method._messageCount);
+ }
+ else
+ {
+ _tcs.SetException(new InvalidOperationException($"Received unexpected command of type {cmd.CommandId}!"));
+ }
+ }
+ finally
+ {
+ cmd.ReturnMethodBuffer();
+ }
+ }
+ }
+
+ internal class TxCommitAsyncRpcContinuation : SimpleAsyncRpcContinuation
+ {
+ public TxCommitAsyncRpcContinuation(TimeSpan continuationTimeout)
+ : base(ProtocolCommandId.TxCommitOk, continuationTimeout)
+ {
+ }
+ }
+
+ internal class TxRollbackAsyncRpcContinuation : SimpleAsyncRpcContinuation
+ {
+ public TxRollbackAsyncRpcContinuation(TimeSpan continuationTimeout)
+ : base(ProtocolCommandId.TxRollbackOk, continuationTimeout)
+ {
+ }
+ }
+
+ internal class TxSelectAsyncRpcContinuation : SimpleAsyncRpcContinuation
+ {
+ public TxSelectAsyncRpcContinuation(TimeSpan continuationTimeout)
+ : base(ProtocolCommandId.TxSelectOk, continuationTimeout)
+ {
+ }
+ }
}
diff --git a/projects/RabbitMQ.Client/client/impl/AutorecoveringChannel.cs b/projects/RabbitMQ.Client/client/impl/AutorecoveringChannel.cs
index aac065c7a2..a9d9d0c77e 100644
--- a/projects/RabbitMQ.Client/client/impl/AutorecoveringChannel.cs
+++ b/projects/RabbitMQ.Client/client/impl/AutorecoveringChannel.cs
@@ -87,12 +87,6 @@ public event EventHandler BasicNacks
remove => InnerChannel.BasicNacks -= value;
}
- public event EventHandler BasicRecoverOk
- {
- add => InnerChannel.BasicRecoverOk += value;
- remove => InnerChannel.BasicRecoverOk -= value;
- }
-
public event EventHandler BasicReturn
{
add => InnerChannel.BasicReturn += value;
@@ -251,6 +245,19 @@ public void Close(ushort replyCode, string replyText, bool abort)
}
}
+ public async ValueTask CloseAsync(ShutdownEventArgs reason, bool abort)
+ {
+ ThrowIfDisposed();
+ try
+ {
+ await _innerChannel.CloseAsync(reason, abort);
+ }
+ finally
+ {
+ _connection.DeleteRecordedChannel(this);
+ }
+ }
+
public override string ToString()
=> InnerChannel.ToString();
@@ -271,16 +278,10 @@ public void Dispose()
}
public void BasicAck(ulong deliveryTag, bool multiple)
- {
- ThrowIfDisposed();
- _innerChannel.BasicAck(deliveryTag, multiple);
- }
+ => InnerChannel.BasicAck(deliveryTag, multiple);
public ValueTask BasicAckAsync(ulong deliveryTag, bool multiple)
- {
- ThrowIfDisposed();
- return _innerChannel.BasicAckAsync(deliveryTag, multiple);
- }
+ => InnerChannel.BasicAckAsync(deliveryTag, multiple);
public void BasicCancel(string consumerTag)
{
@@ -328,9 +329,15 @@ public async ValueTask BasicConsumeAsync(string queue, bool autoAck, str
public BasicGetResult BasicGet(string queue, bool autoAck)
=> InnerChannel.BasicGet(queue, autoAck);
+ public ValueTask BasicGetAsync(string queue, bool autoAck)
+ => InnerChannel.BasicGetAsync(queue, autoAck);
+
public void BasicNack(ulong deliveryTag, bool multiple, bool requeue)
=> InnerChannel.BasicNack(deliveryTag, multiple, requeue);
+ public ValueTask BasicNackAsync(ulong deliveryTag, bool multiple, bool requeue)
+ => InnerChannel.BasicNackAsync(deliveryTag, multiple, requeue);
+
public void BasicPublish(string exchange, string routingKey, in TProperties basicProperties, ReadOnlyMemory body, bool mandatory)
where TProperties : IReadOnlyBasicProperties, IAmqpHeader
=> InnerChannel.BasicPublish(exchange, routingKey, in basicProperties, body, mandatory);
@@ -379,12 +386,6 @@ public ValueTask BasicQosAsync(uint prefetchSize, ushort prefetchCount, bool glo
return _innerChannel.BasicQosAsync(prefetchSize, prefetchCount, global);
}
- public void BasicRecover(bool requeue)
- => InnerChannel.BasicRecover(requeue);
-
- public void BasicRecoverAsync(bool requeue)
- => InnerChannel.BasicRecoverAsync(requeue);
-
public void BasicReject(ulong deliveryTag, bool requeue)
=> InnerChannel.BasicReject(deliveryTag, requeue);
@@ -397,6 +398,13 @@ public void ConfirmSelect()
_usesPublisherConfirms = true;
}
+ public ValueTask ConfirmSelectAsync()
+ {
+ var task = InnerChannel.ConfirmSelectAsync();
+ _usesPublisherConfirms = true;
+ return task;
+ }
+
public void ExchangeBind(string destination, string source, string routingKey, IDictionary arguments)
{
ThrowIfDisposed();
@@ -406,8 +414,7 @@ public void ExchangeBind(string destination, string source, string routingKey, I
public async ValueTask ExchangeBindAsync(string destination, string source, string routingKey, IDictionary arguments)
{
- ThrowIfDisposed();
- await _innerChannel.ExchangeBindAsync(destination, source, routingKey, arguments);
+ await InnerChannel.ExchangeBindAsync(destination, source, routingKey, arguments);
_connection.RecordBinding(new RecordedBinding(false, destination, source, routingKey, arguments));
}
@@ -416,15 +423,13 @@ public void ExchangeBindNoWait(string destination, string source, string routing
public void ExchangeDeclare(string exchange, string type, bool durable, bool autoDelete, IDictionary arguments)
{
- ThrowIfDisposed();
- _innerChannel.ExchangeDeclare(exchange, type, durable, autoDelete, arguments);
+ InnerChannel.ExchangeDeclare(exchange, type, durable, autoDelete, arguments);
_connection.RecordExchange(new RecordedExchange(exchange, type, durable, autoDelete, arguments));
}
public async ValueTask ExchangeDeclareAsync(string exchange, string type, bool passive, bool durable, bool autoDelete, IDictionary arguments)
{
- ThrowIfDisposed();
- await _innerChannel.ExchangeDeclareAsync(exchange, type, passive, durable, autoDelete, arguments);
+ await InnerChannel.ExchangeDeclareAsync(exchange, type, passive, durable, autoDelete, arguments);
if (false == passive)
{
_connection.RecordExchange(new RecordedExchange(exchange, type, durable, autoDelete, arguments));
@@ -433,8 +438,7 @@ public async ValueTask ExchangeDeclareAsync(string exchange, string type, bool p
public void ExchangeDeclareNoWait(string exchange, string type, bool durable, bool autoDelete, IDictionary arguments)
{
- ThrowIfDisposed();
- _innerChannel.ExchangeDeclareNoWait(exchange, type, durable, autoDelete, arguments);
+ InnerChannel.ExchangeDeclareNoWait(exchange, type, durable, autoDelete, arguments);
_connection.RecordExchange(new RecordedExchange(exchange, type, durable, autoDelete, arguments));
}
@@ -469,8 +473,7 @@ public void ExchangeUnbind(string destination, string source, string routingKey,
public async ValueTask ExchangeUnbindAsync(string destination, string source, string routingKey, IDictionary arguments)
{
- ThrowIfDisposed();
- await _innerChannel.ExchangeUnbindAsync(destination, source, routingKey, arguments);
+ await InnerChannel.ExchangeUnbindAsync(destination, source, routingKey, arguments);
_connection.DeleteRecordedBinding(new RecordedBinding(false, destination, source, routingKey, arguments));
_connection.DeleteAutoDeleteExchange(source);
}
@@ -490,23 +493,20 @@ public void QueueBindNoWait(string queue, string exchange, string routingKey, ID
public QueueDeclareOk QueueDeclare(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary arguments)
{
- ThrowIfDisposed();
- QueueDeclareOk result = _innerChannel.QueueDeclare(queue, durable, exclusive, autoDelete, arguments);
+ QueueDeclareOk result = InnerChannel.QueueDeclare(queue, durable, exclusive, autoDelete, arguments);
_connection.RecordQueue(new RecordedQueue(result.QueueName, queue.Length == 0, durable, exclusive, autoDelete, arguments));
return result;
}
public void QueueDeclareNoWait(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary arguments)
{
- ThrowIfDisposed();
- _innerChannel.QueueDeclareNoWait(queue, durable, exclusive, autoDelete, arguments);
+ InnerChannel.QueueDeclareNoWait(queue, durable, exclusive, autoDelete, arguments);
_connection.RecordQueue(new RecordedQueue(queue, queue.Length == 0, durable, exclusive, autoDelete, arguments));
}
public async ValueTask QueueDeclareAsync(string queue, bool passive, bool durable, bool exclusive, bool autoDelete, IDictionary arguments)
{
- ThrowIfDisposed();
- QueueDeclareOk result = await _innerChannel.QueueDeclareAsync(queue, passive, durable, exclusive, autoDelete, arguments);
+ QueueDeclareOk result = await InnerChannel.QueueDeclareAsync(queue, passive, durable, exclusive, autoDelete, arguments);
if (false == passive)
{
_connection.RecordQueue(new RecordedQueue(result.QueueName, queue.Length == 0, durable, exclusive, autoDelete, arguments));
@@ -514,11 +514,8 @@ public async ValueTask QueueDeclareAsync(string queue, bool pass
return result;
}
- public async ValueTask QueueBindAsync(string queue, string exchange, string routingKey, IDictionary arguments)
- {
- ThrowIfDisposed();
- await _innerChannel.QueueBindAsync(queue, exchange, routingKey, arguments);
- }
+ public ValueTask QueueBindAsync(string queue, string exchange, string routingKey, IDictionary arguments)
+ => InnerChannel.QueueBindAsync(queue, exchange, routingKey, arguments);
public QueueDeclareOk QueueDeclarePassive(string queue)
=> InnerChannel.QueueDeclarePassive(queue);
@@ -531,16 +528,14 @@ public uint ConsumerCount(string queue)
public uint QueueDelete(string queue, bool ifUnused, bool ifEmpty)
{
- ThrowIfDisposed();
- uint result = _innerChannel.QueueDelete(queue, ifUnused, ifEmpty);
+ uint result = InnerChannel.QueueDelete(queue, ifUnused, ifEmpty);
_connection.DeleteRecordedQueue(queue);
return result;
}
public async ValueTask QueueDeleteAsync(string queue, bool ifUnused, bool ifEmpty)
{
- ThrowIfDisposed();
- uint result = await _innerChannel.QueueDeleteAsync(queue, ifUnused, ifEmpty);
+ uint result = await InnerChannel.QueueDeleteAsync(queue, ifUnused, ifEmpty);
_connection.DeleteRecordedQueue(queue);
return result;
}
@@ -554,6 +549,9 @@ public void QueueDeleteNoWait(string queue, bool ifUnused, bool ifEmpty)
public uint QueuePurge(string queue)
=> InnerChannel.QueuePurge(queue);
+ public ValueTask QueuePurgeAsync(string queue)
+ => InnerChannel.QueuePurgeAsync(queue);
+
public void QueueUnbind(string queue, string exchange, string routingKey, IDictionary arguments)
{
ThrowIfDisposed();
@@ -562,18 +560,38 @@ public void QueueUnbind(string queue, string exchange, string routingKey, IDicti
_connection.DeleteAutoDeleteExchange(exchange);
}
+ public async ValueTask QueueUnbindAsync(string queue, string exchange, string routingKey, IDictionary arguments)
+ {
+ ThrowIfDisposed();
+ _connection.DeleteRecordedBinding(new RecordedBinding(true, queue, exchange, routingKey, arguments));
+ await _innerChannel.QueueUnbindAsync(queue, exchange, routingKey, arguments);
+ _connection.DeleteAutoDeleteExchange(exchange);
+ }
+
public void TxCommit()
=> InnerChannel.TxCommit();
+ public ValueTask TxCommitAsync()
+ => InnerChannel.TxCommitAsync();
+
public void TxRollback()
=> InnerChannel.TxRollback();
+ public ValueTask TxRollbackAsync()
+ => InnerChannel.TxRollbackAsync();
+
public void TxSelect()
{
InnerChannel.TxSelect();
_usesTransactions = true;
}
+ public ValueTask TxSelectAsync()
+ {
+ _usesTransactions = true;
+ return InnerChannel.TxSelectAsync();
+ }
+
public Task WaitForConfirmsAsync(CancellationToken token = default)
=> InnerChannel.WaitForConfirmsAsync(token);
diff --git a/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.cs b/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.cs
index 625ba1b2d4..82629be0e4 100644
--- a/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.cs
+++ b/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.cs
@@ -173,7 +173,10 @@ public RecoveryAwareChannel CreateNonRecoveringChannel()
public override string ToString()
=> $"AutorecoveringConnection({InnerConnection.Id},{Endpoint},{GetHashCode()})";
- internal IFrameHandler FrameHandler => InnerConnection.FrameHandler;
+ internal void CloseFrameHandler()
+ {
+ InnerConnection.FrameHandler.Close();
+ }
///API-side invocation of updating the secret.
public void UpdateSecret(string newSecret, string reason)
diff --git a/projects/RabbitMQ.Client/client/impl/ChannelBase.cs b/projects/RabbitMQ.Client/client/impl/ChannelBase.cs
index 7367a95b10..4431cdcc6b 100644
--- a/projects/RabbitMQ.Client/client/impl/ChannelBase.cs
+++ b/projects/RabbitMQ.Client/client/impl/ChannelBase.cs
@@ -77,7 +77,6 @@ protected ChannelBase(ConnectionConfig config, ISession session)
Action onException = (exception, context) => OnCallbackException(CallbackExceptionEventArgs.Build(exception, context));
_basicAcksWrapper = new EventingWrapper("OnBasicAck", onException);
_basicNacksWrapper = new EventingWrapper("OnBasicNack", onException);
- _basicRecoverOkWrapper = new EventingWrapper("OnBasicRecover", onException);
_basicReturnWrapper = new EventingWrapper("OnBasicReturn", onException);
_callbackExceptionWrapper = new EventingWrapper(string.Empty, (exception, context) => { });
_flowControlWrapper = new EventingWrapper("OnFlowControl", onException);
@@ -105,13 +104,6 @@ public event EventHandler BasicNacks
}
private EventingWrapper _basicNacksWrapper;
- public event EventHandler BasicRecoverOk
- {
- add => _basicRecoverOkWrapper.AddHandler(value);
- remove => _basicRecoverOkWrapper.RemoveHandler(value);
- }
- private EventingWrapper _basicRecoverOkWrapper;
-
public event EventHandler BasicReturn
{
add => _basicReturnWrapper.AddHandler(value);
@@ -184,7 +176,6 @@ protected void TakeOver(ChannelBase other)
{
_basicAcksWrapper.Takeover(other._basicAcksWrapper);
_basicNacksWrapper.Takeover(other._basicNacksWrapper);
- _basicRecoverOkWrapper.Takeover(other._basicRecoverOkWrapper);
_basicReturnWrapper.Takeover(other._basicReturnWrapper);
_callbackExceptionWrapper.Takeover(other._callbackExceptionWrapper);
_flowControlWrapper.Takeover(other._flowControlWrapper);
@@ -194,24 +185,68 @@ protected void TakeOver(ChannelBase other)
public void Close(ushort replyCode, string replyText, bool abort)
{
- _ = CloseAsync(new ShutdownEventArgs(ShutdownInitiator.Application, replyCode, replyText), abort);
- }
-
- private async Task CloseAsync(ShutdownEventArgs reason, bool abort)
- {
- // TODO LRB #1347 use async continuation
+ var reason = new ShutdownEventArgs(ShutdownInitiator.Application, replyCode, replyText);
var k = new ShutdownContinuation();
ChannelShutdown += k.OnConnectionShutdown;
try
{
ConsumerDispatcher.Quiesce();
+
if (SetCloseReason(reason))
{
_Private_ChannelClose(reason.ReplyCode, reason.ReplyText, 0, 0);
}
k.Wait(TimeSpan.FromMilliseconds(10000));
+ ConsumerDispatcher.WaitForShutdownAsync().ConfigureAwait(false);
+ }
+ catch (AlreadyClosedException)
+ {
+ if (!abort)
+ {
+ throw;
+ }
+ }
+ catch (IOException)
+ {
+ if (!abort)
+ {
+ throw;
+ }
+ }
+ catch (Exception)
+ {
+ if (!abort)
+ {
+ throw;
+ }
+ }
+ finally
+ {
+ ChannelShutdown -= k.OnConnectionShutdown;
+ }
+ }
+
+ public async ValueTask CloseAsync(ShutdownEventArgs reason, bool abort)
+ {
+ await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
+ using var k = new ChannelCloseAsyncRpcContinuation(ContinuationTimeout);
+ try
+ {
+ ChannelShutdown += k.OnConnectionShutdown;
+ Enqueue(k);
+ ConsumerDispatcher.Quiesce();
+
+ if (SetCloseReason(reason))
+ {
+ var method = new ChannelClose(reason.ReplyCode, reason.ReplyText, reason.ClassId, reason.MethodId);
+ await ModelSendAsync(method).ConfigureAwait(false);
+ }
+
+ bool result = await k;
+ Debug.Assert(result);
+
await ConsumerDispatcher.WaitForShutdownAsync().ConfigureAwait(false);
}
catch (AlreadyClosedException)
@@ -237,6 +272,7 @@ private async Task CloseAsync(ShutdownEventArgs reason, bool abort)
}
finally
{
+ _rpcSemaphore.Release();
ChannelShutdown -= k.OnConnectionShutdown;
}
}
@@ -248,11 +284,12 @@ internal async ValueTask ConnectionOpenAsync(string virtualHost)
internal async ValueTask ConnectionSecureOkAsync(byte[] response)
{
- using var k = new ConnectionSecureOrTuneContinuation(ContinuationTimeout);
await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
- Enqueue(k);
try
{
+ using var k = new ConnectionSecureOrTuneContinuation(ContinuationTimeout);
+ Enqueue(k);
+
try
{
_Private_ConnectionSecureOk(response);
@@ -275,11 +312,12 @@ internal async ValueTask ConnectionSecureOkAsync(byte[]
internal async ValueTask ConnectionStartOkAsync(IDictionary clientProperties, string mechanism, byte[] response,
string locale)
{
- using var k = new ConnectionSecureOrTuneContinuation(ContinuationTimeout);
await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
- Enqueue(k);
try
{
+ using var k = new ConnectionSecureOrTuneContinuation(ContinuationTimeout);
+ Enqueue(k);
+
try
{
// TODO LRB rabbitmq/rabbitmq-dotnet-client#1347
@@ -637,24 +675,32 @@ protected void HandleBasicDeliver(in IncomingCommand cmd)
cmd.TakeoverBody());
}
- protected void HandleBasicGetOk(in IncomingCommand cmd)
+ protected bool HandleBasicGetOk(in IncomingCommand cmd)
{
- var method = new BasicGetOk(cmd.MethodBytes.Span);
- cmd.ReturnMethodBuffer();
- var header = new ReadOnlyBasicProperties(cmd.HeaderBytes.Span);
- cmd.ReturnHeaderBuffer();
+ if (_continuationQueue.TryPeek(out var k))
+ {
+ var method = new BasicGetOk(cmd.MethodBytes.Span);
+ cmd.ReturnMethodBuffer();
+ var header = new ReadOnlyBasicProperties(cmd.HeaderBytes.Span);
+ cmd.ReturnHeaderBuffer();
- var k = (BasicGetRpcContinuation)_continuationQueue.Next();
- k.m_result = new BasicGetResult(
- AdjustDeliveryTag(method._deliveryTag),
- method._redelivered,
- method._exchange,
- method._routingKey,
- method._messageCount,
- header,
- cmd.Body,
- cmd.TakeoverBody());
- k.HandleCommand(IncomingCommand.Empty); // release the continuation.
+ _continuationQueue.Next();
+ k.m_result = new BasicGetResult(
+ AdjustDeliveryTag(method._deliveryTag),
+ method._redelivered,
+ method._exchange,
+ method._routingKey,
+ method._messageCount,
+ header,
+ cmd.Body,
+ cmd.TakeoverBody());
+ k.HandleCommand(IncomingCommand.Empty); // release the continuation.
+ return true;
+ }
+ else
+ {
+ return false;
+ }
}
protected virtual ulong AdjustDeliveryTag(ulong deliveryTag)
@@ -669,13 +715,6 @@ protected void HandleBasicGetEmpty()
k.HandleCommand(IncomingCommand.Empty); // release the continuation.
}
- protected void HandleBasicRecoverOk()
- {
- var k = (SimpleBlockingRpcContinuation)_continuationQueue.Next();
- _basicRecoverOkWrapper.Invoke(this, EventArgs.Empty);
- k.HandleCommand(IncomingCommand.Empty);
- }
-
protected void HandleBasicReturn(in IncomingCommand cmd)
{
if (!_basicReturnWrapper.IsEmpty)
@@ -718,9 +757,26 @@ protected void HandleChannelClose(in IncomingCommand cmd)
}
}
- protected void HandleChannelCloseOk()
+ protected void HandleChannelCloseOk(in IncomingCommand cmd)
{
- FinishClose();
+ try
+ {
+ /*
+ * Note:
+ * This call _must_ come before completing the async continuation
+ */
+ FinishClose();
+
+ if (_continuationQueue.TryPeek(out var k))
+ {
+ _continuationQueue.Next();
+ k.HandleCommand(cmd);
+ }
+ }
+ finally
+ {
+ cmd.ReturnMethodBuffer();
+ }
}
protected void HandleChannelFlow(in IncomingCommand cmd)
@@ -836,8 +892,6 @@ protected bool HandleQueueDeclareOk(in IncomingCommand cmd)
public abstract void _Private_BasicGet(string queue, bool autoAck);
- public abstract void _Private_BasicRecover(bool requeue);
-
public abstract void _Private_ChannelClose(ushort replyCode, string replyText, ushort classId, ushort methodId);
public abstract void _Private_ChannelCloseOk();
@@ -898,10 +952,10 @@ public void BasicCancel(string consumerTag)
public async ValueTask BasicCancelAsync(string consumerTag)
{
- using var k = new BasicCancelAsyncRpcContinuation(ContinuationTimeout);
await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
try
{
+ using var k = new BasicCancelAsyncRpcContinuation(ContinuationTimeout);
Enqueue(k);
var method = new Client.Framing.Impl.BasicCancel(consumerTag, false);
@@ -969,10 +1023,10 @@ public async ValueTask BasicConsumeAsync(string queue, bool autoAck, str
}
}
- using var k = new BasicConsumeAsyncRpcContinuation(consumer, ConsumerDispatcher, ContinuationTimeout);
await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
try
{
+ using var k = new BasicConsumeAsyncRpcContinuation(consumer, ConsumerDispatcher, ContinuationTimeout);
Enqueue(k);
var method = new Client.Framing.Impl.BasicConsume(queue, consumerTag, noLocal, autoAck, exclusive, false, arguments);
@@ -1005,8 +1059,29 @@ public BasicGetResult BasicGet(string queue, bool autoAck)
return k.m_result;
}
+ public async ValueTask BasicGetAsync(string queue, bool autoAck)
+ {
+ await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
+ try
+ {
+ using var k = new BasicGetAsyncRpcContinuation(AdjustDeliveryTag, ContinuationTimeout);
+ Enqueue(k);
+
+ var method = new BasicGet(queue, autoAck);
+ await ModelSendAsync(method).ConfigureAwait(false);
+
+ return await k;
+ }
+ finally
+ {
+ _rpcSemaphore.Release();
+ }
+ }
+
public abstract void BasicNack(ulong deliveryTag, bool multiple, bool requeue);
+ public abstract ValueTask BasicNackAsync(ulong deliveryTag, bool multiple, bool requeue);
+
public void BasicPublish(string exchange, string routingKey, in TProperties basicProperties, ReadOnlyMemory body, bool mandatory)
where TProperties : IReadOnlyBasicProperties, IAmqpHeader
{
@@ -1086,10 +1161,10 @@ public void UpdateSecret(string newSecret, string reason)
public async ValueTask BasicQosAsync(uint prefetchSize, ushort prefetchCount, bool global)
{
- using var k = new BasicQosAsyncRpcContinuation(ContinuationTimeout);
await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
try
{
+ using var k = new BasicQosAsyncRpcContinuation(ContinuationTimeout);
Enqueue(k);
var method = new BasicQos(prefetchSize, prefetchCount, global);
@@ -1105,25 +1180,6 @@ public async ValueTask BasicQosAsync(uint prefetchSize, ushort prefetchCount, bo
}
}
- public void BasicRecover(bool requeue)
- {
- var k = new SimpleBlockingRpcContinuation();
-
- _rpcSemaphore.Wait();
- try
- {
- Enqueue(k);
- _Private_BasicRecover(requeue);
- k.GetReply(ContinuationTimeout);
- }
- finally
- {
- _rpcSemaphore.Release();
- }
- }
-
- public abstract void BasicRecoverAsync(bool requeue);
-
public abstract void BasicReject(ulong deliveryTag, bool requeue);
public abstract ValueTask BasicRejectAsync(ulong deliveryTag, bool requeue);
@@ -1139,6 +1195,34 @@ public void ConfirmSelect()
_Private_ConfirmSelect(false);
}
+ public async ValueTask ConfirmSelectAsync()
+ {
+ await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
+ try
+ {
+ if (NextPublishSeqNo == 0UL)
+ {
+ _confirmsTaskCompletionSources = new List>();
+ NextPublishSeqNo = 1;
+ }
+
+ using var k = new ConfirmSelectAsyncRpcContinuation(ContinuationTimeout);
+ Enqueue(k);
+
+ var method = new ConfirmSelect(false);
+ await ModelSendAsync(method).ConfigureAwait(false);
+
+ bool result = await k;
+ Debug.Assert(result);
+
+ return;
+ }
+ finally
+ {
+ _rpcSemaphore.Release();
+ }
+ }
+
public void ExchangeBind(string destination, string source, string routingKey, IDictionary arguments)
{
_Private_ExchangeBind(destination, source, routingKey, false, arguments);
@@ -1146,10 +1230,10 @@ public void ExchangeBind(string destination, string source, string routingKey, I
public async ValueTask ExchangeBindAsync(string destination, string source, string routingKey, IDictionary arguments)
{
- using var k = new ExchangeBindAsyncRpcContinuation(ContinuationTimeout);
await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
try
{
+ using var k = new ExchangeBindAsyncRpcContinuation(ContinuationTimeout);
Enqueue(k);
var method = new ExchangeBind(destination, source, routingKey, false, arguments);
@@ -1177,10 +1261,10 @@ public void ExchangeDeclare(string exchange, string type, bool durable, bool aut
public async ValueTask ExchangeDeclareAsync(string exchange, string type, bool passive, bool durable, bool autoDelete, IDictionary arguments)
{
- using var k = new ExchangeDeclareAsyncRpcContinuation(ContinuationTimeout);
await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
try
{
+ using var k = new ExchangeDeclareAsyncRpcContinuation(ContinuationTimeout);
Enqueue(k);
var method = new ExchangeDeclare(exchange, type, passive, durable, autoDelete, false, false, arguments);
@@ -1244,10 +1328,10 @@ public void ExchangeUnbind(string destination, string source, string routingKey,
public async ValueTask ExchangeUnbindAsync(string destination, string source, string routingKey, IDictionary arguments)
{
- using var k = new ExchangeUnbindAsyncRpcContinuation(ContinuationTimeout);
await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
try
{
+ using var k = new ExchangeUnbindAsyncRpcContinuation(ContinuationTimeout);
Enqueue(k);
var method = new ExchangeUnbind(destination, source, routingKey, false, arguments);
@@ -1285,10 +1369,10 @@ public QueueDeclareOk QueueDeclare(string queue, bool durable, bool exclusive, b
public async ValueTask QueueDeclareAsync(string queue, bool passive, bool durable, bool exclusive, bool autoDelete, IDictionary arguments)
{
- using var k = new QueueDeclareAsyncRpcContinuation(ContinuationTimeout);
await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
try
{
+ using var k = new QueueDeclareAsyncRpcContinuation(ContinuationTimeout);
Enqueue(k);
var method = new QueueDeclare(queue, passive, durable, exclusive, autoDelete, false, arguments);
@@ -1309,10 +1393,10 @@ public async ValueTask QueueDeclareAsync(string queue, bool pass
public async ValueTask QueueBindAsync(string queue, string exchange, string routingKey, IDictionary arguments)
{
- using var k = new QueueBindAsyncRpcContinuation(ContinuationTimeout);
await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
try
{
+ using var k = new QueueBindAsyncRpcContinuation(ContinuationTimeout);
Enqueue(k);
var method = new QueueBind(queue, exchange, routingKey, false, arguments);
@@ -1357,10 +1441,10 @@ public uint QueueDelete(string queue, bool ifUnused, bool ifEmpty)
public async ValueTask QueueDeleteAsync(string queue, bool ifUnused, bool ifEmpty)
{
- var k = new QueueDeleteAsyncRpcContinuation(ContinuationTimeout);
await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
try
{
+ var k = new QueueDeleteAsyncRpcContinuation(ContinuationTimeout);
Enqueue(k);
var method = new QueueDelete(queue, ifUnused, ifEmpty, false);
@@ -1384,14 +1468,117 @@ public uint QueuePurge(string queue)
return _Private_QueuePurge(queue, false);
}
+ public async ValueTask QueuePurgeAsync(string queue)
+ {
+ await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
+ try
+ {
+ var k = new QueuePurgeAsyncRpcContinuation(ContinuationTimeout);
+ Enqueue(k);
+
+ var method = new QueuePurge(queue, false);
+ await ModelSendAsync(method).ConfigureAwait(false);
+
+ return await k;
+ }
+ finally
+ {
+ _rpcSemaphore.Release();
+ }
+ }
+
public abstract void QueueUnbind(string queue, string exchange, string routingKey, IDictionary arguments);
+ public async ValueTask QueueUnbindAsync(string queue, string exchange, string routingKey, IDictionary arguments)
+ {
+ await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
+ try
+ {
+ using var k = new QueueUnbindAsyncRpcContinuation(ContinuationTimeout);
+ Enqueue(k);
+
+ var method = new QueueUnbind(queue, exchange, routingKey, arguments);
+ await ModelSendAsync(method).ConfigureAwait(false);
+
+ bool result = await k;
+ Debug.Assert(result);
+ return;
+ }
+ finally
+ {
+ _rpcSemaphore.Release();
+ }
+ }
+
public abstract void TxCommit();
+ public async ValueTask TxCommitAsync()
+ {
+ await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
+ try
+ {
+ using var k = new TxCommitAsyncRpcContinuation(ContinuationTimeout);
+ Enqueue(k);
+
+ var method = new TxCommit();
+ await ModelSendAsync(method).ConfigureAwait(false);
+
+ bool result = await k;
+ Debug.Assert(result);
+ return;
+ }
+ finally
+ {
+ _rpcSemaphore.Release();
+ }
+ }
+
public abstract void TxRollback();
+ public async ValueTask TxRollbackAsync()
+ {
+ await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
+ try
+ {
+ using var k = new TxRollbackAsyncRpcContinuation(ContinuationTimeout);
+ Enqueue(k);
+
+ var method = new TxRollback();
+ await ModelSendAsync(method).ConfigureAwait(false);
+
+ bool result = await k;
+ Debug.Assert(result);
+ return;
+ }
+ finally
+ {
+ _rpcSemaphore.Release();
+ }
+ }
+
public abstract void TxSelect();
+ public async ValueTask TxSelectAsync()
+ {
+ await _rpcSemaphore.WaitAsync().ConfigureAwait(false);
+ try
+ {
+ using var k = new TxSelectAsyncRpcContinuation(ContinuationTimeout);
+ Enqueue(k);
+
+ var method = new TxSelect();
+ await ModelSendAsync(method).ConfigureAwait(false);
+
+ bool result = await k;
+ Debug.Assert(result);
+ return;
+ }
+ finally
+ {
+ _rpcSemaphore.Release();
+ }
+ }
+
private List> _confirmsTaskCompletionSources;
public Task WaitForConfirmsAsync(CancellationToken token = default)
@@ -1465,13 +1652,16 @@ await CloseAsync(
new IOException("nack received")),
false).ConfigureAwait(false);
}
- catch (TaskCanceledException exception)
+ catch (TaskCanceledException)
{
+ const string msg = "timed out waiting for acks";
+ var ex = new IOException(msg);
await CloseAsync(new ShutdownEventArgs(ShutdownInitiator.Library,
Constants.ReplySuccess,
- "Timed out waiting for acks",
- exception),
+ msg,
+ ex),
false).ConfigureAwait(false);
+ throw ex;
}
}
diff --git a/projects/RabbitMQ.Client/client/impl/Connection.Receive.cs b/projects/RabbitMQ.Client/client/impl/Connection.Receive.cs
index 52bb68d2ec..bbfeda1be9 100644
--- a/projects/RabbitMQ.Client/client/impl/Connection.Receive.cs
+++ b/projects/RabbitMQ.Client/client/impl/Connection.Receive.cs
@@ -144,6 +144,8 @@ private void HandleMainLoopException(ShutdownEventArgs reason)
{
if (!SetCloseReason(reason))
{
+ // TODO LRB
+ // reason.Cause could be an Exception, should we use that?
LogCloseError("Unexpected Main Loop Exception while closing: " + reason, new Exception(reason.ToString()));
return;
}
diff --git a/projects/RabbitMQ.Client/client/impl/RecoveryAwareChannel.cs b/projects/RabbitMQ.Client/client/impl/RecoveryAwareChannel.cs
index 577726a56a..e9972d9743 100644
--- a/projects/RabbitMQ.Client/client/impl/RecoveryAwareChannel.cs
+++ b/projects/RabbitMQ.Client/client/impl/RecoveryAwareChannel.cs
@@ -81,11 +81,7 @@ public override ValueTask BasicAckAsync(ulong deliveryTag, bool multiple)
}
else
{
-#if NET6_0_OR_GREATER
- return ValueTask.CompletedTask;
-#else
- return new ValueTask(Task.CompletedTask);
-#endif
+ return default;
}
}
@@ -98,6 +94,19 @@ public override void BasicNack(ulong deliveryTag, bool multiple, bool requeue)
}
}
+ public override ValueTask BasicNackAsync(ulong deliveryTag, bool multiple, bool requeue)
+ {
+ ulong realTag = deliveryTag - ActiveDeliveryTagOffset;
+ if (realTag > 0 && realTag <= deliveryTag)
+ {
+ return base.BasicNackAsync(realTag, multiple, requeue);
+ }
+ else
+ {
+ return default;
+ }
+ }
+
public override void BasicReject(ulong deliveryTag, bool requeue)
{
ulong realTag = deliveryTag - ActiveDeliveryTagOffset;
@@ -116,11 +125,7 @@ public override ValueTask BasicRejectAsync(ulong deliveryTag, bool requeue)
}
else
{
-#if NET6_0_OR_GREATER
- return ValueTask.CompletedTask;
-#else
- return new ValueTask(Task.CompletedTask);
-#endif
+ return default;
}
}
}
diff --git a/projects/RabbitMQ.Client/client/impl/SocketFrameHandler.cs b/projects/RabbitMQ.Client/client/impl/SocketFrameHandler.cs
index c09bdd1b56..c02693152b 100644
--- a/projects/RabbitMQ.Client/client/impl/SocketFrameHandler.cs
+++ b/projects/RabbitMQ.Client/client/impl/SocketFrameHandler.cs
@@ -282,11 +282,7 @@ public ValueTask WriteAsync(ReadOnlyMemory memory)
{
if (_closed)
{
-#if NET6_0_OR_GREATER
- return ValueTask.CompletedTask;
-#else
- return new ValueTask(Task.CompletedTask);
-#endif
+ return default;
}
return _channelWriter.WriteAsync(memory);
diff --git a/projects/TestApplications/CreateChannel/CreateChannel.csproj b/projects/Test/Applications/CreateChannel/CreateChannel.csproj
similarity index 83%
rename from projects/TestApplications/CreateChannel/CreateChannel.csproj
rename to projects/Test/Applications/CreateChannel/CreateChannel.csproj
index 9fe7c226d7..9316c348d1 100644
--- a/projects/TestApplications/CreateChannel/CreateChannel.csproj
+++ b/projects/Test/Applications/CreateChannel/CreateChannel.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/projects/TestApplications/CreateChannel/Program.cs b/projects/Test/Applications/CreateChannel/Program.cs
similarity index 100%
rename from projects/TestApplications/CreateChannel/Program.cs
rename to projects/Test/Applications/CreateChannel/Program.cs
diff --git a/projects/TestApplications/MassPublish/MassPublish.csproj b/projects/Test/Applications/MassPublish/MassPublish.csproj
similarity index 83%
rename from projects/TestApplications/MassPublish/MassPublish.csproj
rename to projects/Test/Applications/MassPublish/MassPublish.csproj
index 9fe7c226d7..9316c348d1 100644
--- a/projects/TestApplications/MassPublish/MassPublish.csproj
+++ b/projects/Test/Applications/MassPublish/MassPublish.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/projects/TestApplications/MassPublish/Program.cs b/projects/Test/Applications/MassPublish/Program.cs
similarity index 100%
rename from projects/TestApplications/MassPublish/Program.cs
rename to projects/Test/Applications/MassPublish/Program.cs
diff --git a/projects/Test/Common/Common.csproj b/projects/Test/Common/Common.csproj
new file mode 100644
index 0000000000..215713acfa
--- /dev/null
+++ b/projects/Test/Common/Common.csproj
@@ -0,0 +1,38 @@
+
+
+
+ net6.0;net472
+
+
+
+ net6.0
+
+
+
+ ../../rabbit.snk
+ true
+ latest
+ 7.0
+ false
+
+
+
+
+
+ <_Parameter1>Integration, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5
+
+
+ <_Parameter1>SequentialIntegration, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/Test/Common/IntegrationFixture.cs b/projects/Test/Common/IntegrationFixture.cs
new file mode 100644
index 0000000000..19ad04909d
--- /dev/null
+++ b/projects/Test/Common/IntegrationFixture.cs
@@ -0,0 +1,282 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using RabbitMQ.Client;
+using RabbitMQ.Client.Framing.Impl;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Test
+{
+ public class IntegrationFixture : IDisposable
+ {
+ private static int _connectionIdx = 0;
+
+ protected readonly RabbitMQCtl _rabbitMQCtl;
+
+ protected ConnectionFactory _connFactory;
+ protected IConnection _conn;
+ protected IChannel _channel;
+
+ protected static readonly Encoding _encoding = new UTF8Encoding();
+ protected static readonly int _processorCount = Environment.ProcessorCount;
+
+ protected readonly ITestOutputHelper _output;
+ protected readonly string _testDisplayName;
+
+ public static readonly TimeSpan WaitSpan;
+ public static readonly TimeSpan LongWaitSpan;
+ public static readonly TimeSpan RecoveryInterval = TimeSpan.FromSeconds(2);
+ public static readonly Random S_Random;
+
+ static IntegrationFixture()
+ {
+ S_Random = new Random();
+
+ int threadCount = _processorCount * 16;
+ ThreadPool.SetMinThreads(threadCount, threadCount);
+
+ if (IsRunningInCI())
+ {
+ WaitSpan = TimeSpan.FromSeconds(30);
+ LongWaitSpan = TimeSpan.FromSeconds(60);
+ }
+ else
+ {
+ WaitSpan = TimeSpan.FromSeconds(15);
+ LongWaitSpan = TimeSpan.FromSeconds(30);
+ }
+ }
+
+ public IntegrationFixture(ITestOutputHelper output)
+ {
+ _output = output;
+ _rabbitMQCtl = new RabbitMQCtl(_output);
+
+ var type = _output.GetType();
+ var testMember = type.GetField("test", BindingFlags.Instance | BindingFlags.NonPublic);
+ var test = (ITest)testMember.GetValue(output);
+ _testDisplayName = test.DisplayName;
+
+ SetUp();
+ }
+
+ protected virtual void SetUp()
+ {
+ if (_connFactory == null)
+ {
+ _connFactory = CreateConnectionFactory();
+ }
+
+ if (_conn == null)
+ {
+ _conn = _connFactory.CreateConnection();
+ _channel = _conn.CreateChannel();
+ }
+ }
+
+ public virtual void Dispose()
+ {
+ if (_channel != null)
+ {
+ _channel.Dispose();
+ }
+
+ if (_conn != null)
+ {
+ _conn.Dispose();
+ }
+
+ TearDown();
+ }
+
+ protected virtual void TearDown()
+ {
+ }
+
+ protected static byte[] GetRandomBody(ushort size)
+ {
+ var body = new byte[size];
+#if NET6_0_OR_GREATER
+ Random.Shared.NextBytes(body);
+#else
+ S_Random.NextBytes(body);
+#endif
+ return body;
+ }
+
+ internal AutorecoveringConnection CreateAutorecoveringConnection(IList hostnames)
+ {
+ return CreateAutorecoveringConnection(RecoveryInterval, hostnames);
+ }
+
+ internal AutorecoveringConnection CreateAutorecoveringConnection(TimeSpan interval, IList hostnames)
+ {
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = true;
+ // tests that use this helper will likely list unreachable hosts;
+ // make sure we time out quickly on those
+ cf.RequestedConnectionTimeout = TimeSpan.FromSeconds(1);
+ cf.NetworkRecoveryInterval = interval;
+ return (AutorecoveringConnection)cf.CreateConnection(hostnames);
+ }
+
+ protected void WithTemporaryChannel(Action action)
+ {
+ IChannel channel = _conn.CreateChannel();
+
+ try
+ {
+ action(channel);
+ }
+ finally
+ {
+ channel.Abort();
+ }
+ }
+
+ protected string GenerateExchangeName()
+ {
+ return $"{_testDisplayName}-exchange-{Guid.NewGuid()}";
+ }
+
+ protected string GenerateQueueName()
+ {
+ return $"{_testDisplayName}-queue-{Guid.NewGuid()}";
+ }
+
+ protected void WithTemporaryNonExclusiveQueue(Action action)
+ {
+ WithTemporaryNonExclusiveQueue(_channel, action);
+ }
+
+ protected void WithTemporaryNonExclusiveQueue(IChannel channel, Action action)
+ {
+ WithTemporaryNonExclusiveQueue(channel, action, GenerateQueueName());
+ }
+
+ protected void WithTemporaryNonExclusiveQueue(IChannel channel, Action action, string queue)
+ {
+ try
+ {
+ channel.QueueDeclare(queue, false, false, false, null);
+ action(channel, queue);
+ }
+ finally
+ {
+ WithTemporaryChannel(tm => tm.QueueDelete(queue));
+ }
+ }
+
+ protected void AssertMessageCount(string q, uint count)
+ {
+ WithTemporaryChannel((m) =>
+ {
+ RabbitMQ.Client.QueueDeclareOk ok = m.QueueDeclarePassive(q);
+ Assert.Equal(count, ok.MessageCount);
+ });
+ }
+
+ protected void AssertShutdownError(ShutdownEventArgs args, int code)
+ {
+ Assert.Equal(args.ReplyCode, code);
+ }
+
+ protected void AssertPreconditionFailed(ShutdownEventArgs args)
+ {
+ AssertShutdownError(args, Constants.PreconditionFailed);
+ }
+
+ protected void WaitOn(object o)
+ {
+ lock (o)
+ {
+ Monitor.Wait(o, TimingFixture.TestTimeout);
+ }
+ }
+
+ protected void Wait(ManualResetEventSlim latch, string desc)
+ {
+ Assert.True(latch.Wait(WaitSpan),
+ $"waiting {WaitSpan.TotalSeconds} seconds on a latch for '{desc}' timed out");
+ }
+
+ protected void Wait(ManualResetEventSlim latch, TimeSpan timeSpan, string desc)
+ {
+ Assert.True(latch.Wait(timeSpan),
+ $"waiting {timeSpan.TotalSeconds} seconds on a latch for '{desc}' timed out");
+ }
+
+ protected ConnectionFactory CreateConnectionFactory()
+ {
+ string now = DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture);
+ return new ConnectionFactory
+ {
+ ClientProvidedName = $"{_testDisplayName}:{now}:{GetConnectionIdx()}",
+ ContinuationTimeout = WaitSpan,
+ HandshakeContinuationTimeout = WaitSpan,
+ };
+ }
+
+ private static int GetConnectionIdx()
+ {
+ return Interlocked.Increment(ref _connectionIdx);
+ }
+
+ private static bool IsRunningInCI()
+ {
+ bool ci = false;
+
+ if (bool.TryParse(Environment.GetEnvironmentVariable("CI"), out ci))
+ {
+ if (ci == true)
+ {
+ return ci;
+ }
+ }
+ else if (bool.TryParse(Environment.GetEnvironmentVariable("GITHUB_ACTIONS"), out ci))
+ {
+ if (ci == true)
+ {
+ return ci;
+ }
+ }
+
+ return ci;
+ }
+ }
+}
diff --git a/projects/Unit/RabbitMQCtl.cs b/projects/Test/Common/RabbitMQCtl.cs
similarity index 67%
rename from projects/Unit/RabbitMQCtl.cs
rename to projects/Test/Common/RabbitMQCtl.cs
index 7a12e97f51..8faf25ba39 100644
--- a/projects/Unit/RabbitMQCtl.cs
+++ b/projects/Test/Common/RabbitMQCtl.cs
@@ -29,72 +29,56 @@
// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
//---------------------------------------------------------------------------
-#pragma warning disable 2002
-
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
-using System.Threading;
+using RabbitMQ.Client;
+using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test
{
-#nullable enable
- public static class RabbitMQCtl
+ public class RabbitMQCtl
{
private static readonly char[] newLine = new char[] { '\n' };
private static readonly Func s_invokeRabbitMqCtl = GetRabbitMqCtlInvokeAction();
+ private static readonly Regex s_getConnectionProperties =
+ new Regex(@"^(?<[^>]*>)\s\[.*""connection_name"",""(?[^""]*)"".*\]$", RegexOptions.Multiline | RegexOptions.Compiled);
- private static Func GetRabbitMqCtlInvokeAction()
- {
- string precomputedArguments;
- string? envVariable = Environment.GetEnvironmentVariable("RABBITMQ_RABBITMQCTL_PATH");
+ private readonly ITestOutputHelper _output;
- if (!string.IsNullOrWhiteSpace(envVariable))
- {
- const string DockerPrefix = "DOCKER:";
- if (envVariable.StartsWith(DockerPrefix))
- {
- // Call docker
- precomputedArguments = $"exec {envVariable.Substring(DockerPrefix.Length)} rabbitmqctl ";
- return args => CreateProcess("docker", precomputedArguments + args);
- }
-
- // call the path from the env var
- return args => CreateProcess(envVariable, args);
- }
+ public RabbitMQCtl(ITestOutputHelper output)
+ {
+ _output = output;
+ }
- // Try default
- string umbrellaRabbitmqctlPath;
- string providedRabbitmqctlPath;
+ public void CloseConnection(IConnection conn)
+ {
+ CloseConnection(GetConnectionPid(conn.ClientProvidedName));
+ }
- if (IsRunningOnMonoOrDotNetCore())
- {
- umbrellaRabbitmqctlPath = "../../../../../../rabbit/scripts/rabbitmqctl";
- providedRabbitmqctlPath = "rabbitmqctl";
- }
- else
- {
- umbrellaRabbitmqctlPath = @"..\..\..\..\..\..\rabbit\scripts\rabbitmqctl.bat";
- providedRabbitmqctlPath = "rabbitmqctl.bat";
- }
+ public void AddUser(string username, string password)
+ {
+ ExecRabbitMQCtl($"add_user {username} {password}");
+ }
- string path = File.Exists(umbrellaRabbitmqctlPath) ? umbrellaRabbitmqctlPath : providedRabbitmqctlPath;
+ public void ChangePassword(string username, string password)
+ {
+ ExecRabbitMQCtl($"change_password {username} {password}");
+ }
- if (IsRunningOnMonoOrDotNetCore())
- {
- return args => CreateProcess(path, args);
- }
+ public void SetPermissions(string username, string conf, string write, string read)
+ {
+ ExecRabbitMQCtl($"set_permissions {username} \"{conf}\" \"{write}\" \"${read}\" ");
+ }
- precomputedArguments = $"/c \"\"{path}\" ";
- return args => CreateProcess("cmd.exe", precomputedArguments + args);
+ public void DeleteUser(string username)
+ {
+ ExecRabbitMQCtl($"delete_user {username}");
}
- //
- // Shelling Out
- //
- private static string ExecRabbitMQCtl(string args)
+ public string ExecRabbitMQCtl(string args)
{
try
{
@@ -118,66 +102,61 @@ private static string ExecRabbitMQCtl(string args)
}
}
- private static Process CreateProcess(string cmd, string arguments, string? workDirectory = null)
+ private void ReportExecFailure(string cmd, string args, string msg)
{
- return new Process
- {
- StartInfo =
- {
- CreateNoWindow = true,
- UseShellExecute = false,
- RedirectStandardError = true,
- RedirectStandardOutput = true,
- FileName = cmd,
- Arguments = arguments,
- WorkingDirectory = workDirectory
- }
- };
+ _output.WriteLine($"Failure while running {cmd} {args}:\n{msg}");
}
- private static void ReportExecFailure(string cmd, string args, string msg)
+ private static Func GetRabbitMqCtlInvokeAction()
{
- Console.WriteLine($"Failure while running {cmd} {args}:\n{msg}");
- }
+ string precomputedArguments;
+ string envVariable = Environment.GetEnvironmentVariable("RABBITMQ_RABBITMQCTL_PATH");
- private static bool IsRunningOnMonoOrDotNetCore()
- {
-#if NETCOREAPP
- return true;
-#else
- return Type.GetType("Mono.Runtime") != null;
-#endif
- }
+ if (!string.IsNullOrWhiteSpace(envVariable))
+ {
+ const string DockerPrefix = "DOCKER:";
+ if (envVariable.StartsWith(DockerPrefix))
+ {
+ // Call docker
+ precomputedArguments = $"exec {envVariable.Substring(DockerPrefix.Length)} rabbitmqctl ";
+ return args => CreateProcess("docker", precomputedArguments + args);
+ }
+ else
+ {
+ // call the path from the env var
+ return args => CreateProcess(envVariable, args);
+ }
+ }
- //
- // Flow Control
- //
- public static void Block(IConnection conn, Encoding encoding)
- {
- ExecRabbitMQCtl("set_vm_memory_high_watermark 0.000000001");
- // give rabbitmqctl some time to do its job
- Thread.Sleep(1200);
- Publish(conn, encoding);
- }
+ // Try default
+ string umbrellaRabbitmqctlPath;
+ string providedRabbitmqctlPath;
- public static void Publish(IConnection conn, Encoding encoding)
- {
- IChannel ch = conn.CreateChannel();
- ch.BasicPublish("amq.fanout", "", encoding.GetBytes("message"));
- }
+ if (IsRunningOnMonoOrDotNetCore())
+ {
+ umbrellaRabbitmqctlPath = "../../../../../../rabbit/scripts/rabbitmqctl";
+ providedRabbitmqctlPath = "rabbitmqctl";
+ }
+ else
+ {
+ umbrellaRabbitmqctlPath = @"..\..\..\..\..\..\rabbit\scripts\rabbitmqctl.bat";
+ providedRabbitmqctlPath = "rabbitmqctl.bat";
+ }
- public static void Unblock()
- {
- ExecRabbitMQCtl("set_vm_memory_high_watermark 0.4");
- }
+ string path = File.Exists(umbrellaRabbitmqctlPath) ? umbrellaRabbitmqctlPath : providedRabbitmqctlPath;
- public static void CloseConnection(IConnection conn)
- {
- CloseConnection(GetConnectionPid(conn.ClientProvidedName));
+ if (IsRunningOnMonoOrDotNetCore())
+ {
+ return args => CreateProcess(path, args);
+ }
+ else
+ {
+ precomputedArguments = $"/c \"\"{path}\" ";
+ return args => CreateProcess("cmd.exe", precomputedArguments + args);
+ }
}
- private static readonly Regex s_getConnectionProperties = new Regex(@"^(?<[^>]*>)\s\[.*""connection_name"",""(?[^""]*)"".*\]$", RegexOptions.Multiline | RegexOptions.Compiled);
- private static string GetConnectionPid(string connectionName)
+ private string GetConnectionPid(string connectionName)
{
string stdout = ExecRabbitMQCtl("list_connections --silent pid client_properties");
@@ -195,65 +174,41 @@ private static string GetConnectionPid(string connectionName)
throw new Exception($"No connection found with name: {connectionName}");
}
- private static void CloseConnection(string pid)
+ private void CloseConnection(string pid)
{
ExecRabbitMQCtl($"close_connection \"{pid}\" \"Closed via rabbitmqctl\"");
}
- public static void CloseAllConnections()
- {
- foreach (var pid in EnumerateConnectionsPid())
- {
- CloseConnection(pid);
- }
- }
-
- private static string[] EnumerateConnectionsPid()
- {
- string rabbitmqCtlResult = ExecRabbitMQCtl("list_connections --silent pid");
- return rabbitmqCtlResult.Split(newLine, StringSplitOptions.RemoveEmptyEntries);
- }
-
- public static void RestartRabbitMQ()
- {
- StopRabbitMQ();
- Thread.Sleep(500);
- StartRabbitMQ();
- AwaitRabbitMQ();
- }
-
- public static void StopRabbitMQ()
- {
- ExecRabbitMQCtl("stop_app");
- }
-
- public static void StartRabbitMQ()
- {
- ExecRabbitMQCtl("start_app");
- }
-
- public static void AwaitRabbitMQ()
- {
- ExecRabbitMQCtl("await_startup");
- }
-
- public static void AddUser(string username, string password)
- {
- ExecRabbitMQCtl($"add_user {username} {password}");
- }
- public static void ChangePassword(string username, string password)
+ private static bool IsRunningOnMonoOrDotNetCore()
{
- ExecRabbitMQCtl($"change_password {username} {password}");
+#if NETCOREAPP
+ return true;
+#else
+ return Type.GetType("Mono.Runtime") != null;
+#endif
}
- public static void SetPermissions(string username, string conf, string write, string read)
+ private static void Publish(IConnection conn, Encoding encoding)
{
- ExecRabbitMQCtl($"set_permissions {username} \"{conf}\" \"{write}\" \"${read}\" ");
+ IChannel ch = conn.CreateChannel();
+ ch.BasicPublish("amq.fanout", "", encoding.GetBytes("message"));
}
- public static void DeleteUser(string username)
+ private static Process CreateProcess(string cmd, string arguments, string workDirectory = null)
{
- ExecRabbitMQCtl($"delete_user {username}");
+ return new Process
+ {
+ StartInfo =
+ {
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ RedirectStandardError = true,
+ RedirectStandardOutput = true,
+ FileName = cmd,
+ Arguments = arguments,
+ WorkingDirectory = workDirectory
+ }
+ };
}
}
}
diff --git a/projects/RabbitMQ.Client/client/framing/BasicRecoverAsync.cs b/projects/Test/Common/TimingFixture.cs
similarity index 67%
rename from projects/RabbitMQ.Client/client/framing/BasicRecoverAsync.cs
rename to projects/Test/Common/TimingFixture.cs
index e2c76097ea..23ce3a31ce 100644
--- a/projects/RabbitMQ.Client/client/framing/BasicRecoverAsync.cs
+++ b/projects/Test/Common/TimingFixture.cs
@@ -30,30 +30,17 @@
//---------------------------------------------------------------------------
using System;
-using RabbitMQ.Client.client.framing;
-using RabbitMQ.Client.Impl;
-namespace RabbitMQ.Client.Framing.Impl
+namespace Test
{
- internal readonly struct BasicRecoverAsync : IOutgoingAmqpMethod
+ public static class TimingFixture
{
- public readonly bool _requeue;
-
- public BasicRecoverAsync(bool Requeue)
- {
- _requeue = Requeue;
- }
-
- public ProtocolCommandId ProtocolCommandId => ProtocolCommandId.BasicRecoverAsync;
-
- public int WriteTo(Span span)
- {
- return WireFormatting.WriteBits(ref span.GetStart(), _requeue);
- }
-
- public int GetRequiredBufferSize()
- {
- return 1; // bytes for bit fields
- }
+ public static readonly TimeSpan TimingInterval = TimeSpan.FromMilliseconds(300);
+ public static readonly TimeSpan TimingInterval_2X = TimeSpan.FromMilliseconds(600);
+ public static readonly TimeSpan TimingInterval_4X = TimeSpan.FromMilliseconds(1200);
+ public static readonly TimeSpan TimingInterval_8X = TimeSpan.FromMilliseconds(2400);
+ public static readonly TimeSpan TimingInterval_16X = TimeSpan.FromMilliseconds(4800);
+ public static readonly TimeSpan SafetyMargin = TimeSpan.FromMilliseconds(150);
+ public static readonly TimeSpan TestTimeout = TimeSpan.FromSeconds(5);
}
}
diff --git a/projects/Test/Integration/Integration.csproj b/projects/Test/Integration/Integration.csproj
new file mode 100644
index 0000000000..f775877b1e
--- /dev/null
+++ b/projects/Test/Integration/Integration.csproj
@@ -0,0 +1,57 @@
+
+
+
+ net6.0;net472
+
+
+
+ net6.0
+
+
+
+ ../../rabbit.snk
+ true
+ latest
+ 7.0
+ true
+
+
+
+
+
+
+
+
+
+
+ <_Parameter1>Xunit.CollectionBehavior.CollectionPerAssembly
+ <_Parameter1_IsLiteral>true
+ <_Parameter1_TypeName>Xunit.CollectionBehavior.CollectionPerAssembly
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
diff --git a/projects/Unit/SslEnv.cs b/projects/Test/Integration/SslEnv.cs
similarity index 84%
rename from projects/Unit/SslEnv.cs
rename to projects/Test/Integration/SslEnv.cs
index bc286a9d3d..583d90eaa3 100644
--- a/projects/Unit/SslEnv.cs
+++ b/projects/Test/Integration/SslEnv.cs
@@ -32,7 +32,7 @@
using System;
using System.IO;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class SslEnv
{
@@ -48,21 +48,24 @@ public SslEnv()
_sslDir = Environment.GetEnvironmentVariable("SSL_CERTS_DIR");
_certPassphrase = Environment.GetEnvironmentVariable("PASSWORD");
- Boolean.TryParse(Environment.GetEnvironmentVariable("GITHUB_ACTIONS"), out _isGithubActions);
-
_isSslConfigured = Directory.Exists(_sslDir) &&
(false == String.IsNullOrEmpty(_certPassphrase));
- if (_isGithubActions)
- {
- _hostname = "localhost";
- }
- else
+ if (_isSslConfigured)
{
- _hostname = System.Net.Dns.GetHostName();
- }
+ Boolean.TryParse(Environment.GetEnvironmentVariable("GITHUB_ACTIONS"), out _isGithubActions);
+
+ if (_isGithubActions)
+ {
+ _hostname = "localhost";
+ }
+ else
+ {
+ _hostname = System.Net.Dns.GetHostName();
+ }
- _certPath = Path.Combine(_sslDir, $"client_{_hostname}.p12");
+ _certPath = Path.Combine(_sslDir, $"client_{_hostname}.p12");
+ }
}
public string CertPath
diff --git a/projects/Test/Integration/TestAsyncConsumer.cs b/projects/Test/Integration/TestAsyncConsumer.cs
new file mode 100644
index 0000000000..6c6b69a69c
--- /dev/null
+++ b/projects/Test/Integration/TestAsyncConsumer.cs
@@ -0,0 +1,316 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using RabbitMQ.Client;
+using RabbitMQ.Client.Events;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Test.Integration
+{
+ public class TestAsyncConsumer : IntegrationFixture
+ {
+ public TestAsyncConsumer(ITestOutputHelper output) : base(output)
+ {
+ }
+
+ protected override void SetUp()
+ {
+ _connFactory = CreateConnectionFactory();
+ _connFactory.DispatchConsumersAsync = true;
+ _connFactory.ConsumerDispatchConcurrency = 2;
+
+ _conn = _connFactory.CreateConnection();
+ _channel = _conn.CreateChannel();
+ }
+
+ [Fact]
+ public void TestBasicRoundtrip()
+ {
+ QueueDeclareOk q = _channel.QueueDeclare();
+ byte[] body = _encoding.GetBytes("async-hi");
+ _channel.BasicPublish("", q.QueueName, body);
+ var consumer = new AsyncEventingBasicConsumer(_channel);
+ var are = new AutoResetEvent(false);
+ consumer.Received += async (o, a) =>
+ {
+ are.Set();
+ await Task.Yield();
+ };
+ string tag = _channel.BasicConsume(q.QueueName, true, consumer);
+ // ensure we get a delivery
+ bool waitRes = are.WaitOne(2000);
+ Assert.True(waitRes);
+ // unsubscribe and ensure no further deliveries
+ _channel.BasicCancel(tag);
+ _channel.BasicPublish("", q.QueueName, body);
+ bool waitResFalse = are.WaitOne(2000);
+ Assert.False(waitResFalse);
+ }
+
+ [Fact]
+ public async Task TestBasicRoundtripConcurrent()
+ {
+ QueueDeclareOk q = _channel.QueueDeclare();
+ string publish1 = get_unique_string(1024);
+ byte[] body = _encoding.GetBytes(publish1);
+ _channel.BasicPublish("", q.QueueName, body);
+
+ string publish2 = get_unique_string(1024);
+ body = _encoding.GetBytes(publish2);
+ _channel.BasicPublish("", q.QueueName, body);
+
+ var consumer = new AsyncEventingBasicConsumer(_channel);
+
+ var publish1SyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ var publish2SyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ var maximumWaitTime = TimeSpan.FromSeconds(10);
+ var tokenSource = new CancellationTokenSource(maximumWaitTime);
+ tokenSource.Token.Register(() =>
+ {
+ publish1SyncSource.TrySetResult(false);
+ publish2SyncSource.TrySetResult(false);
+ });
+
+ consumer.Received += async (o, a) =>
+ {
+ string decoded = Encoding.ASCII.GetString(a.Body.ToArray());
+ if (decoded == publish1)
+ {
+ publish1SyncSource.TrySetResult(true);
+ await publish2SyncSource.Task;
+ }
+ else if (decoded == publish2)
+ {
+ publish2SyncSource.TrySetResult(true);
+ await publish1SyncSource.Task;
+ }
+ };
+
+ _channel.BasicConsume(q.QueueName, true, consumer);
+
+ // ensure we get a delivery
+ await Task.WhenAll(publish1SyncSource.Task, publish2SyncSource.Task);
+
+ bool result1 = await publish1SyncSource.Task;
+ Assert.True(result1, $"1 - Non concurrent dispatch lead to deadlock after {maximumWaitTime}");
+
+ bool result2 = await publish2SyncSource.Task;
+ Assert.True(result2, $"2 - Non concurrent dispatch lead to deadlock after {maximumWaitTime}");
+ }
+
+ [Fact]
+ public async Task TestBasicRoundtripConcurrentManyMessages()
+ {
+ const int publish_total = 4096;
+ string queueName = $"{nameof(TestBasicRoundtripConcurrentManyMessages)}-{Guid.NewGuid()}";
+
+ string publish1 = get_unique_string(32768);
+ byte[] body1 = Encoding.ASCII.GetBytes(publish1);
+ string publish2 = get_unique_string(32768);
+ byte[] body2 = Encoding.ASCII.GetBytes(publish2);
+
+ QueueDeclareOk q = _channel.QueueDeclare(queue: queueName, exclusive: false, durable: true);
+ Assert.Equal(q.QueueName, queueName);
+
+ Task publishTask = Task.Run(async () =>
+ {
+ using (IChannel m = _conn.CreateChannel())
+ {
+ QueueDeclareOk q = _channel.QueueDeclare(queue: queueName, exclusive: false, durable: true);
+ for (int i = 0; i < publish_total; i++)
+ {
+ await _channel.BasicPublishAsync(string.Empty, queueName, body1);
+ await _channel.BasicPublishAsync(string.Empty, queueName, body2);
+ }
+ }
+ });
+
+ Task consumeTask = Task.Run(async () =>
+ {
+ var publish1SyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ var publish2SyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ var maximumWaitTime = TimeSpan.FromSeconds(30);
+ var tokenSource = new CancellationTokenSource(maximumWaitTime);
+ tokenSource.Token.Register(() =>
+ {
+ publish1SyncSource.TrySetResult(false);
+ publish2SyncSource.TrySetResult(false);
+ });
+
+ using (IChannel m = _conn.CreateChannel())
+ {
+ var consumer = new AsyncEventingBasicConsumer(m);
+
+ int publish1_count = 0;
+ int publish2_count = 0;
+
+ consumer.Received += async (o, a) =>
+ {
+ string decoded = Encoding.ASCII.GetString(a.Body.ToArray());
+ if (decoded == publish1)
+ {
+ if (Interlocked.Increment(ref publish1_count) >= publish_total)
+ {
+ publish1SyncSource.TrySetResult(true);
+ await publish2SyncSource.Task;
+ }
+ }
+ else if (decoded == publish2)
+ {
+ if (Interlocked.Increment(ref publish2_count) >= publish_total)
+ {
+ publish2SyncSource.TrySetResult(true);
+ await publish1SyncSource.Task;
+ }
+ }
+ };
+
+ _channel.BasicConsume(queueName, true, consumer);
+
+ // ensure we get a delivery
+ await Task.WhenAll(publish1SyncSource.Task, publish2SyncSource.Task);
+
+ bool result1 = await publish1SyncSource.Task;
+ Assert.True(result1, $"Non concurrent dispatch lead to deadlock after {maximumWaitTime}");
+
+ bool result2 = await publish2SyncSource.Task;
+ Assert.True(result2, $"Non concurrent dispatch lead to deadlock after {maximumWaitTime}");
+ }
+ });
+
+ await Task.WhenAll(publishTask, consumeTask);
+ }
+
+ [Fact]
+ public void TestBasicRoundtripNoWait()
+ {
+ QueueDeclareOk q = _channel.QueueDeclare();
+ byte[] body = _encoding.GetBytes("async-hi");
+ _channel.BasicPublish("", q.QueueName, body);
+ var consumer = new AsyncEventingBasicConsumer(_channel);
+ var are = new AutoResetEvent(false);
+ consumer.Received += async (o, a) =>
+ {
+ are.Set();
+ await Task.Yield();
+ };
+ string tag = _channel.BasicConsume(q.QueueName, true, consumer);
+ // ensure we get a delivery
+ bool waitRes = are.WaitOne(2000);
+ Assert.True(waitRes);
+ // unsubscribe and ensure no further deliveries
+ _channel.BasicCancelNoWait(tag);
+ _channel.BasicPublish("", q.QueueName, body);
+ bool waitResFalse = are.WaitOne(2000);
+ Assert.False(waitResFalse);
+ }
+
+ [Fact]
+ public async void ConcurrentEventingTestForReceived()
+ {
+ const int NumberOfThreads = 4;
+ const int NumberOfRegistrations = 5000;
+
+ var called = new byte[NumberOfThreads * NumberOfRegistrations];
+
+ QueueDeclareOk q = _channel.QueueDeclare();
+ var consumer = new AsyncEventingBasicConsumer(_channel);
+ _channel.BasicConsume(q.QueueName, true, consumer);
+ var countdownEvent = new CountdownEvent(NumberOfThreads);
+ var tasks = new Task[NumberOfThreads];
+ for (int i = 0; i < NumberOfThreads; i++)
+ {
+ int threadIndex = i;
+ tasks[i] = Task.Run(() =>
+ {
+ countdownEvent.Signal();
+ countdownEvent.Wait();
+ int start = threadIndex * NumberOfRegistrations;
+ for (int j = start; j < start + NumberOfRegistrations; j++)
+ {
+ int receivedIndex = j;
+ consumer.Received += (sender, eventArgs) =>
+ {
+ called[receivedIndex] = 1;
+ return Task.CompletedTask;
+ };
+ }
+ });
+ }
+
+ countdownEvent.Wait();
+ await Task.WhenAll(tasks);
+
+ // Add last receiver
+ var are = new AutoResetEvent(false);
+ consumer.Received += (o, a) =>
+ {
+ are.Set();
+ return Task.CompletedTask;
+ };
+
+ // Send message
+ _channel.BasicPublish("", q.QueueName, ReadOnlyMemory.Empty);
+ are.WaitOne(TimingFixture.TestTimeout);
+
+ // Check received messages
+ Assert.Equal(-1, called.AsSpan().IndexOf((byte)0));
+ }
+
+ [Fact]
+ public void NonAsyncConsumerShouldThrowInvalidOperationException()
+ {
+ QueueDeclareOk q = _channel.QueueDeclare();
+ byte[] body = _encoding.GetBytes("async-hi");
+ _channel.BasicPublish("", q.QueueName, body);
+ var consumer = new EventingBasicConsumer(_channel);
+ Assert.Throws(() => _channel.BasicConsume(q.QueueName, false, consumer));
+ }
+
+ private string get_unique_string(int string_length)
+ {
+ using (var rng = RandomNumberGenerator.Create())
+ {
+ var bit_count = (string_length * 6);
+ var byte_count = ((bit_count + 7) / 8); // rounded up
+ var bytes = new byte[byte_count];
+ rng.GetBytes(bytes);
+ return Convert.ToBase64String(bytes);
+ }
+ }
+ }
+}
diff --git a/projects/Unit/TestAsyncConsumerExceptions.cs b/projects/Test/Integration/TestAsyncConsumerExceptions.cs
similarity index 93%
rename from projects/Unit/TestAsyncConsumerExceptions.cs
rename to projects/Test/Integration/TestAsyncConsumerExceptions.cs
index 610f7388f8..4f0db2490b 100644
--- a/projects/Unit/TestAsyncConsumerExceptions.cs
+++ b/projects/Test/Integration/TestAsyncConsumerExceptions.cs
@@ -32,11 +32,12 @@
using System;
using System.Threading;
using System.Threading.Tasks;
+using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestAsyncConsumerExceptions : IntegrationFixture
{
@@ -52,28 +53,26 @@ protected void TestExceptionHandlingWith(IBasicConsumer consumer,
var resetEvent = new AutoResetEvent(false);
bool notified = false;
string q = _channel.QueueDeclare();
-
_channel.CallbackException += (m, evt) =>
{
- if (evt.Exception != TestException) return;
-
- notified = true;
- resetEvent.Set();
+ if (evt.Exception == TestException)
+ {
+ notified = true;
+ resetEvent.Set();
+ }
};
string tag = _channel.BasicConsume(q, true, consumer);
action(_channel, q, consumer, tag);
- resetEvent.WaitOne(2000);
+ resetEvent.WaitOne(TimeSpan.FromSeconds(2));
Assert.True(notified);
}
protected override void SetUp()
{
- _connFactory = new ConnectionFactory
- {
- DispatchConsumersAsync = true
- };
+ _connFactory = CreateConnectionFactory();
+ _connFactory.DispatchConsumersAsync = true;
_conn = _connFactory.CreateConnection();
_channel = _conn.CreateChannel();
diff --git a/projects/Unit/TestAuth.cs b/projects/Test/Integration/TestAuth.cs
similarity index 78%
rename from projects/Unit/TestAuth.cs
rename to projects/Test/Integration/TestAuth.cs
index 8761cd35d3..790de61041 100644
--- a/projects/Unit/TestAuth.cs
+++ b/projects/Test/Integration/TestAuth.cs
@@ -31,20 +31,29 @@
using RabbitMQ.Client.Exceptions;
using Xunit;
+using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
- [Collection("IntegrationFixture")]
- public class TestAuth
+ public class TestAuth : IntegrationFixture
{
+ public TestAuth(ITestOutputHelper output) : base(output)
+ {
+ }
+
+ protected override void SetUp()
+ {
+ Assert.Null(_connFactory);
+ Assert.Null(_conn);
+ Assert.Null(_channel);
+ }
+
[Fact]
public void TestAuthFailure()
{
- ConnectionFactory connFactory = new ConnectionFactory
- {
- UserName = "guest",
- Password = "incorrect-password"
- };
+ var connFactory = CreateConnectionFactory();
+ connFactory.UserName = "guest";
+ connFactory.Password = "incorrect-password";
try
{
diff --git a/projects/Unit/TestBasicGet.cs b/projects/Test/Integration/TestBasicGet.cs
similarity index 58%
rename from projects/Unit/TestBasicGet.cs
rename to projects/Test/Integration/TestBasicGet.cs
index 5dba9cee8d..9598ecd4a3 100644
--- a/projects/Unit/TestBasicGet.cs
+++ b/projects/Test/Integration/TestBasicGet.cs
@@ -29,11 +29,14 @@
// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
//---------------------------------------------------------------------------
+using System;
+using System.Threading.Tasks;
+using RabbitMQ.Client;
using RabbitMQ.Client.Exceptions;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestBasicGet : IntegrationFixture
{
@@ -74,5 +77,57 @@ public void TestBasicGetWithNonEmptyResponseAndAutoAckMode()
AssertMessageCount(queue, 0);
}, msg);
}
+
+ [Fact]
+ public async Task TestBasicGetAsync()
+ {
+ const string msg = "for async basic.get";
+
+ QueueDeclareOk queueResult = await _channel.QueueDeclareAsync(string.Empty, false, true, true, true, null);
+ string queueName = queueResult.QueueName;
+
+ await _channel.BasicPublishAsync(string.Empty, queueName, _encoding.GetBytes(msg), true);
+
+ BasicGetResult getResult = await _channel.BasicGetAsync(queueName, true);
+ Assert.Equal(msg, _encoding.GetString(getResult.Body.ToArray()));
+
+ QueueDeclareOk queueResultPassive = await _channel.QueueDeclareAsync(queueName, true, true, true, true, null);
+ Assert.Equal((uint)0, queueResultPassive.MessageCount);
+ }
+
+ private void EnsureNotEmpty(string q, string body)
+ {
+ WithTemporaryChannel(x => x.BasicPublish("", q, _encoding.GetBytes(body)));
+ }
+
+ private void WithClosedChannel(Action action)
+ {
+ IChannel channel = _conn.CreateChannel();
+ channel.Close();
+ action(channel);
+ }
+
+ private void WithNonEmptyQueue(Action action)
+ {
+ WithNonEmptyQueue(action, "msg");
+ }
+
+ private void WithNonEmptyQueue(Action action, string msg)
+ {
+ WithTemporaryNonExclusiveQueue((m, q) =>
+ {
+ EnsureNotEmpty(q, msg);
+ action(m, q);
+ });
+ }
+
+ private void WithEmptyQueue(Action action)
+ {
+ WithTemporaryNonExclusiveQueue((channel, queue) =>
+ {
+ channel.QueuePurge(queue);
+ action(channel, queue);
+ });
+ }
}
}
diff --git a/projects/Test/Integration/TestBasicPublish.cs b/projects/Test/Integration/TestBasicPublish.cs
new file mode 100644
index 0000000000..f32e3d9224
--- /dev/null
+++ b/projects/Test/Integration/TestBasicPublish.cs
@@ -0,0 +1,333 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2011-2020 VMware, Inc. or its affiliates. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using RabbitMQ.Client;
+using RabbitMQ.Client.Events;
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Sdk;
+
+namespace Test.Integration
+{
+ public class TestBasicPublish : IntegrationFixture
+ {
+ public TestBasicPublish(ITestOutputHelper output) : base(output)
+ {
+ }
+
+ protected override void SetUp()
+ {
+ /*
+ * NB: the only reason to do a custom SetUp
+ * is for the MaxMessageSize and QueuePurgeAsync tests,
+ * which use custom factory settings. This could be improved.
+ * TODO LRB rabbitmq/rabbitmq-server#1347
+ */
+ _connFactory = CreateConnectionFactory();
+ Assert.Null(_conn);
+ Assert.Null(_channel);
+ }
+
+ [Fact]
+ public void TestBasicRoundtripArray()
+ {
+ _conn = _connFactory.CreateConnection();
+ _channel = _conn.CreateChannel();
+
+ QueueDeclareOk q = _channel.QueueDeclare();
+ var bp = new BasicProperties();
+ byte[] sendBody = _encoding.GetBytes("hi");
+ byte[] consumeBody = null;
+ var consumer = new EventingBasicConsumer(_channel);
+ var are = new AutoResetEvent(false);
+ consumer.Received += async (o, a) =>
+ {
+ consumeBody = a.Body.ToArray();
+ are.Set();
+ await Task.Yield();
+ };
+ string tag = _channel.BasicConsume(q.QueueName, true, consumer);
+
+ _channel.BasicPublish("", q.QueueName, bp, sendBody);
+ bool waitResFalse = are.WaitOne(5000);
+ _channel.BasicCancel(tag);
+
+ Assert.True(waitResFalse);
+ Assert.Equal(sendBody, consumeBody);
+ }
+
+ [Fact]
+ public void TestBasicRoundtripCachedString()
+ {
+ _conn = _connFactory.CreateConnection();
+ _channel = _conn.CreateChannel();
+
+ CachedString exchangeName = new CachedString(string.Empty);
+ CachedString queueName = new CachedString(_channel.QueueDeclare().QueueName);
+ byte[] sendBody = _encoding.GetBytes("hi");
+ byte[] consumeBody = null;
+ var consumer = new EventingBasicConsumer(_channel);
+ var are = new AutoResetEvent(false);
+ consumer.Received += async (o, a) =>
+ {
+ consumeBody = a.Body.ToArray();
+ are.Set();
+ await Task.Yield();
+ };
+ string tag = _channel.BasicConsume(queueName.Value, true, consumer);
+
+ _channel.BasicPublish(exchangeName, queueName, sendBody);
+ bool waitResFalse = are.WaitOne(2000);
+ _channel.BasicCancel(tag);
+
+ Assert.True(waitResFalse);
+ Assert.Equal(sendBody, consumeBody);
+ }
+
+ [Fact]
+ public void TestBasicRoundtripReadOnlyMemory()
+ {
+ _conn = _connFactory.CreateConnection();
+ _channel = _conn.CreateChannel();
+
+ QueueDeclareOk q = _channel.QueueDeclare();
+ byte[] sendBody = _encoding.GetBytes("hi");
+ byte[] consumeBody = null;
+ var consumer = new EventingBasicConsumer(_channel);
+ var are = new AutoResetEvent(false);
+ consumer.Received += async (o, a) =>
+ {
+ consumeBody = a.Body.ToArray();
+ are.Set();
+ await Task.Yield();
+ };
+ string tag = _channel.BasicConsume(q.QueueName, true, consumer);
+
+ _channel.BasicPublish("", q.QueueName, new ReadOnlyMemory(sendBody));
+ bool waitResFalse = are.WaitOne(2000);
+ _channel.BasicCancel(tag);
+
+ Assert.True(waitResFalse);
+ Assert.Equal(sendBody, consumeBody);
+ }
+
+ [Fact]
+ public void CanNotModifyPayloadAfterPublish()
+ {
+ _conn = _connFactory.CreateConnection();
+ _channel = _conn.CreateChannel();
+
+ QueueDeclareOk q = _channel.QueueDeclare();
+ byte[] sendBody = new byte[1000];
+ var consumer = new EventingBasicConsumer(_channel);
+ var receivedMessage = new AutoResetEvent(false);
+ bool modified = true;
+ consumer.Received += (o, a) =>
+ {
+ if (a.Body.Span.IndexOf((byte)1) < 0)
+ {
+ modified = false;
+ }
+ receivedMessage.Set();
+ };
+ string tag = _channel.BasicConsume(q.QueueName, true, consumer);
+
+ _channel.BasicPublish("", q.QueueName, sendBody);
+ sendBody.AsSpan().Fill(1);
+
+ Assert.True(receivedMessage.WaitOne(5000));
+ Assert.False(modified, "Payload was modified after the return of BasicPublish");
+
+ _channel.BasicCancel(tag);
+ }
+
+ [Fact]
+ public void TestMaxMessageSize()
+ {
+ var re = new ManualResetEventSlim();
+ const ushort maxMsgSize = 1024;
+
+ int count = 0;
+ byte[] msg0 = _encoding.GetBytes("hi");
+ byte[] msg1 = GetRandomBody(maxMsgSize * 2);
+
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = false;
+ cf.TopologyRecoveryEnabled = false;
+ cf.MaxMessageSize = maxMsgSize;
+
+ bool sawConnectionShutdown = false;
+ bool sawChannelShutdown = false;
+ bool sawConsumerRegistered = false;
+ bool sawConsumerCancelled = false;
+
+ using (IConnection c = cf.CreateConnection())
+ {
+ c.ConnectionShutdown += (o, a) =>
+ {
+ sawConnectionShutdown = true;
+ };
+
+ Assert.Equal(maxMsgSize, cf.MaxMessageSize);
+ Assert.Equal(maxMsgSize, cf.Endpoint.MaxMessageSize);
+ Assert.Equal(maxMsgSize, c.Endpoint.MaxMessageSize);
+
+ using (IChannel channel = c.CreateChannel())
+ {
+ channel.ChannelShutdown += (o, a) =>
+ {
+ sawChannelShutdown = true;
+ };
+
+ channel.CallbackException += (o, a) =>
+ {
+ throw new XunitException("Unexpected channel.CallbackException");
+ };
+
+ QueueDeclareOk q = channel.QueueDeclare();
+
+ var consumer = new EventingBasicConsumer(channel);
+
+ consumer.Shutdown += (o, a) =>
+ {
+ re.Set();
+ };
+
+ consumer.Registered += (o, a) =>
+ {
+ sawConsumerRegistered = true;
+ };
+
+ consumer.Unregistered += (o, a) =>
+ {
+ throw new XunitException("Unexpected consumer.Unregistered");
+ };
+
+ consumer.ConsumerCancelled += (o, a) =>
+ {
+ sawConsumerCancelled = true;
+ };
+
+ consumer.Received += (o, a) =>
+ {
+ Interlocked.Increment(ref count);
+ };
+
+ string tag = channel.BasicConsume(q.QueueName, true, consumer);
+
+ channel.BasicPublish("", q.QueueName, msg0);
+ channel.BasicPublish("", q.QueueName, msg1);
+ Assert.True(re.Wait(TimeSpan.FromSeconds(5)));
+
+ Assert.Equal(1, count);
+ Assert.True(sawConnectionShutdown);
+ Assert.True(sawChannelShutdown);
+ Assert.True(sawConsumerRegistered);
+ Assert.True(sawConsumerCancelled);
+ }
+ }
+ }
+
+ [Fact]
+ public void TestPropertiesRoundtrip_Headers()
+ {
+ _conn = _connFactory.CreateConnection();
+ _channel = _conn.CreateChannel();
+
+ var subject = new BasicProperties
+ {
+ Headers = new Dictionary()
+ };
+
+ QueueDeclareOk q = _channel.QueueDeclare();
+ var bp = new BasicProperties() { Headers = new Dictionary() };
+ bp.Headers["Hello"] = "World";
+ byte[] sendBody = _encoding.GetBytes("hi");
+ byte[] consumeBody = null;
+ var consumer = new EventingBasicConsumer(_channel);
+ var are = new AutoResetEvent(false);
+ string response = null;
+ consumer.Received += async (o, a) =>
+ {
+ response = _encoding.GetString(a.BasicProperties.Headers["Hello"] as byte[]);
+ consumeBody = a.Body.ToArray();
+ are.Set();
+ await Task.Yield();
+ };
+
+ string tag = _channel.BasicConsume(q.QueueName, true, consumer);
+ _channel.BasicPublish("", q.QueueName, bp, sendBody);
+ bool waitResFalse = are.WaitOne(5000);
+ _channel.BasicCancel(tag);
+ Assert.True(waitResFalse);
+ Assert.Equal(sendBody, consumeBody);
+ Assert.Equal("World", response);
+ }
+
+ [Fact]
+ public async Task TestQueuePurgeAsync()
+ {
+ const int messageCount = 1024;
+
+ var publishSyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+
+ var cf = CreateConnectionFactory();
+ cf.DispatchConsumersAsync = true;
+
+ using IConnection connection = cf.CreateConnection();
+ using IChannel channel = connection.CreateChannel();
+
+ await channel.ConfirmSelectAsync();
+
+ QueueDeclareOk q = await channel.QueueDeclareAsync(string.Empty, false, false, true, false, null);
+ string queueName = q.QueueName;
+
+ var publishTask = Task.Run(async () =>
+ {
+ for (int i = 0; i < messageCount; i++)
+ {
+ byte[] body = _encoding.GetBytes(Guid.NewGuid().ToString());
+ await channel.BasicPublishAsync(string.Empty, queueName, body);
+ }
+ publishSyncSource.SetResult(true);
+ });
+
+ await channel.WaitForConfirmsOrDieAsync();
+ Assert.True(await publishSyncSource.Task);
+
+ Assert.Equal((uint)messageCount, await channel.QueuePurgeAsync(queueName));
+ }
+ }
+}
diff --git a/projects/Unit/TestChannelAllocation.cs b/projects/Test/Integration/TestChannelAllocation.cs
similarity index 66%
rename from projects/Unit/TestChannelAllocation.cs
rename to projects/Test/Integration/TestChannelAllocation.cs
index 3a4671f6bf..bdb3f71a37 100644
--- a/projects/Unit/TestChannelAllocation.cs
+++ b/projects/Test/Integration/TestChannelAllocation.cs
@@ -31,26 +31,28 @@
using System;
using System.Collections.Generic;
+using System.Threading.Tasks;
+using RabbitMQ.Client;
using RabbitMQ.Client.Impl;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
- [Collection("IntegrationFixture")]
public class TestChannelAllocation : IDisposable
{
public const int CHANNEL_COUNT = 100;
IConnection _c;
- public int ChannelNumber(IChannel channel)
- {
- return ((AutorecoveringChannel)channel).ChannelNumber;
- }
-
public TestChannelAllocation()
{
- _c = new ConnectionFactory().CreateConnection();
+ var cf = new ConnectionFactory
+ {
+ ContinuationTimeout = IntegrationFixture.WaitSpan,
+ HandshakeContinuationTimeout = IntegrationFixture.WaitSpan,
+ ClientProvidedName = nameof(TestChannelAllocation)
+ };
+ _c = cf.CreateConnection();
}
public void Dispose() => _c.Close();
@@ -58,44 +60,63 @@ public TestChannelAllocation()
[Fact]
public void AllocateInOrder()
{
+ var channels = new List();
for (int i = 1; i <= CHANNEL_COUNT; i++)
- Assert.Equal(i, ChannelNumber(_c.CreateChannel()));
+ {
+ IChannel channel = _c.CreateChannel();
+ channels.Add(channel);
+ Assert.Equal(i, ChannelNumber(channel));
+ }
+
+ foreach (IChannel channel in channels)
+ {
+ channel.Dispose();
+ }
}
[Fact]
public void AllocateAfterFreeingLast()
{
- IChannel ch = _c.CreateChannel();
- Assert.Equal(1, ChannelNumber(ch));
- ch.Close();
- ch = _c.CreateChannel();
- Assert.Equal(1, ChannelNumber(ch));
+ using IChannel ch0 = _c.CreateChannel();
+ Assert.Equal(1, ChannelNumber(ch0));
+ ch0.Close();
+
+ using IChannel ch1 = _c.CreateChannel();
+ Assert.Equal(1, ChannelNumber(ch1));
}
- public int CompareChannels(IChannel x, IChannel y)
+ [Fact]
+ public async Task AllocateAfterFreeingLastAsync()
{
- int i = ChannelNumber(x);
- int j = ChannelNumber(y);
- return (i < j) ? -1 : (i == j) ? 0 : 1;
+ using IChannel ch0 = _c.CreateChannel();
+ Assert.Equal(1, ChannelNumber(ch0));
+ await ch0.CloseAsync();
+
+ using IChannel ch1 = _c.CreateChannel();
+ Assert.Equal(1, ChannelNumber(ch1));
}
[Fact]
public void AllocateAfterFreeingMany()
{
- List channels = new List();
+ var channels = new List();
for (int i = 1; i <= CHANNEL_COUNT; i++)
+ {
channels.Add(_c.CreateChannel());
+ }
foreach (IChannel channel in channels)
{
channel.Close();
}
- channels = new List();
+ channels.Clear();
for (int j = 1; j <= CHANNEL_COUNT; j++)
+ {
channels.Add(_c.CreateChannel());
+ }
// In the current implementation the list should actually
// already be sorted, but we don't want to force that behaviour
@@ -103,7 +124,22 @@ public void AllocateAfterFreeingMany()
int k = 1;
foreach (IChannel channel in channels)
+ {
Assert.Equal(k++, ChannelNumber(channel));
+ channel.Close();
+ }
+ }
+
+ public int ChannelNumber(IChannel channel)
+ {
+ return ((AutorecoveringChannel)channel).ChannelNumber;
+ }
+
+ public int CompareChannels(IChannel x, IChannel y)
+ {
+ int i = ChannelNumber(x);
+ int j = ChannelNumber(y);
+ return (i < j) ? -1 : (i == j) ? 0 : 1;
}
}
}
diff --git a/projects/Unit/TestChannelShutdown.cs b/projects/Test/Integration/TestChannelShutdown.cs
similarity index 94%
rename from projects/Unit/TestChannelShutdown.cs
rename to projects/Test/Integration/TestChannelShutdown.cs
index 04b385fe68..21fbbc6c55 100644
--- a/projects/Unit/TestChannelShutdown.cs
+++ b/projects/Test/Integration/TestChannelShutdown.cs
@@ -31,11 +31,12 @@
using System;
using System.Threading;
+using RabbitMQ.Client;
using RabbitMQ.Client.Impl;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestChannelShutdown : IntegrationFixture
{
@@ -55,7 +56,7 @@ public void TestConsumerDispatcherShutdown()
};
Assert.False(m.ConsumerDispatcher.IsShutdown, "dispatcher should NOT be shut down before Close");
_channel.Close();
- Wait(latch, TimeSpan.FromSeconds(3));
+ Wait(latch, TimeSpan.FromSeconds(3), "channel shutdown");
Assert.True(m.ConsumerDispatcher.IsShutdown, "dispatcher should be shut down after Close");
}
}
diff --git a/projects/Unit/TestChannelSoftErrors.cs b/projects/Test/Integration/TestChannelSoftErrors.cs
similarity index 98%
rename from projects/Unit/TestChannelSoftErrors.cs
rename to projects/Test/Integration/TestChannelSoftErrors.cs
index 4f0bed6850..b369c6a048 100644
--- a/projects/Unit/TestChannelSoftErrors.cs
+++ b/projects/Test/Integration/TestChannelSoftErrors.cs
@@ -29,12 +29,13 @@
// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
//---------------------------------------------------------------------------
+using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.Exceptions;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestChannelSoftErrors : IntegrationFixture
{
diff --git a/projects/Test/Integration/TestConcurrentAccessWithSharedConnection.cs b/projects/Test/Integration/TestConcurrentAccessWithSharedConnection.cs
new file mode 100644
index 0000000000..0519b8d3ec
--- /dev/null
+++ b/projects/Test/Integration/TestConcurrentAccessWithSharedConnection.cs
@@ -0,0 +1,268 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using RabbitMQ.Client;
+using RabbitMQ.Client.Events;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Test.Integration
+{
+ public class TestConcurrentAccessWithSharedConnection : IntegrationFixture
+ {
+ private const ushort _messageCount = 200;
+
+ public TestConcurrentAccessWithSharedConnection(ITestOutputHelper output) : base(output)
+ {
+ }
+
+ protected override void SetUp()
+ {
+ _connFactory = CreateConnectionFactory();
+ _conn = _connFactory.CreateConnection();
+ // NB: not creating _channel because this test suite doesn't use it.
+ Assert.Null(_channel);
+ }
+
+ [Fact]
+ public void TestConcurrentChannelOpenAndPublishingWithBlankMessages()
+ {
+ TestConcurrentChannelOpenAndPublishingWithBody(Array.Empty(), 30);
+ }
+
+ [Fact]
+ public void TestConcurrentChannelOpenAndPublishingSize64()
+ {
+ TestConcurrentChannelOpenAndPublishingWithBodyOfSize(64);
+ }
+
+ [Fact]
+ public void TestConcurrentChannelOpenAndPublishingSize256()
+ {
+ TestConcurrentChannelOpenAndPublishingWithBodyOfSize(256);
+ }
+
+ [Fact]
+ public void TestConcurrentChannelOpenAndPublishingSize1024()
+ {
+ TestConcurrentChannelOpenAndPublishingWithBodyOfSize(1024);
+ }
+
+ [Fact]
+ public Task TestConcurrentChannelOpenAndPublishingWithBlankMessagesAsync()
+ {
+ return TestConcurrentChannelOpenAndPublishingWithBodyAsync(Array.Empty(), 30);
+ }
+
+ [Fact]
+ public Task TestConcurrentChannelOpenAndPublishingSize64Async()
+ {
+ return TestConcurrentChannelOpenAndPublishingWithBodyOfSizeAsync(64);
+ }
+
+ [Fact]
+ public Task TestConcurrentChannelOpenAndPublishingSize256Async()
+ {
+ return TestConcurrentChannelOpenAndPublishingWithBodyOfSizeAsync(256);
+ }
+
+ [Fact]
+ public Task TestConcurrentChannelOpenAndPublishingSize1024Async()
+ {
+ return TestConcurrentChannelOpenAndPublishingWithBodyOfSizeAsync(1024);
+ }
+
+ [Fact]
+ public void TestConcurrentChannelOpenCloseLoop()
+ {
+ TestConcurrentChannelOperations((conn) =>
+ {
+ using (IChannel ch = conn.CreateChannel())
+ {
+ ch.Close();
+ }
+ }, 50);
+ }
+
+ private void TestConcurrentChannelOpenAndPublishingWithBodyOfSize(ushort length, int iterations = 30)
+ {
+ byte[] body = GetRandomBody(length);
+ TestConcurrentChannelOpenAndPublishingWithBody(body, iterations);
+ }
+
+ private Task TestConcurrentChannelOpenAndPublishingWithBodyOfSizeAsync(ushort length, int iterations = 30)
+ {
+ byte[] body = GetRandomBody(length);
+ return TestConcurrentChannelOpenAndPublishingWithBodyAsync(body, iterations);
+ }
+
+ private void TestConcurrentChannelOpenAndPublishingWithBody(byte[] body, int iterations)
+ {
+ TestConcurrentChannelOperations((conn) =>
+ {
+ using (var localLatch = new ManualResetEvent(false))
+ {
+ // publishing on a shared channel is not supported
+ // and would missing the point of this test anyway
+ using (IChannel ch = _conn.CreateChannel())
+ {
+ ch.ConfirmSelect();
+
+ ch.BasicAcks += (object sender, BasicAckEventArgs e) =>
+ {
+ if (e.DeliveryTag >= _messageCount)
+ {
+ localLatch.Set();
+ }
+ };
+
+ ch.BasicNacks += (object sender, BasicNackEventArgs e) =>
+ {
+ localLatch.Set();
+ Assert.Fail("should never see a nack");
+ };
+
+ QueueDeclareOk q = ch.QueueDeclare(queue: string.Empty, exclusive: true, autoDelete: true);
+ for (ushort j = 0; j < _messageCount; j++)
+ {
+ ch.BasicPublish("", q.QueueName, body, true);
+ }
+
+ Assert.True(localLatch.WaitOne(WaitSpan));
+ }
+ }
+ }, iterations);
+ }
+
+ ///
+ /// Tests the concurrent channel open and publishing with body asynchronous.
+ ///
+ /// The body.
+ /// The iterations.
+ ///
+ private Task TestConcurrentChannelOpenAndPublishingWithBodyAsync(byte[] body, int iterations)
+ {
+ return TestConcurrentChannelOperationsAsync(async (conn) =>
+ {
+ var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ var tokenSource = new CancellationTokenSource(LongWaitSpan);
+ tokenSource.Token.Register(() =>
+ {
+ tcs.TrySetResult(false);
+ });
+
+ using (IChannel ch = _conn.CreateChannel())
+ {
+ await ch.ConfirmSelectAsync();
+
+ ch.BasicAcks += (object sender, BasicAckEventArgs e) =>
+ {
+ if (e.DeliveryTag >= _messageCount)
+ {
+ tcs.SetResult(true);
+ }
+ };
+
+ ch.BasicNacks += (object sender, BasicNackEventArgs e) =>
+ {
+ tcs.SetResult(false);
+ Assert.Fail(String.Format("async test channel saw a nack, deliveryTag: {0}, multiple: {1}", e.DeliveryTag, e.Multiple));
+ };
+
+ QueueDeclareOk q = await ch.QueueDeclareAsync(queue: string.Empty, passive: false, durable: false, exclusive: true, autoDelete: true, arguments: null);
+ for (ushort j = 0; j < _messageCount; j++)
+ {
+ await ch.BasicPublishAsync("", q.QueueName, body, mandatory: true);
+ }
+
+ Assert.True(await tcs.Task);
+ }
+ }, iterations);
+ }
+
+ private void TestConcurrentChannelOperations(Action actions, int iterations)
+ {
+ TestConcurrentChannelOperations(actions, iterations, LongWaitSpan);
+ }
+
+ private Task TestConcurrentChannelOperationsAsync(Func actions, int iterations)
+ {
+ return TestConcurrentChannelOperationsAsync(actions, iterations, LongWaitSpan);
+ }
+
+ private void TestConcurrentChannelOperations(Action actions, int iterations, TimeSpan timeout)
+ {
+ var tasks = new List();
+ for (int i = 0; i < _processorCount; i++)
+ {
+ tasks.Add(Task.Run(() =>
+ {
+ for (int j = 0; j < iterations; j++)
+ {
+ actions(_conn);
+ }
+ }));
+ }
+
+ Assert.True(Task.WaitAll(tasks.ToArray(), timeout));
+
+ // incorrect frame interleaving in these tests will result
+ // in an unrecoverable connection-level exception, thus
+ // closing the connection
+ Assert.True(_conn.IsOpen);
+ }
+
+ private async Task TestConcurrentChannelOperationsAsync(Func actions, int iterations, TimeSpan timeout)
+ {
+ var tasks = new List();
+ for (int i = 0; i < _processorCount; i++)
+ {
+ for (int j = 0; j < iterations; j++)
+ {
+ tasks.Add(actions(_conn));
+ }
+ }
+
+ Task t = Task.WhenAll(tasks);
+ await t;
+ Assert.Equal(TaskStatus.RanToCompletion, t.Status);
+
+ // incorrect frame interleaving in these tests will result
+ // in an unrecoverable connection-level exception, thus
+ // closing the connection
+ Assert.True(_conn.IsOpen);
+ }
+ }
+}
diff --git a/projects/Unit/TestConfirmSelect.cs b/projects/Test/Integration/TestConfirmSelect.cs
similarity index 97%
rename from projects/Unit/TestConfirmSelect.cs
rename to projects/Test/Integration/TestConfirmSelect.cs
index 102282f14f..eb3040f2de 100644
--- a/projects/Unit/TestConfirmSelect.cs
+++ b/projects/Test/Integration/TestConfirmSelect.cs
@@ -29,10 +29,11 @@
// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
//---------------------------------------------------------------------------
+using RabbitMQ.Client;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestConfirmSelect : IntegrationFixture
{
diff --git a/projects/Unit/TestConnectionFactory.cs b/projects/Test/Integration/TestConnectionFactory.cs
similarity index 66%
rename from projects/Unit/TestConnectionFactory.cs
rename to projects/Test/Integration/TestConnectionFactory.cs
index 994a18a065..e1c3e356ab 100644
--- a/projects/Unit/TestConnectionFactory.cs
+++ b/projects/Test/Integration/TestConnectionFactory.cs
@@ -30,14 +30,28 @@
//---------------------------------------------------------------------------
using System.Collections.Generic;
+using RabbitMQ.Client;
using RabbitMQ.Client.Exceptions;
using Xunit;
+using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
- [Collection("IntegrationFixture")]
- public class TestConnectionFactory
+ public class TestConnectionFactory : IntegrationFixture
{
+ public TestConnectionFactory(ITestOutputHelper output) : base(output)
+ {
+ }
+
+ protected override void SetUp()
+ {
+ // NB: nothing to do here since each test creates its own factory,
+ // connections and channels
+ Assert.Null(_connFactory);
+ Assert.Null(_conn);
+ Assert.Null(_channel);
+ }
+
[Fact]
public void TestProperties()
{
@@ -73,94 +87,95 @@ public void TestProperties()
[Fact]
public void TestCreateConnectionUsesSpecifiedPort()
{
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = true,
- HostName = "localhost",
- Port = 1234
- };
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = true;
+ cf.HostName = "localhost";
+ cf.Port = 1234;
- Assert.Throws(() => { using IConnection conn = cf.CreateConnection(); });
+ Assert.Throws(() =>
+ {
+ using IConnection conn = cf.CreateConnection();
+ });
}
[Fact]
public void TestCreateConnectionWithClientProvidedNameUsesSpecifiedPort()
{
- var cf = new ConnectionFactory
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = true;
+ cf.HostName = "localhost";
+ cf.Port = 123;
+
+ Assert.Throws(() =>
{
- AutomaticRecoveryEnabled = true,
- HostName = "localhost",
- Port = 1234
- };
- Assert.Throws(() => { using IConnection conn = cf.CreateConnection("some_name"); });
+ using IConnection conn = cf.CreateConnection();
+ });
}
[Fact]
public void TestCreateConnectionWithClientProvidedNameUsesDefaultName()
{
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = false,
- ClientProvidedName = "some_name"
- };
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = false;
+ string expectedName = cf.ClientProvidedName;
+
using (IConnection conn = cf.CreateConnection())
{
- Assert.Equal("some_name", conn.ClientProvidedName);
- Assert.Equal("some_name", conn.ClientProperties["connection_name"]);
+ Assert.Equal(expectedName, conn.ClientProvidedName);
+ Assert.Equal(expectedName, conn.ClientProperties["connection_name"]);
}
}
[Fact]
public void TestCreateConnectionWithClientProvidedNameUsesNameArgumentValue()
{
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = false
- };
- using (IConnection conn = cf.CreateConnection("some_name"))
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = false;
+ string expectedName = cf.ClientProvidedName;
+
+ using (IConnection conn = cf.CreateConnection(expectedName))
{
- Assert.Equal("some_name", conn.ClientProvidedName);
- Assert.Equal("some_name", conn.ClientProperties["connection_name"]);
+ Assert.Equal(expectedName, conn.ClientProvidedName);
+ Assert.Equal(expectedName, conn.ClientProperties["connection_name"]);
}
}
[Fact]
public void TestCreateConnectionWithClientProvidedNameAndAutorecoveryUsesNameArgumentValue()
{
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = true
- };
- using (IConnection conn = cf.CreateConnection("some_name"))
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = true;
+ string expectedName = cf.ClientProvidedName;
+
+ using (IConnection conn = cf.CreateConnection(expectedName))
{
- Assert.Equal("some_name", conn.ClientProvidedName);
- Assert.Equal("some_name", conn.ClientProperties["connection_name"]);
+ Assert.Equal(expectedName, conn.ClientProvidedName);
+ Assert.Equal(expectedName, conn.ClientProperties["connection_name"]);
}
}
[Fact]
public void TestCreateConnectionAmqpTcpEndpointListAndClientProvidedName()
{
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = true
- };
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = true;
+ string expectedName = cf.ClientProvidedName;
+
var xs = new List { new AmqpTcpEndpoint("localhost") };
- using (IConnection conn = cf.CreateConnection(xs, "some_name"))
+ using (IConnection conn = cf.CreateConnection(xs, expectedName))
{
- Assert.Equal("some_name", conn.ClientProvidedName);
- Assert.Equal("some_name", conn.ClientProperties["connection_name"]);
+ Assert.Equal(expectedName, conn.ClientProvidedName);
+ Assert.Equal(expectedName, conn.ClientProperties["connection_name"]);
}
}
[Fact]
public void TestCreateConnectionUsesDefaultPort()
{
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = true,
- HostName = "localhost"
- };
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = true;
+ cf.HostName = "localhost";
+
using (IConnection conn = cf.CreateConnection())
{
Assert.Equal(5672, conn.Endpoint.Port);
@@ -170,11 +185,9 @@ public void TestCreateConnectionUsesDefaultPort()
[Fact]
public void TestCreateConnectionUsesDefaultMaxMessageSize()
{
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = true,
- HostName = "localhost"
- };
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = true;
+ cf.HostName = "localhost";
Assert.Equal(ConnectionFactory.DefaultMaxMessageSize, cf.MaxMessageSize);
Assert.Equal(ConnectionFactory.DefaultMaxMessageSize, cf.Endpoint.MaxMessageSize);
@@ -188,12 +201,11 @@ public void TestCreateConnectionUsesDefaultMaxMessageSize()
[Fact]
public void TestCreateConnectionWithoutAutoRecoverySelectsAHostFromTheList()
{
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = false,
- HostName = "not_localhost"
- };
- IConnection conn = cf.CreateConnection(new List { "localhost" }, "oregano");
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = false;
+ cf.HostName = "not_localhost";
+
+ IConnection conn = cf.CreateConnection(new List { "localhost" });
conn.Close();
conn.Dispose();
Assert.Equal("not_localhost", cf.HostName);
@@ -203,12 +215,10 @@ public void TestCreateConnectionWithoutAutoRecoverySelectsAHostFromTheList()
[Fact]
public void TestCreateConnectionWithAutoRecoveryUsesAmqpTcpEndpoint()
{
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = true,
- HostName = "not_localhost",
- Port = 1234
- };
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = true;
+ cf.HostName = "not_localhost";
+ cf.Port = 1234;
var ep = new AmqpTcpEndpoint("localhost");
using (IConnection conn = cf.CreateConnection(new List { ep })) { }
}
@@ -216,10 +226,8 @@ public void TestCreateConnectionWithAutoRecoveryUsesAmqpTcpEndpoint()
[Fact]
public void TestCreateConnectionWithAutoRecoveryUsesInvalidAmqpTcpEndpoint()
{
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = true
- };
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = true;
var ep = new AmqpTcpEndpoint("localhost", 1234);
Assert.Throws(() => { using IConnection conn = cf.CreateConnection(new List { ep }); });
}
@@ -227,11 +235,9 @@ public void TestCreateConnectionWithAutoRecoveryUsesInvalidAmqpTcpEndpoint()
[Fact]
public void TestCreateConnectionUsesAmqpTcpEndpoint()
{
- var cf = new ConnectionFactory
- {
- HostName = "not_localhost",
- Port = 1234
- };
+ var cf = CreateConnectionFactory();
+ cf.HostName = "not_localhost";
+ cf.Port = 1234;
var ep = new AmqpTcpEndpoint("localhost");
using (IConnection conn = cf.CreateConnection(new List { ep })) { }
}
@@ -239,33 +245,34 @@ public void TestCreateConnectionUsesAmqpTcpEndpoint()
[Fact]
public void TestCreateConnectionWithForcedAddressFamily()
{
- var cf = new ConnectionFactory
- {
- HostName = "not_localhost"
- };
+ var cf = CreateConnectionFactory();
+ cf.HostName = "not_localhost";
var ep = new AmqpTcpEndpoint("localhost")
{
AddressFamily = System.Net.Sockets.AddressFamily.InterNetwork
};
cf.Endpoint = ep;
- using (IConnection conn = cf.CreateConnection()) { };
+ using IConnection conn = cf.CreateConnection();
}
[Fact]
public void TestCreateConnectionUsesInvalidAmqpTcpEndpoint()
{
- var cf = new ConnectionFactory();
+ var cf = CreateConnectionFactory();
var ep = new AmqpTcpEndpoint("localhost", 1234);
- Assert.Throws(() => { using (IConnection conn = cf.CreateConnection(new List { ep })) { } });
+ Assert.Throws(() =>
+ {
+ using IConnection conn = cf.CreateConnection(new List { ep });
+ });
}
[Fact]
public void TestCreateConnectioUsesValidEndpointWhenMultipleSupplied()
{
- var cf = new ConnectionFactory();
+ var cf = CreateConnectionFactory();
var invalidEp = new AmqpTcpEndpoint("not_localhost");
var ep = new AmqpTcpEndpoint("localhost");
- using (IConnection conn = cf.CreateConnection(new List { invalidEp, ep })) { };
+ using IConnection conn = cf.CreateConnection(new List { invalidEp, ep });
}
[Fact]
@@ -277,7 +284,7 @@ public void TestCreateAmqpTCPEndPointOverridesMaxMessageSizeWhenGreaterThanMaxim
[Fact]
public void TestCreateConnectionUsesConfiguredMaxMessageSize()
{
- var cf = new ConnectionFactory();
+ var cf = CreateConnectionFactory();
cf.MaxMessageSize = 1500;
using (IConnection conn = cf.CreateConnection())
{
@@ -287,7 +294,7 @@ public void TestCreateConnectionUsesConfiguredMaxMessageSize()
[Fact]
public void TestCreateConnectionWithAmqpEndpointListUsesAmqpTcpEndpointMaxMessageSize()
{
- var cf = new ConnectionFactory();
+ var cf = CreateConnectionFactory();
cf.MaxMessageSize = 1500;
var ep = new AmqpTcpEndpoint("localhost");
Assert.Equal(ConnectionFactory.DefaultMaxMessageSize, ep.MaxMessageSize);
@@ -300,7 +307,7 @@ public void TestCreateConnectionWithAmqpEndpointListUsesAmqpTcpEndpointMaxMessag
[Fact]
public void TestCreateConnectionWithAmqpEndpointResolverUsesAmqpTcpEndpointMaxMessageSize()
{
- var cf = new ConnectionFactory();
+ var cf = CreateConnectionFactory();
cf.MaxMessageSize = 1500;
var ep = new AmqpTcpEndpoint("localhost", -1, new SslOption(), 1200);
using (IConnection conn = cf.CreateConnection(new List { ep }))
@@ -312,7 +319,7 @@ public void TestCreateConnectionWithAmqpEndpointResolverUsesAmqpTcpEndpointMaxMe
[Fact]
public void TestCreateConnectionWithHostnameListUsesConnectionFactoryMaxMessageSize()
{
- var cf = new ConnectionFactory();
+ var cf = CreateConnectionFactory();
cf.MaxMessageSize = 1500;
using (IConnection conn = cf.CreateConnection(new List { "localhost" }))
{
diff --git a/projects/Unit/TestConnectionFactoryContinuationTimeout.cs b/projects/Test/Integration/TestConnectionFactoryContinuationTimeout.cs
similarity index 85%
rename from projects/Unit/TestConnectionFactoryContinuationTimeout.cs
rename to projects/Test/Integration/TestConnectionFactoryContinuationTimeout.cs
index 5eb79b4ef8..01a9bd0513 100644
--- a/projects/Unit/TestConnectionFactoryContinuationTimeout.cs
+++ b/projects/Test/Integration/TestConnectionFactoryContinuationTimeout.cs
@@ -30,10 +30,11 @@
//---------------------------------------------------------------------------
using System;
+using RabbitMQ.Client;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestConnectionFactoryContinuationTimeout : IntegrationFixture
{
@@ -60,5 +61,13 @@ public void TestConnectionFactoryContinuationTimeoutOnNonRecoveringConnection()
Assert.Equal(continuationTimeout, c.CreateChannel().ContinuationTimeout);
}
}
+
+ private IConnection CreateConnectionWithContinuationTimeout(bool automaticRecoveryEnabled, TimeSpan continuationTimeout)
+ {
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = automaticRecoveryEnabled;
+ cf.ContinuationTimeout = continuationTimeout;
+ return cf.CreateConnection();
+ }
}
}
diff --git a/projects/Unit/TestConnectionShutdown.cs b/projects/Test/Integration/TestConnectionShutdown.cs
similarity index 84%
rename from projects/Unit/TestConnectionShutdown.cs
rename to projects/Test/Integration/TestConnectionShutdown.cs
index d063742fde..5489ec4611 100644
--- a/projects/Unit/TestConnectionShutdown.cs
+++ b/projects/Test/Integration/TestConnectionShutdown.cs
@@ -31,12 +31,13 @@
using System;
using System.Threading;
+using RabbitMQ.Client;
using RabbitMQ.Client.Framing.Impl;
using RabbitMQ.Client.Impl;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestConnectionShutdown : IntegrationFixture
{
@@ -47,9 +48,6 @@ public TestConnectionShutdown(ITestOutputHelper output) : base(output)
[Fact]
public void TestCleanClosureWithSocketClosedOutOfBand()
{
- _conn = CreateAutorecoveringConnection();
- _channel = _conn.CreateChannel();
-
var latch = new ManualResetEventSlim(false);
_channel.ChannelShutdown += (channel, args) =>
{
@@ -57,18 +55,15 @@ public void TestCleanClosureWithSocketClosedOutOfBand()
};
var c = (AutorecoveringConnection)_conn;
- c.FrameHandler.Close();
+ c.CloseFrameHandler();
_conn.Close(TimeSpan.FromSeconds(4));
- Wait(latch, TimeSpan.FromSeconds(5));
+ Wait(latch, TimeSpan.FromSeconds(5), "channel shutdown");
}
[Fact]
public void TestAbortWithSocketClosedOutOfBand()
{
- _conn = CreateAutorecoveringConnection();
- _channel = _conn.CreateChannel();
-
var latch = new ManualResetEventSlim(false);
_channel.ChannelShutdown += (channel, args) =>
{
@@ -76,19 +71,16 @@ public void TestAbortWithSocketClosedOutOfBand()
};
var c = (AutorecoveringConnection)_conn;
- c.FrameHandler.Close();
+ c.CloseFrameHandler();
_conn.Abort();
// default Connection.Abort() timeout and then some
- Wait(latch, TimeSpan.FromSeconds(6));
+ Wait(latch, TimeSpan.FromSeconds(6), "channel shutdown");
}
[Fact]
public void TestDisposedWithSocketClosedOutOfBand()
{
- _conn = CreateAutorecoveringConnection();
- _channel = _conn.CreateChannel();
-
var latch = new ManualResetEventSlim(false);
_channel.ChannelShutdown += (channel, args) =>
{
@@ -96,10 +88,10 @@ public void TestDisposedWithSocketClosedOutOfBand()
};
var c = (AutorecoveringConnection)_conn;
- c.FrameHandler.Close();
+ c.CloseFrameHandler();
_conn.Dispose();
- Wait(latch, TimeSpan.FromSeconds(3));
+ Wait(latch, TimeSpan.FromSeconds(3), "channel shutdown");
}
[Fact]
@@ -113,7 +105,7 @@ public void TestShutdownSignalPropagationToChannels()
};
_conn.Close();
- Wait(latch, TimeSpan.FromSeconds(3));
+ Wait(latch, TimeSpan.FromSeconds(3), "channel shutdown");
}
[Fact]
@@ -128,7 +120,7 @@ public void TestConsumerDispatcherShutdown()
};
Assert.False(m.ConsumerDispatcher.IsShutdown, "dispatcher should NOT be shut down before Close");
_conn.Close();
- Wait(latch, TimeSpan.FromSeconds(3));
+ Wait(latch, TimeSpan.FromSeconds(3), "channel shutdown");
Assert.True(m.ConsumerDispatcher.IsShutdown, "dispatcher should be shut down after Close");
}
}
diff --git a/projects/Test/Integration/TestConsumer.cs b/projects/Test/Integration/TestConsumer.cs
new file mode 100644
index 0000000000..42db2cb6f6
--- /dev/null
+++ b/projects/Test/Integration/TestConsumer.cs
@@ -0,0 +1,297 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using RabbitMQ.Client;
+using RabbitMQ.Client.Events;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Test.Integration
+{
+ public class TestConsumer : IntegrationFixture
+ {
+ private readonly ShutdownEventArgs _closeArgs = new ShutdownEventArgs(ShutdownInitiator.Application, Constants.ReplySuccess, "normal shutdown");
+
+ public TestConsumer(ITestOutputHelper output) : base(output)
+ {
+ }
+
+ protected override void SetUp()
+ {
+ // NB: nothing to do here
+ Assert.Null(_connFactory);
+ Assert.Null(_conn);
+ Assert.Null(_channel);
+ }
+
+ [Fact]
+ public async Task TestBasicRoundtripConcurrent()
+ {
+ var cf = CreateConnectionFactory();
+ cf.ConsumerDispatchConcurrency = 2;
+
+ using IConnection conn = cf.CreateConnection();
+ using IChannel channel = conn.CreateChannel();
+
+ QueueDeclareOk q = channel.QueueDeclare();
+ const string publish1 = "sync-hi-1";
+ byte[] body = _encoding.GetBytes(publish1);
+ channel.BasicPublish("", q.QueueName, body);
+ const string publish2 = "sync-hi-2";
+ body = _encoding.GetBytes(publish2);
+ channel.BasicPublish("", q.QueueName, body);
+
+ var consumer = new EventingBasicConsumer(channel);
+
+ var publish1SyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ var publish2SyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ var maximumWaitTime = TimeSpan.FromSeconds(10);
+ var tokenSource = new CancellationTokenSource(maximumWaitTime);
+ tokenSource.Token.Register(() =>
+ {
+ publish1SyncSource.TrySetResult(false);
+ publish2SyncSource.TrySetResult(false);
+ });
+
+ consumer.Received += (o, a) =>
+ {
+ switch (_encoding.GetString(a.Body.ToArray()))
+ {
+ case publish1:
+ publish1SyncSource.TrySetResult(true);
+ break;
+ case publish2:
+ publish2SyncSource.TrySetResult(true);
+ break;
+ }
+ };
+
+ channel.BasicConsume(q.QueueName, true, consumer);
+
+ await Task.WhenAll(publish1SyncSource.Task, publish2SyncSource.Task);
+
+ bool result1 = await publish1SyncSource.Task;
+ Assert.True(result1, $"Non concurrent dispatch lead to deadlock after {maximumWaitTime}");
+
+ bool result2 = await publish1SyncSource.Task;
+ Assert.True(result2, $"Non concurrent dispatch lead to deadlock after {maximumWaitTime}");
+
+ // Note: closing channel explicitly just to test it.
+ await channel.CloseAsync(_closeArgs, false);
+ }
+
+ [Fact]
+ public async Task TestBasicRejectAsync()
+ {
+ var publishSyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+
+ var cf = CreateConnectionFactory();
+ cf.DispatchConsumersAsync = true;
+
+ using IConnection connection = cf.CreateConnection();
+ using IChannel channel = connection.CreateChannel();
+
+ var consumer = new AsyncEventingBasicConsumer(channel);
+ consumer.Received += async (object sender, BasicDeliverEventArgs args) =>
+ {
+ var c = sender as AsyncEventingBasicConsumer;
+ Assert.NotNull(c);
+ await channel.BasicCancelAsync(c.ConsumerTags[0]);
+ await channel.BasicRejectAsync(args.DeliveryTag, true);
+ publishSyncSource.SetResult(true);
+ };
+
+ QueueDeclareOk q = await channel.QueueDeclareAsync(string.Empty, false, false, true, false, null);
+ string queueName = q.QueueName;
+ const string publish1 = "sync-hi-1";
+ byte[] body = _encoding.GetBytes(publish1);
+ await channel.BasicPublishAsync(string.Empty, queueName, body);
+
+ await channel.BasicConsumeAsync(queue: queueName, autoAck: false,
+ consumerTag: string.Empty, noLocal: false, exclusive: false,
+ arguments: null, consumer);
+
+ Assert.True(await publishSyncSource.Task);
+
+ uint messageCount, consumerCount = 0;
+ ushort tries = 5;
+ do
+ {
+ QueueDeclareOk result = await channel.QueueDeclareAsync(queue: queueName, passive: true, false, false, false, null);
+ consumerCount = result.ConsumerCount;
+ messageCount = result.MessageCount;
+ if (consumerCount == 0 && messageCount > 0)
+ {
+ break;
+ }
+ else
+ {
+ await Task.Delay(500);
+ }
+ } while (tries-- > 0);
+
+ if (tries == 0)
+ {
+ Assert.Fail("[ERROR] failed waiting for MessageCount > 0 && ConsumerCount == 0");
+ }
+ else
+ {
+ Assert.Equal((uint)1, messageCount);
+ Assert.Equal((uint)0, consumerCount);
+ }
+
+ // Note: closing channel explicitly just to test it.
+ await channel.CloseAsync(_closeArgs, false);
+ }
+
+ [Fact]
+ public async Task TestBasicAckAsync()
+ {
+ const int messageCount = 1024;
+ int messagesReceived = 0;
+
+ var publishSyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+
+ var cf = CreateConnectionFactory();
+ cf.DispatchConsumersAsync = true;
+
+ using IConnection connection = cf.CreateConnection();
+ using IChannel channel = connection.CreateChannel();
+
+ await channel.ConfirmSelectAsync();
+
+ var consumer = new AsyncEventingBasicConsumer(channel);
+ consumer.Received += async (object sender, BasicDeliverEventArgs args) =>
+ {
+ var c = sender as AsyncEventingBasicConsumer;
+ Assert.NotNull(c);
+ await channel.BasicAckAsync(args.DeliveryTag, false);
+ messagesReceived++;
+ if (messagesReceived == messageCount)
+ {
+ publishSyncSource.SetResult(true);
+ }
+ };
+
+ QueueDeclareOk q = await channel.QueueDeclareAsync(string.Empty, false, false, true, false, null);
+ string queueName = q.QueueName;
+
+ await channel.BasicQosAsync(0, 1, false);
+ await channel.BasicConsumeAsync(queue: queueName, autoAck: false,
+ consumerTag: string.Empty, noLocal: false, exclusive: false,
+ arguments: null, consumer);
+
+ var publishTask = Task.Run(async () =>
+ {
+ for (int i = 0; i < messageCount; i++)
+ {
+ byte[] body = _encoding.GetBytes(Guid.NewGuid().ToString());
+ await channel.BasicPublishAsync(string.Empty, queueName, body);
+ }
+ });
+
+ await channel.WaitForConfirmsOrDieAsync();
+ Assert.True(await publishSyncSource.Task);
+
+ Assert.Equal(messageCount, messagesReceived);
+
+ // Note: closing channel explicitly just to test it.
+ await channel.CloseAsync(_closeArgs, false);
+ }
+
+ [Fact]
+ public async Task TestBasicNackAsync()
+ {
+ var publishSyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+
+ var cf = CreateConnectionFactory();
+ cf.DispatchConsumersAsync = true;
+
+ using IConnection connection = cf.CreateConnection();
+ using IChannel channel = connection.CreateChannel();
+
+ var consumer = new AsyncEventingBasicConsumer(channel);
+ consumer.Received += async (object sender, BasicDeliverEventArgs args) =>
+ {
+ var c = sender as AsyncEventingBasicConsumer;
+ Assert.NotNull(c);
+ await channel.BasicCancelAsync(c.ConsumerTags[0]);
+ await channel.BasicNackAsync(args.DeliveryTag, false, true);
+ publishSyncSource.SetResult(true);
+ };
+
+ QueueDeclareOk q = await channel.QueueDeclareAsync(string.Empty, false, false, true, false, null);
+ string queueName = q.QueueName;
+ const string publish1 = "sync-hi-1";
+ byte[] body = _encoding.GetBytes(publish1);
+ await channel.BasicPublishAsync(string.Empty, queueName, body);
+
+ await channel.BasicConsumeAsync(queue: queueName, autoAck: false,
+ consumerTag: string.Empty, noLocal: false, exclusive: false,
+ arguments: null, consumer);
+
+ Assert.True(await publishSyncSource.Task);
+
+ uint messageCount, consumerCount = 0;
+ ushort tries = 5;
+ do
+ {
+ QueueDeclareOk result = await channel.QueueDeclareAsync(queue: queueName, passive: true, false, false, false, null);
+ consumerCount = result.ConsumerCount;
+ messageCount = result.MessageCount;
+ if (consumerCount == 0 && messageCount > 0)
+ {
+ break;
+ }
+ else
+ {
+ await Task.Delay(500);
+ }
+ } while (tries-- > 0);
+
+ if (tries == 0)
+ {
+ Assert.Fail("[ERROR] failed waiting for MessageCount > 0 && ConsumerCount == 0");
+ }
+ else
+ {
+ Assert.Equal((uint)1, messageCount);
+ Assert.Equal((uint)0, consumerCount);
+ }
+
+ // Note: closing channel explicitly just to test it.
+ await channel.CloseAsync(_closeArgs, false);
+ }
+ }
+}
diff --git a/projects/Unit/TestConsumerCancelNotify.cs b/projects/Test/Integration/TestConsumerCancelNotify.cs
similarity index 99%
rename from projects/Unit/TestConsumerCancelNotify.cs
rename to projects/Test/Integration/TestConsumerCancelNotify.cs
index f298852965..152ed8e9f5 100644
--- a/projects/Unit/TestConsumerCancelNotify.cs
+++ b/projects/Test/Integration/TestConsumerCancelNotify.cs
@@ -31,11 +31,12 @@
using System.Linq;
using System.Threading;
+using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestConsumerCancelNotify : IntegrationFixture
{
diff --git a/projects/Unit/TestConsumerCount.cs b/projects/Test/Integration/TestConsumerCount.cs
similarity index 97%
rename from projects/Unit/TestConsumerCount.cs
rename to projects/Test/Integration/TestConsumerCount.cs
index 1f846e8621..8b38ea0360 100644
--- a/projects/Unit/TestConsumerCount.cs
+++ b/projects/Test/Integration/TestConsumerCount.cs
@@ -29,11 +29,12 @@
// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
//---------------------------------------------------------------------------
+using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestConsumerCount : IntegrationFixture
{
diff --git a/projects/Unit/TestConsumerExceptions.cs b/projects/Test/Integration/TestConsumerExceptions.cs
similarity index 99%
rename from projects/Unit/TestConsumerExceptions.cs
rename to projects/Test/Integration/TestConsumerExceptions.cs
index 5bf62391fc..e856e380d8 100644
--- a/projects/Unit/TestConsumerExceptions.cs
+++ b/projects/Test/Integration/TestConsumerExceptions.cs
@@ -31,10 +31,11 @@
using System;
using System.Threading;
+using RabbitMQ.Client;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestConsumerExceptions : IntegrationFixture
{
diff --git a/projects/Unit/TestConsumerOperationDispatch.cs b/projects/Test/Integration/TestConsumerOperationDispatch.cs
similarity index 91%
rename from projects/Unit/TestConsumerOperationDispatch.cs
rename to projects/Test/Integration/TestConsumerOperationDispatch.cs
index 1f3a1129e2..19f853155e 100644
--- a/projects/Unit/TestConsumerOperationDispatch.cs
+++ b/projects/Test/Integration/TestConsumerOperationDispatch.cs
@@ -32,32 +32,33 @@
using System;
using System.Collections.Generic;
using System.Threading;
+using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestConsumerOperationDispatch : IntegrationFixture
{
- public TestConsumerOperationDispatch(ITestOutputHelper output) : base(output)
- {
- }
+ // number of channels (and consumers)
+ private const int Y = 100;
+ // number of messages to be published
+ private const int N = 100;
+
+ private static readonly CountdownEvent counter = new CountdownEvent(Y);
- private readonly string _x = "dotnet.tests.consumer-operation-dispatch.fanout";
+ private const string _x = "dotnet.tests.consumer-operation-dispatch.fanout";
private readonly List _channels = new List();
private readonly List _queues = new List();
private readonly List _consumers = new List();
- // number of channels (and consumers)
- private const int Y = 100;
- // number of messages to be published
- private const int N = 100;
-
- public static CountdownEvent counter = new CountdownEvent(Y);
+ public TestConsumerOperationDispatch(ITestOutputHelper output) : base(output)
+ {
+ }
- public override void Dispose()
+ protected override void TearDown()
{
foreach (IChannel ch in _channels)
{
@@ -67,10 +68,7 @@ public override void Dispose()
}
}
- _queues.Clear();
- _consumers.Clear();
counter.Reset();
- base.ReleaseResources();
}
private class CollectingConsumer : DefaultBasicConsumer
@@ -104,8 +102,7 @@ public override void HandleBasicDeliver(string consumerTag,
[Fact]
public void TestDeliveryOrderingWithSingleChannel()
{
- IChannel Ch = _conn.CreateChannel();
- Ch.ExchangeDeclare(_x, "fanout", durable: false);
+ _channel.ExchangeDeclare(_x, "fanout", durable: false);
for (int i = 0; i < Y; i++)
{
@@ -121,9 +118,10 @@ public void TestDeliveryOrderingWithSingleChannel()
for (int i = 0; i < N; i++)
{
- Ch.BasicPublish(_x, "", _encoding.GetBytes("msg"));
+ _channel.BasicPublish(_x, "", _encoding.GetBytes("msg"));
}
- counter.Wait(TimeSpan.FromSeconds(120));
+
+ counter.Wait(TimeSpan.FromMinutes(2));
foreach (CollectingConsumer cons in _consumers)
{
@@ -145,9 +143,10 @@ public void TestDeliveryOrderingWithSingleChannel()
[Fact]
public void TestChannelShutdownDoesNotShutDownDispatcher()
{
+ _channel.ExchangeDeclare(_x, "fanout", durable: false);
+
IChannel ch1 = _conn.CreateChannel();
IChannel ch2 = _conn.CreateChannel();
- _channel.ExchangeDeclare(_x, "fanout", durable: false);
string q1 = ch1.QueueDeclare().QueueName;
string q2 = ch2.QueueDeclare().QueueName;
@@ -165,7 +164,7 @@ public void TestChannelShutdownDoesNotShutDownDispatcher()
ch1.Close();
ch2.BasicPublish(_x, "", _encoding.GetBytes("msg"));
- Wait(latch);
+ Wait(latch, "received event");
}
private class ShutdownLatchConsumer : DefaultBasicConsumer
@@ -203,7 +202,7 @@ public void TestChannelShutdownHandler()
_channel.BasicConsume(queue: q, autoAck: true, consumer: c);
_channel.Close();
- Wait(latch, TimeSpan.FromSeconds(5));
+ Wait(latch, TimeSpan.FromSeconds(5), "channel shutdown");
Assert.False(duplicateLatch.Wait(TimeSpan.FromSeconds(5)),
"event handler fired more than once");
}
diff --git a/projects/Unit/TestEventingConsumer.cs b/projects/Test/Integration/TestEventingConsumer.cs
similarity index 96%
rename from projects/Unit/TestEventingConsumer.cs
rename to projects/Test/Integration/TestEventingConsumer.cs
index 6660af4f13..c12da36c8f 100644
--- a/projects/Unit/TestEventingConsumer.cs
+++ b/projects/Test/Integration/TestEventingConsumer.cs
@@ -30,11 +30,12 @@
//---------------------------------------------------------------------------
using System.Threading;
+using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestEventingConsumer : IntegrationFixture
{
@@ -66,14 +67,14 @@ public void TestEventingConsumerRegistrationEvents()
};
string tag = _channel.BasicConsume(q, false, ec);
- Wait(registeredLatch);
+ Wait(registeredLatch, "consumer registered");
Assert.NotNull(registeredSender);
Assert.Equal(ec, registeredSender);
Assert.Equal(_channel, ((EventingBasicConsumer)registeredSender).Channel);
_channel.BasicCancel(tag);
- Wait(unregisteredLatch);
+ Wait(unregisteredLatch, "consumer unregistered");
Assert.NotNull(unregisteredSender);
Assert.Equal(ec, unregisteredSender);
Assert.Equal(_channel, ((EventingBasicConsumer)unregisteredSender).Channel);
diff --git a/projects/Unit/TestExceptionMessages.cs b/projects/Test/Integration/TestExceptionMessages.cs
similarity index 98%
rename from projects/Unit/TestExceptionMessages.cs
rename to projects/Test/Integration/TestExceptionMessages.cs
index 2f87a9f430..7901096f54 100644
--- a/projects/Unit/TestExceptionMessages.cs
+++ b/projects/Test/Integration/TestExceptionMessages.cs
@@ -34,7 +34,7 @@
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestExceptionMessages : IntegrationFixture
{
diff --git a/projects/Unit/TestExchangeDeclare.cs b/projects/Test/Integration/TestExchangeDeclare.cs
similarity index 99%
rename from projects/Unit/TestExchangeDeclare.cs
rename to projects/Test/Integration/TestExchangeDeclare.cs
index 8fdf3e6fd4..fe1c728807 100644
--- a/projects/Unit/TestExchangeDeclare.cs
+++ b/projects/Test/Integration/TestExchangeDeclare.cs
@@ -33,10 +33,11 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using RabbitMQ.Client;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestExchangeDeclare : IntegrationFixture
{
diff --git a/projects/Unit/TestExtensions.cs b/projects/Test/Integration/TestExtensions.cs
similarity index 93%
rename from projects/Unit/TestExtensions.cs
rename to projects/Test/Integration/TestExtensions.cs
index 2392011edb..69c1b0be57 100644
--- a/projects/Unit/TestExtensions.cs
+++ b/projects/Test/Integration/TestExtensions.cs
@@ -31,10 +31,11 @@
using System;
using System.Threading.Tasks;
+using RabbitMQ.Client;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestExtensions : IntegrationFixture
{
@@ -54,9 +55,9 @@ public async Task TestConfirmBarrier()
}
[Fact]
- public async Task TestConfirmBeforeWait()
+ public Task TestConfirmBeforeWait()
{
- await Assert.ThrowsAsync(async () => await _channel.WaitForConfirmsAsync());
+ return Assert.ThrowsAsync(() => _channel.WaitForConfirmsAsync());
}
[Fact]
diff --git a/projects/Test/Integration/TestFloodPublishing.cs b/projects/Test/Integration/TestFloodPublishing.cs
new file mode 100644
index 0000000000..752f2b24a7
--- /dev/null
+++ b/projects/Test/Integration/TestFloodPublishing.cs
@@ -0,0 +1,148 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using RabbitMQ.Client;
+using RabbitMQ.Client.Events;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Test.Integration
+{
+ public class TestFloodPublishing : IntegrationFixture
+ {
+ private readonly byte[] _body = new byte[2048];
+ private readonly TimeSpan _tenSeconds = TimeSpan.FromSeconds(10);
+
+ public TestFloodPublishing(ITestOutputHelper output) : base(output)
+ {
+ }
+
+ protected override void SetUp()
+ {
+ _connFactory = CreateConnectionFactory();
+ _connFactory.RequestedHeartbeat = TimeSpan.FromSeconds(60);
+ _connFactory.AutomaticRecoveryEnabled = false;
+
+ _conn = _connFactory.CreateConnection();
+ _channel = _conn.CreateChannel();
+ }
+
+ [Fact]
+ public async Task TestUnthrottledFloodPublishingAsync()
+ {
+ _conn.ConnectionShutdown += (_, args) =>
+ {
+ if (args.Initiator != ShutdownInitiator.Application)
+ {
+ Assert.Fail("Unexpected connection shutdown!");
+ }
+ };
+
+ var stopwatch = Stopwatch.StartNew();
+ int i = 0;
+ try
+ {
+ for (i = 0; i < 65535 * 64; i++)
+ {
+ if (i % 65536 == 0)
+ {
+ if (stopwatch.Elapsed > _tenSeconds)
+ {
+ break;
+ }
+ }
+
+ await _channel.BasicPublishAsync(CachedString.Empty, CachedString.Empty, _body);
+ }
+ }
+ finally
+ {
+ stopwatch.Stop();
+ }
+
+ Assert.True(_conn.IsOpen);
+ }
+
+ [Fact]
+ public async Task TestMultithreadFloodPublishingAsync()
+ {
+ string message = "Hello from test TestMultithreadFloodPublishing";
+ byte[] sendBody = _encoding.GetBytes(message);
+ int publishCount = 4096;
+ int receivedCount = 0;
+ var autoResetEvent = new AutoResetEvent(false);
+
+ var cf = CreateConnectionFactory();
+ cf.RequestedHeartbeat = TimeSpan.FromSeconds(60);
+ cf.AutomaticRecoveryEnabled = false;
+
+ string queueName = null;
+ QueueDeclareOk q = _channel.QueueDeclare();
+ queueName = q.QueueName;
+
+ Task pub = Task.Run(async () =>
+ {
+ using (IChannel pubCh = _conn.CreateChannel())
+ {
+ for (int i = 0; i < publishCount; i++)
+ {
+ await pubCh.BasicPublishAsync(string.Empty, queueName, sendBody);
+ }
+ }
+ });
+
+ using (IChannel consumeCh = _conn.CreateChannel())
+ {
+ var consumer = new EventingBasicConsumer(consumeCh);
+ consumer.Received += (o, a) =>
+ {
+ string receivedMessage = _encoding.GetString(a.Body.ToArray());
+ Assert.Equal(message, receivedMessage);
+ Interlocked.Increment(ref receivedCount);
+ if (receivedCount == publishCount)
+ {
+ autoResetEvent.Set();
+ }
+ };
+ consumeCh.BasicConsume(queueName, true, consumer);
+
+ Assert.True(autoResetEvent.WaitOne(_tenSeconds));
+ }
+
+ await pub;
+ Assert.Equal(publishCount, receivedCount);
+ }
+ }
+}
diff --git a/projects/Unit/TestHeartbeats.cs b/projects/Test/Integration/TestHeartbeats.cs
similarity index 62%
rename from projects/Unit/TestHeartbeats.cs
rename to projects/Test/Integration/TestHeartbeats.cs
index 40deb350f7..86e1600821 100644
--- a/projects/Unit/TestHeartbeats.cs
+++ b/projects/Test/Integration/TestHeartbeats.cs
@@ -31,34 +31,43 @@
using System;
using System.Collections.Generic;
-using System.IO;
using System.Threading;
+using RabbitMQ.Client;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestHeartbeats : IntegrationFixture
{
+ private readonly TimeSpan _heartbeatTimeout = TimeSpan.FromSeconds(2);
+
public TestHeartbeats(ITestOutputHelper output) : base(output)
{
}
- private readonly TimeSpan _heartbeatTimeout = TimeSpan.FromSeconds(2);
+ protected override void SetUp()
+ {
+ Assert.Null(_connFactory);
+ Assert.Null(_conn);
+ Assert.Null(_channel);
+ }
- [Fact(Timeout = 35000)]
+ [SkippableFact(Timeout = 35000)]
[Trait("Category", "LongRunning")]
public void TestThatHeartbeatWriterUsesConfigurableInterval()
{
- var cf = new ConnectionFactory()
- {
- RequestedHeartbeat = _heartbeatTimeout,
- AutomaticRecoveryEnabled = false
- };
+ Skip.IfNot(LongRunningTestsEnabled(), "RABBITMQ_LONG_RUNNING_TESTS is not set, skipping test");
+
+ var cf = CreateConnectionFactory();
+ cf.RequestedHeartbeat = _heartbeatTimeout;
+ cf.AutomaticRecoveryEnabled = false;
+
RunSingleConnectionTest(cf);
}
[SkippableFact]
+ [Trait("Category", "LongRunning")]
public void TestThatHeartbeatWriterWithTLSEnabled()
{
Skip.IfNot(LongRunningTestsEnabled(), "RABBITMQ_LONG_RUNNING_TESTS is not set, skipping test");
@@ -66,12 +75,10 @@ public void TestThatHeartbeatWriterWithTLSEnabled()
var sslEnv = new SslEnv();
Skip.IfNot(sslEnv.IsSslConfigured, "SSL_CERTS_DIR and/or PASSWORD are not configured, skipping test");
- var cf = new ConnectionFactory()
- {
- Port = 5671,
- RequestedHeartbeat = _heartbeatTimeout,
- AutomaticRecoveryEnabled = false
- };
+ var cf = CreateConnectionFactory();
+ cf.Port = 5671;
+ cf.RequestedHeartbeat = _heartbeatTimeout;
+ cf.AutomaticRecoveryEnabled = false;
cf.Ssl.ServerName = sslEnv.Hostname;
cf.Ssl.CertPath = sslEnv.CertPath;
@@ -81,43 +88,59 @@ public void TestThatHeartbeatWriterWithTLSEnabled()
RunSingleConnectionTest(cf);
}
- [Fact(Timeout = 90000)]
+ [SkippableFact(Timeout = 90000)]
[Trait("Category", "LongRunning")]
public void TestHundredsOfConnectionsWithRandomHeartbeatInterval()
{
- var rnd = new Random();
- List xs = new List();
- // Since we are using the ThreadPool, let's set MinThreads to a high-enough value.
- ThreadPool.SetMinThreads(200, 200);
- for (int i = 0; i < 200; i++)
+ Skip.IfNot(LongRunningTestsEnabled(), "RABBITMQ_LONG_RUNNING_TESTS is not set, skipping test");
+
+ const ushort connectionCount = 200;
+
+ ThreadPool.GetMinThreads(out int origWorkerThreads, out int origCompletionPortThreads);
+ try
{
- ushort n = Convert.ToUInt16(rnd.Next(2, 6));
- var cf = new ConnectionFactory()
+ var rnd = new Random();
+ var conns = new List();
+
+ // Since we are using the ThreadPool, let's set MinThreads to a high-enough value.
+ ThreadPool.SetMinThreads(connectionCount, connectionCount);
+
+ try
{
- RequestedHeartbeat = TimeSpan.FromSeconds(n),
- AutomaticRecoveryEnabled = false
- };
- IConnection conn = cf.CreateConnection();
- xs.Add(conn);
- IChannel ch = conn.CreateChannel();
-
- conn.ConnectionShutdown += (sender, evt) =>
+ for (int i = 0; i < connectionCount; i++)
{
- CheckInitiator(evt);
- };
+ ushort n = Convert.ToUInt16(rnd.Next(2, 6));
+ var cf = CreateConnectionFactory();
+ cf.RequestedHeartbeat = TimeSpan.FromSeconds(n);
+ cf.AutomaticRecoveryEnabled = false;
+
+ IConnection conn = cf.CreateConnection($"_testDisplayName:{i}");
+ conns.Add(conn);
+ IChannel ch = conn.CreateChannel();
+ conn.ConnectionShutdown += (sender, evt) =>
+ {
+ CheckInitiator(evt);
+ };
+ }
+ SleepFor(60);
+ }
+ finally
+ {
+ foreach (IConnection conn in conns)
+ {
+ conn.Close();
+ }
+ }
}
-
- SleepFor(60);
-
- foreach (IConnection x in xs)
+ finally
{
- x.Close();
+ Assert.True(ThreadPool.SetMinThreads(origWorkerThreads, origCompletionPortThreads));
}
}
- protected void RunSingleConnectionTest(ConnectionFactory cf)
+ private void RunSingleConnectionTest(ConnectionFactory cf)
{
- using (IConnection conn = cf.CreateConnection())
+ using (IConnection conn = cf.CreateConnection(_testDisplayName))
{
using (IChannel ch = conn.CreateChannel())
{
@@ -145,11 +168,18 @@ protected void RunSingleConnectionTest(ConnectionFactory cf)
private bool LongRunningTestsEnabled()
{
string s = Environment.GetEnvironmentVariable("RABBITMQ_LONG_RUNNING_TESTS");
+
if (String.IsNullOrEmpty(s))
{
return false;
}
- return true;
+
+ if (Boolean.TryParse(s, out bool enabled))
+ {
+ return enabled;
+ }
+
+ return false;
}
private void SleepFor(int t)
diff --git a/projects/Unit/TestInitialConnection.cs b/projects/Test/Integration/TestInitialConnection.cs
similarity index 88%
rename from projects/Unit/TestInitialConnection.cs
rename to projects/Test/Integration/TestInitialConnection.cs
index f20afb9bb9..b0a34e0d36 100644
--- a/projects/Unit/TestInitialConnection.cs
+++ b/projects/Test/Integration/TestInitialConnection.cs
@@ -30,11 +30,12 @@
//---------------------------------------------------------------------------
using System.Collections.Generic;
+using RabbitMQ.Client;
using RabbitMQ.Client.Exceptions;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestInitialConnection : IntegrationFixture
{
@@ -45,7 +46,7 @@ public TestInitialConnection(ITestOutputHelper output) : base(output)
[Fact]
public void TestBasicConnectionRecoveryWithHostnameList()
{
- Framing.Impl.AutorecoveringConnection c = CreateAutorecoveringConnection(new List() { "127.0.0.1", "localhost" });
+ var c = CreateAutorecoveringConnection(new List() { "127.0.0.1", "localhost" });
Assert.True(c.IsOpen);
c.Close();
}
@@ -53,7 +54,7 @@ public void TestBasicConnectionRecoveryWithHostnameList()
[Fact]
public void TestBasicConnectionRecoveryWithHostnameListAndUnreachableHosts()
{
- Framing.Impl.AutorecoveringConnection c = CreateAutorecoveringConnection(new List() { "191.72.44.22", "127.0.0.1", "localhost" });
+ var c = CreateAutorecoveringConnection(new List() { "191.72.44.22", "127.0.0.1", "localhost" });
Assert.True(c.IsOpen);
c.Close();
}
diff --git a/projects/Unit/TestInvalidAck.cs b/projects/Test/Integration/TestInvalidAck.cs
similarity index 97%
rename from projects/Unit/TestInvalidAck.cs
rename to projects/Test/Integration/TestInvalidAck.cs
index 0d3d5dc750..427e8a9342 100644
--- a/projects/Unit/TestInvalidAck.cs
+++ b/projects/Test/Integration/TestInvalidAck.cs
@@ -30,10 +30,11 @@
//---------------------------------------------------------------------------
using System.Threading;
+using RabbitMQ.Client;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestInvalidAck : IntegrationFixture
{
diff --git a/projects/Unit/TestMainLoop.cs b/projects/Test/Integration/TestMainLoop.cs
similarity index 82%
rename from projects/Unit/TestMainLoop.cs
rename to projects/Test/Integration/TestMainLoop.cs
index 597f1898f3..5eb3798b40 100644
--- a/projects/Unit/TestMainLoop.cs
+++ b/projects/Test/Integration/TestMainLoop.cs
@@ -31,11 +31,12 @@
using System;
using System.Threading;
+using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestMainLoop : IntegrationFixture
{
@@ -62,27 +63,24 @@ public override void HandleBasicDeliver(string consumerTag,
[Fact]
public void TestCloseWithFaultyConsumer()
{
- ConnectionFactory connFactory = new ConnectionFactory();
- IConnection c = connFactory.CreateConnection();
- IChannel m = _conn.CreateChannel();
object o = new object();
string q = GenerateQueueName();
- m.QueueDeclare(q, false, false, false, null);
+ _channel.QueueDeclare(q, false, false, false, null);
CallbackExceptionEventArgs ea = null;
- m.CallbackException += (_, evt) =>
+ _channel.CallbackException += (_, evt) =>
{
ea = evt;
- c.Close();
+ _channel.Close();
Monitor.PulseAll(o);
};
- m.BasicConsume(q, true, new FaultyConsumer(_channel));
- m.BasicPublish("", q, _encoding.GetBytes("message"));
+ _channel.BasicConsume(q, true, new FaultyConsumer(_channel));
+ _channel.BasicPublish("", q, _encoding.GetBytes("message"));
WaitOn(o);
Assert.NotNull(ea);
- Assert.False(c.IsOpen);
- Assert.Equal(200, c.CloseReason.ReplyCode);
+ Assert.False(_channel.IsOpen);
+ Assert.Equal(200, _channel.CloseReason.ReplyCode);
}
}
}
diff --git a/projects/Unit/TestMessageCount.cs b/projects/Test/Integration/TestMessageCount.cs
similarity index 97%
rename from projects/Unit/TestMessageCount.cs
rename to projects/Test/Integration/TestMessageCount.cs
index ccee33b724..acbf70a6c7 100644
--- a/projects/Unit/TestMessageCount.cs
+++ b/projects/Test/Integration/TestMessageCount.cs
@@ -30,10 +30,11 @@
//---------------------------------------------------------------------------
using System.Threading.Tasks;
+using RabbitMQ.Client;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestMessageCount : IntegrationFixture
{
diff --git a/projects/Unit/TestNowait.cs b/projects/Test/Integration/TestNowait.cs
similarity index 98%
rename from projects/Unit/TestNowait.cs
rename to projects/Test/Integration/TestNowait.cs
index 33df3c69d4..4fa6f06b30 100644
--- a/projects/Unit/TestNowait.cs
+++ b/projects/Test/Integration/TestNowait.cs
@@ -29,10 +29,11 @@
// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
//---------------------------------------------------------------------------
+using RabbitMQ.Client;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestNoWait : IntegrationFixture
{
diff --git a/projects/Unit/TestPassiveDeclare.cs b/projects/Test/Integration/TestPassiveDeclare.cs
similarity index 98%
rename from projects/Unit/TestPassiveDeclare.cs
rename to projects/Test/Integration/TestPassiveDeclare.cs
index b4871d75bb..6063697159 100644
--- a/projects/Unit/TestPassiveDeclare.cs
+++ b/projects/Test/Integration/TestPassiveDeclare.cs
@@ -34,7 +34,7 @@
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestPassiveDeclare : IntegrationFixture
{
diff --git a/projects/Unit/TestPublishSharedChannel.cs b/projects/Test/Integration/TestPublishSharedChannel.cs
similarity index 83%
rename from projects/Unit/TestPublishSharedChannel.cs
rename to projects/Test/Integration/TestPublishSharedChannel.cs
index aab3f9db4c..3477d7f802 100644
--- a/projects/Unit/TestPublishSharedChannel.cs
+++ b/projects/Test/Integration/TestPublishSharedChannel.cs
@@ -32,12 +32,13 @@
using System;
using System.Threading;
using System.Threading.Tasks;
+using RabbitMQ.Client;
using Xunit;
+using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
- [Collection("IntegrationFixture")]
- public class TestPublishSharedChannel
+ public class TestPublishSharedChannel : IntegrationFixture
{
private const string QueueName = "TestPublishSharedChannel_Queue";
private static readonly CachedString ExchangeName = new CachedString("TestPublishSharedChannel_Ex");
@@ -49,17 +50,26 @@ public class TestPublishSharedChannel
private Exception _raisedException;
+ public TestPublishSharedChannel(ITestOutputHelper output) : base(output)
+ {
+ }
+
+ protected override void SetUp()
+ {
+ // NB: test sets up its own factory, conns, channels
+ Assert.Null(_connFactory);
+ Assert.Null(_conn);
+ Assert.Null(_channel);
+ }
+
[Fact]
public async Task MultiThreadPublishOnSharedChannel()
{
- // Arrange
- var connFactory = new ConnectionFactory
- {
- RequestedHeartbeat = TimeSpan.FromSeconds(60),
- AutomaticRecoveryEnabled = false
- };
+ var cf = CreateConnectionFactory();
+ cf.RequestedHeartbeat = TimeSpan.FromSeconds(60);
+ cf.AutomaticRecoveryEnabled = false;
- using (IConnection conn = connFactory.CreateConnection())
+ using (IConnection conn = cf.CreateConnection())
{
conn.ConnectionShutdown += (_, args) =>
{
diff --git a/projects/Unit/TestPublisherConfirms.cs b/projects/Test/Integration/TestPublisherConfirms.cs
similarity index 66%
rename from projects/Unit/TestPublisherConfirms.cs
rename to projects/Test/Integration/TestPublisherConfirms.cs
index 4f910b8784..d5d59a8202 100644
--- a/projects/Unit/TestPublisherConfirms.cs
+++ b/projects/Test/Integration/TestPublisherConfirms.cs
@@ -33,40 +33,37 @@
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
+using RabbitMQ.Client;
using RabbitMQ.Client.Impl;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+// TODO rabbitmq/rabbitmq-dotnet-client#1347
+// Test mixed sync/async with WaitForConfirmsAsync
+namespace Test.Integration
{
public class TestPublisherConfirms : IntegrationFixture
{
- private const string QueueName = "RabbitMQ.Client.Unit.TestPublisherConfirms";
- private readonly byte[] _body = new byte[4096];
+ private readonly byte[] _messageBody;
public TestPublisherConfirms(ITestOutputHelper output) : base(output)
{
-#if NET6_0_OR_GREATER
- Random.Shared.NextBytes(_body);
-#else
- var rnd = new Random();
- rnd.NextBytes(_body);
-#endif
+ _messageBody = GetRandomBody(4096);
}
[Fact]
- public void TestWaitForConfirmsWithoutTimeout()
+ public Task TestWaitForConfirmsWithoutTimeoutAsync()
{
- TestWaitForConfirms(200, async (ch) =>
+ return TestWaitForConfirmsAsync(200, async (ch) =>
{
Assert.True(await ch.WaitForConfirmsAsync());
});
}
[Fact]
- public void TestWaitForConfirmsWithTimeout()
+ public Task TestWaitForConfirmsWithTimeout()
{
- TestWaitForConfirms(200, async (ch) =>
+ return TestWaitForConfirmsAsync(200, async (ch) =>
{
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(4)))
{
@@ -76,12 +73,12 @@ public void TestWaitForConfirmsWithTimeout()
}
[Fact]
- public void TestWaitForConfirmsWithTimeout_MightThrowTaskCanceledException()
+ public async Task TestWaitForConfirmsWithTimeoutAsync_MightThrowTaskCanceledException()
{
bool waitResult = false;
- bool sawTaskCanceled = false;
+ bool sawException = false;
- TestWaitForConfirms(10000, async (ch) =>
+ Task t = TestWaitForConfirmsAsync(10000, async (ch) =>
{
using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(1)))
{
@@ -89,23 +86,25 @@ public void TestWaitForConfirmsWithTimeout_MightThrowTaskCanceledException()
{
waitResult = await ch.WaitForConfirmsAsync(cts.Token);
}
- catch (TaskCanceledException)
+ catch
{
- sawTaskCanceled = true;
+ sawException = true;
}
}
});
- if (waitResult == false && sawTaskCanceled == false)
+ await t;
+
+ if (waitResult == false && sawException == false)
{
- Assert.Fail("test failed, both waitResult and sawTaskCanceled are still false");
+ Assert.Fail("test failed, both waitResult and sawException are still false");
}
}
[Fact]
- public void TestWaitForConfirmsWithTimeout_MessageNacked_WaitingHasTimedout_ReturnFalse()
+ public Task TestWaitForConfirmsWithTimeoutAsync_MessageNacked_WaitingHasTimedout_ReturnFalse()
{
- TestWaitForConfirms(2000, async (ch) =>
+ return TestWaitForConfirmsAsync(2000, async (ch) =>
{
IChannel actualChannel = ((AutorecoveringChannel)ch).InnerChannel;
actualChannel
@@ -121,13 +120,14 @@ public void TestWaitForConfirmsWithTimeout_MessageNacked_WaitingHasTimedout_Retu
}
[Fact]
- public async Task TestWaitForConfirmsWithEvents()
+ public async Task TestWaitForConfirmsWithEventsAsync()
{
+ string queueName = string.Format("{0}:{1}", _testDisplayName, Guid.NewGuid());
using (IChannel ch = _conn.CreateChannel())
{
- ch.ConfirmSelect();
+ await ch.ConfirmSelectAsync();
+ await ch.QueueDeclareAsync(queue: queueName, passive: false, durable: false, exclusive: false, autoDelete: false, arguments: null);
- ch.QueueDeclare(QueueName);
int n = 200;
// number of event handler invocations
int c = 0;
@@ -136,12 +136,14 @@ public async Task TestWaitForConfirmsWithEvents()
{
Interlocked.Increment(ref c);
};
+
try
{
for (int i = 0; i < n; i++)
{
- ch.BasicPublish("", QueueName, _encoding.GetBytes("msg"));
+ await ch.BasicPublishAsync("", queueName, _encoding.GetBytes("msg"));
}
+
await ch.WaitForConfirmsAsync();
// Note: number of event invocations is not guaranteed
@@ -152,32 +154,33 @@ public async Task TestWaitForConfirmsWithEvents()
}
finally
{
- ch.QueueDelete(QueueName);
+ await ch.QueueDeleteAsync(queue: queueName, ifUnused: false, ifEmpty: false);
}
}
}
- protected void TestWaitForConfirms(int numberOfMessagesToPublish, Action fn)
+ private async Task TestWaitForConfirmsAsync(int numberOfMessagesToPublish, Func fn)
{
+ string queueName = string.Format("{0}:{1}", _testDisplayName, Guid.NewGuid());
using (IChannel ch = _conn.CreateChannel())
{
var props = new BasicProperties { Persistent = true };
- ch.ConfirmSelect();
- ch.QueueDeclare(QueueName);
+ await ch.ConfirmSelectAsync();
+ await ch.QueueDeclareAsync(queue: queueName, passive: false, durable: false, exclusive: false, autoDelete: false, arguments: null);
for (int i = 0; i < numberOfMessagesToPublish; i++)
{
- ch.BasicPublish(exchange: "", routingKey: QueueName, body: _body, mandatory: true, basicProperties: props);
+ await ch.BasicPublishAsync(exchange: string.Empty, routingKey: queueName, body: _messageBody, mandatory: true, basicProperties: props);
}
try
{
- fn(ch);
+ await fn(ch);
}
finally
{
- ch.QueueDelete(QueueName);
+ await ch.QueueDeleteAsync(queue: queueName, ifUnused: false, ifEmpty: false);
}
}
}
diff --git a/projects/Unit/TestQueueDeclare.cs b/projects/Test/Integration/TestQueueDeclare.cs
similarity index 97%
rename from projects/Unit/TestQueueDeclare.cs
rename to projects/Test/Integration/TestQueueDeclare.cs
index d6c9c99816..0bb872c102 100644
--- a/projects/Unit/TestQueueDeclare.cs
+++ b/projects/Test/Integration/TestQueueDeclare.cs
@@ -33,10 +33,11 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using RabbitMQ.Client;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestQueueDeclare : IntegrationFixture
{
@@ -167,6 +168,8 @@ async Task f()
QueueDeclareOk r = await _channel.QueueDeclareAsync(qname, passive: true, false, false, false, null);
Assert.Equal(qname, r.QueueName);
+ await _channel.QueueUnbindAsync(queue: qname, exchange: "amq.fanout", routingKey: qname, null);
+
uint deletedMessageCount = await _channel.QueueDeleteAsync(qname, false, false);
Assert.Equal((uint)0, deletedMessageCount);
}
diff --git a/projects/Unit/TestSsl.cs b/projects/Test/Integration/TestSsl.cs
similarity index 71%
rename from projects/Unit/TestSsl.cs
rename to projects/Test/Integration/TestSsl.cs
index 4695b188e5..570c2b9c5f 100644
--- a/projects/Unit/TestSsl.cs
+++ b/projects/Test/Integration/TestSsl.cs
@@ -29,39 +29,39 @@
// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
//---------------------------------------------------------------------------
-using System;
using System.IO;
using System.Net.Security;
-using System.Reflection;
using System.Security.Authentication;
+using RabbitMQ.Client;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
- [Collection("IntegrationFixture")]
- public class TestSsl
+ public class TestSsl : IntegrationFixture
{
- private readonly ITestOutputHelper _output;
- private readonly string _testDisplayName;
private readonly SslEnv _sslEnv;
- public TestSsl(ITestOutputHelper output)
+ public TestSsl(ITestOutputHelper output) : base(output)
{
- _output = output;
- var type = _output.GetType();
- var testMember = type.GetField("test", BindingFlags.Instance | BindingFlags.NonPublic);
- var test = (ITest)testMember.GetValue(output);
- _testDisplayName = test.DisplayName;
_sslEnv = new SslEnv();
}
+ protected override void SetUp()
+ {
+ Assert.Null(_connFactory);
+ Assert.Null(_conn);
+ Assert.Null(_channel);
+ }
+
[SkippableFact]
public void TestServerVerifiedIgnoringNameMismatch()
{
Skip.IfNot(_sslEnv.IsSslConfigured, "SSL_CERTS_DIR and/or PASSWORD are not configured, skipping test");
- ConnectionFactory cf = new ConnectionFactory { Port = 5671 };
+ var cf = CreateConnectionFactory();
+ cf.Port = 5671;
+
cf.Ssl.ServerName = "*";
cf.Ssl.AcceptablePolicyErrors = SslPolicyErrors.RemoteCertificateNameMismatch;
cf.Ssl.Enabled = true;
@@ -73,7 +73,8 @@ public void TestServerVerified()
{
Skip.IfNot(_sslEnv.IsSslConfigured, "SSL_CERTS_DIR and/or PASSWORD are not configured, skipping test");
- ConnectionFactory cf = new ConnectionFactory { Port = 5671 };
+ var cf = CreateConnectionFactory();
+ cf.Port = 5671;
cf.Ssl.ServerName = _sslEnv.Hostname;
cf.Ssl.Enabled = true;
SendReceive(cf);
@@ -85,10 +86,10 @@ public void TestClientAndServerVerified()
Skip.IfNot(_sslEnv.IsSslConfigured, "SSL_CERTS_DIR and/or PASSWORD are not configured, skipping test");
string certPath = _sslEnv.CertPath;
- _output.WriteLine($"[INFO] certPath: {certPath}");
Assert.True(File.Exists(certPath));
- ConnectionFactory cf = new ConnectionFactory { Port = 5671 };
+ var cf = CreateConnectionFactory();
+ cf.Port = 5671;
cf.Ssl.ServerName = _sslEnv.Hostname;
cf.Ssl.CertPath = certPath;
cf.Ssl.CertPassphrase = _sslEnv.CertPassphrase;
@@ -102,19 +103,17 @@ public void TestNoClientCertificate()
{
Skip.IfNot(_sslEnv.IsSslConfigured, "SSL_CERTS_DIR and/or PASSWORD are not configured, skipping test");
- ConnectionFactory cf = new ConnectionFactory
+ var cf = CreateConnectionFactory();
+ cf.Port = 5671;
+ cf.Ssl = new SslOption()
{
- Port = 5671,
- Ssl = new SslOption()
- {
- CertPath = null,
- Enabled = true,
- ServerName = _sslEnv.Hostname,
- Version = SslProtocols.None,
- AcceptablePolicyErrors =
- SslPolicyErrors.RemoteCertificateNotAvailable |
- SslPolicyErrors.RemoteCertificateNameMismatch
- }
+ CertPath = null,
+ Enabled = true,
+ ServerName = _sslEnv.Hostname,
+ Version = SslProtocols.None,
+ AcceptablePolicyErrors =
+ SslPolicyErrors.RemoteCertificateNotAvailable |
+ SslPolicyErrors.RemoteCertificateNameMismatch
};
SendReceive(cf);
@@ -122,7 +121,7 @@ public void TestNoClientCertificate()
private void SendReceive(ConnectionFactory cf)
{
- using (IConnection conn = cf.CreateConnection($"{_testDisplayName}:{Guid.NewGuid()}"))
+ using (IConnection conn = cf.CreateConnection(_testDisplayName))
{
using (IChannel ch = conn.CreateChannel())
{
@@ -131,13 +130,13 @@ private void SendReceive(ConnectionFactory cf)
ch.QueueBind(qName, "Exchange_TestSslEndPoint", "Key_TestSslEndpoint", null);
string message = "Hello C# SSL Client World";
- byte[] msgBytes = System.Text.Encoding.UTF8.GetBytes(message);
+ byte[] msgBytes = _encoding.GetBytes(message);
ch.BasicPublish("Exchange_TestSslEndPoint", "Key_TestSslEndpoint", msgBytes);
bool autoAck = false;
BasicGetResult result = ch.BasicGet(qName, autoAck);
byte[] body = result.Body.ToArray();
- string resultMessage = System.Text.Encoding.UTF8.GetString(body);
+ string resultMessage = _encoding.GetString(body);
Assert.Equal(message, resultMessage);
}
diff --git a/projects/Unit/TestUpdateSecret.cs b/projects/Test/Integration/TestUpdateSecret.cs
similarity index 95%
rename from projects/Unit/TestUpdateSecret.cs
rename to projects/Test/Integration/TestUpdateSecret.cs
index b81ef5ce15..d2396ca831 100644
--- a/projects/Unit/TestUpdateSecret.cs
+++ b/projects/Test/Integration/TestUpdateSecret.cs
@@ -31,7 +31,7 @@
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Integration
{
public class TestUpdateSecret : IntegrationFixture
{
@@ -39,7 +39,6 @@ public TestUpdateSecret(ITestOutputHelper output) : base(output)
{
}
- [IgnoreOnVersionsEarlierThan(3, 8)]
public void TestUpdatingConnectionSecret()
{
_conn.UpdateSecret("new-secret", "Test Case");
diff --git a/projects/Test/Integration/TimingFixture.cs b/projects/Test/Integration/TimingFixture.cs
new file mode 100644
index 0000000000..65ca598f35
--- /dev/null
+++ b/projects/Test/Integration/TimingFixture.cs
@@ -0,0 +1,46 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+
+namespace Test.Integration
+{
+ public static class TimingFixture
+ {
+ public static readonly TimeSpan TimingInterval = TimeSpan.FromMilliseconds(300);
+ public static readonly TimeSpan TimingInterval_2X = TimeSpan.FromMilliseconds(600);
+ public static readonly TimeSpan TimingInterval_4X = TimeSpan.FromMilliseconds(1200);
+ public static readonly TimeSpan TimingInterval_8X = TimeSpan.FromMilliseconds(2400);
+ public static readonly TimeSpan TimingInterval_16X = TimeSpan.FromMilliseconds(4800);
+ public static readonly TimeSpan SafetyMargin = TimeSpan.FromMilliseconds(150);
+ public static readonly TimeSpan TestTimeout = TimeSpan.FromSeconds(5);
+ }
+}
diff --git a/projects/OAuth2Test/APIApproval.Approve.verified.txt b/projects/Test/OAuth2/APIApproval.Approve.verified.txt
similarity index 100%
rename from projects/OAuth2Test/APIApproval.Approve.verified.txt
rename to projects/Test/OAuth2/APIApproval.Approve.verified.txt
diff --git a/projects/OAuth2Test/APIApproval.cs b/projects/Test/OAuth2/APIApproval.cs
similarity index 100%
rename from projects/OAuth2Test/APIApproval.cs
rename to projects/Test/OAuth2/APIApproval.cs
diff --git a/projects/OAuth2Test/OAuth2Test.csproj b/projects/Test/OAuth2/OAuth2.csproj
similarity index 79%
rename from projects/OAuth2Test/OAuth2Test.csproj
rename to projects/Test/OAuth2/OAuth2.csproj
index e262304e8f..72ed6fce76 100644
--- a/projects/OAuth2Test/OAuth2Test.csproj
+++ b/projects/Test/OAuth2/OAuth2.csproj
@@ -9,15 +9,15 @@
- ../rabbit.snk
+ ../../rabbit.snk
true
latest
7.0
-
-
+
+
@@ -27,15 +27,15 @@
all
-
-
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
diff --git a/projects/OAuth2Test/README.md b/projects/Test/OAuth2/README.md
similarity index 100%
rename from projects/OAuth2Test/README.md
rename to projects/Test/OAuth2/README.md
diff --git a/projects/OAuth2Test/RequestFormMatcher.cs b/projects/Test/OAuth2/RequestFormMatcher.cs
similarity index 100%
rename from projects/OAuth2Test/RequestFormMatcher.cs
rename to projects/Test/OAuth2/RequestFormMatcher.cs
diff --git a/projects/OAuth2Test/TestOAuth2.cs b/projects/Test/OAuth2/TestOAuth2.cs
similarity index 99%
rename from projects/OAuth2Test/TestOAuth2.cs
rename to projects/Test/OAuth2/TestOAuth2.cs
index bb968407de..ba9db4a41c 100644
--- a/projects/OAuth2Test/TestOAuth2.cs
+++ b/projects/Test/OAuth2/TestOAuth2.cs
@@ -79,7 +79,8 @@ public TestOAuth2(ITestOutputHelper testOutputHelper)
{
AutomaticRecoveryEnabled = true,
CredentialsProvider = GetCredentialsProvider(options),
- CredentialsRefresher = GetCredentialsRefresher()
+ CredentialsRefresher = GetCredentialsRefresher(),
+ ClientProvidedName = nameof(TestOAuth2)
};
_connection = connectionFactory.CreateConnection();
diff --git a/projects/OAuth2Test/TestOAuth2Client.cs b/projects/Test/OAuth2/TestOAuth2Client.cs
similarity index 100%
rename from projects/OAuth2Test/TestOAuth2Client.cs
rename to projects/Test/OAuth2/TestOAuth2Client.cs
diff --git a/projects/OAuth2Test/TestOAuth2ClientCredentialsProvider.cs b/projects/Test/OAuth2/TestOAuth2ClientCredentialsProvider.cs
similarity index 100%
rename from projects/OAuth2Test/TestOAuth2ClientCredentialsProvider.cs
rename to projects/Test/OAuth2/TestOAuth2ClientCredentialsProvider.cs
diff --git a/projects/OAuth2Test/enabled_plugins b/projects/Test/OAuth2/enabled_plugins
similarity index 100%
rename from projects/OAuth2Test/enabled_plugins
rename to projects/Test/OAuth2/enabled_plugins
diff --git a/projects/OAuth2Test/keycloak/import/test-realm.json b/projects/Test/OAuth2/keycloak/import/test-realm.json
similarity index 100%
rename from projects/OAuth2Test/keycloak/import/test-realm.json
rename to projects/Test/OAuth2/keycloak/import/test-realm.json
diff --git a/projects/OAuth2Test/keycloak/rabbitmq.conf b/projects/Test/OAuth2/keycloak/rabbitmq.conf
similarity index 100%
rename from projects/OAuth2Test/keycloak/rabbitmq.conf
rename to projects/Test/OAuth2/keycloak/rabbitmq.conf
diff --git a/projects/OAuth2Test/keycloak/signing-key/signing-key.pem b/projects/Test/OAuth2/keycloak/signing-key/signing-key.pem
similarity index 100%
rename from projects/OAuth2Test/keycloak/signing-key/signing-key.pem
rename to projects/Test/OAuth2/keycloak/signing-key/signing-key.pem
diff --git a/projects/OAuth2Test/uaa/log4j2.properties b/projects/Test/OAuth2/uaa/log4j2.properties
similarity index 100%
rename from projects/OAuth2Test/uaa/log4j2.properties
rename to projects/Test/OAuth2/uaa/log4j2.properties
diff --git a/projects/OAuth2Test/uaa/rabbitmq.conf b/projects/Test/OAuth2/uaa/rabbitmq.conf
similarity index 100%
rename from projects/OAuth2Test/uaa/rabbitmq.conf
rename to projects/Test/OAuth2/uaa/rabbitmq.conf
diff --git a/projects/OAuth2Test/uaa/signing-key/signing-key.pem b/projects/Test/OAuth2/uaa/signing-key/signing-key.pem
similarity index 100%
rename from projects/OAuth2Test/uaa/signing-key/signing-key.pem
rename to projects/Test/OAuth2/uaa/signing-key/signing-key.pem
diff --git a/projects/OAuth2Test/uaa/uaa.yml b/projects/Test/OAuth2/uaa/uaa.yml
similarity index 100%
rename from projects/OAuth2Test/uaa/uaa.yml
rename to projects/Test/OAuth2/uaa/uaa.yml
diff --git a/projects/Test/SequentialIntegration/SequentialIntegration.csproj b/projects/Test/SequentialIntegration/SequentialIntegration.csproj
new file mode 100644
index 0000000000..6cda456251
--- /dev/null
+++ b/projects/Test/SequentialIntegration/SequentialIntegration.csproj
@@ -0,0 +1,51 @@
+
+
+
+ net6.0;net472
+
+
+
+ net6.0
+
+
+
+ ../../rabbit.snk
+ true
+ latest
+ 7.0
+ true
+
+
+
+
+
+
+
+
+
+
+ <_Parameter1>Xunit.CollectionBehavior.CollectionPerAssembly
+ <_Parameter1_IsLiteral>true
+ <_Parameter1_TypeName>Xunit.CollectionBehavior.CollectionPerAssembly
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
diff --git a/projects/Test/SequentialIntegration/SequentialIntegrationFixture.cs b/projects/Test/SequentialIntegration/SequentialIntegrationFixture.cs
new file mode 100644
index 0000000000..9d769bfc8f
--- /dev/null
+++ b/projects/Test/SequentialIntegration/SequentialIntegrationFixture.cs
@@ -0,0 +1,89 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+using System.Threading;
+using RabbitMQ.Client;
+using Xunit.Abstractions;
+
+namespace Test.SequentialIntegration
+{
+ public class SequentialIntegrationFixture : IntegrationFixture
+ {
+ public SequentialIntegrationFixture(ITestOutputHelper output) : base(output)
+ {
+ }
+
+ public void Block()
+ {
+ _rabbitMQCtl.ExecRabbitMQCtl("set_vm_memory_high_watermark 0.000000001");
+ // give rabbitmqctl some time to do its job
+ Thread.Sleep(TimeSpan.FromSeconds(2));
+ Publish();
+ }
+
+ public void Unblock()
+ {
+ _rabbitMQCtl.ExecRabbitMQCtl("set_vm_memory_high_watermark 0.4");
+ }
+
+ public void RestartRabbitMQ()
+ {
+ StopRabbitMQ();
+ Thread.Sleep(TimeSpan.FromMilliseconds(500));
+ StartRabbitMQ();
+ AwaitRabbitMQ();
+ }
+
+ public void StopRabbitMQ()
+ {
+ _rabbitMQCtl.ExecRabbitMQCtl("stop_app");
+ }
+
+ public void StartRabbitMQ()
+ {
+ _rabbitMQCtl.ExecRabbitMQCtl("start_app");
+ }
+
+ private void AwaitRabbitMQ()
+ {
+ _rabbitMQCtl.ExecRabbitMQCtl("await_startup");
+ }
+
+ private void Publish()
+ {
+ using (IChannel ch = _conn.CreateChannel())
+ {
+ ch.BasicPublish("amq.fanout", "", _encoding.GetBytes("message"));
+ }
+ }
+ }
+}
diff --git a/projects/Unit/TestConnectionBlocked.cs b/projects/Test/SequentialIntegration/TestConnectionBlocked.cs
similarity index 96%
rename from projects/Unit/TestConnectionBlocked.cs
rename to projects/Test/SequentialIntegration/TestConnectionBlocked.cs
index 4d9986aad6..232a83b954 100644
--- a/projects/Unit/TestConnectionBlocked.cs
+++ b/projects/Test/SequentialIntegration/TestConnectionBlocked.cs
@@ -36,9 +36,9 @@
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.SequentialIntegration
{
- public class TestConnectionBlocked : IntegrationFixture
+ public class TestConnectionBlocked : SequentialIntegrationFixture
{
private readonly ManualResetEventSlim _connDisposed = new ManualResetEventSlim(false);
private readonly object _lockObject = new object();
@@ -100,7 +100,7 @@ public void TestDisposeOnBlockedConnectionDoesNotHang()
}
}
- protected override void ReleaseResources()
+ protected override void TearDown()
{
Unblock();
}
diff --git a/projects/Unit/TestConnectionRecovery.cs b/projects/Test/SequentialIntegration/TestConnectionRecovery.cs
similarity index 67%
rename from projects/Unit/TestConnectionRecovery.cs
rename to projects/Test/SequentialIntegration/TestConnectionRecovery.cs
index 2c8c42ab50..fada4e8948 100644
--- a/projects/Unit/TestConnectionRecovery.cs
+++ b/projects/Test/SequentialIntegration/TestConnectionRecovery.cs
@@ -31,9 +31,9 @@
using System;
using System.Collections.Generic;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.Exceptions;
using RabbitMQ.Client.Framing.Impl;
@@ -41,46 +41,25 @@
using Xunit;
using Xunit.Abstractions;
-#pragma warning disable 0618
-
-namespace RabbitMQ.Client.Unit
+namespace Test.SequentialIntegration
{
- public class TestConnectionRecovery : IntegrationFixture
+ public class TestConnectionRecovery : TestConnectionRecoveryBase
{
- private readonly byte[] _messageBody;
- private readonly ushort _totalMessageCount = 8192;
- private readonly ushort _closeAtCount = 16;
- private string _queueName;
+ private readonly string _queueName;
public TestConnectionRecovery(ITestOutputHelper output) : base(output)
- {
- var rnd = new Random();
- _messageBody = new byte[4096];
- rnd.NextBytes(_messageBody);
- }
-
- protected override void SetUp()
{
_queueName = $"TestConnectionRecovery-queue-{Guid.NewGuid()}";
- _conn = CreateAutorecoveringConnection();
- _channel = _conn.CreateChannel();
- _channel.QueueDelete(_queueName);
}
- protected override void ReleaseResources()
+ protected override void TearDown()
{
- // TODO LRB not really necessary
- if (_channel.IsOpen)
- {
- _channel.Close();
- }
-
- if (_conn.IsOpen)
- {
- _conn.Close();
- }
-
- Unblock();
+ var cf = CreateConnectionFactory();
+ cf.ClientProvidedName = cf.ClientProvidedName + "-TearDown";
+ using IConnection conn = cf.CreateConnection();
+ using IChannel ch = conn.CreateChannel();
+ ch.QueueDelete(_queueName);
+ base.TearDown();
}
[Fact]
@@ -93,16 +72,16 @@ public void TestBasicAckAfterChannelRecovery()
Assert.Equal(queueName, _queueName);
_channel.BasicQos(0, 1, false);
- string consumerTag = _channel.BasicConsume(queueName, false, cons);
+ _channel.BasicConsume(queueName, false, cons);
ManualResetEventSlim sl = PrepareForShutdown(_conn);
ManualResetEventSlim rl = PrepareForRecovery(_conn);
PublishMessagesWhileClosingConn(queueName);
- Wait(sl);
- Wait(rl);
- Wait(allMessagesSeenLatch);
+ Wait(sl, "connection shutdown");
+ Wait(rl, "connection recovery");
+ Wait(allMessagesSeenLatch, "all messages seen");
}
[Fact]
@@ -115,16 +94,16 @@ public void TestBasicNackAfterChannelRecovery()
Assert.Equal(queueName, _queueName);
_channel.BasicQos(0, 1, false);
- string consumerTag = _channel.BasicConsume(queueName, false, cons);
+ _channel.BasicConsume(queueName, false, cons);
ManualResetEventSlim sl = PrepareForShutdown(_conn);
ManualResetEventSlim rl = PrepareForRecovery(_conn);
PublishMessagesWhileClosingConn(queueName);
- Wait(sl);
- Wait(rl);
- Wait(allMessagesSeenLatch);
+ Wait(sl, "connection shutdown");
+ Wait(rl, "connection recovery");
+ Wait(allMessagesSeenLatch, "all messages seen");
}
[Fact]
@@ -137,16 +116,16 @@ public void TestBasicRejectAfterChannelRecovery()
Assert.Equal(queueName, _queueName);
_channel.BasicQos(0, 1, false);
- string consumerTag = _channel.BasicConsume(queueName, false, cons);
+ _channel.BasicConsume(queueName, false, cons);
ManualResetEventSlim sl = PrepareForShutdown(_conn);
ManualResetEventSlim rl = PrepareForRecovery(_conn);
PublishMessagesWhileClosingConn(queueName);
- Wait(sl);
- Wait(rl);
- Wait(allMessagesSeenLatch);
+ Wait(sl, "connection shutdown");
+ Wait(rl, "connection recovery");
+ Wait(allMessagesSeenLatch, "all messages seen");
}
[Fact]
@@ -180,7 +159,7 @@ public void TestBasicAckEventHandlerRecovery()
Assert.True(_channel.IsOpen);
WithTemporaryNonExclusiveQueue(_channel, (m, q) => m.BasicPublish("", q, _messageBody));
- Wait(latch);
+ Wait(latch, "basic acks/nacks");
}
[Fact]
@@ -191,86 +170,6 @@ public void TestBasicConnectionRecovery()
Assert.True(_conn.IsOpen);
}
- [Fact]
- public void TestBasicConnectionRecoveryWithHostnameList()
- {
- using (AutorecoveringConnection c = CreateAutorecoveringConnection(new List { "127.0.0.1", "localhost" }))
- {
- Assert.True(c.IsOpen);
- CloseAndWaitForRecovery(c);
- Assert.True(c.IsOpen);
- }
- }
-
- [Fact]
- public void TestBasicConnectionRecoveryWithHostnameListAndUnreachableHosts()
- {
- using (AutorecoveringConnection c = CreateAutorecoveringConnection(new List { "191.72.44.22", "127.0.0.1", "localhost" }))
- {
- Assert.True(c.IsOpen);
- CloseAndWaitForRecovery(c);
- Assert.True(c.IsOpen);
- }
- }
-
- [Fact]
- public void TestBasicConnectionRecoveryWithEndpointList()
- {
- using (AutorecoveringConnection c = CreateAutorecoveringConnection(
- new List
- {
- new AmqpTcpEndpoint("127.0.0.1"),
- new AmqpTcpEndpoint("localhost")
- }))
- {
- Assert.True(c.IsOpen);
- CloseAndWaitForRecovery(c);
- Assert.True(c.IsOpen);
- }
- }
-
- [Fact]
- public void TestBasicConnectionRecoveryStopsAfterManualClose()
- {
- Assert.True(_conn.IsOpen);
- AutorecoveringConnection c = CreateAutorecoveringConnection();
- var latch = new AutoResetEvent(false);
- c.ConnectionRecoveryError += (o, args) => latch.Set();
-
- try
- {
- StopRabbitMQ();
- latch.WaitOne(30000); // we got the failed reconnection event.
- bool triedRecoveryAfterClose = false;
- c.Close();
- Thread.Sleep(5000);
- c.ConnectionRecoveryError += (o, args) => triedRecoveryAfterClose = true;
- Thread.Sleep(10000);
- Assert.False(triedRecoveryAfterClose);
- }
- finally
- {
- StartRabbitMQ();
- }
- }
-
- [Fact]
- public void TestBasicConnectionRecoveryWithEndpointListAndUnreachableHosts()
- {
- using (AutorecoveringConnection c = CreateAutorecoveringConnection(
- new List
- {
- new AmqpTcpEndpoint("191.72.44.22"),
- new AmqpTcpEndpoint("127.0.0.1"),
- new AmqpTcpEndpoint("localhost")
- }))
- {
- Assert.True(c.IsOpen);
- CloseAndWaitForRecovery(c);
- Assert.True(c.IsOpen);
- }
- }
-
[Fact]
public void TestBasicConnectionRecoveryOnBrokerRestart()
{
@@ -304,7 +203,7 @@ public void TestBlockedListenersRecovery()
CloseAndWaitForRecovery();
Block();
- Wait(latch);
+ Wait(latch, "connection blocked");
Unblock();
}
@@ -344,104 +243,6 @@ public void TestClientNamedQueueRecoveryOnServerRestart()
}, s);
}
- [Fact]
- public void TestConsumerWorkServiceRecovery()
- {
- using (AutorecoveringConnection c = CreateAutorecoveringConnection())
- {
- IChannel m = c.CreateChannel();
- string q = m.QueueDeclare("dotnet-client.recovery.consumer_work_pool1",
- false, false, false, null).QueueName;
- var cons = new EventingBasicConsumer(m);
- m.BasicConsume(q, true, cons);
- AssertConsumerCount(m, q, 1);
-
- CloseAndWaitForRecovery(c);
-
- Assert.True(m.IsOpen);
- var latch = new ManualResetEventSlim(false);
- cons.Received += (s, args) => latch.Set();
-
- m.BasicPublish("", q, _encoding.GetBytes("msg"));
- Wait(latch);
-
- m.QueueDelete(q);
- }
- }
-
- [Fact]
- public void TestConsumerRecoveryOnClientNamedQueueWithOneRecovery()
- {
- string q0 = "dotnet-client.recovery.queue1";
- using (AutorecoveringConnection c = CreateAutorecoveringConnection())
- {
- IChannel m = c.CreateChannel();
- string q1 = m.QueueDeclare(q0, false, false, false, null).QueueName;
- Assert.Equal(q0, q1);
-
- var cons = new EventingBasicConsumer(m);
- m.BasicConsume(q1, true, cons);
- AssertConsumerCount(m, q1, 1);
-
- bool queueNameChangeAfterRecoveryCalled = false;
-
- c.QueueNameChangeAfterRecovery += (source, ea) => { queueNameChangeAfterRecoveryCalled = true; };
-
- CloseAndWaitForRecovery(c);
- AssertConsumerCount(m, q1, 1);
- Assert.False(queueNameChangeAfterRecoveryCalled);
-
- CloseAndWaitForRecovery(c);
- AssertConsumerCount(m, q1, 1);
- Assert.False(queueNameChangeAfterRecoveryCalled);
-
- CloseAndWaitForRecovery(c);
- AssertConsumerCount(m, q1, 1);
- Assert.False(queueNameChangeAfterRecoveryCalled);
-
- var latch = new ManualResetEventSlim(false);
- cons.Received += (s, args) => latch.Set();
-
- m.BasicPublish("", q1, _encoding.GetBytes("msg"));
- Wait(latch);
-
- m.QueueDelete(q1);
- }
- }
-
- [Fact]
- public void TestConsumerRecoveryWithServerNamedQueue()
- {
- // https://github.com/rabbitmq/rabbitmq-dotnet-client/issues/1238
- using (AutorecoveringConnection c = CreateAutorecoveringConnection())
- {
- IChannel ch = c.CreateChannel();
- QueueDeclareOk queueDeclareResult = ch.QueueDeclare(queue: string.Empty, durable: false, exclusive: true, autoDelete: true, arguments: null);
- string qname = queueDeclareResult.QueueName;
- Assert.False(string.IsNullOrEmpty(qname));
-
- var cons = new EventingBasicConsumer(ch);
- ch.BasicConsume(string.Empty, true, cons);
- AssertConsumerCount(ch, qname, 1);
-
- bool queueNameBeforeIsEqual = false;
- bool queueNameChangeAfterRecoveryCalled = false;
- string qnameAfterRecovery = null;
- c.QueueNameChangeAfterRecovery += (source, ea) =>
- {
- queueNameChangeAfterRecoveryCalled = true;
- queueNameBeforeIsEqual = qname.Equals(ea.NameBefore);
- qnameAfterRecovery = ea.NameAfter;
- };
-
- CloseAndWaitForRecovery(c);
-
- AssertConsumerCount(ch, qnameAfterRecovery, 1);
- Assert.True(queueNameChangeAfterRecoveryCalled);
- Assert.True(queueNameBeforeIsEqual);
- }
- }
-
[Fact]
public void TestConsumerRecoveryWithManyConsumers()
{
@@ -458,39 +259,11 @@ public void TestConsumerRecoveryWithManyConsumers()
((AutorecoveringConnection)_conn).ConsumerTagChangeAfterRecovery += (prev, current) => latch.Set();
CloseAndWaitForRecovery();
- Wait(latch);
+ Wait(latch, "consumer tag change after recovery");
Assert.True(_channel.IsOpen);
AssertConsumerCount(q, n);
}
- [Fact]
- public void TestCreateChannelOnClosedAutorecoveringConnectionDoesNotHang()
- {
- // we don't want this to recover quickly in this test
- AutorecoveringConnection c = CreateAutorecoveringConnection(TimeSpan.FromSeconds(20));
-
- try
- {
- c.Close();
- WaitForShutdown(c);
- Assert.False(c.IsOpen);
- c.CreateChannel();
- Assert.Fail("Expected an exception");
- }
- catch (AlreadyClosedException)
- {
- // expected
- }
- finally
- {
- StartRabbitMQ();
- if (c.IsOpen)
- {
- c.Abort();
- }
- }
- }
-
[Fact]
public void TestDeclarationOfManyAutoDeleteExchangesWithTransientExchangesThatAreDeleted()
{
@@ -532,7 +305,7 @@ public void TestDeclarationOfManyAutoDeleteExchangesWithTransientQueuesThatAreDe
{
string x = Guid.NewGuid().ToString();
_channel.ExchangeDeclare(x, "fanout", false, true, null);
- QueueDeclareOk q = _channel.QueueDeclare();
+ RabbitMQ.Client.QueueDeclareOk q = _channel.QueueDeclare();
_channel.QueueBind(q, x, "");
_channel.QueueDelete(q);
}
@@ -547,7 +320,7 @@ public void TestDeclarationOfManyAutoDeleteExchangesWithTransientQueuesThatAreUn
{
string x = Guid.NewGuid().ToString();
_channel.ExchangeDeclare(x, "fanout", false, true, null);
- QueueDeclareOk q = _channel.QueueDeclare();
+ RabbitMQ.Client.QueueDeclareOk q = _channel.QueueDeclare();
_channel.QueueBind(q, x, "");
_channel.QueueUnbind(q, x, "", null);
}
@@ -654,7 +427,7 @@ public void TestClientNamedTransientAutoDeleteQueueAndBindingRecovery()
ch.ExchangeDeclare(exchange: x, type: "fanout");
ch.BasicPublish(exchange: x, routingKey: "", body: _encoding.GetBytes("msg"));
WaitForConfirms(ch);
- QueueDeclareOk ok = ch.QueueDeclare(queue: q, durable: false, exclusive: false, autoDelete: true, arguments: null);
+ RabbitMQ.Client.QueueDeclareOk ok = ch.QueueDeclare(queue: q, durable: false, exclusive: false, autoDelete: true, arguments: null);
Assert.Equal(1u, ok.MessageCount);
ch.QueueDelete(q);
ch.ExchangeDelete(x);
@@ -680,14 +453,14 @@ public void TestServerNamedTransientAutoDeleteQueueAndBindingRecovery()
};
ch.QueueBind(queue: nameBefore, exchange: x, routingKey: "");
RestartServerAndWaitForRecovery();
- Wait(latch);
+ Wait(latch, "queue name change after recovery");
Assert.True(ch.IsOpen);
Assert.NotEqual(nameBefore, nameAfter);
ch.ConfirmSelect();
ch.ExchangeDeclare(exchange: x, type: "fanout");
ch.BasicPublish(exchange: x, routingKey: "", body: _encoding.GetBytes("msg"));
WaitForConfirms(ch);
- QueueDeclareOk ok = ch.QueueDeclarePassive(nameAfter);
+ RabbitMQ.Client.QueueDeclareOk ok = ch.QueueDeclarePassive(nameAfter);
Assert.Equal(1u, ok.MessageCount);
ch.QueueDelete(q);
ch.ExchangeDelete(x);
@@ -768,34 +541,6 @@ public void TestRecoveringConsumerHandlerOnConnection_EventArgumentsArePassedDow
Assert.Equal("event-handler-set-this-value", actualVal);
}
- [Fact]
- public void TestRecoveryWithTopologyDisabled()
- {
- AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryDisabled();
- IChannel ch = conn.CreateChannel();
- string s = "dotnet-client.test.recovery.q2";
- ch.QueueDelete(s);
- ch.QueueDeclare(s, false, true, false, null);
- ch.QueueDeclarePassive(s);
- Assert.True(ch.IsOpen);
-
- try
- {
- CloseAndWaitForRecovery(conn);
- Assert.True(ch.IsOpen);
- ch.QueueDeclarePassive(s);
- Assert.Fail("Expected an exception");
- }
- catch (OperationInterruptedException)
- {
- // expected
- }
- finally
- {
- conn.Abort();
- }
- }
-
[Fact]
public void TestServerNamedQueueRecovery()
{
@@ -812,7 +557,7 @@ public void TestServerNamedQueueRecovery()
connection.QueueNameChangeAfterRecovery += (source, ea) => { nameAfter = ea.NameAfter; };
CloseAndWaitForRecovery();
- Wait(latch);
+ Wait(latch, "recovery succeeded");
Assert.NotNull(nameAfter);
Assert.StartsWith("amq.", nameBefore);
@@ -851,7 +596,6 @@ public void TestShutdownEventHandlersRecoveryOnConnectionAfterDelayedServerResta
try
{
StopRabbitMQ();
- Console.WriteLine("Stopped RabbitMQ. About to sleep for multiple recovery intervals...");
Thread.Sleep(7000);
}
finally
@@ -859,8 +603,8 @@ public void TestShutdownEventHandlersRecoveryOnConnectionAfterDelayedServerResta
StartRabbitMQ();
}
- Wait(shutdownLatch, TimeSpan.FromSeconds(30));
- Wait(recoveryLatch, TimeSpan.FromSeconds(30));
+ Wait(shutdownLatch, WaitSpan, "connection shutdown");
+ Wait(recoveryLatch, WaitSpan, "connection recovery");
Assert.True(_conn.IsOpen);
Assert.True(counter >= 1);
}
@@ -906,7 +650,7 @@ public void TestRecoverTopologyOnDisposedChannel()
cons.Received += (s, args) => latch.Set();
_channel.BasicPublish("", q, _messageBody);
- Wait(latch);
+ Wait(latch, "received event");
_channel.QueueUnbind(q, x, rk);
_channel.ExchangeDelete(x);
@@ -1083,7 +827,7 @@ public void TestUnblockedListenersRecovery()
Block();
Unblock();
- Wait(latch);
+ Wait(latch, "connection unblocked");
}
[Fact]
@@ -1109,7 +853,7 @@ public void TestTopologyRecoveryQueueFilter()
try
{
CloseAndWaitForRecovery(conn);
- Wait(latch);
+ Wait(latch, "recovery succeeded");
Assert.True(ch.IsOpen);
AssertQueueRecovery(ch, queueToRecover, false);
@@ -1153,7 +897,7 @@ public void TestTopologyRecoveryExchangeFilter()
try
{
CloseAndWaitForRecovery(conn);
- Wait(latch);
+ Wait(latch, "recovery succeeded");
Assert.True(ch.IsOpen);
AssertExchangeRecovery(ch, exchangeToRecover);
@@ -1206,78 +950,11 @@ public void TestTopologyRecoveryBindingFilter()
try
{
CloseAndWaitForRecovery(conn);
- Wait(latch);
-
- Assert.True(ch.IsOpen);
- Assert.True(SendAndConsumeMessage(queueWithRecoveredBinding, exchange, bindingToRecover));
- Assert.False(SendAndConsumeMessage(queueWithIgnoredBinding, exchange, bindingToIgnore));
- }
- finally
- {
- conn.Abort();
- }
- }
-
- [Fact]
- public void TestTopologyRecoveryConsumerFilter()
- {
- var filter = new TopologyRecoveryFilter
- {
- ConsumerFilter = consumer => !consumer.ConsumerTag.Contains("filtered")
- };
- var latch = new ManualResetEventSlim(false);
- AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryFilter(filter);
- conn.RecoverySucceeded += (source, ea) => latch.Set();
- IChannel ch = conn.CreateChannel();
- ch.ConfirmSelect();
-
- var exchange = "topology.recovery.exchange";
- var queueWithRecoveredConsumer = "topology.recovery.queue.1";
- var queueWithIgnoredConsumer = "topology.recovery.queue.2";
- var binding1 = "recovered.binding.1";
- var binding2 = "recovered.binding.2";
-
- ch.ExchangeDeclare(exchange, "direct");
- ch.QueueDeclare(queueWithRecoveredConsumer, false, false, false, null);
- ch.QueueDeclare(queueWithIgnoredConsumer, false, false, false, null);
- ch.QueueBind(queueWithRecoveredConsumer, exchange, binding1);
- ch.QueueBind(queueWithIgnoredConsumer, exchange, binding2);
- ch.QueuePurge(queueWithRecoveredConsumer);
- ch.QueuePurge(queueWithIgnoredConsumer);
-
- var recoverLatch = new ManualResetEventSlim(false);
- var consumerToRecover = new EventingBasicConsumer(ch);
- consumerToRecover.Received += (source, ea) => recoverLatch.Set();
- ch.BasicConsume(queueWithRecoveredConsumer, true, "recovered.consumer", consumerToRecover);
-
- var ignoredLatch = new ManualResetEventSlim(false);
- var consumerToIgnore = new EventingBasicConsumer(ch);
- consumerToIgnore.Received += (source, ea) => ignoredLatch.Set();
- ch.BasicConsume(queueWithIgnoredConsumer, true, "filtered.consumer", consumerToIgnore);
-
- try
- {
- CloseAndWaitForRecovery(conn);
- Wait(latch);
+ Wait(latch, "recovery succeeded");
Assert.True(ch.IsOpen);
- ch.BasicPublish(exchange, binding1, Encoding.UTF8.GetBytes("test message"));
- ch.BasicPublish(exchange, binding2, Encoding.UTF8.GetBytes("test message"));
-
- Assert.True(recoverLatch.Wait(TimeSpan.FromSeconds(5)));
- Assert.False(ignoredLatch.Wait(TimeSpan.FromSeconds(5)));
-
- ch.BasicConsume(queueWithIgnoredConsumer, true, "filtered.consumer", consumerToIgnore);
-
- try
- {
- ch.BasicConsume(queueWithRecoveredConsumer, true, "recovered.consumer", consumerToRecover);
- Assert.Fail("Expected an exception");
- }
- catch (OperationInterruptedException e)
- {
- AssertShutdownError(e.ShutdownReason, 530); // NOT_ALLOWED - not allowed to reuse consumer tag
- }
+ Assert.True(SendAndConsumeMessage(_conn, queueWithRecoveredBinding, exchange, bindingToRecover));
+ Assert.False(SendAndConsumeMessage(_conn, queueWithIgnoredBinding, exchange, bindingToIgnore));
}
finally
{
@@ -1326,15 +1003,15 @@ public void TestTopologyRecoveryDefaultFilterRecoversAllEntities()
try
{
CloseAndWaitForRecovery(conn);
- Wait(latch);
+ Wait(latch, "recovery succeeded");
Assert.True(ch.IsOpen);
AssertExchangeRecovery(ch, exchange);
ch.QueueDeclarePassive(queue1);
ch.QueueDeclarePassive(queue2);
- ch.BasicPublish(exchange, binding1, Encoding.UTF8.GetBytes("test message"));
- ch.BasicPublish(exchange, binding2, Encoding.UTF8.GetBytes("test message"));
+ ch.BasicPublish(exchange, binding1, _encoding.GetBytes("test message"));
+ ch.BasicPublish(exchange, binding2, _encoding.GetBytes("test message"));
Assert.True(consumerLatch1.Wait(TimeSpan.FromSeconds(5)));
Assert.True(consumerLatch2.Wait(TimeSpan.FromSeconds(5)));
@@ -1385,7 +1062,7 @@ public void TestTopologyRecoveryQueueExceptionHandler()
try
{
CloseAndWaitForRecovery(conn);
- Wait(latch);
+ Wait(latch, "recovery succeded");
Assert.True(ch.IsOpen);
AssertQueueRecovery(ch, queueToRecoverSuccessfully, false);
@@ -1436,7 +1113,7 @@ public void TestTopologyRecoveryExchangeExceptionHandler()
try
{
CloseAndWaitForRecovery(conn);
- Wait(latch);
+ Wait(latch, "recovery succeeded");
Assert.True(ch.IsOpen);
AssertExchangeRecovery(ch, exchangeToRecoverSuccessfully);
@@ -1499,11 +1176,11 @@ public void TestTopologyRecoveryBindingExceptionHandler()
try
{
CloseAndWaitForRecovery(conn);
- Wait(latch);
+ Wait(latch, "recovery succeeded");
Assert.True(ch.IsOpen);
- Assert.True(SendAndConsumeMessage(queueWithRecoveredBinding, exchange, bindingToRecoverSuccessfully));
- Assert.True(SendAndConsumeMessage(queueWithExceptionBinding, exchange, bindingToRecoverWithException));
+ Assert.True(SendAndConsumeMessage(conn, queueWithRecoveredBinding, exchange, bindingToRecoverSuccessfully));
+ Assert.True(SendAndConsumeMessage(conn, queueWithExceptionBinding, exchange, bindingToRecoverWithException));
}
finally
{
@@ -1555,11 +1232,11 @@ public void TestTopologyRecoveryConsumerExceptionHandler()
try
{
CloseAndWaitForShutdown(conn);
- Wait(latch, TimeSpan.FromSeconds(20));
+ Wait(latch, TimeSpan.FromSeconds(20), "recovery succeeded");
Assert.True(ch.IsOpen);
- ch.BasicPublish("", queueWithExceptionConsumer, Encoding.UTF8.GetBytes("test message"));
+ ch.BasicPublish("", queueWithExceptionConsumer, _encoding.GetBytes("test message"));
Assert.True(recoverLatch.Wait(TimeSpan.FromSeconds(5)));
@@ -1578,236 +1255,5 @@ public void TestTopologyRecoveryConsumerExceptionHandler()
conn.Abort();
}
}
-
- internal bool SendAndConsumeMessage(string queue, string exchange, string routingKey)
- {
- using (var ch = _conn.CreateChannel())
- {
- var latch = new ManualResetEventSlim(false);
-
- var consumer = new AckingBasicConsumer(ch, 1, latch);
-
- ch.BasicConsume(queue, false, consumer);
-
- ch.BasicPublish(exchange, routingKey, Encoding.UTF8.GetBytes("test message"));
-
- return latch.Wait(TimeSpan.FromSeconds(5));
- }
- }
-
- internal void AssertExchangeRecovery(IChannel m, string x)
- {
- m.ConfirmSelect();
- WithTemporaryNonExclusiveQueue(m, (_, q) =>
- {
- string rk = "routing-key";
- m.QueueBind(q, x, rk);
- m.BasicPublish(x, rk, _messageBody);
-
- Assert.True(WaitForConfirms(m));
- m.ExchangeDeclarePassive(x);
- });
- }
-
- internal void AssertQueueRecovery(IChannel m, string q)
- {
- AssertQueueRecovery(m, q, true);
- }
-
- internal void AssertQueueRecovery(IChannel m, string q, bool exclusive, IDictionary arguments = null)
- {
- m.ConfirmSelect();
- m.QueueDeclarePassive(q);
- QueueDeclareOk ok1 = m.QueueDeclare(q, false, exclusive, false, arguments);
- Assert.Equal(0u, ok1.MessageCount);
- m.BasicPublish("", q, _messageBody);
- Assert.True(WaitForConfirms(m));
- QueueDeclareOk ok2 = m.QueueDeclare(q, false, exclusive, false, arguments);
- Assert.Equal(1u, ok2.MessageCount);
- }
-
- internal void AssertRecordedExchanges(AutorecoveringConnection c, int n)
- {
- Assert.Equal(n, c.RecordedExchangesCount);
- }
-
- internal void AssertRecordedQueues(AutorecoveringConnection c, int n)
- {
- Assert.Equal(n, c.RecordedQueuesCount);
- }
-
- internal void CloseAndWaitForRecovery()
- {
- CloseAndWaitForRecovery((AutorecoveringConnection)_conn);
- }
-
- internal void CloseAndWaitForRecovery(AutorecoveringConnection conn)
- {
- ManualResetEventSlim sl = PrepareForShutdown(conn);
- ManualResetEventSlim rl = PrepareForRecovery(conn);
- CloseConnection(conn);
- Wait(sl);
- Wait(rl);
- }
-
- internal void CloseAndWaitForShutdown(AutorecoveringConnection conn)
- {
- ManualResetEventSlim sl = PrepareForShutdown(conn);
- CloseConnection(conn);
- Wait(sl);
- }
-
- internal ManualResetEventSlim PrepareForRecovery(IConnection conn)
- {
- var latch = new ManualResetEventSlim(false);
-
- AutorecoveringConnection aconn = conn as AutorecoveringConnection;
- aconn.RecoverySucceeded += (source, ea) => latch.Set();
-
- return latch;
- }
-
- internal static ManualResetEventSlim PrepareForShutdown(IConnection conn)
- {
- var latch = new ManualResetEventSlim(false);
-
- AutorecoveringConnection aconn = conn as AutorecoveringConnection;
- aconn.ConnectionShutdown += (c, args) => latch.Set();
-
- return latch;
- }
-
- internal void RestartServerAndWaitForRecovery()
- {
- RestartServerAndWaitForRecovery((AutorecoveringConnection)_conn);
- }
-
- internal void RestartServerAndWaitForRecovery(AutorecoveringConnection conn)
- {
- ManualResetEventSlim sl = PrepareForShutdown(conn);
- ManualResetEventSlim rl = PrepareForRecovery(conn);
- RestartRabbitMQ();
- Wait(sl);
- Wait(rl);
- }
-
- internal void WaitForRecovery()
- {
- Wait(PrepareForRecovery((AutorecoveringConnection)_conn));
- }
-
- internal void WaitForRecovery(AutorecoveringConnection conn)
- {
- Wait(PrepareForRecovery(conn));
- }
-
- internal void WaitForShutdown()
- {
- Wait(PrepareForShutdown(_conn));
- }
-
- internal void WaitForShutdown(IConnection conn)
- {
- Wait(PrepareForShutdown(conn));
- }
-
- internal void PublishMessagesWhileClosingConn(string queueName)
- {
- using (AutorecoveringConnection publishingConn = CreateAutorecoveringConnection())
- {
- using (IChannel publishingChannel = publishingConn.CreateChannel())
- {
- for (ushort i = 0; i < _totalMessageCount; i++)
- {
- if (i == _closeAtCount)
- {
- CloseConnection(_conn);
- }
- publishingChannel.BasicPublish(string.Empty, queueName, _messageBody);
- }
- }
- }
- }
-
- public class AckingBasicConsumer : TestBasicConsumer
- {
- public AckingBasicConsumer(IChannel channel, ushort totalMessageCount, ManualResetEventSlim allMessagesSeenLatch)
- : base(channel, totalMessageCount, allMessagesSeenLatch)
- {
- }
-
- public override void PostHandleDelivery(ulong deliveryTag)
- {
- Channel.BasicAck(deliveryTag, false);
- }
- }
-
- public class NackingBasicConsumer : TestBasicConsumer
- {
- public NackingBasicConsumer(IChannel channel, ushort totalMessageCount, ManualResetEventSlim allMessagesSeenLatch)
- : base(channel, totalMessageCount, allMessagesSeenLatch)
- {
- }
-
- public override void PostHandleDelivery(ulong deliveryTag)
- {
- Channel.BasicNack(deliveryTag, false, false);
- }
- }
-
- public class RejectingBasicConsumer : TestBasicConsumer
- {
- public RejectingBasicConsumer(IChannel channel, ushort totalMessageCount, ManualResetEventSlim allMessagesSeenLatch)
- : base(channel, totalMessageCount, allMessagesSeenLatch)
- {
- }
-
- public override void PostHandleDelivery(ulong deliveryTag)
- {
- Channel.BasicReject(deliveryTag, false);
- }
- }
-
- public class TestBasicConsumer : DefaultBasicConsumer
- {
- private readonly ManualResetEventSlim _allMessagesSeenLatch;
- private readonly ushort _totalMessageCount;
- private ushort _counter = 0;
-
- public TestBasicConsumer(IChannel channel, ushort totalMessageCount, ManualResetEventSlim allMessagesSeenLatch)
- : base(channel)
- {
- _totalMessageCount = totalMessageCount;
- _allMessagesSeenLatch = allMessagesSeenLatch;
- }
-
- public override void HandleBasicDeliver(string consumerTag,
- ulong deliveryTag,
- bool redelivered,
- string exchange,
- string routingKey,
- in ReadOnlyBasicProperties properties,
- ReadOnlyMemory body)
- {
- try
- {
- PostHandleDelivery(deliveryTag);
- }
- finally
- {
- ++_counter;
- if (_counter >= _totalMessageCount)
- {
- _allMessagesSeenLatch.Set();
- }
- }
- }
-
- public virtual void PostHandleDelivery(ulong deliveryTag)
- {
- }
- }
}
}
-
-#pragma warning restore 0168
diff --git a/projects/Test/SequentialIntegration/TestConnectionRecoveryBase.cs b/projects/Test/SequentialIntegration/TestConnectionRecoveryBase.cs
new file mode 100644
index 0000000000..773a19a8b8
--- /dev/null
+++ b/projects/Test/SequentialIntegration/TestConnectionRecoveryBase.cs
@@ -0,0 +1,389 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using RabbitMQ.Client;
+using RabbitMQ.Client.Framing.Impl;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Test.SequentialIntegration
+{
+ public class TestConnectionRecoveryBase : SequentialIntegrationFixture
+ {
+ protected readonly byte[] _messageBody;
+ protected const ushort _totalMessageCount = 8192;
+ protected const ushort _closeAtCount = 16;
+
+ public TestConnectionRecoveryBase(ITestOutputHelper output) : base(output)
+ {
+ _messageBody = GetRandomBody(4096);
+ }
+
+ protected override void TearDown()
+ {
+ Unblock();
+ }
+
+ protected void AssertConsumerCount(string q, int count)
+ {
+ WithTemporaryChannel((m) =>
+ {
+ RabbitMQ.Client.QueueDeclareOk ok = m.QueueDeclarePassive(q);
+ Assert.Equal((uint)count, ok.ConsumerCount);
+ });
+ }
+
+ protected void AssertConsumerCount(IChannel m, string q, uint count)
+ {
+ RabbitMQ.Client.QueueDeclareOk ok = m.QueueDeclarePassive(q);
+ Assert.Equal(count, ok.ConsumerCount);
+ }
+
+ protected void AssertExchangeRecovery(IChannel m, string x)
+ {
+ m.ConfirmSelect();
+ WithTemporaryNonExclusiveQueue(m, (_, q) =>
+ {
+ string rk = "routing-key";
+ m.QueueBind(q, x, rk);
+ m.BasicPublish(x, rk, _messageBody);
+
+ Assert.True(WaitForConfirms(m));
+ m.ExchangeDeclarePassive(x);
+ });
+ }
+
+ protected void AssertQueueRecovery(IChannel m, string q)
+ {
+ AssertQueueRecovery(m, q, true);
+ }
+
+ protected void AssertQueueRecovery(IChannel m, string q, bool exclusive, IDictionary arguments = null)
+ {
+ m.ConfirmSelect();
+ m.QueueDeclarePassive(q);
+ RabbitMQ.Client.QueueDeclareOk ok1 = m.QueueDeclare(q, false, exclusive, false, arguments);
+ Assert.Equal(0u, ok1.MessageCount);
+ m.BasicPublish("", q, _messageBody);
+ Assert.True(WaitForConfirms(m));
+ RabbitMQ.Client.QueueDeclareOk ok2 = m.QueueDeclare(q, false, exclusive, false, arguments);
+ Assert.Equal(1u, ok2.MessageCount);
+ }
+
+ internal void AssertRecordedExchanges(AutorecoveringConnection c, int n)
+ {
+ Assert.Equal(n, c.RecordedExchangesCount);
+ }
+
+ internal void AssertRecordedQueues(AutorecoveringConnection c, int n)
+ {
+ Assert.Equal(n, c.RecordedQueuesCount);
+ }
+
+ internal AutorecoveringConnection CreateAutorecoveringConnection()
+ {
+ return CreateAutorecoveringConnection(RecoveryInterval);
+ }
+
+ internal AutorecoveringConnection CreateAutorecoveringConnection(TimeSpan networkRecoveryInterval)
+ {
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = true;
+ cf.NetworkRecoveryInterval = networkRecoveryInterval;
+ return (AutorecoveringConnection)cf.CreateConnection();
+ }
+
+ internal AutorecoveringConnection CreateAutorecoveringConnection(IList endpoints)
+ {
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = true;
+ // tests that use this helper will likely list unreachable hosts,
+ // make sure we time out quickly on those
+ cf.RequestedConnectionTimeout = TimeSpan.FromSeconds(1);
+ cf.NetworkRecoveryInterval = RecoveryInterval;
+ return (AutorecoveringConnection)cf.CreateConnection(endpoints);
+ }
+
+ internal AutorecoveringConnection CreateAutorecoveringConnectionWithTopologyRecoveryDisabled()
+ {
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = true;
+ cf.TopologyRecoveryEnabled = false;
+ cf.NetworkRecoveryInterval = RecoveryInterval;
+ return (AutorecoveringConnection)cf.CreateConnection();
+ }
+
+ internal AutorecoveringConnection CreateAutorecoveringConnectionWithTopologyRecoveryFilter(TopologyRecoveryFilter filter)
+ {
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = true;
+ cf.TopologyRecoveryEnabled = true;
+ cf.TopologyRecoveryFilter = filter;
+ return (AutorecoveringConnection)cf.CreateConnection();
+ }
+
+ internal AutorecoveringConnection CreateAutorecoveringConnectionWithTopologyRecoveryExceptionHandler(TopologyRecoveryExceptionHandler handler)
+ {
+ var cf = CreateConnectionFactory();
+ cf.AutomaticRecoveryEnabled = true;
+ cf.TopologyRecoveryEnabled = true;
+ cf.TopologyRecoveryExceptionHandler = handler;
+ return (AutorecoveringConnection)cf.CreateConnection();
+ }
+
+ protected void CloseConnection(IConnection conn)
+ {
+ _rabbitMQCtl.CloseConnection(conn);
+ }
+
+ protected void CloseAndWaitForRecovery()
+ {
+ CloseAndWaitForRecovery((AutorecoveringConnection)_conn);
+ }
+
+ internal void CloseAndWaitForRecovery(AutorecoveringConnection conn)
+ {
+ ManualResetEventSlim sl = PrepareForShutdown(conn);
+ ManualResetEventSlim rl = PrepareForRecovery(conn);
+ CloseConnection(conn);
+ Wait(sl, "connection shutdown");
+ Wait(rl, "connection recovery");
+ }
+
+ internal void CloseAndWaitForShutdown(AutorecoveringConnection conn)
+ {
+ ManualResetEventSlim sl = PrepareForShutdown(conn);
+ CloseConnection(conn);
+ Wait(sl, "connection shutdown");
+ }
+
+ protected string DeclareNonDurableExchange(IChannel m, string x)
+ {
+ m.ExchangeDeclare(x, "fanout", false);
+ return x;
+ }
+
+ protected string DeclareNonDurableExchangeNoWait(IChannel m, string x)
+ {
+ m.ExchangeDeclareNoWait(x, "fanout", false, false, null);
+ return x;
+ }
+
+ protected ManualResetEventSlim PrepareForRecovery(IConnection conn)
+ {
+ var latch = new ManualResetEventSlim(false);
+
+ AutorecoveringConnection aconn = conn as AutorecoveringConnection;
+ aconn.RecoverySucceeded += (source, ea) => latch.Set();
+
+ return latch;
+ }
+
+ protected void PublishMessagesWhileClosingConn(string queueName)
+ {
+ using (AutorecoveringConnection publishingConn = CreateAutorecoveringConnection())
+ {
+ using (IChannel publishingChannel = publishingConn.CreateChannel())
+ {
+ for (ushort i = 0; i < _totalMessageCount; i++)
+ {
+ if (i == _closeAtCount)
+ {
+ CloseConnection(_conn);
+ }
+ publishingChannel.BasicPublish(string.Empty, queueName, _messageBody);
+ }
+ }
+ }
+ }
+
+ protected static ManualResetEventSlim PrepareForShutdown(IConnection conn)
+ {
+ var latch = new ManualResetEventSlim(false);
+
+ AutorecoveringConnection aconn = conn as AutorecoveringConnection;
+ aconn.ConnectionShutdown += (c, args) => latch.Set();
+
+ return latch;
+ }
+
+ protected void RestartServerAndWaitForRecovery()
+ {
+ RestartServerAndWaitForRecovery((AutorecoveringConnection)_conn);
+ }
+
+ internal void RestartServerAndWaitForRecovery(AutorecoveringConnection conn)
+ {
+ ManualResetEventSlim sl = PrepareForShutdown(conn);
+ ManualResetEventSlim rl = PrepareForRecovery(conn);
+ RestartRabbitMQ();
+ Wait(sl, "connection shutdown");
+ Wait(rl, "connection recovery");
+ }
+
+ protected bool WaitForConfirms(IChannel m)
+ {
+ using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(4));
+ return m.WaitForConfirmsAsync(cts.Token).GetAwaiter().GetResult();
+ }
+
+ protected void WaitForRecovery()
+ {
+ Wait(PrepareForRecovery((AutorecoveringConnection)_conn), "recovery succeded");
+ }
+
+ internal void WaitForRecovery(AutorecoveringConnection conn)
+ {
+ Wait(PrepareForRecovery(conn), "recovery succeeded");
+ }
+
+ protected void WaitForShutdown()
+ {
+ Wait(PrepareForShutdown(_conn), "connection shutdown");
+ }
+
+ protected void WaitForShutdown(IConnection conn)
+ {
+ Wait(PrepareForShutdown(conn), "connection shutdown");
+ }
+
+ protected void WithTemporaryQueueNoWait(IChannel channel, Action action, string queue)
+ {
+ try
+ {
+ channel.QueueDeclareNoWait(queue, false, true, false, null);
+ action(channel, queue);
+ }
+ finally
+ {
+ WithTemporaryChannel(x => x.QueueDelete(queue));
+ }
+ }
+
+ public class AckingBasicConsumer : TestBasicConsumer
+ {
+ public AckingBasicConsumer(IChannel channel, ushort totalMessageCount, ManualResetEventSlim allMessagesSeenLatch)
+ : base(channel, totalMessageCount, allMessagesSeenLatch)
+ {
+ }
+
+ public override void PostHandleDelivery(ulong deliveryTag)
+ {
+ Channel.BasicAck(deliveryTag, false);
+ }
+ }
+
+ public class NackingBasicConsumer : TestBasicConsumer
+ {
+ public NackingBasicConsumer(IChannel channel, ushort totalMessageCount, ManualResetEventSlim allMessagesSeenLatch)
+ : base(channel, totalMessageCount, allMessagesSeenLatch)
+ {
+ }
+
+ public override void PostHandleDelivery(ulong deliveryTag)
+ {
+ Channel.BasicNack(deliveryTag, false, false);
+ }
+ }
+
+ public class RejectingBasicConsumer : TestBasicConsumer
+ {
+ public RejectingBasicConsumer(IChannel channel, ushort totalMessageCount, ManualResetEventSlim allMessagesSeenLatch)
+ : base(channel, totalMessageCount, allMessagesSeenLatch)
+ {
+ }
+
+ public override void PostHandleDelivery(ulong deliveryTag)
+ {
+ Channel.BasicReject(deliveryTag, false);
+ }
+ }
+
+ public class TestBasicConsumer : DefaultBasicConsumer
+ {
+ protected readonly ManualResetEventSlim _allMessagesSeenLatch;
+ protected readonly ushort _totalMessageCount;
+ protected ushort _counter = 0;
+
+ public TestBasicConsumer(IChannel channel, ushort totalMessageCount, ManualResetEventSlim allMessagesSeenLatch)
+ : base(channel)
+ {
+ _totalMessageCount = totalMessageCount;
+ _allMessagesSeenLatch = allMessagesSeenLatch;
+ }
+
+ public override void HandleBasicDeliver(string consumerTag,
+ ulong deliveryTag,
+ bool redelivered,
+ string exchange,
+ string routingKey,
+ in ReadOnlyBasicProperties properties,
+ ReadOnlyMemory body)
+ {
+ try
+ {
+ PostHandleDelivery(deliveryTag);
+ }
+ finally
+ {
+ ++_counter;
+ if (_counter >= _totalMessageCount)
+ {
+ _allMessagesSeenLatch.Set();
+ }
+ }
+ }
+
+ public virtual void PostHandleDelivery(ulong deliveryTag)
+ {
+ }
+ }
+
+ protected bool SendAndConsumeMessage(IConnection conn, string queue, string exchange, string routingKey)
+ {
+ using (IChannel ch = conn.CreateChannel())
+ {
+ var latch = new ManualResetEventSlim(false);
+
+ var consumer = new AckingBasicConsumer(ch, 1, latch);
+
+ ch.BasicConsume(queue, false, consumer);
+
+ ch.BasicPublish(exchange, routingKey, _encoding.GetBytes("test message"));
+
+ return latch.Wait(TimeSpan.FromSeconds(5));
+ }
+ }
+ }
+}
diff --git a/projects/Test/SequentialIntegration/TestConnectionRecoveryWithoutSetup.cs b/projects/Test/SequentialIntegration/TestConnectionRecoveryWithoutSetup.cs
new file mode 100644
index 0000000000..8832c5e92e
--- /dev/null
+++ b/projects/Test/SequentialIntegration/TestConnectionRecoveryWithoutSetup.cs
@@ -0,0 +1,333 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using RabbitMQ.Client;
+using RabbitMQ.Client.Events;
+using RabbitMQ.Client.Exceptions;
+using RabbitMQ.Client.Framing.Impl;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Test.SequentialIntegration
+{
+ public class TestConnectionRecoveryWithoutSetup : TestConnectionRecoveryBase
+ {
+ public TestConnectionRecoveryWithoutSetup(ITestOutputHelper output) : base(output)
+ {
+ }
+
+ protected override void SetUp()
+ {
+ Assert.Null(_connFactory);
+ Assert.Null(_conn);
+ Assert.Null(_channel);
+ }
+
+ [Fact]
+ public void TestBasicConnectionRecoveryWithHostnameList()
+ {
+ using (AutorecoveringConnection c = CreateAutorecoveringConnection(new List { "127.0.0.1", "localhost" }))
+ {
+ Assert.True(c.IsOpen);
+ CloseAndWaitForRecovery(c);
+ Assert.True(c.IsOpen);
+ }
+ }
+
+ [Fact]
+ public void TestBasicConnectionRecoveryWithHostnameListAndUnreachableHosts()
+ {
+ using (AutorecoveringConnection c = CreateAutorecoveringConnection(new List { "191.72.44.22", "127.0.0.1", "localhost" }))
+ {
+ Assert.True(c.IsOpen);
+ CloseAndWaitForRecovery(c);
+ Assert.True(c.IsOpen);
+ }
+ }
+
+ [Fact]
+ public void TestBasicConnectionRecoveryWithEndpointList()
+ {
+ using (AutorecoveringConnection c = CreateAutorecoveringConnection(
+ new List
+ {
+ new AmqpTcpEndpoint("127.0.0.1"),
+ new AmqpTcpEndpoint("localhost")
+ }))
+ {
+ Assert.True(c.IsOpen);
+ CloseAndWaitForRecovery(c);
+ Assert.True(c.IsOpen);
+ }
+ }
+
+ [Fact]
+ public void TestBasicConnectionRecoveryWithEndpointListAndUnreachableHosts()
+ {
+ using (AutorecoveringConnection c = CreateAutorecoveringConnection(
+ new List
+ {
+ new AmqpTcpEndpoint("191.72.44.22"),
+ new AmqpTcpEndpoint("127.0.0.1"),
+ new AmqpTcpEndpoint("localhost")
+ }))
+ {
+ Assert.True(c.IsOpen);
+ CloseAndWaitForRecovery(c);
+ Assert.True(c.IsOpen);
+ }
+ }
+
+ [Fact]
+ public void TestConsumerWorkServiceRecovery()
+ {
+ using (AutorecoveringConnection c = CreateAutorecoveringConnection())
+ {
+ IChannel m = c.CreateChannel();
+ string q = m.QueueDeclare("dotnet-client.recovery.consumer_work_pool1",
+ false, false, false, null).QueueName;
+ var cons = new EventingBasicConsumer(m);
+ m.BasicConsume(q, true, cons);
+ AssertConsumerCount(m, q, 1);
+
+ CloseAndWaitForRecovery(c);
+
+ Assert.True(m.IsOpen);
+ var latch = new ManualResetEventSlim(false);
+ cons.Received += (s, args) => latch.Set();
+
+ m.BasicPublish("", q, _encoding.GetBytes("msg"));
+ Wait(latch, "received event");
+
+ m.QueueDelete(q);
+ }
+ }
+
+ [Fact]
+ public void TestConsumerRecoveryOnClientNamedQueueWithOneRecovery()
+ {
+ string q0 = "dotnet-client.recovery.queue1";
+ using (AutorecoveringConnection c = CreateAutorecoveringConnection())
+ {
+ IChannel m = c.CreateChannel();
+ string q1 = m.QueueDeclare(q0, false, false, false, null).QueueName;
+ Assert.Equal(q0, q1);
+
+ var cons = new EventingBasicConsumer(m);
+ m.BasicConsume(q1, true, cons);
+ AssertConsumerCount(m, q1, 1);
+
+ bool queueNameChangeAfterRecoveryCalled = false;
+
+ c.QueueNameChangeAfterRecovery += (source, ea) => { queueNameChangeAfterRecoveryCalled = true; };
+
+ CloseAndWaitForRecovery(c);
+ AssertConsumerCount(m, q1, 1);
+ Assert.False(queueNameChangeAfterRecoveryCalled);
+
+ CloseAndWaitForRecovery(c);
+ AssertConsumerCount(m, q1, 1);
+ Assert.False(queueNameChangeAfterRecoveryCalled);
+
+ CloseAndWaitForRecovery(c);
+ AssertConsumerCount(m, q1, 1);
+ Assert.False(queueNameChangeAfterRecoveryCalled);
+
+ var latch = new ManualResetEventSlim(false);
+ cons.Received += (s, args) => latch.Set();
+
+ m.BasicPublish("", q1, _encoding.GetBytes("msg"));
+ Wait(latch, "received event");
+
+ m.QueueDelete(q1);
+ }
+ }
+
+ [Fact]
+ public void TestConsumerRecoveryWithServerNamedQueue()
+ {
+ // https://github.com/rabbitmq/rabbitmq-dotnet-client/issues/1238
+ using (AutorecoveringConnection c = CreateAutorecoveringConnection())
+ {
+ IChannel ch = c.CreateChannel();
+ RabbitMQ.Client.QueueDeclareOk queueDeclareResult = ch.QueueDeclare(queue: string.Empty, durable: false, exclusive: true, autoDelete: true, arguments: null);
+ string qname = queueDeclareResult.QueueName;
+ Assert.False(string.IsNullOrEmpty(qname));
+
+ var cons = new EventingBasicConsumer(ch);
+ ch.BasicConsume(string.Empty, true, cons);
+ AssertConsumerCount(ch, qname, 1);
+
+ bool queueNameBeforeIsEqual = false;
+ bool queueNameChangeAfterRecoveryCalled = false;
+ string qnameAfterRecovery = null;
+ c.QueueNameChangeAfterRecovery += (source, ea) =>
+ {
+ queueNameChangeAfterRecoveryCalled = true;
+ queueNameBeforeIsEqual = qname.Equals(ea.NameBefore);
+ qnameAfterRecovery = ea.NameAfter;
+ };
+
+ CloseAndWaitForRecovery(c);
+
+ AssertConsumerCount(ch, qnameAfterRecovery, 1);
+ Assert.True(queueNameChangeAfterRecoveryCalled);
+ Assert.True(queueNameBeforeIsEqual);
+ }
+ }
+
+ [Fact]
+ public void TestCreateChannelOnClosedAutorecoveringConnectionDoesNotHang()
+ {
+ // we don't want this to recover quickly in this test
+ AutorecoveringConnection c = CreateAutorecoveringConnection(TimeSpan.FromSeconds(20));
+
+ try
+ {
+ c.Close();
+ WaitForShutdown(c);
+ Assert.False(c.IsOpen);
+ c.CreateChannel();
+ Assert.Fail("Expected an exception");
+ }
+ catch (AlreadyClosedException)
+ {
+ // expected
+ }
+ finally
+ {
+ StartRabbitMQ();
+ if (c.IsOpen)
+ {
+ c.Abort();
+ }
+ }
+ }
+
+ [Fact]
+ public void TestTopologyRecoveryConsumerFilter()
+ {
+ var filter = new TopologyRecoveryFilter
+ {
+ ConsumerFilter = consumer => !consumer.ConsumerTag.Contains("filtered")
+ };
+ var latch = new ManualResetEventSlim(false);
+ AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryFilter(filter);
+ conn.RecoverySucceeded += (source, ea) => latch.Set();
+ IChannel ch = conn.CreateChannel();
+ ch.ConfirmSelect();
+
+ var exchange = "topology.recovery.exchange";
+ var queueWithRecoveredConsumer = "topology.recovery.queue.1";
+ var queueWithIgnoredConsumer = "topology.recovery.queue.2";
+ var binding1 = "recovered.binding.1";
+ var binding2 = "recovered.binding.2";
+
+ ch.ExchangeDeclare(exchange, "direct");
+ ch.QueueDeclare(queueWithRecoveredConsumer, false, false, false, null);
+ ch.QueueDeclare(queueWithIgnoredConsumer, false, false, false, null);
+ ch.QueueBind(queueWithRecoveredConsumer, exchange, binding1);
+ ch.QueueBind(queueWithIgnoredConsumer, exchange, binding2);
+ ch.QueuePurge(queueWithRecoveredConsumer);
+ ch.QueuePurge(queueWithIgnoredConsumer);
+
+ var recoverLatch = new ManualResetEventSlim(false);
+ var consumerToRecover = new EventingBasicConsumer(ch);
+ consumerToRecover.Received += (source, ea) => recoverLatch.Set();
+ ch.BasicConsume(queueWithRecoveredConsumer, true, "recovered.consumer", consumerToRecover);
+
+ var ignoredLatch = new ManualResetEventSlim(false);
+ var consumerToIgnore = new EventingBasicConsumer(ch);
+ consumerToIgnore.Received += (source, ea) => ignoredLatch.Set();
+ ch.BasicConsume(queueWithIgnoredConsumer, true, "filtered.consumer", consumerToIgnore);
+
+ try
+ {
+ CloseAndWaitForRecovery(conn);
+ Wait(latch, "recovery succeeded");
+
+ Assert.True(ch.IsOpen);
+ ch.BasicPublish(exchange, binding1, _encoding.GetBytes("test message"));
+ ch.BasicPublish(exchange, binding2, _encoding.GetBytes("test message"));
+
+ Assert.True(recoverLatch.Wait(TimeSpan.FromSeconds(5)));
+ Assert.False(ignoredLatch.Wait(TimeSpan.FromSeconds(5)));
+
+ ch.BasicConsume(queueWithIgnoredConsumer, true, "filtered.consumer", consumerToIgnore);
+
+ try
+ {
+ ch.BasicConsume(queueWithRecoveredConsumer, true, "recovered.consumer", consumerToRecover);
+ Assert.Fail("Expected an exception");
+ }
+ catch (OperationInterruptedException e)
+ {
+ AssertShutdownError(e.ShutdownReason, 530); // NOT_ALLOWED - not allowed to reuse consumer tag
+ }
+ }
+ finally
+ {
+ conn.Abort();
+ }
+ }
+
+ [Fact]
+ public void TestRecoveryWithTopologyDisabled()
+ {
+ AutorecoveringConnection conn = CreateAutorecoveringConnectionWithTopologyRecoveryDisabled();
+ IChannel ch = conn.CreateChannel();
+ string s = "dotnet-client.test.recovery.q2";
+ ch.QueueDelete(s);
+ ch.QueueDeclare(s, false, true, false, null);
+ ch.QueueDeclarePassive(s);
+ Assert.True(ch.IsOpen);
+
+ try
+ {
+ CloseAndWaitForRecovery(conn);
+ Assert.True(ch.IsOpen);
+ ch.QueueDeclarePassive(s);
+ Assert.Fail("Expected an exception");
+ }
+ catch (OperationInterruptedException)
+ {
+ // expected
+ }
+ finally
+ {
+ conn.Abort();
+ }
+ }
+ }
+}
diff --git a/projects/Unit/APIApproval.Approve.verified.txt b/projects/Test/Unit/APIApproval.Approve.verified.txt
similarity index 96%
rename from projects/Unit/APIApproval.Approve.verified.txt
rename to projects/Test/Unit/APIApproval.Approve.verified.txt
index a44010c887..728a396384 100644
--- a/projects/Unit/APIApproval.Approve.verified.txt
+++ b/projects/Test/Unit/APIApproval.Approve.verified.txt
@@ -1,4 +1,7 @@
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Benchmarks, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5")]
+[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Common, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5")]
+[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Integration, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5")]
+[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"SequentialIntegration, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Unit, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5")]
namespace RabbitMQ.Client
{
@@ -403,7 +406,6 @@ namespace RabbitMQ.Client
ulong NextPublishSeqNo { get; }
event System.EventHandler BasicAcks;
event System.EventHandler BasicNacks;
- event System.EventHandler BasicRecoverOk;
event System.EventHandler BasicReturn;
event System.EventHandler CallbackException;
event System.EventHandler ChannelShutdown;
@@ -416,7 +418,9 @@ namespace RabbitMQ.Client
string BasicConsume(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, System.Collections.Generic.IDictionary arguments, RabbitMQ.Client.IBasicConsumer consumer);
System.Threading.Tasks.ValueTask BasicConsumeAsync(string queue, bool autoAck, string consumerTag, bool noLocal, bool exclusive, System.Collections.Generic.IDictionary arguments, RabbitMQ.Client.IBasicConsumer consumer);
RabbitMQ.Client.BasicGetResult BasicGet(string queue, bool autoAck);
+ System.Threading.Tasks.ValueTask BasicGetAsync(string queue, bool autoAck);
void BasicNack(ulong deliveryTag, bool multiple, bool requeue);
+ System.Threading.Tasks.ValueTask BasicNackAsync(ulong deliveryTag, bool multiple, bool requeue);
void BasicPublish(RabbitMQ.Client.CachedString exchange, RabbitMQ.Client.CachedString routingKey, in TProperties basicProperties, System.ReadOnlyMemory body = default, bool mandatory = false)
where TProperties : RabbitMQ.Client.IReadOnlyBasicProperties, RabbitMQ.Client.IAmqpHeader;
void BasicPublish(string exchange, string routingKey, in TProperties basicProperties, System.ReadOnlyMemory body = default, bool mandatory = false)
@@ -427,14 +431,12 @@ namespace RabbitMQ.Client
where TProperties : RabbitMQ.Client.IReadOnlyBasicProperties, RabbitMQ.Client.IAmqpHeader;
void BasicQos(uint prefetchSize, ushort prefetchCount, bool global);
System.Threading.Tasks.ValueTask BasicQosAsync(uint prefetchSize, ushort prefetchCount, bool global);
- [System.Obsolete]
- void BasicRecover(bool requeue);
- [System.Obsolete]
- void BasicRecoverAsync(bool requeue);
void BasicReject(ulong deliveryTag, bool requeue);
System.Threading.Tasks.ValueTask BasicRejectAsync(ulong deliveryTag, bool requeue);
void Close(ushort replyCode, string replyText, bool abort);
+ System.Threading.Tasks.ValueTask CloseAsync(RabbitMQ.Client.ShutdownEventArgs reason, bool abort);
void ConfirmSelect();
+ System.Threading.Tasks.ValueTask ConfirmSelectAsync();
uint ConsumerCount(string queue);
void ExchangeBind(string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments);
System.Threading.Tasks.ValueTask ExchangeBindAsync(string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments);
@@ -461,10 +463,15 @@ namespace RabbitMQ.Client
System.Threading.Tasks.ValueTask QueueDeleteAsync(string queue, bool ifUnused, bool ifEmpty);
void QueueDeleteNoWait(string queue, bool ifUnused, bool ifEmpty);
uint QueuePurge(string queue);
+ System.Threading.Tasks.ValueTask QueuePurgeAsync(string queue);
void QueueUnbind(string queue, string exchange, string routingKey, System.Collections.Generic.IDictionary arguments);
+ System.Threading.Tasks.ValueTask QueueUnbindAsync(string queue, string exchange, string routingKey, System.Collections.Generic.IDictionary arguments);
void TxCommit();
+ System.Threading.Tasks.ValueTask TxCommitAsync();
void TxRollback();
+ System.Threading.Tasks.ValueTask TxRollbackAsync();
void TxSelect();
+ System.Threading.Tasks.ValueTask TxSelectAsync();
System.Threading.Tasks.Task WaitForConfirmsAsync(System.Threading.CancellationToken token = default);
System.Threading.Tasks.Task WaitForConfirmsOrDieAsync(System.Threading.CancellationToken token = default);
}
@@ -484,6 +491,7 @@ namespace RabbitMQ.Client
public static System.Threading.Tasks.ValueTask BasicPublishAsync(this RabbitMQ.Client.IChannel channel, string exchange, string routingKey, System.ReadOnlyMemory body = default, bool mandatory = false) { }
public static void Close(this RabbitMQ.Client.IChannel channel) { }
public static void Close(this RabbitMQ.Client.IChannel channel, ushort replyCode, string replyText) { }
+ public static System.Threading.Tasks.ValueTask CloseAsync(this RabbitMQ.Client.IChannel channel) { }
public static void ExchangeBind(this RabbitMQ.Client.IChannel channel, string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments = null) { }
public static void ExchangeBindNoWait(this RabbitMQ.Client.IChannel channel, string destination, string source, string routingKey, System.Collections.Generic.IDictionary arguments = null) { }
public static void ExchangeDeclare(this RabbitMQ.Client.IChannel channel, string exchange, string type, bool durable = false, bool autoDelete = false, System.Collections.Generic.IDictionary arguments = null) { }
diff --git a/projects/Unit/APIApproval.cs b/projects/Test/Unit/APIApproval.cs
similarity index 98%
rename from projects/Unit/APIApproval.cs
rename to projects/Test/Unit/APIApproval.cs
index 25e35bd8d4..8ce97b5c34 100644
--- a/projects/Unit/APIApproval.cs
+++ b/projects/Test/Unit/APIApproval.cs
@@ -36,7 +36,7 @@
using VerifyXunit;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
[UsesVerify]
public class APIApproval
diff --git a/projects/Unit/Helper/DebugUtil.cs b/projects/Test/Unit/DebugUtil.cs
similarity index 98%
rename from projects/Unit/Helper/DebugUtil.cs
rename to projects/Test/Unit/DebugUtil.cs
index 9ee8ed76bd..34d44ce90d 100644
--- a/projects/Unit/Helper/DebugUtil.cs
+++ b/projects/Test/Unit/DebugUtil.cs
@@ -34,7 +34,7 @@
using System.IO;
using System.Reflection;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
///Miscellaneous debugging and development utilities.
///
@@ -62,7 +62,7 @@ public static void Dump(byte[] bytes, TextWriter writer)
{
writer.Write("{0:X2}", bytes[count + i]);
}
- for (int i = 0; i < (rowlen - thisRow); i++)
+ for (int i = 0; i < rowlen - thisRow; i++)
{
writer.Write(" ");
}
diff --git a/projects/Unit/TestAmqpTcpEndpointParsing.cs b/projects/Test/Unit/TestAmqpTcpEndpointParsing.cs
similarity index 99%
rename from projects/Unit/TestAmqpTcpEndpointParsing.cs
rename to projects/Test/Unit/TestAmqpTcpEndpointParsing.cs
index 8719bb00d4..379a5b34d0 100644
--- a/projects/Unit/TestAmqpTcpEndpointParsing.cs
+++ b/projects/Test/Unit/TestAmqpTcpEndpointParsing.cs
@@ -30,9 +30,10 @@
//---------------------------------------------------------------------------
using System;
+using RabbitMQ.Client;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class TestAmqpTcpEndpointParsing
{
diff --git a/projects/Unit/TestAmqpUri.cs b/projects/Test/Unit/TestAmqpUri.cs
similarity index 99%
rename from projects/Unit/TestAmqpUri.cs
rename to projects/Test/Unit/TestAmqpUri.cs
index e58ae23ae1..823f41205b 100644
--- a/projects/Unit/TestAmqpUri.cs
+++ b/projects/Test/Unit/TestAmqpUri.cs
@@ -30,9 +30,10 @@
//---------------------------------------------------------------------------
using System;
+using RabbitMQ.Client;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class TestAmqpUri
{
diff --git a/projects/Unit/TestBasicProperties.cs b/projects/Test/Unit/TestBasicProperties.cs
similarity index 97%
rename from projects/Unit/TestBasicProperties.cs
rename to projects/Test/Unit/TestBasicProperties.cs
index 9fdbd634b3..0eb2cc04c4 100644
--- a/projects/Unit/TestBasicProperties.cs
+++ b/projects/Test/Unit/TestBasicProperties.cs
@@ -30,14 +30,10 @@
//---------------------------------------------------------------------------
using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using RabbitMQ.Client.Events;
+using RabbitMQ.Client;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class TestBasicProperties
{
diff --git a/projects/Unit/TestBlockingCell.cs b/projects/Test/Unit/TestBlockingCell.cs
similarity index 99%
rename from projects/Unit/TestBlockingCell.cs
rename to projects/Test/Unit/TestBlockingCell.cs
index ecbb48db5d..9439e4298a 100644
--- a/projects/Unit/TestBlockingCell.cs
+++ b/projects/Test/Unit/TestBlockingCell.cs
@@ -34,7 +34,7 @@
using RabbitMQ.Util;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class TestBlockingCell : TimingFixture
{
diff --git a/projects/Unit/TestContentHeaderCodec.cs b/projects/Test/Unit/TestContentHeaderCodec.cs
similarity index 99%
rename from projects/Unit/TestContentHeaderCodec.cs
rename to projects/Test/Unit/TestContentHeaderCodec.cs
index 72050bdf38..4429b468eb 100644
--- a/projects/Unit/TestContentHeaderCodec.cs
+++ b/projects/Test/Unit/TestContentHeaderCodec.cs
@@ -31,9 +31,10 @@
using System;
using System.Collections.Generic;
+using RabbitMQ.Client;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class TestContentHeaderCodec
{
diff --git a/projects/Unit/TestFieldTableFormatting.cs b/projects/Test/Unit/TestFieldTableFormatting.cs
similarity index 99%
rename from projects/Unit/TestFieldTableFormatting.cs
rename to projects/Test/Unit/TestFieldTableFormatting.cs
index 211a1ca161..87232c2b87 100644
--- a/projects/Unit/TestFieldTableFormatting.cs
+++ b/projects/Test/Unit/TestFieldTableFormatting.cs
@@ -32,10 +32,12 @@
using System;
using System.Collections;
using System.Text;
+using RabbitMQ;
+using RabbitMQ.Client;
using RabbitMQ.Client.Impl;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class TestFieldTableFormatting : WireFormattingFixture
{
diff --git a/projects/Unit/TestFieldTableFormattingGeneric.cs b/projects/Test/Unit/TestFieldTableFormattingGeneric.cs
similarity index 99%
rename from projects/Unit/TestFieldTableFormattingGeneric.cs
rename to projects/Test/Unit/TestFieldTableFormattingGeneric.cs
index 4df0022264..03ca347c89 100644
--- a/projects/Unit/TestFieldTableFormattingGeneric.cs
+++ b/projects/Test/Unit/TestFieldTableFormattingGeneric.cs
@@ -33,10 +33,12 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
+using RabbitMQ;
+using RabbitMQ.Client;
using RabbitMQ.Client.Impl;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class TestFieldTableFormattingGeneric : WireFormattingFixture
{
diff --git a/projects/Unit/TestFrameFormatting.cs b/projects/Test/Unit/TestFrameFormatting.cs
similarity index 87%
rename from projects/Unit/TestFrameFormatting.cs
rename to projects/Test/Unit/TestFrameFormatting.cs
index 08e1d8427f..24afa90133 100644
--- a/projects/Unit/TestFrameFormatting.cs
+++ b/projects/Test/Unit/TestFrameFormatting.cs
@@ -32,17 +32,18 @@
using System;
using System.Buffers;
using System.Runtime.InteropServices;
+using RabbitMQ.Client;
using RabbitMQ.Client.Framing.Impl;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class TestFrameFormatting : WireFormattingFixture
{
[Fact]
public void HeartbeatFrame()
{
- Memory memory = Impl.Framing.Heartbeat.GetHeartbeatFrame();
+ Memory memory = RabbitMQ.Client.Impl.Framing.Heartbeat.GetHeartbeatFrame();
Span frameSpan = memory.Span;
try
@@ -74,10 +75,10 @@ public void HeaderFrame()
var basicProperties = new BasicProperties { AppId = "A" };
int payloadSize = ((IAmqpWriteable)basicProperties).GetRequiredBufferSize();
- byte[] frameBytes = new byte[Impl.Framing.Header.FrameSize + BodyLength + payloadSize];
- Impl.Framing.Header.WriteTo(frameBytes, Channel, ref basicProperties, BodyLength);
+ byte[] frameBytes = new byte[RabbitMQ.Client.Impl.Framing.Header.FrameSize + BodyLength + payloadSize];
+ RabbitMQ.Client.Impl.Framing.Header.WriteTo(frameBytes, Channel, ref basicProperties, BodyLength);
- Assert.Equal(20, Impl.Framing.Header.FrameSize);
+ Assert.Equal(20, RabbitMQ.Client.Impl.Framing.Header.FrameSize);
Assert.Equal(Constants.FrameHeader, frameBytes[0]);
Assert.Equal(0, frameBytes[1]); // channel
Assert.Equal(Channel, frameBytes[2]); // channel
@@ -111,10 +112,10 @@ public void MethodFrame()
var method = new BasicPublish("E", "R", true, true);
int payloadSize = method.GetRequiredBufferSize();
- byte[] frameBytes = new byte[Impl.Framing.Method.FrameSize + payloadSize];
- Impl.Framing.Method.WriteTo(frameBytes, Channel, ref method);
+ byte[] frameBytes = new byte[RabbitMQ.Client.Impl.Framing.Method.FrameSize + payloadSize];
+ RabbitMQ.Client.Impl.Framing.Method.WriteTo(frameBytes, Channel, ref method);
- Assert.Equal(12, Impl.Framing.Method.FrameSize);
+ Assert.Equal(12, RabbitMQ.Client.Impl.Framing.Method.FrameSize);
Assert.Equal(Constants.FrameMethod, frameBytes[0]);
Assert.Equal(0, frameBytes[1]); // channel
Assert.Equal(Channel, frameBytes[2]); // channel
@@ -142,10 +143,10 @@ public void BodySegmentFrame()
const int Channel = 3;
byte[] payload = new byte[4];
- byte[] frameBytes = new byte[Impl.Framing.BodySegment.FrameSize + payload.Length];
- Impl.Framing.BodySegment.WriteTo(frameBytes, Channel, payload);
+ byte[] frameBytes = new byte[RabbitMQ.Client.Impl.Framing.BodySegment.FrameSize + payload.Length];
+ RabbitMQ.Client.Impl.Framing.BodySegment.WriteTo(frameBytes, Channel, payload);
- Assert.Equal(8, Impl.Framing.BodySegment.FrameSize);
+ Assert.Equal(8, RabbitMQ.Client.Impl.Framing.BodySegment.FrameSize);
Assert.Equal(Constants.FrameBody, frameBytes[0]);
Assert.Equal(0, frameBytes[1]); // channel
Assert.Equal(Channel, frameBytes[2]); // channel
diff --git a/projects/Unit/TestIEndpointResolverExtensions.cs b/projects/Test/Unit/TestIEndpointResolverExtensions.cs
similarity index 98%
rename from projects/Unit/TestIEndpointResolverExtensions.cs
rename to projects/Test/Unit/TestIEndpointResolverExtensions.cs
index b09a6f6612..b9fcd81750 100644
--- a/projects/Unit/TestIEndpointResolverExtensions.cs
+++ b/projects/Test/Unit/TestIEndpointResolverExtensions.cs
@@ -31,9 +31,10 @@
using System;
using System.Collections.Generic;
+using RabbitMQ.Client;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class TestEndpointResolver : IEndpointResolver
{
diff --git a/projects/Unit/TestIntAllocator.cs b/projects/Test/Unit/TestIntAllocator.cs
similarity index 98%
rename from projects/Unit/TestIntAllocator.cs
rename to projects/Test/Unit/TestIntAllocator.cs
index cdf81c202b..18c41bcce5 100644
--- a/projects/Unit/TestIntAllocator.cs
+++ b/projects/Test/Unit/TestIntAllocator.cs
@@ -34,7 +34,7 @@
using RabbitMQ.Util;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class TestIntAllocator
{
diff --git a/projects/Unit/TestMethodArgumentCodec.cs b/projects/Test/Unit/TestMethodArgumentCodec.cs
similarity index 99%
rename from projects/Unit/TestMethodArgumentCodec.cs
rename to projects/Test/Unit/TestMethodArgumentCodec.cs
index cf8b9cf572..7c8f279c9e 100644
--- a/projects/Unit/TestMethodArgumentCodec.cs
+++ b/projects/Test/Unit/TestMethodArgumentCodec.cs
@@ -32,10 +32,11 @@
using System;
using System.Collections;
using System.Text;
+using RabbitMQ;
using RabbitMQ.Client.Impl;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class TestMethodArgumentCodec
{
diff --git a/projects/Unit/TestNetworkByteOrderSerialization.cs b/projects/Test/Unit/TestNetworkByteOrderSerialization.cs
similarity index 99%
rename from projects/Unit/TestNetworkByteOrderSerialization.cs
rename to projects/Test/Unit/TestNetworkByteOrderSerialization.cs
index 95368903fa..a0d0e5cf95 100644
--- a/projects/Unit/TestNetworkByteOrderSerialization.cs
+++ b/projects/Test/Unit/TestNetworkByteOrderSerialization.cs
@@ -30,10 +30,11 @@
//---------------------------------------------------------------------------
using System;
+using RabbitMQ;
using RabbitMQ.Util;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class TestNetworkByteOrderSerialization
{
diff --git a/projects/Unit/TestPublicationAddress.cs b/projects/Test/Unit/TestPublicationAddress.cs
similarity index 98%
rename from projects/Unit/TestPublicationAddress.cs
rename to projects/Test/Unit/TestPublicationAddress.cs
index 824dcc5218..2d570bcf7f 100644
--- a/projects/Unit/TestPublicationAddress.cs
+++ b/projects/Test/Unit/TestPublicationAddress.cs
@@ -30,9 +30,10 @@
//---------------------------------------------------------------------------
using System;
+using RabbitMQ.Client;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class TestPublicationAddress
{
diff --git a/projects/Unit/TestRpcContinuationQueue.cs b/projects/Test/Unit/TestRpcContinuationQueue.cs
similarity index 98%
rename from projects/Unit/TestRpcContinuationQueue.cs
rename to projects/Test/Unit/TestRpcContinuationQueue.cs
index 8ec90ea162..f3fcfaea80 100644
--- a/projects/Unit/TestRpcContinuationQueue.cs
+++ b/projects/Test/Unit/TestRpcContinuationQueue.cs
@@ -33,7 +33,7 @@
using RabbitMQ.Client.Impl;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class TestRpcContinuationQueue
{
diff --git a/projects/Unit/TestTcpClientAdapter.cs b/projects/Test/Unit/TestTcpClientAdapter.cs
similarity index 98%
rename from projects/Unit/TestTcpClientAdapter.cs
rename to projects/Test/Unit/TestTcpClientAdapter.cs
index f3f9009484..14ef1aa3ff 100644
--- a/projects/Unit/TestTcpClientAdapter.cs
+++ b/projects/Test/Unit/TestTcpClientAdapter.cs
@@ -34,7 +34,7 @@
using RabbitMQ.Client.Impl;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class TestTcpClientAdapter
{
diff --git a/projects/Unit/TestTimerBasedCredentialRefresher.cs b/projects/Test/Unit/TestTimerBasedCredentialRefresher.cs
similarity index 99%
rename from projects/Unit/TestTimerBasedCredentialRefresher.cs
rename to projects/Test/Unit/TestTimerBasedCredentialRefresher.cs
index 4d047e9d80..367f7caa7c 100644
--- a/projects/Unit/TestTimerBasedCredentialRefresher.cs
+++ b/projects/Test/Unit/TestTimerBasedCredentialRefresher.cs
@@ -31,10 +31,11 @@
using System;
using System.Threading;
+using RabbitMQ.Client;
using Xunit;
using Xunit.Abstractions;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class MockCredentialsProvider : ICredentialsProvider
{
diff --git a/projects/RabbitMQ.Client/client/framing/BasicRecover.cs b/projects/Test/Unit/TimingFixture.cs
similarity index 67%
rename from projects/RabbitMQ.Client/client/framing/BasicRecover.cs
rename to projects/Test/Unit/TimingFixture.cs
index 471e9b2274..fff7d4b031 100644
--- a/projects/RabbitMQ.Client/client/framing/BasicRecover.cs
+++ b/projects/Test/Unit/TimingFixture.cs
@@ -30,30 +30,17 @@
//---------------------------------------------------------------------------
using System;
-using RabbitMQ.Client.client.framing;
-using RabbitMQ.Client.Impl;
-namespace RabbitMQ.Client.Framing.Impl
+namespace Test.Unit
{
- internal readonly struct BasicRecover : IOutgoingAmqpMethod
+ public class TimingFixture
{
- public readonly bool _requeue;
-
- public BasicRecover(bool Requeue)
- {
- _requeue = Requeue;
- }
-
- public ProtocolCommandId ProtocolCommandId => ProtocolCommandId.BasicRecover;
-
- public int WriteTo(Span span)
- {
- return WireFormatting.WriteBits(ref span.GetStart(), _requeue);
- }
-
- public int GetRequiredBufferSize()
- {
- return 1; // bytes for bit fields
- }
+ public static readonly TimeSpan TimingInterval = TimeSpan.FromMilliseconds(300);
+ public static readonly TimeSpan TimingInterval_2X = TimeSpan.FromMilliseconds(600);
+ public static readonly TimeSpan TimingInterval_4X = TimeSpan.FromMilliseconds(1200);
+ public static readonly TimeSpan TimingInterval_8X = TimeSpan.FromMilliseconds(2400);
+ public static readonly TimeSpan TimingInterval_16X = TimeSpan.FromMilliseconds(4800);
+ public static readonly TimeSpan SafetyMargin = TimeSpan.FromMilliseconds(150);
+ public static readonly TimeSpan TestTimeout = TimeSpan.FromSeconds(5);
}
}
diff --git a/projects/Unit/Unit.csproj b/projects/Test/Unit/Unit.csproj
similarity index 79%
rename from projects/Unit/Unit.csproj
rename to projects/Test/Unit/Unit.csproj
index 77e4d3ed39..75bae9fff7 100644
--- a/projects/Unit/Unit.csproj
+++ b/projects/Test/Unit/Unit.csproj
@@ -9,14 +9,15 @@
- ../rabbit.snk
+ ../../rabbit.snk
true
latest
7.0
+ true
-
+
@@ -26,12 +27,12 @@
all
-
-
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
diff --git a/projects/Unit/WireFormattingFixture.cs b/projects/Test/Unit/WireFormattingFixture.cs
similarity index 98%
rename from projects/Unit/WireFormattingFixture.cs
rename to projects/Test/Unit/WireFormattingFixture.cs
index ce1443f1f0..36986070c8 100644
--- a/projects/Unit/WireFormattingFixture.cs
+++ b/projects/Test/Unit/WireFormattingFixture.cs
@@ -32,7 +32,7 @@
using System;
using Xunit;
-namespace RabbitMQ.Client.Unit
+namespace Test.Unit
{
public class WireFormattingFixture
{
diff --git a/projects/Unit/Fixtures.cs b/projects/Unit/Fixtures.cs
deleted file mode 100644
index 1f5869b7b2..0000000000
--- a/projects/Unit/Fixtures.cs
+++ /dev/null
@@ -1,514 +0,0 @@
-// This source code is dual-licensed under the Apache License, version
-// 2.0, and the Mozilla Public License, version 2.0.
-//
-// The APL v2.0:
-//
-//---------------------------------------------------------------------------
-// Copyright (c) 2007-2020 VMware, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//---------------------------------------------------------------------------
-//
-// The MPL v2.0:
-//
-//---------------------------------------------------------------------------
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-//
-// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
-//---------------------------------------------------------------------------
-
-#pragma warning disable 2002
-
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-using System.Text;
-using System.Threading;
-using RabbitMQ.Client.Framing.Impl;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace RabbitMQ.Client.Unit
-{
- [Collection("IntegrationFixture")]
- public class IntegrationFixture : IDisposable
- {
- internal IConnectionFactory _connFactory;
- internal IConnection _conn;
- internal IChannel _channel;
- internal Encoding _encoding = new UTF8Encoding();
-
- protected readonly TimeSpan _waitSpan;
- protected readonly ITestOutputHelper _output;
- protected readonly string _testDisplayName;
-
- public static TimeSpan RECOVERY_INTERVAL = TimeSpan.FromSeconds(2);
-
- public IntegrationFixture(ITestOutputHelper output)
- {
- _output = output;
- var type = _output.GetType();
- var testMember = type.GetField("test", BindingFlags.Instance | BindingFlags.NonPublic);
- var test = (ITest)testMember.GetValue(output);
- _testDisplayName = test.DisplayName;
-
- SetUp();
-
- if (IsRunningInCI())
- {
- _waitSpan = TimeSpan.FromSeconds(30);
- }
- else
- {
- _waitSpan = TimeSpan.FromSeconds(10);
- }
- }
-
- protected virtual void SetUp()
- {
- _connFactory = new ConnectionFactory();
- _conn = _connFactory.CreateConnection();
- _channel = _conn.CreateChannel();
- }
-
- public virtual void Dispose()
- {
- if (_channel.IsOpen)
- {
- _channel.Close();
- }
-
- if (_conn.IsOpen)
- {
- _conn.Close();
- }
-
- ReleaseResources();
- }
-
- protected virtual void ReleaseResources()
- {
- // no-op
- }
-
- //
- // Connections
- //
-
- internal AutorecoveringConnection CreateAutorecoveringConnection()
- {
- return CreateAutorecoveringConnection(RECOVERY_INTERVAL);
- }
-
- internal AutorecoveringConnection CreateAutorecoveringConnection(ICredentialsProvider credentialsProvider)
- {
- return CreateAutorecoveringConnection(RECOVERY_INTERVAL, credentialsProvider);
- }
-
- internal AutorecoveringConnection CreateAutorecoveringConnection(IList hostnames)
- {
- return CreateAutorecoveringConnection(RECOVERY_INTERVAL, hostnames);
- }
-
- internal AutorecoveringConnection CreateAutorecoveringConnection(TimeSpan interval, ICredentialsProvider credentialsProvider = null)
- {
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = true,
- CredentialsProvider = credentialsProvider,
- NetworkRecoveryInterval = interval
- };
- return (AutorecoveringConnection)cf.CreateConnection($"{_testDisplayName}:{Guid.NewGuid()}");
- }
-
- internal AutorecoveringConnection CreateAutorecoveringConnection(TimeSpan interval, IList hostnames)
- {
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = true,
- // tests that use this helper will likely list unreachable hosts,
- // make sure we time out quickly on those
- RequestedConnectionTimeout = TimeSpan.FromSeconds(1),
- NetworkRecoveryInterval = interval
- };
- return (AutorecoveringConnection)cf.CreateConnection(hostnames, $"{_testDisplayName}:{Guid.NewGuid()}");
- }
-
- internal AutorecoveringConnection CreateAutorecoveringConnection(IList endpoints)
- {
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = true,
- // tests that use this helper will likely list unreachable hosts,
- // make sure we time out quickly on those
- RequestedConnectionTimeout = TimeSpan.FromSeconds(1),
- NetworkRecoveryInterval = RECOVERY_INTERVAL
- };
- return (AutorecoveringConnection)cf.CreateConnection(endpoints, $"{_testDisplayName}:{Guid.NewGuid()}");
- }
-
- internal AutorecoveringConnection CreateAutorecoveringConnectionWithTopologyRecoveryDisabled()
- {
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = true,
- TopologyRecoveryEnabled = false,
- NetworkRecoveryInterval = RECOVERY_INTERVAL
- };
- return (AutorecoveringConnection)cf.CreateConnection($"{_testDisplayName}:{Guid.NewGuid()}");
- }
-
- internal AutorecoveringConnection CreateAutorecoveringConnectionWithTopologyRecoveryFilter(TopologyRecoveryFilter filter)
- {
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = true,
- TopologyRecoveryEnabled = true,
- TopologyRecoveryFilter = filter
- };
-
- return (AutorecoveringConnection)cf.CreateConnection($"{_testDisplayName}:{Guid.NewGuid()}");
- }
-
- internal AutorecoveringConnection CreateAutorecoveringConnectionWithTopologyRecoveryExceptionHandler(TopologyRecoveryExceptionHandler handler)
- {
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = true,
- TopologyRecoveryEnabled = true,
- TopologyRecoveryExceptionHandler = handler
- };
-
- return (AutorecoveringConnection)cf.CreateConnection($"{_testDisplayName}:{Guid.NewGuid()}");
- }
-
- internal IConnection CreateConnectionWithContinuationTimeout(bool automaticRecoveryEnabled, TimeSpan continuationTimeout)
- {
- var cf = new ConnectionFactory
- {
- AutomaticRecoveryEnabled = automaticRecoveryEnabled,
- ContinuationTimeout = continuationTimeout
- };
- return cf.CreateConnection($"{_testDisplayName}:{Guid.NewGuid()}");
- }
-
- //
- // Channels
- //
-
- internal void WithTemporaryChannel(Action action)
- {
- IChannel channel = _conn.CreateChannel();
-
- try
- {
- action(channel);
- }
- finally
- {
- channel.Abort();
- }
- }
-
- internal void WithClosedChannel(Action action)
- {
- IChannel channel = _conn.CreateChannel();
- channel.Close();
-
- action(channel);
- }
-
- internal bool WaitForConfirms(IChannel m)
- {
- using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(4));
- return m.WaitForConfirmsAsync(cts.Token).GetAwaiter().GetResult();
- }
-
- //
- // Exchanges
- //
-
- internal string GenerateExchangeName()
- {
- return $"exchange{Guid.NewGuid()}";
- }
-
- internal byte[] RandomMessageBody()
- {
- return _encoding.GetBytes(Guid.NewGuid().ToString());
- }
-
- internal string DeclareNonDurableExchange(IChannel m, string x)
- {
- m.ExchangeDeclare(x, "fanout", false);
- return x;
- }
-
- internal string DeclareNonDurableExchangeNoWait(IChannel m, string x)
- {
- m.ExchangeDeclareNoWait(x, "fanout", false, false, null);
- return x;
- }
-
- //
- // Queues
- //
-
- internal string GenerateQueueName()
- {
- return $"queue{Guid.NewGuid()}";
- }
-
- internal void WithTemporaryNonExclusiveQueue(Action action)
- {
- WithTemporaryNonExclusiveQueue(_channel, action);
- }
-
- internal void WithTemporaryNonExclusiveQueue(IChannel channel, Action action)
- {
- WithTemporaryNonExclusiveQueue(channel, action, GenerateQueueName());
- }
-
- internal void WithTemporaryNonExclusiveQueue(IChannel channel, Action action, string queue)
- {
- try
- {
- channel.QueueDeclare(queue, false, false, false, null);
- action(channel, queue);
- }
- finally
- {
- WithTemporaryChannel(tm => tm.QueueDelete(queue));
- }
- }
-
- internal void WithTemporaryQueueNoWait(IChannel channel, Action action, string queue)
- {
- try
- {
- channel.QueueDeclareNoWait(queue, false, true, false, null);
- action(channel, queue);
- }
- finally
- {
- WithTemporaryChannel(x => x.QueueDelete(queue));
- }
- }
-
- internal void EnsureNotEmpty(string q, string body)
- {
- WithTemporaryChannel(x => x.BasicPublish("", q, _encoding.GetBytes(body)));
- }
-
- internal void WithNonEmptyQueue(Action action)
- {
- WithNonEmptyQueue(action, "msg");
- }
-
- internal void WithNonEmptyQueue(Action action, string msg)
- {
- WithTemporaryNonExclusiveQueue((m, q) =>
- {
- EnsureNotEmpty(q, msg);
- action(m, q);
- });
- }
-
- internal void WithEmptyQueue(Action action)
- {
- WithTemporaryNonExclusiveQueue((channel, queue) =>
- {
- channel.QueuePurge(queue);
- action(channel, queue);
- });
- }
-
- internal void AssertMessageCount(string q, uint count)
- {
- WithTemporaryChannel((m) =>
- {
- QueueDeclareOk ok = m.QueueDeclarePassive(q);
- Assert.Equal(count, ok.MessageCount);
- });
- }
-
- internal void AssertConsumerCount(string q, int count)
- {
- WithTemporaryChannel((m) =>
- {
- QueueDeclareOk ok = m.QueueDeclarePassive(q);
- Assert.Equal((uint)count, ok.ConsumerCount);
- });
- }
-
- internal void AssertConsumerCount(IChannel m, string q, uint count)
- {
- QueueDeclareOk ok = m.QueueDeclarePassive(q);
- Assert.Equal(count, ok.ConsumerCount);
- }
-
- //
- // Shutdown
- //
-
- internal void AssertShutdownError(ShutdownEventArgs args, int code)
- {
- Assert.Equal(args.ReplyCode, code);
- }
-
- internal void AssertPreconditionFailed(ShutdownEventArgs args)
- {
- AssertShutdownError(args, Constants.PreconditionFailed);
- }
-
- //
- // Concurrency
- //
-
- internal void WaitOn(object o)
- {
- lock (o)
- {
- Monitor.Wait(o, TimingFixture.TestTimeout);
- }
- }
-
- //
- // Flow Control
- //
-
- internal void Block()
- {
- RabbitMQCtl.Block(_conn, _encoding);
- }
-
- internal void Unblock()
- {
- RabbitMQCtl.Unblock();
- }
-
- //
- // Connection Closure
- //
-
- internal void CloseConnection(IConnection conn)
- {
- RabbitMQCtl.CloseConnection(conn);
- }
-
- internal void CloseAllConnections()
- {
- RabbitMQCtl.CloseAllConnections();
- }
-
- internal void RestartRabbitMQ()
- {
- RabbitMQCtl.RestartRabbitMQ();
- }
-
- internal void StopRabbitMQ()
- {
- RabbitMQCtl.StopRabbitMQ();
- }
-
- internal void StartRabbitMQ()
- {
- RabbitMQCtl.StartRabbitMQ();
- RabbitMQCtl.AwaitRabbitMQ();
- }
-
- //
- // Concurrency and Coordination
- //
-
- internal void Wait(ManualResetEventSlim latch)
- {
- Assert.True(latch.Wait(_waitSpan), "waiting on a latch timed out");
- }
-
- internal void Wait(ManualResetEventSlim latch, TimeSpan timeSpan)
- {
- Assert.True(latch.Wait(timeSpan), "waiting on a latch timed out");
- }
-
- private static bool IsRunningInCI()
- {
- if (bool.TryParse(Environment.GetEnvironmentVariable("CI"), out bool ci))
- {
- if (ci == true)
- {
- return true;
- }
- }
-
- string concourse = Environment.GetEnvironmentVariable("CONCOURSE_CI_BUILD");
- string gha = Environment.GetEnvironmentVariable("GITHUB_ACTIONS");
- if (String.IsNullOrWhiteSpace(concourse) && String.IsNullOrWhiteSpace(gha))
- {
- return false;
- }
-
- return true;
- }
- }
-
- public sealed class IgnoreOnVersionsEarlierThan : FactAttribute
- {
- public IgnoreOnVersionsEarlierThan(int major, int minor)
- {
- if (!CheckMiniumVersion(new Version(major, minor)))
- {
- Skip = $"Skipped test. It requires RabbitMQ +{major}.{minor}";
- }
- }
-
- private bool CheckMiniumVersion(Version miniumVersion)
- {
- using (var _conn = new ConnectionFactory().CreateConnection())
- {
- System.Collections.Generic.IDictionary properties = _conn.ServerProperties;
-
- if (properties.TryGetValue("version", out object versionVal))
- {
- string versionStr = Encoding.UTF8.GetString((byte[])versionVal);
-
- int dashIdx = Math.Max(versionStr.IndexOf('-'), versionStr.IndexOf('+'));
- if (dashIdx > 0)
- {
- versionStr = versionStr.Remove(dashIdx);
- }
-
- if (Version.TryParse(versionStr, out Version version))
- {
- return version >= miniumVersion;
- }
- }
-
- return false;
- }
- }
- }
-
- public class TimingFixture
- {
- public static readonly TimeSpan TimingInterval = TimeSpan.FromMilliseconds(300);
- public static readonly TimeSpan TimingInterval_2X = TimeSpan.FromMilliseconds(600);
- public static readonly TimeSpan TimingInterval_4X = TimeSpan.FromMilliseconds(1200);
- public static readonly TimeSpan TimingInterval_8X = TimeSpan.FromMilliseconds(2400);
- public static readonly TimeSpan TimingInterval_16X = TimeSpan.FromMilliseconds(4800);
- public static readonly TimeSpan SafetyMargin = TimeSpan.FromMilliseconds(150);
- public static readonly TimeSpan TestTimeout = TimeSpan.FromSeconds(5);
- }
-}
diff --git a/projects/Unit/TestAsyncConsumer.cs b/projects/Unit/TestAsyncConsumer.cs
deleted file mode 100644
index 5ab094a360..0000000000
--- a/projects/Unit/TestAsyncConsumer.cs
+++ /dev/null
@@ -1,358 +0,0 @@
-// This source code is dual-licensed under the Apache License, version
-// 2.0, and the Mozilla Public License, version 2.0.
-//
-// The APL v2.0:
-//
-//---------------------------------------------------------------------------
-// Copyright (c) 2007-2020 VMware, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//---------------------------------------------------------------------------
-//
-// The MPL v2.0:
-//
-//---------------------------------------------------------------------------
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-//
-// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
-//---------------------------------------------------------------------------
-
-using System;
-using System.Security.Cryptography;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using RabbitMQ.Client.Events;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace RabbitMQ.Client.Unit
-{
- [Collection("IntegrationFixture")]
- public class TestAsyncConsumer
- {
- private readonly ITestOutputHelper _output;
-
- public TestAsyncConsumer(ITestOutputHelper output)
- {
- _output = output;
- }
-
- [Fact]
- public void TestBasicRoundtrip()
- {
- var cf = new ConnectionFactory { DispatchConsumersAsync = true };
- using (IConnection c = cf.CreateConnection())
- {
- using (IChannel m = c.CreateChannel())
- {
- QueueDeclareOk q = m.QueueDeclare();
- byte[] body = System.Text.Encoding.UTF8.GetBytes("async-hi");
- m.BasicPublish("", q.QueueName, body);
- var consumer = new AsyncEventingBasicConsumer(m);
- var are = new AutoResetEvent(false);
- consumer.Received += async (o, a) =>
- {
- are.Set();
- await Task.Yield();
- };
- string tag = m.BasicConsume(q.QueueName, true, consumer);
- // ensure we get a delivery
- bool waitRes = are.WaitOne(2000);
- Assert.True(waitRes);
- // unsubscribe and ensure no further deliveries
- m.BasicCancel(tag);
- m.BasicPublish("", q.QueueName, body);
- bool waitResFalse = are.WaitOne(2000);
- Assert.False(waitResFalse);
- }
- }
- }
-
- [Fact]
- public async Task TestBasicRoundtripConcurrent()
- {
- var cf = new ConnectionFactory { DispatchConsumersAsync = true, ConsumerDispatchConcurrency = 2 };
- using (IConnection c = cf.CreateConnection())
- {
- using (IChannel m = c.CreateChannel())
- {
- QueueDeclareOk q = m.QueueDeclare();
- string publish1 = get_unique_string(1024);
- byte[] body = Encoding.UTF8.GetBytes(publish1);
- m.BasicPublish("", q.QueueName, body);
-
- string publish2 = get_unique_string(1024);
- body = Encoding.UTF8.GetBytes(publish2);
- m.BasicPublish("", q.QueueName, body);
-
- var consumer = new AsyncEventingBasicConsumer(m);
-
- var publish1SyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- var publish2SyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- var maximumWaitTime = TimeSpan.FromSeconds(10);
- var tokenSource = new CancellationTokenSource(maximumWaitTime);
- tokenSource.Token.Register(() =>
- {
- publish1SyncSource.TrySetResult(false);
- publish2SyncSource.TrySetResult(false);
- });
-
- consumer.Received += async (o, a) =>
- {
- string decoded = Encoding.ASCII.GetString(a.Body.ToArray());
- if (decoded == publish1)
- {
- publish1SyncSource.TrySetResult(true);
- await publish2SyncSource.Task;
- }
- else if (decoded == publish2)
- {
- publish2SyncSource.TrySetResult(true);
- await publish1SyncSource.Task;
- }
- };
-
- m.BasicConsume(q.QueueName, true, consumer);
-
- // ensure we get a delivery
- await Task.WhenAll(publish1SyncSource.Task, publish2SyncSource.Task);
-
- bool result1 = await publish1SyncSource.Task;
- Assert.True(result1, $"1 - Non concurrent dispatch lead to deadlock after {maximumWaitTime}");
-
- bool result2 = await publish2SyncSource.Task;
- Assert.True(result2, $"2 - Non concurrent dispatch lead to deadlock after {maximumWaitTime}");
- }
- }
- }
-
- [Fact]
- public async Task TestBasicRoundtripConcurrentManyMessages()
- {
- const int publish_total = 4096;
- string queueName = $"{nameof(TestBasicRoundtripConcurrentManyMessages)}-{Guid.NewGuid()}";
-
- string publish1 = get_unique_string(32768);
- byte[] body1 = Encoding.ASCII.GetBytes(publish1);
- string publish2 = get_unique_string(32768);
- byte[] body2 = Encoding.ASCII.GetBytes(publish2);
-
- var cf = new ConnectionFactory { DispatchConsumersAsync = true, ConsumerDispatchConcurrency = 2 };
-
- using (IConnection c = cf.CreateConnection())
- {
- using (IChannel m = c.CreateChannel())
- {
- QueueDeclareOk q = m.QueueDeclare(queue: queueName, exclusive: false, durable: true);
- Assert.Equal(q.QueueName, queueName);
- }
- }
-
- Task publishTask = Task.Run(async () =>
- {
- using (IConnection c = cf.CreateConnection())
- {
- using (IChannel m = c.CreateChannel())
- {
- QueueDeclareOk q = m.QueueDeclare(queue: queueName, exclusive: false, durable: true);
- for (int i = 0; i < publish_total; i++)
- {
- await m.BasicPublishAsync(string.Empty, queueName, body1);
- await m.BasicPublishAsync(string.Empty, queueName, body2);
- }
- }
- }
- });
-
- Task consumeTask = Task.Run(async () =>
- {
- var publish1SyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- var publish2SyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- var maximumWaitTime = TimeSpan.FromSeconds(30);
- var tokenSource = new CancellationTokenSource(maximumWaitTime);
- tokenSource.Token.Register(() =>
- {
- publish1SyncSource.TrySetResult(false);
- publish2SyncSource.TrySetResult(false);
- });
-
- using (IConnection c = cf.CreateConnection())
- {
- using (IChannel m = c.CreateChannel())
- {
- var consumer = new AsyncEventingBasicConsumer(m);
-
- int publish1_count = 0;
- int publish2_count = 0;
-
- consumer.Received += async (o, a) =>
- {
- string decoded = Encoding.ASCII.GetString(a.Body.ToArray());
- if (decoded == publish1)
- {
- if (Interlocked.Increment(ref publish1_count) >= publish_total)
- {
- publish1SyncSource.TrySetResult(true);
- await publish2SyncSource.Task;
- }
- }
- else if (decoded == publish2)
- {
- if (Interlocked.Increment(ref publish2_count) >= publish_total)
- {
- publish2SyncSource.TrySetResult(true);
- await publish1SyncSource.Task;
- }
- }
- };
-
- m.BasicConsume(queueName, true, consumer);
-
- // ensure we get a delivery
- await Task.WhenAll(publish1SyncSource.Task, publish2SyncSource.Task);
-
- bool result1 = await publish1SyncSource.Task;
- Assert.True(result1, $"Non concurrent dispatch lead to deadlock after {maximumWaitTime}");
-
- bool result2 = await publish2SyncSource.Task;
- Assert.True(result2, $"Non concurrent dispatch lead to deadlock after {maximumWaitTime}");
- }
- }
- });
-
- await Task.WhenAll(publishTask, consumeTask);
- }
-
- [Fact]
- public void TestBasicRoundtripNoWait()
- {
- var cf = new ConnectionFactory { DispatchConsumersAsync = true };
- using (IConnection c = cf.CreateConnection())
- {
- using (IChannel m = c.CreateChannel())
- {
- QueueDeclareOk q = m.QueueDeclare();
- byte[] body = System.Text.Encoding.UTF8.GetBytes("async-hi");
- m.BasicPublish("", q.QueueName, body);
- var consumer = new AsyncEventingBasicConsumer(m);
- var are = new AutoResetEvent(false);
- consumer.Received += async (o, a) =>
- {
- are.Set();
- await Task.Yield();
- };
- string tag = m.BasicConsume(q.QueueName, true, consumer);
- // ensure we get a delivery
- bool waitRes = are.WaitOne(2000);
- Assert.True(waitRes);
- // unsubscribe and ensure no further deliveries
- m.BasicCancelNoWait(tag);
- m.BasicPublish("", q.QueueName, body);
- bool waitResFalse = are.WaitOne(2000);
- Assert.False(waitResFalse);
- }
- }
- }
-
- [Fact]
- public async void ConcurrentEventingTestForReceived()
- {
- const int NumberOfThreads = 4;
- const int NumberOfRegistrations = 5000;
-
- var called = new byte[NumberOfThreads * NumberOfRegistrations];
-
- var cf = new ConnectionFactory { DispatchConsumersAsync = true };
- using (IConnection c = cf.CreateConnection())
- {
- using (IChannel m = c.CreateChannel())
- {
- QueueDeclareOk q = m.QueueDeclare();
- var consumer = new AsyncEventingBasicConsumer(m);
- m.BasicConsume(q.QueueName, true, consumer);
- var countdownEvent = new CountdownEvent(NumberOfThreads);
- var tasks = new Task[NumberOfThreads];
- for (int i = 0; i < NumberOfThreads; i++)
- {
- int threadIndex = i;
- tasks[i] = Task.Run(() =>
- {
- countdownEvent.Signal();
- countdownEvent.Wait();
- int start = threadIndex * NumberOfRegistrations;
- for (int j = start; j < start + NumberOfRegistrations; j++)
- {
- int receivedIndex = j;
- consumer.Received += (sender, eventArgs) =>
- {
- called[receivedIndex] = 1;
- return Task.CompletedTask;
- };
- }
- });
- }
-
- countdownEvent.Wait();
- await Task.WhenAll(tasks);
-
- // Add last receiver
- var are = new AutoResetEvent(false);
- consumer.Received += (o, a) =>
- {
- are.Set();
- return Task.CompletedTask;
- };
-
- // Send message
- m.BasicPublish("", q.QueueName, ReadOnlyMemory.Empty);
- are.WaitOne(TimingFixture.TestTimeout);
- }
- }
-
- // Check received messages
- Assert.Equal(-1, called.AsSpan().IndexOf((byte)0));
- }
-
- [Fact]
- public void NonAsyncConsumerShouldThrowInvalidOperationException()
- {
- var cf = new ConnectionFactory { DispatchConsumersAsync = true };
- using (IConnection c = cf.CreateConnection())
- {
- using (IChannel m = c.CreateChannel())
- {
- QueueDeclareOk q = m.QueueDeclare();
- byte[] body = System.Text.Encoding.UTF8.GetBytes("async-hi");
- m.BasicPublish("", q.QueueName, body);
- var consumer = new EventingBasicConsumer(m);
- Assert.Throws(() => m.BasicConsume(q.QueueName, false, consumer));
- }
- }
- }
-
- private string get_unique_string(int string_length)
- {
- using (var rng = RandomNumberGenerator.Create())
- {
- var bit_count = (string_length * 6);
- var byte_count = ((bit_count + 7) / 8); // rounded up
- var bytes = new byte[byte_count];
- rng.GetBytes(bytes);
- return Convert.ToBase64String(bytes);
- }
- }
- }
-}
diff --git a/projects/Unit/TestBasicPublish.cs b/projects/Unit/TestBasicPublish.cs
deleted file mode 100644
index 518a6e413a..0000000000
--- a/projects/Unit/TestBasicPublish.cs
+++ /dev/null
@@ -1,296 +0,0 @@
-// This source code is dual-licensed under the Apache License, version
-// 2.0, and the Mozilla Public License, version 2.0.
-//
-// The APL v2.0:
-//
-//---------------------------------------------------------------------------
-// Copyright (c) 2007-2020 VMware, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//---------------------------------------------------------------------------
-//
-// The MPL v2.0:
-//
-//---------------------------------------------------------------------------
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-//
-// Copyright (c) 2011-2020 VMware, Inc. or its affiliates. All rights reserved.
-//---------------------------------------------------------------------------
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using RabbitMQ.Client.Events;
-using Xunit;
-using Xunit.Sdk;
-
-namespace RabbitMQ.Client.Unit
-{
- [Collection("IntegrationFixture")]
- public class TestBasicPublish
- {
- [Fact]
- public void TestBasicRoundtripArray()
- {
- var cf = new ConnectionFactory();
- using (IConnection c = cf.CreateConnection())
- using (IChannel m = c.CreateChannel())
- {
- QueueDeclareOk q = m.QueueDeclare();
- var bp = new BasicProperties();
- byte[] sendBody = System.Text.Encoding.UTF8.GetBytes("hi");
- byte[] consumeBody = null;
- var consumer = new EventingBasicConsumer(m);
- var are = new AutoResetEvent(false);
- consumer.Received += async (o, a) =>
- {
- consumeBody = a.Body.ToArray();
- are.Set();
- await Task.Yield();
- };
- string tag = m.BasicConsume(q.QueueName, true, consumer);
-
- m.BasicPublish("", q.QueueName, bp, sendBody);
- bool waitResFalse = are.WaitOne(5000);
- m.BasicCancel(tag);
-
- Assert.True(waitResFalse);
- Assert.Equal(sendBody, consumeBody);
- }
- }
-
- [Fact]
- public void TestBasicRoundtripCachedString()
- {
- var cf = new ConnectionFactory();
- using (IConnection c = cf.CreateConnection())
- using (IChannel m = c.CreateChannel())
- {
- CachedString exchangeName = new CachedString(string.Empty);
- CachedString queueName = new CachedString(m.QueueDeclare().QueueName);
- byte[] sendBody = System.Text.Encoding.UTF8.GetBytes("hi");
- byte[] consumeBody = null;
- var consumer = new EventingBasicConsumer(m);
- var are = new AutoResetEvent(false);
- consumer.Received += async (o, a) =>
- {
- consumeBody = a.Body.ToArray();
- are.Set();
- await Task.Yield();
- };
- string tag = m.BasicConsume(queueName.Value, true, consumer);
-
- m.BasicPublish(exchangeName, queueName, sendBody);
- bool waitResFalse = are.WaitOne(2000);
- m.BasicCancel(tag);
-
- Assert.True(waitResFalse);
- Assert.Equal(sendBody, consumeBody);
- }
- }
-
- [Fact]
- public void TestBasicRoundtripReadOnlyMemory()
- {
- var cf = new ConnectionFactory();
- using (IConnection c = cf.CreateConnection())
- using (IChannel m = c.CreateChannel())
- {
- QueueDeclareOk q = m.QueueDeclare();
- byte[] sendBody = System.Text.Encoding.UTF8.GetBytes("hi");
- byte[] consumeBody = null;
- var consumer = new EventingBasicConsumer(m);
- var are = new AutoResetEvent(false);
- consumer.Received += async (o, a) =>
- {
- consumeBody = a.Body.ToArray();
- are.Set();
- await Task.Yield();
- };
- string tag = m.BasicConsume(q.QueueName, true, consumer);
-
- m.BasicPublish("", q.QueueName, new ReadOnlyMemory(sendBody));
- bool waitResFalse = are.WaitOne(2000);
- m.BasicCancel(tag);
-
- Assert.True(waitResFalse);
- Assert.Equal(sendBody, consumeBody);
- }
- }
-
- [Fact]
- public void CanNotModifyPayloadAfterPublish()
- {
- var cf = new ConnectionFactory();
- using (IConnection c = cf.CreateConnection())
- using (IChannel m = c.CreateChannel())
- {
- QueueDeclareOk q = m.QueueDeclare();
- byte[] sendBody = new byte[1000];
- var consumer = new EventingBasicConsumer(m);
- var receivedMessage = new AutoResetEvent(false);
- bool modified = true;
- consumer.Received += (o, a) =>
- {
- if (a.Body.Span.IndexOf((byte)1) < 0)
- {
- modified = false;
- }
- receivedMessage.Set();
- };
- string tag = m.BasicConsume(q.QueueName, true, consumer);
-
- m.BasicPublish("", q.QueueName, sendBody);
- sendBody.AsSpan().Fill(1);
-
- Assert.True(receivedMessage.WaitOne(5000));
- Assert.False(modified, "Payload was modified after the return of BasicPublish");
-
- m.BasicCancel(tag);
- }
- }
-
- [Fact]
- public void TestMaxMessageSize()
- {
- var re = new ManualResetEventSlim();
- const ushort maxMsgSize = 1024;
-
- int count = 0;
- byte[] msg0 = Encoding.UTF8.GetBytes("hi");
-
- var r = new System.Random();
- byte[] msg1 = new byte[maxMsgSize * 2];
- r.NextBytes(msg1);
-
- var cf = new ConnectionFactory();
- cf.AutomaticRecoveryEnabled = false;
- cf.TopologyRecoveryEnabled = false;
- cf.MaxMessageSize = maxMsgSize;
-
- bool sawConnectionShutdown = false;
- bool sawChannelShutdown = false;
- bool sawConsumerRegistered = false;
- bool sawConsumerCancelled = false;
-
- using (IConnection c = cf.CreateConnection())
- {
- c.ConnectionShutdown += (o, a) =>
- {
- sawConnectionShutdown = true;
- };
-
- Assert.Equal(maxMsgSize, cf.MaxMessageSize);
- Assert.Equal(maxMsgSize, cf.Endpoint.MaxMessageSize);
- Assert.Equal(maxMsgSize, c.Endpoint.MaxMessageSize);
-
- using (IChannel m = c.CreateChannel())
- {
- m.ChannelShutdown += (o, a) =>
- {
- sawChannelShutdown = true;
- };
-
- m.CallbackException += (o, a) =>
- {
- throw new XunitException("Unexpected m.CallbackException");
- };
-
- QueueDeclareOk q = m.QueueDeclare();
-
- var consumer = new EventingBasicConsumer(m);
-
- consumer.Shutdown += (o, a) =>
- {
- re.Set();
- };
-
- consumer.Registered += (o, a) =>
- {
- sawConsumerRegistered = true;
- };
-
- consumer.Unregistered += (o, a) =>
- {
- throw new XunitException("Unexpected consumer.Unregistered");
- };
-
- consumer.ConsumerCancelled += (o, a) =>
- {
- sawConsumerCancelled = true;
- };
-
- consumer.Received += (o, a) =>
- {
- Interlocked.Increment(ref count);
- };
-
- string tag = m.BasicConsume(q.QueueName, true, consumer);
-
- m.BasicPublish("", q.QueueName, msg0);
- m.BasicPublish("", q.QueueName, msg1);
- Assert.True(re.Wait(TimeSpan.FromSeconds(5)));
-
- Assert.Equal(1, count);
- Assert.True(sawConnectionShutdown);
- Assert.True(sawChannelShutdown);
- Assert.True(sawConsumerRegistered);
- Assert.True(sawConsumerCancelled);
- }
- }
- }
-
- [Fact]
- public void TestPropertiesRountrip_Headers()
- {
- // Arrange
- var subject = new BasicProperties
- {
- Headers = new Dictionary()
- };
-
- var cf = new ConnectionFactory();
- using (IConnection c = cf.CreateConnection())
- using (IChannel m = c.CreateChannel())
- {
- QueueDeclareOk q = m.QueueDeclare();
- var bp = new BasicProperties() { Headers = new Dictionary() };
- bp.Headers["Hello"] = "World";
- byte[] sendBody = Encoding.UTF8.GetBytes("hi");
- byte[] consumeBody = null;
- var consumer = new EventingBasicConsumer(m);
- var are = new AutoResetEvent(false);
- string response = null;
- consumer.Received += async (o, a) =>
- {
- response = Encoding.UTF8.GetString(a.BasicProperties.Headers["Hello"] as byte[]);
- consumeBody = a.Body.ToArray();
- are.Set();
- await Task.Yield();
- };
-
- string tag = m.BasicConsume(q.QueueName, true, consumer);
- m.BasicPublish("", q.QueueName, bp, sendBody);
- bool waitResFalse = are.WaitOne(5000);
- m.BasicCancel(tag);
- Assert.True(waitResFalse);
- Assert.Equal(sendBody, consumeBody);
- Assert.Equal("World", response);
- }
- }
- }
-}
diff --git a/projects/Unit/TestConcurrentAccessWithSharedConnection.cs b/projects/Unit/TestConcurrentAccessWithSharedConnection.cs
deleted file mode 100644
index c6750fd7f3..0000000000
--- a/projects/Unit/TestConcurrentAccessWithSharedConnection.cs
+++ /dev/null
@@ -1,223 +0,0 @@
-// This source code is dual-licensed under the Apache License, version
-// 2.0, and the Mozilla Public License, version 2.0.
-//
-// The APL v2.0:
-//
-//---------------------------------------------------------------------------
-// Copyright (c) 2007-2020 VMware, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//---------------------------------------------------------------------------
-//
-// The MPL v2.0:
-//
-//---------------------------------------------------------------------------
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-//
-// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
-//---------------------------------------------------------------------------
-
-using System;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace RabbitMQ.Client.Unit
-{
- public class TestConcurrentAccessWithSharedConnection : IntegrationFixture
- {
- internal const int Threads = 32;
- internal CountdownEvent _latch;
- internal TimeSpan _completionTimeout = TimeSpan.FromSeconds(90);
-
- public TestConcurrentAccessWithSharedConnection(ITestOutputHelper output) : base(output)
- {
- }
-
- protected override void SetUp()
- {
- base.SetUp();
- ThreadPool.SetMinThreads(Threads, Threads);
- _latch = new CountdownEvent(Threads);
- }
-
- public override void Dispose()
- {
- base.Dispose();
- _latch.Dispose();
- }
-
- [Fact]
- public void TestConcurrentChannelOpenAndPublishingWithBlankMessages()
- {
- TestConcurrentChannelOpenAndPublishingWithBody(Array.Empty(), 30);
- }
-
- [Fact]
- public void TestConcurrentChannelOpenAndPublishingSize64()
- {
- TestConcurrentChannelOpenAndPublishingWithBodyOfSize(64);
- }
-
- [Fact]
- public void TestConcurrentChannelOpenAndPublishingSize256()
- {
- TestConcurrentChannelOpenAndPublishingWithBodyOfSize(256);
- }
-
- [Fact]
- public void TestConcurrentChannelOpenAndPublishingSize1024()
- {
- TestConcurrentChannelOpenAndPublishingWithBodyOfSize(1024);
- }
-
- [Fact]
- public Task TestConcurrentChannelOpenAndPublishingWithBlankMessagesAsync()
- {
- return TestConcurrentChannelOpenAndPublishingWithBodyAsync(Array.Empty(), 30);
- }
-
- [Fact]
- public Task TestConcurrentChannelOpenAndPublishingSize64Async()
- {
- return TestConcurrentChannelOpenAndPublishingWithBodyOfSizeAsync(64);
- }
-
- [Fact]
- public Task TestConcurrentChannelOpenAndPublishingSize256Async()
- {
- return TestConcurrentChannelOpenAndPublishingWithBodyOfSizeAsync(256);
- }
-
- [Fact]
- public Task TestConcurrentChannelOpenAndPublishingSize1024Async()
- {
- return TestConcurrentChannelOpenAndPublishingWithBodyOfSizeAsync(1024);
- }
-
- [Fact]
- public void TestConcurrentChannelOpenCloseLoop()
- {
- TestConcurrentChannelOperations((conn) =>
- {
- IChannel ch = conn.CreateChannel();
- ch.Close();
- }, 50);
- }
-
- internal void TestConcurrentChannelOpenAndPublishingWithBodyOfSize(int length, int iterations = 30)
- {
- TestConcurrentChannelOpenAndPublishingWithBody(new byte[length], iterations);
- }
-
- internal Task TestConcurrentChannelOpenAndPublishingWithBodyOfSizeAsync(int length, int iterations = 30)
- {
- return TestConcurrentChannelOpenAndPublishingWithBodyAsync(new byte[length], iterations);
- }
-
- internal void TestConcurrentChannelOpenAndPublishingWithBody(byte[] body, int iterations)
- {
- TestConcurrentChannelOperations((conn) =>
- {
- // publishing on a shared channel is not supported
- // and would missing the point of this test anyway
- IChannel ch = _conn.CreateChannel();
- ch.ConfirmSelect();
- for (int j = 0; j < 200; j++)
- {
- ch.BasicPublish("", "_______", body);
- }
- using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(40));
- ch.WaitForConfirmsAsync(cts.Token).GetAwaiter().GetResult();
- }, iterations);
- }
-
- internal Task TestConcurrentChannelOpenAndPublishingWithBodyAsync(byte[] body, int iterations)
- {
- return TestConcurrentChannelOperationsAsync(async (conn) =>
- {
- // publishing on a shared channel is not supported
- // and would missing the point of this test anyway
- IChannel ch = _conn.CreateChannel();
- ch.ConfirmSelect();
- for (int j = 0; j < 200; j++)
- {
- await ch.BasicPublishAsync("", "_______", body).ConfigureAwait(false);
- }
- using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(40));
- await ch.WaitForConfirmsAsync(cts.Token).ConfigureAwait(false);
- }, iterations);
- }
-
- internal void TestConcurrentChannelOperations(Action actions,
- int iterations)
- {
- TestConcurrentChannelOperations(actions, iterations, _completionTimeout);
- }
-
- internal Task TestConcurrentChannelOperationsAsync(Func actions, int iterations)
- {
- return TestConcurrentChannelOperationsAsync(actions, iterations, _completionTimeout);
- }
-
- internal void TestConcurrentChannelOperations(Action actions,
- int iterations, TimeSpan timeout)
- {
- _ = Enumerable.Range(0, Threads).Select(x =>
- {
- return Task.Run(() =>
- {
- for (int j = 0; j < iterations; j++)
- {
- actions(_conn);
- }
-
- _latch.Signal();
- });
- }).ToArray();
-
- Assert.True(_latch.Wait(timeout));
- // incorrect frame interleaving in these tests will result
- // in an unrecoverable connection-level exception, thus
- // closing the connection
- Assert.True(_conn.IsOpen);
- }
-
- internal async Task TestConcurrentChannelOperationsAsync(Func actions,
- int iterations, TimeSpan timeout)
- {
- await Task.WhenAll(Enumerable.Range(0, Threads).Select(x =>
- {
- return Task.Run(() =>
- {
- for (int j = 0; j < iterations; j++)
- {
- actions(_conn);
- }
-
- _latch.Signal();
- });
- }).ToArray());
-
- Assert.True(_latch.Wait(timeout));
- // incorrect frame interleaving in these tests will result
- // in an unrecoverable connection-level exception, thus
- // closing the connection
- Assert.True(_conn.IsOpen);
- }
- }
-}
diff --git a/projects/Unit/TestConsumer.cs b/projects/Unit/TestConsumer.cs
deleted file mode 100644
index 2d647570a9..0000000000
--- a/projects/Unit/TestConsumer.cs
+++ /dev/null
@@ -1,204 +0,0 @@
-// This source code is dual-licensed under the Apache License, version
-// 2.0, and the Mozilla Public License, version 2.0.
-//
-// The APL v2.0:
-//
-//---------------------------------------------------------------------------
-// Copyright (c) 2007-2020 VMware, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//---------------------------------------------------------------------------
-//
-// The MPL v2.0:
-//
-//---------------------------------------------------------------------------
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-//
-// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
-//---------------------------------------------------------------------------
-
-using System;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using RabbitMQ.Client.Events;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace RabbitMQ.Client.Unit
-{
- [Collection("IntegrationFixture")]
- public class TestConsumer
- {
- private readonly ITestOutputHelper _output;
-
- public TestConsumer(ITestOutputHelper output) => _output = output;
-
- [Fact]
- public async Task TestBasicRoundtripConcurrent()
- {
- var cf = new ConnectionFactory { ConsumerDispatchConcurrency = 2 };
- using (IConnection c = cf.CreateConnection())
- using (IChannel m = c.CreateChannel())
- {
- QueueDeclareOk q = m.QueueDeclare();
- const string publish1 = "sync-hi-1";
- byte[] body = Encoding.UTF8.GetBytes(publish1);
- m.BasicPublish("", q.QueueName, body);
- const string publish2 = "sync-hi-2";
- body = Encoding.UTF8.GetBytes(publish2);
- m.BasicPublish("", q.QueueName, body);
-
- var consumer = new EventingBasicConsumer(m);
-
- var publish1SyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- var publish2SyncSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- var maximumWaitTime = TimeSpan.FromSeconds(10);
- var tokenSource = new CancellationTokenSource(maximumWaitTime);
- tokenSource.Token.Register(() =>
- {
- publish1SyncSource.TrySetResult(false);
- publish2SyncSource.TrySetResult(false);
- });
-
- consumer.Received += (o, a) =>
- {
- switch (Encoding.UTF8.GetString(a.Body.ToArray()))
- {
- case publish1:
- publish1SyncSource.TrySetResult(true);
- break;
- case publish2:
- publish2SyncSource.TrySetResult(true);
- break;
- }
- };
-
- m.BasicConsume(q.QueueName, true, consumer);
-
- await Task.WhenAll(publish1SyncSource.Task, publish2SyncSource.Task);
-
- bool result1 = await publish1SyncSource.Task;
- Assert.True(result1, $"Non concurrent dispatch lead to deadlock after {maximumWaitTime}");
-
- bool result2 = await publish1SyncSource.Task;
- Assert.True(result2, $"Non concurrent dispatch lead to deadlock after {maximumWaitTime}");
- }
- }
-
- [Fact]
- public async Task TestBasicRejectAsync()
- {
- var s = new SemaphoreSlim(0, 1);
- var cf = new ConnectionFactory { DispatchConsumersAsync = true };
- using IConnection connection = cf.CreateConnection();
- using IChannel channel = connection.CreateChannel();
-
- var consumer = new AsyncEventingBasicConsumer(channel);
- consumer.Received += async (object sender, BasicDeliverEventArgs args) =>
- {
- var c = sender as AsyncEventingBasicConsumer;
- Assert.NotNull(c);
- await channel.BasicCancelAsync(c.ConsumerTags[0]);
- await channel.BasicRejectAsync(args.DeliveryTag, true);
- s.Release(1);
- };
-
- QueueDeclareOk q = await channel.QueueDeclareAsync(string.Empty, false, false, true, false, null);
- string queueName = q.QueueName;
- const string publish1 = "sync-hi-1";
- byte[] body = Encoding.UTF8.GetBytes(publish1);
- await channel.BasicPublishAsync(string.Empty, queueName, body);
-
- await channel.BasicConsumeAsync(queue: queueName, autoAck: false,
- consumerTag: string.Empty, noLocal: false, exclusive: false,
- arguments: null, consumer);
-
- await s.WaitAsync();
-
- uint messageCount, consumerCount = 0;
- ushort tries = 5;
- do
- {
- QueueDeclareOk result = await channel.QueueDeclareAsync(queue: queueName, passive: true, false, false, false, null);
- consumerCount = result.ConsumerCount;
- messageCount = result.MessageCount;
- if (consumerCount == 0 && messageCount > 0)
- {
- break;
- }
- else
- {
- await Task.Delay(500);
- }
- } while (tries-- > 0);
-
- if (tries == 0)
- {
- Assert.Fail("[ERROR] failed waiting for MessageCount > 0 && ConsumerCount == 0");
- }
- else
- {
- Assert.Equal((uint)1, messageCount);
- Assert.Equal((uint)0, consumerCount);
- }
- }
-
- [Fact]
- public async Task TestBasicAckAsync()
- {
- int messageCount = 1024;
- var s = new SemaphoreSlim(0, 1);
- var cf = new ConnectionFactory { DispatchConsumersAsync = true };
- using IConnection connection = cf.CreateConnection();
- using IChannel channel = connection.CreateChannel();
-
- var consumer = new AsyncEventingBasicConsumer(channel);
- consumer.Received += async (object sender, BasicDeliverEventArgs args) =>
- {
- var c = sender as AsyncEventingBasicConsumer;
- Assert.NotNull(c);
- await channel.BasicAckAsync(args.DeliveryTag, false);
- --messageCount;
- if (messageCount == 0)
- {
- s.Release(1);
- }
- };
-
- QueueDeclareOk q = await channel.QueueDeclareAsync(string.Empty, false, false, true, false, null);
- string queueName = q.QueueName;
-
- await channel.BasicQosAsync(0, 1, false);
- await channel.BasicConsumeAsync(queue: queueName, autoAck: false,
- consumerTag: string.Empty, noLocal: false, exclusive: false,
- arguments: null, consumer);
-
- var publishTask = Task.Run(async () =>
- {
- for (int i = 0; i < messageCount; i++)
- {
- byte[] body = Encoding.UTF8.GetBytes(Guid.NewGuid().ToString());
- await channel.BasicPublishAsync(string.Empty, queueName, body);
- }
- });
-
- await s.WaitAsync();
- await publishTask;
-
- Assert.Equal(0, messageCount);
- }
- }
-}
diff --git a/projects/Unit/TestFloodPublishing.cs b/projects/Unit/TestFloodPublishing.cs
deleted file mode 100644
index e541e5399c..0000000000
--- a/projects/Unit/TestFloodPublishing.cs
+++ /dev/null
@@ -1,166 +0,0 @@
-// This source code is dual-licensed under the Apache License, version
-// 2.0, and the Mozilla Public License, version 2.0.
-//
-// The APL v2.0:
-//
-//---------------------------------------------------------------------------
-// Copyright (c) 2007-2020 VMware, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//---------------------------------------------------------------------------
-//
-// The MPL v2.0:
-//
-//---------------------------------------------------------------------------
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-//
-// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
-//---------------------------------------------------------------------------
-
-using System;
-using System.Diagnostics;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using RabbitMQ.Client.Events;
-using Xunit;
-using Xunit.Abstractions;
-
-namespace RabbitMQ.Client.Unit
-{
- [Collection("IntegrationFixture")]
- public class TestFloodPublishing
- {
- private readonly ITestOutputHelper _output;
- private readonly byte[] _body = new byte[2048];
- private readonly TimeSpan _tenSeconds = TimeSpan.FromSeconds(10);
-
- public TestFloodPublishing(ITestOutputHelper output)
- {
- _output = output;
- }
-
- [Fact]
- public async Task TestUnthrottledFloodPublishingAsync()
- {
- var connFactory = new ConnectionFactory()
- {
- RequestedHeartbeat = TimeSpan.FromSeconds(60),
- AutomaticRecoveryEnabled = false
- };
-
- var closeWatch = new Stopwatch();
- using (IConnection conn = connFactory.CreateConnection())
- {
- using (IChannel channel = conn.CreateChannel())
- {
- conn.ConnectionShutdown += (_, args) =>
- {
- if (args.Initiator != ShutdownInitiator.Application)
- {
- Assert.Fail("Unexpected connection shutdown!");
- }
- };
-
- var stopwatch = Stopwatch.StartNew();
- int i = 0;
- try
- {
- for (i = 0; i < 65535 * 64; i++)
- {
- if (i % 65536 == 0)
- {
- if (stopwatch.Elapsed > _tenSeconds)
- {
- break;
- }
- }
-
- await channel.BasicPublishAsync(CachedString.Empty, CachedString.Empty, _body);
- }
- }
- finally
- {
- stopwatch.Stop();
- _output.WriteLine($"sent {i}, done in {stopwatch.Elapsed.TotalMilliseconds} ms");
- }
-
- Assert.True(conn.IsOpen);
- closeWatch.Start();
- }
- }
- closeWatch.Stop();
- _output.WriteLine($"Closing took {closeWatch.Elapsed.TotalMilliseconds} ms");
- }
-
- [Fact]
- public async Task TestMultithreadFloodPublishingAsync()
- {
- string message = "Hello from test TestMultithreadFloodPublishing";
- byte[] sendBody = Encoding.UTF8.GetBytes(message);
- int publishCount = 4096;
- int receivedCount = 0;
- AutoResetEvent autoResetEvent = new AutoResetEvent(false);
-
- var cf = new ConnectionFactory()
- {
- RequestedHeartbeat = TimeSpan.FromSeconds(60),
- AutomaticRecoveryEnabled = false
- };
-
- using (IConnection c = cf.CreateConnection())
- {
- string queueName = null;
- using (IChannel m = c.CreateChannel())
- {
- QueueDeclareOk q = m.QueueDeclare();
- queueName = q.QueueName;
- }
-
- Task pub = Task.Run(async () =>
- {
- using (IChannel pubCh = c.CreateChannel())
- {
- for (int i = 0; i < publishCount; i++)
- {
- await pubCh.BasicPublishAsync(string.Empty, queueName, sendBody);
- }
- }
- });
-
- using (IChannel consumeCh = c.CreateChannel())
- {
- var consumer = new EventingBasicConsumer(consumeCh);
- consumer.Received += (o, a) =>
- {
- string receivedMessage = Encoding.UTF8.GetString(a.Body.ToArray());
- Assert.Equal(message, receivedMessage);
- Interlocked.Increment(ref receivedCount);
- if (receivedCount == publishCount)
- {
- autoResetEvent.Set();
- }
- };
- consumeCh.BasicConsume(queueName, true, consumer);
-
- Assert.True(autoResetEvent.WaitOne(_tenSeconds));
- }
-
- await pub;
- Assert.Equal(publishCount, receivedCount);
- }
- }
- }
-}
diff --git a/projects/Unit/TestRecoverAfterCancel.cs b/projects/Unit/TestRecoverAfterCancel.cs
deleted file mode 100644
index 9342a4a7bd..0000000000
--- a/projects/Unit/TestRecoverAfterCancel.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-// This source code is dual-licensed under the Apache License, version
-// 2.0, and the Mozilla Public License, version 2.0.
-//
-// The APL v2.0:
-//
-//---------------------------------------------------------------------------
-// Copyright (c) 2007-2020 VMware, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//---------------------------------------------------------------------------
-//
-// The MPL v2.0:
-//
-//---------------------------------------------------------------------------
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-//
-// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
-//---------------------------------------------------------------------------
-
-using System;
-using System.Collections.Concurrent;
-using System.Text;
-using RabbitMQ.Client.Events;
-using RabbitMQ.Client.Impl;
-using Xunit;
-
-#pragma warning disable 0618
-
-namespace RabbitMQ.Client.Unit
-{
- [Collection("IntegrationFixture")]
- public class TestRecoverAfterCancel : IDisposable
- {
- IConnection _connection;
- IChannel _channel;
- string _queue;
- int _callbackCount;
-
- public TestRecoverAfterCancel()
- {
- _connection = new ConnectionFactory().CreateConnection();
- _channel = _connection.CreateChannel();
- _queue = _channel.QueueDeclare("", false, true, false, null);
- }
-
- public int ChannelNumber(IChannel channel)
- {
- return ((ChannelBase)channel).Session.ChannelNumber;
- }
-
- public void Dispose()
- {
- _connection.Abort();
- }
-
- [Fact]
- public void TestRecoverAfterCancel_()
- {
- UTF8Encoding enc = new UTF8Encoding();
- _channel.BasicPublish("", _queue, enc.GetBytes("message"));
- EventingBasicConsumer Consumer = new EventingBasicConsumer(_channel);
- BlockingCollection<(bool Redelivered, byte[] Body)> EventQueue = new BlockingCollection<(bool Redelivered, byte[] Body)>();
- // Making sure we copy the delivery body since it could be disposed at any time.
- Consumer.Received += (_, e) => EventQueue.Add((e.Redelivered, e.Body.ToArray()));
-
- string CTag = _channel.BasicConsume(_queue, false, Consumer);
- (bool Redelivered, byte[] Body) Event = EventQueue.Take();
- _channel.BasicCancel(CTag);
- _channel.BasicRecover(true);
-
- EventingBasicConsumer Consumer2 = new EventingBasicConsumer(_channel);
- BlockingCollection<(bool Redelivered, byte[] Body)> EventQueue2 = new BlockingCollection<(bool Redelivered, byte[] Body)>();
- // Making sure we copy the delivery body since it could be disposed at any time.
- Consumer2.Received += (_, e) => EventQueue2.Add((e.Redelivered, e.Body.ToArray()));
- _channel.BasicConsume(_queue, false, Consumer2);
- (bool Redelivered, byte[] Body) Event2 = EventQueue2.Take();
-
- Assert.Equal(Event.Body, Event2.Body);
- Assert.False(Event.Redelivered);
- Assert.True(Event2.Redelivered);
- }
-
- [Fact]
- public void TestRecoverCallback()
- {
- _callbackCount = 0;
- _channel.BasicRecoverOk += IncrCallback;
- _channel.BasicRecover(true);
- Assert.Equal(1, _callbackCount);
- }
-
- void IncrCallback(object sender, EventArgs args)
- {
- _callbackCount++;
- }
- }
-}