Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
commit-message:
prefix: ci
cooldown:
default-days: 7
- package-ecosystem: gitsubmodule
directory: /
schedule:
interval: weekly
commit-message:
prefix: test
cooldown:
default-days: 7
34 changes: 23 additions & 11 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,32 @@ name: CI

on:
push:
branches:
- main
pull_request:

env:
GO111MODULE: 'on'
permissions:
contents: read

# Superseded pull request runs are worthless; main runs still upload coverage.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Go
uses: actions/setup-go@v5
- name: Lint Go Code
uses: golangci/golangci-lint-action@v6
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: stable
cache: false # golangci-lint-action manages its own cache
- name: Lint Go code
uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0

test:
name: Test
Expand All @@ -28,15 +38,17 @@ jobs:
go-version: ['stable', 'oldstable']
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
submodules: true
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: ${{ matrix.go-version }}
- name: Test
run: go test -race ${{ matrix.go-version == 'stable' && '-covermode atomic -coverprofile=profile.cov' || ''}}
run: go test -race ${{ matrix.go-version == 'stable' && '-covermode atomic -coverprofile=profile.cov' || '' }}
- name: Upload coverage results
if: matrix.go-version == 'stable'
uses: shogo82148/actions-goveralls@v1
uses: shogo82148/actions-goveralls@77a1912dca42260ee3e97f61bd13ea7ef40baa93 # v1.11.2
with:
path-to-profile: profile.cov
40 changes: 40 additions & 0 deletions .github/workflows/dependabot.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Dependabot Auto-Merge

on:
pull_request_target:
branches:
- main

permissions: {}

jobs:
auto-merge:
runs-on: ubuntu-latest
environment: release
if: github.event.pull_request.user.login == 'dependabot[bot]'
steps:
# The default GITHUB_TOKEN lacks the `workflows` scope and is refused on
# PRs that touch .github/workflows; use the release app token instead.
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
id: app-token
with:
app-id: ${{ vars.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
permission-contents: write
permission-pull-requests: write
permission-workflows: write
- name: Fetch Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0
with:
github-token: ${{ steps.app-token.outputs.token }}
# Only minor and patch GitHub Actions bumps auto-merge; majors and the
# test suite submodule wait for a human review.
- name: Auto-merge minor and patch GitHub Actions updates
if: steps.metadata.outputs.package-ecosystem == 'github_actions' && steps.metadata.outputs.update-type != 'version-update:semver-major'
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
PR_URL: ${{ github.event.pull_request.html_url }}
run: |
gh pr review --approve "${PR_URL}"
gh pr merge --auto --squash "${PR_URL}"
6 changes: 3 additions & 3 deletions dictionary.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
// Dictionary is an ordered map of name-value pairs.
// See https://httpwg.org/specs/rfc9651.html#dictionary
// Values can be:
// * Item (Section 3.3.)
// * Inner List (Section 3.1.1.)
// - Item (Section 3.3.)
// - Inner List (Section 3.1.1.)
type Dictionary struct {
names []string
values map[string]Member
Expand Down Expand Up @@ -104,7 +104,7 @@ func (d *Dictionary) marshalSFV(b *strings.Builder) error {
// https://httpwg.org/specs/rfc9651.html#parse-dictionary.
func UnmarshalDictionary(v []string) (*Dictionary, error) {
s := &scanner{
data: strings.Join(v, ","),
data: strings.Join(v, ", "),
}

s.scanWhileSp()
Expand Down
103 changes: 61 additions & 42 deletions httpwg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,25 +141,68 @@ func valToDictionary(e interface{}) *Dictionary {
return d
}

func TestOfficialTestSuiteParsing(t *testing.T) {
const dir = "structured-field-tests/"
f, _ := os.Open(dir)
files, _ := f.Readdir(-1)
// listTestFiles returns the JSON test suite files in dir.
func listTestFiles(tb testing.TB, dir string) []string {
tb.Helper()

for _, fi := range files {
n := fi.Name()
if !strings.HasSuffix(n, ".json") {
continue
f, err := os.Open(dir)
if err != nil {
tb.Fatalf("%s: %s (is the structured-field-tests submodule checked out?)", dir, err)
}

defer func() { _ = f.Close() }()

entries, err := f.Readdir(-1)
if err != nil {
tb.Fatalf("%s: %s", dir, err)
}

var names []string

for _, fi := range entries {
if strings.HasSuffix(fi.Name(), ".json") {
names = append(names, fi.Name())
}
}

if len(names) == 0 {
tb.Fatalf("%s: no JSON test file found (is the structured-field-tests submodule checked out?)", dir)
}

file, _ := os.Open(dir + n)
dec := json.NewDecoder(file)
dec.UseNumber()
return names
}

var tests []test
_ = dec.Decode(&tests)
// loadTests decodes the test cases contained in the given test suite file.
func loadTests(tb testing.TB, path string) []test {
tb.Helper()

for _, te := range tests {
file, err := os.Open(path)
if err != nil {
tb.Fatalf("%s: %s (is the structured-field-tests submodule checked out?)", path, err)
}

defer func() { _ = file.Close() }()

dec := json.NewDecoder(file)
dec.UseNumber()

var tests []test
if err := dec.Decode(&tests); err != nil {
tb.Fatalf("%s: %s", path, err)
}

if len(tests) == 0 {
tb.Fatalf("%s: no test case found", path)
}

return tests
}

func TestOfficialTestSuiteParsing(t *testing.T) {
const dir = "structured-field-tests/"

for _, n := range listTestFiles(t, dir) {
for _, te := range loadTests(t, dir+n) {
t.Run(n+"/"+te.Name, func(t *testing.T) {
var (
expected, got StructuredFieldValue
Expand Down Expand Up @@ -201,11 +244,7 @@ func TestOfficialTestSuiteParsing(t *testing.T) {
}

func BenchmarkParsingOfficialExamples(b *testing.B) {
file, _ := os.Open("structured-field-tests/examples.json")
dec := json.NewDecoder(file)

var tests []test
_ = dec.Decode(&tests)
tests := loadTests(b, "structured-field-tests/examples.json")

for n := 0; n < b.N; n++ {
for _, te := range tests {
Expand All @@ -222,12 +261,7 @@ func BenchmarkParsingOfficialExamples(b *testing.B) {
}

func BenchmarkSerializingOfficialExamples(b *testing.B) {
file, _ := os.Open("structured-field-tests/examples.json")
dec := json.NewDecoder(file)
dec.UseNumber()

var tests []test
_ = dec.Decode(&tests)
tests := loadTests(b, "structured-field-tests/examples.json")

var sfv []StructuredFieldValue

Expand Down Expand Up @@ -258,23 +292,8 @@ func TestOfficialTestSuiteSerialization(t *testing.T) {

const dir = "structured-field-tests/serialisation-tests/"

f, _ := os.Open(dir)
files, _ := f.Readdir(-1)

for _, fi := range files {
n := fi.Name()
if !strings.HasSuffix(n, ".json") {
continue
}

file, _ := os.Open(dir + n)
dec := json.NewDecoder(file)
dec.UseNumber()

var tests []test
_ = dec.Decode(&tests)

for _, te := range tests {
for _, n := range listTestFiles(t, dir) {
for _, te := range loadTests(t, dir+n) {
var sfv StructuredFieldValue

switch te.HeaderType {
Expand Down
2 changes: 1 addition & 1 deletion item.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (i Item) marshalSFV(b *strings.Builder) error {
// https://httpwg.org/specs/rfc9651.html#parse-item.
func UnmarshalItem(v []string) (Item, error) {
s := &scanner{
data: strings.Join(v, ","),
data: strings.Join(v, ", "),
}

s.scanWhileSp()
Expand Down
2 changes: 1 addition & 1 deletion item_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func TestUnmarshalItem(t *testing.T) {
}{
{[]string{"?1;foo;*bar=tok"}, i1, false},
{[]string{" ?1;foo;*bar=tok "}, i1, false},
{[]string{`"foo`, `bar"`}, NewItem("foo,bar"), false},
{[]string{`"foo`, `bar"`}, NewItem("foo, bar"), false},
{[]string{"é", ""}, Item{}, true},
{[]string{"tok;é"}, Item{}, true},
{[]string{" ?1;foo;*bar=tok é"}, Item{}, true},
Expand Down
2 changes: 1 addition & 1 deletion list.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (l List) marshalSFV(b *strings.Builder) error {
// https://httpwg.org/specs/rfc9651.html#parse-list.
func UnmarshalList(v []string) (List, error) {
s := &scanner{
data: strings.Join(v, ","),
data: strings.Join(v, ", "),
}

s.scanWhileSp()
Expand Down