From 535a0d7a3eb60be0e54ca8b54c26f3dadd9a058c Mon Sep 17 00:00:00 2001 From: Shiwei Zhang Date: Wed, 27 Dec 2023 11:01:37 +0800 Subject: [PATCH] build: build check with code coverage uploaded (#78) Signed-off-by: Shiwei Zhang --- .github/workflows/build.yml | 45 ++++++++++++++++ src/OrasProject.Oras/Content/Content.cs | 4 +- .../Remote/ManifestUtility.cs | 4 +- src/OrasProject.Oras/Remote/Registry.cs | 8 +-- .../Remote/RemoteReference.cs | 1 - src/OrasProject.Oras/Remote/Repository.cs | 4 +- tests/OrasProject.Oras.Tests/CopyTest.cs | 5 +- .../MemoryTest/MemoryTargetTest.cs | 4 +- .../OrasProject.Oras.Tests.csproj | 14 ++--- .../RemoteTest/AuthTest.cs | 5 +- .../RemoteTest/RepositoryTest.cs | 51 ++++++++++++------- 11 files changed, 101 insertions(+), 44 deletions(-) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..362c5a7 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,45 @@ +# Copyright The ORAS Authors. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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: build + +on: + push: + branches: + - main + - release-* + pull_request: + branches: + - main + - release-* + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + dotnet-version: ['8.0.x'] + fail-fast: true + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up dotnet ${{ matrix.dotnet-version }} environment + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ matrix.dotnet-version }} + - name: Run unit tests + run: dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover ./tests/OrasProject.Oras.Tests + - name: Upload coverage to codecov.io + uses: codecov/codecov-action@v3 + with: + file: ./tests/OrasProject.Oras.Tests/coverage.opencover.xml diff --git a/src/OrasProject.Oras/Content/Content.cs b/src/OrasProject.Oras/Content/Content.cs index 9b26def..3740cb2 100644 --- a/src/OrasProject.Oras/Content/Content.cs +++ b/src/OrasProject.Oras/Content/Content.cs @@ -1,7 +1,7 @@ -using OrasProject.Oras.Exceptions; +using OrasProject.Oras.Constants; +using OrasProject.Oras.Exceptions; using OrasProject.Oras.Interfaces; using OrasProject.Oras.Models; -using OrasProject.Oras.Constants; using System; using System.Collections.Generic; using System.IO; diff --git a/src/OrasProject.Oras/Remote/ManifestUtility.cs b/src/OrasProject.Oras/Remote/ManifestUtility.cs index 4e5baa6..a24eaff 100644 --- a/src/OrasProject.Oras/Remote/ManifestUtility.cs +++ b/src/OrasProject.Oras/Remote/ManifestUtility.cs @@ -1,5 +1,5 @@ -using OrasProject.Oras.Models; -using OrasProject.Oras.Constants; +using OrasProject.Oras.Constants; +using OrasProject.Oras.Models; using System.Linq; namespace OrasProject.Oras.Remote diff --git a/src/OrasProject.Oras/Remote/Registry.cs b/src/OrasProject.Oras/Remote/Registry.cs index 1931971..9b37d07 100644 --- a/src/OrasProject.Oras/Remote/Registry.cs +++ b/src/OrasProject.Oras/Remote/Registry.cs @@ -32,8 +32,8 @@ public Registry(string name) } public Registry(string name, HttpClient httpClient) - { - var reference = new RemoteReference + { + var reference = new RemoteReference { Registry = name, }; @@ -79,8 +79,8 @@ public Task Repository(string name, CancellationToken cancellationT { Registry = RemoteReference.Registry, Repository = name, - }; - + }; + return Task.FromResult(new Repository(reference, this)); } diff --git a/src/OrasProject.Oras/Remote/RemoteReference.cs b/src/OrasProject.Oras/Remote/RemoteReference.cs index a42a19d..0da989d 100644 --- a/src/OrasProject.Oras/Remote/RemoteReference.cs +++ b/src/OrasProject.Oras/Remote/RemoteReference.cs @@ -1,7 +1,6 @@ using OrasProject.Oras.Content; using OrasProject.Oras.Exceptions; using System; -using System.Net.NetworkInformation; using System.Text.RegularExpressions; namespace OrasProject.Oras.Remote diff --git a/src/OrasProject.Oras/Remote/Repository.cs b/src/OrasProject.Oras/Remote/Repository.cs index e41334a..f042b74 100644 --- a/src/OrasProject.Oras/Remote/Repository.cs +++ b/src/OrasProject.Oras/Remote/Repository.cs @@ -63,8 +63,8 @@ public Repository(string reference) RemoteReference = RemoteReference.ParseReference(reference); HttpClient = new HttpClient(); HttpClient.DefaultRequestHeaders.Add("User-Agent", new string[] { "oras-dotnet" }); - } - + } + /// /// Creates a client to the remote repository using a reference and a HttpClient /// diff --git a/tests/OrasProject.Oras.Tests/CopyTest.cs b/tests/OrasProject.Oras.Tests/CopyTest.cs index fdb0c1c..bb15ee1 100644 --- a/tests/OrasProject.Oras.Tests/CopyTest.cs +++ b/tests/OrasProject.Oras.Tests/CopyTest.cs @@ -1,7 +1,6 @@ -using OrasProject.Oras.Memory; +using OrasProject.Oras.Constants; +using OrasProject.Oras.Memory; using OrasProject.Oras.Models; -using OrasProject.Oras; -using OrasProject.Oras.Constants; using System.Text; using System.Text.Json; using Xunit; diff --git a/tests/OrasProject.Oras.Tests/MemoryTest/MemoryTargetTest.cs b/tests/OrasProject.Oras.Tests/MemoryTest/MemoryTargetTest.cs index cb8fdad..e643c73 100644 --- a/tests/OrasProject.Oras.Tests/MemoryTest/MemoryTargetTest.cs +++ b/tests/OrasProject.Oras.Tests/MemoryTest/MemoryTargetTest.cs @@ -1,7 +1,7 @@ -using OrasProject.Oras.Exceptions; +using OrasProject.Oras.Constants; +using OrasProject.Oras.Exceptions; using OrasProject.Oras.Memory; using OrasProject.Oras.Models; -using OrasProject.Oras.Constants; using System.Text; using System.Text.Json; using Xunit; diff --git a/tests/OrasProject.Oras.Tests/OrasProject.Oras.Tests.csproj b/tests/OrasProject.Oras.Tests/OrasProject.Oras.Tests.csproj index 0ec19b8..14276da 100644 --- a/tests/OrasProject.Oras.Tests/OrasProject.Oras.Tests.csproj +++ b/tests/OrasProject.Oras.Tests/OrasProject.Oras.Tests.csproj @@ -9,14 +9,14 @@ - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all + + runtime; build; native; contentfiles; analyzers; buildtransitive + all - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/OrasProject.Oras.Tests/RemoteTest/AuthTest.cs b/tests/OrasProject.Oras.Tests/RemoteTest/AuthTest.cs index ec23970..8be8621 100644 --- a/tests/OrasProject.Oras.Tests/RemoteTest/AuthTest.cs +++ b/tests/OrasProject.Oras.Tests/RemoteTest/AuthTest.cs @@ -1,6 +1,5 @@ using Moq; using Moq.Protected; -using OrasProject.Oras.Remote; using OrasProject.Oras.Remote.Auth; using System.Net; using System.Text; @@ -36,8 +35,8 @@ public async Task TestClient_CustomHttpBasicAuthClient() var res = new HttpResponseMessage { RequestMessage = req - }; - + }; + if (req.Method != HttpMethod.Get && req.RequestUri?.AbsolutePath == $"/") { res.StatusCode = HttpStatusCode.NotFound; diff --git a/tests/OrasProject.Oras.Tests/RemoteTest/RepositoryTest.cs b/tests/OrasProject.Oras.Tests/RemoteTest/RepositoryTest.cs index a3e7cf0..51c6709 100644 --- a/tests/OrasProject.Oras.Tests/RemoteTest/RepositoryTest.cs +++ b/tests/OrasProject.Oras.Tests/RemoteTest/RepositoryTest.cs @@ -1,11 +1,10 @@ using Moq; using Moq.Protected; +using OrasProject.Oras.Constants; using OrasProject.Oras.Exceptions; using OrasProject.Oras.Memory; using OrasProject.Oras.Models; using OrasProject.Oras.Remote; -using OrasProject.Oras; -using OrasProject.Oras.Constants; using System.Collections.Immutable; using System.Diagnostics; using System.Net; @@ -135,6 +134,17 @@ public static HttpClient CustomClient(Func> func) + { + var moqHandler = new Mock(); + moqHandler.Protected().Setup>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny() + ).Returns(func); + return new HttpClient(moqHandler.Object); + } + /// /// Repository_FetchAsync tests the FetchAsync method of the Repository. /// @@ -189,7 +199,6 @@ public async Task Repository_FetchAsync() resp.Content.Headers.Add("Content-Type", indexDesc.MediaType); resp.Content.Headers.Add("Docker-Content-Digest", indexDesc.Digest); return resp; - } resp.StatusCode = HttpStatusCode.NotFound; @@ -522,10 +531,10 @@ public async Task Repository_TagAsync() MediaType = OCIMediaTypes.ImageIndex, Size = index.Length }; - var gotIndex = new byte[indexDesc.Size]; + byte[]? gotIndex = null; var reference = "foobar"; - var func = (HttpRequestMessage req, CancellationToken cancellationToken) => + var func = async (HttpRequestMessage req, CancellationToken cancellationToken) => { var res = new HttpResponseMessage(); res.RequestMessage = req; @@ -560,7 +569,10 @@ public async Task Repository_TagAsync() return new HttpResponseMessage(HttpStatusCode.BadRequest); } - gotIndex = req.Content?.ReadAsByteArrayAsync().Result; + if (req.Content != null) + { + gotIndex = await req.Content.ReadAsByteArrayAsync(cancellationToken); + } res.Content.Headers.Add("Docker-Content-Digest", indexDesc.Digest); res.StatusCode = HttpStatusCode.Created; return res; @@ -594,10 +606,10 @@ public async Task Repository_PushReferenceAsync() MediaType = OCIMediaTypes.ImageIndex, Size = index.Length }; - var gotIndex = new byte[indexDesc.Size]; + byte[]? gotIndex = null; var reference = "foobar"; - var func = (HttpRequestMessage req, CancellationToken cancellationToken) => + var func = async (HttpRequestMessage req, CancellationToken cancellationToken) => { var res = new HttpResponseMessage(); res.RequestMessage = req; @@ -609,7 +621,10 @@ public async Task Repository_PushReferenceAsync() return new HttpResponseMessage(HttpStatusCode.BadRequest); } - gotIndex = req.Content?.ReadAsByteArrayAsync().Result; + if (req.Content != null) + { + gotIndex = await req.Content.ReadAsByteArrayAsync(); + } res.Content.Headers.Add("Docker-Content-Digest", indexDesc.Digest); res.StatusCode = HttpStatusCode.Created; return res; @@ -1556,7 +1571,7 @@ public async Task ManifestStore_PushAsync() }; byte[]? gotManifest = null; - var func = (HttpRequestMessage req, CancellationToken cancellationToken) => + var func = async (HttpRequestMessage req, CancellationToken cancellationToken) => { var res = new HttpResponseMessage(); res.RequestMessage = req; @@ -1570,7 +1585,7 @@ public async Task ManifestStore_PushAsync() if (req.Content?.Headers?.ContentLength != null) { var buf = new byte[req.Content.Headers.ContentLength.Value]; - req.Content.ReadAsByteArrayAsync().Result.CopyTo(buf, 0); + (await req.Content.ReadAsByteArrayAsync()).CopyTo(buf, 0); gotManifest = buf; } res.Content.Headers.Add("Docker-Content-Digest", new string[] { manifestDesc.Digest }); @@ -1766,7 +1781,7 @@ public async Task ManifestStore_ResolveAsync() Digest = CalculateDigest(content), Size = content.Length }; - + await Assert.ThrowsAsync(async () => await store.ResolveAsync(contentDesc.Digest, cancellationToken)); } @@ -1873,7 +1888,7 @@ public async Task ManifestStore_TagAsync() var gotIndex = new byte[index.Length]; var reference = "foobar"; - var func = (HttpRequestMessage req, CancellationToken cancellationToken) => + var func = async (HttpRequestMessage req, CancellationToken cancellationToken) => { var res = new HttpResponseMessage(); res.RequestMessage = req; @@ -1903,7 +1918,7 @@ public async Task ManifestStore_TagAsync() if (req.Content?.Headers?.ContentLength != null) { var buf = new byte[req.Content.Headers.ContentLength.Value]; - req.Content.ReadAsByteArrayAsync().Result.CopyTo(buf, 0); + (await req.Content.ReadAsByteArrayAsync()).CopyTo(buf, 0); gotIndex = buf; } @@ -1949,7 +1964,7 @@ public async Task ManifestStore_PushReferenceAsync() var gotIndex = new byte[index.Length]; var reference = "foobar"; - var func = (HttpRequestMessage req, CancellationToken cancellationToken) => + var func = async (HttpRequestMessage req, CancellationToken cancellationToken) => { var res = new HttpResponseMessage(); res.RequestMessage = req; @@ -1965,7 +1980,7 @@ public async Task ManifestStore_PushReferenceAsync() if (req.Content?.Headers?.ContentLength != null) { var buf = new byte[req.Content.Headers.ContentLength.Value]; - req.Content.ReadAsByteArrayAsync().Result.CopyTo(buf, 0); + (await req.Content.ReadAsByteArrayAsync()).CopyTo(buf, 0); gotIndex = buf; } @@ -2005,7 +2020,7 @@ public async Task CopyFromRepositoryToMemory() { var res = new HttpResponseMessage(); res.RequestMessage = req; - var path = req.RequestUri != null? req.RequestUri.AbsolutePath : string.Empty; + var path = req.RequestUri != null ? req.RequestUri.AbsolutePath : string.Empty; var method = req.Method; if (path.Contains("/blobs/uploads/") && method == HttpMethod.Post) { @@ -2074,7 +2089,7 @@ public async Task CopyFromRepositoryToMemory() var reg = new Registry("localhost:5000"); reg.HttpClient = CustomClient(func); var src = await reg.Repository("source", CancellationToken.None); - + var dst = new MemoryTarget(); var tagName = "latest"; var desc = await Copy.CopyAsync(src, tagName, dst, tagName, CancellationToken.None);