This is an automated email from the ASF dual-hosted git repository.

nightowl888 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/lucenenet.git

commit 9f3fb72eb8678cd5e41326e4f26b6c83a928bc59
Author: Shad Storhaug <[email protected]>
AuthorDate: Sun Jul 14 02:12:20 2019 +0700

    Created azure-pipelines.yml for Azure DevOps
---
 Directory.Build.props                              |  10 +
 Directory.Build.targets                            |  27 ++
 Lucene.Net.sln                                     |  10 +-
 azure-pipelines.yml                                | 377 +++++++++++++++++++++
 build/Dependencies.props                           |  20 ++
 build/TestReferences.Common.targets                |  20 ++
 build/azure-templates/publish-nuget-packages.yml   |  64 ++++
 .../publish-test-results-for-target-frameworks.yml |  67 ++++
 .../publish-test-results-for-test-projects.yml     | 282 +++++++++++++++
 build/azure-templates/publish-test-results.yml     |  81 +++++
 build/azure-templates/run-tests-on-os.yml          | 211 ++++++++++++
 .../show-all-environment-variables.yml             |  28 ++
 build/azure-templates/show-all-files.yml           |  26 ++
 build/build.ps1                                    | 314 ++++++++++++-----
 .../Analysis/Th/TestThaiAnalyzer.cs                |  42 +++
 15 files changed, 1496 insertions(+), 83 deletions(-)

diff --git a/Directory.Build.props b/Directory.Build.props
index 31f0f94..16f9c18 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -37,6 +37,9 @@
 
   <PropertyGroup Label="NuGet Package Defaults">
     <IsPackable>false</IsPackable>
+    <IncludeSymbols>true</IncludeSymbols>
+    <!-- This is the new symbols format (the only one currently supported at 
NuGet.org) -->
+    <SymbolPackageFormat>snupkg</SymbolPackageFormat>
   </PropertyGroup>
   
   <PropertyGroup Label="Copyright Info">
@@ -48,4 +51,11 @@
     <CopyrightYearRange Condition=" '$(BeginCopyrightYear)' == 
'$(CurrentYear)' ">$(CurrentYear)</CopyrightYearRange>
     <Copyright>Copyright © $(CopyrightYearRange) $(Company)</Copyright>
   </PropertyGroup>
+
+  <!-- Settings to override the above Version of Builds. These can be used to 
+      "freeze" the build number for a release, so whether building within 
+      an IDE or from the commmand line, the version is always what is 
+      in Version.props, if it exists and the PrepareForBuild argument 
+      passed into build.ps1 is 'false'. -->
+  <Import Project="Version.props" Condition="Exists('Version.props')" />
 </Project>
\ No newline at end of file
diff --git a/Directory.Build.targets b/Directory.Build.targets
new file mode 100644
index 0000000..04e1c76
--- /dev/null
+++ b/Directory.Build.targets
@@ -0,0 +1,27 @@
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+<Project>
+  <PropertyGroup>
+    <!-- NuGet.org only supports portable debug symbol format: 
+         
https://docs.microsoft.com/en-us/nuget/create-packages/symbol-packages-snupkg#nugetorg-symbol-package-constraints
 -->
+    <DebugType Condition=" '$(PortableDebugTypeOnly)' == 'true' 
">portable</DebugType>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/Lucene.Net.sln b/Lucene.Net.sln
index 904843e..bde08f5 100644
--- a/Lucene.Net.sln
+++ b/Lucene.Net.sln
@@ -1,4 +1,3 @@
-
 Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 15
 
@@ -30,6 +29,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = 
"azure-templates", "azure-te
                build\azure-templates\run-tests-on-os.yml = 
build\azure-templates\run-tests-on-os.yml
                build\azure-templates\show-all-environment-variables.yml = 
build\azure-templates\show-all-environment-variables.yml
                build\azure-templates\show-all-files.yml = 
build\azure-templates\show-all-files.yml
+               build\azure-templates\publish-test-results.yml = 
build\azure-templates\publish-test-results.yml
+               
build\azure-templates\publish-test-results-for-target-frameworks.yml = 
build\azure-templates\publish-test-results-for-target-frameworks.yml
+               
build\azure-templates\publish-test-results-for-test-projects.yml = 
build\azure-templates\publish-test-results-for-test-projects.yml
+               build\azure-templates\run-tests-on-os.yml = 
build\azure-templates\run-tests-on-os.yml
+               build\azure-templates\show-all-environment-variables.yml = 
build\azure-templates\show-all-environment-variables.yml
+               build\azure-templates\show-all-files.yml = 
build\azure-templates\show-all-files.yml
        EndProjectSection
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dotnet", "dotnet", 
"{8CA61D33-3590-4024-A304-7B1F75B50653}"
@@ -39,6 +44,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dotnet", 
"dotnet", "{8CA61D
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", 
"{4016BDAB-6C33-4D1E-9439-57B416EA45D5}"
        ProjectSection(SolutionItems) = preProject
+               azure-pipelines.yml = azure-pipelines.yml
                build.bat = build.bat
                build\build.ps1 = build\build.ps1
                build\Dependencies.props = build\Dependencies.props
@@ -64,6 +70,7 @@ EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", 
"Solution Items", "{4DF0A2A1-B9C7-4EE5-BAF0-BEEF53E34220}"
        ProjectSection(SolutionItems) = preProject
                Directory.Build.props = Directory.Build.props
+               Directory.Build.targets = Directory.Build.targets
        EndProjectSection
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lucene.Net", 
"src\Lucene.Net\Lucene.Net.csproj", "{3A0AA37E-2B7B-4416-B528-DA4E0E6A6706}"
@@ -401,6 +408,7 @@ Global
                HideSolutionNode = FALSE
        EndGlobalSection
        GlobalSection(NestedProjects) = preSolution
+               {05CE3A39-40D4-452D-AFE0-E57E536A08C6} = 
{4016BDAB-6C33-4D1E-9439-57B416EA45D5}
                {4DF7EACE-2B25-43F6-B558-8520BF20BD76} = 
{8CA61D33-3590-4024-A304-7B1F75B50653}
                {EFB2E31A-5917-49D5-A808-FE5061A550B4} = 
{8CA61D33-3590-4024-A304-7B1F75B50653}
                {119BBACD-D4DB-4E3B-922F-3DA83E0B29E2} = 
{4DF7EACE-2B25-43F6-B558-8520BF20BD76}
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
new file mode 100644
index 0000000..48ddfaf
--- /dev/null
+++ b/azure-pipelines.yml
@@ -0,0 +1,377 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: 'vNext$(rev:.r)' # Format for build number (will be overridden)
+
+# DevOps Setup: Define the following pipeline level variables in Azure DevOps 
build pipeline
+#
+# ArtifactFeedID: (Optional - set to your Azure DevOps Artifact (NuGet) feed. 
If not provided, publish job will be skipped.)
+# BuildConfiguration: (Optional. Defaults to 'Release')
+# BuildPlatform: (Optional. Defaults to 'Any CPU')
+# IsRelease: (Optional. By default the Release job is disabled, setting this 
to 'true' will enable it)
+# RunTests: 'true' (Optional - set to 'false' to disable test jobs - useful 
for debugging. If not provided, tests will be run.)
+
+# Versioning Variables
+
+# BuildCounterSeed: (Optional - Set in conjunction with VersionSuffix, will 
cause the build counter to begin at this value. Note that it is set once, to 
reset is an API call.)
+# PackageVersion: (Optional - This can be used to explicitly set the whole 
version number to a specific version, i.e. 4.8.0-beta00005. It overrides all 
other version settings.)
+# PreReleaseCounterPattern: (Optional. Set to '0000000000' in ci pipeline or 
'00000' in release pipeline. The default is '0000000000'. This setting has no 
effect if VersionSuffix is ''.)
+# VersionSuffix: (Optional. Defaults to 'ci'. Set to 'beta' or 'rc' or '' in 
production pipeline.)
+
+variables:
+- name: BuildCounter
+  value: 
$[counter(variables['VersionSuffix'],coalesce(variables['BuildCounterSeed'], 
1250))]
+- name: BinaryArtifactName
+  value: 'testbinaries'
+- name: NuGetArtifactName
+  value: 'nuget'
+- name: ReleaseArtifactName
+  value: 'release'
+- name: TestResultsArtifactName
+  value: 'testresults'
+- name: VersionArtifactName
+  value: 'version'
+- name: BuildNumberFileName
+  value: 'buildNumber.txt'
+- name: PackageVersionFileName
+  value: 'packageVersion.txt'
+- name: FileVersionFileName
+  value: 'fileVersion.txt'
+- name: BuildDirectory # Where the build scripts and configs are
+  value: '$(System.DefaultWorkingDirectory)/build'
+- name: PublishDirectory # Test binaries directory
+  value: '$(Build.ArtifactStagingDirectory)/$(BinaryArtifactName)'
+- name: NuGetArtifactDirectory # NuGet binaries directory
+  value: '$(Build.ArtifactStagingDirectory)/$(NuGetArtifactName)'
+- name: PublishedArtifactZipFileName
+  value: 'published.zip'
+
+stages:
+- stage: Build_Stage
+  displayName: 'Build Stage:'
+  jobs:
+
+  - job: Build
+    pool:
+      vmImage: 'windows-2019'
+
+    variables:
+      PublishTempDirectory: '$(Build.BinariesDirectory)/publish'
+
+    steps:
+    - powershell: |
+        $configuration = if ($env:BUILDCONFIGURATION) { 
$env:BUILDCONFIGURATION } else { "Release" }
+        Write-Host "##vso[task.setvariable 
variable=BuildConfiguration;]$configuration"
+      displayName: 'Setup Default Variable Values'
+
+    - task: DotNetCoreInstaller@0
+      displayName: 'Use .NET Core sdk 2.2.300'
+      inputs:
+        version: 2.2.300
+
+    - powershell: |
+        Import-Module "$(BuildDirectory)/psake.psm1"
+        $generateBuildBat = if ($Env:ISRELEASE -eq 'true') { 'true' } else { 
'false' }
+        $parameters = @{}
+        $properties = @{
+            backup_files='false';
+            publish_directory='$(PublishTempDirectory)';
+            nuget_package_directory='$(NuGetArtifactDirectory)'
+            # Lock the build.bat so it only builds this version in the release 
distribution
+            generateBuildBat=$generateBuildBat
+        }
+        [string[]]$tasks = 'Pack'
+        if ($Env:RunTests -ne 'false') {
+            [string[]]$tasks = 'Pack','Publish'
+        }
+        Invoke-psake $(BuildDirectory)/build.ps1 -Task $tasks -properties 
$properties -parameters $parameters
+        exit !($psake.build_success)
+      displayName: 'PSake Build, Pack, and Publish'
+
+    - template: 'build/azure-templates/show-all-environment-variables.yml'
+
+    - task: ArchiveFiles@2
+      displayName: 'Zip $(PublishTempDirectory)'
+      inputs:
+        rootFolderOrFile: '$(PublishTempDirectory)'
+        includeRootFolder: false
+        archiveFile: '$(PublishDirectory)/$(PublishedArtifactZipFileName)'
+      condition: and(succeeded(), ne(variables['RunTests'], 'false'))
+
+    - powershell: |
+        $dir = '$(Build.ArtifactStagingDirectory)/$(VersionArtifactName)'
+        if (!(Test-Path $dir)) { New-Item -ItemType Directory -Path "$dir" 
-Force }
+        '$(PackageVersion)' | Out-File -FilePath 
"$dir/$(PackageVersionFileName)" -Force
+        '$(FileVersion)' | Out-File -FilePath "$dir/$(FileVersionFileName)" 
-Force
+        '$(Build.BuildNumber)' | Out-File -FilePath 
"$dir/$(BuildNumberFileName)" -Force
+      displayName: 'Write Versions to Files'
+
+    # If this is a release pipeline, copy the build.bat and Version.props 
files as version artifacts, which will 
+    # overwrite the build.bat and Version.props files of the release.
+    - task: CopyFiles@2
+      displayName: 'Copy build.bat and Version.props Files to: 
/$(VersionArtifactName)'
+      inputs:
+        SourceFolder: '$(System.DefaultWorkingDirectory)'
+        Contents: |
+          build.bat
+          Version.props
+        TargetFolder: 
'$(Build.ArtifactStagingDirectory)/$(VersionArtifactName)'
+      condition: and(succeeded(), eq(variables['IsRelease'], 'true'))
+
+    - task: PublishBuildArtifacts@1
+      displayName: 'Publish Artifact: $(VersionArtifactName)'
+      inputs:
+        PathtoPublish: 
'$(Build.ArtifactStagingDirectory)/$(VersionArtifactName)'
+        ArtifactName: '$(VersionArtifactName)'
+
+    # Copy the .pdb files as build artifacts, which will 
+    # later be used to push to the Azure Artifacts symbol server.
+    - task: CopyFiles@2
+      displayName: 'Copy .pdb Files to: /$(NuGetArtifactName)'
+      inputs:
+        SourceFolder: '$(System.DefaultWorkingDirectory)'
+        Contents: '**/bin/$(BuildConfiguration)/**/*.pdb'
+        TargetFolder: '$(Build.ArtifactStagingDirectory)/$(NuGetArtifactName)'
+
+    - task: PublishBuildArtifacts@1
+      displayName: 'Publish Artifact: $(BinaryArtifactName)'
+      inputs:
+        PathtoPublish: 
'$(Build.ArtifactStagingDirectory)/$(BinaryArtifactName)'
+        ArtifactName: '$(BinaryArtifactName)'
+      condition: and(succeeded(), ne(variables['RunTests'], 'false'))
+
+    - task: PublishBuildArtifacts@1
+      displayName: 'Publish Artifact: $(NuGetArtifactName)'
+      inputs:
+        PathtoPublish: '$(Build.ArtifactStagingDirectory)/$(NuGetArtifactName)'
+        ArtifactName: '$(NuGetArtifactName)'
+
+
+- stage: Test_Stage
+  displayName: 'Test Stage:'
+  jobs:
+  - job: Test_netcoreapp2_1
+    condition: and(succeeded(), ne(variables['RunTests'], 'false'))
+    strategy:
+      matrix:
+        Windows:
+          osName: 'Windows'
+          imageName: 'windows-2019'
+          maximumAllowedFailures: 4 # Maximum allowed failures for a 
successful build
+        Linux:
+          osName: 'Linux'
+          imageName: 'ubuntu-16.04'
+          maximumAllowedFailures: 30 # Maximum allowed failures for a 
successful build
+        macOS:
+          osName: 'macOS'
+          imageName: 'macOS-10.14'
+          maximumAllowedFailures: 30 # Maximum allowed failures for a 
successful build
+    displayName: 'Test netcoreapp2.1 on'
+    pool:
+      vmImage: $(imageName)
+    steps:
+    - template: 'build/azure-templates/run-tests-on-os.yml'
+      parameters:
+        osName: $(osName)
+        testTargetFrameworks: 'netcoreapp2.1'
+        testResultsArtifactName: '$(TestResultsArtifactName)'
+        publishedArtifactZipFileName: '$(PublishedArtifactZipFileName)'
+        maximumParallelJobs: 8
+        maximumAllowedFailures: $(maximumAllowedFailures)
+
+  - job: Test_netcoreapp1_0
+    condition: and(succeeded(), ne(variables['RunTests'], 'false'))
+    strategy:
+      matrix:
+        Windows:
+          osName: 'Windows'
+          imageName: 'windows-2019'
+          maximumAllowedFailures: 4 # Maximum allowed failures for a 
successful build
+        Linux:
+          osName: 'Linux'
+          imageName: 'ubuntu-16.04'
+          maximumAllowedFailures: 30 # Maximum allowed failures for a 
successful build
+        macOS:
+          osName: 'macOS'
+          imageName: 'macOS-10.14'
+          maximumAllowedFailures: 30 # Maximum allowed failures for a 
successful build
+    displayName: 'Test netcoreapp1.0 on'
+    pool:
+      vmImage: $(imageName)
+    steps:
+    - template: 'build/azure-templates/run-tests-on-os.yml'
+      parameters:
+        osName: $(osName)
+        testTargetFrameworks: 'netcoreapp1.0'
+        testResultsArtifactName: '$(TestResultsArtifactName)'
+        publishedArtifactZipFileName: '$(PublishedArtifactZipFileName)'
+        maximumParallelJobs: 8
+        maximumAllowedFailures: $(maximumAllowedFailures)
+
+  - job: Test_net451
+    condition: and(succeeded(), ne(variables['RunTests'], 'false'))
+    displayName: 'Test net451 on Windows'
+    pool:
+      vmImage: 'windows-2019'
+    steps:
+    - template: 'build/azure-templates/run-tests-on-os.yml'
+      parameters:
+        osName: 'Windows'
+        testTargetFrameworks: 'net451'
+        testResultsArtifactName: '$(TestResultsArtifactName)'
+        publishedArtifactZipFileName: '$(PublishedArtifactZipFileName)'
+        maximumParallelJobs: 8
+        maximumAllowedFailures: 4 # Maximum allowed failures for a successful 
build
+
+
+- stage: Publish_Stage
+  displayName: 'Publish Stage:'
+  jobs:
+
+  # Optional job to push to Azure Artifact feed. Just pass in
+  # the GUID of the artifact feed as ArtifactFeedID to enable.
+  - job: Publish_Azure_Artifacts
+    condition: and(succeeded(), ne(variables['ArtifactFeedID'], ''))
+    pool:
+      vmImage: 'windows-2019'
+
+    steps:
+    - template: 'build/azure-templates/show-all-environment-variables.yml'
+
+    - task: DownloadBuildArtifacts@0
+      displayName: 'Download Build Artifacts: $(VersionArtifactName)'
+      inputs:
+        artifactName: '$(VersionArtifactName)'
+        downloadPath: '$(Build.ArtifactStagingDirectory)'
+
+      # NOTE: We are setting Build.BuildNumber here to the NuGet package 
version to work around the limitation that
+      # the version cannot be passed to the Index Sources & Publish Symbols 
task.
+    - powershell: |
+        $version = Get-Content 
'$(Build.ArtifactStagingDirectory)/$(VersionArtifactName)/$(PackageVersionFileName)'
 -Raw
+        Write-Host "##vso[task.setvariable variable=PackageVersion;]$version"
+        Write-Host "##vso[task.setvariable 
variable=Build.BuildNumber;]$version"
+      displayName: 'Read PackageVersion from File to Build.BuildNumber'
+
+    - template: 'build/azure-templates/show-all-environment-variables.yml'
+
+    - template: 'build/azure-templates/publish-nuget-packages.yml'
+      parameters:
+        artifactFeedID: '$(ArtifactFeedID)'
+        nugetArtifactName: '$(NuGetArtifactName)'
+
+
+- stage: Release_Stage
+  displayName: 'Release Stage:'
+  jobs:
+  - job: Release
+    condition: and(succeeded(), eq(variables['IsRelease'], 'true'))
+    displayName: 'Build Release Artifacts for [VOTE]'
+    pool:
+      vmImage: 'windows-2019'
+
+    steps:
+    - template: 'build/azure-templates/show-all-environment-variables.yml'
+
+    - task: DownloadBuildArtifacts@0
+      displayName: 'Download Build Artifacts: $(NuGetArtifactName)'
+      inputs:
+        artifactName: '$(NuGetArtifactName)'
+        downloadPath: '$(Build.ArtifactStagingDirectory)'
+
+    - task: DownloadBuildArtifacts@0
+      displayName: 'Download Build Artifacts: $(VersionArtifactName)'
+      inputs:
+        artifactName: '$(VersionArtifactName)'
+        downloadPath: '$(Build.ArtifactStagingDirectory)'
+
+    - template: 'build/azure-templates/show-all-files.yml' # Uncomment for 
debugging
+
+      # NOTE: We are setting Build.BuildNumber here to the NuGet package 
version to work around the limitation that
+      # the version cannot be passed to the Index Sources & Publish Symbols 
task.
+    - powershell: |
+        $version = Get-Content 
'$(Build.ArtifactStagingDirectory)/$(VersionArtifactName)/$(PackageVersionFileName)'
 -Raw
+        $vcsLabel = 'Lucene.Net_' + $version.Replace('.', '_').Replace('-', 
'_')
+        Write-Host "##vso[task.setvariable variable=VCSLabel;]$vcsLabel"
+        Write-Host "##vso[task.setvariable variable=PackageVersion;]$version"
+        Write-Host "##vso[task.setvariable 
variable=Build.BuildNumber;]$version"
+      displayName: 'Build VCS Label and Rehydrate Version Variables'
+
+    - powershell: |
+        $files = 'build.bat','Version.props'
+        foreach ($file in $files) {
+            Copy-Item -Path 
"$(Build.ArtifactStagingDirectory)/$(VersionArtifactName)/$file" -Destination 
"$(Build.SourcesDirectory)/$file" -Force -ErrorAction Continue
+        }
+      displayName: 'Update build.bat and Version.props to build only version 
$(PackageVersion)'
+
+    - template: 'build/azure-templates/show-all-environment-variables.yml'
+
+    - task: CopyFiles@2
+      displayName: 'Copy Source Code Files to: 
$(Build.ArtifactStagingDirectory)/srctemp'
+      inputs:
+        SourceFolder: '$(Build.SourcesDirectory)'
+        Contents: |
+         **
+         !.git/**/*
+         !branding/**/*
+         !release/**/*
+         !src/**/bin/**/*
+         !src/**/obj/**/*
+        TargetFolder: '$(Build.ArtifactStagingDirectory)/srctemp'
+
+    - task: ArchiveFiles@2
+      displayName: 'Archive Source Code Files'
+      inputs:
+        rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/srctemp'
+        includeRootFolder: false
+        archiveFile: 
'$(Build.ArtifactStagingDirectory)/$(ReleaseArtifactName)/Apache-Lucene.Net-$(PackageVersion).src.zip'
+
+    - task: CopyFiles@2
+      displayName: 'Copy License/Notice Files to: 
$(Build.ArtifactStagingDirectory)/$(NuGetArtifactName)'
+      inputs:
+        SourceFolder: '$(Build.SourcesDirectory)'
+        Contents: |
+         LICENSE.txt
+         NOTICE.txt
+        TargetFolder: '$(Build.ArtifactStagingDirectory)/$(NuGetArtifactName)'
+
+    - task: ArchiveFiles@2
+      displayName: 'Archive Binary Files'
+      inputs:
+        rootFolderOrFile: 
'$(Build.ArtifactStagingDirectory)/$(NuGetArtifactName)'
+        includeRootFolder: false
+        archiveFile: 
'$(Build.ArtifactStagingDirectory)/$(ReleaseArtifactName)/Apache-Lucene.Net-$(PackageVersion).bin.zip'
+
+    - powershell: |
+        $dir = '$(Build.ArtifactStagingDirectory)/$(ReleaseArtifactName)'
+        if (!(Test-Path $dir)) { New-Item -ItemType Directory -Path "$dir" 
-Force }
+        $nl = [Environment]::NewLine
+        "TODO: Review: http://www.apache.org/legal/release-policy.html"; + $nl 
+ `
+        "TODO: Tag Repository" + $nl + `
+        "  commit: $(Build.SourceVersion)" + $nl + `
+        "  tag: $(VCSLabel)" + $nl + `
+        "TODO: Sign release artifacts (see 
https://www.apache.org/dev/release-signing.html)" + $nl + `
+        "TODO: Push release artifacts to dev 
(https://dist.apache.org/repos/dist/dev/lucenenet/)" + $nl + `
+        "TODO: Start release [VOTE] (see 
https://www.apache.org/foundation/voting.html)" + $nl | Out-File -FilePath 
"$dir/RELEASE-TODO.txt" -Force
+      displayName: 'Write RELEASE-TODO.txt'
+
+    - task: PublishBuildArtifacts@1
+      displayName: 'Publish Artifact: $(ReleaseArtifactName)'
+      inputs:
+        PathtoPublish: 
'$(Build.ArtifactStagingDirectory)/$(ReleaseArtifactName)'
+        ArtifactName: '$(ReleaseArtifactName)'
+
+# LUCENENET TODO: Write VCS Label (git tag) automatically
\ No newline at end of file
diff --git a/build/Dependencies.props b/build/Dependencies.props
index 4e20f0b..b46153b 100644
--- a/build/Dependencies.props
+++ b/build/Dependencies.props
@@ -1,3 +1,23 @@
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
 <Project> 
   <PropertyGroup Label="NuGet Package Reference Versions">
     <Antlr3RuntimePackageVersion>3.5.1</Antlr3RuntimePackageVersion>
diff --git a/build/TestReferences.Common.targets 
b/build/TestReferences.Common.targets
index b2986a6..6411a7b 100644
--- a/build/TestReferences.Common.targets
+++ b/build/TestReferences.Common.targets
@@ -1,3 +1,23 @@
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
 <Project>
   <ItemGroup Label="Test Project Common References">
     <PackageReference Include="Microsoft.NET.Test.Sdk" 
Version="$(MicrosoftNETTestSdkPackageVersion)" />
diff --git a/build/azure-templates/publish-nuget-packages.yml 
b/build/azure-templates/publish-nuget-packages.yml
new file mode 100644
index 0000000..7d5ab61
--- /dev/null
+++ b/build/azure-templates/publish-nuget-packages.yml
@@ -0,0 +1,64 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Downloads an artifact with the specified nugetArtifactName,
+# pushes any .nupkg files to the specified artifactFeedID,
+# and pushes any debugging symbols (.pdb files) in the artifact
+# (except for those named *.Tests.pdb) to TeamServices.
+
+parameters:
+  nugetArtifactName: 'nuget' # The name of the artifact where the NuGet assets 
(.nupkg and .pdb files) can be downloaded
+  artifactFeedID: '' # The GUID of the Azure Artifacts NuGet feed
+  testSymbolFilesConvention: '**/*.Tests*.pdb' # The glob pattern (within the 
nugetArtifact) where to look for test project symbols (.pdb) files, so they can 
be distinguished from other project file types.
+
+steps:
+- powershell: |
+    function EnsureNotNullOrEmpty([string]$param, [string]$nameOfParam) {
+        if ([string]::IsNullOrEmpty($param)) {
+            Write-Host "##vso[task.logissue type=error;]Missing template 
parameter \"$nameOfParam\""
+            Write-Host "##vso[task.complete result=Failed;]"
+        }
+    }
+    EnsureNotNullOrEmpty('${{ parameters.nugetArtifactName }}', 
'nugetArtifactName')
+    EnsureNotNullOrEmpty('${{ parameters.artifactFeedID }}', 'artifactFeedID')
+    EnsureNotNullOrEmpty('${{ parameters.testSymbolFilesConvention }}', 
'testSymbolFilesConvention')
+  displayName: 'Validate Template Parameters'
+
+- task: DownloadBuildArtifacts@0
+  displayName: 'Download Build Artifacts: ${{ parameters.nugetArtifactName }}'
+  inputs:
+    artifactName: ${{ parameters.nugetArtifactName }}
+    downloadPath: '$(Build.ArtifactStagingDirectory)'
+
+- task: NuGetCommand@2
+  displayName: 'NuGet push'
+  inputs:
+    command: push
+    packagesToPush: '$(Build.ArtifactStagingDirectory)/${{ 
parameters.nugetArtifactName 
}}/**/*.nupkg;!$(Build.ArtifactStagingDirectory)/${{ 
parameters.nugetArtifactName }}/**/*.symbols.nupkg'
+    publishVstsFeed: '/${{ parameters.artifactFeedID }}'
+    allowPackageConflicts: true
+
+- task: PublishSymbols@2
+  displayName: 'Publish symbols path'
+  inputs:
+    SymbolsFolder: '$(Build.ArtifactStagingDirectory)/${{ 
parameters.nugetArtifactName }}'
+    SearchPattern: |
+      **/bin/**/*.pdb
+      !${{ parameters.testSymbolFilesConvention }}
+    IndexSources: true
+    PublishSymbols: true
+    SymbolServerType: TeamServices
\ No newline at end of file
diff --git 
a/build/azure-templates/publish-test-results-for-target-frameworks.yml 
b/build/azure-templates/publish-test-results-for-target-frameworks.yml
new file mode 100644
index 0000000..cc3af4f
--- /dev/null
+++ b/build/azure-templates/publish-test-results-for-target-frameworks.yml
@@ -0,0 +1,67 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Runs Publish Test Results task for a specific framework
+
+parameters:
+  testProjectName: '' # The name of the test project
+  osName: '' # The name of the operating system for display purposes.
+  testResultsFormat: VSTest # Specify the format of the results files you want 
to publish. The following formats are supported: JUnit, NUnit, VSTest, XUnit, 
CTest
+  testResultsArtifactName: 'testresults' # The name of the Azure DevOps build 
artifact where the test results will be published. Default 'testresults'.
+  testResultsFileName: 'TestResults.trx' # The name of the file (not path) of 
the test results. Default 'TestResults.trx'.
+
+steps:
+- powershell: |
+    function EnsureNotNullOrEmpty([string]$param, [string]$nameOfParam) {
+        if ([string]::IsNullOrEmpty($param)) {
+            Write-Host "##vso[task.logissue type=error;]Missing template 
parameter \"$nameOfParam\""
+            Write-Host "##vso[task.complete result=Failed;]"
+        }
+    }
+    EnsureNotNullOrEmpty('${{ parameters.testProjectName }}', 
'testProjectName')
+    EnsureNotNullOrEmpty('${{ parameters.osName }}', 'osName')
+    EnsureNotNullOrEmpty('${{ parameters.testResultsFormat }}', 
'testResultsFormat')
+    EnsureNotNullOrEmpty('${{ parameters.testResultsArtifactName }}', 
'testResultsArtifactName')
+    EnsureNotNullOrEmpty('${{ parameters.testResultsFileName }}', 
'testResultsFileName')
+  displayName: 'Validate Template Parameters'
+
+- template: publish-test-results.yml
+  parameters:
+    framework: 'netcoreapp2.1'
+    testProjectName: '${{ parameters.testProjectName }}'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results.yml
+  parameters:
+    framework: 'netcoreapp1.0'
+    testProjectName: '${{ parameters.testProjectName }}'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results.yml
+  parameters:
+    framework: 'net451'
+    testProjectName: '${{ parameters.testProjectName }}'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
\ No newline at end of file
diff --git a/build/azure-templates/publish-test-results-for-test-projects.yml 
b/build/azure-templates/publish-test-results-for-test-projects.yml
new file mode 100644
index 0000000..62db26f
--- /dev/null
+++ b/build/azure-templates/publish-test-results-for-test-projects.yml
@@ -0,0 +1,282 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,-with-title
+# 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.
+
+# Runs Publish Test Results task for all Lucene.Net test projects
+
+# The tasks can be easily re-generated (except for any special cases) using 
the following powershell script
+
+#$source_directory = "F:/Projects/lucenenet" # Change to your repo location
+#
+#$testProjects = Get-ChildItem -Path "$source_directory/**/*.csproj" -Recurse 
| ? { $_.Directory.Name.Contains(".Tests") } | Sort-Object -Property FullName
+#
+#[string]$output = ''
+#
+#foreach ($testProject in $testProjects) {
+#    $projectName = [System.IO.Path]::GetFileNameWithoutExtension($testProject)
+#
+#    $block = `
+#        "- template: publish-test-results-for-target-frameworks.yml`n" + `
+#        "  parameters:`n" + `
+#        "    testProjectName: '$projectName'`n" + `
+#        "    osName: '$`{`{ parameters.osName `}`}'`n" + `
+#        "    testResultsFormat: '$`{`{ parameters.testResultsFormat `}`}'`n" 
+ `
+#        "    testResultsArtifactName: '$`{`{ 
parameters.testResultsArtifactName `}`}'`n" + `
+#        "    testResultsFileName: '$`{`{ parameters.testResultsFileName 
`}`}'`n" + `
+#        "`n"
+#
+#    $output = "$output$block"
+#}
+#
+#Set-Clipboard -Value $output
+
+parameters:
+  osName: '' # The name of the operating system for display purposes.
+  testResultsFormat: VSTest # Specify the format of the results files you want 
to publish. The following formats are supported: JUnit, NUnit, VSTest, XUnit, 
CTest
+  testResultsArtifactName: 'testresults' # The name of the Azure DevOps build 
artifact where the test results will be published. Default 'testresults'.
+  testResultsFileName: 'TestResults.trx' # The name of the file (not path) of 
the test results. Default 'TestResults.trx'.
+
+steps:
+- powershell: |
+    function EnsureNotNullOrEmpty([string]$param, [string]$nameOfParam) {
+        if ([string]::IsNullOrEmpty($param)) {
+            Write-Host "##vso[task.logissue type=error;]Missing template 
parameter \"$nameOfParam\""
+            Write-Host "##vso[task.complete result=Failed;]"
+        }
+    }
+    EnsureNotNullOrEmpty('${{ parameters.osName }}', 'osName')
+    EnsureNotNullOrEmpty('${{ parameters.testResultsFormat }}', 
'testResultsFormat')
+    EnsureNotNullOrEmpty('${{ parameters.testResultsArtifactName }}', 
'testResultsArtifactName')
+    EnsureNotNullOrEmpty('${{ parameters.testResultsFileName }}', 
'testResultsFileName')
+  displayName: 'Validate Template Parameters'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.ICU'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+# Special case: Only supports .netcoreapp2.1
+- template: publish-test-results.yml
+  parameters:
+    framework: 'netcoreapp2.1'
+    testProjectName: 'Lucene.Net.Tests.Cli'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests._A-I'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests._J-U'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests._U-Z'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Analysis.Common'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Analysis.Kuromoji'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Analysis.Phonetic'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Analysis.SmartCn'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Analysis.Stempel'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Benchmark'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Classification'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Codecs'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Demo'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Expressions'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Facet'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Grouping'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Highlighter'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Join'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Memory'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Misc'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Queries'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.QueryParser'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Replicator'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Sandbox'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Spatial'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
+
+- template: publish-test-results-for-target-frameworks.yml
+  parameters:
+    testProjectName: 'Lucene.Net.Tests.Suggest'
+    osName: '${{ parameters.osName }}'
+    testResultsFormat: '${{ parameters.testResultsFormat }}'
+    testResultsArtifactName: '${{ parameters.testResultsArtifactName }}'
+    testResultsFileName: '${{ parameters.testResultsFileName }}'
\ No newline at end of file
diff --git a/build/azure-templates/publish-test-results.yml 
b/build/azure-templates/publish-test-results.yml
new file mode 100644
index 0000000..b7c9d0a
--- /dev/null
+++ b/build/azure-templates/publish-test-results.yml
@@ -0,0 +1,81 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Runs Publish Test Results task for a specific framework
+
+parameters:
+  testProjectName: '' # The name of the test project
+  framework: '' # The target framework for display purposes.
+  osName: '' # The name of the operating system for display purposes.
+  testResultsFormat: VSTest # Specify the format of the results files you want 
to publish. The following formats are supported: JUnit, NUnit, VSTest, XUnit, 
CTest
+  testResultsArtifactName: 'testresults' # The name of the Azure DevOps build 
artifact where the test results will be published. Default 'testresults'.
+  testResultsFileName: 'TestResults.trx' # The name of the file (not path) of 
the test results. Default 'TestResults.trx'.
+
+steps:
+- powershell: |
+    function EnsureNotNullOrEmpty([string]$param, [string]$nameOfParam) {
+        if ([string]::IsNullOrEmpty($param)) {
+            Write-Host "##vso[task.logissue type=error;]Missing template 
parameter \"$nameOfParam\""
+            Write-Host "##vso[task.complete result=Failed;]"
+        }
+    }
+    EnsureNotNullOrEmpty('${{ parameters.testProjectName }}', 
'testProjectName')
+    EnsureNotNullOrEmpty('${{ parameters.framework }}', 'framework')
+    EnsureNotNullOrEmpty('${{ parameters.osName }}', 'osName')
+    EnsureNotNullOrEmpty('${{ parameters.testResultsFormat }}', 
'testResultsFormat')
+    EnsureNotNullOrEmpty('${{ parameters.testResultsArtifactName }}', 
'testResultsArtifactName')
+    EnsureNotNullOrEmpty('${{ parameters.testResultsFileName }}', 
'testResultsFileName')
+  displayName: 'Validate Template Parameters'
+
+#- template: 'show-all-files.yml' # Uncomment for debugging
+
+- powershell: |
+    $testResultsFileName = "$(Build.ArtifactStagingDirectory)/${{ 
parameters.testResultsArtifactName }}/${{ parameters.osName }}/${{ 
parameters.framework }}/${{ parameters.testProjectName }}/${{ 
parameters.testResultsFileName }}"
+    $testResultsFileExists = Test-Path $testResultsFileName
+    if ($testResultsFileExists) {
+
+        $reader = [System.Xml.XmlReader]::Create($testResultsFileName)
+        try {
+            while ($reader.Read()) {
+                if ($reader.NodeType -eq [System.Xml.XmlNodeType]::Element 
-and $reader.Name -eq 'Counters') {
+                    $failed = $reader.GetAttribute('failed')
+                    $passed = $reader.GetAttribute('passed')
+                    $ignored = (([int]$reader.GetAttribute('total')) - 
([int]$reader.GetAttribute('executed'))).ToString()
+                    $testResults = "Tests failed: $failed, passed: $passed, 
ignored: $ignored"
+                    Write-Host "##vso[task.setvariable 
variable=TestResults;]$testResults"
+                    # Report a running total of failures
+                    $totalFailures = ([int]$Env:TOTALFAILURES + 
[int]$failed).ToString()
+                    Write-Host "##vso[task.setvariable 
variable=TotalFailures;]$totalFailures"
+                    break;
+                }
+            }
+        } finally {
+            $reader.Dispose()
+        }
+    } else {
+        Write-Host "WARNING: File not found: $testResultsFileName"
+    }
+    Write-Host "##vso[task.setvariable 
variable=TestResultsFileExists;]$testResultsFileExists"      
+  displayName: 'Parse Test Results File'
+
+- task: PublishTestResults@2
+  displayName: 'Publish Test Results ${{ parameters.testProjectName }},${{ 
parameters.framework }}'
+  inputs:
+    testResultsFormat: ${{ parameters.testResultsFormat }}
+    testResultsFiles: '$(Build.ArtifactStagingDirectory)/${{ 
parameters.testResultsArtifactName }}/${{ parameters.osName }}/${{ 
parameters.framework }}/${{ parameters.testProjectName }}/${{ 
parameters.testResultsFileName }}'
+    testRunTitle: '${{ parameters.testProjectName }} - ${{ 
parameters.framework }} - ${{ parameters.osName }} | $(TestResults)'
+  condition: and(succeeded(), eq(variables['TestResultsFileExists'], 'true'))
\ No newline at end of file
diff --git a/build/azure-templates/run-tests-on-os.yml 
b/build/azure-templates/run-tests-on-os.yml
new file mode 100644
index 0000000..e9e605e
--- /dev/null
+++ b/build/azure-templates/run-tests-on-os.yml
@@ -0,0 +1,211 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Downloads test binaries and executes tests using dotnet vstest,
+# runs the tests for each project on a background job in parallel,
+# then uploads the results to Azure DevOps pipelines
+
+parameters:
+  osName: 'Windows' # The name of the operating system for display purposes.
+  testTargetFrameworks: '' # A semicolon separated list of target frameworks 
indicating which frameworks tests will be run on. See: 
https://docs.microsoft.com/en-us/dotnet/standard/frameworks.
+  binaryArtifactName: 'testbinaries' # The name of the Azure DevOps build 
artifact where the test assemblies will be downloaded from. Default 
'testbinaries'.
+  testResultsArtifactName: 'testresults' # The name of the Azure DevOps build 
artifact where the test results will be published. Default 'testresults'.
+  vsTestPlatform: 'x64' # Target platform architecture used for test 
execution. Valid values are x86, x64, and ARM.
+  testBinaryFilesPattern: '\.*\.Tests\.?[^\\/]*?\.?[^\\/]*?.dll$' # The regex 
pattern (within $(System.DefaultWorkingDirectory)/**/<TargetFramework>/) where 
to look for test .dll files, so they can be distinguished from other .dll file 
types.
+  testResultsFileName: 'TestResults.trx' # The name of the file (not path) of 
the test results. Default 'TestResults.trx'.
+  publishedArtifactZipFileName: 'published.zip' # The name of the zip file 
(within the Azure DevOps build artifact binaryArtifactName) where the published 
test binaries can be extracted from. Default 'published.zip'.
+  maximumParallelJobs: 8
+  maximumAllowedFailures: 0
+
+steps:
+- powershell: |
+    function EnsureNotNullOrEmpty([string]$param, [string]$nameOfParam) {
+        if ([string]::IsNullOrEmpty($param)) {
+            Write-Host "##vso[task.logissue type=error;]Missing template 
parameter \"$nameOfParam\""
+            Write-Host "##vso[task.complete result=Failed;]"
+        }
+    }
+    EnsureNotNullOrEmpty('${{ parameters.osName }}', 'osName')
+    EnsureNotNullOrEmpty('${{ parameters.testTargetFrameworks }}', 
'testTargetFrameworks')
+    EnsureNotNullOrEmpty('${{ parameters.binaryArtifactName }}', 
'binaryArtifactName')
+    EnsureNotNullOrEmpty('${{ parameters.testResultsArtifactName }}', 
'testResultsArtifactName')
+    EnsureNotNullOrEmpty('${{ parameters.vsTestPlatform }}', 'vsTestPlatform')
+    EnsureNotNullOrEmpty('${{ parameters.testBinaryFilesPattern }}', 
'testBinaryFilesPattern')
+    EnsureNotNullOrEmpty('${{ parameters.testResultsFileName }}', 
'testResultsFileName')
+    EnsureNotNullOrEmpty('${{ parameters.publishedArtifactZipFileName }}', 
'publishedArtifactZipFileName')
+    EnsureNotNullOrEmpty('${{ parameters.maximumParallelJobs }}', 
'maximumParallelJobs')
+    EnsureNotNullOrEmpty('${{ parameters.maximumAllowedFailures }}', 
'maximumAllowedFailures')
+  displayName: 'Validate Template Parameters'
+
+- task: DownloadBuildArtifacts@0
+  displayName: 'Download Build Artifacts: ${{ parameters.binaryArtifactName }}'
+  inputs:
+    artifactName: ${{ parameters.binaryArtifactName }}
+    downloadPath: '$(System.DefaultWorkingDirectory)'
+
+#- template: 'show-all-files.yml' # Uncomment for debugging
+
+- task: ExtractFiles@1
+  displayName: 'Extract files from ${{ parameters.publishedArtifactZipFileName 
}}'
+  inputs:
+    archiveFilePatterns: '${{ parameters.binaryArtifactName }}/${{ 
parameters.publishedArtifactZipFileName }}'
+    destinationFolder: '$(System.DefaultWorkingDirectory)/${{ 
parameters.binaryArtifactName }}'
+    cleanDestinationFolder: false
+
+- powershell: |
+    $testTargetFrameworksString = '${{ parameters.testTargetFrameworks }}'
+    $testBinaryRootDirectory = "$(System.DefaultWorkingDirectory)"
+    $testResultsArtifactDirectory = "${{ 
format('$(Build.ArtifactStagingDirectory)/{0}',parameters.testResultsArtifactName)
 }}"
+    $testPlatform = '${{ parameters.vsTestPlatform }}'
+    $testOSName = '${{ parameters.osName }}'
+    $testBinaryFilesPattern = '${{ parameters.testBinaryFilesPattern }}'
+    $testResultsFileName = '${{ parameters.testResultsFileName }}'
+    $maximumParalellJobs = '${{ parameters.maximumParallelJobs }}'
+    $testTargetFrameworks = 
$testTargetFrameworksString.Split([char]',',[char]';')
+    
+    function SeparateVersionDigits([string]$digits) {
+        return (&{ for ($i = 0;$i -lt $digits.Length;$i++) { 
$digits.Substring($i,1) }}) -join '.'
+    }
+    
+    # Convert $framework (i.e. net461) into format for dotnet vstest (i.e. 
.NETFramework,Version=4.6.1)
+    function ConvertFrameworkName([string]$framework) {
+        $match = [regex]::Match($framework, '^net(\d+)$') # .NET Framework
+        if ($match.Success) {
+            $ver = SeparateVersionDigits($match.Groups[1].Value)
+            return ".NETFramework,Version=v$($ver)"
+        }
+        $match = [regex]::Match($framework, 
'^netcoreapp(\d+\.\d+(?:\.\d+)?)$') # .NET Core
+        if ($match.Success) {
+            $ver = $match.Groups[1].Value
+            return ".NETCoreApp,Version=v$($ver)"
+        } 
+        $match = [regex]::Match($framework, '^uap(\d+\.\d+)?$') # Universal 
Windows Platform
+        if ($match.Success) {
+            $ver = $match.Groups[1].Value
+            $ver = if ([string]::IsNullOrEmpty($ver)) { '10' } else { 
$ver.Replace('.0','').Replace('.','') }
+            return "FrameworkUap$($ver)"
+        }
+        return $framework
+    }
+    
+    function IsSupportedFramework([string]$framework) {
+        if ($IsWindows -eq $null) {
+            $IsWindows = $env:OS.StartsWith('Win')
+        }
+        if (!$IsWindows -and !$framework.StartsWith('netcoreapp')) {
+            return $false
+        }
+        return $true
+    }
+    
+    function RunTests([string]$framework, [string]$fileRegexPattern) {
+        if (!(IsSupportedFramework($framework))) { continue }
+    
+        $testBinaries = Get-ChildItem -Path "$testBinaryRootDirectory" -File 
-Recurse | Where-Object {$_.FullName -match "$framework"} | Where-Object 
{$_.FullName -match "$fileRegexPattern"} | Sort-Object -Property FullName
+        Write-Host $testBinaries
+        foreach ($testBinary in $testBinaries) {
+            $testName = 
[System.IO.Path]::GetFileNameWithoutExtension($testBinary.FullName)
+            $testDirectory = $testBinary.Directory.Name
+    
+            # Safety check - only run tests for the DLL that matches the 
directory name so we don't run the same one twice
+            if (!($testName -eq $testDirectory)) { continue }
+    
+            if ($maximumParalellJobs -gt 1) {
+                # Pause if we have queued too many parallel jobs
+                $running = @(Get-Job | Where-Object { $_.State -eq 'Running' })
+                if ($running.Count -ge $maximumParalellJobs) {
+                    Write-Host ""
+                    Write-Host "  Running tests in parallel on 
$($running.Count) projects." -ForegroundColor Cyan
+                    Write-Host "  Next in queue is $testName on $framework. 
This will take a bit, please wait..." -ForegroundColor Cyan
+                    $running | Wait-Job -Any | Out-Null
+                }
+            }
+    
+            $fwork = ConvertFrameworkName($framework)
+            $testResultDirectory = 
"$testResultsArtifactDirectory/$testOSName/$framework/$testName"                
+            if (!(Test-Path "$testResultDirectory")) {
+                New-Item "$testResultDirectory" -ItemType Directory -Force
+            }
+    
+            $testExpression = "dotnet vstest ""$($testBinary.FullName)"" 
--Framework:""$fwork"" --Platform:""$testPlatform""" + `
+                " --logger:""console;verbosity=normal"" 
--logger:""trx;LogFileName=$testResultsFileName""" + `
+                " --ResultsDirectory:""$testResultDirectory"" --Blame"
+    
+            Write-Host "Testing '$($testBinary.FullName)' on framework 
'$fwork' and outputting test results to 
'$testResultDirectory/$testResultsFileName'..."
+            Write-Host $testExpression -ForegroundColor Magenta
+            if ($maximumParalellJobs -le 1) {
+                Invoke-Expression $testExpression # For running in the 
foreground
+            } else {
+    
+                $testExpression += " > 
""$testResultDirectory/dotnet-vstest.log"" 2> 
""$testResultDirectory/dotnet-vstest-error.log"""
+                $scriptBlock = {
+                    param([string]$testExpression)
+                    Invoke-Expression $testExpression
+                }
+    
+                # Execute the jobs in parallel
+                Start-Job $scriptBlock -ArgumentList $testExpression
+            }
+        }
+    }
+    
+    foreach ($framework in $testTargetFrameworks) {
+        RunTests -Framework "$framework" -FileRegexPattern 
"$testBinaryFilesPattern"
+    }
+    
+    if ($maximumParalellJobs -gt 1) {
+        # Wait for it all to complete
+        do {
+            $running = @(Get-Job | Where-Object { $_.State -eq 'Running' })
+            if ($running.Count -gt 0) {
+                Write-Host ""
+                Write-Host "  Almost finished, only $($running.Count) projects 
left..." -ForegroundColor Cyan
+                $running | Wait-Job -Any
+            }
+        } until ($running.Count -eq 0)
+    }
+    
+    $global:LASTEXITCODE = 0 # Force the script to continue on error
+  displayName: 'dotnet vstest ${{ parameters.testTargetFrameworks }}'
+  ignoreLASTEXITCODE: true
+
+#- template: 'show-all-files.yml' # Uncomment for debugging
+
+- task: PublishBuildArtifacts@1
+  displayName: 'Publish Artifact: ${{ parameters.testResultsArtifactName }}'
+  inputs:
+    PathtoPublish: '$(Build.ArtifactStagingDirectory)/${{ 
parameters.testResultsArtifactName }}'
+    ArtifactName: '${{ parameters.testResultsArtifactName }}'
+  condition: succeededOrFailed()
+
+# Due to the fact that it is not possible to loop a task and
+# it would be a ton of work to make a replacement for the
+# Publish Test Results task or the (deprecated) TfsPublisher
+# our only other option is to make a task for every supported
+# platform and project and update it whenever a new platform 
+# is targeted or test project is created in Lucene.Net.
+
+- template: 'publish-test-results-for-test-projects.yml'
+  parameters:
+    osName: '${{ parameters.osName }}'
+
+- pwsh: |
+    $maximumAllowedFailures = '${{ parameters.maximumAllowedFailures }}'
+    if ([int]$Env:TOTALFAILURES -gt [int]$maximumAllowedFailures) {
+        Write-Host "##vso[task.logissue type=error;]Test run failed due to too 
many failed tests. Maximum failures allowed: $maximumAllowedFailures, total 
failures: $($Env:TOTALFAILURES)."
+        Write-Host "##vso[task.complete result=Failed;]"
+    }
\ No newline at end of file
diff --git a/build/azure-templates/show-all-environment-variables.yml 
b/build/azure-templates/show-all-environment-variables.yml
new file mode 100644
index 0000000..1815e62
--- /dev/null
+++ b/build/azure-templates/show-all-environment-variables.yml
@@ -0,0 +1,28 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Writes all environment variables to the host (helpful for debugging)
+
+steps:
+- powershell: |
+    $environmentVars = Get-ChildItem -path env:* | sort Name
+    foreach($var in $environmentVars) {
+        $keyname = $var.Key
+        $keyvalue = $var.Value
+        Write-Output "${keyname}: $keyvalue"
+    }
+  displayName: 'Show all Environment Variables'
\ No newline at end of file
diff --git a/build/azure-templates/show-all-files.yml 
b/build/azure-templates/show-all-files.yml
new file mode 100644
index 0000000..9591775
--- /dev/null
+++ b/build/azure-templates/show-all-files.yml
@@ -0,0 +1,26 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Downloads test binaries and executes tests using dotnet vstest,
+# runs the tests for each project on a background job in parallel,
+# then uploads the results to Azure DevOps pipelines
+
+# Writes all file names (from the parent directory) to the host (helpful for 
debugging)
+
+steps:
+- powershell: cd ..;dir -r  | Where-Object {$_.PsIsContainer -eq $false} | % { 
$_.FullName }
+  displayName: 'Show all Files'
\ No newline at end of file
diff --git a/build/build.ps1 b/build/build.ps1
index 6ba4106..a29d765 100644
--- a/build/build.ps1
+++ b/build/build.ps1
@@ -29,22 +29,23 @@ properties {
        [string]$sdkPath = "$env:programfiles/dotnet/sdk"
        [string]$sdkVersion = "2.2.300"
        [string]$globalJsonFile = "$base_directory/global.json"
+       [string]$versionPropsFile = "$base_directory/Version.props"
+       [string]$build_bat = "$base_directory/build.bat"
 
        [string]$buildCounter     = $(if ($buildCounter) { $buildCounter } else 
{ $env:BuildCounter }) #NOTE: Pass in as a parameter (not a property) or 
environment variable to override
-       [string]$preReleaseCounterPattern = $(if ($preReleaseCounterPattern) { 
$preReleaseCounterPattern } else { if ($env:PreReleaseCounterPattern) { 
$env:PreReleaseCounterPattern } else { "00000" } })  #NOTE: Pass in as a 
parameter (not a property) or environment variable to override
-       [string]$versionSuffix    = $(if ($versionSuffix) { $versionSuffix } 
else { $env:VersionSuffix })  #NOTE: Pass in as a parameter (not a property) or 
environment variable to override
+       [string]$preReleaseCounterPattern = $(if ($preReleaseCounterPattern) { 
$preReleaseCounterPattern } else { if ($env:PreReleaseCounterPattern) { 
$env:PreReleaseCounterPattern } else { "0000000000" } })  #NOTE: Pass in as a 
parameter (not a property) or environment variable to override
+       [string]$versionSuffix    = $(if ($versionSuffix -ne $null) { 
$versionSuffix } else { if ($env:VersionSuffix -ne $null) { $env:VersionSuffix 
} else { 'ci' }}) #NOTE: Pass in as a parameter (not a property) or environment 
variable to override
        [string]$packageVersion   = Get-Package-Version #NOTE: Pass in as a 
parameter (not a property) or environment variable to override
        [string]$version          = Get-Version
-       [string]$configuration    = "Release"
+       [string]$configuration    = $(if ($configuration) { $configuration } 
else { if ($env:BuildConfiguration) { $env:BuildConfiguration } else { 
"Release" } })  #NOTE: Pass in as a parameter (not a property) or environment 
variable to override
+       [string]$platform   = $(if ($platform) { $platform } else { if 
($env:BuildPlatform) { $env:BuildPlatform } else { "Any CPU" } })  #NOTE: Pass 
in as a parameter (not a property) or environment variable to override
        [bool]$backup_files       = $true
        [bool]$prepareForBuild    = $true
        [bool]$generateBuildBat   = $false
+       [bool]$zipPublishedArtifacts = $false
+       [string]$publishedArtifactZipFileName = "artifact.zip"
 
-       [string]$build_bat = "$base_directory/build.bat"
-       [string]$copyright_year = [DateTime]::Today.Year.ToString() #Get the 
current year from the system
-       [string]$copyright = "Copyright " + $([char]0x00A9) + " 2006 - 
$copyright_year The Apache Software Foundation"
-       [string]$company_name = "The Apache Software Foundation"
-       [string]$product_name = "Lucene.Net"
+       [int]$maximumParalellJobs = 8
        
        #test paramters
        [string]$frameworks_to_test = "netcoreapp2.1,netcoreapp1.0,net451"
@@ -56,11 +57,11 @@ if ($IsWindows -eq $null) {
        $IsWindows = $Env:OS.StartsWith('Windows')
 }
 
-
 task default -depends Pack
 
 task Clean -description "This task cleans up the build directory" {
        Write-Host "##teamcity[progressMessage 'Cleaning']"
+       Write-Host "##vso[task.setprogress]'Cleaning'"
        Remove-Item $release_directory -Force -Recurse -ErrorAction 
SilentlyContinue
        Get-ChildItem $base_directory -Include *.bak -Recurse | foreach ($_) 
{Remove-Item $_.FullName}
 }
@@ -74,6 +75,7 @@ task UpdateLocalSDKVersion -description "Backs up the 
project.json file and pins
 
 task InstallSDK -description "This task makes sure the correct SDK version is 
installed to build" -ContinueOnError {
        Write-Host "##teamcity[progressMessage 'Installing SDK $sdkVersion']"
+       Write-Host "##vso[task.setprogress]'Installing SDK $sdkVersion'"
        $installed = Is-Sdk-Version-Installed $sdkVersion
        if (!$installed) {
                Write-Host "Requires SDK version $sdkVersion, installing..." 
-ForegroundColor Red
@@ -89,9 +91,11 @@ task InstallSDK -description "This task makes sure the 
correct SDK version is in
 }
 
 task Init -depends InstallSDK, UpdateLocalSDKVersion -description "This task 
makes sure the build environment is correctly setup" {
-       #Update TeamCity or MyGet with packageVersion
+       #Update TeamCity, MyGet, or Azure Pipelines with packageVersion
        Write-Output "##teamcity[buildNumber '$packageVersion']"
        Write-Output "##myget[buildNumber '$packageVersion']"
+       Write-Host "##vso[task.setvariable 
variable=Build.BuildNumber;]$packageVersion"
+       Write-Host "##vso[build.updatebuildnumber]$packageVersion"
 
        & dotnet.exe --version
        & dotnet.exe --info
@@ -106,12 +110,14 @@ task Init -depends InstallSDK, UpdateLocalSDKVersion 
-description "This task mak
        Write-Host "Package Version: $packageVersion"
        Write-Host "Version: $version"
        Write-Host "Configuration: $configuration"
+       Write-Host "Platform: $platform"
 
        Ensure-Directory-Exists "$release_directory"
 }
 
 task Restore -description "This task restores the dependencies" {
        Write-Host "##teamcity[progressMessage 'Restoring']"
+       Write-Host "##vso[task.setprogress]'Restoring'"
        Exec { 
                & dotnet.exe restore $solutionFile --no-dependencies 
/p:TestFrameworks=true
        }
@@ -119,42 +125,25 @@ task Restore -description "This task restores the 
dependencies" {
 
 task Compile -depends Clean, Init, Restore -description "This task compiles 
the solution" {
        Write-Host "##teamcity[progressMessage 'Compiling']"
+       Write-Host "##vso[task.setprogress]'Compiling'"
        try {
                if ($prepareForBuild -eq $true) {
                        Prepare-For-Build
                }
 
-               #Use only the major version as the assembly version.
-               #This ensures binary compatibility unless the major version 
changes.
-               $version-match "(^\d+)"
-               $assemblyVersion = $Matches[0]
-               $assemblyVersion = "$assemblyVersion.0.0"
-
-               Write-Host "Assembly version set to: $assemblyVersion" 
-ForegroundColor Green
-
-               $pv = $packageVersion
-               #check for presense of Git
-               & where.exe git.exe
-               if ($LASTEXITCODE -eq 0) {
-                       $gitCommit = ((git rev-parse --verify --short=10 head) 
| Out-String).Trim()
-                       $pv = "$packageVersion commit:[$gitCommit]"
-               }
-
-               Write-Host "Assembly informational version set to: $pv" 
-ForegroundColor Green
-
-               $testFrameworks = $frameworks_to_test.Replace(',', ';')
+               $testFrameworks = [string]::Join(';', (Get-FrameworksToTest))
 
                Write-Host "TestFrameworks set to: $testFrameworks" 
-ForegroundColor Green
 
                Exec {
+                       # NOTE: Version information is not passed in at the 
command line,
+                       # instead it is output to the Version.props file. This 
file is then
+                       # used during a release to "freeze" the build at a 
specific version
+                       # so it is always a constant in release distributions.
                        & dotnet.exe msbuild $solutionFile /t:Build `
                                /p:Configuration=$configuration `
-                               /p:AssemblyVersion=$assemblyVersion `
-                               /p:FileVersion=$version `
-                               /p:InformationalVersion=$pv `
-                               /p:Product=$product_name `
-                               /p:Company=$company_name `
-                               /p:Copyright=$copyright `
+                               /p:Platform=$platform `
+                               /p:PortableDebugTypeOnly=true `
                                /p:TestFrameworks=true # workaround for parsing 
issue: https://github.com/Microsoft/msbuild/issues/471#issuecomment-181963350
                }
 
@@ -168,18 +157,27 @@ task Compile -depends Clean, Init, Restore -description 
"This task compiles the
 
 task Pack -depends Compile -description "This task creates the NuGet packages" 
{
        Write-Host "##teamcity[progressMessage 'Packing']"
+       Write-Host "##vso[task.setprogress]'Packing'"
        #create the nuget package output directory
        Ensure-Directory-Exists "$nuget_package_directory"
 
        try {
                Exec {
-                       & dotnet.exe pack $solutionFile --configuration 
$Configuration --output $nuget_package_directory --no-build --include-symbols 
/p:PackageVersion=$packageVersion
+                       # NOTE: Package version information is not passed in at 
the command line,
+                       # instead it is output to the Version.props file. This 
file is then
+                       # used during a release to "freeze" the build at a 
specific version
+                       # so it is always a constant in release distributions.
+                       & dotnet.exe pack $solutionFile --configuration 
$configuration --output $nuget_package_directory --no-build
                }
 
                $success = $true
        } finally {
                #if ($success -ne $true) {
                        Restore-Files $backedUpFiles
+                       #Remove Version.props, as we don't want it to be 
committed to the repository
+                       if ($backup_files -eq $true -and (Test-Path -Path 
"$versionPropsFile") -eq $true) {
+                               Remove-Item -Path "$versionPropsFile" -Force
+                       }
                #}
        }
 }
@@ -192,11 +190,23 @@ task Publish -depends Compile -description "This task 
uses dotnet publish to pac
        Write-Host "##vso[task.setprogress]'Publishing'"
 
        try {
-               $frameworksToTest = $frameworks_to_test -split "\s*?,\s*?"
+               $frameworksToTest = Get-FrameworksToTest
+
+               if ($zipPublishedArtifacts) {
+                       $outDirectory = New-TemporaryDirectory
+               } else {
+                       $outDirectory = $publish_directory
+               }
                
                foreach ($framework in $frameworksToTest) {
-                       $testProjects = Get-ChildItem -Path 
"$source_directory/**/*.csproj" -Recurse | ? { 
$_.Directory.Name.Contains(".Tests") } | ForEach-Object { $_.FullName }
+                       $testProjects = Get-ChildItem -Path 
"$source_directory/**/*.csproj" -Recurse | ? { 
$_.Directory.Name.Contains(".Tests") } | Select -ExpandProperty FullName
                        foreach ($testProject in $testProjects) {
+                               # Pause if we have queued too many parallel jobs
+                               $running = @(Get-Job | Where-Object { $_.State 
-eq 'Running' })
+                               if ($running.Count -ge $maximumParalellJobs) {
+                                       $running | Wait-Job -Any | Out-Null
+                               }
+
                                $projectName = 
[System.IO.Path]::GetFileNameWithoutExtension($testProject)
 
                                # Special case - our CLI tool only supports 
.NET Core 2.1
@@ -204,36 +214,43 @@ task Publish -depends Compile -description "This task 
uses dotnet publish to pac
                                        continue
                                }
 
+                               $logPath = "$outDirectory/$framework"
+                               $outputPath = "$logPath/$projectName"
+
                                # Do this first so there is no conflict
-                               $outputPath = 
"$publish_directory/$framework/$projectName"
                                Ensure-Directory-Exists $outputPath
 
                                $scriptBlock = {
-                                       param([string]$testProject, 
[string]$publish_directory, [string]$framework, [string]$configuration, 
[string]$projectName)
-                                       $logPath = 
"$publish_directory/$framework"
-                                       $outputPath = "$logPath/$projectName"
+                                       param([string]$testProject, 
[string]$outputPath, [string]$logPath, [string]$framework, 
[string]$configuration, [string]$projectName)
                                        Write-Host "Publishing '$testProject' 
on '$framework' to '$outputPath'..."
                                        # Note: Cannot use Psake Exec in 
background
-                                       dotnet publish "$testProject" --output 
"$outputPath" --framework "$framework" --configuration "$configuration" 
--no-build --verbosity Detailed /p:TestFrameworks=true > 
"$logPath/$projectName-dotnet-publish.log" 2> 
"$logPath/$projectName-dotnet-publish-error.log"
+                                       dotnet publish "$testProject" --output 
"$outputPath" --framework "$framework" --configuration "$configuration" 
--no-build --verbosity Detailed /p:TestFrameworks=true /p:Platform="$platform" 
> "$logPath/$projectName-dotnet-publish.log" 2> 
"$logPath/$projectName-dotnet-publish-error.log"
                                }
 
                                # Execute the jobs in parallel
-                               Start-Job $scriptBlock -ArgumentList 
$testProject,$publish_directory,$framework,$configuration,$projectName
+                               Start-Job $scriptBlock -ArgumentList 
$testProject,$outputPath,$logPath,$framework,$configuration,$projectName
                        }
                }
 
-               Write-Host "Executing dotnet publish of all projects in 
parallel. This will take a bit, please wait..."
-
-               Get-Job
-
                # Wait for it all to complete
-        While (Get-Job -State "Running") {
-                       Start-Sleep 10
-               }
+               do {
+                       $running = @(Get-Job | Where-Object { $_.State -eq 
'Running' })
+                       if ($running.Count -gt 0) {
+                               Write-Host ""
+                               Write-Host "  Almost finished, only 
$($running.Count) projects left to publish..." -ForegroundColor Cyan
+                               $running | Wait-Job -Any | Out-Null
+                       }
+               } until ($running.Count -eq 0)
 
                # Getting the information back from the jobs (time consuming)
                #Get-Job | Receive-Job
 
+               if ($zipPublishedArtifacts) {
+                       Ensure-Directory-Exists $publish_directory
+                       Add-Type -assembly "System.IO.Compression.Filesystem"
+                       
[System.IO.Compression.ZipFile]::CreateFromDirectory($outDirectory, 
"$publish_directory/$publishedArtifactZipFileName")
+               }
+
                $success = $true
        } finally {
                #if ($success -ne $true) {
@@ -244,27 +261,48 @@ task Publish -depends Compile -description "This task 
uses dotnet publish to pac
 
 task Test -depends InstallSDK, UpdateLocalSDKVersion, Restore -description 
"This task runs the tests" {
        Write-Host "##teamcity[progressMessage 'Testing']"
+       Write-Host "##vso[task.setprogress]'Testing'"
        Write-Host "Running tests..." -ForegroundColor DarkCyan
 
        pushd $base_directory
        $testProjects = Get-ChildItem -Path "$source_directory/**/*.csproj" 
-Recurse | ? { $_.Directory.Name.Contains(".Tests") }
        popd
 
-       Write-Host "frameworks_to_test: $frameworks_to_test" -ForegroundColor 
Yellow
+       $testProjects = $testProjects | Sort-Object -Property FullName
 
-       $frameworksToTest = $frameworks_to_test -split "\s*?,\s*?"
+       $frameworksToTest = Get-FrameworksToTest
+       
+       Write-Host "frameworksToTest: $frameworksToTest" -ForegroundColor Yellow
+
+       [int]$totalProjects = $testProjects.Length * $frameworksToTest.Length
+       [int]$remainingProjects = $totalProjects
 
-       foreach ($framework in $frameworksToTest) {
-               Write-Host "Framework: $framework" -ForegroundColor Blue
+       Ensure-Directory-Exists $test_results_directory
 
-               foreach ($testProject in $testProjects) {
+       foreach ($testProject in $testProjects) {
+
+               foreach ($framework in $frameworksToTest) {
                        $testName = $testProject.Directory.Name
 
                        # Special case - our CLI tool only supports .NET Core 
2.1
                        if ($testName.Contains("Tests.Cli") -and 
(!$framework.StartsWith("netcoreapp2."))) {
+                               $totalProjects--
+                               $remainingProjects--
                                continue
                        }
 
+                       Write-Host "  Next Project in Queue: $testName, 
Framework: $framework" -ForegroundColor Yellow
+
+                       # Pause if we have queued too many parallel jobs
+                       $running = @(Get-Job | Where-Object { $_.State -eq 
'Running' })
+                       if ($running.Count -ge $maximumParalellJobs) {
+                               Write-Host ""
+                               Write-Host "  Running tests in parallel on 
$($running.Count) projects out of approximately $totalProjects total." 
-ForegroundColor Cyan
+                               Write-Host "  $remainingProjects projects are 
waiting in the queue to run. This will take a bit, please wait..." 
-ForegroundColor Cyan
+                               $running | Wait-Job -Any | Out-Null
+                       }
+                       $remainingProjects -= 1
+
                        $testResultDirectory = 
"$test_results_directory/$framework/$testName"
                        Ensure-Directory-Exists $testResultDirectory
 
@@ -290,15 +328,33 @@ task Test -depends InstallSDK, UpdateLocalSDKVersion, 
Restore -description "This
 
                        Write-Host $testExpression -ForegroundColor Magenta
 
-                       Invoke-Expression $testExpression
-                       # fail the build on negative exit codes (NUnit errors - 
if positive it is a test count or, if 1, it could be a dotnet error)
-                       if ($LASTEXITCODE -lt 0) {
-                               throw "Test execution failed"
+                       $scriptBlock = {
+                               param([string]$testExpression, 
[string]$testResultDirectory)
+                               $testExpression = "$testExpression > 
'$testResultDirectory/dotnet-test.log' 2> 
'$testResultDirectory/dotnet-test-error.log'"
+                               Invoke-Expression $testExpression
                        }
+
+                       # Execute the jobs in parallel
+                       Start-Job $scriptBlock -ArgumentList 
$testExpression,$testResultDirectory
+
+                       #Invoke-Expression $testExpression
+                       ## fail the build on negative exit codes (NUnit errors 
- if positive it is a test count or, if 1, it could be a dotnet error)
+                       #if ($LASTEXITCODE -lt 0) {
+                       #       throw "Test execution failed"
+                       #}
                }
        }
 
-       Summarize-Test-Results
+       do {
+               $running = @(Get-Job | Where-Object { $_.State -eq 'Running' })
+               if ($running.Count -gt 0) {
+                       Write-Host ""
+                       Write-Host "  Almost finished, only $($running.Count) 
test projects left..." -ForegroundColor Cyan
+                       $running | Wait-Job -Any
+               }
+       } until ($running.Count -eq 0)
+
+       Summarize-Test-Results -FrameworksToTest $frameworksToTest
 }
 
 function Get-Package-Version() {
@@ -345,6 +401,19 @@ function Get-Version() {
        return $version
 }
 
+function Get-FrameworksToTest() {
+    $frameworksToTest = New-Object Collections.Generic.List[string]
+    $frameworks = $frameworks_to_test -split "\s*?,\s*?"
+    foreach ($framework in $frameworks) {
+        if ($IsWindows) {
+            $frameworksToTest.Add($framework)
+        } elseif ($framework.StartsWith('netcore')) {
+            $frameworksToTest.Add($framework)
+        }
+    }
+    return [System.Linq.Enumerable]::ToArray($frameworksToTest)
+}
+
 function Is-Sdk-Version-Installed([string]$sdkVersion) {
        & where.exe dotnet.exe | Out-Null
        if ($LASTEXITCODE -eq 0) {
@@ -375,6 +444,35 @@ function Is-Sdk-Version-Installed([string]$sdkVersion) {
 }
 
 function Prepare-For-Build() {
+       #Use only the major version as the assembly version.
+       #This ensures binary compatibility unless the major version changes.
+       $version -match "(^\d+)"
+       $assemblyVersion = $Matches[0]
+       $assemblyVersion = "$assemblyVersion.0.0"
+
+       Write-Host "Assembly version set to: $assemblyVersion" -ForegroundColor 
Green
+
+       $informationalVersion = $packageVersion
+       #check for presense of Git
+       & where.exe git.exe
+       if ($LASTEXITCODE -eq 0) {
+               $gitCommit = ((git rev-parse --verify --short=10 head) | 
Out-String).Trim()
+               $informationalVersion = "$packageVersion commit:[$gitCommit]"
+       }
+
+       Write-Host "##vso[task.setvariable 
variable=AssemblyVersion;]$assemblyVersion"
+       Write-Host "##vso[task.setvariable variable=FileVersion;]$version"
+       Write-Host "##vso[task.setvariable 
variable=InformationalVersion;]$informationalVersion"
+       Write-Host "##vso[task.setvariable 
variable=PackageVersion;]$packageVersion"
+
+       Write-Host "Assembly informational version set to: 
$informationalVersion" -ForegroundColor Green
+
+       Generate-Version-Props `
+               -AssemblyVersion $assemblyVersion `
+               -FileVersion $version `
+               -InformationalVersion $informationalVersion `
+               -PackageVersion $packageVersion `
+               -File $versionPropsFile
        Update-Constants-Version $packageVersion
 
        if ($generateBuildBat -eq $true) {
@@ -411,6 +509,50 @@ $fileText = "{
        Out-File -filePath $file -encoding UTF8 -inputObject $fileText
 }
 
+function Generate-Version-Props {
+param(
+       [string]$assemblyVersion,
+       [string]$fileVersion,
+       [string]$informationalVersion,
+       [string]$packageVersion,
+       [string]$file = $(throw "file is a required parameter.")
+)
+
+$fileText = "<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ ""License""); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ ""AS IS"" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+<Project>
+  <PropertyGroup Label=""Version Override Properties"">
+    <AssemblyVersion>$assemblyVersion</AssemblyVersion>
+    <FileVersion>$fileVersion</FileVersion>
+    <InformationalVersion>$informationalVersion</InformationalVersion>
+    <PackageVersion>$packageVersion</PackageVersion>
+  </PropertyGroup>
+</Project>"
+       $dir = [System.IO.Path]::GetDirectoryName($file)
+       Ensure-Directory-Exists $dir
+
+       Write-Host "Generating Version.props file: $file"
+       Out-File -filePath $file -encoding UTF8 -inputObject $fileText
+}
+
 function Generate-Build-Bat {
 param(
        [string]$file = $(throw "file is a required parameter.")
@@ -494,9 +636,7 @@ function New-CountersObject ([string]$project, 
[string]$outcome, [int]$total, [i
     return $counters
 }
 
-function Summarize-Test-Results() {
-    Write-Host "frameworks_to_test: $frameworks_to_test" -ForegroundColor Gray
-    $frameworksToTest = $frameworks_to_test -split "\s*?,\s*?"
+function Summarize-Test-Results([string[]]$frameworksToTest) {
 
     foreach ($framework in $frameworksToTest) {
         pushd $base_directory
@@ -517,11 +657,11 @@ function Summarize-Test-Results() {
 
         Write-Host ""
         Write-Host ""
-        Write-Host 
"************************************************************************************************************"
 -ForegroundColor Yellow
-        Write-Host "*                                                          
                                                *" -ForegroundColor Yellow
-        Write-Host "*                                        Test Summary For 
$framework"  -ForegroundColor Yellow
-        Write-Host "*                                                          
                                                *" -ForegroundColor Yellow
-        Write-Host 
"************************************************************************************************************"
 -ForegroundColor Yellow
+        Write-Host 
"**********************************************************************" 
-ForegroundColor Yellow
+        Write-Host "*                                                          
          *" -ForegroundColor Yellow
+        Write-Host "*                        Test Summary For $framework"  
-ForegroundColor Yellow
+        Write-Host "*                                                          
          *" -ForegroundColor Yellow
+        Write-Host 
"**********************************************************************" 
-ForegroundColor Yellow
 
         foreach ($testReport in $testReports) {
             $testName = 
[System.IO.Path]::GetFileName([System.IO.Path]::GetDirectoryName($testReport))
@@ -555,15 +695,17 @@ function Summarize-Test-Results() {
                         $skippedCountForFramework += $counters.Skipped
 
                         $format = 
@{Expression={$_.Project};Label='Project';Width=35},
-                            @{Expression={$_.Outcome};Label='Outcome';Width=9},
-                            @{Expression={$_.Total};Label='Total';Width=8},
-                            
@{Expression={$_.Executed};Label='Executed';Width=10},
-                            @{Expression={$_.Passed};Label='Passed';Width=8},
-                            @{Expression={$_.Failed};Label='Failed';Width=8},
-                            @{Expression={$_.Warning};Label='Warning';Width=9},
+                            @{Expression={$_.Outcome};Label='Outcome';Width=7},
+                            @{Expression={$_.Total};Label='Total';Width=6},
+                            
@{Expression={$_.Executed};Label='Executed';Width=8},
+                            @{Expression={$_.Passed};Label='Passed';Width=6},
+                            @{Expression={$_.Failed};Label='Failed';Width=6},
+                            @{Expression={$_.Warning};Label='Warning';Width=7},
                             
@{Expression={$_.Inconclusive};Label='Inconclusive';Width=14}
 
-                        $Counters | Format-Table $format
+                                               if ($counters.Failed -gt 0) {
+                                                       $Counters | 
Format-Table $format
+                                               }
                     }
                 }
 
@@ -575,12 +717,12 @@ function Summarize-Test-Results() {
 
         # FOOTER FOR FRAMEWORK
 
-        Write-Host 
"************************************************************************************************************"
 -ForegroundColor Magenta
-        Write-Host "*                                                          
                                                *" -ForegroundColor Magenta
-        Write-Host "*                                        Totals For 
$framework"  -ForegroundColor Magenta
-        Write-Host "*                                                          
                                                *" -ForegroundColor Magenta
-        Write-Host 
"************************************************************************************************************"
 -ForegroundColor Magenta
-        Write-Host ""
+        #Write-Host 
"**********************************************************************" 
-ForegroundColor Magenta
+        #Write-Host "*                                                         
           *" -ForegroundColor Magenta
+        #Write-Host "*                           Totals For $framework"  
-ForegroundColor Magenta
+        #Write-Host "*                                                         
           *" -ForegroundColor Magenta
+        #Write-Host 
"**********************************************************************" 
-ForegroundColor Magenta
+        #Write-Host ""
         $foreground = if ($outcomeForFramework -eq 'Failed') { 'Red' } else { 
'Green' }
         Write-Host "Result: " -NoNewline; Write-Host "$outcomeForFramework" 
-ForegroundColor $foreground
         Write-Host ""
@@ -594,6 +736,8 @@ function Summarize-Test-Results() {
         Write-Host "Warning: " -NoNewline; Write-Host 
"$warningCountForFramework" -ForegroundColor $foreground
         $foreground = if ($failedCountForFramework -gt 0) { 'Cyan' } else { 
(Get-Host).UI.RawUI.ForegroundColor }
         Write-Host "Inconclusive: " -NoNewline; Write-Host 
"$inconclusiveCountForFramework" -ForegroundColor $foreground
+               Write-Host ""
+               Write-Host "See the .trx logs in 
$test_results_directory/$framework for more details." -ForegroundColor DarkCyan
     }
 }
 
@@ -631,4 +775,10 @@ function Ensure-Directory-Exists([string] $path) {
        if (!(Test-Path $path)) {
                New-Item $path -ItemType Directory
        }
+}
+
+function New-TemporaryDirectory {
+    $parent = [System.IO.Path]::GetTempPath()
+    [string] $name = [System.Guid]::NewGuid()
+    New-Item -ItemType Directory -Path (Join-Path $parent $name)
 }
\ No newline at end of file
diff --git 
a/src/Lucene.Net.Tests.Analysis.Common/Analysis/Th/TestThaiAnalyzer.cs 
b/src/Lucene.Net.Tests.Analysis.Common/Analysis/Th/TestThaiAnalyzer.cs
index c208618..4dc1c2a 100644
--- a/src/Lucene.Net.Tests.Analysis.Common/Analysis/Th/TestThaiAnalyzer.cs
+++ b/src/Lucene.Net.Tests.Analysis.Common/Analysis/Th/TestThaiAnalyzer.cs
@@ -92,6 +92,48 @@ namespace Lucene.Net.Analysis.Th
             AssertAnalyzesTo(analyzer, "ประโยคว่า The quick brown fox jumped 
over the lazy dogs", new string[] { "ประโยค", "ว่า", "quick", "brown", "fox", 
"jumped", "over", "lazy", "dogs" });
         }
 
+        // Ellision character
+        private static readonly char THAI_PAIYANNOI = (char)0x0E2F;
+        // Repeat character
+        private static readonly char THAI_MAIYAMOK = (char)0x0E46;
+
+        [Test]
+        [LuceneNetSpecific]
+        public virtual void TestThaiBreakEngineInitializerCode()
+        {
+            // Initialize UnicodeSets
+            var fThaiWordSet = new ICU4N.Text.UnicodeSet();
+            var fMarkSet = new ICU4N.Text.UnicodeSet();
+            var fBeginWordSet = new ICU4N.Text.UnicodeSet();
+            var fSuffixSet = new ICU4N.Text.UnicodeSet();
+
+            fThaiWordSet.ApplyPattern("[[:Thai:]&[:LineBreak=SA:]]");
+            fThaiWordSet.Compact();
+
+            fMarkSet.ApplyPattern("[[:Thai:]&[:LineBreak=SA:]&[:M:]]");
+            fMarkSet.Add(0x0020);
+            var fEndWordSet = new ICU4N.Text.UnicodeSet(fThaiWordSet);
+            fEndWordSet.Remove(0x0E31); // MAI HAN-AKAT
+            fEndWordSet.Remove(0x0E40, 0x0E44); // SARA E through SARA AI 
MAIMALAI
+            fBeginWordSet.Add(0x0E01, 0x0E2E); //KO KAI through HO NOKHUK
+            fBeginWordSet.Add(0x0E40, 0x0E44); // SARA E through SARA AI 
MAIMALAI
+            fSuffixSet.Add(THAI_PAIYANNOI);
+            fSuffixSet.Add(THAI_MAIYAMOK);
+
+            // Compact for caching
+            fMarkSet.Compact();
+            fEndWordSet.Compact();
+            fBeginWordSet.Compact();
+            fSuffixSet.Compact();
+
+            // Freeze the static UnicodeSet
+            fThaiWordSet.Freeze();
+            fMarkSet.Freeze();
+            fEndWordSet.Freeze();
+            fBeginWordSet.Freeze();
+            fSuffixSet.Freeze();
+        }
+
         /*
          * Test that position increments are adjusted correctly for stopwords.
          */

Reply via email to