diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5a94157..beed5b6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,10 +39,6 @@ jobs: working-directory: src run: dotnet tool install coveralls.net --version 2.0.0-beta0002 - - name: Download samples - working-directory: src - run: powershell .\DownloadSamples.ps1 - - name: Run tests working-directory: src run: powershell .\Test.ps1 diff --git a/doc/contributing/continuous-integration.md b/doc/contributing/continuous-integration.md index eb1d2dc..588c4c4 100644 --- a/doc/contributing/continuous-integration.md +++ b/doc/contributing/continuous-integration.md @@ -62,9 +62,6 @@ Please see the source code of `src\Check.ps1` for more details. Our tests with external dependencies use environment variables to specify the location of the dependencies. While `src\Check.ps1` and related scripts set up the expected locations in the environment automatically, you need to adjust your test setting accordingly. -So far, we need to set `SAMPLE_AASX_DIR` environment variable to point to the absolute path where AASX samples have been downloaded (using `src\DownloadSamples.ps1`). -This is by definition `{your repository}\sample-aasx`. - ## Github Workflows Github Actions allow for running continuous integration on Github servers. diff --git a/src/AasCore.Aas3.Package.Tests/SampleAasxDir.cs b/src/AasCore.Aas3.Package.Tests/SampleAasxDir.cs deleted file mode 100644 index f626747..0000000 --- a/src/AasCore.Aas3.Package.Tests/SampleAasxDir.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Directory = System.IO.Directory; -using Environment = System.Environment; -using File = System.IO.File; -using InvalidOperationException = System.InvalidOperationException; -using Path = System.IO.Path; - -using System.Collections.Generic; // can't alias -using System.IO; // can't alias -using System.Linq; // can't alias - -namespace AasCore.Aas3.Package.Tests -{ - /** - * List the directory with the downloaded AASX sample files. - */ - internal static class SampleAasxDir - { - private static string DirPathFromEnvironment() - { - var variable = "SAMPLE_AASX_DIR"; - - var result = Environment.GetEnvironmentVariable(variable); - - if (result == null) - { - throw new InvalidOperationException( - $"The environment variable {variable} has not been set. " + - "Did you set it manually to the directory containing sample AASXs? " + - "Otherwise, run the test through Test.ps1?"); - } - - return result; - } - - public static List ListPaths() - { - var sampleAasxDir = DirPathFromEnvironment(); - - if (!Directory.Exists(sampleAasxDir)) - { - throw new InvalidOperationException( - "The directory containing the sample AASXs " + - $"does not exist or is not a directory: {sampleAasxDir}; " + - "did you download the samples with DownloadSamples.ps1?"); - } - - var result = Directory.GetFiles(sampleAasxDir) - .Where(p => Path.GetExtension(p).ToLower() == ".aasx") - .ToList(); - - result.Sort(); - - return result; - } - - public static string Path34Festo() - { - var sampleAasxDir = DirPathFromEnvironment(); - var pth = Path.Combine(new[] { sampleAasxDir, "34_Festo.aasx" }); - if (!File.Exists(pth)) - { - throw new FileNotFoundException( - $"Expected the AAS package to exist, but it does not: {pth}"); - } - - return pth; - } - } - -} \ No newline at end of file diff --git a/src/AasCore.Aas3.Package.Tests/Table.cs b/src/AasCore.Aas3.Package.Tests/Table.cs deleted file mode 100644 index 0e507f5..0000000 --- a/src/AasCore.Aas3.Package.Tests/Table.cs +++ /dev/null @@ -1,138 +0,0 @@ -using Math = System.Math; -using String = System.String; -using StringWriter = System.IO.StringWriter; - -using System.Collections.Generic; // can't alias -using System.Linq; // can't alias - -// This is necessary only for InspectCode. -// See https://resharper-support.jetbrains.com/hc/en-us/community/posts/360008362700-Not-recognising-Nullable-enable-Nullable- -#nullable enable - -namespace AasCore.Aas3.Package.Tests -{ - /** - * Draw a table as a string. - * - * If you have new-line characters or tabs in your table, the appearance might be - * scrambled. - * - */ - public class Table - { - private readonly List _headers; - private readonly List> _rows = new List>(); - - public Table(List headers) - { - _headers = headers; - } - - /** - * - * Add the cells to the table. - * - * - * No cell may contain a new-line character or a tab. - * If it does, the appearance of your table will be scrambled. - * - */ - public void Add(IEnumerable cells) - { - _rows.Add(cells.ToList()); - } - - /** - * - * Render the table as the ascii. - * - * If you did not specify the new-line character, - * will be used. - * - */ - public string Render(string? newline = null) - { - newline ??= System.Environment.NewLine; - - List columnWidths = _headers.Select( - header => header.Length).ToList(); - - int columnIndex; - foreach (List row in _rows) - { - columnIndex = 0; - foreach (string cell in row) - { - if (columnIndex + 1 > columnWidths.Count) - { - columnWidths.Add(cell.Length); - } - else - { - columnWidths[columnIndex] = Math.Max(columnWidths[columnIndex], - cell.Length); - } - - columnIndex++; - } - } - - StringWriter writer = new StringWriter(); - columnIndex = 0; - foreach (string header in _headers) - { - if (columnIndex > 0) - { - writer.Write(" | "); - } - - writer.Write(columnIndex < _headers.Count - 1 - ? header.PadRight(columnWidths[columnIndex]) - : header); - - columnIndex++; - } - writer.Write(newline); - - columnIndex = 0; - foreach (var _ in _headers) - { - if (columnIndex > 0) - { - writer.Write("-+-"); - } - - writer.Write(new String('-', columnWidths[columnIndex])); - - columnIndex++; - } - - foreach (List row in _rows) - { - writer.Write(newline); - - columnIndex = 0; - foreach (string cell in row) - { - if (columnIndex > 0) - { - writer.Write(" | "); - } - - writer.Write(columnIndex < row.Count - 1 - ? cell.PadRight(columnWidths[columnIndex]) - : cell); - - columnIndex++; - } - } - - if (_rows.Count > 0) - { - writer.Write(newline); - } - - return writer.ToString(); - } - } -} \ No newline at end of file diff --git a/src/AasCore.Aas3.Package.Tests/TestPackageRead.cs b/src/AasCore.Aas3.Package.Tests/TestPackageRead.cs index 6aeccc9..aabe545 100644 --- a/src/AasCore.Aas3.Package.Tests/TestPackageRead.cs +++ b/src/AasCore.Aas3.Package.Tests/TestPackageRead.cs @@ -1,12 +1,9 @@ -using Convert = System.Convert; -using Directory = System.IO.Directory; using Encoding = System.Text.Encoding; using File = System.IO.File; using FileAccess = System.IO.FileAccess; using FileFormatException = System.IO.FileFormatException; using FileMode = System.IO.FileMode; using InvalidDataException = System.IO.InvalidDataException; -using InvalidOperationException = System.InvalidOperationException; using MemoryStream = System.IO.MemoryStream; using Path = System.IO.Path; using SystemPackage = System.IO.Packaging.Package; // renamed @@ -20,63 +17,6 @@ namespace AasCore.Aas3.Package.Tests { public class TestPackageRead { - private static string GetTestResourcesPath() - { - return Path.Combine( - TestContext.CurrentContext.TestDirectory, - Path.Combine( - new[] - { - "TestResources", - $"{nameof(AasCore)}.{nameof(Aas3)}.{nameof(Package)}" + - $".{nameof(Tests)}", - nameof(TestPackageRead) - } - )); - } - - /** - * Execute the test action on the given package opened for reading. - * - * - * The files in the are grouped for the package. - * Please make sure that all the file names that you pick are unique. - * - */ - delegate void SampleReadTestAction(PackageRead package, string expectedDir); - - private static void ForEachReadOfSampleAasx(SampleReadTestAction action) - { - foreach (string pth in SampleAasxDir.ListPaths()) - { - string name = Path.GetFileNameWithoutExtension(pth); - - string expectedDir = Path.Combine( - new[] { GetTestResourcesPath(), name }); - const bool record = false; - -#pragma warning disable 162 - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - // ReSharper disable once HeuristicUnreachableCode - if (record) Directory.CreateDirectory(expectedDir); -#pragma warning restore 162 - - Packaging packaging = new Packaging(); - - using var pkgOrErr = packaging.OpenRead(pth); - if (pkgOrErr.MaybeException != null) - { - throw new AssertionException( - $"Failed to open for reading: {pth}", - pkgOrErr.MaybeException); - } - - var pkg = pkgOrErr.Must(); - - action(pkg, expectedDir); - } - } - [Test] public void Test_that_must_returns_the_exception() { @@ -155,264 +95,6 @@ public void Test_grouping_specs_by_content_type() } } - [Test] - public void Test_listing_specs_against_the_recorded_outputs() - { - // Please set to true if you want to re-record the expected outputs. - const bool record = false; - - ForEachReadOfSampleAasx((pkg, expectedDir) => - { - Table tbl = new Table(new List { "Content Type", "URIs" }); - foreach (var item in pkg.SpecsByContentType()) - { - tbl.Add(new List - { - item.Key, - string.Join( - ", ", - item.Value.Select((spec) => spec.Uri.ToString())) - }); - } - - var tblPth = Path.Combine( - new[] { expectedDir, "specsTable.txt" }); - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (record) -#pragma warning disable 162 - { - File.WriteAllText(tblPth, tbl.Render()); - TestContext.Out.WriteLine($"Recorded: {tblPth}"); - } -#pragma warning restore 162 - - Assert.AreEqual( - File.ReadAllText(tblPth), - tbl.Render()); - }); - } - - [Test] - public void Test_reading_spec_content_against_the_recorded_outputs() - { - // Please set to true if you want to re-record the expected outputs. - const bool record = false; - - ForEachReadOfSampleAasx((pkg, expectedDir) => - { - HashSet filenameSet = new HashSet(); - - foreach (var spec in pkg.Specs()) - { - Assert.IsFalse( - spec.Uri.IsAbsoluteUri, - "Expected a local URI of an AAS spec " + - "(*i.e.* an URI without a host), " + - "but got an absolute one: {spec.Uri}"); - - string filename = Path.GetFileName(spec.Uri.ToString()); - - // Assume that the file name of the specs is unique in the samples - if (filenameSet.Contains(filename)) - { - throw new AssertionException( - "Assumed that all sample AASXs contain specs " + - "with unique file names, but the file name " + - $"for the spec is a duplicate: {filename} " + - $"(URI: {spec.Uri}, sample file: {pkg.Path}"); - } - - filenameSet.Add(filename); - - string contentPth = Path.Combine(new[] { expectedDir, filename }); - - // We need to convert Windows newlines to Linux newlines since - // we can run the tests on both systems. Additionally, Git will - // most probably do the conversion for us anyhow. - string content = spec - .ReadAllText() - .Replace("\r\n", "\n"); - - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (record) -#pragma warning disable 162 - { - File.WriteAllText(contentPth, content); - TestContext.Out.WriteLine($"Recorded: {contentPth}"); - } -#pragma warning restore 162 - - string expectedContent = File.ReadAllText(contentPth) - .Replace("\r\n", "\n"); - - Assert.AreEqual(expectedContent, content); - } - }); - } - - [Test] - public void Test_listing_supplementary_files_against_the_recorded_outputs() - { - // Please set to true if you want to re-record the expected outputs. - const bool record = false; - - ForEachReadOfSampleAasx((pkg, expectedDir) => - { - Table tbl = new Table(new List - { - "Supplementary Content Type", "Supplementary URI", - "Spec Content Type", "Spec URI" - }); - - try - { - foreach (var supplRel in pkg.SupplementaryRelationships()) - { - tbl.Add(new List - { - supplRel.Supplementary.ContentType, - supplRel.Supplementary.Uri.ToString(), - supplRel.Spec.ContentType, - supplRel.Spec.Uri.ToString() - }); - } - } - catch (InvalidDataException err) - { - tbl.Add(new List - { - "N/A", - "N/A", - "N/A", - err.Message - }); - } - - var tblPth = Path.Combine( - new[] { expectedDir, "supplementariesTable.txt" }); - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (record) -#pragma warning disable 162 - { - File.WriteAllText(tblPth, tbl.Render()); - TestContext.Out.WriteLine($"Recorded: {tblPth}"); - } -#pragma warning restore 162 - - Assert.AreEqual( - File.ReadAllText(tblPth), - tbl.Render()); - }); - } - - [Test] - public void Test_getting_a_thumbnail_against_recorded_outputs() - { - // Please set to true if you want to re-record the expected outputs. - const bool record = false; - - ForEachReadOfSampleAasx((pkg, expectedDir) => - { - var thumbnail = pkg.Thumbnail(); - - var thumbSummaryPth = Path.Combine( - new[] { expectedDir, "thumbnail.txt" }); - - var thumbUriText = thumbnail == null - ? "Not available" - : thumbnail.Uri.ToString(); - - var firstBytes = new byte[] { 0, 0, 0, 0 }; - using var stream = thumbnail?.Stream(); - var bytesRead = stream?.Read(firstBytes, 0, 4); - if (bytesRead != 4) - { - throw new InvalidOperationException( - $"Unexpected to read only {bytesRead} bytes"); - } - - var firstBytesText = string.Join( - ", ", - firstBytes.Select( - aByte => aByte > 32 && aByte < 127 - ? Convert.ToChar(aByte).ToString() - : aByte.ToString())); - - var text = $"URI: {thumbUriText}, " + - $"first bytes: {firstBytesText}"; - - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (record) -#pragma warning disable 162 - { - File.WriteAllText(thumbSummaryPth, text); - TestContext.Out.WriteLine($"Recorded: {thumbSummaryPth}"); - } -#pragma warning restore 162 - - Assert.AreEqual( - File.ReadAllText(thumbSummaryPth), text); - }); - } - - [Test] - // ReSharper disable once InconsistentNaming - public void Test_open_stream_against_open_path() - { - foreach (string pth in SampleAasxDir.ListPaths()) - { - Packaging packaging = new Packaging(); - - using var pkgPathOrErr = packaging.OpenRead(pth); - var pkgPath = pkgPathOrErr.Must(); - - using var stream = File.OpenRead(pth); - using var pkgStreamOrErr = packaging.OpenRead(stream); - var pkgStream = pkgStreamOrErr.Must(); - - var uriSpecsPath = - pkgPath.Specs().Select(spec => spec.Uri.ToString()).ToList(); - - var uriSpecsStream = - pkgStream.Specs().Select(spec => spec.Uri.ToString()).ToList(); - - Assert.That(uriSpecsPath, Is.EqualTo(uriSpecsStream)); - } - } - - [Test] - public void Test_that_part_returns_null_if_it_does_not_exist() - { - var pth = SampleAasxDir.Path34Festo(); - - Packaging packaging = new Packaging(); - - using var pkgPathOrErr = packaging.OpenRead(pth); - var pkg = pkgPathOrErr.Must(); - - Assert.IsNull( - pkg.FindPart( - new Uri( - "/the/supplementary/does/not/exist", UriKind.Relative))); - } - - [Test] - public void Test_that_must_part_throws_the_adequate_exception() - { - var pth = SampleAasxDir.Path34Festo(); - - Packaging packaging = new Packaging(); - - using var pkgPathOrErr = packaging.OpenRead(pth); - var pkg = pkgPathOrErr.Must(); - - Assert.Catch(() => - { - pkg.MustPart( - new Uri("/the/supplementary/does/not/exist", UriKind.Relative)); - }); - } - [Test] public void Test_that_opening_a_non_package_file_returns_the_exception() { diff --git a/src/AasCore.Aas3.Package/Package.cs b/src/AasCore.Aas3.Package/Package.cs index af8d294..0fa1e62 100644 --- a/src/AasCore.Aas3.Package/Package.cs +++ b/src/AasCore.Aas3.Package/Package.cs @@ -130,6 +130,7 @@ public Part(Uri uri, PackagePart packagePart) * The caller is responsible for disposing it. * */ + // ReSharper disable once MemberCanBePrivate.Global public Stream Stream() { return PackagePart.GetStream(FileMode.Open, FileAccess.Read); @@ -149,6 +150,7 @@ public byte[] ReadAllBytes() /** * Read the content of the part as UTF-8 text. */ + // ReSharper disable once UnusedMember.Global public string ReadAllText() { return ReadAllText(Encoding.UTF8); @@ -177,13 +179,13 @@ public class Packaging internal static class RelationType { internal const string AasxOrigin = - "http://www.admin-shell.io/aasx/relationships/aasx-origin"; + "http://admin-shell.io/aasx/relationships/aasx-origin"; internal const string AasxSpec = - "http://www.admin-shell.io/aasx/relationships/aas-spec"; + "http://admin-shell.io/aasx/relationships/aas-spec"; internal const string AasxSupplementary = - "http://www.admin-shell.io/aasx/relationships/aas-suppl"; + "http://admin-shell.io/aasx/relationships/aas-suppl"; internal const string Thumbnail = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"; @@ -797,6 +799,8 @@ public IEnumerable SupplementariesFor(Part spec) public class SupplementaryRelationship { + // ReSharper disable once MemberCanBePrivate.Global + // ReSharper disable once NotAccessedField.Global public readonly Part Spec; public readonly Part Supplementary; @@ -1361,4 +1365,4 @@ public void Flush() UnderlyingPackage.Flush(); } } -} \ No newline at end of file +} diff --git a/src/DownloadSamples.ps1 b/src/DownloadSamples.ps1 deleted file mode 100644 index aa16c59..0000000 --- a/src/DownloadSamples.ps1 +++ /dev/null @@ -1,124 +0,0 @@ -param( - [Parameter(HelpMessage = "If set, cleans up the previously downloaded samples")] - [switch] - $clean = $false -) -<# -.SYNOPSIS -This script downloads samples AASXs from www.admin-shell-io.com. -#> - -function Main -{ - Set-Location $PSScriptRoot - - $samplesDir = Join-Path (Split-Path $PSScriptRoot -Parent) "sample-aasx" - - if (!$clean) - { - New-Item -ItemType Directory -Force -Path $samplesDir|Out-Null - - $urlPrefix = "http://admin-shell-io.com/samples/aasx/" - $sampleFilenames = @( - "01_Festo.aasx", - "02_Bosch.aasx", - "03_Bosch.aasx", - "04_Bosch.aasx", - "05_Bosch.aasx", - "06_Bosch.aasx", - "07_PhoenixContact.aasx", - "08_SchneiderElectric.aasx", - "09_SchneiderElectric.aasx", - "10_SchneiderElectric.aasx", - "11_SchneiderElectric.aasx", - "12_Pepperl+Fuchs.aasx", - "13_DKE.aasx", - "14_Siemens.aasx", - "15_Siemens.aasx", - "16_Lenze.aasx", - "17_ABB.aasx", - "18_Hitachi.aasx", - "34_Festo.aasx" - ) - - # Set up the default proxy. This is necessary for many enterprise - # settings, as the direct connection with Invoke-WebRequest does not - # work. - # - # See also: https://www.reddit.com/r/PowerShell/comments/77m4lj/, - # http://woshub.com/using-powershell-behind-a-proxy/ and - # https://community.idera.com/database-tools/powershell/powertips/b/tips/posts/use-internet-connection-with-default-proxy - - $proxy = [System.Net.WebRequest]::GetSystemWebProxy() - $proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials - $webClient = New-Object System.Net.WebClient - $webClient.proxy = $proxy - $webClient.UseDefaultCredentials = $true - - foreach ($sampleFilename in $sampleFilenames) - { - $url = $urlPrefix + $sampleFilename - $samplePath = Join-Path $samplesDir $sampleFilename - - if (Test-Path $samplePath) - { - Write-Host "File exists: $samplePath; skipping." - continue - } - - Write-Host "Downloading $url -> $samplePath" - $samplePathTmp = $samplePath + ".DownloadSamples.ps1.temp" - try - { - $webClient.DownloadFile($url, $samplePathTmp) - Move-Item -Path $samplePathTmp -Destination $samplePath - } - catch - { - if ($null -eq $proxyAddress) - { - $proxyMessage = "no default proxy could be inferred by the script" - } - else - { - $proxyMessage = "the script uses the default proxy: $( "$proxyAddress"|ConvertTo-Json ))" - } - - $nl = [Environment]::NewLine - - throw ( - "The script failed to download the sample AASX from: $url.$nl$nl" + - "* Do you use a proxy and was it properly set up?$nl" + - " * powershell `"[System.Net.WebRequest]::GetSystemWebProxy()`"?$nl" + - "* Was there enough disk space?$nl" + - "* Could the downloaded file be moved from $samplePathTmp to ${samplePath}?$nl" + - "* Can you access $url from your browser?" - ) - } - finally - { - if (Test-Path $samplePathTmp) - { - Remove-Item $samplePathTmp - } - } - } - } - else - { - if (Test-Path $samplesDir) - { - Write-Host "Removing: $samplesDir" - Remove-Item -Recurse -Force $samplesDir - } - } -} - -$previousLocation = Get-Location; try -{ - Main -} -finally -{ - Set-Location $previousLocation -} diff --git a/src/InstallSolutionDependencies.ps1 b/src/InstallSolutionDependencies.ps1 index 98c8809..06feb8e 100644 --- a/src/InstallSolutionDependencies.ps1 +++ b/src/InstallSolutionDependencies.ps1 @@ -18,9 +18,6 @@ function Main Write-Host "Restoring the tools..." dotnet tool restore - - Write-Host "Downloading the AASX samples..." - & powershell "./DownloadSamples.ps1" } $previousLocation = Get-Location; try diff --git a/src/Test.ps1 b/src/Test.ps1 index ed04daa..0958479 100644 --- a/src/Test.ps1 +++ b/src/Test.ps1 @@ -7,49 +7,14 @@ $ErrorActionPreference = "Stop" -Import-Module (Join-Path $PSScriptRoot Common.psm1) -Function ` - GetSamplesDir - function Main { - $samplesDir = GetSamplesDir - if(!(Test-Path $samplesDir)) - { - throw ( - "The directory containing samples could not be found: " + - "$samplesDir; these samples are necessary to " + - "perform the integration tests. " + - "Did you maybe forget to download them with DownloadSamples.ps1?" - ) - } - - if (Test-Path env:SAMPLE_AASX_DIR) - { - $prevEnvSampleAasxDir = $env:SAMPLE_AASX_DIR - } - else - { - $prevEnvSampleAasxDir = $null - } - - try - { - $env:SAMPLE_AASX_DIR = $samplesDir - - & dotnet test -c DebugSlow ` - /p:CollectCoverage=true ` - /p:CoverletOutputFormat=opencover - if ($LASTEXITCODE -ne 0) - { - throw "The unit tests failed." - } - } - finally + & dotnet test -c DebugSlow ` + /p:CollectCoverage=true ` + /p:CoverletOutputFormat=opencover + if ($LASTEXITCODE -ne 0) { - if ($null -ne $prevEnvSampleAasxDir) - { - $env:SAMPLE_AASX_DIR = $prevEnvSampleAasxDir - } + throw "The unit tests failed." } }