Compare commits

..

1 Commits

Author SHA1 Message Date
Viktor Scharf
53d0bb467b fix issue-2146 2026-01-15 16:57:26 +01:00
49 changed files with 1744 additions and 2166 deletions

View File

@@ -3,83 +3,79 @@
### Prerequisites
* [ ] DEV/QA: bump web version
* [ ] DEV/QA: bump reva version
* [ ] DEV/QA: DEV: Create rc tag `vx.y.z-rc.x`
* [ ] DEV: update introductionVersion
* [ ] DEV: add new production version
* [ ] DEV/QA: Kickoff meeting [Kickoff meeting] (https://???)
* [ ] DEV/QA: Define client versions and provide list of breaking changes for desktop/mobile team
* [ ] DEV/QA: Check new strings and align with clients
* [ ] DEV/DOCS: Create list of pending docs tasks
* [ ] DEV: Create branch `release-x.x.x-rc.x` -> CODEFREEZE
* [ ] DEV: bump opencloud version in necessary files
* [ ] DEV: `changelog/CHANGELOG.tmpl`
* [ ] DEV: `pkg/version/version.go`
* [ ] DEV: `sonar-project.properties`
* [ ] DEV: prepare changelog folder in `changelog/x.x.x_????_??_??`
* [ ] DEV: Check successful CI run on release branch
* [ ] DEV: Create signed tag `vx.y.z-rc.x`
* [ ] DEV: Check successful CI run on `vx.y.z-rc.x` tag / BLOCKING for all further activity
* [ ] DEV: Merge back release branch
* [ ] DEV: bump released deployments to `vx.y.z-rc.x`
* [ ] DEV: https://cloud.opencloud.eu/
* [ ] DEV: needs snapshot and migration
### QA Phase
* [ ] QA: Compatibility test with posix fs
* [ ] QA: Compatibility test with decomposed fs
* [ ] DEV/QA: Performance test
* [ ] STORAGE_USERS_DRIVER=posix
* [ ] 75vu's, 60m
* [ ] 75vu's, 60m
* [ ] STORAGE_USERS_DRIVER=decomposed
* [ ] 75vu's, 60m
* [ ] 75vu's, 60m
* [ ] QA: documentation test
* [ ] QA: Review documentation
* [ ] QA: Verify all new features documented
* [ ] QA: Create upgrade documentation
* [ ] QA: Check installation guides
* [ ] QA: Confirmatory testing (if needed)
* [ ] QA: [Compatibility test](???)
* [ ] QA: [Performance test](https://github.com/opencloud-eu/cdperf/tree/main/packages/k6-tests/src)
* [ ] QA: Documentation test:
* [ ] QA: Single binary - setup
* [ ] QA: Docker - setup
* [ ] QA: Docker-compose - setup
* [ ] QA: helm/k8s - setup
* [ ] QA: e2e with different deployment:
* [ ] QA: [wopi](???.works)
* [ ] QA: [traefik](???.works)
* [ ] QA: [ldap](???.works)
* [ ] QA: e2e with different storage:
* [ ] QA: decomposed
* [ ] QA: decomposeds3
* [ ] QA: posix
* [ ] QA: posix with enabled watch_fs
* [ ] QA: e2e with different deployments deployments:
* [ ] QA: e2e tests agains opencloud-charts
* [ ] QA: binary
* [ ] QA: multitanacy
* [ ] QA: docker using [docker-compose_test_plan](https://github.com/opencloud-eu/qa/blob/main/.github/ISSUE_TEMPLATE/docker-compose_test_plan_template.md)
* [ ] QA: local
* [ ] QA: nfs
* [ ] QA: s3
* [ ] QA: Different clients:
* [ ] QA: desktop (define version) https://github.com/opencloud-eu/client/releases
* [ ] QA: against mac - exploratory testing
* [ ] QA: against windows - exploratory testing
* [ ] QA: against mac - smoke test
* [ ] QA: against windows - smoke test
* [ ] QA: against linux (use auto tests)
* [ ] QA: android (define version) https://github.com/opencloud-eu/android/releases
* [ ] QA: ios (define version)
* [ ] QA: check docs german translation
* [ ] QA: german translations desktop at 100%
* [ ] QA: exploratory testing
* [ ] QA: [Smoke test](???) on Web Office (Collabora, Onlyoffice, Microsoft office)
* [ ] QA: Smoke test Hello extension
* [ ] QA: [Smoke test](???) ldap
* [ ] QA: Collecting errors found
### Collected bugs
* [ ] Please place all bugs found here
### After QA Phase
### After QA Phase (IT related)
* [ ] Brief company-wide heads up via mail @tbsbdr
* [ ] Create list of changed ENV vars and send to release-coordination@opencloud.eu
* [ ] Variable Name
* [ ] Introduced in version
* [ ] Default Value
* [ ] Description
* [ ] dependencies with user other components
* [ ] DEV: Create branch `release-x.x.x`
* [ ] DEV: bump OpenCloud version in necessary files
* [ ] DEV: `pkg/version/version.go`
* [ ] DEV: `sonar-project.properties`
* [ ] DEV: released deployment versions
* [ ] DEV: prepare changelog folder in `changelog/x.x.x_???`
* [ ] Release Notes + Breaking Changes @tbsbdr
* [ ] Migration + Breaking Changes Admin Doc @???
* [ ] DEV: Create final signed tag
* [ ] DEV: Check successful CI run on `vx.y.z` tag / BLOCKING for all further activity
* [ ] Merge release notes
* [ ] QA:bump version in pkg/version.go
* [ ] QA: Run CI
* [ ] DEV/QA: create final tag
* [ ] QA: observe CI Run on tag
* [ ] DEV/QA: Create a new `stable-*` branch
* [ ] (opencloud)[https://github.com/opencloud-eu/opencloud/branches]
* [ ] (web)[https://github.com/opencloud-eu/web/branches]
* [ ] (reva)[https://github.com/opencloud-eu/reva/branches]
* [ ] (opencloud-compose)[https://github.com/opencloud-eu/opencloud-compose/branches]
* [ ] DEV/QA:: publish release notes to the docs
* [ ] DEV/QA:: update (demo.opencloud.eu)[https://demo.opencloud.eu/]
### After QA Phase ( Marketing / Product / Sales related )
* [ ] notify marketing that the release is ready @tbsbdr
* [ ] announce in the public channel (matrix channel)[https://matrix.to/#/#opencloud:matrix.org]
* [ ] press information @AnneGo137
* [ ] press information @AnneGo137
* [ ] Blogentry @AnneGo137
* [ ] Internal meeting (Groupe Pre-Webinar) @db-ot
* [ ] Partner briefing (Partner should be informed about features, new) @matthias
* [ ] Webinar DE & EN @AnneGo137
* [ ] Präsentation DE @tbsbdr / @db-ot
* [ ] Präsentation EN @tbsbdr / @db-ot
* [ ] Website ergänzen @AnneGo137
* [ ] Features @AnneGo137
* [ ] Service & Support - New Enterprise Features @tbsbdr
* [ ] OpenCloud_Benefits.pdf updates @AnneGo137
* [ ] Welcome Files: Features as media @tbsbdr
* [ ] Flyer update @AnneGo137
* [ ] Sales presentation @matthias
### Post-release communication
* [ ] DEV: Create a `docs-stable-x.y` branch based on the docs folder in the OpenCloud repo @micbar
* [ ] DEV/QA: Ping documentation in RC about the new release tag (for opencloud/helm chart version bump in docs)
* [ ] DEV/QA: Ping marketing to update all download links (download mirrors are updated at the full hour, wait with ping until download is actually available)
* [ ] DEV/QA: Ping @??? once the demo instances are running this release
* [ ] DEV: Merge back release branch
* [ ] DEV: Create stable-x.y branch in the OpenCloud repo from final tag

View File

@@ -1314,7 +1314,7 @@ def localApiTestPipeline(ctx):
def localApiTest(suites, storage = "decomposed", extra_environment = {}, with_remote_php = False, generate_virus_files = False):
test_dir = "%s/tests/acceptance" % dirs["base"]
expected_failures_file = "%s/expected-failures-%s-storage.md" % (test_dir, storage)
expected_failures_file = "%s/expected-failures-localAPI-on-%s-storage.md" % (test_dir, storage)
environment = {
"TEST_SERVER_URL": OC_URL,
@@ -1440,7 +1440,7 @@ def coreApiTestPipeline(ctx):
def coreApiTest(part_number = 1, number_of_parts = 1, with_remote_php = False, storage = "posix"):
filter_tags = "~@skipOnOpencloud-%s-Storage" % storage
test_dir = "%s/tests/acceptance" % dirs["base"]
expected_failures_file = "%s/expected-failures-%s-storage.md" % (test_dir, storage)
expected_failures_file = "%s/expected-failures-API-on-%s-storage.md" % (test_dir, storage)
return [{
"name": "api-tests",
@@ -3293,7 +3293,7 @@ def wopiCollaborationService(name):
environment["COLLABORATION_APP_ADDR"] = "https://onlyoffice"
environment["COLLABORATION_APP_ICON"] = "https://onlyoffice/web-apps/apps/documenteditor/main/resources/img/favicon.ico"
elif name == "fakeoffice":
environment["COLLABORATION_SERVICE_NAME"] = "collaboration-fakeoffice"
environment["COLLABORATION_SERVICE_NAME"] = "collboration-fakeoficce"
environment["COLLABORATION_APP_NAME"] = "FakeOffice"
environment["COLLABORATION_APP_PRODUCT"] = "Microsoft"
environment["COLLABORATION_APP_ADDR"] = "http://fakeoffice:8080"

14
go.mod
View File

@@ -41,13 +41,13 @@ require (
github.com/google/uuid v1.6.0
github.com/gookit/config/v2 v2.2.7
github.com/gorilla/mux v1.8.1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3
github.com/invopop/validation v0.8.0
github.com/jellydator/ttlcache/v2 v2.11.1
github.com/jellydator/ttlcache/v3 v3.4.0
github.com/jinzhu/now v1.1.5
github.com/justinas/alice v1.2.0
github.com/kovidgoyal/imaging v1.8.19
github.com/kovidgoyal/imaging v1.8.18
github.com/leonelquinteros/gotext v1.7.2
github.com/libregraph/idm v0.5.0
github.com/libregraph/lico v0.66.0
@@ -57,7 +57,7 @@ require (
github.com/nats-io/nats-server/v2 v2.12.3
github.com/nats-io/nats.go v1.48.0
github.com/oklog/run v1.2.0
github.com/olekukonko/tablewriter v1.1.3
github.com/olekukonko/tablewriter v1.1.2
github.com/onsi/ginkgo v1.16.5
github.com/onsi/ginkgo/v2 v2.27.5
github.com/onsi/gomega v1.39.0
@@ -110,7 +110,7 @@ require (
golang.org/x/sync v0.19.0
golang.org/x/term v0.39.0
golang.org/x/text v0.33.0
google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217
google.golang.org/grpc v1.78.0
google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v2 v2.4.0
@@ -165,7 +165,7 @@ require (
github.com/ceph/go-ceph v0.37.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73 // indirect
github.com/clipperhouse/displaywidth v0.6.2 // indirect
github.com/clipperhouse/displaywidth v0.6.0 // indirect
github.com/clipperhouse/stringish v0.1.1 // indirect
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
@@ -312,7 +312,7 @@ require (
github.com/nxadm/tail v1.4.8 // indirect
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0 // indirect
github.com/olekukonko/ll v0.1.3 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
@@ -395,7 +395,7 @@ require (
golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.40.0 // indirect
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect

28
go.sum
View File

@@ -223,8 +223,8 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/clipperhouse/displaywidth v0.6.2 h1:ZDpTkFfpHOKte4RG5O/BOyf3ysnvFswpyYrV7z2uAKo=
github.com/clipperhouse/displaywidth v0.6.2/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
github.com/clipperhouse/displaywidth v0.6.0 h1:k32vueaksef9WIKCNcoqRNyKbyvkvkysNYnAWz2fN4s=
github.com/clipperhouse/displaywidth v0.6.0/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
@@ -624,8 +624,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vb
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 h1:kEISI/Gx67NzH3nJxAmY/dGac80kKZgZt134u7Y/k1s=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4/go.mod h1:6Nz966r3vQYCqIzWsuEl9d7cf7mRhtDmm++sOxlnfxI=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
@@ -745,8 +745,8 @@ github.com/kovidgoyal/go-parallel v1.1.1 h1:1OzpNjtrUkBPq3UaqrnvOoB2F9RttSt811ui
github.com/kovidgoyal/go-parallel v1.1.1/go.mod h1:BJNIbe6+hxyFWv7n6oEDPj3PA5qSw5OCtf0hcVxWJiw=
github.com/kovidgoyal/go-shm v1.0.0 h1:HJEel9D1F9YhULvClEHJLawoRSj/1u/EDV7MJbBPgQo=
github.com/kovidgoyal/go-shm v1.0.0/go.mod h1:Yzb80Xf9L3kaoB2RGok9hHwMIt7Oif61kT6t3+VnZds=
github.com/kovidgoyal/imaging v1.8.19 h1:zWJdQqF2tfSKjvoB7XpLRhVGbYsze++M0iaqZ4ZkhNk=
github.com/kovidgoyal/imaging v1.8.19/go.mod h1:I0q8RdoEuyc4G8GFOF9CaluTUHQSf68d6TmsqpvfRI8=
github.com/kovidgoyal/imaging v1.8.18 h1:42JCqJnQBzBo0hGllLEJVYDARWXPP9MT3HgiTno9Chc=
github.com/kovidgoyal/imaging v1.8.18/go.mod h1:bqjHpeAxSuTLvKob6HuqAr9td2wP9G54Snbgd+1QLoU=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -940,11 +940,11 @@ github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0 h1:jrYnow5+hy3WRDCBypUFvVKNSPPCdqgSXIE9eJDD8LM=
github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew=
github.com/olekukonko/ll v0.1.3 h1:sV2jrhQGq5B3W0nENUISCR6azIPf7UBUpVq0x/y70Fg=
github.com/olekukonko/ll v0.1.3/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/olekukonko/tablewriter v1.1.3 h1:VSHhghXxrP0JHl+0NnKid7WoEmd9/urKRJLysb70nnA=
github.com/olekukonko/tablewriter v1.1.3/go.mod h1:9VU0knjhmMkXjnMKrZ3+L2JhhtsQ/L38BbL3CRNE8tM=
github.com/olekukonko/tablewriter v1.1.2 h1:L2kI1Y5tZBct/O/TyZK1zIE9GlBj/TVs+AY5tZDCDSc=
github.com/olekukonko/tablewriter v1.1.2/go.mod h1:z7SYPugVqGVavWoA2sGsFIoOVNmEHxUAAMrhXONtfkg=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
@@ -1744,10 +1744,10 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b h1:uA40e2M6fYRBf0+8uN5mLlqUtV192iiksiICIBkYJ1E=
google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:Xa7le7qx2vmqB/SzWUBa7KdMjpdpAHlh5QCSnjessQk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=

View File

@@ -1,11 +1,6 @@
# Webfinger
The webfinger service provides an RFC7033 WebFinger lookup of OpenCloud resources, relevant for a given user account at the /.well-known/webfinger enpoint.
1. An [OpenID Connect Discovery](#openid-connect-discovery) for the IdP, based on the OpenCloud URL.
2. An [Authenticated Instance Discovery](#authenticated-instance-discovery), based on the user account.
These two request are only needed for discovery.
The webfinger service provides an RFC7033 WebFinger lookup of OpenCloud instances relevant for a given user account via endpoints a the /.well-known/webfinger implementation.
## OpenID Connect Discovery
@@ -23,7 +18,7 @@ Clients can make an unauthenticated `GET https://drive.opencloud.test/.well-know
}
```
Here, the `resource` takes the instance domain URI, but an `acct:` URI works as well.
Here, the `resource` takes the instance domain URI, but an `acct:` URI works as well.
## Authenticated Instance Discovery
@@ -63,14 +58,14 @@ webfinger:
- claim: email
regex: alan@example\.org
href: "https://{{.preferred_username}}.cloud.opencloud.test"
title:
title:
"en": "OpenCloud Instance for Alan"
"de": "OpenCloud Instanz für Alan"
break: true
- claim: "email"
regex: mary@example\.org
href: "https://{{.preferred_username}}.cloud.opencloud.test"
title:
title:
"en": "OpenCloud Instance for Mary"
"de": "OpenCloud Instanz für Mary"
break: false

View File

@@ -4,129 +4,166 @@ To run tests in the test suite you have two options. You may go the easy way and
Both ways to run tests with the test suites are described here.
## Table of Contents
- [Running Test Suite in Docker](#running-test-suite-in-docker)
- [Running API Tests](#running-api-tests)
- [Run Tests With Required Services](#run-tests-with-required-services)
- [Run Tests Only](#run-tests-only)
- [Skip Local Image Build While Running Tests](#skip-local-image-build-while-running-tests)
- [Check Test Logs](#check-test-logs)
- [Cleanup the Setup](#cleanup-the-setup)
- [Running WOPI Validator Tests](#running-wopi-validator-tests)
- [Running Test Suite in Local Environment](#running-test-suite-in-local-environment)
- [Running Tests With And Without `remote.php`](#running-tests-with-and-without-remotephp)
- [Running ENV Config Tests (@env-Config)](#running-env-config-tests-env-config)
- [Running Test Suite With Email Service (@email)](#running-test-suite-with-email-service-email)
- [Running Test Suite With Tika Service (@tikaServiceNeeded)](#running-test-suite-with-tika-service-tikaserviceneeded)
- [Running Test Suite With Antivirus Service (@antivirus)](#running-test-suite-with-antivirus-service-antivirus)
- [Running Test Suite With Federated Sharing (@ocm)](#running-test-suite-with-federated-sharing-ocm)
- [Running Text Preview Tests Containing Unicode Characters](#running-text-preview-tests-containing-unicode-characters)
- [Running All API Tests Locally](#running-all-api-tests-locally)
## Running Test Suite in Docker
Check the available commands and environment variables with:
Let's see what is available. Invoke the following command from within the root of the OpenCloud repository.
```bash
make -C tests/acceptance/docker help
```
### Running API Tests
Basically we have two sources for feature tests and test suites:
#### Run Tests With Required Services
- [OpenCloud feature test and test suites](https://github.com/opencloud-eu/opencloud/tree/main/tests/acceptance/features)
- [tests and test suites transferred from core, they have prefix coreApi](https://github.com/opencloud-eu/opencloud/tree/main/tests/acceptance/features)
We can run a single feature or a single test suite with different storage drivers.
At the moment, both can be applied to OpenCloud.
1. Run a specific feature file:
As a storage backend, we support the OpenCloud native storage, also called `decomposed`. This stores files directly on disk. Along with that we also provide `decomposeds3`, `posix` storage drivers.
```bash
BEHAT_FEATURE='tests/acceptance/features/apiGraphUserGroup/createUser.feature' \
make -C tests/acceptance/docker run-api-tests
```
You can invoke two types of test suite runs:
or a single scenario in a feature:
- run a full test suite, which consists of multiple feature tests
- run a single feature or single scenario in a feature
```bash
BEHAT_FEATURE='tests/acceptance/features/apiGraphUserGroup/createUser.feature:24' \
make -C tests/acceptance/docker run-api-tests
```
### Run Full Test Suite
2. Run a specific test suite:
#### Local OpenCloud Tests (prefix `api`)
```bash
BEHAT_SUITE='apiGraphUserGroup' \
make -C tests/acceptance/docker run-api-tests
```
The names of the full test suite make targets have the same naming as in the CI pipeline. See the available local OpenCloud specific test suites [here](https://github.com/opencloud-eu/opencloud/tree/main/tests/acceptance/features). They can be run with `decomposed` storage, `decomposeds3` storage and `posix` storage
3. Run with different storage driver (default is `posix`):
```bash
STORAGE_DRIVER='posix' \
BEHAT_SUITE='apiGraphUserGroup' \
make -C tests/acceptance/docker run-api-tests
```
4. Run the tests that require an email server (tests tagged with `@email`). Provide `START_EMAIL=true` while running the tests:
```bash
START_EMAIL=true \
BEHAT_FEATURE='tests/acceptance/features/apiNotification/emailNotification.feature' \
make -C tests/acceptance/docker run-api-tests
```
5. Run the tests that require tika service (tests tagged with `@tikaServiceNeeded`). Provide `START_TIKA=true` while running the tests:
```bash
START_TIKA=true \
BEHAT_FEATURE='tests/acceptance/features/apiSearchContent/contentSearch.feature' \
make -C tests/acceptance/docker run-api-tests
```
6. Run the tests that require an antivirus service (tests tagged with `@antivirus`). Provide `START_ANTIVIRUS=true` while running the tests:
```bash
START_ANTIVIRUS=true \
BEHAT_FEATURE='tests/acceptance/features/apiAntivirus/antivirus.feature' \
make -C tests/acceptance/docker run-api-tests
```
7. Run the wopi tests. Provide `ENABLE_WOPI=true` while running the tests:
```bash
ENABLE_WOPI=true \
BEHAT_FEATURE='tests/acceptance/features/apiCollaboration/checkFileInfo.feature' \
make -C tests/acceptance/docker run-api-tests
```
#### Run Tests Only
If you want to re-run the tests because of some failures or any other reason, you can use the following command to run only the tests without starting the services again.
Also, this command can be used to run the tests against the already hosted OpenCloud server by providing the `TEST_SERVER_URL` and `USE_BEARER_TOKEN` environment variables.
> [!NOTE]
> You can utilize the following environment variables:
>
> - `BEHAT_FEATURE`
> - `BEHAT_SUITE`
> - `USE_BEARER_TOKEN`
> - `TEST_SERVER_URL`
For example, command:
```bash
BEHAT_FEATURE='tests/acceptance/features/apiGraphUserGroup/createUser.feature:24' \
make -C tests/acceptance/docker run-test-only
make -C tests/acceptance/docker localApiTests-apiGraph-decomposed
```
#### Skip Local Image Build While Running Tests
runs the same tests as the `localApiTests-apiGraph-decomposed` CI pipeline, which runs the OpenCloud test suite "apiGraph" against the OpenCloud server with `decomposed` storage.
While running the tests, opencloud docker image is built with `opencloudeu/opencloud:dev` tag. If you want to skip building the local image, you can use `OC_IMAGE_TAG` env which must contain an available docker tag of the [opencloudeu/opencloud registry on Docker Hub](https://hub.docker.com/r/opencloudeu/opencloud) (e.g. 'latest').
command:
```bash
make -C tests/acceptance/docker localApiTests-apiGraph-decomposeds3
```
runs the OpenCloud test suite `apiGraph` against the OpenCloud server with `decomposeds3` storage.
And command:
```bash
make -C tests/acceptance/docker localApiTests-apiGraph-posix
```
runs the OpenCloud test suite `apiGraph` against the OpenCloud server with `posix` storage.
Note:
While running the tests, OpenCloud server is started with [ocwrapper](https://github.com/opencloud-eu/opencloud/blob/main/tests/ocwrapper/README.md) (i.e. `WITH_WRAPPER=true`) by default. In order to run the tests without ocwrapper, provide `WITH_WRAPPER=false` when running the tests. For example:
```bash
WITH_WRAPPER=false \
BEHAT_FEATURE='tests/acceptance/features/apiGraphUserGroup/createUser.feature:26' \
make -C tests/acceptance/docker test-opencloud-feature-decomposed-storage
```
But some test suites that are tagged with `@env-config` require the OpenCloud server to be run with ocwrapper. So, running those tests require `WITH_WRAPPER=true` (default setting).
Note:
To run the tests that require an email server (tests tagged with `@email`), you need to provide `START_EMAIL=true` while running the tests.
```bash
START_EMAIL=true \
BEHAT_FEATURE='tests/acceptance/features/apiNotification/emailNotification.feature' \
make -C tests/acceptance/docker test-opencloud-feature-decomposed-storage
```
Note:
To run the tests that require tika service (tests tagged with `@tikaServiceNeeded`), you need to provide `START_TIKA=true` while running the tests.
```bash
START_TIKA=true \
BEHAT_FEATURE='tests/acceptance/features/apiSearchContent/contentSearch.feature' \
make -C tests/acceptance/docker test-opencloud-feature-decomposed-storage
```
Note:
To run the tests that require an antivirus service (tests tagged with `@antivirus`), you need to provide the following environment variables while running the tests.
```bash
START_ANTIVIRUS=true \
OC_ASYNC_UPLOADS=true \
OC_ADD_RUN_SERVICES=antivirus \
POSTPROCESSING_STEPS=virusscan \
BEHAT_FEATURE='tests/acceptance/features/apiAntivirus/antivirus.feature' \
make -C tests/acceptance/docker test-opencloud-feature-decomposed-storage
```
#### Tests Transferred From Core (prefix `coreApi`)
Command `make -C tests/acceptance/docker Core-API-Tests-decomposed-storage-3` runs the same tests as the `Core-API-Tests-decomposed-storage-3` CI pipeline, which runs the third (out of ten) test suite groups transferred from core against the OpenCloud server with `decomposed` storage.
And `make -C tests/acceptance/docker Core-API-Tests-decomposeds3-storage-3` runs the third (out of ten) test suite groups transferred from core against the OpenCloud server with `decomposeds3` storage.
### Run Single Feature Test
The tests for a single feature (a feature file) can also be run against the different storage backends. To do that, multiple make targets with the schema **test-_\<test-source\>_-feature-_\<storage-backend\>_** are available. To select a single feature you have to add an additional `BEHAT_FEATURE=<path-to-feature-file>` parameter when invoking the make command.
For example;
```bash
BEHAT_FEATURE='tests/acceptance/features/apiGraphUserGroup/createUser.feature' \
make -C tests/acceptance/docker test-opencloud-feature-decomposed-storage
```
Note:
`BEHAT_FEATURE` must be pointing to a valid feature file
And to run a single scenario in a feature, you can do:
Note:
A specific scenario from a feature can be run by adding `:<line-number>` at the end of the feature file path. For example, to run the scenario at line 26 of the feature file `apiGraphUserGroup/createUser.feature`, simply add the line number like this: `apiGraphUserGroup/createUser.feature:26`. Note that the line numbers mentioned in the examples might not always point to a scenario, so always check the line numbers before running the test.
```bash
BEHAT_FEATURE='tests/acceptance/features/apiGraphUserGroup/createUser.feature:26' \
make -C tests/acceptance/docker test-opencloud-feature-decomposed-storage
```
Similarly, with `decomposeds3` storage;
```bash
# run a whole feature
BEHAT_FEATURE='tests/acceptance/features/apiGraphUserGroup/createUser.feature' \
make -C tests/acceptance/docker test-opencloud-feature-decomposeds3-storage
# run a single scenario
BEHAT_FEATURE='tests/acceptance/features/apiGraphUserGroup/createUser.feature:26' \
make -C tests/acceptance/docker test-opencloud-feature-decomposeds3-storage
```
In the same way, tests transferred from core can be run as:
```bash
# run a whole feature
BEHAT_FEATURE='tests/acceptance/features/coreApiAuth/webDavAuth.feature' \
make -C tests/acceptance/docker test-core-feature-decomposed-storage
# run a single scenario
BEHAT_FEATURE='tests/acceptance/features/coreApiAuth/webDavAuth.feature:15' \
make -C tests/acceptance/docker test-core-feature-decomposed-storage
```
Note:
The test suites transferred from core have `coreApi` prefixed
### OpenCloud Image to Be Tested (Skip Local Image Build)
By default, the tests will be run against the docker image built from your current working state of the OpenCloud repository. For some purposes it might also be handy to use an OpenCloud image from Docker Hub. Therefore, you can provide the optional flag `OC_IMAGE_TAG=...` which must contain an available docker tag of the [opencloud-eu/opencloud registry on Docker Hub](https://hub.docker.com/r/opencloud-eu/opencloud) (e.g. 'latest').
```bash
OC_IMAGE_TAG=latest \
BEHAT_FEATURE='tests/acceptance/features/apiGraphUserGroup/createUser.feature' \
make -C tests/acceptance/docker run-api-tests
make -C tests/acceptance/docker localApiTests-apiGraph-opencloud
```
#### Check Test Logs
### Test Log Output
While a test is running or when it is finished, you can attach to the logs generated by the tests.
@@ -134,49 +171,15 @@ While a test is running or when it is finished, you can attach to the logs gener
make -C tests/acceptance/docker show-test-logs
```
> [!NOTE]
> The log output is opened in `less`. You can navigate up and down with your cursors. By pressing "F" you can follow the latest line of the output.
Note:
The log output is opened in `less`. You can navigate up and down with your cursors. By pressing "F" you can follow the latest line of the output.
#### Cleanup the Setup
### Cleanup
Run the following command to clean all the resources created while running the tests:
During testing we start a redis and OpenCloud docker container. These will not be stopped automatically. You can stop them with:
```bash
make -C tests/acceptance/docker clean-all
```
### Running WOPI Validator Tests
#### Available Test Groups
```text
BaseWopiViewing
CheckFileInfoSchema
EditFlows
Locks
AccessTokens
GetLock
ExtendedLockLength
FileVersion
Features
PutRelativeFile
RenameFileIfCreateChildFileIsNotSupported
```
#### Run Test
```bash
TEST_GROUP=BaseWopiViewing docker compose -f tests/acceptance/docker/src/wopi-validator-test.yml up -d
```
#### Run Test (macOS)
Use the arm image for macOS to run the validator tests.
```bash
WOPI_VALIDATOR_IMAGE=scharfvi/wopi-validator \
TEST_GROUP=BaseWopiViewing \
docker compose -f tests/acceptance/docker/src/wopi-validator-test.yml up -d
make -C tests/acceptance/docker clean
```
## Running Test Suite in Local Environment
@@ -242,7 +245,7 @@ A specific scenario from a feature can be run by adding `:<line-number>` at the
### Use Existing Tests for BDD
As a lot of scenarios are written for core, we can use those tests for Behaviour driven development in OpenCloud.
Every scenario that does not work in OpenCloud with `decomposed` storage, is listed in `tests/acceptance/expected-failures-decomposed-storage.md` with a link to the related issue.
Every scenario that does not work in OpenCloud with `decomposed` storage, is listed in `tests/acceptance/expected-failures-API-on-decomposed-storage.md` with a link to the related issue.
Those scenarios are run in the ordinary acceptance test pipeline in CI. The scenarios that fail are checked against the
expected failures. If there are any differences then the CI pipeline fails.
@@ -266,7 +269,7 @@ If you want to work on a specific issue
5. remove those tests from the expected failures file
6. make a PR that has the fixed code, and the relevant lines removed from the expected failures file.
### Running Tests With And Without `remote.php`
## Running Tests With And Without `remote.php`
By default, the tests are run with `remote.php` enabled. If you want to run the tests without `remote.php`, you can disable it by setting the environment variable `WITH_REMOTE_PHP=false` while running the tests.
@@ -276,11 +279,11 @@ TEST_SERVER_URL="https://localhost:9200" \
make test-acceptance-api
```
### Running ENV Config Tests (@env-Config)
## Running ENV Config Tests (@env-Config)
Test suites tagged with `@env-config` are used to test the environment variables that are used to configure OpenCloud. These tests are special tests that require the OpenCloud server to be run using [ocwrapper](https://github.com/opencloud-eu/opencloud/blob/main/tests/ocwrapper/README.md).
#### Run OpenCloud With ocwrapper
### Run OpenCloud With ocwrapper
```bash
# working dir: OpenCloud repo root dir
@@ -298,7 +301,7 @@ PROXY_ENABLE_BASIC_AUTH=true \
./bin/ocwrapper serve --bin=../../opencloud/bin/opencloud
```
#### Run the Tests
### Run the Tests
```bash
OC_WRAPPER_URL=http://localhost:5200 \
@@ -307,7 +310,7 @@ BEHAT_FEATURE=tests/acceptance/features/apiAsyncUpload/delayPostprocessing.featu
make test-acceptance-api
```
#### Writing New ENV Config Tests
### Writing New ENV Config Tests
While writing tests for a new OpenCloud ENV configuration, please make sure to follow these guidelines:
@@ -315,11 +318,11 @@ While writing tests for a new OpenCloud ENV configuration, please make sure to f
2. Use `OcConfigHelper.php` for helper functions - provides functions to reconfigure the running OpenCloud instance.
3. Recommended: add the new step implementations in `OcConfigContext.php`
### Running Test Suite With Email Service (@email)
## Running Test Suite With Email Service (@email)
Test suites that are tagged with `@email` require an email service. We use inbucket as the email service in our tests.
#### Setup Inbucket
### Setup Inbucket
Run the following command to setup inbucket
@@ -327,7 +330,7 @@ Run the following command to setup inbucket
docker run -d -p9000:9000 -p2500:2500 --name inbucket inbucket/inbucket
```
#### Run OpenCloud
### Run OpenCloud
Documentation for environment variables is available [here](https://docs.opencloud.eu/services/notifications/#environment-variables)
@@ -346,7 +349,7 @@ NOTIFICATIONS_SMTP_SENDER="OpenCloud <noreply@example.com>" \
opencloud/bin/opencloud server
```
#### Run the Acceptance Test
### Run the Acceptance Test
Run the acceptance test with the following command:
@@ -358,11 +361,11 @@ BEHAT_FEATURE="tests/acceptance/features/apiNotification/emailNotification.featu
make test-acceptance-api
```
### Running Test Suite With Tika Service (@tikaServiceNeeded)
## Running Test Suite With Tika Service (@tikaServiceNeeded)
Test suites that are tagged with `@tikaServiceNeeded` require tika service.
#### Setup Tika Service
### Setup Tika Service
Run the following docker command to setup tika service
@@ -370,7 +373,7 @@ Run the following docker command to setup tika service
docker run -d -p 127.0.0.1:9998:9998 apache/tika
```
#### Run OpenCloud
### Run OpenCloud
TODO: Documentation related to the content based search and tika extractor will be added later.
@@ -388,7 +391,7 @@ SEARCH_EXTRACTOR_CS3SOURCE_INSECURE=true \
opencloud/bin/opencloud server
```
#### Run the Acceptance Test
### Run the Acceptance Test
Run the acceptance test with the following command:
@@ -398,15 +401,15 @@ BEHAT_FEATURE="tests/acceptance/features/apiSearchContent/contentSearch.feature"
make test-acceptance-api
```
### Running Test Suite With Antivirus Service (@antivirus)
## Running Test Suite With Antivirus Service (@antivirus)
Test suites that are tagged with `@antivirus` require antivirus service. TODO The available antivirus and the configuration related to them will be added latert. This documentation is only going to use `clamav` as antivirus.
#### Setup clamAV
### Setup clamAV
**Option 1. Setup Locally**
#### 1. Setup Locally
Linux OS user:
##### Linux OS user
Run the following command to set up calmAV and clamAV daemon
@@ -423,7 +426,7 @@ sudo service clamav-daemon status
Note:
The commands are ubuntu specific and may differ according to your system. You can find information related to installation of clamAV in their official documentation [here](https://docs.clamav.net/manual/Installing/Packages.html).
Mac OS user:
##### Mac OS user
Install ClamAV using [here](https://gist.github.com/mendozao/3ea393b91f23a813650baab9964425b9)
Start ClamAV daemon
@@ -432,7 +435,7 @@ Start ClamAV daemon
/your/location/to/brew/Cellar/clamav/1.1.0/sbin/clamd
```
**Option 2. Setup clamAV With Docker**
#### 2. Setup clamAV With Docker
Run `clamAV` through docker
@@ -440,7 +443,7 @@ Run `clamAV` through docker
docker run -d -p 3310:3310 opencloudeu/clamav-ci:latest
```
#### Run OpenCloud
### Run OpenCloud
As `antivirus` service is not enabled by default we need to enable the service while running OpenCloud server. We also need to enable `async upload` and as virus scan is performed in post-processing step, we need to set it as well. Documentation for environment variables related to antivirus is available [here](https://docs.opencloud.eu/services/antivirus/#environment-variables)
@@ -466,7 +469,7 @@ For antivirus running localy on Linux OS, use `ANTIVIRUS_CLAMAV_SOCKET= "/var/ru
For antivirus running localy on Mac OS, use `ANTIVIRUS_CLAMAV_SOCKET= "/tmp/clamd.sock"`.
For antivirus running with docker, use `ANTIVIRUS_CLAMAV_SOCKET= "tcp://host.docker.internal:3310"`
#### Create Virus Files
### Create virus files
The antivirus tests require EICAR test files which are not stored in the repository
They are generated dynamically when needed for testing.
@@ -485,11 +488,11 @@ BEHAT_FEATURE="tests/acceptance/features/apiAntivirus/antivirus.feature" \
make test-acceptance-api
```
### Running Test Suite With Federated Sharing (@ocm)
## Running Test Suite With Federated Sharing (@ocm)
Test suites that are tagged with `@ocm` require running two different OpenCloud instances. TODO More detailed information and configuration related to it will be added later.
#### Setup First OpenCloud Instance
### Setup First OpenCloud Instance
```bash
# init OpenCloud
@@ -511,15 +514,15 @@ opencloud/bin/opencloud server
The first OpenCloud instance should be available at: https://localhost:9200/
#### Setup Second OpenCloud Instance
### Setup Second OpenCloud Instance
You can run the second OpenCloud instance in two ways:
**Option 1. Using `.vscode/launch.json`**
#### Using `.vscode/launch.json`
From the `Run and Debug` panel of VSCode, select `Fed OpenCloud Server` and start the debugger.
**Option 2. Using env file**
#### Using env file
```bash
# init OpenCloud
@@ -547,7 +550,7 @@ BEHAT_FEATURE="tests/acceptance/features/apiOcm/ocm.feature" \
make test-acceptance-api
```
### Running Text Preview Tests Containing Unicode Characters
## Running Text Preview Tests Containing Unicode Characters
There are some tests that check the text preview of files containing Unicode characters. The OpenCloud server by default cannot generate the thumbnail of such files correctly but it provides an environment variable to allow the use of custom fonts that support Unicode characters. So to run such tests successfully, we have to run the OpenCloud server with this environment variable.
@@ -570,15 +573,46 @@ The sample `fontsMap.json` file is located in `tests/config/drone/fontsMap.json`
### Build dev docker
```bash
make -C opencloud dev-docker
make -C opencloud dev-docker
```
### Choose STORAGE_DRIVER
By default, the system uses `posix` storage. However, you can override this by setting the `STORAGE_DRIVER` environment variable.
### Run a script that starts the openCloud server in the docker and runs the API tests locally (for debugging purposes)
### Run a script that starts the openCloud server in the docker and runs the API tests locally (for debugging purposes)
```bash
STORAGE_DRIVER=posix ./tests/acceptance/run_api_tests.sh
STORAGE_DRIVER=posix ./tests/acceptance/run_api_tests.sh
```
## Running WOPI Validator Tests
### Available Test Groups
```text
BaseWopiViewing
CheckFileInfoSchema
EditFlows
Locks
AccessTokens
GetLock
ExtendedLockLength
FileVersion
Features
PutRelativeFile
RenameFileIfCreateChildFileIsNotSupported
```
### Run Test
```bash
TEST_GROUP=BaseWopiViewing docker compose -f tests/acceptance/docker/src/wopi-validator-test.yml up -d
```
### for macOS use arm image
```bash
WOPI_VALIDATOR_IMAGE=scharfvi/wopi-validator \
TEST_GROUP=BaseWopiViewing \
docker compose -f tests/acceptance/docker/src/wopi-validator-test.yml up -d
```

View File

@@ -37,7 +37,6 @@ use TestHelpers\SetupHelper;
use TestHelpers\HttpRequestHelper;
use TestHelpers\HttpLogger;
use TestHelpers\OcHelper;
use TestHelpers\StorageDriver;
use TestHelpers\GraphHelper;
use TestHelpers\WebDavHelper;
use TestHelpers\SettingsHelper;
@@ -2968,10 +2967,10 @@ class FeatureContext extends BehatVariablesContext {
public static function isExpectedToFail(string $scenarioLine): bool {
$expectedFailFile = \getenv('EXPECTED_FAILURES_FILE');
if (!$expectedFailFile) {
if (OcHelper::getStorageDriver() === StorageDriver::POSIX) {
$expectedFailFile = __DIR__ . '/../expected-failures-posix-storage.md';
$expectedFailFile = __DIR__ . '/../expected-failures-localAPI-on-decomposed-storage.md';
if (\strpos($scenarioLine, "coreApi") === 0) {
$expectedFailFile = __DIR__ . '/../expected-failures-API-on-decomposed-storage.md';
}
$expectedFailFile = __DIR__ . '/../expected-failures-decomposed-storage.md';
}
$reader = \fopen($expectedFailFile, 'r');

View File

@@ -1325,6 +1325,20 @@ trait WebDav {
if ($statusCode === 404 || $statusCode === 405) {
return;
}
// After MOVE the source path might still be visible for a short time
// We wait 1 second and retry once to avoid flaky failures.
if ($statusCode === 207) {
sleep(1);
$response = $this->listFolder(
$user,
$path,
'0',
null,
null,
$type
);
$statusCode = $response->getStatusCode();
}
if ($statusCode === 207) {
$responseXmlObject = HttpRequestHelper::getResponseXml(
$response,

View File

@@ -18,7 +18,7 @@ log_success() {
SCRIPT_PATH=$(dirname "$0")
PATH_TO_SUITES="${SCRIPT_PATH}/features"
EXPECTED_FAILURE_FILES=("expected-failures-decomposed-storage.md" "expected-failures-posix-storage.md" "expected-failures-without-remotephp.md")
EXPECTED_FAILURE_FILES=("expected-failures-localAPI-on-decomposed-storage.md" "expected-failures-API-on-decomposed-storage.md" "expected-failures-without-remotephp.md")
# contains all the suites names inside tests/acceptance/features
AVAILABLE_SUITES=($(ls -l "$PATH_TO_SUITES" | grep '^d' | awk '{print $NF}'))

View File

@@ -1,4 +1,3 @@
.ONESHELL:
SHELL := bash
# define standard colors
@@ -6,43 +5,47 @@ BLACK := $(shell tput -Txterm setaf 0)
RED := $(shell tput -Txterm setaf 1)
GREEN := $(shell tput -Txterm setaf 2)
YELLOW := $(shell tput -Txterm setaf 3)
BLUE := $(shell tput -Txterm setaf 4)
LIGHTPURPLE := $(shell tput -Txterm setaf 4)
PURPLE := $(shell tput -Txterm setaf 5)
CYAN := $(shell tput -Txterm setaf 6)
BLUE := $(shell tput -Txterm setaf 6)
WHITE := $(shell tput -Txterm setaf 7)
RESET := $(shell tput -Txterm sgr0)
COMPOSE_FILE := src/opencloud-base.yml
## default values only for sub-make calls
ifeq ($(LOCAL_TEST),true)
COMPOSE_FILE ?= src/opencloud-base.yml:src/tika.yml
ifeq ($(START_EMAIL),true)
COMPOSE_FILE := $(COMPOSE_FILE):src/email.yml
endif
else
COMPOSE_FILE ?= src/redis.yml:src/opencloud-base.yml:src/acceptance.yml
endif
## user input
BEHAT_FEATURE ?=
ifdef OC_IMAGE_TAG
BUILD_DEV_IMAGE := 0
else
BUILD_DEV_IMAGE := 1
endif
OC_IMAGE_TAG ?= dev
# run tests with ocwrapper by default
WITH_WRAPPER ?= true
OC_WRAPPER := ../../ocwrapper/bin/ocwrapper
# enable tika for full text extraction
ifeq ($(START_TIKA),true)
COMPOSE_FILE := src/tika.yml:$(COMPOSE_FILE)
export SEARCH_EXTRACTOR_TYPE := tika
ifdef START_TIKA
ifeq ($(START_TIKA),true)
COMPOSE_FILE := $(COMPOSE_FILE):src/tika.yml
SEARCH_EXTRACTOR_TYPE := tika
else
SEARCH_EXTRACTOR_TYPE := basic
endif
else
export SEARCH_EXTRACTOR_TYPE := basic
endif
# enable email server
ifeq ($(START_EMAIL),true)
COMPOSE_FILE := src/email.yml:$(COMPOSE_FILE)
export OC_ADD_RUN_SERVICES := notifications
endif
# enable antivirus
ifeq ($(START_ANTIVIRUS),true)
COMPOSE_FILE := src/antivirus.yml:$(COMPOSE_FILE)
export OC_ADD_RUN_SERVICES := $(OC_ADD_RUN_SERVICES) antivirus
export POSTPROCESSING_STEPS := virusscan
endif
# enable wopi services
ifeq ($(ENABLE_WOPI),true)
COMPOSE_FILE := $(COMPOSE_FILE):src/wopi.yml
SEARCH_EXTRACTOR_TYPE := basic
endif
# default to posix
@@ -50,92 +53,223 @@ STORAGE_DRIVER ?= posix
ifeq ($(STORAGE_DRIVER),posix)
# posix requires a additional driver config
COMPOSE_FILE := $(COMPOSE_FILE):src/posix.yml
else ifeq ($(STORAGE_DRIVER),decomposeds3)
COMPOSE_FILE := src/ceph.yml:$(COMPOSE_FILE)
endif
# use latest as default tag if OC_IMAGE is provided but no tag is set
ifneq ($(strip $(OC_IMAGE)),)
ifeq ($(strip $(OC_IMAGE_TAG)),)
OC_IMAGE_TAG := latest
endif
endif
# static
DIVIDE_INTO_NUM_PARTS := 10
PARTS = 1 2 3 4 5 6 7 8 9 10
LOCAL_API_SUITES = $(shell ls ../features | grep ^api*)
COMPOSE_PROJECT_NAME := opencloud-acceptance-tests
# Export variables for sub-make calls
export COMPOSE_PROJECT_NAME
export COMPOSE_FILE
export OC_IMAGE
export OC_IMAGE_TAG
# test configurations
export STORAGE_DRIVER
export WITH_WRAPPER
export BEHAT_SUITE
export BEHAT_FEATURE
export TEST_SERVER_URL
export USE_BEARER_TOKEN
## make definition
.PHONY: help
help:
@echo -e "Test suites: ${CYAN}https://github.com/opencloud-eu/opencloud/tree/main/tests/acceptance/features${RESET}"
@echo -e "Testing docs: ${CYAN}https://github.com/opencloud-eu/opencloud/tree/main/tests/README.md${RESET}"
@echo "Please use 'make <target>' where <target> is one of the following:"
@echo
@echo "Available commands (targets):"
@echo -e " ${GREEN}run-api-tests\t\t${RESET}Build dev image, start services and run the tests."
@echo -e " ${GREEN}start-services\t${RESET}Start service containers."
@echo -e " ${GREEN}run-test-only\t\t${RESET}Run the tests only."
@echo -e " ${GREEN}show-test-logs\t${RESET}Show the test logs."
@echo -e " ${GREEN}ps\t\t\t${RESET}Show the running containers."
@echo -e "${PURPLE}docs: https://docs.opencloud.eu/opencloud/development/testing/#testing-with-test-suite-in-docker${RESET}\n"
@echo
@echo -e " ${YELLOW}clean-dev-image\t${RESET}Delete the docker image built during acceptance tests."
@echo -e " ${YELLOW}clean-containers\t${RESET}Delete the docker containers and volumes."
@echo -e " ${YELLOW}clean-tests\t\t${RESET}Delete test dependencies and results."
@echo -e " ${YELLOW}clean-all\t\t${RESET}Clean all resources: images, containers, volumes, test files."
@echo -e "OpenCloud feature tests and test suites can be found here:"
@echo -e "\thttps://github.com/opencloud-eu/opencloud/tree/main/tests/acceptance/features"
@echo
@echo "Available environment variables:"
@echo -e " ${PURPLE}OC_IMAGE\t\t${RESET}${CYAN}[image_repo]${RESET} OpenCloud image to use. If provided, the dev image build is skipped."
@echo -e " ${PURPLE}OC_IMAGE_TAG\t\t${RESET}${CYAN}[image_tag]${RESET} OpenCloud image tag to use. If provided, the dev image build is skipped."
@echo -e " ${PURPLE}WITH_WRAPPER\t\t${RESET}${CYAN}[true|false]${RESET} Start OpenCloud server using ocwrapper. Default: ${YELLOW}true${RESET}"
@echo -e " ${PURPLE}STORAGE_DRIVER\t${RESET}${CYAN}[posix|decomposed|decomposeds3]${RESET} Storage driver to use. Default: ${YELLOW}posix${RESET}"
@echo -e " ${PURPLE}BEHAT_FEATURE\t\t${RESET}${RESET}${CYAN}[path]${RESET} Path to a feature file. Example: ${YELLOW}tests/acceptance/features/apiGraph/changeRole.feature${RESET}"
@echo -e " ${PURPLE}BEHAT_SUITE\t\t${RESET}${RESET}${CYAN}[suite_name]${RESET} Test suite to run. Example: ${YELLOW}apiGraph${RESET}"
@echo -e " ${PURPLE}TEST_SERVER_URL\t${RESET}${CYAN}[url]${RESET} URL of the OpenCloud server to test against."
@echo -e " ${PURPLE}USE_BEARER_TOKEN\t${RESET}${CYAN}[true|false]${RESET} Use a bearer token for authentication. Default: ${YELLOW}false${RESET}"
@echo -e "test suites that test core compatibility are found here and they start with prefix coreApi-:"
@echo -e "\thttps://github.com/opencloud-eu/opencloud/tree/main/tests/acceptance/features"
@echo
@echo -e "Example usage:"
@echo -e " ${PURPLE}WITH_WRAPPER${RESET}=${YELLOW}false${RESET} \\"
@echo -e " ${PURPLE}STORAGE_DRIVER${RESET}=${YELLOW}posix${RESET} \\"
@echo -e " ${PURPLE}BEHAT_FEATURE${RESET}=${YELLOW}tests/acceptance/features/apiGraph/changeRole.feature${RESET} \\"
@echo -e " make ${GREEN}run-api-tests${RESET}"
@echo -e "The OpenCloud to be tested will be build from your current working state."
@echo -e "You also can select the OpenCloud Docker image for all tests by setting"
@echo -e "\tmake ... ${YELLOW}OC_IMAGE_TAG=latest${RESET}"
@echo -e "where ${YELLOW}latest${RESET} is an example for any valid Docker image tag from"
@echo -e "https://hub.docker.com/r/opencloud-eu/opencloud."
@echo
@echo -e "${GREEN}Run full OpenCloud test suites with decomposed storage:${RESET}\n"
@echo -e "\tmake localApiTests-apiAccountsHashDifficulty-decomposed\t\t${BLUE}run apiAccountsHashDifficulty test suite, where available test suite are apiAccountsHashDifficulty apiArchiver apiContract apiGraph apiSpaces apiSpacesShares apiAsyncUpload apiCors${RESET}"
@echo
@echo -e "${GREEN}Run full OpenCloud test suites with decomposeds3 storage:${RESET}\n"
@echo -e "\tmake localApiTests-apiAccountsHashDifficulty-decomposeds3\t\t${BLUE}run apiAccountsHashDifficulty test suite, where available test suite are apiAccountsHashDifficulty apiArchiver apiContract apiGraph apiSpaces apiSpacesShares apiAsyncUpload apiCors${RESET}"
@echo
@echo -e "${GREEN}Run full OpenCloud test suites with decomposed storage:${RESET}\n"
@echo -e "\tmake Core-API-Tests-decomposed-storage-${RED}X${RESET}\t\t${BLUE}run test suite number X, where ${RED}X = 1 .. 10${RESET}"
@echo
@echo -e "${GREEN}Run full OpenCloud test suites with decomposeds3 storage:${RESET}\n"
@echo -e "\tmake Core-API-Tests-decomposeds3-storage-${RED}X${RESET}\t\t${BLUE}run test suite number X, where ${RED}X = 1 .. 10${RESET}"
@echo
@echo -e "${GREEN}Run an OpenCloud feature test with decomposed storage:${RESET}\n"
@echo -e "\tmake test-opencloud-feature-decomposed-storage ${YELLOW}BEHAT_FEATURE='...'${RESET}\t${BLUE}run single feature test${RESET}"
@echo
@echo -e "\twhere ${YELLOW}BEHAT_FEATURE='...'${RESET} contains a relative path to the feature definition."
@echo -e "\texample: ${RED}tests/acceptance/features/apiAccountsHashDifficulty/addUser.feature${RESET}"
@echo
@echo -e "${GREEN}Run an OpenCloud feature test with decomposeds3 storage:${RESET}\n"
@echo -e "\tmake test-opencloud-feature-decomposeds3-storage ${YELLOW}BEHAT_FEATURE='...'${RESET}\t${BLUE}run single feature test${RESET}"
@echo
@echo -e "\twhere ${YELLOW}BEHAT_FEATURE='...'${RESET} contains a relative path to the feature definition."
@echo -e "\texample: ${RED}tests/acceptance/features/apiAccountsHashDifficulty/addUser.feature${RESET}"
@echo
@echo -e "\twhere ${YELLOW}BEHAT_FEATURE='...'${RESET} contains a relative path to the feature definition."
@echo -e "\texample: ${RED}tests/acceptance/features/apiAccountsHashDifficulty/addUser.feature${RESET}"
@echo
@echo -e "${GREEN}Run a core test against OpenCloud with decomposed storage:${RESET}\n"
@echo -e "\tmake test-core-feature-decomposed-storage ${YELLOW}BEHAT_FEATURE='...'${RESET}\t${BLUE}run single feature test${RESET}"
@echo
@echo -e "\twhere ${YELLOW}BEHAT_FEATURE='...'${RESET} contains a relative path to the feature definition."
@echo -e "\texample: ${RED}tests/acceptance/features/coreApiAuth/webDavAuth.feature${RESET}"
@echo
@echo -e "${GREEN}Run a core test against OpenCloud with decomposeds3 storage:${RESET}\n"
@echo -e "\tmake test-core-feature-decomposeds3-storage ${YELLOW}BEHAT_FEATURE='...'${RESET}\t${BLUE}run single feature test${RESET}"
@echo
@echo -e "\twhere ${YELLOW}BEHAT_FEATURE='...'${RESET} contains a relative path to the feature definition."
@echo -e "\texample: ${RED}tests/acceptance/features/coreApiAuth/webDavAuth.feature${RESET}"
@echo
@echo -e "\twhere ${YELLOW}BEHAT_FEATURE='...'${RESET} contains a relative path to the feature definition."
@echo -e "\texample: ${RED}tests/acceptance/features/coreApiAuth/webDavAuth.feature${RESET}"
@echo
@echo
@echo -e "${GREEN}Show output of tests:${RESET}\n"
@echo -e "\tmake show-test-logs\t\t${BLUE}show output of running or finished tests${RESET}"
@echo
@echo
@echo -e "${GREEN}Clean up after testing:${RESET}\n"
@echo -e "\tmake clean\t${BLUE}clean up all${RESET}"
@echo -e "\tmake clean-docker-container\t\t${BLUE}stops and removes used docker containers${RESET}"
@echo -e "\tmake clean-docker-volumes\t\t${BLUE}removes used docker volumes (used for caching)${RESET}"
@echo
.PHONY: test-opencloud-feature-decomposed-storage
test-opencloud-feature-decomposed-storage: ## test a OpenCloud feature with decomposed storage, usage: make ... BEHAT_FEATURE='tests/acceptance/features/apiAccountsHashDifficulty/addUser.feature:10'
@TEST_SOURCE=opencloud \
STORAGE_DRIVER=decomposed \
BEHAT_FEATURE=$(BEHAT_FEATURE) \
$(MAKE) --no-print-directory testSuite
.PHONY: run-api-tests
run-api-tests: $(OC_WRAPPER) build-dev-image clean-containers
@echo "${BLUE}[INFO]${RESET} Compose project: ${YELLOW}$(COMPOSE_PROJECT_NAME)${RESET}"
@echo "${BLUE}[INFO]${RESET} Compose file: ${YELLOW}$(COMPOSE_FILE)${RESET}"
@echo "${BLUE}[INFO]${RESET} Using storage driver: ${YELLOW}$(STORAGE_DRIVER)${RESET}"
# force use local server when using this command
export TEST_SERVER_URL=https://opencloud-server:9200
$(MAKE) --no-print-directory start-services
$(MAKE) --no-print-directory run-test-only
.PHONY: test-opencloud-feature-decomposeds3-storage
test-opencloud-feature-decomposeds3-storage: ## test a OpenCloud feature with decomposeds3 storage, usage: make ... BEHAT_FEATURE='tests/acceptance/features/apiAccountsHashDifficulty/addUser.feature:10'
@TEST_SOURCE=opencloud \
STORAGE_DRIVER=decomposeds3 \
BEHAT_FEATURE=$(BEHAT_FEATURE) \
START_CEPH=1 \
$(MAKE) --no-print-directory testSuite
.PHONY: start-services
start-services: $(OC_WRAPPER) ## start services
.PHONY: test-opencloud-feature-posix-storage
test-opencloud-feature-posix-storage: ## test a OpenCloud feature with posix storage, usage: make ... BEHAT_FEATURE='tests/acceptance/features/apiAccountsHashDifficulty/addUser.feature:10'
@TEST_SOURCE=opencloud \
STORAGE_DRIVER=posix \
BEHAT_FEATURE=$(BEHAT_FEATURE) \
$(MAKE) --no-print-directory testSuite
.PHONY: test-core-feature-decomposed-storage
test-core-feature-decomposed-storage: ## test a core feature with decomposed storage, usage: make ... BEHAT_FEATURE='tests/acceptance/features/coreApiAuth/webDavAuth.feature'
@TEST_SOURCE=core \
STORAGE_DRIVER=decomposed \
BEHAT_FEATURE=$(BEHAT_FEATURE) \
$(MAKE) --no-print-directory testSuite
.PHONY: test-core-feature-decomposeds3-storage
test-core-feature-decomposeds3-storage: ## test a core feature with decomposeds3 storage, usage: make ... BEHAT_FEATURE='tests/acceptance/features/coreApiAuth/webDavAuth.feature'
@TEST_SOURCE=core \
STORAGE_DRIVER=decomposeds3 \
BEHAT_FEATURE=$(BEHAT_FEATURE) \
START_CEPH=1 \
$(MAKE) --no-print-directory testSuite
.PHONY: test-opencloud-feature-posix-storage
test-core-opencloud-feature-posix-storage: ## test a core feature with posix storage, usage: make ... BEHAT_FEATURE='tests/acceptance/features/apiAccountsHashDifficulty/addUser.feature:10'
@TEST_SOURCE=core \
STORAGE_DRIVER=posix \
BEHAT_FEATURE=$(BEHAT_FEATURE) \
$(MAKE) --no-print-directory testSuite
localSuiteOpencloud = $(addprefix localApiTests-, $(addsuffix -decomposed,${LOCAL_API_SUITES}))
.PHONY: $(localSuiteOpencloud)
$(localSuiteOpencloud): ## run local api test suite with decomposed storage
@$(eval BEHAT_SUITE=$(shell echo "$@" | cut -d'-' -f2))
@TEST_SOURCE=opencloud \
STORAGE_DRIVER=decomposed \
BEHAT_SUITE=$(BEHAT_SUITE) \
$(MAKE) --no-print-directory testSuite
localSuiteDecomposedS3 = $(addprefix localApiTests-, $(addsuffix -decomposeds3,${LOCAL_API_SUITES}))
.PHONY: $(localSuiteDecomposedS3)
$(localSuiteDecomposedS3): ## run local api test suite with s3 storage
@$(eval BEHAT_SUITE=$(shell echo "$@" | cut -d'-' -f2))
@TEST_SOURCE=opencloud \
STORAGE_DRIVER=decomposeds3 \
BEHAT_SUITE=$(BEHAT_SUITE) \
$(MAKE) --no-print-directory testSuite
localSuitePosix = $(addprefix localApiTests-, $(addsuffix -posix,${LOCAL_API_SUITES}))
.PHONY: $(localSuitePosix)
$(localSuitePosix): ## run local api test suite with posix storage
@$(eval BEHAT_SUITE=$(shell echo "$@" | cut -d'-' -f2))
@TEST_SOURCE=opencloud \
STORAGE_DRIVER=posix \
BEHAT_SUITE=$(BEHAT_SUITE) \
$(MAKE) --no-print-directory testSuite
targetsOC = $(addprefix Core-API-Tests-decomposed-storage-,$(PARTS))
.PHONY: $(targetsOC)
$(targetsOC):
@$(eval RUN_PART=$(shell echo "$@" | tr -dc '0-9'))
@TEST_SOURCE=core \
STORAGE_DRIVER=decomposed \
RUN_PART=$(RUN_PART) \
$(MAKE) --no-print-directory testSuite
targetsDecomposedS3 = $(addprefix Core-API-Tests-decomposeds3-storage-,$(PARTS))
.PHONY: $(targetsDecomposedS3)
$(targets):
@$(eval RUN_PART=$(shell echo "$@" | tr -dc '0-9'))
@TEST_SOURCE=core \
STORAGE_DRIVER=decomposeds3 \
RUN_PART=$(RUN_PART) \
$(MAKE) --no-print-directory testSuite
.PHONY: testSuite
testSuite: $(OC_WRAPPER) build-dev-image clean-docker-container
@if [ -n "${START_CEPH}" ]; then \
COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=src/ceph.yml \
docker compose run start_ceph; \
fi; \
if [ "${START_EMAIL}" == "true" ]; then \
COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=src/email.yml \
docker compose run start_email; \
fi; \
if [ "${START_ANTIVIRUS}" == "true" ]; then \
COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=src/antivirus.yml \
docker compose run start_antivirus; \
fi; \
if [ "${START_TIKA}" == "true" ]; then \
COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=src/tika.yml \
docker compose run tika-service; \
fi; \
COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=$(COMPOSE_FILE) \
STORAGE_DRIVER=$(STORAGE_DRIVER) \
TEST_SOURCE=$(TEST_SOURCE) \
WITH_WRAPPER=$(WITH_WRAPPER) \
OC_ASYNC_UPLOADS=$(OC_ASYNC_UPLOADS) \
OC_ADD_RUN_SERVICES=$(OC_ADD_RUN_SERVICES) \
POSTPROCESSING_STEPS=$(POSTPROCESSING_STEPS) \
SEARCH_EXTRACTOR_TYPE=$(SEARCH_EXTRACTOR_TYPE) \
OC_IMAGE_TAG=$(OC_IMAGE_TAG) \
BEHAT_SUITE=$(BEHAT_SUITE) \
BEHAT_FEATURE=$(BEHAT_FEATURE) \
DIVIDE_INTO_NUM_PARTS=$(DIVIDE_INTO_NUM_PARTS) \
RUN_PART=$(RUN_PART) \
docker compose up -d --build --force-recreate
.PHONY: run-test-only
run-test-only:
docker compose -f src/acceptance.yml up
.PHONY: show-test-logs
show-test-logs: ## show test logs
show-test-logs: ## show logs of test
@COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=$(COMPOSE_FILE) \
docker compose logs --no-log-prefix -f acceptance-tests | less
.PHONY: ps
ps: ## show running containers
ps: ## show docker status
@COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=$(COMPOSE_FILE) \
docker compose ps
$(OC_WRAPPER):
@@ -145,21 +279,55 @@ $(OC_WRAPPER):
.PHONY: build-dev-image
build-dev-image:
@if [ -z "$(OC_IMAGE)" ] && [ -z "$(OC_IMAGE_TAG)" ]; then \
@if [ $(BUILD_DEV_IMAGE) -eq 1 ]; then \
$(MAKE) --no-print-directory -C ../../../opencloud dev-docker \
; fi;
.PHONY: clean-dev-image
clean-dev-image: ## clean docker image built during acceptance tests
@docker image rm opencloudeu/opencloud:dev || true
.PHONY: clean-dev-docker-image
clean-dev-docker-image: ## clean docker image built during acceptance tests
@docker image rm opencloud-eu/opencloud:dev || true
.PHONY: clean-containers
clean-containers: ## clean docker containers created during acceptance tests
.PHONY: clean-docker-container
clean-docker-container: ## clean docker containers created during acceptance tests
@COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=$(COMPOSE_FILE) \
BEHAT_SUITE="" \
DIVIDE_INTO_NUM_PARTS="" \
OC_IMAGE_TAG="" \
RUN_PART="" \
STORAGE_DRIVER="" \
TEST_SOURCE="" \
docker compose down --remove-orphans
.PHONY: clean-docker-volumes
clean-docker-volumes: ## clean docker volumes created during acceptance tests
@COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
COMPOSE_FILE=$(COMPOSE_FILE) \
BEHAT_SUITE="" \
DIVIDE_INTO_NUM_PARTS="" \
OC_IMAGE_TAG="" \
RUN_PART="" \
STORAGE_DRIVER="" \
TEST_SOURCE="" \
docker compose down --remove-orphans -v
.PHONY: clean-tests
clean-tests:
.PHONY: clean-files
clean-files:
@$(MAKE) --no-print-directory -C ../../../. clean-tests
.PHONY: clean
clean-all: clean-containers clean-dev-image clean-tests ## clean all
clean: clean-docker-container clean-docker-volumes clean-dev-docker-image clean-files ## clean all
.PHONY: start-server
start-server: $(OC_WRAPPER) ## build and start server
@echo "Build and start server..."
COMPOSE_FILE=$(COMPOSE_FILE) \
COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) \
OC_IMAGE_TAG=dev \
WITH_WRAPPER=$(WITH_WRAPPER) \
TEST_SOURCE=opencloud \
STORAGE_DRIVER=$(STORAGE_DRIVER) \
OC_ASYNC_UPLOADS=true \
SEARCH_EXTRACTOR_TYPE=tika \
OC_ADD_RUN_SERVICES=notifications \
docker compose up -d --build --force-recreate

View File

@@ -5,12 +5,14 @@ services:
command: /bin/bash /test/run-tests.sh
environment:
OC_ROOT: /woodpecker/src/github.com/opencloud-eu/opencloud
TEST_SERVER_URL: ${TEST_SERVER_URL:-https://opencloud-server:9200}
TEST_SERVER_URL: https://opencloud-server:9200
OC_WRAPPER_URL: http://opencloud-server:5200
STORAGE_DRIVER: ${STORAGE_DRIVER:-posix}
STORAGE_DRIVER: $STORAGE_DRIVER
TEST_SOURCE: $TEST_SOURCE
BEHAT_SUITE: ${BEHAT_SUITE:-}
BEHAT_FEATURE: ${BEHAT_FEATURE:-}
USE_BEARER_TOKEN: ${USE_BEARER_TOKEN:-false}
DIVIDE_INTO_NUM_PARTS: $DIVIDE_INTO_NUM_PARTS
RUN_PART: $RUN_PART
# email
EMAIL_HOST: email
EMAIL_PORT: 9000

View File

@@ -1,10 +0,0 @@
#!/bin/bash
set -e
mkdir -p /var/www/onlyoffice/Data/certs
cd /var/www/onlyoffice/Data/certs
openssl req -x509 -newkey rsa:4096 -keyout onlyoffice.key -out onlyoffice.crt -sha256 -days 365 -batch -nodes
chmod 400 /var/www/onlyoffice/Data/certs/onlyoffice.key
/app/ds/run-document-server.sh

View File

@@ -1,12 +1,12 @@
services:
opencloud-server:
image: ${OC_IMAGE:-opencloudeu/opencloud}:${OC_IMAGE_TAG:-dev}
entrypoint: ["/bin/sh", "/usr/bin/serve-opencloud.sh"]
image: opencloudeu/opencloud:dev
entrypoint: [ "/bin/sh", "/usr/bin/serve-opencloud.sh" ]
user: root
environment:
WITH_WRAPPER: ${WITH_WRAPPER:-true}
WITH_WRAPPER: $WITH_WRAPPER
OC_URL: "https://opencloud-server:9200"
STORAGE_USERS_DRIVER: ${STORAGE_DRIVER:-posix}
STORAGE_USERS_DRIVER: $STORAGE_DRIVER
STORAGE_USERS_POSIX_WATCH_FS: "true"
STORAGE_USERS_DRIVER_LOCAL_ROOT: /srv/app/tmp/opencloud/local/root
STORAGE_USERS_DRIVER_OC_ROOT: /srv/app/tmp/opencloud/storage/users
@@ -19,8 +19,10 @@ services:
IDM_CREATE_DEMO_USERS: "true"
IDM_ADMIN_PASSWORD: "admin"
FRONTEND_SEARCH_MIN_LENGTH: "2"
OC_ADD_RUN_SERVICES: ${OC_ADD_RUN_SERVICES:-}
OC_ASYNC_UPLOADS: $OC_ASYNC_UPLOADS
OC_ADD_RUN_SERVICES: $OC_ADD_RUN_SERVICES
PROXY_HTTP_ADDR: "0.0.0.0:9200"
OC_JWT_SECRET: "some-random-jwt-secret"
# decomposeds3 specific settings
STORAGE_USERS_DECOMPOSEDS3_ENDPOINT: http://ceph:8080
@@ -40,19 +42,19 @@ services:
ANTIVIRUS_CLAMAV_SOCKET: tcp://clamav:3310
# postprocessing step
POSTPROCESSING_STEPS: ${POSTPROCESSING_STEPS:-}
POSTPROCESSING_STEPS: $POSTPROCESSING_STEPS
# tika
SEARCH_EXTRACTOR_TYPE: ${SEARCH_EXTRACTOR_TYPE:-basic}
SEARCH_EXTRACTOR_TYPE: $SEARCH_EXTRACTOR_TYPE
SEARCH_EXTRACTOR_TIKA_TIKA_URL: "http://tika:9998"
SEARCH_EXTRACTOR_CS3SOURCE_INSECURE: "true"
# fonts map for txt thumbnails (including unicode support)
THUMBNAILS_TXT_FONTMAP_FILE: "/woodpecker/src/github.com/opencloud-eu/opencloud/tests/config/drone/fontsMap.json"
ports:
- "9200:9200"
- "5200:5200" ## ocwrapper
- "9174:9174" ## notifications debug
- '9200:9200'
- '5200:5200' ## ocwrapper
- '9174:9174' ## notifications debug
volumes:
- ../../../config:/woodpecker/src/github.com/opencloud-eu/opencloud/tests/config
- ../../../ocwrapper/bin/ocwrapper:/usr/bin/ocwrapper

View File

@@ -2,6 +2,8 @@
services:
opencloud-server:
environment:
# activate posix storage driver for users
STORAGE_USERS_DRIVER: posix
# posix requires a shared cache store
STORAGE_USERS_ID_CACHE_STORE: "nats-js-kv"
STORAGE_USERS_POSIX_WATCH_FS: "true"

View File

@@ -0,0 +1,4 @@
services:
redis:
image: redis:6
command: ["--databases", "1"]

View File

@@ -1,42 +1,70 @@
#!/bin/bash
mkdir -p "${OC_ROOT}/vendor-bin/behat"
if [ ! -f "${OC_ROOT}/vendor-bin/behat/composer.json" ]; then
cp /tmp/vendor-bin/behat/composer.json "${OC_ROOT}/vendor-bin/behat/composer.json"
fi
#mkdir -p /drone/src/vendor-bin/behat
#cp /tmp/vendor-bin/behat/composer.json /drone/src/vendor-bin/behat/composer.json
git config --global advice.detachedHead false
## CONFIGURE TEST
BEHAT_FILTER_TAGS='~@skip'
EXPECTED_FAILURES_FILE=''
if [ "$STORAGE_DRIVER" = "posix" ]; then
BEHAT_FILTER_TAGS+='&&~@skipOnOpencloud-posix-Storage'
EXPECTED_FAILURES_FILE="${OC_ROOT}/tests/acceptance/expected-failures-posix-storage.md"
elif [ "$STORAGE_DRIVER" = "decomposed" ]; then
BEHAT_FILTER_TAGS+='&&~@skipOnOpencloud-decomposed-Storage'
EXPECTED_FAILURES_FILE="${OC_ROOT}/tests/acceptance/expected-failures-decomposed-storage.md"
if [ "$TEST_SOURCE" = "core" ]; then
export ACCEPTANCE_TEST_TYPE='core-api'
if [ "$STORAGE_DRIVER" = "decomposed" ]; then
export OC_REVA_DATA_ROOT=''
export BEHAT_FILTER_TAGS='~@skipOnOpencloud-decomposed-Storage'
export EXPECTED_FAILURES_FILE='/drone/src/tests/acceptance/expected-failures-API-on-decomposed-storage.md'
elif [ "$STORAGE_DRIVER" = "decomposeds3" ]; then
export BEHAT_FILTER_TAGS='~@skip&&~@skipOnOpencloud-decomposeds3-Storage'
export OC_REVA_DATA_ROOT=''
else
echo "non existing STORAGE selected"
exit 1
fi
unset BEHAT_SUITE
elif [ "$TEST_SOURCE" = "opencloud" ]; then
if [ "$STORAGE_DRIVER" = "decomposed" ]; then
export BEHAT_FILTER_TAGS='~@skip&&~@skipOnOpencloud-decomposed-Storage'
export OC_REVA_DATA_ROOT=''
elif [ "$STORAGE_DRIVER" = "decomposeds3" ]; then
export BEHAT_FILTER_TAGS='~@skip&&~@skipOnOpencloud-decomposeds3-Storage'
export OC_REVA_DATA_ROOT=''
elif [ "$STORAGE_DRIVER" = "posix" ]; then
export BEHAT_FILTER_TAGS='~@skip&&~@skipOnOpencloud-posix-Storage'
export OC_REVA_DATA_ROOT=''
else
echo "non existing storage selected"
exit 1
fi
unset DIVIDE_INTO_NUM_PARTS
unset RUN_PART
else
echo "non existing TEST_SOURCE selected"
exit 1
fi
export BEHAT_FILTER_TAGS
export EXPECTED_FAILURES_FILE
if [ -n "$BEHAT_FEATURE" ]; then
export BEHAT_FEATURE
echo "[INFO] Running feature: $BEHAT_FEATURE"
if [ ! -z "$BEHAT_FEATURE" ]; then
echo "feature selected: " + $BEHAT_FEATURE
# allow running without filters if its a feature
unset BEHAT_FILTER_TAGS
unset BEHAT_SUITE
unset DIVIDE_INTO_NUM_PARTS
unset RUN_PART
unset EXPECTED_FAILURES_FILE
elif [ -n "$BEHAT_SUITE" ]; then
export BEHAT_SUITE
echo "[INFO] Running suite: $BEHAT_SUITE"
else
unset BEHAT_FEATURE
fi
## RUN TEST
sleep 10
make -C "$OC_ROOT" test-acceptance-api
chmod -R 777 "${OC_ROOT}/vendor-bin/"*"/vendor" "${OC_ROOT}/vendor-bin/"*"/composer.lock" "${OC_ROOT}/tests/acceptance/output" 2>/dev/null || true
if [[ -z "$TEST_SOURCE" ]]; then
echo "non existing TEST_SOURCE selected"
exit 1
else
sleep 10
make -C $OC_ROOT test-acceptance-api
fi
chmod -R 777 vendor-bin/**/vendor vendor-bin/**/composer.lock tests/acceptance/output

View File

@@ -1,105 +0,0 @@
x-common_config: &common_config
image: opencloudeu/opencloud:dev
restart: unless-stopped
entrypoint: /bin/sh
command: ["-c", "opencloud collaboration server"]
user: root
x-common_env: &common_env
OC_CONFIG_DIR: /etc/opencloud
MICRO_REGISTRY: nats-js-kv
MICRO_REGISTRY_ADDRESS: opencloud-server:9233
COLLABORATION_LOG_LEVEL: info
COLLABORATION_GRPC_ADDR: 0.0.0.0:9301
COLLABORATION_HTTP_ADDR: 0.0.0.0:9300
COLLABORATION_DEBUG_ADDR: 0.0.0.0:9304
COLLABORATION_APP_PROOF_DISABLE: true
COLLABORATION_APP_INSECURE: true
COLLABORATION_CS3API_DATAGATEWAY_INSECURE: true
COLLABORATION_WOPI_SECRET: some-wopi-secret
x-config_volume: &config_volume
- config:/etc/opencloud
x-depends_on: &depends_on
- opencloud-server
services:
opencloud-server:
environment:
OC_CONFIG_DIR: /etc/opencloud
GATEWAY_GRPC_ADDR: 0.0.0.0:9142
NATS_NATS_HOST: 0.0.0.0
NATS_NATS_PORT: 9233
volumes: *config_volume
fakeoffice:
image: alpine:latest
entrypoint: /bin/sh
command:
[
"-c",
"while true; do echo -e \"HTTP/1.1 200 OK\n\n$(cat /fakeoffice-discovery.xml)\" | nc -l -k -p 8080; done",
]
healthcheck:
test: ["CMD", "curl", "-f", "http://fakeoffice:8080"]
volumes:
- ./../../../config/woodpecker/hosting-discovery.xml:/fakeoffice-discovery.xml
collabora:
image: collabora/code:24.04.5.1.1
environment:
DONT_GEN_SSL_CERT: set
extra_params: --o:ssl.enable=true --o:ssl.termination=true --o:welcome.enable=false --o:net.frame_ancestors=https://opencloud-server:9200
entrypoint: /bin/sh
command: ["-c", "coolconfig generate-proof-key; /start-collabora-online.sh"]
onlyoffice:
image: onlyoffice/documentserver:7.5.1
environment:
WOPI_ENABLED: true
USE_UNAUTHORIZED_STORAGE: true
entrypoint: bash /entrypoint.sh
volumes:
- ./onlyoffice-entrypoint.sh:/entrypoint.sh
collaboration-fakeoffice:
<<: *common_config
environment:
<<: *common_env
COLLABORATION_SERVICE_NAME: collaboration-fakeoffice
COLLABORATION_APP_NAME: FakeOffice
COLLABORATION_APP_PRODUCT: Microsoft
COLLABORATION_APP_ADDR: http://fakeoffice:8080
COLLABORATION_WOPI_SRC: http://collaboration-fakeoffice:9300
volumes: *config_volume
depends_on: *depends_on
collaboration-collabora:
<<: *common_config
environment:
<<: *common_env
COLLABORATION_SERVICE_NAME: collaboration-collabora
COLLABORATION_APP_NAME: Collabora
COLLABORATION_APP_PRODUCT: Collabora
COLLABORATION_APP_ADDR: https://collabora:9980
COLLABORATION_APP_ICON: https://collabora:9980/favicon.ico
COLLABORATION_WOPI_SRC: http://collaboration-collabora:9300
volumes: *config_volume
depends_on: *depends_on
collaboration-onlyoffice:
<<: *common_config
environment:
<<: *common_env
COLLABORATION_SERVICE_NAME: collaboration-onlyoffice
COLLABORATION_APP_NAME: OnlyOffice
COLLABORATION_APP_PRODUCT: OnlyOffice
COLLABORATION_APP_ADDR: https://onlyoffice
COLLABORATION_APP_ICON: https://onlyoffice/web-apps/apps/documenteditor/main/resources/img/favicon.ico
COLLABORATION_WOPI_SRC: http://collaboration-onlyoffice:9300
volumes: *config_volume
depends_on: *depends_on
volumes:
config:

View File

@@ -0,0 +1,175 @@
## Scenarios from core API tests that are expected to fail with decomposed storage while running with the Graph API
### File
Basic file management like up and download, move, copy, properties, trash, versions and chunking.
#### [Custom dav properties with namespaces are rendered incorrectly](https://github.com/owncloud/ocis/issues/2140)
_ocdav: double-check the webdav property parsing when custom namespaces are used_
- [coreApiWebdavProperties/setFileProperties.feature:128](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/setFileProperties.feature#L128)
- [coreApiWebdavProperties/setFileProperties.feature:129](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/setFileProperties.feature#L129)
- [coreApiWebdavProperties/setFileProperties.feature:130](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/setFileProperties.feature#L130)
### Sync
Synchronization features like etag propagation, setting mtime and locking files
#### [Uploading an old method chunked file with checksum should fail using new DAV path](https://github.com/owncloud/ocis/issues/2323)
- [coreApiMain/checksums.feature:233](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiMain/checksums.feature#L233)
- [coreApiMain/checksums.feature:234](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiMain/checksums.feature#L234)
- [coreApiMain/checksums.feature:235](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiMain/checksums.feature#L235)
### Share
#### [d:quota-available-bytes in dprop of PROPFIND give wrong response value](https://github.com/owncloud/ocis/issues/8197)
- [coreApiWebdavProperties/getQuota.feature:57](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L57)
- [coreApiWebdavProperties/getQuota.feature:58](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L58)
- [coreApiWebdavProperties/getQuota.feature:59](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L59)
- [coreApiWebdavProperties/getQuota.feature:73](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L73)
- [coreApiWebdavProperties/getQuota.feature:74](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L74)
- [coreApiWebdavProperties/getQuota.feature:75](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L75)
#### [deleting a file inside a received shared folder is moved to the trash-bin of the sharer not the receiver](https://github.com/owncloud/ocis/issues/1124)
- [coreApiTrashbin/trashbinSharingToShares.feature:54](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L54)
- [coreApiTrashbin/trashbinSharingToShares.feature:55](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L55)
- [coreApiTrashbin/trashbinSharingToShares.feature:56](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L56)
- [coreApiTrashbin/trashbinSharingToShares.feature:83](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L83)
- [coreApiTrashbin/trashbinSharingToShares.feature:84](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L84)
- [coreApiTrashbin/trashbinSharingToShares.feature:85](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L85)
- [coreApiTrashbin/trashbinSharingToShares.feature:142](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L142)
- [coreApiTrashbin/trashbinSharingToShares.feature:143](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L143)
- [coreApiTrashbin/trashbinSharingToShares.feature:144](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L144)
- [coreApiTrashbin/trashbinSharingToShares.feature:202](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L202)
- [coreApiTrashbin/trashbinSharingToShares.feature:203](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L203)
### Other
API, search, favorites, config, capabilities, not existing endpoints, CORS and others
#### [sending MKCOL requests to another or non-existing user's webDav endpoints as normal user should return 404](https://github.com/owncloud/ocis/issues/5049)
_ocdav: api compatibility, return correct status code_
- [coreApiAuth/webDavMKCOLAuth.feature:42](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavMKCOLAuth.feature#L42)
- [coreApiAuth/webDavMKCOLAuth.feature:53](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavMKCOLAuth.feature#L53)
#### [trying to lock file of another user gives http 500](https://github.com/owncloud/ocis/issues/2176)
- [coreApiAuth/webDavLOCKAuth.feature:46](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavLOCKAuth.feature#L46)
- [coreApiAuth/webDavLOCKAuth.feature:58](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavLOCKAuth.feature#L58)
#### [Support for favorites](https://github.com/owncloud/ocis/issues/1228)
- [coreApiFavorites/favorites.feature:101](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L101)
- [coreApiFavorites/favorites.feature:102](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L102)
- [coreApiFavorites/favorites.feature:103](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L103)
- [coreApiFavorites/favorites.feature:124](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L124)
- [coreApiFavorites/favorites.feature:125](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L125)
- [coreApiFavorites/favorites.feature:126](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L126)
- [coreApiFavorites/favorites.feature:189](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L189)
- [coreApiFavorites/favorites.feature:190](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L190)
- [coreApiFavorites/favorites.feature:191](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L191)
- [coreApiFavorites/favorites.feature:145](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L145)
- [coreApiFavorites/favorites.feature:146](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L146)
- [coreApiFavorites/favorites.feature:147](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L147)
- [coreApiFavorites/favorites.feature:174](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L174)
- [coreApiFavorites/favorites.feature:175](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L175)
- [coreApiFavorites/favorites.feature:176](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L176)
- [coreApiFavorites/favoritesSharingToShares.feature:91](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favoritesSharingToShares.feature#L91)
- [coreApiFavorites/favoritesSharingToShares.feature:92](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favoritesSharingToShares.feature#L92)
- [coreApiFavorites/favoritesSharingToShares.feature:93](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favoritesSharingToShares.feature#L93)
#### [WWW-Authenticate header for unauthenticated requests is not clear](https://github.com/owncloud/ocis/issues/2285)
- [coreApiWebdavOperations/refuseAccess.feature:21](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/refuseAccess.feature#L21)
- [coreApiWebdavOperations/refuseAccess.feature:22](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/refuseAccess.feature#L22)
#### [PATCH request for TUS upload with wrong checksum gives incorrect response](https://github.com/owncloud/ocis/issues/1755)
- [coreApiWebdavUploadTUS/checksums.feature:74](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L74)
- [coreApiWebdavUploadTUS/checksums.feature:75](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L75)
- [coreApiWebdavUploadTUS/checksums.feature:76](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L76)
- [coreApiWebdavUploadTUS/checksums.feature:77](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L77)
- [coreApiWebdavUploadTUS/checksums.feature:79](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L79)
- [coreApiWebdavUploadTUS/checksums.feature:78](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L78)
- [coreApiWebdavUploadTUS/checksums.feature:147](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L147)
- [coreApiWebdavUploadTUS/checksums.feature:148](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L148)
- [coreApiWebdavUploadTUS/checksums.feature:149](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L149)
- [coreApiWebdavUploadTUS/checksums.feature:192](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L192)
- [coreApiWebdavUploadTUS/checksums.feature:193](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L193)
- [coreApiWebdavUploadTUS/checksums.feature:194](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L194)
- [coreApiWebdavUploadTUS/checksums.feature:195](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L195)
- [coreApiWebdavUploadTUS/checksums.feature:196](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L196)
- [coreApiWebdavUploadTUS/checksums.feature:197](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L197)
- [coreApiWebdavUploadTUS/checksums.feature:240](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L240)
- [coreApiWebdavUploadTUS/checksums.feature:241](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L241)
- [coreApiWebdavUploadTUS/checksums.feature:242](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L242)
- [coreApiWebdavUploadTUS/checksums.feature:243](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L243)
- [coreApiWebdavUploadTUS/checksums.feature:244](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L244)
- [coreApiWebdavUploadTUS/checksums.feature:245](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L245)
- [coreApiWebdavUploadTUS/uploadToShare.feature:255](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L255)
- [coreApiWebdavUploadTUS/uploadToShare.feature:256](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L256)
- [coreApiWebdavUploadTUS/uploadToShare.feature:279](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L279)
- [coreApiWebdavUploadTUS/uploadToShare.feature:280](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L280)
- [coreApiWebdavUploadTUS/uploadToShare.feature:376](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L376)
- [coreApiWebdavUploadTUS/uploadToShare.feature:377](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L377)
#### [Renaming resource to banned name is allowed in spaces webdav](https://github.com/owncloud/ocis/issues/3099)
- [coreApiWebdavMove2/moveFile.feature:143](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveFile.feature#L143)
- [coreApiWebdavMove1/moveFolder.feature:36](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L36)
- [coreApiWebdavMove1/moveFolder.feature:50](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L50)
- [coreApiWebdavMove1/moveFolder.feature:64](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L64)
#### [Trying to delete other user's trashbin item returns 409 for spaces path instead of 404](https://github.com/owncloud/ocis/issues/9791)
- [coreApiTrashbin/trashbinDelete.feature:92](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinDelete.feature#L92)
#### [MOVE a file into same folder with same name returns 404 instead of 403](https://github.com/owncloud/ocis/issues/1976)
- [coreApiWebdavMove2/moveFile.feature:100](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveFile.feature#L100)
- [coreApiWebdavMove2/moveFile.feature:101](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveFile.feature#L101)
- [coreApiWebdavMove2/moveFile.feature:102](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveFile.feature#L102)
- [coreApiWebdavMove1/moveFolder.feature:217](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L217)
- [coreApiWebdavMove1/moveFolder.feature:218](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L218)
- [coreApiWebdavMove1/moveFolder.feature:219](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L219)
- [coreApiWebdavMove2/moveShareOnOpencloud.feature:334](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveShareOnOpencloud.feature#L334)
- [coreApiWebdavMove2/moveShareOnOpencloud.feature:337](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveShareOnOpencloud.feature#L337)
- [coreApiWebdavMove2/moveShareOnOpencloud.feature:340](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveShareOnOpencloud.feature#L340)
#### [COPY file/folder to same name is possible (but 500 code error for folder with spaces path)](https://github.com/owncloud/ocis/issues/8711)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:198](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L198)
- [coreApiWebdavProperties/copyFile.feature:1094](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/copyFile.feature#L1094)
- [coreApiWebdavProperties/copyFile.feature:1095](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/copyFile.feature#L1095)
- [coreApiWebdavProperties/copyFile.feature:1096](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/copyFile.feature#L1096)
#### [Trying to restore personal file to file of share received folder returns 403 but the share file is deleted (new dav path)](https://github.com/owncloud/ocis/issues/10356)
- [coreApiTrashbin/trashbinSharingToShares.feature:277](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L277)
#### [Preview. UTF characters do not display on prievew](https://github.com/opencloud-eu/opencloud/issues/1451)
- [coreApiWebdavPreviews/previews.feature:249](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L249)
- [coreApiWebdavPreviews/previews.feature:250](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L250)
- [coreApiWebdavPreviews/previews.feature:251](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L251)
#### [Preview of text file truncated](https://github.com/opencloud-eu/opencloud/issues/1452)
- [coreApiWebdavPreviews/previews.feature:263](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L263)
- [coreApiWebdavPreviews/previews.feature:264](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L264)
- [coreApiWebdavPreviews/previews.feature:265](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L265)
### Won't fix
Not everything needs to be implemented for opencloud.
- _Blacklisted ignored files are no longer required because opencloud can handle `.htaccess` files without security implications introduced by serving user provided files with apache._
Note: always have an empty line at the end of this file.
The bash script that processes this file requires that the last line has a newline on the end.

View File

@@ -0,0 +1,175 @@
## Scenarios from core API tests that are expected to fail with decomposed storage while running with the Graph API
### File
Basic file management like up and download, move, copy, properties, trash, versions and chunking.
#### [Custom dav properties with namespaces are rendered incorrectly](https://github.com/owncloud/ocis/issues/2140)
_ocdav: double-check the webdav property parsing when custom namespaces are used_
- [coreApiWebdavProperties/setFileProperties.feature:128](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/setFileProperties.feature#L128)
- [coreApiWebdavProperties/setFileProperties.feature:129](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/setFileProperties.feature#L129)
- [coreApiWebdavProperties/setFileProperties.feature:130](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/setFileProperties.feature#L130)
### Sync
Synchronization features like etag propagation, setting mtime and locking files
#### [Uploading an old method chunked file with checksum should fail using new DAV path](https://github.com/owncloud/ocis/issues/2323)
- [coreApiMain/checksums.feature:233](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiMain/checksums.feature#L233)
- [coreApiMain/checksums.feature:234](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiMain/checksums.feature#L234)
- [coreApiMain/checksums.feature:235](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiMain/checksums.feature#L235)
### Share
#### [d:quota-available-bytes in dprop of PROPFIND give wrong response value](https://github.com/owncloud/ocis/issues/8197)
- [coreApiWebdavProperties/getQuota.feature:57](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L57)
- [coreApiWebdavProperties/getQuota.feature:58](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L58)
- [coreApiWebdavProperties/getQuota.feature:59](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L59)
- [coreApiWebdavProperties/getQuota.feature:73](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L73)
- [coreApiWebdavProperties/getQuota.feature:74](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L74)
- [coreApiWebdavProperties/getQuota.feature:75](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L75)
#### [deleting a file inside a received shared folder is moved to the trash-bin of the sharer not the receiver](https://github.com/owncloud/ocis/issues/1124)
- [coreApiTrashbin/trashbinSharingToShares.feature:54](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L54)
- [coreApiTrashbin/trashbinSharingToShares.feature:55](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L55)
- [coreApiTrashbin/trashbinSharingToShares.feature:56](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L56)
- [coreApiTrashbin/trashbinSharingToShares.feature:83](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L83)
- [coreApiTrashbin/trashbinSharingToShares.feature:84](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L84)
- [coreApiTrashbin/trashbinSharingToShares.feature:85](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L85)
- [coreApiTrashbin/trashbinSharingToShares.feature:142](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L142)
- [coreApiTrashbin/trashbinSharingToShares.feature:143](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L143)
- [coreApiTrashbin/trashbinSharingToShares.feature:144](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L144)
- [coreApiTrashbin/trashbinSharingToShares.feature:202](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L202)
- [coreApiTrashbin/trashbinSharingToShares.feature:203](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L203)
### Other
API, search, favorites, config, capabilities, not existing endpoints, CORS and others
#### [sending MKCOL requests to another or non-existing user's webDav endpoints as normal user should return 404](https://github.com/owncloud/ocis/issues/5049)
_ocdav: api compatibility, return correct status code_
- [coreApiAuth/webDavMKCOLAuth.feature:42](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavMKCOLAuth.feature#L42)
- [coreApiAuth/webDavMKCOLAuth.feature:53](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavMKCOLAuth.feature#L53)
#### [trying to lock file of another user gives http 500](https://github.com/owncloud/ocis/issues/2176)
- [coreApiAuth/webDavLOCKAuth.feature:46](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavLOCKAuth.feature#L46)
- [coreApiAuth/webDavLOCKAuth.feature:58](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavLOCKAuth.feature#L58)
#### [Support for favorites](https://github.com/owncloud/ocis/issues/1228)
- [coreApiFavorites/favorites.feature:101](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L101)
- [coreApiFavorites/favorites.feature:102](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L102)
- [coreApiFavorites/favorites.feature:103](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L103)
- [coreApiFavorites/favorites.feature:124](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L124)
- [coreApiFavorites/favorites.feature:125](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L125)
- [coreApiFavorites/favorites.feature:126](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L126)
- [coreApiFavorites/favorites.feature:189](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L189)
- [coreApiFavorites/favorites.feature:190](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L190)
- [coreApiFavorites/favorites.feature:191](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L191)
- [coreApiFavorites/favorites.feature:145](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L145)
- [coreApiFavorites/favorites.feature:146](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L146)
- [coreApiFavorites/favorites.feature:147](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L147)
- [coreApiFavorites/favorites.feature:174](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L174)
- [coreApiFavorites/favorites.feature:175](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L175)
- [coreApiFavorites/favorites.feature:176](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L176)
- [coreApiFavorites/favoritesSharingToShares.feature:91](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favoritesSharingToShares.feature#L91)
- [coreApiFavorites/favoritesSharingToShares.feature:92](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favoritesSharingToShares.feature#L92)
- [coreApiFavorites/favoritesSharingToShares.feature:93](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favoritesSharingToShares.feature#L93)
#### [WWW-Authenticate header for unauthenticated requests is not clear](https://github.com/owncloud/ocis/issues/2285)
- [coreApiWebdavOperations/refuseAccess.feature:21](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/refuseAccess.feature#L21)
- [coreApiWebdavOperations/refuseAccess.feature:22](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/refuseAccess.feature#L22)
#### [PATCH request for TUS upload with wrong checksum gives incorrect response](https://github.com/owncloud/ocis/issues/1755)
- [coreApiWebdavUploadTUS/checksums.feature:74](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L74)
- [coreApiWebdavUploadTUS/checksums.feature:75](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L75)
- [coreApiWebdavUploadTUS/checksums.feature:76](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L76)
- [coreApiWebdavUploadTUS/checksums.feature:77](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L77)
- [coreApiWebdavUploadTUS/checksums.feature:79](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L79)
- [coreApiWebdavUploadTUS/checksums.feature:78](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L78)
- [coreApiWebdavUploadTUS/checksums.feature:147](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L147)
- [coreApiWebdavUploadTUS/checksums.feature:148](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L148)
- [coreApiWebdavUploadTUS/checksums.feature:149](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L149)
- [coreApiWebdavUploadTUS/checksums.feature:192](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L192)
- [coreApiWebdavUploadTUS/checksums.feature:193](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L193)
- [coreApiWebdavUploadTUS/checksums.feature:194](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L194)
- [coreApiWebdavUploadTUS/checksums.feature:195](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L195)
- [coreApiWebdavUploadTUS/checksums.feature:196](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L196)
- [coreApiWebdavUploadTUS/checksums.feature:197](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L197)
- [coreApiWebdavUploadTUS/checksums.feature:240](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L240)
- [coreApiWebdavUploadTUS/checksums.feature:241](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L241)
- [coreApiWebdavUploadTUS/checksums.feature:242](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L242)
- [coreApiWebdavUploadTUS/checksums.feature:243](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L243)
- [coreApiWebdavUploadTUS/checksums.feature:244](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L244)
- [coreApiWebdavUploadTUS/checksums.feature:245](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L245)
- [coreApiWebdavUploadTUS/uploadToShare.feature:255](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L255)
- [coreApiWebdavUploadTUS/uploadToShare.feature:256](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L256)
- [coreApiWebdavUploadTUS/uploadToShare.feature:279](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L279)
- [coreApiWebdavUploadTUS/uploadToShare.feature:280](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L280)
- [coreApiWebdavUploadTUS/uploadToShare.feature:376](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L376)
- [coreApiWebdavUploadTUS/uploadToShare.feature:377](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L377)
#### [Renaming resource to banned name is allowed in spaces webdav](https://github.com/owncloud/ocis/issues/3099)
- [coreApiWebdavMove2/moveFile.feature:143](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveFile.feature#L143)
- [coreApiWebdavMove1/moveFolder.feature:36](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L36)
- [coreApiWebdavMove1/moveFolder.feature:50](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L50)
- [coreApiWebdavMove1/moveFolder.feature:64](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L64)
#### [Trying to delete other user's trashbin item returns 409 for spaces path instead of 404](https://github.com/owncloud/ocis/issues/9791)
- [coreApiTrashbin/trashbinDelete.feature:92](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinDelete.feature#L92)
#### [MOVE a file into same folder with same name returns 404 instead of 403](https://github.com/owncloud/ocis/issues/1976)
- [coreApiWebdavMove2/moveFile.feature:100](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveFile.feature#L100)
- [coreApiWebdavMove2/moveFile.feature:101](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveFile.feature#L101)
- [coreApiWebdavMove2/moveFile.feature:102](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveFile.feature#L102)
- [coreApiWebdavMove1/moveFolder.feature:217](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L217)
- [coreApiWebdavMove1/moveFolder.feature:218](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L218)
- [coreApiWebdavMove1/moveFolder.feature:219](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L219)
- [coreApiWebdavMove2/moveShareOnOpencloud.feature:334](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveShareOnOpencloud.feature#L334)
- [coreApiWebdavMove2/moveShareOnOpencloud.feature:337](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveShareOnOpencloud.feature#L337)
- [coreApiWebdavMove2/moveShareOnOpencloud.feature:340](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveShareOnOpencloud.feature#L340)
#### [COPY file/folder to same name is possible (but 500 code error for folder with spaces path)](https://github.com/owncloud/ocis/issues/8711)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:198](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L198)
- [coreApiWebdavProperties/copyFile.feature:1094](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/copyFile.feature#L1094)
- [coreApiWebdavProperties/copyFile.feature:1095](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/copyFile.feature#L1095)
- [coreApiWebdavProperties/copyFile.feature:1096](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/copyFile.feature#L1096)
#### [Trying to restore personal file to file of share received folder returns 403 but the share file is deleted (new dav path)](https://github.com/owncloud/ocis/issues/10356)
- [coreApiTrashbin/trashbinSharingToShares.feature:277](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L277)
#### [Preview. UTF characters do not display on prievew](https://github.com/opencloud-eu/opencloud/issues/1451)
- [coreApiWebdavPreviews/previews.feature:249](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L249)
- [coreApiWebdavPreviews/previews.feature:250](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L250)
- [coreApiWebdavPreviews/previews.feature:251](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L251)
#### [Preview of text file truncated](https://github.com/opencloud-eu/opencloud/issues/1452)
- [coreApiWebdavPreviews/previews.feature:263](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L263)
- [coreApiWebdavPreviews/previews.feature:264](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L264)
- [coreApiWebdavPreviews/previews.feature:265](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L265)
### Won't fix
Not everything needs to be implemented for opencloud.
- _Blacklisted ignored files are no longer required because opencloud can handle `.htaccess` files without security implications introduced by serving user provided files with apache._
Note: always have an empty line at the end of this file.
The bash script that processes this file requires that the last line has a newline on the end.

View File

@@ -193,9 +193,9 @@
- [apiServiceAvailability/serviceAvailabilityCheck.feature:123](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature#L123)
#### [Skip tests for different languages](https://github.com/opencloud-eu/opencloud/issues/183)
- [apiActivities/activities.feature:2598](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiActivities/activities.feature#L2598)
#### [Missing properties in REPORT response](https://github.com/owncloud/ocis/issues/9780), [d:getetag property has empty value in REPORT response](https://github.com/owncloud/ocis/issues/9783)
- [apiSearch1/search.feature:437](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L437)
@@ -205,160 +205,6 @@
- [apiSearch1/search.feature:466](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L466)
- [apiSearch1/search.feature:467](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L467)
## Scenarios from core API tests that are expected to fail with decomposed storage
### File
Basic file management like up and download, move, copy, properties, trash, versions and chunking.
#### [Custom dav properties with namespaces are rendered incorrectly](https://github.com/owncloud/ocis/issues/2140)
_ocdav: double-check the webdav property parsing when custom namespaces are used_
- [coreApiWebdavProperties/setFileProperties.feature:128](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/setFileProperties.feature#L128)
- [coreApiWebdavProperties/setFileProperties.feature:129](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/setFileProperties.feature#L129)
- [coreApiWebdavProperties/setFileProperties.feature:130](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/setFileProperties.feature#L130)
### Sync
Synchronization features like etag propagation, setting mtime and locking files
#### [Uploading an old method chunked file with checksum should fail using new DAV path](https://github.com/owncloud/ocis/issues/2323)
- [coreApiMain/checksums.feature:233](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiMain/checksums.feature#L233)
- [coreApiMain/checksums.feature:234](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiMain/checksums.feature#L234)
- [coreApiMain/checksums.feature:235](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiMain/checksums.feature#L235)
### Share
#### [d:quota-available-bytes in dprop of PROPFIND give wrong response value](https://github.com/owncloud/ocis/issues/8197)
- [coreApiWebdavProperties/getQuota.feature:57](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L57)
- [coreApiWebdavProperties/getQuota.feature:58](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L58)
- [coreApiWebdavProperties/getQuota.feature:59](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L59)
- [coreApiWebdavProperties/getQuota.feature:73](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L73)
- [coreApiWebdavProperties/getQuota.feature:74](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L74)
- [coreApiWebdavProperties/getQuota.feature:75](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L75)
#### [deleting a file inside a received shared folder is moved to the trash-bin of the sharer not the receiver](https://github.com/owncloud/ocis/issues/1124)
- [coreApiTrashbin/trashbinSharingToShares.feature:54](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L54)
- [coreApiTrashbin/trashbinSharingToShares.feature:55](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L55)
- [coreApiTrashbin/trashbinSharingToShares.feature:56](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L56)
- [coreApiTrashbin/trashbinSharingToShares.feature:83](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L83)
- [coreApiTrashbin/trashbinSharingToShares.feature:84](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L84)
- [coreApiTrashbin/trashbinSharingToShares.feature:85](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L85)
- [coreApiTrashbin/trashbinSharingToShares.feature:142](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L142)
- [coreApiTrashbin/trashbinSharingToShares.feature:143](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L143)
- [coreApiTrashbin/trashbinSharingToShares.feature:144](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L144)
- [coreApiTrashbin/trashbinSharingToShares.feature:202](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L202)
- [coreApiTrashbin/trashbinSharingToShares.feature:203](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L203)
### Other
API, search, favorites, config, capabilities, not existing endpoints, CORS and others
#### [sending MKCOL requests to another or non-existing user's webDav endpoints as normal user should return 404](https://github.com/owncloud/ocis/issues/5049)
_ocdav: api compatibility, return correct status code_
- [coreApiAuth/webDavMKCOLAuth.feature:42](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavMKCOLAuth.feature#L42)
- [coreApiAuth/webDavMKCOLAuth.feature:53](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavMKCOLAuth.feature#L53)
#### [trying to lock file of another user gives http 500](https://github.com/owncloud/ocis/issues/2176)
- [coreApiAuth/webDavLOCKAuth.feature:46](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavLOCKAuth.feature#L46)
- [coreApiAuth/webDavLOCKAuth.feature:58](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavLOCKAuth.feature#L58)
#### [Support for favorites](https://github.com/owncloud/ocis/issues/1228)
- [coreApiFavorites/favorites.feature:101](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L101)
- [coreApiFavorites/favorites.feature:102](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L102)
- [coreApiFavorites/favorites.feature:103](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L103)
- [coreApiFavorites/favorites.feature:124](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L124)
- [coreApiFavorites/favorites.feature:125](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L125)
- [coreApiFavorites/favorites.feature:126](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L126)
- [coreApiFavorites/favorites.feature:189](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L189)
- [coreApiFavorites/favorites.feature:190](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L190)
- [coreApiFavorites/favorites.feature:191](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L191)
- [coreApiFavorites/favorites.feature:145](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L145)
- [coreApiFavorites/favorites.feature:146](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L146)
- [coreApiFavorites/favorites.feature:147](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L147)
- [coreApiFavorites/favorites.feature:174](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L174)
- [coreApiFavorites/favorites.feature:175](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L175)
- [coreApiFavorites/favorites.feature:176](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L176)
- [coreApiFavorites/favoritesSharingToShares.feature:91](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favoritesSharingToShares.feature#L91)
- [coreApiFavorites/favoritesSharingToShares.feature:92](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favoritesSharingToShares.feature#L92)
- [coreApiFavorites/favoritesSharingToShares.feature:93](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favoritesSharingToShares.feature#L93)
#### [WWW-Authenticate header for unauthenticated requests is not clear](https://github.com/owncloud/ocis/issues/2285)
- [coreApiWebdavOperations/refuseAccess.feature:21](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/refuseAccess.feature#L21)
- [coreApiWebdavOperations/refuseAccess.feature:22](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/refuseAccess.feature#L22)
#### [PATCH request for TUS upload with wrong checksum gives incorrect response](https://github.com/owncloud/ocis/issues/1755)
- [coreApiWebdavUploadTUS/checksums.feature:74](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L74)
- [coreApiWebdavUploadTUS/checksums.feature:75](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L75)
- [coreApiWebdavUploadTUS/checksums.feature:76](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L76)
- [coreApiWebdavUploadTUS/checksums.feature:77](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L77)
- [coreApiWebdavUploadTUS/checksums.feature:79](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L79)
- [coreApiWebdavUploadTUS/checksums.feature:78](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L78)
- [coreApiWebdavUploadTUS/checksums.feature:147](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L147)
- [coreApiWebdavUploadTUS/checksums.feature:148](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L148)
- [coreApiWebdavUploadTUS/checksums.feature:149](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L149)
- [coreApiWebdavUploadTUS/checksums.feature:192](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L192)
- [coreApiWebdavUploadTUS/checksums.feature:193](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L193)
- [coreApiWebdavUploadTUS/checksums.feature:194](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L194)
- [coreApiWebdavUploadTUS/checksums.feature:195](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L195)
- [coreApiWebdavUploadTUS/checksums.feature:196](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L196)
- [coreApiWebdavUploadTUS/checksums.feature:197](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L197)
- [coreApiWebdavUploadTUS/checksums.feature:240](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L240)
- [coreApiWebdavUploadTUS/checksums.feature:241](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L241)
- [coreApiWebdavUploadTUS/checksums.feature:242](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L242)
- [coreApiWebdavUploadTUS/checksums.feature:243](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L243)
- [coreApiWebdavUploadTUS/checksums.feature:244](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L244)
- [coreApiWebdavUploadTUS/checksums.feature:245](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L245)
- [coreApiWebdavUploadTUS/uploadToShare.feature:255](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L255)
- [coreApiWebdavUploadTUS/uploadToShare.feature:256](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L256)
- [coreApiWebdavUploadTUS/uploadToShare.feature:279](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L279)
- [coreApiWebdavUploadTUS/uploadToShare.feature:280](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L280)
- [coreApiWebdavUploadTUS/uploadToShare.feature:376](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L376)
- [coreApiWebdavUploadTUS/uploadToShare.feature:377](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L377)
#### [Renaming resource to banned name is allowed in spaces webdav](https://github.com/owncloud/ocis/issues/3099)
- [coreApiWebdavMove2/moveFile.feature:143](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveFile.feature#L143)
- [coreApiWebdavMove1/moveFolder.feature:36](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L36)
- [coreApiWebdavMove1/moveFolder.feature:50](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L50)
- [coreApiWebdavMove1/moveFolder.feature:64](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L64)
#### [Trying to delete other user's trashbin item returns 409 for spaces path instead of 404](https://github.com/owncloud/ocis/issues/9791)
- [coreApiTrashbin/trashbinDelete.feature:92](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinDelete.feature#L92)
#### [MOVE a file into same folder with same name returns 404 instead of 403](https://github.com/owncloud/ocis/issues/1976)
- [coreApiWebdavMove2/moveFile.feature:100](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveFile.feature#L100)
- [coreApiWebdavMove2/moveFile.feature:101](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveFile.feature#L101)
- [coreApiWebdavMove2/moveFile.feature:102](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveFile.feature#L102)
- [coreApiWebdavMove1/moveFolder.feature:217](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L217)
- [coreApiWebdavMove1/moveFolder.feature:218](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L218)
- [coreApiWebdavMove1/moveFolder.feature:219](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L219)
- [coreApiWebdavMove2/moveShareOnOpencloud.feature:334](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveShareOnOpencloud.feature#L334)
- [coreApiWebdavMove2/moveShareOnOpencloud.feature:337](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveShareOnOpencloud.feature#L337)
- [coreApiWebdavMove2/moveShareOnOpencloud.feature:340](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveShareOnOpencloud.feature#L340)
#### [COPY file/folder to same name is possible (but 500 code error for folder with spaces path)](https://github.com/owncloud/ocis/issues/8711)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:198](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L198)
- [coreApiWebdavProperties/copyFile.feature:1094](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/copyFile.feature#L1094)
- [coreApiWebdavProperties/copyFile.feature:1095](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/copyFile.feature#L1095)
- [coreApiWebdavProperties/copyFile.feature:1096](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/copyFile.feature#L1096)
#### [Trying to restore personal file to file of share received folder returns 403 but the share file is deleted (new dav path)](https://github.com/owncloud/ocis/issues/10356)
- [coreApiTrashbin/trashbinSharingToShares.feature:277](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L277)
#### [Preview. UTF characters do not display on prievew](https://github.com/opencloud-eu/opencloud/issues/1451)
@@ -372,11 +218,5 @@ _ocdav: api compatibility, return correct status code_
- [coreApiWebdavPreviews/previews.feature:264](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L264)
- [coreApiWebdavPreviews/previews.feature:265](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L265)
### Won't fix
Not everything needs to be implemented for opencloud.
- _Blacklisted ignored files are no longer required because opencloud can handle `.htaccess` files without security implications introduced by serving user provided files with apache._
Note: always have an empty line at the end of this file.
The bash script that processes this file requires that the last line has a newline on the end.

View File

@@ -1,4 +1,4 @@
## Scenarios from OpenCloud API tests that are expected to fail with posix storage
## Scenarios from OpenCloud API tests that are expected to fail with decomposed storage
#### [Downloading the archive of the resource (files | folder) using resource path is not possible](https://github.com/owncloud/ocis/issues/4637)
@@ -193,9 +193,9 @@
- [apiServiceAvailability/serviceAvailabilityCheck.feature:123](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature#L123)
#### [Skip tests for different languages](https://github.com/opencloud-eu/opencloud/issues/183)
- [apiActivities/activities.feature:2598](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiActivities/activities.feature#L2598)
#### [Missing properties in REPORT response](https://github.com/owncloud/ocis/issues/9780), [d:getetag property has empty value in REPORT response](https://github.com/owncloud/ocis/issues/9783)
- [apiSearch1/search.feature:437](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L437)
@@ -205,178 +205,5 @@
- [apiSearch1/search.feature:466](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L466)
- [apiSearch1/search.feature:467](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L467)
## Scenarios from core API tests that are expected to fail with posix storage
### File
Basic file management like up and download, move, copy, properties, trash, versions and chunking.
#### [Custom dav properties with namespaces are rendered incorrectly](https://github.com/owncloud/ocis/issues/2140)
_ocdav: double-check the webdav property parsing when custom namespaces are used_
- [coreApiWebdavProperties/setFileProperties.feature:128](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/setFileProperties.feature#L128)
- [coreApiWebdavProperties/setFileProperties.feature:129](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/setFileProperties.feature#L129)
- [coreApiWebdavProperties/setFileProperties.feature:130](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/setFileProperties.feature#L130)
### Sync
Synchronization features like etag propagation, setting mtime and locking files
#### [Uploading an old method chunked file with checksum should fail using new DAV path](https://github.com/owncloud/ocis/issues/2323)
- [coreApiMain/checksums.feature:233](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiMain/checksums.feature#L233)
- [coreApiMain/checksums.feature:234](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiMain/checksums.feature#L234)
- [coreApiMain/checksums.feature:235](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiMain/checksums.feature#L235)
### Share
#### [d:quota-available-bytes in dprop of PROPFIND give wrong response value](https://github.com/owncloud/ocis/issues/8197)
- [coreApiWebdavProperties/getQuota.feature:57](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L57)
- [coreApiWebdavProperties/getQuota.feature:58](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L58)
- [coreApiWebdavProperties/getQuota.feature:59](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L59)
- [coreApiWebdavProperties/getQuota.feature:73](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L73)
- [coreApiWebdavProperties/getQuota.feature:74](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L74)
- [coreApiWebdavProperties/getQuota.feature:75](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/getQuota.feature#L75)
#### [deleting a file inside a received shared folder is moved to the trash-bin of the sharer not the receiver](https://github.com/owncloud/ocis/issues/1124)
- [coreApiTrashbin/trashbinSharingToShares.feature:54](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L54)
- [coreApiTrashbin/trashbinSharingToShares.feature:55](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L55)
- [coreApiTrashbin/trashbinSharingToShares.feature:56](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L56)
- [coreApiTrashbin/trashbinSharingToShares.feature:83](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L83)
- [coreApiTrashbin/trashbinSharingToShares.feature:84](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L84)
- [coreApiTrashbin/trashbinSharingToShares.feature:85](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L85)
- [coreApiTrashbin/trashbinSharingToShares.feature:142](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L142)
- [coreApiTrashbin/trashbinSharingToShares.feature:143](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L143)
- [coreApiTrashbin/trashbinSharingToShares.feature:144](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L144)
- [coreApiTrashbin/trashbinSharingToShares.feature:202](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L202)
- [coreApiTrashbin/trashbinSharingToShares.feature:203](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L203)
### Other
API, search, favorites, config, capabilities, not existing endpoints, CORS and others
#### [sending MKCOL requests to another or non-existing user's webDav endpoints as normal user should return 404](https://github.com/owncloud/ocis/issues/5049)
_ocdav: api compatibility, return correct status code_
- [coreApiAuth/webDavMKCOLAuth.feature:42](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavMKCOLAuth.feature#L42)
- [coreApiAuth/webDavMKCOLAuth.feature:53](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavMKCOLAuth.feature#L53)
#### [trying to lock file of another user gives http 500](https://github.com/owncloud/ocis/issues/2176)
- [coreApiAuth/webDavLOCKAuth.feature:46](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavLOCKAuth.feature#L46)
- [coreApiAuth/webDavLOCKAuth.feature:58](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavLOCKAuth.feature#L58)
#### [Support for favorites](https://github.com/owncloud/ocis/issues/1228)
- [coreApiFavorites/favorites.feature:101](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L101)
- [coreApiFavorites/favorites.feature:102](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L102)
- [coreApiFavorites/favorites.feature:103](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L103)
- [coreApiFavorites/favorites.feature:124](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L124)
- [coreApiFavorites/favorites.feature:125](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L125)
- [coreApiFavorites/favorites.feature:126](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L126)
- [coreApiFavorites/favorites.feature:189](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L189)
- [coreApiFavorites/favorites.feature:190](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L190)
- [coreApiFavorites/favorites.feature:191](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L191)
- [coreApiFavorites/favorites.feature:145](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L145)
- [coreApiFavorites/favorites.feature:146](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L146)
- [coreApiFavorites/favorites.feature:147](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L147)
- [coreApiFavorites/favorites.feature:174](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L174)
- [coreApiFavorites/favorites.feature:175](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L175)
- [coreApiFavorites/favorites.feature:176](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favorites.feature#L176)
- [coreApiFavorites/favoritesSharingToShares.feature:91](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favoritesSharingToShares.feature#L91)
- [coreApiFavorites/favoritesSharingToShares.feature:92](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favoritesSharingToShares.feature#L92)
- [coreApiFavorites/favoritesSharingToShares.feature:93](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiFavorites/favoritesSharingToShares.feature#L93)
#### [WWW-Authenticate header for unauthenticated requests is not clear](https://github.com/owncloud/ocis/issues/2285)
- [coreApiWebdavOperations/refuseAccess.feature:21](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/refuseAccess.feature#L21)
- [coreApiWebdavOperations/refuseAccess.feature:22](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/refuseAccess.feature#L22)
#### [PATCH request for TUS upload with wrong checksum gives incorrect response](https://github.com/owncloud/ocis/issues/1755)
- [coreApiWebdavUploadTUS/checksums.feature:74](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L74)
- [coreApiWebdavUploadTUS/checksums.feature:75](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L75)
- [coreApiWebdavUploadTUS/checksums.feature:76](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L76)
- [coreApiWebdavUploadTUS/checksums.feature:77](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L77)
- [coreApiWebdavUploadTUS/checksums.feature:79](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L79)
- [coreApiWebdavUploadTUS/checksums.feature:78](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L78)
- [coreApiWebdavUploadTUS/checksums.feature:147](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L147)
- [coreApiWebdavUploadTUS/checksums.feature:148](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L148)
- [coreApiWebdavUploadTUS/checksums.feature:149](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L149)
- [coreApiWebdavUploadTUS/checksums.feature:192](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L192)
- [coreApiWebdavUploadTUS/checksums.feature:193](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L193)
- [coreApiWebdavUploadTUS/checksums.feature:194](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L194)
- [coreApiWebdavUploadTUS/checksums.feature:195](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L195)
- [coreApiWebdavUploadTUS/checksums.feature:196](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L196)
- [coreApiWebdavUploadTUS/checksums.feature:197](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L197)
- [coreApiWebdavUploadTUS/checksums.feature:240](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L240)
- [coreApiWebdavUploadTUS/checksums.feature:241](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L241)
- [coreApiWebdavUploadTUS/checksums.feature:242](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L242)
- [coreApiWebdavUploadTUS/checksums.feature:243](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L243)
- [coreApiWebdavUploadTUS/checksums.feature:244](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L244)
- [coreApiWebdavUploadTUS/checksums.feature:245](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/checksums.feature#L245)
- [coreApiWebdavUploadTUS/uploadToShare.feature:255](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L255)
- [coreApiWebdavUploadTUS/uploadToShare.feature:256](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L256)
- [coreApiWebdavUploadTUS/uploadToShare.feature:279](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L279)
- [coreApiWebdavUploadTUS/uploadToShare.feature:280](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L280)
- [coreApiWebdavUploadTUS/uploadToShare.feature:376](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L376)
- [coreApiWebdavUploadTUS/uploadToShare.feature:377](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L377)
#### [Renaming resource to banned name is allowed in spaces webdav](https://github.com/owncloud/ocis/issues/3099)
- [coreApiWebdavMove2/moveFile.feature:143](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveFile.feature#L143)
- [coreApiWebdavMove1/moveFolder.feature:36](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L36)
- [coreApiWebdavMove1/moveFolder.feature:50](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L50)
- [coreApiWebdavMove1/moveFolder.feature:64](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L64)
#### [Trying to delete other user's trashbin item returns 409 for spaces path instead of 404](https://github.com/owncloud/ocis/issues/9791)
- [coreApiTrashbin/trashbinDelete.feature:92](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinDelete.feature#L92)
#### [MOVE a file into same folder with same name returns 404 instead of 403](https://github.com/owncloud/ocis/issues/1976)
- [coreApiWebdavMove2/moveFile.feature:100](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveFile.feature#L100)
- [coreApiWebdavMove2/moveFile.feature:101](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveFile.feature#L101)
- [coreApiWebdavMove2/moveFile.feature:102](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveFile.feature#L102)
- [coreApiWebdavMove1/moveFolder.feature:217](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L217)
- [coreApiWebdavMove1/moveFolder.feature:218](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L218)
- [coreApiWebdavMove1/moveFolder.feature:219](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove1/moveFolder.feature#L219)
- [coreApiWebdavMove2/moveShareOnOpencloud.feature:334](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveShareOnOpencloud.feature#L334)
- [coreApiWebdavMove2/moveShareOnOpencloud.feature:337](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveShareOnOpencloud.feature#L337)
- [coreApiWebdavMove2/moveShareOnOpencloud.feature:340](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove2/moveShareOnOpencloud.feature#L340)
#### [COPY file/folder to same name is possible (but 500 code error for folder with spaces path)](https://github.com/owncloud/ocis/issues/8711)
- [coreApiSharePublicLink2/copyFromPublicLink.feature:198](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L198)
- [coreApiWebdavProperties/copyFile.feature:1094](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/copyFile.feature#L1094)
- [coreApiWebdavProperties/copyFile.feature:1095](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/copyFile.feature#L1095)
- [coreApiWebdavProperties/copyFile.feature:1096](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/copyFile.feature#L1096)
#### [Trying to restore personal file to file of share received folder returns 403 but the share file is deleted (new dav path)](https://github.com/owncloud/ocis/issues/10356)
- [coreApiTrashbin/trashbinSharingToShares.feature:277](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinSharingToShares.feature#L277)
#### [Preview. UTF characters do not display on prievew](https://github.com/opencloud-eu/opencloud/issues/1451)
- [coreApiWebdavPreviews/previews.feature:249](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L249)
- [coreApiWebdavPreviews/previews.feature:250](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L250)
- [coreApiWebdavPreviews/previews.feature:251](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L251)
#### [Preview of text file truncated](https://github.com/opencloud-eu/opencloud/issues/1452)
- [coreApiWebdavPreviews/previews.feature:263](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L263)
- [coreApiWebdavPreviews/previews.feature:264](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L264)
- [coreApiWebdavPreviews/previews.feature:265](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L265)
### Won't fix
Not everything needs to be implemented for opencloud.
- _Blacklisted ignored files are no longer required because opencloud can handle `.htaccess` files without security implications introduced by serving user provided files with apache._
Note: always have an empty line at the end of this file.
The bash script that processes this file requires that the last line has a newline on the end.

View File

@@ -1,32 +1,4 @@
#!/usr/bin/env bash
# Terminal colors
if [ -n "${PLAIN_OUTPUT}" ]; then
# No colors
TC_GREEN=""
TC_RED=""
TC_CYAN=""
TC_RESET=""
else
# Colors
TC_GREEN="\033[32m"
TC_RED="\033[31m"
TC_CYAN="\033[36m"
TC_RESET="\033[0m"
fi
function log_failed(){
printf "${TC_RED}[FAILED] %s\n${TC_RESET}" "$1"
}
function log_info(){
printf "${TC_CYAN}[INFO] %s\n${TC_RESET}" "$1"
}
function log_error(){
printf "${TC_RED}[ERROR] %s\n${TC_RESET}" "$1"
}
[[ "${DEBUG}" == "true" ]] && set -x
# from http://stackoverflow.com/a/630387
@@ -59,8 +31,15 @@ if [ -n "${PLAIN_OUTPUT}" ]
then
# explicitly tell Behat to not do colored output
COLORS_OPTION="--no-colors"
# Use the Bash "null" command to do nothing, rather than use tput to set a color
RED_COLOR=":"
GREEN_COLOR=":"
YELLOW_COLOR=":"
else
COLORS_OPTION="--colors"
RED_COLOR="tput setaf 1"
GREEN_COLOR="tput setaf 2"
YELLOW_COLOR="tput setaf 3"
fi
# The following environment variables can be specified:
@@ -82,7 +61,7 @@ then
LINT_STATUS=$?
if [ ${LINT_STATUS} -ne 0 ]
then
log_error "Expected failures file ${EXPECTED_FAILURES_FILE} is invalid"
echo "Error: expected failures file ${EXPECTED_FAILURES_FILE} is invalid"
exit ${LINT_STATUS}
fi
fi
@@ -245,7 +224,7 @@ function run_behat_tests() {
# So exit the tests and do not lint expected failures when undefined steps exist.
if [[ ${SCENARIO_RESULTS} == *"undefined"* ]]
then
log_error "Undefined steps: There were some undefined steps found."
${RED_COLOR}; echo -e "Undefined steps: There were some undefined steps found."
exit 1
fi
# If there were no scenarios in the requested suite or feature that match
@@ -258,7 +237,7 @@ function run_behat_tests() {
MATCHING_COUNT=`grep -ca '^No scenarios$' ${TEST_LOG_FILE}`
if [ ${MATCHING_COUNT} -eq 1 ]
then
log_info "No matching scenarios were found."
echo "Information: no matching scenarios were found."
BEHAT_EXIT_STATUS=0
else
# Find the count of scenarios that passed and failed
@@ -301,9 +280,9 @@ function run_behat_tests() {
then
if [ -n "${BEHAT_SUITE_TO_RUN}" ]
then
log_info "Checking expected failures for suite: ${BEHAT_SUITE_TO_RUN}"
echo "Checking expected failures for suite ${BEHAT_SUITE_TO_RUN}"
else
log_info "Checking expected failures..."
echo "Checking expected failures"
fi
# Check that every failed scenario is in the list of expected failures
@@ -316,7 +295,7 @@ function run_behat_tests() {
grep "\[${SUITE_SCENARIO}\]" "${EXPECTED_FAILURES_FILE}" > /dev/null
if [ $? -ne 0 ]
then
log_error "Scenario ${SUITE_SCENARIO} failed but was not expected to fail."
echo "Error: Scenario ${SUITE_SCENARIO} failed but was not expected to fail."
UNEXPECTED_FAILED_SCENARIOS+=("${SUITE_SCENARIO}")
fi
done
@@ -357,7 +336,7 @@ function run_behat_tests() {
echo "${FAILED_SCENARIO_PATHS}" | grep ${SUITE_SCENARIO}$ > /dev/null
if [ $? -ne 0 ]
then
log_error "Scenario ${SUITE_SCENARIO} was expected to fail but did not fail."
echo "Info: Scenario ${SUITE_SCENARIO} was expected to fail but did not fail."
UNEXPECTED_PASSED_SCENARIOS+=("${SUITE_SCENARIO}")
fi
done < ${EXPECTED_FAILURES_FILE}
@@ -394,7 +373,7 @@ function run_behat_tests() {
:
else
echo ""
log_info "The following tests were skipped because they are tagged @skip:"
echo "The following tests were skipped because they are tagged @skip:"
cat "${DRY_RUN_FILE}" | tee -a ${TEST_LOG_FILE}
fi
rm -f "${DRY_RUN_FILE}"
@@ -589,6 +568,10 @@ for i in "${!BEHAT_SUITES[@]}"
done
done
TOTAL_SCENARIOS=$((SCENARIOS_THAT_PASSED + SCENARIOS_THAT_FAILED))
echo "runsh: Total ${TOTAL_SCENARIOS} scenarios (${SCENARIOS_THAT_PASSED} passed, ${SCENARIOS_THAT_FAILED} failed)"
# 3 types of things can have gone wrong:
# - some scenario failed (and it was not expected to fail)
# - some scenario passed (but it was expected to fail)
@@ -660,42 +643,37 @@ fi
if [ -n "${EXPECTED_FAILURES_FILE}" ]
then
log_info "Exit code after checking expected failures: ${FINAL_EXIT_STATUS}"
echo "runsh: Exit code after checking expected failures: ${FINAL_EXIT_STATUS}"
fi
if [ "${UNEXPECTED_FAILURE}" = true ]
then
log_failed "Total unexpected failed scenarios:"
printf "${TC_RED}- %s\n${TC_RESET}" "${UNEXPECTED_FAILED_SCENARIOS[@]}"
echo ""
${YELLOW_COLOR}; echo "runsh: Total unexpected failed scenarios throughout the test run:"
${RED_COLOR}; printf "%s\n" "${UNEXPECTED_FAILED_SCENARIOS[@]}"
else
log_info "There were no unexpected failures."
${GREEN_COLOR}; echo "runsh: There were no unexpected failures."
fi
if [ "${UNEXPECTED_SUCCESS}" = true ]
then
log_error "Total unexpected passed scenarios:"
printf "${TC_GREEN}- %s\n${TC_RESET}" "${ACTUAL_UNEXPECTED_PASS[@]}"
echo ""
${YELLOW_COLOR}; echo "runsh: Total unexpected passed scenarios throughout the test run:"
${RED_COLOR}; printf "%s\n" "${ACTUAL_UNEXPECTED_PASS[@]}"
else
log_info "There were no unexpected success."
${GREEN_COLOR}; echo "runsh: There were no unexpected success."
fi
if [ "${UNEXPECTED_BEHAT_EXIT_STATUS}" = true ]
then
log_error "The following Behat test runs exited with non-zero status:"
printf "${TC_RED}%s\n${TC_RESET}" "${UNEXPECTED_BEHAT_EXIT_STATUSES[@]}"
${YELLOW_COLOR}; echo "runsh: The following Behat test runs exited with non-zero status:"
${RED_COLOR}; printf "%s\n" "${UNEXPECTED_BEHAT_EXIT_STATUSES[@]}"
fi
TOTAL_SCENARIOS=$((SCENARIOS_THAT_PASSED + SCENARIOS_THAT_FAILED))
printf "Summary: %s scenarios (${TC_GREEN}%s passed${TC_RESET}, ${TC_RED}%s failed${TC_RESET})" "${TOTAL_SCENARIOS}" "${SCENARIOS_THAT_PASSED}" "${SCENARIOS_THAT_FAILED}"
echo ""
# # sync the file-system so all output will be flushed to storage.
# # In CI, we sometimes see that the last lines of output are missing.
# # In drone we sometimes see that the last lines of output are missing from the
# # drone log.
# sync
# # If we are running in CI, then sleep for a bit to (hopefully) let the
# # If we are running in drone CI, then sleep for a bit to (hopefully) let the
# # drone agent send all the output to the drone server.
# if [ -n "${CI_REPO}" ]
# then

View File

@@ -1,7 +1,7 @@
#!/bin/bash
# Set required environment variables
export START_TIKA=true
export LOCAL_TEST=true
export START_EMAIL=true
export WITH_WRAPPER=true
export STORAGE_DRIVER=${STORAGE_DRIVER:-posix}
@@ -10,15 +10,11 @@ export TEST_ROOT_PATH="/drone/src/tests"
# LOCAL TEST WITHOUT EXTRA ENVS
TEST_SERVER_URL="https://opencloud-server:9200"
OC_WRAPPER_URL="http://opencloud-server:5200"
if [ "$STORAGE_DRIVER" = "posix" ]; then
EXPECTED_FAILURES_FILE="tests/acceptance/expected-failures-posix-storage.md"
else
EXPECTED_FAILURES_FILE="tests/acceptance/expected-failures-decomposed-storage.md"
fi
EXPECTED_FAILURES_FILE="tests/acceptance/expected-failures-localAPI-on-decomposed-storage.md"
EXPECTED_FAILURES_FILE_FROM_CORE="tests/acceptance/expected-failures-API-on-decomposed-storage.md"
# Start server
make -C tests/acceptance/docker start-services
make -C tests/acceptance/docker start-server
# Wait until the server responds with HTTP 200
echo "Waiting for server to start..."
@@ -143,7 +139,7 @@ for SUITE in "${CORE_SUITES[@]}"; do
LOG_FILE="$LOG_DIR/${SUITE}.log"
# Run suite
make test-acceptance-api TEST_SERVER_URL=$TEST_SERVER_URL EXPECTED_FAILURES_FILE=$EXPECTED_FAILURES_FILE BEHAT_SUITE=$SUITE SEND_SCENARIO_LINE_REFERENCES=true > "$LOG_FILE" 2>&1
make test-acceptance-api TEST_SERVER_URL=$TEST_SERVER_URL EXPECTED_FAILURES_FILE=$EXPECTED_FAILURES_FILE_FROM_CORE BEHAT_SUITE=$SUITE SEND_SCENARIO_LINE_REFERENCES=true > "$LOG_FILE" 2>&1
# Check if suite was successful
if [ $? -eq 0 ]; then

View File

@@ -1,13 +1,13 @@
#!/bin/bash
# Set required environment variables
export START_TIKA=true
export LOCAL_TEST=true
export WITH_WRAPPER=false
TEST_SERVER_URL="https://opencloud-server:9200"
# Start server
make -C tests/acceptance/docker start-services
make -C tests/acceptance/docker start-server
# Wait until the server responds with HTTP 200
echo "Waiting for server to start..."

View File

@@ -1,15 +1,5 @@
# Changelog
## [0.6.1]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.6.0...v0.6.1)
### Changed
- Perf improvements: replaced the ASCII lookup table with a simple
function. A bit more cache-friendly. More inlining.
- Bug fix: single regional indicators are now treated as width 2, since that
is what actual terminals do.
## [0.6.0]
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.5.0...v0.6.0)

View File

@@ -33,82 +33,42 @@ func main() {
}
```
For most purposes, you should use the `String` or `Bytes` methods. They sum
the widths of grapheme clusters in the string or byte slice.
For most purposes, you should use the `String` or `Bytes` methods.
> Note: in your application, iterating over runes to measure width is likely incorrect;
the smallest unit of display is a grapheme, not a rune.
### Iterating over graphemes
If you need the individual graphemes:
```go
import (
"fmt"
"github.com/clipperhouse/displaywidth"
)
func main() {
g := displaywidth.StringGraphemes("Hello, 世界!")
for g.Next() {
width := g.Width()
value := g.Value()
// do something with the width or value
}
}
```
### Options
There is one option, `displaywidth.Options.EastAsianWidth`, which defines
how [East Asian Ambiguous characters](https://www.unicode.org/reports/tr11/#Ambiguous)
are treated.
When `false` (default), East Asian Ambiguous characters are treated as width 1.
When `true`, they are treated as width 2.
You may wish to configure this based on environment variables or locale.
`go-runewidth`, for example, does so
[during package initialization](https://github.com/mattn/go-runewidth/blob/master/runewidth.go#L26C1-L45C2).
`displaywidth` does not do this automatically, we prefer to leave it to you.
You might do something like:
You can specify East Asian Width settings. When false (default),
[East Asian Ambiguous characters](https://www.unicode.org/reports/tr11/#Ambiguous)
are treated as width 1. When true, East Asian Ambiguous characters are treated
as width 2.
```go
var width displaywidth.Options // zero value is default
func init() {
if os.Getenv("EAST_ASIAN_WIDTH") == "true" {
width = displaywidth.Options{EastAsianWidth: true}
}
// or check locale, or any other logic you want
myOptions := displaywidth.Options{
EastAsianWidth: true,
}
// use it in your logic
func myApp() {
fmt.Println(width.String("Hello, 世界!"))
}
width := myOptions.String("Hello, 世界!")
fmt.Println(width)
```
## Technical standards and compatibility
## Technical details
This package implements the Unicode East Asian Width standard
([UAX #11](https://www.unicode.org/reports/tr11/tr11-43.html)), and handles
([UAX #11](https://www.unicode.org/reports/tr11/)), and handles
[version selectors](https://en.wikipedia.org/wiki/Variation_Selectors_(Unicode_block)),
and [regional indicator pairs](https://en.wikipedia.org/wiki/Regional_indicator_symbol)
(flags). We implement [Unicode TR51](https://www.unicode.org/reports/tr51/tr51-27.html). We are keeping
an eye on [emerging standards](https://www.jeffquast.com/post/state-of-terminal-emulation-2025/).
(flags). We implement [Unicode TR51](https://unicode.org/reports/tr51/).
`clipperhouse/displaywidth`, `mattn/go-runewidth`, and `rivo/uniseg` will
give the same outputs for most real-world text. Extensive details are in the
give the same outputs for most real-world text. See extensive details in the
[compatibility analysis](comparison/COMPATIBILITY_ANALYSIS.md).
If you wish to investigate the core logic, see the `lookupProperties` and `width`
functions in [width.go](width.go#L139). The essential trie generation logic is in
`buildPropertyBitmap` in [unicode.go](internal/gen/unicode.go#L316).
functions in [width.go](width.go#L135). The essential trie generation logic is in
`buildPropertyBitmap` in [unicode.go](internal/gen/unicode.go#L317).
I (@clipperhouse) am keeping an eye on [emerging standards and test suites](https://www.jeffquast.com/post/state-of-terminal-emulation-2025/).
## Prior Art
@@ -133,33 +93,31 @@ goarch: arm64
pkg: github.com/clipperhouse/displaywidth/comparison
cpu: Apple M2
BenchmarkString_Mixed/clipperhouse/displaywidth-8 10326 ns/op 163.37 MB/s 0 B/op 0 allocs/op
BenchmarkString_Mixed/mattn/go-runewidth-8 14415 ns/op 117.03 MB/s 0 B/op 0 allocs/op
BenchmarkString_Mixed/rivo/uniseg-8 19343 ns/op 87.21 MB/s 0 B/op 0 allocs/op
BenchmarkString_Mixed/clipperhouse/displaywidth-8 10469 ns/op 161.15 MB/s 0 B/op 0 allocs/op
BenchmarkString_Mixed/mattn/go-runewidth-8 14250 ns/op 118.39 MB/s 0 B/op 0 allocs/op
BenchmarkString_Mixed/rivo/uniseg-8 19258 ns/op 87.60 MB/s 0 B/op 0 allocs/op
BenchmarkString_EastAsian/clipperhouse/displaywidth-8 10561 ns/op 159.74 MB/s 0 B/op 0 allocs/op
BenchmarkString_EastAsian/mattn/go-runewidth-8 23790 ns/op 70.91 MB/s 0 B/op 0 allocs/op
BenchmarkString_EastAsian/rivo/uniseg-8 19322 ns/op 87.31 MB/s 0 B/op 0 allocs/op
BenchmarkString_EastAsian/clipperhouse/displaywidth-8 10518 ns/op 160.39 MB/s 0 B/op 0 allocs/op
BenchmarkString_EastAsian/mattn/go-runewidth-8 23827 ns/op 70.80 MB/s 0 B/op 0 allocs/op
BenchmarkString_EastAsian/rivo/uniseg-8 19537 ns/op 86.35 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/clipperhouse/displaywidth-8 1033 ns/op 123.88 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/mattn/go-runewidth-8 1168 ns/op 109.59 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/rivo/uniseg-8 1585 ns/op 80.74 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/clipperhouse/displaywidth-8 1027 ns/op 124.61 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/mattn/go-runewidth-8 1166 ns/op 109.78 MB/s 0 B/op 0 allocs/op
BenchmarkString_ASCII/rivo/uniseg-8 1551 ns/op 82.52 MB/s 0 B/op 0 allocs/op
BenchmarkString_Emoji/clipperhouse/displaywidth-8 3034 ns/op 238.61 MB/s 0 B/op 0 allocs/op
BenchmarkString_Emoji/mattn/go-runewidth-8 4797 ns/op 150.94 MB/s 0 B/op 0 allocs/op
BenchmarkString_Emoji/rivo/uniseg-8 6612 ns/op 109.50 MB/s 0 B/op 0 allocs/op
BenchmarkString_Emoji/clipperhouse/displaywidth-8 3164 ns/op 228.84 MB/s 0 B/op 0 allocs/op
BenchmarkString_Emoji/mattn/go-runewidth-8 4728 ns/op 153.13 MB/s 0 B/op 0 allocs/op
BenchmarkString_Emoji/rivo/uniseg-8 6489 ns/op 111.57 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Mixed/clipperhouse/displaywidth-8 3343 ns/op 504.67 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Mixed/mattn/go-runewidth-8 5414 ns/op 311.62 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Mixed/clipperhouse/displaywidth-8 3429 ns/op 491.96 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Mixed/mattn/go-runewidth-8 5308 ns/op 317.81 MB/s 0 B/op 0 allocs/op
BenchmarkRune_EastAsian/clipperhouse/displaywidth-8 3393 ns/op 497.17 MB/s 0 B/op 0 allocs/op
BenchmarkRune_EastAsian/mattn/go-runewidth-8 15312 ns/op 110.17 MB/s 0 B/op 0 allocs/op
BenchmarkRune_EastAsian/clipperhouse/displaywidth-8 3419 ns/op 493.49 MB/s 0 B/op 0 allocs/op
BenchmarkRune_EastAsian/mattn/go-runewidth-8 15321 ns/op 110.11 MB/s 0 B/op 0 allocs/op
BenchmarkRune_ASCII/clipperhouse/displaywidth-8 256.9 ns/op 498.32 MB/s 0 B/op 0 allocs/op
BenchmarkRune_ASCII/mattn/go-runewidth-8 265.7 ns/op 481.75 MB/s 0 B/op 0 allocs/op
BenchmarkRune_ASCII/clipperhouse/displaywidth-8 254.4 ns/op 503.19 MB/s 0 B/op 0 allocs/op
BenchmarkRune_ASCII/mattn/go-runewidth-8 264.3 ns/op 484.31 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Emoji/clipperhouse/displaywidth-8 1336 ns/op 541.96 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Emoji/mattn/go-runewidth-8 2304 ns/op 314.23 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Emoji/clipperhouse/displaywidth-8 1374 ns/op 527.02 MB/s 0 B/op 0 allocs/op
BenchmarkRune_Emoji/mattn/go-runewidth-8 2210 ns/op 327.66 MB/s 0 B/op 0 allocs/op
```
Here are some notes on [how to make Unicode things fast](https://clipperhouse.com/go-unicode/).

91
vendor/github.com/clipperhouse/displaywidth/tables.go generated vendored Normal file
View File

@@ -0,0 +1,91 @@
package displaywidth
// propertyWidths is a jump table of sorts, instead of a switch
var propertyWidths = [5]int{
_Default: 1,
_Zero_Width: 0,
_East_Asian_Wide: 2,
_East_Asian_Ambiguous: 1,
_Emoji: 2,
}
// asciiWidths is a lookup table for single-byte character widths. Printable
// ASCII characters have width 1, control characters have width 0.
//
// It is intended for valid single-byte UTF-8, which means <128.
//
// If you look up an index >= 128, that is either:
// - invalid UTF-8, or
// - a multi-byte UTF-8 sequence, in which case you should be operating on
// the grapheme cluster, and not using this table
//
// We will return a default value of 1 in those cases, so as not to panic.
var asciiWidths = [256]int8{
// Control characters (0x00-0x1F): width 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// Printable ASCII (0x20-0x7E): width 1
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
// DEL (0x7F): width 0
0,
// >= 128
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
}
// asciiProperties is a lookup table for single-byte character properties.
// It is intended for valid single-byte UTF-8, which means <128.
//
// If you look up an index >= 128, that is either:
// - invalid UTF-8, or
// - a multi-byte UTF-8 sequence, in which case you should be operating on
// the grapheme cluster, and not using this table
//
// We will return a default value of _Default in those cases, so as not to
// panic.
var asciiProperties = [256]property{
// Control characters (0x00-0x1F): _Zero_Width
_Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width,
_Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width,
_Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width,
_Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width,
// Printable ASCII (0x20-0x7E): _Default
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default,
// DEL (0x7F): _Zero_Width
_Zero_Width,
// >= 128
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
}

View File

@@ -10,10 +10,12 @@ type property uint8
const (
// Always 0 width, includes combining marks, control characters, non-printable, etc
_Zero_Width property = iota + 1
// Always 2 wide (East Asian Wide F/W, Emoji, Regional Indicator)
_Wide
// Always 2 wide (East Asian Wide F/W)
_East_Asian_Wide
// Width depends on EastAsianWidth option
_East_Asian_Ambiguous
// Extended_Pictographic + Emoji_Presentation
_Emoji
)
// lookup returns the trie value for the first UTF-8 encoding in s and
@@ -79,7 +81,7 @@ func lookup[T stringish.Interface](s T) (v uint8, sz int) {
return 0, 1
}
// stringWidthTrie. Total size: 17664 bytes (17.25 KiB). Checksum: c77d82ff2d69f0d2.
// stringWidthTrie. Total size: 17728 bytes (17.31 KiB). Checksum: b4b51ae347944fdb.
// type stringWidthTrie struct { }
// func newStringWidthTrie(i int) *stringWidthTrie {
@@ -94,9 +96,9 @@ func lookupValue(n uint32, b byte) uint8 {
}
}
// stringWidthValues: 246 blocks, 15744 entries, 15744 bytes
// stringWidthValues: 247 blocks, 15808 entries, 15808 bytes
// The third block is the zero block.
var stringWidthValues = [15744]uint8{
var stringWidthValues = [15808]uint8{
// Block 0x0, offset 0x0
// Block 0x1, offset 0x40
// Block 0x2, offset 0x80
@@ -575,13 +577,13 @@ var stringWidthValues = [15744]uint8{
0x167f: 0x0003,
// Block 0x5a, offset 0x1680
0x1692: 0x0003,
0x169a: 0x0002, 0x169b: 0x0002,
0x169a: 0x0004, 0x169b: 0x0004,
0x16a9: 0x0002,
0x16aa: 0x0002,
// Block 0x5b, offset 0x16c0
0x16e9: 0x0002,
0x16ea: 0x0002, 0x16eb: 0x0002, 0x16ec: 0x0002,
0x16f0: 0x0002, 0x16f3: 0x0002,
0x16e9: 0x0004,
0x16ea: 0x0004, 0x16eb: 0x0004, 0x16ec: 0x0004,
0x16f0: 0x0004, 0x16f3: 0x0004,
// Block 0x5c, offset 0x1700
0x1720: 0x0003, 0x1721: 0x0003, 0x1722: 0x0003, 0x1723: 0x0003,
0x1724: 0x0003, 0x1725: 0x0003, 0x1726: 0x0003, 0x1727: 0x0003, 0x1728: 0x0003, 0x1729: 0x0003,
@@ -640,63 +642,63 @@ var stringWidthValues = [15744]uint8{
0x1862: 0x0003, 0x1863: 0x0003,
0x1864: 0x0003, 0x1865: 0x0003,
0x186f: 0x0003,
0x187d: 0x0002, 0x187e: 0x0002,
0x187d: 0x0004, 0x187e: 0x0004,
// Block 0x62, offset 0x1880
0x1885: 0x0003,
0x1886: 0x0003, 0x1889: 0x0003,
0x188e: 0x0003, 0x188f: 0x0003,
0x1894: 0x0002, 0x1895: 0x0002,
0x1894: 0x0004, 0x1895: 0x0004,
0x189c: 0x0003,
0x189e: 0x0003,
0x18b0: 0x0002, 0x18b1: 0x0002, 0x18b2: 0x0002, 0x18b3: 0x0002, 0x18b4: 0x0002, 0x18b5: 0x0002,
0x18b6: 0x0002, 0x18b7: 0x0002,
// Block 0x63, offset 0x18c0
0x18c0: 0x0003, 0x18c2: 0x0003,
0x18c8: 0x0002, 0x18c9: 0x0002, 0x18ca: 0x0002, 0x18cb: 0x0002,
0x18cc: 0x0002, 0x18cd: 0x0002, 0x18ce: 0x0002, 0x18cf: 0x0002, 0x18d0: 0x0002, 0x18d1: 0x0002,
0x18d2: 0x0002, 0x18d3: 0x0002,
0x18c8: 0x0004, 0x18c9: 0x0004, 0x18ca: 0x0004, 0x18cb: 0x0004,
0x18cc: 0x0004, 0x18cd: 0x0004, 0x18ce: 0x0004, 0x18cf: 0x0004, 0x18d0: 0x0004, 0x18d1: 0x0004,
0x18d2: 0x0004, 0x18d3: 0x0004,
0x18e0: 0x0003, 0x18e1: 0x0003, 0x18e3: 0x0003,
0x18e4: 0x0003, 0x18e5: 0x0003, 0x18e7: 0x0003, 0x18e8: 0x0003, 0x18e9: 0x0003,
0x18ea: 0x0003, 0x18ec: 0x0003, 0x18ed: 0x0003, 0x18ef: 0x0003,
0x18ff: 0x0002,
0x18ff: 0x0004,
// Block 0x64, offset 0x1900
0x190a: 0x0002, 0x190b: 0x0002,
0x190c: 0x0002, 0x190d: 0x0002, 0x190e: 0x0002, 0x190f: 0x0002,
0x1913: 0x0002,
0x191e: 0x0003, 0x191f: 0x0003, 0x1921: 0x0002,
0x192a: 0x0002, 0x192b: 0x0002,
0x193d: 0x0002, 0x193e: 0x0002, 0x193f: 0x0003,
0x1913: 0x0004,
0x191e: 0x0003, 0x191f: 0x0003, 0x1921: 0x0004,
0x192a: 0x0004, 0x192b: 0x0004,
0x193d: 0x0004, 0x193e: 0x0004, 0x193f: 0x0003,
// Block 0x65, offset 0x1940
0x1944: 0x0002, 0x1945: 0x0002,
0x1944: 0x0004, 0x1945: 0x0004,
0x1946: 0x0003, 0x1947: 0x0003, 0x1948: 0x0003, 0x1949: 0x0003, 0x194a: 0x0003, 0x194b: 0x0003,
0x194c: 0x0003, 0x194d: 0x0003, 0x194e: 0x0002, 0x194f: 0x0003, 0x1950: 0x0003, 0x1951: 0x0003,
0x1952: 0x0003, 0x1953: 0x0003, 0x1954: 0x0002, 0x1955: 0x0003, 0x1956: 0x0003, 0x1957: 0x0003,
0x194c: 0x0003, 0x194d: 0x0003, 0x194e: 0x0004, 0x194f: 0x0003, 0x1950: 0x0003, 0x1951: 0x0003,
0x1952: 0x0003, 0x1953: 0x0003, 0x1954: 0x0004, 0x1955: 0x0003, 0x1956: 0x0003, 0x1957: 0x0003,
0x1958: 0x0003, 0x1959: 0x0003, 0x195a: 0x0003, 0x195b: 0x0003, 0x195c: 0x0003, 0x195d: 0x0003,
0x195e: 0x0003, 0x195f: 0x0003, 0x1960: 0x0003, 0x1961: 0x0003, 0x1963: 0x0003,
0x1968: 0x0003, 0x1969: 0x0003,
0x196a: 0x0002, 0x196b: 0x0003, 0x196c: 0x0003, 0x196d: 0x0003, 0x196e: 0x0003, 0x196f: 0x0003,
0x1970: 0x0003, 0x1971: 0x0003, 0x1972: 0x0002, 0x1973: 0x0002, 0x1974: 0x0003, 0x1975: 0x0002,
0x1976: 0x0003, 0x1977: 0x0003, 0x1978: 0x0003, 0x1979: 0x0003, 0x197a: 0x0002, 0x197b: 0x0003,
0x197c: 0x0003, 0x197d: 0x0002, 0x197e: 0x0003, 0x197f: 0x0003,
0x196a: 0x0004, 0x196b: 0x0003, 0x196c: 0x0003, 0x196d: 0x0003, 0x196e: 0x0003, 0x196f: 0x0003,
0x1970: 0x0003, 0x1971: 0x0003, 0x1972: 0x0004, 0x1973: 0x0004, 0x1974: 0x0003, 0x1975: 0x0004,
0x1976: 0x0003, 0x1977: 0x0003, 0x1978: 0x0003, 0x1979: 0x0003, 0x197a: 0x0004, 0x197b: 0x0003,
0x197c: 0x0003, 0x197d: 0x0004, 0x197e: 0x0003, 0x197f: 0x0003,
// Block 0x66, offset 0x1980
0x1985: 0x0002,
0x198a: 0x0002, 0x198b: 0x0002,
0x19a8: 0x0002,
0x1985: 0x0004,
0x198a: 0x0004, 0x198b: 0x0004,
0x19a8: 0x0004,
0x19bd: 0x0003,
// Block 0x67, offset 0x19c0
0x19cc: 0x0002, 0x19ce: 0x0002,
0x19d3: 0x0002, 0x19d4: 0x0002, 0x19d5: 0x0002, 0x19d7: 0x0002,
0x19cc: 0x0004, 0x19ce: 0x0004,
0x19d3: 0x0004, 0x19d4: 0x0004, 0x19d5: 0x0004, 0x19d7: 0x0004,
0x19f6: 0x0003, 0x19f7: 0x0003, 0x19f8: 0x0003, 0x19f9: 0x0003, 0x19fa: 0x0003, 0x19fb: 0x0003,
0x19fc: 0x0003, 0x19fd: 0x0003, 0x19fe: 0x0003, 0x19ff: 0x0003,
// Block 0x68, offset 0x1a00
0x1a15: 0x0002, 0x1a16: 0x0002, 0x1a17: 0x0002,
0x1a30: 0x0002,
0x1a3f: 0x0002,
0x1a15: 0x0004, 0x1a16: 0x0004, 0x1a17: 0x0004,
0x1a30: 0x0004,
0x1a3f: 0x0004,
// Block 0x69, offset 0x1a40
0x1a5b: 0x0002, 0x1a5c: 0x0002,
0x1a5b: 0x0004, 0x1a5c: 0x0004,
// Block 0x6a, offset 0x1a80
0x1a90: 0x0002,
0x1a95: 0x0002, 0x1a96: 0x0003, 0x1a97: 0x0003,
0x1a90: 0x0004,
0x1a95: 0x0004, 0x1a96: 0x0003, 0x1a97: 0x0003,
0x1a98: 0x0003, 0x1a99: 0x0003,
// Block 0x6b, offset 0x1ac0
0x1aef: 0x0001,
@@ -1271,9 +1273,9 @@ var stringWidthValues = [15744]uint8{
0x3604: 0x0001, 0x3605: 0x0001,
0x3606: 0x0001, 0x3607: 0x0001, 0x3608: 0x0001, 0x3609: 0x0001, 0x360a: 0x0001,
// Block 0xd9, offset 0x3640
0x3644: 0x0002,
0x3644: 0x0004,
// Block 0xda, offset 0x3680
0x368f: 0x0002,
0x368f: 0x0004,
// Block 0xdb, offset 0x36c0
0x36c0: 0x0003, 0x36c1: 0x0003, 0x36c2: 0x0003, 0x36c3: 0x0003, 0x36c4: 0x0003, 0x36c5: 0x0003,
0x36c6: 0x0003, 0x36c7: 0x0003, 0x36c8: 0x0003, 0x36c9: 0x0003, 0x36ca: 0x0003,
@@ -1300,228 +1302,246 @@ var stringWidthValues = [15744]uint8{
// Block 0xdd, offset 0x3740
0x3740: 0x0003, 0x3741: 0x0003, 0x3742: 0x0003, 0x3743: 0x0003, 0x3744: 0x0003, 0x3745: 0x0003,
0x3746: 0x0003, 0x3747: 0x0003, 0x3748: 0x0003, 0x3749: 0x0003, 0x374a: 0x0003, 0x374b: 0x0003,
0x374c: 0x0003, 0x374d: 0x0003, 0x374e: 0x0002, 0x374f: 0x0003, 0x3750: 0x0003, 0x3751: 0x0002,
0x3752: 0x0002, 0x3753: 0x0002, 0x3754: 0x0002, 0x3755: 0x0002, 0x3756: 0x0002, 0x3757: 0x0002,
0x3758: 0x0002, 0x3759: 0x0002, 0x375a: 0x0002, 0x375b: 0x0003, 0x375c: 0x0003, 0x375d: 0x0003,
0x374c: 0x0003, 0x374d: 0x0003, 0x374e: 0x0004, 0x374f: 0x0003, 0x3750: 0x0003, 0x3751: 0x0004,
0x3752: 0x0004, 0x3753: 0x0004, 0x3754: 0x0004, 0x3755: 0x0004, 0x3756: 0x0004, 0x3757: 0x0004,
0x3758: 0x0004, 0x3759: 0x0004, 0x375a: 0x0004, 0x375b: 0x0003, 0x375c: 0x0003, 0x375d: 0x0003,
0x375e: 0x0003, 0x375f: 0x0003, 0x3760: 0x0003, 0x3761: 0x0003, 0x3762: 0x0003, 0x3763: 0x0003,
0x3764: 0x0003, 0x3765: 0x0003, 0x3766: 0x0003, 0x3767: 0x0003, 0x3768: 0x0003, 0x3769: 0x0003,
0x376a: 0x0003, 0x376b: 0x0003, 0x376c: 0x0003,
// Block 0xde, offset 0x3780
0x37a6: 0x0002, 0x37a7: 0x0002, 0x37a8: 0x0002, 0x37a9: 0x0002,
0x37aa: 0x0002, 0x37ab: 0x0002, 0x37ac: 0x0002, 0x37ad: 0x0002, 0x37ae: 0x0002, 0x37af: 0x0002,
0x37b0: 0x0002, 0x37b1: 0x0002, 0x37b2: 0x0002, 0x37b3: 0x0002, 0x37b4: 0x0002, 0x37b5: 0x0002,
0x37b6: 0x0002, 0x37b7: 0x0002, 0x37b8: 0x0002, 0x37b9: 0x0002, 0x37ba: 0x0002, 0x37bb: 0x0002,
0x37bc: 0x0002, 0x37bd: 0x0002, 0x37be: 0x0002, 0x37bf: 0x0002,
0x3780: 0x0002, 0x3781: 0x0004, 0x3782: 0x0002,
0x3790: 0x0002, 0x3791: 0x0002,
0x3792: 0x0002, 0x3793: 0x0002, 0x3794: 0x0002, 0x3795: 0x0002, 0x3796: 0x0002, 0x3797: 0x0002,
0x3798: 0x0002, 0x3799: 0x0002, 0x379a: 0x0004, 0x379b: 0x0002, 0x379c: 0x0002, 0x379d: 0x0002,
0x379e: 0x0002, 0x379f: 0x0002, 0x37a0: 0x0002, 0x37a1: 0x0002, 0x37a2: 0x0002, 0x37a3: 0x0002,
0x37a4: 0x0002, 0x37a5: 0x0002, 0x37a6: 0x0002, 0x37a7: 0x0002, 0x37a8: 0x0002, 0x37a9: 0x0002,
0x37aa: 0x0002, 0x37ab: 0x0002, 0x37ac: 0x0002, 0x37ad: 0x0002, 0x37ae: 0x0002, 0x37af: 0x0004,
0x37b0: 0x0002, 0x37b1: 0x0002, 0x37b2: 0x0004, 0x37b3: 0x0004, 0x37b4: 0x0004, 0x37b5: 0x0004,
0x37b6: 0x0004, 0x37b7: 0x0002, 0x37b8: 0x0004, 0x37b9: 0x0004, 0x37ba: 0x0004, 0x37bb: 0x0002,
// Block 0xdf, offset 0x37c0
0x37c0: 0x0002, 0x37c1: 0x0002, 0x37c2: 0x0002,
0x37d0: 0x0002, 0x37d1: 0x0002,
0x37d2: 0x0002, 0x37d3: 0x0002, 0x37d4: 0x0002, 0x37d5: 0x0002, 0x37d6: 0x0002, 0x37d7: 0x0002,
0x37d8: 0x0002, 0x37d9: 0x0002, 0x37da: 0x0002, 0x37db: 0x0002, 0x37dc: 0x0002, 0x37dd: 0x0002,
0x37de: 0x0002, 0x37df: 0x0002, 0x37e0: 0x0002, 0x37e1: 0x0002, 0x37e2: 0x0002, 0x37e3: 0x0002,
0x37e4: 0x0002, 0x37e5: 0x0002, 0x37e6: 0x0002, 0x37e7: 0x0002, 0x37e8: 0x0002, 0x37e9: 0x0002,
0x37ea: 0x0002, 0x37eb: 0x0002, 0x37ec: 0x0002, 0x37ed: 0x0002, 0x37ee: 0x0002, 0x37ef: 0x0002,
0x37f0: 0x0002, 0x37f1: 0x0002, 0x37f2: 0x0002, 0x37f3: 0x0002, 0x37f4: 0x0002, 0x37f5: 0x0002,
0x37f6: 0x0002, 0x37f7: 0x0002, 0x37f8: 0x0002, 0x37f9: 0x0002, 0x37fa: 0x0002, 0x37fb: 0x0002,
0x37c0: 0x0002, 0x37c1: 0x0002, 0x37c2: 0x0002, 0x37c3: 0x0002, 0x37c4: 0x0002, 0x37c5: 0x0002,
0x37c6: 0x0002, 0x37c7: 0x0002, 0x37c8: 0x0002,
0x37d0: 0x0004, 0x37d1: 0x0004,
0x37e0: 0x0002, 0x37e1: 0x0002, 0x37e2: 0x0002, 0x37e3: 0x0002,
0x37e4: 0x0002, 0x37e5: 0x0002,
// Block 0xe0, offset 0x3800
0x3800: 0x0002, 0x3801: 0x0002, 0x3802: 0x0002, 0x3803: 0x0002, 0x3804: 0x0002, 0x3805: 0x0002,
0x3806: 0x0002, 0x3807: 0x0002, 0x3808: 0x0002,
0x3810: 0x0002, 0x3811: 0x0002,
0x3820: 0x0002, 0x3821: 0x0002, 0x3822: 0x0002, 0x3823: 0x0002,
0x3824: 0x0002, 0x3825: 0x0002,
0x3800: 0x0004, 0x3801: 0x0004, 0x3802: 0x0004, 0x3803: 0x0004, 0x3804: 0x0004, 0x3805: 0x0004,
0x3806: 0x0004, 0x3807: 0x0004, 0x3808: 0x0004, 0x3809: 0x0004, 0x380a: 0x0004, 0x380b: 0x0004,
0x380c: 0x0004, 0x380d: 0x0004, 0x380e: 0x0004, 0x380f: 0x0004, 0x3810: 0x0004, 0x3811: 0x0004,
0x3812: 0x0004, 0x3813: 0x0004, 0x3814: 0x0004, 0x3815: 0x0004, 0x3816: 0x0004, 0x3817: 0x0004,
0x3818: 0x0004, 0x3819: 0x0004, 0x381a: 0x0004, 0x381b: 0x0004, 0x381c: 0x0004, 0x381d: 0x0004,
0x381e: 0x0004, 0x381f: 0x0004, 0x3820: 0x0004,
0x382d: 0x0004, 0x382e: 0x0004, 0x382f: 0x0004,
0x3830: 0x0004, 0x3831: 0x0004, 0x3832: 0x0004, 0x3833: 0x0004, 0x3834: 0x0004, 0x3835: 0x0004,
0x3837: 0x0004, 0x3838: 0x0004, 0x3839: 0x0004, 0x383a: 0x0004, 0x383b: 0x0004,
0x383c: 0x0004, 0x383d: 0x0004, 0x383e: 0x0004, 0x383f: 0x0004,
// Block 0xe1, offset 0x3840
0x3840: 0x0002, 0x3841: 0x0002, 0x3842: 0x0002, 0x3843: 0x0002, 0x3844: 0x0002, 0x3845: 0x0002,
0x3846: 0x0002, 0x3847: 0x0002, 0x3848: 0x0002, 0x3849: 0x0002, 0x384a: 0x0002, 0x384b: 0x0002,
0x384c: 0x0002, 0x384d: 0x0002, 0x384e: 0x0002, 0x384f: 0x0002, 0x3850: 0x0002, 0x3851: 0x0002,
0x3852: 0x0002, 0x3853: 0x0002, 0x3854: 0x0002, 0x3855: 0x0002, 0x3856: 0x0002, 0x3857: 0x0002,
0x3858: 0x0002, 0x3859: 0x0002, 0x385a: 0x0002, 0x385b: 0x0002, 0x385c: 0x0002, 0x385d: 0x0002,
0x385e: 0x0002, 0x385f: 0x0002, 0x3860: 0x0002,
0x386d: 0x0002, 0x386e: 0x0002, 0x386f: 0x0002,
0x3870: 0x0002, 0x3871: 0x0002, 0x3872: 0x0002, 0x3873: 0x0002, 0x3874: 0x0002, 0x3875: 0x0002,
0x3877: 0x0002, 0x3878: 0x0002, 0x3879: 0x0002, 0x387a: 0x0002, 0x387b: 0x0002,
0x387c: 0x0002, 0x387d: 0x0002, 0x387e: 0x0002, 0x387f: 0x0002,
0x3840: 0x0004, 0x3841: 0x0004, 0x3842: 0x0004, 0x3843: 0x0004, 0x3844: 0x0004, 0x3845: 0x0004,
0x3846: 0x0004, 0x3847: 0x0004, 0x3848: 0x0004, 0x3849: 0x0004, 0x384a: 0x0004, 0x384b: 0x0004,
0x384c: 0x0004, 0x384d: 0x0004, 0x384e: 0x0004, 0x384f: 0x0004, 0x3850: 0x0004, 0x3851: 0x0004,
0x3852: 0x0004, 0x3853: 0x0004, 0x3854: 0x0004, 0x3855: 0x0004, 0x3856: 0x0004, 0x3857: 0x0004,
0x3858: 0x0004, 0x3859: 0x0004, 0x385a: 0x0004, 0x385b: 0x0004, 0x385c: 0x0004, 0x385d: 0x0004,
0x385e: 0x0004, 0x385f: 0x0004, 0x3860: 0x0004, 0x3861: 0x0004, 0x3862: 0x0004, 0x3863: 0x0004,
0x3864: 0x0004, 0x3865: 0x0004, 0x3866: 0x0004, 0x3867: 0x0004, 0x3868: 0x0004, 0x3869: 0x0004,
0x386a: 0x0004, 0x386b: 0x0004, 0x386c: 0x0004, 0x386d: 0x0004, 0x386e: 0x0004, 0x386f: 0x0004,
0x3870: 0x0004, 0x3871: 0x0004, 0x3872: 0x0004, 0x3873: 0x0004, 0x3874: 0x0004, 0x3875: 0x0004,
0x3876: 0x0004, 0x3877: 0x0004, 0x3878: 0x0004, 0x3879: 0x0004, 0x387a: 0x0004, 0x387b: 0x0004,
0x387c: 0x0004, 0x387e: 0x0004, 0x387f: 0x0004,
// Block 0xe2, offset 0x3880
0x3880: 0x0002, 0x3881: 0x0002, 0x3882: 0x0002, 0x3883: 0x0002, 0x3884: 0x0002, 0x3885: 0x0002,
0x3886: 0x0002, 0x3887: 0x0002, 0x3888: 0x0002, 0x3889: 0x0002, 0x388a: 0x0002, 0x388b: 0x0002,
0x388c: 0x0002, 0x388d: 0x0002, 0x388e: 0x0002, 0x388f: 0x0002, 0x3890: 0x0002, 0x3891: 0x0002,
0x3892: 0x0002, 0x3893: 0x0002, 0x3894: 0x0002, 0x3895: 0x0002, 0x3896: 0x0002, 0x3897: 0x0002,
0x3898: 0x0002, 0x3899: 0x0002, 0x389a: 0x0002, 0x389b: 0x0002, 0x389c: 0x0002, 0x389d: 0x0002,
0x389e: 0x0002, 0x389f: 0x0002, 0x38a0: 0x0002, 0x38a1: 0x0002, 0x38a2: 0x0002, 0x38a3: 0x0002,
0x38a4: 0x0002, 0x38a5: 0x0002, 0x38a6: 0x0002, 0x38a7: 0x0002, 0x38a8: 0x0002, 0x38a9: 0x0002,
0x38aa: 0x0002, 0x38ab: 0x0002, 0x38ac: 0x0002, 0x38ad: 0x0002, 0x38ae: 0x0002, 0x38af: 0x0002,
0x38b0: 0x0002, 0x38b1: 0x0002, 0x38b2: 0x0002, 0x38b3: 0x0002, 0x38b4: 0x0002, 0x38b5: 0x0002,
0x38b6: 0x0002, 0x38b7: 0x0002, 0x38b8: 0x0002, 0x38b9: 0x0002, 0x38ba: 0x0002, 0x38bb: 0x0002,
0x38bc: 0x0002, 0x38be: 0x0002, 0x38bf: 0x0002,
0x3880: 0x0004, 0x3881: 0x0004, 0x3882: 0x0004, 0x3883: 0x0004, 0x3884: 0x0004, 0x3885: 0x0004,
0x3886: 0x0004, 0x3887: 0x0004, 0x3888: 0x0004, 0x3889: 0x0004, 0x388a: 0x0004, 0x388b: 0x0004,
0x388c: 0x0004, 0x388d: 0x0004, 0x388e: 0x0004, 0x388f: 0x0004, 0x3890: 0x0004, 0x3891: 0x0004,
0x3892: 0x0004, 0x3893: 0x0004,
0x38a0: 0x0004, 0x38a1: 0x0004, 0x38a2: 0x0004, 0x38a3: 0x0004,
0x38a4: 0x0004, 0x38a5: 0x0004, 0x38a6: 0x0004, 0x38a7: 0x0004, 0x38a8: 0x0004, 0x38a9: 0x0004,
0x38aa: 0x0004, 0x38ab: 0x0004, 0x38ac: 0x0004, 0x38ad: 0x0004, 0x38ae: 0x0004, 0x38af: 0x0004,
0x38b0: 0x0004, 0x38b1: 0x0004, 0x38b2: 0x0004, 0x38b3: 0x0004, 0x38b4: 0x0004, 0x38b5: 0x0004,
0x38b6: 0x0004, 0x38b7: 0x0004, 0x38b8: 0x0004, 0x38b9: 0x0004, 0x38ba: 0x0004, 0x38bb: 0x0004,
0x38bc: 0x0004, 0x38bd: 0x0004, 0x38be: 0x0004, 0x38bf: 0x0004,
// Block 0xe3, offset 0x38c0
0x38c0: 0x0002, 0x38c1: 0x0002, 0x38c2: 0x0002, 0x38c3: 0x0002, 0x38c4: 0x0002, 0x38c5: 0x0002,
0x38c6: 0x0002, 0x38c7: 0x0002, 0x38c8: 0x0002, 0x38c9: 0x0002, 0x38ca: 0x0002, 0x38cb: 0x0002,
0x38cc: 0x0002, 0x38cd: 0x0002, 0x38ce: 0x0002, 0x38cf: 0x0002, 0x38d0: 0x0002, 0x38d1: 0x0002,
0x38d2: 0x0002, 0x38d3: 0x0002,
0x38e0: 0x0002, 0x38e1: 0x0002, 0x38e2: 0x0002, 0x38e3: 0x0002,
0x38e4: 0x0002, 0x38e5: 0x0002, 0x38e6: 0x0002, 0x38e7: 0x0002, 0x38e8: 0x0002, 0x38e9: 0x0002,
0x38ea: 0x0002, 0x38eb: 0x0002, 0x38ec: 0x0002, 0x38ed: 0x0002, 0x38ee: 0x0002, 0x38ef: 0x0002,
0x38f0: 0x0002, 0x38f1: 0x0002, 0x38f2: 0x0002, 0x38f3: 0x0002, 0x38f4: 0x0002, 0x38f5: 0x0002,
0x38f6: 0x0002, 0x38f7: 0x0002, 0x38f8: 0x0002, 0x38f9: 0x0002, 0x38fa: 0x0002, 0x38fb: 0x0002,
0x38c0: 0x0004, 0x38c1: 0x0004, 0x38c2: 0x0004, 0x38c3: 0x0004, 0x38c4: 0x0004, 0x38c5: 0x0004,
0x38c6: 0x0004, 0x38c7: 0x0004, 0x38c8: 0x0004, 0x38c9: 0x0004, 0x38ca: 0x0004,
0x38cf: 0x0004, 0x38d0: 0x0004, 0x38d1: 0x0004,
0x38d2: 0x0004, 0x38d3: 0x0004,
0x38e0: 0x0004, 0x38e1: 0x0004, 0x38e2: 0x0004, 0x38e3: 0x0004,
0x38e4: 0x0004, 0x38e5: 0x0004, 0x38e6: 0x0004, 0x38e7: 0x0004, 0x38e8: 0x0004, 0x38e9: 0x0004,
0x38ea: 0x0004, 0x38eb: 0x0004, 0x38ec: 0x0004, 0x38ed: 0x0004, 0x38ee: 0x0004, 0x38ef: 0x0004,
0x38f0: 0x0004, 0x38f4: 0x0004,
0x38f8: 0x0004, 0x38f9: 0x0004, 0x38fa: 0x0004, 0x38fb: 0x0002,
0x38fc: 0x0002, 0x38fd: 0x0002, 0x38fe: 0x0002, 0x38ff: 0x0002,
// Block 0xe4, offset 0x3900
0x3900: 0x0002, 0x3901: 0x0002, 0x3902: 0x0002, 0x3903: 0x0002, 0x3904: 0x0002, 0x3905: 0x0002,
0x3906: 0x0002, 0x3907: 0x0002, 0x3908: 0x0002, 0x3909: 0x0002, 0x390a: 0x0002,
0x390f: 0x0002, 0x3910: 0x0002, 0x3911: 0x0002,
0x3912: 0x0002, 0x3913: 0x0002,
0x3920: 0x0002, 0x3921: 0x0002, 0x3922: 0x0002, 0x3923: 0x0002,
0x3924: 0x0002, 0x3925: 0x0002, 0x3926: 0x0002, 0x3927: 0x0002, 0x3928: 0x0002, 0x3929: 0x0002,
0x392a: 0x0002, 0x392b: 0x0002, 0x392c: 0x0002, 0x392d: 0x0002, 0x392e: 0x0002, 0x392f: 0x0002,
0x3930: 0x0002, 0x3934: 0x0002,
0x3938: 0x0002, 0x3939: 0x0002, 0x393a: 0x0002, 0x393b: 0x0002,
0x393c: 0x0002, 0x393d: 0x0002, 0x393e: 0x0002, 0x393f: 0x0002,
0x3900: 0x0004, 0x3901: 0x0004, 0x3902: 0x0004, 0x3903: 0x0004, 0x3904: 0x0004, 0x3905: 0x0004,
0x3906: 0x0004, 0x3907: 0x0004, 0x3908: 0x0004, 0x3909: 0x0004, 0x390a: 0x0004, 0x390b: 0x0004,
0x390c: 0x0004, 0x390d: 0x0004, 0x390e: 0x0004, 0x390f: 0x0004, 0x3910: 0x0004, 0x3911: 0x0004,
0x3912: 0x0004, 0x3913: 0x0004, 0x3914: 0x0004, 0x3915: 0x0004, 0x3916: 0x0004, 0x3917: 0x0004,
0x3918: 0x0004, 0x3919: 0x0004, 0x391a: 0x0004, 0x391b: 0x0004, 0x391c: 0x0004, 0x391d: 0x0004,
0x391e: 0x0004, 0x391f: 0x0004, 0x3920: 0x0004, 0x3921: 0x0004, 0x3922: 0x0004, 0x3923: 0x0004,
0x3924: 0x0004, 0x3925: 0x0004, 0x3926: 0x0004, 0x3927: 0x0004, 0x3928: 0x0004, 0x3929: 0x0004,
0x392a: 0x0004, 0x392b: 0x0004, 0x392c: 0x0004, 0x392d: 0x0004, 0x392e: 0x0004, 0x392f: 0x0004,
0x3930: 0x0004, 0x3931: 0x0004, 0x3932: 0x0004, 0x3933: 0x0004, 0x3934: 0x0004, 0x3935: 0x0004,
0x3936: 0x0004, 0x3937: 0x0004, 0x3938: 0x0004, 0x3939: 0x0004, 0x393a: 0x0004, 0x393b: 0x0004,
0x393c: 0x0004, 0x393d: 0x0004, 0x393e: 0x0004,
// Block 0xe5, offset 0x3940
0x3940: 0x0002, 0x3941: 0x0002, 0x3942: 0x0002, 0x3943: 0x0002, 0x3944: 0x0002, 0x3945: 0x0002,
0x3946: 0x0002, 0x3947: 0x0002, 0x3948: 0x0002, 0x3949: 0x0002, 0x394a: 0x0002, 0x394b: 0x0002,
0x394c: 0x0002, 0x394d: 0x0002, 0x394e: 0x0002, 0x394f: 0x0002, 0x3950: 0x0002, 0x3951: 0x0002,
0x3952: 0x0002, 0x3953: 0x0002, 0x3954: 0x0002, 0x3955: 0x0002, 0x3956: 0x0002, 0x3957: 0x0002,
0x3958: 0x0002, 0x3959: 0x0002, 0x395a: 0x0002, 0x395b: 0x0002, 0x395c: 0x0002, 0x395d: 0x0002,
0x395e: 0x0002, 0x395f: 0x0002, 0x3960: 0x0002, 0x3961: 0x0002, 0x3962: 0x0002, 0x3963: 0x0002,
0x3964: 0x0002, 0x3965: 0x0002, 0x3966: 0x0002, 0x3967: 0x0002, 0x3968: 0x0002, 0x3969: 0x0002,
0x396a: 0x0002, 0x396b: 0x0002, 0x396c: 0x0002, 0x396d: 0x0002, 0x396e: 0x0002, 0x396f: 0x0002,
0x3970: 0x0002, 0x3971: 0x0002, 0x3972: 0x0002, 0x3973: 0x0002, 0x3974: 0x0002, 0x3975: 0x0002,
0x3976: 0x0002, 0x3977: 0x0002, 0x3978: 0x0002, 0x3979: 0x0002, 0x397a: 0x0002, 0x397b: 0x0002,
0x397c: 0x0002, 0x397d: 0x0002, 0x397e: 0x0002,
0x3940: 0x0004, 0x3942: 0x0004, 0x3943: 0x0004, 0x3944: 0x0004, 0x3945: 0x0004,
0x3946: 0x0004, 0x3947: 0x0004, 0x3948: 0x0004, 0x3949: 0x0004, 0x394a: 0x0004, 0x394b: 0x0004,
0x394c: 0x0004, 0x394d: 0x0004, 0x394e: 0x0004, 0x394f: 0x0004, 0x3950: 0x0004, 0x3951: 0x0004,
0x3952: 0x0004, 0x3953: 0x0004, 0x3954: 0x0004, 0x3955: 0x0004, 0x3956: 0x0004, 0x3957: 0x0004,
0x3958: 0x0004, 0x3959: 0x0004, 0x395a: 0x0004, 0x395b: 0x0004, 0x395c: 0x0004, 0x395d: 0x0004,
0x395e: 0x0004, 0x395f: 0x0004, 0x3960: 0x0004, 0x3961: 0x0004, 0x3962: 0x0004, 0x3963: 0x0004,
0x3964: 0x0004, 0x3965: 0x0004, 0x3966: 0x0004, 0x3967: 0x0004, 0x3968: 0x0004, 0x3969: 0x0004,
0x396a: 0x0004, 0x396b: 0x0004, 0x396c: 0x0004, 0x396d: 0x0004, 0x396e: 0x0004, 0x396f: 0x0004,
0x3970: 0x0004, 0x3971: 0x0004, 0x3972: 0x0004, 0x3973: 0x0004, 0x3974: 0x0004, 0x3975: 0x0004,
0x3976: 0x0004, 0x3977: 0x0004, 0x3978: 0x0004, 0x3979: 0x0004, 0x397a: 0x0004, 0x397b: 0x0004,
0x397c: 0x0004, 0x397d: 0x0004, 0x397e: 0x0004, 0x397f: 0x0004,
// Block 0xe6, offset 0x3980
0x3980: 0x0002, 0x3982: 0x0002, 0x3983: 0x0002, 0x3984: 0x0002, 0x3985: 0x0002,
0x3986: 0x0002, 0x3987: 0x0002, 0x3988: 0x0002, 0x3989: 0x0002, 0x398a: 0x0002, 0x398b: 0x0002,
0x398c: 0x0002, 0x398d: 0x0002, 0x398e: 0x0002, 0x398f: 0x0002, 0x3990: 0x0002, 0x3991: 0x0002,
0x3992: 0x0002, 0x3993: 0x0002, 0x3994: 0x0002, 0x3995: 0x0002, 0x3996: 0x0002, 0x3997: 0x0002,
0x3998: 0x0002, 0x3999: 0x0002, 0x399a: 0x0002, 0x399b: 0x0002, 0x399c: 0x0002, 0x399d: 0x0002,
0x399e: 0x0002, 0x399f: 0x0002, 0x39a0: 0x0002, 0x39a1: 0x0002, 0x39a2: 0x0002, 0x39a3: 0x0002,
0x39a4: 0x0002, 0x39a5: 0x0002, 0x39a6: 0x0002, 0x39a7: 0x0002, 0x39a8: 0x0002, 0x39a9: 0x0002,
0x39aa: 0x0002, 0x39ab: 0x0002, 0x39ac: 0x0002, 0x39ad: 0x0002, 0x39ae: 0x0002, 0x39af: 0x0002,
0x39b0: 0x0002, 0x39b1: 0x0002, 0x39b2: 0x0002, 0x39b3: 0x0002, 0x39b4: 0x0002, 0x39b5: 0x0002,
0x39b6: 0x0002, 0x39b7: 0x0002, 0x39b8: 0x0002, 0x39b9: 0x0002, 0x39ba: 0x0002, 0x39bb: 0x0002,
0x39bc: 0x0002, 0x39bd: 0x0002, 0x39be: 0x0002, 0x39bf: 0x0002,
0x3980: 0x0004, 0x3981: 0x0004, 0x3982: 0x0004, 0x3983: 0x0004, 0x3984: 0x0004, 0x3985: 0x0004,
0x3986: 0x0004, 0x3987: 0x0004, 0x3988: 0x0004, 0x3989: 0x0004, 0x398a: 0x0004, 0x398b: 0x0004,
0x398c: 0x0004, 0x398d: 0x0004, 0x398e: 0x0004, 0x398f: 0x0004, 0x3990: 0x0004, 0x3991: 0x0004,
0x3992: 0x0004, 0x3993: 0x0004, 0x3994: 0x0004, 0x3995: 0x0004, 0x3996: 0x0004, 0x3997: 0x0004,
0x3998: 0x0004, 0x3999: 0x0004, 0x399a: 0x0004, 0x399b: 0x0004, 0x399c: 0x0004, 0x399d: 0x0004,
0x399e: 0x0004, 0x399f: 0x0004, 0x39a0: 0x0004, 0x39a1: 0x0004, 0x39a2: 0x0004, 0x39a3: 0x0004,
0x39a4: 0x0004, 0x39a5: 0x0004, 0x39a6: 0x0004, 0x39a7: 0x0004, 0x39a8: 0x0004, 0x39a9: 0x0004,
0x39aa: 0x0004, 0x39ab: 0x0004, 0x39ac: 0x0004, 0x39ad: 0x0004, 0x39ae: 0x0004, 0x39af: 0x0004,
0x39b0: 0x0004, 0x39b1: 0x0004, 0x39b2: 0x0004, 0x39b3: 0x0004, 0x39b4: 0x0004, 0x39b5: 0x0004,
0x39b6: 0x0004, 0x39b7: 0x0004, 0x39b8: 0x0004, 0x39b9: 0x0004, 0x39ba: 0x0004, 0x39bb: 0x0004,
0x39bc: 0x0004, 0x39bd: 0x0004, 0x39be: 0x0004, 0x39bf: 0x0004,
// Block 0xe7, offset 0x39c0
0x39c0: 0x0002, 0x39c1: 0x0002, 0x39c2: 0x0002, 0x39c3: 0x0002, 0x39c4: 0x0002, 0x39c5: 0x0002,
0x39c6: 0x0002, 0x39c7: 0x0002, 0x39c8: 0x0002, 0x39c9: 0x0002, 0x39ca: 0x0002, 0x39cb: 0x0002,
0x39cc: 0x0002, 0x39cd: 0x0002, 0x39ce: 0x0002, 0x39cf: 0x0002, 0x39d0: 0x0002, 0x39d1: 0x0002,
0x39d2: 0x0002, 0x39d3: 0x0002, 0x39d4: 0x0002, 0x39d5: 0x0002, 0x39d6: 0x0002, 0x39d7: 0x0002,
0x39d8: 0x0002, 0x39d9: 0x0002, 0x39da: 0x0002, 0x39db: 0x0002, 0x39dc: 0x0002, 0x39dd: 0x0002,
0x39de: 0x0002, 0x39df: 0x0002, 0x39e0: 0x0002, 0x39e1: 0x0002, 0x39e2: 0x0002, 0x39e3: 0x0002,
0x39e4: 0x0002, 0x39e5: 0x0002, 0x39e6: 0x0002, 0x39e7: 0x0002, 0x39e8: 0x0002, 0x39e9: 0x0002,
0x39ea: 0x0002, 0x39eb: 0x0002, 0x39ec: 0x0002, 0x39ed: 0x0002, 0x39ee: 0x0002, 0x39ef: 0x0002,
0x39f0: 0x0002, 0x39f1: 0x0002, 0x39f2: 0x0002, 0x39f3: 0x0002, 0x39f4: 0x0002, 0x39f5: 0x0002,
0x39f6: 0x0002, 0x39f7: 0x0002, 0x39f8: 0x0002, 0x39f9: 0x0002, 0x39fa: 0x0002, 0x39fb: 0x0002,
0x39fc: 0x0002, 0x39ff: 0x0002,
0x39c0: 0x0004, 0x39c1: 0x0004, 0x39c2: 0x0004, 0x39c3: 0x0004, 0x39c4: 0x0004, 0x39c5: 0x0004,
0x39c6: 0x0004, 0x39c7: 0x0004, 0x39c8: 0x0004, 0x39c9: 0x0004, 0x39ca: 0x0004, 0x39cb: 0x0004,
0x39cc: 0x0004, 0x39cd: 0x0004, 0x39ce: 0x0004, 0x39cf: 0x0004, 0x39d0: 0x0004, 0x39d1: 0x0004,
0x39d2: 0x0004, 0x39d3: 0x0004, 0x39d4: 0x0004, 0x39d5: 0x0004, 0x39d6: 0x0004, 0x39d7: 0x0004,
0x39d8: 0x0004, 0x39d9: 0x0004, 0x39da: 0x0004, 0x39db: 0x0004, 0x39dc: 0x0004, 0x39dd: 0x0004,
0x39de: 0x0004, 0x39df: 0x0004, 0x39e0: 0x0004, 0x39e1: 0x0004, 0x39e2: 0x0004, 0x39e3: 0x0004,
0x39e4: 0x0004, 0x39e5: 0x0004, 0x39e6: 0x0004, 0x39e7: 0x0004, 0x39e8: 0x0004, 0x39e9: 0x0004,
0x39ea: 0x0004, 0x39eb: 0x0004, 0x39ec: 0x0004, 0x39ed: 0x0004, 0x39ee: 0x0004, 0x39ef: 0x0004,
0x39f0: 0x0004, 0x39f1: 0x0004, 0x39f2: 0x0004, 0x39f3: 0x0004, 0x39f4: 0x0004, 0x39f5: 0x0004,
0x39f6: 0x0004, 0x39f7: 0x0004, 0x39f8: 0x0004, 0x39f9: 0x0004, 0x39fa: 0x0004, 0x39fb: 0x0004,
0x39fc: 0x0004, 0x39ff: 0x0004,
// Block 0xe8, offset 0x3a00
0x3a00: 0x0002, 0x3a01: 0x0002, 0x3a02: 0x0002, 0x3a03: 0x0002, 0x3a04: 0x0002, 0x3a05: 0x0002,
0x3a06: 0x0002, 0x3a07: 0x0002, 0x3a08: 0x0002, 0x3a09: 0x0002, 0x3a0a: 0x0002, 0x3a0b: 0x0002,
0x3a0c: 0x0002, 0x3a0d: 0x0002, 0x3a0e: 0x0002, 0x3a0f: 0x0002, 0x3a10: 0x0002, 0x3a11: 0x0002,
0x3a12: 0x0002, 0x3a13: 0x0002, 0x3a14: 0x0002, 0x3a15: 0x0002, 0x3a16: 0x0002, 0x3a17: 0x0002,
0x3a18: 0x0002, 0x3a19: 0x0002, 0x3a1a: 0x0002, 0x3a1b: 0x0002, 0x3a1c: 0x0002, 0x3a1d: 0x0002,
0x3a1e: 0x0002, 0x3a1f: 0x0002, 0x3a20: 0x0002, 0x3a21: 0x0002, 0x3a22: 0x0002, 0x3a23: 0x0002,
0x3a24: 0x0002, 0x3a25: 0x0002, 0x3a26: 0x0002, 0x3a27: 0x0002, 0x3a28: 0x0002, 0x3a29: 0x0002,
0x3a2a: 0x0002, 0x3a2b: 0x0002, 0x3a2c: 0x0002, 0x3a2d: 0x0002, 0x3a2e: 0x0002, 0x3a2f: 0x0002,
0x3a30: 0x0002, 0x3a31: 0x0002, 0x3a32: 0x0002, 0x3a33: 0x0002, 0x3a34: 0x0002, 0x3a35: 0x0002,
0x3a36: 0x0002, 0x3a37: 0x0002, 0x3a38: 0x0002, 0x3a39: 0x0002, 0x3a3a: 0x0002, 0x3a3b: 0x0002,
0x3a3c: 0x0002, 0x3a3d: 0x0002,
0x3a00: 0x0004, 0x3a01: 0x0004, 0x3a02: 0x0004, 0x3a03: 0x0004, 0x3a04: 0x0004, 0x3a05: 0x0004,
0x3a06: 0x0004, 0x3a07: 0x0004, 0x3a08: 0x0004, 0x3a09: 0x0004, 0x3a0a: 0x0004, 0x3a0b: 0x0004,
0x3a0c: 0x0004, 0x3a0d: 0x0004, 0x3a0e: 0x0004, 0x3a0f: 0x0004, 0x3a10: 0x0004, 0x3a11: 0x0004,
0x3a12: 0x0004, 0x3a13: 0x0004, 0x3a14: 0x0004, 0x3a15: 0x0004, 0x3a16: 0x0004, 0x3a17: 0x0004,
0x3a18: 0x0004, 0x3a19: 0x0004, 0x3a1a: 0x0004, 0x3a1b: 0x0004, 0x3a1c: 0x0004, 0x3a1d: 0x0004,
0x3a1e: 0x0004, 0x3a1f: 0x0004, 0x3a20: 0x0004, 0x3a21: 0x0004, 0x3a22: 0x0004, 0x3a23: 0x0004,
0x3a24: 0x0004, 0x3a25: 0x0004, 0x3a26: 0x0004, 0x3a27: 0x0004, 0x3a28: 0x0004, 0x3a29: 0x0004,
0x3a2a: 0x0004, 0x3a2b: 0x0004, 0x3a2c: 0x0004, 0x3a2d: 0x0004, 0x3a2e: 0x0004, 0x3a2f: 0x0004,
0x3a30: 0x0004, 0x3a31: 0x0004, 0x3a32: 0x0004, 0x3a33: 0x0004, 0x3a34: 0x0004, 0x3a35: 0x0004,
0x3a36: 0x0004, 0x3a37: 0x0004, 0x3a38: 0x0004, 0x3a39: 0x0004, 0x3a3a: 0x0004, 0x3a3b: 0x0004,
0x3a3c: 0x0004, 0x3a3d: 0x0004,
// Block 0xe9, offset 0x3a40
0x3a4b: 0x0002,
0x3a4c: 0x0002, 0x3a4d: 0x0002, 0x3a4e: 0x0002, 0x3a50: 0x0002, 0x3a51: 0x0002,
0x3a52: 0x0002, 0x3a53: 0x0002, 0x3a54: 0x0002, 0x3a55: 0x0002, 0x3a56: 0x0002, 0x3a57: 0x0002,
0x3a58: 0x0002, 0x3a59: 0x0002, 0x3a5a: 0x0002, 0x3a5b: 0x0002, 0x3a5c: 0x0002, 0x3a5d: 0x0002,
0x3a5e: 0x0002, 0x3a5f: 0x0002, 0x3a60: 0x0002, 0x3a61: 0x0002, 0x3a62: 0x0002, 0x3a63: 0x0002,
0x3a64: 0x0002, 0x3a65: 0x0002, 0x3a66: 0x0002, 0x3a67: 0x0002,
0x3a7a: 0x0002,
0x3a4b: 0x0004,
0x3a4c: 0x0004, 0x3a4d: 0x0004, 0x3a4e: 0x0004, 0x3a50: 0x0004, 0x3a51: 0x0004,
0x3a52: 0x0004, 0x3a53: 0x0004, 0x3a54: 0x0004, 0x3a55: 0x0004, 0x3a56: 0x0004, 0x3a57: 0x0004,
0x3a58: 0x0004, 0x3a59: 0x0004, 0x3a5a: 0x0004, 0x3a5b: 0x0004, 0x3a5c: 0x0004, 0x3a5d: 0x0004,
0x3a5e: 0x0004, 0x3a5f: 0x0004, 0x3a60: 0x0004, 0x3a61: 0x0004, 0x3a62: 0x0004, 0x3a63: 0x0004,
0x3a64: 0x0004, 0x3a65: 0x0004, 0x3a66: 0x0004, 0x3a67: 0x0004,
0x3a7a: 0x0004,
// Block 0xea, offset 0x3a80
0x3a95: 0x0002, 0x3a96: 0x0002,
0x3aa4: 0x0002,
0x3a95: 0x0004, 0x3a96: 0x0004,
0x3aa4: 0x0004,
// Block 0xeb, offset 0x3ac0
0x3afb: 0x0002,
0x3afc: 0x0002, 0x3afd: 0x0002, 0x3afe: 0x0002, 0x3aff: 0x0002,
0x3afb: 0x0004,
0x3afc: 0x0004, 0x3afd: 0x0004, 0x3afe: 0x0004, 0x3aff: 0x0004,
// Block 0xec, offset 0x3b00
0x3b00: 0x0002, 0x3b01: 0x0002, 0x3b02: 0x0002, 0x3b03: 0x0002, 0x3b04: 0x0002, 0x3b05: 0x0002,
0x3b06: 0x0002, 0x3b07: 0x0002, 0x3b08: 0x0002, 0x3b09: 0x0002, 0x3b0a: 0x0002, 0x3b0b: 0x0002,
0x3b0c: 0x0002, 0x3b0d: 0x0002, 0x3b0e: 0x0002, 0x3b0f: 0x0002,
0x3b00: 0x0004, 0x3b01: 0x0004, 0x3b02: 0x0004, 0x3b03: 0x0004, 0x3b04: 0x0004, 0x3b05: 0x0004,
0x3b06: 0x0004, 0x3b07: 0x0004, 0x3b08: 0x0004, 0x3b09: 0x0004, 0x3b0a: 0x0004, 0x3b0b: 0x0004,
0x3b0c: 0x0004, 0x3b0d: 0x0004, 0x3b0e: 0x0004, 0x3b0f: 0x0004,
// Block 0xed, offset 0x3b40
0x3b40: 0x0002, 0x3b41: 0x0002, 0x3b42: 0x0002, 0x3b43: 0x0002, 0x3b44: 0x0002, 0x3b45: 0x0002,
0x3b4c: 0x0002, 0x3b50: 0x0002, 0x3b51: 0x0002,
0x3b52: 0x0002, 0x3b55: 0x0002, 0x3b56: 0x0002, 0x3b57: 0x0002,
0x3b5c: 0x0002, 0x3b5d: 0x0002,
0x3b5e: 0x0002, 0x3b5f: 0x0002,
0x3b6b: 0x0002, 0x3b6c: 0x0002,
0x3b74: 0x0002, 0x3b75: 0x0002,
0x3b76: 0x0002, 0x3b77: 0x0002, 0x3b78: 0x0002, 0x3b79: 0x0002, 0x3b7a: 0x0002, 0x3b7b: 0x0002,
0x3b7c: 0x0002,
0x3b40: 0x0004, 0x3b41: 0x0004, 0x3b42: 0x0004, 0x3b43: 0x0004, 0x3b44: 0x0004, 0x3b45: 0x0004,
0x3b4c: 0x0004, 0x3b50: 0x0004, 0x3b51: 0x0004,
0x3b52: 0x0004, 0x3b55: 0x0004, 0x3b56: 0x0004, 0x3b57: 0x0004,
0x3b5c: 0x0004, 0x3b5d: 0x0004,
0x3b5e: 0x0004, 0x3b5f: 0x0004,
0x3b6b: 0x0004, 0x3b6c: 0x0004,
0x3b74: 0x0004, 0x3b75: 0x0004,
0x3b76: 0x0004, 0x3b77: 0x0004, 0x3b78: 0x0004, 0x3b79: 0x0004, 0x3b7a: 0x0004, 0x3b7b: 0x0004,
0x3b7c: 0x0004,
// Block 0xee, offset 0x3b80
0x3ba0: 0x0002, 0x3ba1: 0x0002, 0x3ba2: 0x0002, 0x3ba3: 0x0002,
0x3ba4: 0x0002, 0x3ba5: 0x0002, 0x3ba6: 0x0002, 0x3ba7: 0x0002, 0x3ba8: 0x0002, 0x3ba9: 0x0002,
0x3baa: 0x0002, 0x3bab: 0x0002,
0x3bb0: 0x0002,
0x3ba0: 0x0004, 0x3ba1: 0x0004, 0x3ba2: 0x0004, 0x3ba3: 0x0004,
0x3ba4: 0x0004, 0x3ba5: 0x0004, 0x3ba6: 0x0004, 0x3ba7: 0x0004, 0x3ba8: 0x0004, 0x3ba9: 0x0004,
0x3baa: 0x0004, 0x3bab: 0x0004,
0x3bb0: 0x0004,
// Block 0xef, offset 0x3bc0
0x3bcc: 0x0002, 0x3bcd: 0x0002, 0x3bce: 0x0002, 0x3bcf: 0x0002, 0x3bd0: 0x0002, 0x3bd1: 0x0002,
0x3bd2: 0x0002, 0x3bd3: 0x0002, 0x3bd4: 0x0002, 0x3bd5: 0x0002, 0x3bd6: 0x0002, 0x3bd7: 0x0002,
0x3bd8: 0x0002, 0x3bd9: 0x0002, 0x3bda: 0x0002, 0x3bdb: 0x0002, 0x3bdc: 0x0002, 0x3bdd: 0x0002,
0x3bde: 0x0002, 0x3bdf: 0x0002, 0x3be0: 0x0002, 0x3be1: 0x0002, 0x3be2: 0x0002, 0x3be3: 0x0002,
0x3be4: 0x0002, 0x3be5: 0x0002, 0x3be6: 0x0002, 0x3be7: 0x0002, 0x3be8: 0x0002, 0x3be9: 0x0002,
0x3bea: 0x0002, 0x3beb: 0x0002, 0x3bec: 0x0002, 0x3bed: 0x0002, 0x3bee: 0x0002, 0x3bef: 0x0002,
0x3bf0: 0x0002, 0x3bf1: 0x0002, 0x3bf2: 0x0002, 0x3bf3: 0x0002, 0x3bf4: 0x0002, 0x3bf5: 0x0002,
0x3bf6: 0x0002, 0x3bf7: 0x0002, 0x3bf8: 0x0002, 0x3bf9: 0x0002, 0x3bfa: 0x0002,
0x3bfc: 0x0002, 0x3bfd: 0x0002, 0x3bfe: 0x0002, 0x3bff: 0x0002,
0x3bcc: 0x0004, 0x3bcd: 0x0004, 0x3bce: 0x0004, 0x3bcf: 0x0004, 0x3bd0: 0x0004, 0x3bd1: 0x0004,
0x3bd2: 0x0004, 0x3bd3: 0x0004, 0x3bd4: 0x0004, 0x3bd5: 0x0004, 0x3bd6: 0x0004, 0x3bd7: 0x0004,
0x3bd8: 0x0004, 0x3bd9: 0x0004, 0x3bda: 0x0004, 0x3bdb: 0x0004, 0x3bdc: 0x0004, 0x3bdd: 0x0004,
0x3bde: 0x0004, 0x3bdf: 0x0004, 0x3be0: 0x0004, 0x3be1: 0x0004, 0x3be2: 0x0004, 0x3be3: 0x0004,
0x3be4: 0x0004, 0x3be5: 0x0004, 0x3be6: 0x0004, 0x3be7: 0x0004, 0x3be8: 0x0004, 0x3be9: 0x0004,
0x3bea: 0x0004, 0x3beb: 0x0004, 0x3bec: 0x0004, 0x3bed: 0x0004, 0x3bee: 0x0004, 0x3bef: 0x0004,
0x3bf0: 0x0004, 0x3bf1: 0x0004, 0x3bf2: 0x0004, 0x3bf3: 0x0004, 0x3bf4: 0x0004, 0x3bf5: 0x0004,
0x3bf6: 0x0004, 0x3bf7: 0x0004, 0x3bf8: 0x0004, 0x3bf9: 0x0004, 0x3bfa: 0x0004,
0x3bfc: 0x0004, 0x3bfd: 0x0004, 0x3bfe: 0x0004, 0x3bff: 0x0004,
// Block 0xf0, offset 0x3c00
0x3c00: 0x0002, 0x3c01: 0x0002, 0x3c02: 0x0002, 0x3c03: 0x0002, 0x3c04: 0x0002, 0x3c05: 0x0002,
0x3c07: 0x0002, 0x3c08: 0x0002, 0x3c09: 0x0002, 0x3c0a: 0x0002, 0x3c0b: 0x0002,
0x3c0c: 0x0002, 0x3c0d: 0x0002, 0x3c0e: 0x0002, 0x3c0f: 0x0002, 0x3c10: 0x0002, 0x3c11: 0x0002,
0x3c12: 0x0002, 0x3c13: 0x0002, 0x3c14: 0x0002, 0x3c15: 0x0002, 0x3c16: 0x0002, 0x3c17: 0x0002,
0x3c18: 0x0002, 0x3c19: 0x0002, 0x3c1a: 0x0002, 0x3c1b: 0x0002, 0x3c1c: 0x0002, 0x3c1d: 0x0002,
0x3c1e: 0x0002, 0x3c1f: 0x0002, 0x3c20: 0x0002, 0x3c21: 0x0002, 0x3c22: 0x0002, 0x3c23: 0x0002,
0x3c24: 0x0002, 0x3c25: 0x0002, 0x3c26: 0x0002, 0x3c27: 0x0002, 0x3c28: 0x0002, 0x3c29: 0x0002,
0x3c2a: 0x0002, 0x3c2b: 0x0002, 0x3c2c: 0x0002, 0x3c2d: 0x0002, 0x3c2e: 0x0002, 0x3c2f: 0x0002,
0x3c30: 0x0002, 0x3c31: 0x0002, 0x3c32: 0x0002, 0x3c33: 0x0002, 0x3c34: 0x0002, 0x3c35: 0x0002,
0x3c36: 0x0002, 0x3c37: 0x0002, 0x3c38: 0x0002, 0x3c39: 0x0002, 0x3c3a: 0x0002, 0x3c3b: 0x0002,
0x3c3c: 0x0002, 0x3c3d: 0x0002, 0x3c3e: 0x0002, 0x3c3f: 0x0002,
0x3c00: 0x0004, 0x3c01: 0x0004, 0x3c02: 0x0004, 0x3c03: 0x0004, 0x3c04: 0x0004, 0x3c05: 0x0004,
0x3c07: 0x0004, 0x3c08: 0x0004, 0x3c09: 0x0004, 0x3c0a: 0x0004, 0x3c0b: 0x0004,
0x3c0c: 0x0004, 0x3c0d: 0x0004, 0x3c0e: 0x0004, 0x3c0f: 0x0004, 0x3c10: 0x0004, 0x3c11: 0x0004,
0x3c12: 0x0004, 0x3c13: 0x0004, 0x3c14: 0x0004, 0x3c15: 0x0004, 0x3c16: 0x0004, 0x3c17: 0x0004,
0x3c18: 0x0004, 0x3c19: 0x0004, 0x3c1a: 0x0004, 0x3c1b: 0x0004, 0x3c1c: 0x0004, 0x3c1d: 0x0004,
0x3c1e: 0x0004, 0x3c1f: 0x0004, 0x3c20: 0x0004, 0x3c21: 0x0004, 0x3c22: 0x0004, 0x3c23: 0x0004,
0x3c24: 0x0004, 0x3c25: 0x0004, 0x3c26: 0x0004, 0x3c27: 0x0004, 0x3c28: 0x0004, 0x3c29: 0x0004,
0x3c2a: 0x0004, 0x3c2b: 0x0004, 0x3c2c: 0x0004, 0x3c2d: 0x0004, 0x3c2e: 0x0004, 0x3c2f: 0x0004,
0x3c30: 0x0004, 0x3c31: 0x0004, 0x3c32: 0x0004, 0x3c33: 0x0004, 0x3c34: 0x0004, 0x3c35: 0x0004,
0x3c36: 0x0004, 0x3c37: 0x0004, 0x3c38: 0x0004, 0x3c39: 0x0004, 0x3c3a: 0x0004, 0x3c3b: 0x0004,
0x3c3c: 0x0004, 0x3c3d: 0x0004, 0x3c3e: 0x0004, 0x3c3f: 0x0004,
// Block 0xf1, offset 0x3c40
0x3c70: 0x0002, 0x3c71: 0x0002, 0x3c72: 0x0002, 0x3c73: 0x0002, 0x3c74: 0x0002, 0x3c75: 0x0002,
0x3c76: 0x0002, 0x3c77: 0x0002, 0x3c78: 0x0002, 0x3c79: 0x0002, 0x3c7a: 0x0002, 0x3c7b: 0x0002,
0x3c7c: 0x0002,
0x3c70: 0x0004, 0x3c71: 0x0004, 0x3c72: 0x0004, 0x3c73: 0x0004, 0x3c74: 0x0004, 0x3c75: 0x0004,
0x3c76: 0x0004, 0x3c77: 0x0004, 0x3c78: 0x0004, 0x3c79: 0x0004, 0x3c7a: 0x0004, 0x3c7b: 0x0004,
0x3c7c: 0x0004,
// Block 0xf2, offset 0x3c80
0x3c80: 0x0002, 0x3c81: 0x0002, 0x3c82: 0x0002, 0x3c83: 0x0002, 0x3c84: 0x0002, 0x3c85: 0x0002,
0x3c86: 0x0002, 0x3c87: 0x0002, 0x3c88: 0x0002, 0x3c89: 0x0002,
0x3c8f: 0x0002, 0x3c90: 0x0002, 0x3c91: 0x0002,
0x3c92: 0x0002, 0x3c93: 0x0002, 0x3c94: 0x0002, 0x3c95: 0x0002, 0x3c96: 0x0002, 0x3c97: 0x0002,
0x3c98: 0x0002, 0x3c99: 0x0002, 0x3c9a: 0x0002, 0x3c9b: 0x0002, 0x3c9c: 0x0002, 0x3c9d: 0x0002,
0x3c9e: 0x0002, 0x3c9f: 0x0002, 0x3ca0: 0x0002, 0x3ca1: 0x0002, 0x3ca2: 0x0002, 0x3ca3: 0x0002,
0x3ca4: 0x0002, 0x3ca5: 0x0002, 0x3ca6: 0x0002, 0x3ca7: 0x0002, 0x3ca8: 0x0002, 0x3ca9: 0x0002,
0x3caa: 0x0002, 0x3cab: 0x0002, 0x3cac: 0x0002, 0x3cad: 0x0002, 0x3cae: 0x0002, 0x3caf: 0x0002,
0x3cb0: 0x0002, 0x3cb1: 0x0002, 0x3cb2: 0x0002, 0x3cb3: 0x0002, 0x3cb4: 0x0002, 0x3cb5: 0x0002,
0x3cb6: 0x0002, 0x3cb7: 0x0002, 0x3cb8: 0x0002, 0x3cb9: 0x0002, 0x3cba: 0x0002, 0x3cbb: 0x0002,
0x3cbc: 0x0002, 0x3cbd: 0x0002, 0x3cbe: 0x0002, 0x3cbf: 0x0002,
0x3c80: 0x0004, 0x3c81: 0x0004, 0x3c82: 0x0004, 0x3c83: 0x0004, 0x3c84: 0x0004, 0x3c85: 0x0004,
0x3c86: 0x0004, 0x3c87: 0x0004, 0x3c88: 0x0004, 0x3c89: 0x0004,
0x3c8f: 0x0004, 0x3c90: 0x0004, 0x3c91: 0x0004,
0x3c92: 0x0004, 0x3c93: 0x0004, 0x3c94: 0x0004, 0x3c95: 0x0004, 0x3c96: 0x0004, 0x3c97: 0x0004,
0x3c98: 0x0004, 0x3c99: 0x0004, 0x3c9a: 0x0004, 0x3c9b: 0x0004, 0x3c9c: 0x0004, 0x3c9d: 0x0004,
0x3c9e: 0x0004, 0x3c9f: 0x0004, 0x3ca0: 0x0004, 0x3ca1: 0x0004, 0x3ca2: 0x0004, 0x3ca3: 0x0004,
0x3ca4: 0x0004, 0x3ca5: 0x0004, 0x3ca6: 0x0004, 0x3ca7: 0x0004, 0x3ca8: 0x0004, 0x3ca9: 0x0004,
0x3caa: 0x0004, 0x3cab: 0x0004, 0x3cac: 0x0004, 0x3cad: 0x0004, 0x3cae: 0x0004, 0x3caf: 0x0004,
0x3cb0: 0x0004, 0x3cb1: 0x0004, 0x3cb2: 0x0004, 0x3cb3: 0x0004, 0x3cb4: 0x0004, 0x3cb5: 0x0004,
0x3cb6: 0x0004, 0x3cb7: 0x0004, 0x3cb8: 0x0004, 0x3cb9: 0x0004, 0x3cba: 0x0004, 0x3cbb: 0x0004,
0x3cbc: 0x0004, 0x3cbd: 0x0004, 0x3cbe: 0x0004, 0x3cbf: 0x0004,
// Block 0xf3, offset 0x3cc0
0x3cc0: 0x0002, 0x3cc1: 0x0002, 0x3cc2: 0x0002, 0x3cc3: 0x0002, 0x3cc4: 0x0002, 0x3cc5: 0x0002,
0x3cc6: 0x0002,
0x3cce: 0x0002, 0x3ccf: 0x0002, 0x3cd0: 0x0002, 0x3cd1: 0x0002,
0x3cd2: 0x0002, 0x3cd3: 0x0002, 0x3cd4: 0x0002, 0x3cd5: 0x0002, 0x3cd6: 0x0002, 0x3cd7: 0x0002,
0x3cd8: 0x0002, 0x3cd9: 0x0002, 0x3cda: 0x0002, 0x3cdb: 0x0002, 0x3cdc: 0x0002,
0x3cdf: 0x0002, 0x3ce0: 0x0002, 0x3ce1: 0x0002, 0x3ce2: 0x0002, 0x3ce3: 0x0002,
0x3ce4: 0x0002, 0x3ce5: 0x0002, 0x3ce6: 0x0002, 0x3ce7: 0x0002, 0x3ce8: 0x0002, 0x3ce9: 0x0002,
0x3cf0: 0x0002, 0x3cf1: 0x0002, 0x3cf2: 0x0002, 0x3cf3: 0x0002, 0x3cf4: 0x0002, 0x3cf5: 0x0002,
0x3cf6: 0x0002, 0x3cf7: 0x0002, 0x3cf8: 0x0002,
0x3cc0: 0x0004, 0x3cc1: 0x0004, 0x3cc2: 0x0004, 0x3cc3: 0x0004, 0x3cc4: 0x0004, 0x3cc5: 0x0004,
0x3cc6: 0x0004,
0x3cce: 0x0004, 0x3ccf: 0x0004, 0x3cd0: 0x0004, 0x3cd1: 0x0004,
0x3cd2: 0x0004, 0x3cd3: 0x0004, 0x3cd4: 0x0004, 0x3cd5: 0x0004, 0x3cd6: 0x0004, 0x3cd7: 0x0004,
0x3cd8: 0x0004, 0x3cd9: 0x0004, 0x3cda: 0x0004, 0x3cdb: 0x0004, 0x3cdc: 0x0004,
0x3cdf: 0x0004, 0x3ce0: 0x0004, 0x3ce1: 0x0004, 0x3ce2: 0x0004, 0x3ce3: 0x0004,
0x3ce4: 0x0004, 0x3ce5: 0x0004, 0x3ce6: 0x0004, 0x3ce7: 0x0004, 0x3ce8: 0x0004, 0x3ce9: 0x0004,
0x3cf0: 0x0004, 0x3cf1: 0x0004, 0x3cf2: 0x0004, 0x3cf3: 0x0004, 0x3cf4: 0x0004, 0x3cf5: 0x0004,
0x3cf6: 0x0004, 0x3cf7: 0x0004, 0x3cf8: 0x0004,
// Block 0xf4, offset 0x3d00
0x3d01: 0x0001,
0x3d20: 0x0001, 0x3d21: 0x0001, 0x3d22: 0x0001, 0x3d23: 0x0001,
0x3d24: 0x0001, 0x3d25: 0x0001, 0x3d26: 0x0001, 0x3d27: 0x0001, 0x3d28: 0x0001, 0x3d29: 0x0001,
0x3d2a: 0x0001, 0x3d2b: 0x0001, 0x3d2c: 0x0001, 0x3d2d: 0x0001, 0x3d2e: 0x0001, 0x3d2f: 0x0001,
0x3d30: 0x0001, 0x3d31: 0x0001, 0x3d32: 0x0001, 0x3d33: 0x0001, 0x3d34: 0x0001, 0x3d35: 0x0001,
0x3d36: 0x0001, 0x3d37: 0x0001, 0x3d38: 0x0001, 0x3d39: 0x0001, 0x3d3a: 0x0001, 0x3d3b: 0x0001,
0x3d3c: 0x0001, 0x3d3d: 0x0001, 0x3d3e: 0x0001, 0x3d3f: 0x0001,
0x3d00: 0x0002, 0x3d01: 0x0002, 0x3d02: 0x0002, 0x3d03: 0x0002, 0x3d04: 0x0002, 0x3d05: 0x0002,
0x3d06: 0x0002, 0x3d07: 0x0002, 0x3d08: 0x0002, 0x3d09: 0x0002, 0x3d0a: 0x0002, 0x3d0b: 0x0002,
0x3d0c: 0x0002, 0x3d0d: 0x0002, 0x3d0e: 0x0002, 0x3d0f: 0x0002, 0x3d10: 0x0002, 0x3d11: 0x0002,
0x3d12: 0x0002, 0x3d13: 0x0002, 0x3d14: 0x0002, 0x3d15: 0x0002, 0x3d16: 0x0002, 0x3d17: 0x0002,
0x3d18: 0x0002, 0x3d19: 0x0002, 0x3d1a: 0x0002, 0x3d1b: 0x0002, 0x3d1c: 0x0002, 0x3d1d: 0x0002,
0x3d1e: 0x0002, 0x3d1f: 0x0002, 0x3d20: 0x0002, 0x3d21: 0x0002, 0x3d22: 0x0002, 0x3d23: 0x0002,
0x3d24: 0x0002, 0x3d25: 0x0002, 0x3d26: 0x0002, 0x3d27: 0x0002, 0x3d28: 0x0002, 0x3d29: 0x0002,
0x3d2a: 0x0002, 0x3d2b: 0x0002, 0x3d2c: 0x0002, 0x3d2d: 0x0002, 0x3d2e: 0x0002, 0x3d2f: 0x0002,
0x3d30: 0x0002, 0x3d31: 0x0002, 0x3d32: 0x0002, 0x3d33: 0x0002, 0x3d34: 0x0002, 0x3d35: 0x0002,
0x3d36: 0x0002, 0x3d37: 0x0002, 0x3d38: 0x0002, 0x3d39: 0x0002, 0x3d3a: 0x0002, 0x3d3b: 0x0002,
0x3d3c: 0x0002, 0x3d3d: 0x0002,
// Block 0xf5, offset 0x3d40
0x3d40: 0x0003, 0x3d41: 0x0003, 0x3d42: 0x0003, 0x3d43: 0x0003, 0x3d44: 0x0003, 0x3d45: 0x0003,
0x3d46: 0x0003, 0x3d47: 0x0003, 0x3d48: 0x0003, 0x3d49: 0x0003, 0x3d4a: 0x0003, 0x3d4b: 0x0003,
0x3d4c: 0x0003, 0x3d4d: 0x0003, 0x3d4e: 0x0003, 0x3d4f: 0x0003, 0x3d50: 0x0003, 0x3d51: 0x0003,
0x3d52: 0x0003, 0x3d53: 0x0003, 0x3d54: 0x0003, 0x3d55: 0x0003, 0x3d56: 0x0003, 0x3d57: 0x0003,
0x3d58: 0x0003, 0x3d59: 0x0003, 0x3d5a: 0x0003, 0x3d5b: 0x0003, 0x3d5c: 0x0003, 0x3d5d: 0x0003,
0x3d5e: 0x0003, 0x3d5f: 0x0003, 0x3d60: 0x0003, 0x3d61: 0x0003, 0x3d62: 0x0003, 0x3d63: 0x0003,
0x3d64: 0x0003, 0x3d65: 0x0003, 0x3d66: 0x0003, 0x3d67: 0x0003, 0x3d68: 0x0003, 0x3d69: 0x0003,
0x3d6a: 0x0003, 0x3d6b: 0x0003, 0x3d6c: 0x0003, 0x3d6d: 0x0003, 0x3d6e: 0x0003, 0x3d6f: 0x0003,
0x3d70: 0x0003, 0x3d71: 0x0003, 0x3d72: 0x0003, 0x3d73: 0x0003, 0x3d74: 0x0003, 0x3d75: 0x0003,
0x3d76: 0x0003, 0x3d77: 0x0003, 0x3d78: 0x0003, 0x3d79: 0x0003, 0x3d7a: 0x0003, 0x3d7b: 0x0003,
0x3d7c: 0x0003, 0x3d7d: 0x0003,
0x3d41: 0x0001,
0x3d60: 0x0001, 0x3d61: 0x0001, 0x3d62: 0x0001, 0x3d63: 0x0001,
0x3d64: 0x0001, 0x3d65: 0x0001, 0x3d66: 0x0001, 0x3d67: 0x0001, 0x3d68: 0x0001, 0x3d69: 0x0001,
0x3d6a: 0x0001, 0x3d6b: 0x0001, 0x3d6c: 0x0001, 0x3d6d: 0x0001, 0x3d6e: 0x0001, 0x3d6f: 0x0001,
0x3d70: 0x0001, 0x3d71: 0x0001, 0x3d72: 0x0001, 0x3d73: 0x0001, 0x3d74: 0x0001, 0x3d75: 0x0001,
0x3d76: 0x0001, 0x3d77: 0x0001, 0x3d78: 0x0001, 0x3d79: 0x0001, 0x3d7a: 0x0001, 0x3d7b: 0x0001,
0x3d7c: 0x0001, 0x3d7d: 0x0001, 0x3d7e: 0x0001, 0x3d7f: 0x0001,
// Block 0xf6, offset 0x3d80
0x3d80: 0x0003, 0x3d81: 0x0003, 0x3d82: 0x0003, 0x3d83: 0x0003, 0x3d84: 0x0003, 0x3d85: 0x0003,
0x3d86: 0x0003, 0x3d87: 0x0003, 0x3d88: 0x0003, 0x3d89: 0x0003, 0x3d8a: 0x0003, 0x3d8b: 0x0003,
0x3d8c: 0x0003, 0x3d8d: 0x0003, 0x3d8e: 0x0003, 0x3d8f: 0x0003, 0x3d90: 0x0003, 0x3d91: 0x0003,
0x3d92: 0x0003, 0x3d93: 0x0003, 0x3d94: 0x0003, 0x3d95: 0x0003, 0x3d96: 0x0003, 0x3d97: 0x0003,
0x3d98: 0x0003, 0x3d99: 0x0003, 0x3d9a: 0x0003, 0x3d9b: 0x0003, 0x3d9c: 0x0003, 0x3d9d: 0x0003,
0x3d9e: 0x0003, 0x3d9f: 0x0003, 0x3da0: 0x0003, 0x3da1: 0x0003, 0x3da2: 0x0003, 0x3da3: 0x0003,
0x3da4: 0x0003, 0x3da5: 0x0003, 0x3da6: 0x0003, 0x3da7: 0x0003, 0x3da8: 0x0003, 0x3da9: 0x0003,
0x3daa: 0x0003, 0x3dab: 0x0003, 0x3dac: 0x0003, 0x3dad: 0x0003, 0x3dae: 0x0003, 0x3daf: 0x0003,
0x3db0: 0x0003, 0x3db1: 0x0003, 0x3db2: 0x0003, 0x3db3: 0x0003, 0x3db4: 0x0003, 0x3db5: 0x0003,
0x3db6: 0x0003, 0x3db7: 0x0003, 0x3db8: 0x0003, 0x3db9: 0x0003, 0x3dba: 0x0003, 0x3dbb: 0x0003,
0x3dbc: 0x0003, 0x3dbd: 0x0003,
}
// stringWidthIndex: 30 blocks, 1920 entries, 1920 bytes
@@ -1653,11 +1673,11 @@ var stringWidthIndex = [1920]uint8{
0x593: 0xd4,
0x5a3: 0xd5, 0x5a5: 0xd6,
// Block 0x17, offset 0x5c0
0x5c0: 0xd7, 0x5c3: 0xd8, 0x5c4: 0xd9, 0x5c5: 0xda, 0x5c6: 0xdb, 0x5c7: 0xdc,
0x5c8: 0xdd, 0x5c9: 0xde, 0x5cc: 0xdf, 0x5cd: 0xe0, 0x5ce: 0xe1, 0x5cf: 0xe2,
0x5d0: 0xe3, 0x5d1: 0xe4, 0x5d2: 0x39, 0x5d3: 0xe5, 0x5d4: 0xe6, 0x5d5: 0xe7, 0x5d6: 0xe8, 0x5d7: 0xe9,
0x5d8: 0x39, 0x5d9: 0xea, 0x5da: 0x39, 0x5db: 0xeb, 0x5df: 0xec,
0x5e4: 0xed, 0x5e5: 0xee, 0x5e6: 0x39, 0x5e7: 0x39,
0x5c0: 0xd7, 0x5c3: 0xd8, 0x5c4: 0xd9, 0x5c5: 0xda, 0x5c6: 0xdb,
0x5c8: 0xdc, 0x5c9: 0xdd, 0x5cc: 0xde, 0x5cd: 0xdf, 0x5ce: 0xe0, 0x5cf: 0xe1,
0x5d0: 0xe2, 0x5d1: 0xe3, 0x5d2: 0xe4, 0x5d3: 0xe5, 0x5d4: 0xe6, 0x5d5: 0xe7, 0x5d6: 0xe8, 0x5d7: 0xe9,
0x5d8: 0xe4, 0x5d9: 0xea, 0x5da: 0xe4, 0x5db: 0xeb, 0x5df: 0xec,
0x5e4: 0xed, 0x5e5: 0xee, 0x5e6: 0xe4, 0x5e7: 0xe4,
0x5e9: 0xef, 0x5ea: 0xf0, 0x5eb: 0xf1,
// Block 0x18, offset 0x600
0x600: 0x39, 0x601: 0x39, 0x602: 0x39, 0x603: 0x39, 0x604: 0x39, 0x605: 0x39, 0x606: 0x39, 0x607: 0x39,
@@ -1667,7 +1687,7 @@ var stringWidthIndex = [1920]uint8{
0x620: 0x39, 0x621: 0x39, 0x622: 0x39, 0x623: 0x39, 0x624: 0x39, 0x625: 0x39, 0x626: 0x39, 0x627: 0x39,
0x628: 0x39, 0x629: 0x39, 0x62a: 0x39, 0x62b: 0x39, 0x62c: 0x39, 0x62d: 0x39, 0x62e: 0x39, 0x62f: 0x39,
0x630: 0x39, 0x631: 0x39, 0x632: 0x39, 0x633: 0x39, 0x634: 0x39, 0x635: 0x39, 0x636: 0x39, 0x637: 0x39,
0x638: 0x39, 0x639: 0x39, 0x63a: 0x39, 0x63b: 0x39, 0x63c: 0x39, 0x63d: 0x39, 0x63e: 0x39, 0x63f: 0xe6,
0x638: 0x39, 0x639: 0x39, 0x63a: 0x39, 0x63b: 0x39, 0x63c: 0x39, 0x63d: 0x39, 0x63e: 0x39, 0x63f: 0xf2,
// Block 0x19, offset 0x640
0x650: 0x0b, 0x651: 0x0c, 0x653: 0x0d, 0x656: 0x0e, 0x657: 0x06,
0x658: 0x0f, 0x65a: 0x10, 0x65b: 0x11, 0x65c: 0x12, 0x65d: 0x13, 0x65e: 0x14, 0x65f: 0x15,
@@ -1676,7 +1696,7 @@ var stringWidthIndex = [1920]uint8{
0x670: 0x06, 0x671: 0x06, 0x672: 0x06, 0x673: 0x06, 0x674: 0x06, 0x675: 0x06, 0x676: 0x06, 0x677: 0x06,
0x678: 0x06, 0x679: 0x06, 0x67a: 0x06, 0x67b: 0x06, 0x67c: 0x06, 0x67d: 0x06, 0x67e: 0x06, 0x67f: 0x16,
// Block 0x1a, offset 0x680
0x680: 0xf2, 0x681: 0x08, 0x684: 0x08, 0x685: 0x08, 0x686: 0x08, 0x687: 0x09,
0x680: 0xf3, 0x681: 0x08, 0x684: 0x08, 0x685: 0x08, 0x686: 0x08, 0x687: 0x09,
// Block 0x1b, offset 0x6c0
0x6c0: 0x5b, 0x6c1: 0x5b, 0x6c2: 0x5b, 0x6c3: 0x5b, 0x6c4: 0x5b, 0x6c5: 0x5b, 0x6c6: 0x5b, 0x6c7: 0x5b,
0x6c8: 0x5b, 0x6c9: 0x5b, 0x6ca: 0x5b, 0x6cb: 0x5b, 0x6cc: 0x5b, 0x6cd: 0x5b, 0x6ce: 0x5b, 0x6cf: 0x5b,
@@ -1685,7 +1705,7 @@ var stringWidthIndex = [1920]uint8{
0x6e0: 0x5b, 0x6e1: 0x5b, 0x6e2: 0x5b, 0x6e3: 0x5b, 0x6e4: 0x5b, 0x6e5: 0x5b, 0x6e6: 0x5b, 0x6e7: 0x5b,
0x6e8: 0x5b, 0x6e9: 0x5b, 0x6ea: 0x5b, 0x6eb: 0x5b, 0x6ec: 0x5b, 0x6ed: 0x5b, 0x6ee: 0x5b, 0x6ef: 0x5b,
0x6f0: 0x5b, 0x6f1: 0x5b, 0x6f2: 0x5b, 0x6f3: 0x5b, 0x6f4: 0x5b, 0x6f5: 0x5b, 0x6f6: 0x5b, 0x6f7: 0x5b,
0x6f8: 0x5b, 0x6f9: 0x5b, 0x6fa: 0x5b, 0x6fb: 0x5b, 0x6fc: 0x5b, 0x6fd: 0x5b, 0x6fe: 0x5b, 0x6ff: 0xf3,
0x6f8: 0x5b, 0x6f9: 0x5b, 0x6fa: 0x5b, 0x6fb: 0x5b, 0x6fc: 0x5b, 0x6fd: 0x5b, 0x6fe: 0x5b, 0x6ff: 0xf4,
// Block 0x1c, offset 0x700
0x720: 0x18,
0x730: 0x09, 0x731: 0x09, 0x732: 0x09, 0x733: 0x09, 0x734: 0x09, 0x735: 0x09, 0x736: 0x09, 0x737: 0x09,

View File

@@ -34,7 +34,7 @@ func (options Options) String(s string) int {
case 0:
return 0
case 1:
return asciiWidth(s[0])
return int(asciiWidths[s[0]])
}
width := 0
@@ -60,7 +60,7 @@ func (options Options) Bytes(s []byte) int {
case 0:
return 0
case 1:
return asciiWidth(s[0])
return int(asciiWidths[s[0]])
}
width := 0
@@ -90,7 +90,7 @@ func Rune(r rune) int {
// Iterating over runes to measure width is incorrect in many cases.
func (options Options) Rune(r rune) int {
if r < utf8.RuneSelf {
return asciiWidth(byte(r))
return int(asciiWidths[byte(r)])
}
// Surrogates (U+D800-U+DFFF) are invalid UTF-8.
@@ -102,11 +102,9 @@ func (options Options) Rune(r rune) int {
n := utf8.EncodeRune(buf[:], r)
// Skip the grapheme iterator
return graphemeWidth(buf[:n], options)
return lookupProperties(buf[:n]).width(options)
}
const _Default property = 0
// graphemeWidth returns the display width of a grapheme cluster.
// The passed string must be a single grapheme cluster.
func graphemeWidth[T stringish.Interface](s T, options Options) int {
@@ -115,39 +113,16 @@ func graphemeWidth[T stringish.Interface](s T, options Options) int {
case 0:
return 0
case 1:
return asciiWidth(s[0])
return int(asciiWidths[s[0]])
}
p, sz := lookup(s)
prop := property(p)
// Variation Selector 16 (VS16) requests emoji presentation
if prop != _Wide && sz > 0 && len(s) >= sz+3 {
vs := s[sz : sz+3]
if isVS16(vs) {
prop = _Wide
}
// VS15 (0x8E) requests text presentation but does not affect width,
// in my reading of Unicode TR51. Falls through to return the base
// character's property.
}
if options.EastAsianWidth && prop == _East_Asian_Ambiguous {
prop = _Wide
}
if prop > upperBound {
prop = _Default
}
return propertyWidths[prop]
return lookupProperties(s).width(options)
}
func asciiWidth(b byte) int {
if b <= 0x1F || b == 0x7F {
return 0
}
return 1
// isRIPrefix checks if the slice matches the Regional Indicator prefix
// (F0 9F 87). It assumes len(s) >= 3.
func isRIPrefix[T stringish.Interface](s T) bool {
return s[0] == 0xF0 && s[1] == 0x9F && s[2] == 0x87
}
// isVS16 checks if the slice matches VS16 (U+FE0F) UTF-8 encoding
@@ -156,12 +131,81 @@ func isVS16[T stringish.Interface](s T) bool {
return s[0] == 0xEF && s[1] == 0xB8 && s[2] == 0x8F
}
// propertyWidths is a jump table of sorts, instead of a switch
var propertyWidths = [4]int{
_Default: 1,
_Zero_Width: 0,
_Wide: 2,
_East_Asian_Ambiguous: 1,
// lookupProperties returns the properties for a grapheme.
// The passed string must be at least one byte long.
//
// Callers must handle zero and single-byte strings upstream, both as an
// optimization, and to reduce the scope of this function.
func lookupProperties[T stringish.Interface](s T) property {
l := len(s)
if s[0] < utf8.RuneSelf {
// Check for variation selector after ASCII (e.g., keycap sequences like 1⃣)
if l >= 4 {
// Subslice may help eliminate bounds checks
vs := s[1:4]
if isVS16(vs) {
// VS16 requests emoji presentation (width 2)
return _Emoji
}
// VS15 (0x8E) requests text presentation but does not affect width,
// in my reading of Unicode TR51. Falls through to _Default.
}
return asciiProperties[s[0]]
}
// Regional indicator pair (flag)
if l >= 8 {
// Subslice may help eliminate bounds checks
ri := s[:8]
// First rune
if isRIPrefix(ri[0:3]) {
b3 := ri[3]
if b3 >= 0xA6 && b3 <= 0xBF {
// Second rune
if isRIPrefix(ri[4:7]) {
b7 := ri[7]
if b7 >= 0xA6 && b7 <= 0xBF {
return _Emoji
}
}
}
}
}
p, sz := lookup(s)
// Variation Selectors
if sz > 0 && l >= sz+3 {
// Subslice may help eliminate bounds checks
vs := s[sz : sz+3]
if isVS16(vs) {
// VS16 requests emoji presentation (width 2)
return _Emoji
}
// VS15 (0x8E) requests text presentation but does not affect width,
// in my reading of Unicode TR51. Falls through to return the base
// character's property.
}
return property(p)
}
const upperBound = property(len(propertyWidths) - 1)
const _Default property = 0
const boundsCheck = property(len(propertyWidths) - 1)
// width determines the display width of a character based on its properties,
// and configuration options
func (p property) width(options Options) int {
if options.EastAsianWidth && p == _East_Asian_Ambiguous {
return 2
}
// Bounds check may help the compiler eliminate its bounds check,
// and safety of course.
if p > boundsCheck {
return 1 // default width
}
return propertyWidths[p]
}

View File

@@ -3476,9 +3476,6 @@ type JSONSchema_FieldConfiguration struct {
// parameter. Use this to avoid having auto generated path parameter names
// for overlapping paths.
PathParamName string `protobuf:"bytes,47,opt,name=path_param_name,json=pathParamName,proto3" json:"path_param_name,omitempty"`
// Declares this field to be deprecated. Allows for the generated OpenAPI
// parameter to be marked as deprecated without affecting the proto field.
Deprecated bool `protobuf:"varint,49,opt,name=deprecated,proto3" json:"deprecated,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -3515,21 +3512,10 @@ func (x *JSONSchema_FieldConfiguration) GetPathParamName() string {
return ""
}
func (x *JSONSchema_FieldConfiguration) GetDeprecated() bool {
if x != nil {
return x.Deprecated
}
return false
}
func (x *JSONSchema_FieldConfiguration) SetPathParamName(v string) {
x.PathParamName = v
}
func (x *JSONSchema_FieldConfiguration) SetDeprecated(v bool) {
x.Deprecated = v
}
type JSONSchema_FieldConfiguration_builder struct {
_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
@@ -3538,9 +3524,6 @@ type JSONSchema_FieldConfiguration_builder struct {
// parameter. Use this to avoid having auto generated path parameter names
// for overlapping paths.
PathParamName string
// Declares this field to be deprecated. Allows for the generated OpenAPI
// parameter to be marked as deprecated without affecting the proto field.
Deprecated bool
}
func (b0 JSONSchema_FieldConfiguration_builder) Build() *JSONSchema_FieldConfiguration {
@@ -3548,7 +3531,6 @@ func (b0 JSONSchema_FieldConfiguration_builder) Build() *JSONSchema_FieldConfigu
b, x := &b0, m0
_, _ = b, x
x.PathParamName = b.PathParamName
x.Deprecated = b.Deprecated
return m0
}
@@ -3922,7 +3904,7 @@ var file_protoc_gen_openapiv2_options_openapiv2_proto_rawDesc = []byte{
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61,
0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf7,
0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd7,
0x0a, 0x0a, 0x0a, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x10, 0x0a,
0x03, 0x72, 0x65, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12,
0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
@@ -3986,13 +3968,11 @@ var file_protoc_gen_openapiv2_options_openapiv2_proto_rawDesc = []byte{
0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2e, 0x6f, 0x70,
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61,
0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x5c, 0x0a, 0x12,
0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3c, 0x0a, 0x12,
0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d,
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x2f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x74,
0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65,
0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x31, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a,
0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78,
0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78,
0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16,

View File

@@ -612,9 +612,6 @@ message JSONSchema {
// parameter. Use this to avoid having auto generated path parameter names
// for overlapping paths.
string path_param_name = 47;
// Declares this field to be deprecated. Allows for the generated OpenAPI
// parameter to be marked as deprecated without affecting the proto field.
bool deprecated = 49;
}
// Custom properties that start with "x-" such as "x-foo" used to describe
// extra functionality that is not covered by the standard OpenAPI Specification.

View File

@@ -3268,7 +3268,6 @@ func (b0 Scopes_builder) Build() *Scopes {
type JSONSchema_FieldConfiguration struct {
state protoimpl.MessageState `protogen:"opaque.v1"`
xxx_hidden_PathParamName string `protobuf:"bytes,47,opt,name=path_param_name,json=pathParamName,proto3" json:"path_param_name,omitempty"`
xxx_hidden_Deprecated bool `protobuf:"varint,49,opt,name=deprecated,proto3" json:"deprecated,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -3305,21 +3304,10 @@ func (x *JSONSchema_FieldConfiguration) GetPathParamName() string {
return ""
}
func (x *JSONSchema_FieldConfiguration) GetDeprecated() bool {
if x != nil {
return x.xxx_hidden_Deprecated
}
return false
}
func (x *JSONSchema_FieldConfiguration) SetPathParamName(v string) {
x.xxx_hidden_PathParamName = v
}
func (x *JSONSchema_FieldConfiguration) SetDeprecated(v bool) {
x.xxx_hidden_Deprecated = v
}
type JSONSchema_FieldConfiguration_builder struct {
_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
@@ -3328,9 +3316,6 @@ type JSONSchema_FieldConfiguration_builder struct {
// parameter. Use this to avoid having auto generated path parameter names
// for overlapping paths.
PathParamName string
// Declares this field to be deprecated. Allows for the generated OpenAPI
// parameter to be marked as deprecated without affecting the proto field.
Deprecated bool
}
func (b0 JSONSchema_FieldConfiguration_builder) Build() *JSONSchema_FieldConfiguration {
@@ -3338,7 +3323,6 @@ func (b0 JSONSchema_FieldConfiguration_builder) Build() *JSONSchema_FieldConfigu
b, x := &b0, m0
_, _ = b, x
x.xxx_hidden_PathParamName = b.PathParamName
x.xxx_hidden_Deprecated = b.Deprecated
return m0
}
@@ -3712,7 +3696,7 @@ var file_protoc_gen_openapiv2_options_openapiv2_proto_rawDesc = []byte{
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61,
0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf7,
0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd7,
0x0a, 0x0a, 0x0a, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x10, 0x0a,
0x03, 0x72, 0x65, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12,
0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
@@ -3776,13 +3760,11 @@ var file_protoc_gen_openapiv2_options_openapiv2_proto_rawDesc = []byte{
0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2e, 0x6f, 0x70,
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61,
0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x5c, 0x0a, 0x12,
0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3c, 0x0a, 0x12,
0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d,
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x2f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x74,
0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65,
0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x31, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a,
0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78,
0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78,
0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16,

View File

@@ -163,20 +163,15 @@ func (self *Image) populate_from_gif(g *gif.GIF) {
Delay: gifmeta.CalculateFrameDelay(g.Delay[i], min_gap),
}
switch prev_disposal {
case gif.DisposalNone, 0: // 1
case gif.DisposalNone, 0:
frame.ComposeOnto = frame.Number - 1
case gif.DisposalPrevious: // 3
case gif.DisposalPrevious:
frame.ComposeOnto = prev_compose_onto
case gif.DisposalBackground: // 2
if i > 0 && g.Delay[i-1] == 0 {
// this is in contravention of the GIF spec but browsers and
// gif2apng both do this, so follow them. Test images for this
// are apple.gif and disposal-background-with-delay.gif
frame.ComposeOnto = frame.Number - 1
} else {
// delay present, frame visible, so clear to background as the spec requires
frame.ComposeOnto = 0
}
case gif.DisposalBackground:
// this is in contravention of the GIF spec but browsers and
// gif2apng both do this, so follow them. Test image for this
// is apple.gif
frame.ComposeOnto = frame.Number - 1
}
prev_disposal, prev_compose_onto = g.Disposal[i], frame.ComposeOnto
self.Frames = append(self.Frames, &frame)

View File

@@ -5,7 +5,7 @@ import os
import subprocess
VERSION = "1.8.19"
VERSION = "1.8.18"
def run(*args: str):

View File

@@ -667,8 +667,3 @@ func Inspect(values ...interface{}) {
o := NewInspector(defaultLogger)
o.Log(2, values...)
}
func Apply(opts ...Option) *Logger {
return defaultLogger.Apply(opts...)
}

View File

@@ -29,7 +29,6 @@ type Palette struct {
Info string // Color for Info level messages
Warn string // Color for Warn level messages
Error string // Color for Error level messages
Fatal string // Color for Fatal level messages
Title string // Color for dump titles (BEGIN/END separators)
}
@@ -48,11 +47,10 @@ var darkPalette = Palette{
Hex: "\033[38;5;156m", // Light green for hex values
Ascii: "\033[38;5;224m", // Light pink for ASCII values
Debug: "\033[36m", // Cyan for Debug level
Info: "\033[32m", // Green for Info level
Warn: "\033[33m", // Yellow for Warn level
Error: "\033[31m", // Standard red
Fatal: "\033[1;31m", // Bold red - stands out more
Debug: "\033[36m", // Cyan for Debug level
Info: "\033[32m", // Green for Info level
Warn: "\033[33m", // Yellow for Warn level
Error: "\033[31m", // Red for Error level
}
// lightPalette defines colors optimized for light terminal backgrounds.
@@ -70,11 +68,10 @@ var lightPalette = Palette{
Hex: "\033[38;5;156m", // Light green for hex values
Ascii: "\033[38;5;224m", // Light pink for ASCII values
Debug: "\033[36m", // Cyan for Debug level
Info: "\033[32m", // Green for Info level
Warn: "\033[33m", // Yellow for Warn level
Error: "\033[31m", // Standard red
Fatal: "\033[1;31m", // Bold red - stands out more
Debug: "\033[36m", // Cyan for Debug level
Info: "\033[32m", // Green for Info level
Warn: "\033[33m", // Yellow for Warn level
Error: "\033[31m", // Red for Error level
}
// ColorizedHandler is a handler that outputs log entries with ANSI color codes.
@@ -253,7 +250,6 @@ func (h *ColorizedHandler) formatLevel(b *strings.Builder, e *lx.Entry) {
lx.LevelInfo: h.palette.Info, // Green
lx.LevelWarn: h.palette.Warn, // Yellow
lx.LevelError: h.palette.Error, // Red
lx.LevelFatal: h.palette.Fatal, // Bold Red
}[e.Level]
b.WriteString(color)

View File

@@ -3,7 +3,6 @@ package lh
import (
"errors"
"fmt"
"github.com/olekukonko/ll/lx"
)
@@ -31,18 +30,6 @@ func NewMultiHandler(h ...lx.Handler) *MultiHandler {
}
}
// Len returns the number of handlers in the MultiHandler.
func (h *MultiHandler) Len() int {
return len(h.Handlers)
}
// Append adds one or more lx.Handler instances to the MultiHandler's list of handlers.
func (h *MultiHandler) Append(handlers ...lx.Handler) {
for _, e := range handlers {
h.Handlers = append(h.Handlers, e)
}
}
// Handle implements the Handler interface, calling Handle on each handler in sequence.
// It collects any errors from handlers and combines them into a single error using errors.Join.
// If no errors occur, it returns nil. Thread-safe if the underlying handlers are thread-safe.

View File

@@ -2,9 +2,8 @@ package lh
import (
"context"
"log/slog"
"github.com/olekukonko/ll/lx"
"log/slog"
)
// SlogHandler adapts a slog.Handler to implement lx.Handler.
@@ -82,7 +81,7 @@ func toSlogLevel(level lx.LevelType) slog.Level {
return slog.LevelInfo
case lx.LevelWarn:
return slog.LevelWarn
case lx.LevelError, lx.LevelFatal:
case lx.LevelError:
return slog.LevelError
default:
return slog.LevelInfo // Default for unknown levels

128
vendor/github.com/olekukonko/ll/ll.go generated vendored
View File

@@ -39,8 +39,6 @@ type Logger struct {
stackBufferSize int // Buffer size for capturing stack traces
separator string // Separator for namespace paths (e.g., "/")
entries atomic.Int64 // Tracks total log entries sent to handler
fatalExits bool
fatalStack bool
}
// New creates a new Logger with the given namespace and optional configurations.
@@ -73,71 +71,22 @@ func New(namespace string, opts ...Option) *Logger {
return logger
}
// Apply applies one or more functional options to the default/global logger.
// Useful for late configuration (e.g., after migration, attach VictoriaLogs handler,
// set level, add middleware, etc.) without changing existing New() calls.
//
// AddContext adds a key-value pair to the logger's context, modifying it directly.
// Unlike Context, it mutates the existing context. It is thread-safe using a write lock.
// Example:
//
// // In main() or init(), after setting up handler
// ll.Apply(
// ll.Handler(vlBatched),
// ll.Level(ll.LevelInfo),
// ll.Use(rateLimiterMiddleware),
// )
//
// Returns the default logger for chaining (if needed).
func (l *Logger) Apply(opts ...Option) *Logger {
l.mu.Lock()
defer l.mu.Unlock()
for _, opt := range opts {
if opt != nil {
opt(l)
}
}
return l
}
// AddContext adds one or more key-value pairs to the logger's persistent context.
// These fields will be included in **every** subsequent log message from this logger
// (and its child namespace loggers).
//
// It supports variadic key-value pairs (string key, any value).
// Non-string keys or uneven number of arguments will be safely ignored/logged.
//
// Returns the logger for chaining.
//
// Examples:
//
// logger.AddContext("user", "alice", "env", "prod")
// logger.AddContext("request_id", reqID, "trace_id", traceID)
// logger.AddContext("service", "payment") // single pair
func (l *Logger) AddContext(pairs ...any) *Logger {
// logger := New("app").Enable()
// logger.AddContext("user", "alice")
// logger.Info("Action") // Output: [app] INFO: Action [user=alice]
func (l *Logger) AddContext(key string, value interface{}) *Logger {
l.mu.Lock()
defer l.mu.Unlock()
// Lazy initialization of context map
// Initialize context map if nil
if l.context == nil {
l.context = make(map[string]interface{})
}
// Process key-value pairs
for i := 0; i < len(pairs)-1; i += 2 {
key, ok := pairs[i].(string)
if !ok {
l.Warnf("AddContext: non-string key at index %d: %v", i, pairs[i])
continue
}
value := pairs[i+1]
l.context[key] = value
}
// Optional: warn about uneven number of arguments
if len(pairs)%2 != 0 {
l.Warn("AddContext: uneven number of arguments, last value ignored")
}
l.context[key] = value
return l
}
@@ -408,7 +357,6 @@ func (l *Logger) Output(values ...interface{}) {
l.output(2, values...)
}
// mark logs the caller's file and line number along with an optional custom name label for tracing execution flow.
func (l *Logger) output(skip int, values ...interface{}) {
if !l.shouldLog(lx.LevelInfo) {
return
@@ -588,10 +536,8 @@ func (l *Logger) Fatal(args ...any) {
os.Exit(1)
}
l.log(lx.LevelFatal, lx.ClassText, cat.Space(args...), nil, l.fatalStack)
if l.fatalExits {
os.Exit(1)
}
l.log(lx.LevelError, lx.ClassText, cat.Space(args...), nil, false)
os.Exit(1)
}
// Fatalf logs a formatted message at Error level with a stack trace and exits the program.
@@ -849,7 +795,6 @@ func (l *Logger) Mark(name ...string) {
l.mark(2, name...)
}
// mark logs the caller's file and line number along with an optional custom name label for tracing execution flow.
func (l *Logger) mark(skip int, names ...string) {
// Skip logging if Info level is not enabled
if !l.shouldLog(lx.LevelInfo) {
@@ -1033,7 +978,7 @@ func (l *Logger) Panic(args ...any) {
panic(msg)
}
l.log(lx.LevelFatal, lx.ClassText, msg, nil, true)
l.log(lx.LevelError, lx.ClassText, msg, nil, true)
panic(msg)
}
@@ -1514,3 +1459,54 @@ func (l *Logger) shouldLog(level lx.LevelType) bool {
return true
}
// WithHandler sets the handler for the logger as a functional option for configuring
// a new logger instance.
// Example:
//
// logger := New("app", WithHandler(lh.NewJSONHandler(os.Stdout)))
func WithHandler(handler lx.Handler) Option {
return func(l *Logger) {
l.handler = handler
}
}
// WithTimestamped returns an Option that configures timestamp settings for the logger's existing handler.
// It enables or disables timestamp logging and optionally sets the timestamp format if the handler
// supports the lx.Timestamper interface. If no handler is set, the function has no effect.
// Parameters:
//
// enable: Boolean to enable or disable timestamp logging
// format: Optional string(s) to specify the timestamp format
func WithTimestamped(enable bool, format ...string) Option {
return func(l *Logger) {
if l.handler != nil { // Check if a handler is set
// Verify if the handler supports the lx.Timestamper interface
if h, ok := l.handler.(lx.Timestamper); ok {
h.Timestamped(enable, format...) // Apply timestamp settings to the handler
}
}
}
}
// WithLevel sets the minimum log level for the logger as a functional option for
// configuring a new logger instance.
// Example:
//
// logger := New("app", WithLevel(lx.LevelWarn))
func WithLevel(level lx.LevelType) Option {
return func(l *Logger) {
l.level = level
}
}
// WithStyle sets the namespace formatting style for the logger as a functional option
// for configuring a new logger instance.
// Example:
//
// logger := New("app", WithStyle(lx.NestedPath))
func WithStyle(style lx.StyleType) Option {
return func(l *Logger) {
l.style = style
}
}

View File

@@ -36,7 +36,6 @@ const (
LevelInfo // Info level for general operational messages
LevelWarn // Warn level for warning conditions
LevelError // Error level for error conditions requiring attention
LevelFatal // Fatal level for critical error conditions
LevelDebug // None level for logs without a specific severity (e.g., raw output)
LevelUnknown // None level for logs without a specific severity (e.g., raw output)
)
@@ -46,9 +45,7 @@ const (
DebugString = "DEBUG"
InfoString = "INFO"
WarnString = "WARN"
WarningString = "WARNING"
ErrorString = "ERROR"
FatalString = "FATAL"
NoneString = "NONE"
UnknownString = "UNKNOWN"
@@ -101,8 +98,6 @@ func (l LevelType) String() string {
return WarnString
case LevelError:
return ErrorString
case LevelFatal:
return FatalString
case LevelNone:
return NoneString
default:
@@ -119,7 +114,7 @@ func LevelParse(s string) LevelType {
return LevelDebug
case InfoString:
return LevelInfo
case WarnString, WarningString: // Allow both "WARN" and "WARNING"
case WarnString, "WARNING": // Allow both "WARN" and "WARNING"
return LevelWarn
case ErrorString:
return LevelError

View File

@@ -1,67 +0,0 @@
package ll
import "github.com/olekukonko/ll/lx"
// WithHandler sets the handler for the logger as a functional option for configuring
// a new logger instance.
// Example:
//
// logger := New("app", WithHandler(lh.NewJSONHandler(os.Stdout)))
func WithHandler(handler lx.Handler) Option {
return func(l *Logger) {
l.handler = handler
}
}
// WithTimestamped returns an Option that configures timestamp settings for the logger's existing handler.
// It enables or disables timestamp logging and optionally sets the timestamp format if the handler
// supports the lx.Timestamper interface. If no handler is set, the function has no effect.
// Parameters:
//
// enable: Boolean to enable or disable timestamp logging
// format: Optional string(s) to specify the timestamp format
func WithTimestamped(enable bool, format ...string) Option {
return func(l *Logger) {
if l.handler != nil { // Check if a handler is set
// Verify if the handler supports the lx.Timestamper interface
if h, ok := l.handler.(lx.Timestamper); ok {
h.Timestamped(enable, format...) // Apply timestamp settings to the handler
}
}
}
}
// WithLevel sets the minimum log level for the logger as a functional option for
// configuring a new logger instance.
// Example:
//
// logger := New("app", WithLevel(lx.LevelWarn))
func WithLevel(level lx.LevelType) Option {
return func(l *Logger) {
l.level = level
}
}
// WithStyle sets the namespace formatting style for the logger as a functional option
// for configuring a new logger instance.
// Example:
//
// logger := New("app", WithStyle(lx.NestedPath))
func WithStyle(style lx.StyleType) Option {
return func(l *Logger) {
l.style = style
}
}
// Functional options (can be passed to New() or applied later)
func WithFatalExits(enabled bool) Option {
return func(l *Logger) {
l.fatalExits = enabled
}
}
func WithFatalStack(enabled bool) Option {
return func(l *Logger) {
l.fatalStack = enabled
}
}

View File

@@ -28,7 +28,7 @@ go get github.com/olekukonko/tablewriter@v0.0.5
#### Latest Version
The latest stable version
```bash
go get github.com/olekukonko/tablewriter@v1.1.3
go get github.com/olekukonko/tablewriter@v1.1.2
```
**Warning:** Version `v1.0.0` contains missing functionality and should not be used.
@@ -62,7 +62,7 @@ func main() {
data := [][]string{
{"Package", "Version", "Status"},
{"tablewriter", "v0.0.5", "legacy"},
{"tablewriter", "v1.1.3", "latest"},
{"tablewriter", "v1.1.2", "latest"},
}
table := tablewriter.NewWriter(os.Stdout)
@@ -77,7 +77,7 @@ func main() {
│ PACKAGE │ VERSION │ STATUS │
├─────────────┼─────────┼────────┤
│ tablewriter │ v0.0.5 │ legacy │
│ tablewriter │ v1.1.3 │ latest │
│ tablewriter │ v1.1.2 │ latest │
└─────────────┴─────────┴────────┘
```

View File

@@ -1,424 +0,0 @@
/*
Package twwidth provides intelligent East Asian width detection.
In 2025/2026, most modern terminal emulators (VSCode, Windows Terminal, iTerm2,
Alacritty) and modern monospace fonts (Hack, Fira Code, Cascadia Code) treat
box-drawing characters as Single Width, regardless of the underlying OS Locale.
Detection Logic (in order of priority):
- RUNEWIDTH_EASTASIAN environment variable (explicit user override)
- Force Legacy Mode (programmatic override for backward compatibility)
- Modern environment detection (VSCode, Windows Terminal, etc. -> Narrow)
- Locale-based detection (CJK locales in traditional terminals -> Wide)
This prioritization ensures that:
- Users can always override behavior using RUNEWIDTH_EASTASIAN
- Modern development environments work correctly by default
- Traditional CJK terminals maintain compatibility via locale checks
Examples:
// Force narrow borders (for Hack font in zh_CN)
RUNEWIDTH_EASTASIAN=0 go run .
// Force wide borders (for legacy CJK terminals)
RUNEWIDTH_EASTASIAN=1 go run .
*/
package twwidth
import (
"os"
"runtime"
"strings"
"sync"
)
// Environment Variable Constants
const (
EnvLCAll = "LC_ALL"
EnvLCCtype = "LC_CTYPE"
EnvLang = "LANG"
EnvRuneWidthEastAsian = "RUNEWIDTH_EASTASIAN"
EnvTerm = "TERM"
EnvTermProgram = "TERM_PROGRAM"
EnvTermProgramWsl = "TERM_PROGRAM_WSL"
EnvWTProfile = "WT_PROFILE_ID" // Windows Terminal
EnvConEmuANSI = "ConEmuANSI" // ConEmu
EnvAlacritty = "ALACRITTY_LOG" // Alacritty
EnvVTEVersion = "VTE_VERSION" // GNOME/VTE
)
const (
overwriteOn = "override_on"
overwriteOff = "override_off"
envModern = "modern_env"
envCjk = "locale_cjk"
envAscii = "default_ascii"
)
// CJK Language Codes (Prefixes)
// Covers ISO 639-1 (2-letter) and common full names used in some systems.
var cjkPrefixes = []string{
"zh", "ja", "ko", // Standard: Chinese, Japanese, Korean
"chi", "zho", // ISO 639-2/B and T for Chinese
"jpn", "kor", // ISO 639-2 for Japanese, Korean
"chinese", "japanese", "korean", // Full names (rare but possible in some legacy systems)
}
// CJK Region Codes
// Checks for specific regions that imply CJK font usage (e.g., en_HK).
var cjkRegions = map[string]bool{
"cn": true, // China
"tw": true, // Taiwan
"hk": true, // Hong Kong
"mo": true, // Macau
"jp": true, // Japan
"kr": true, // South Korea
"kp": true, // North Korea
"sg": true, // Singapore (Often uses CJK fonts)
}
// Modern environments that should use narrow borders (1-width box chars)
var modernEnvironments = map[string]bool{
// Terminal programs
"vscode": true, "visual studio code": true,
"iterm.app": true, "iterm2": true,
"windows terminal": true, "windowsterminal": true,
"alacritty": true, "kitty": true,
"hyper": true, "tabby": true, "terminus": true, "fluentterminal": true,
"warp": true, "ghostty": true, "rio": true,
"jetbrains-jediterm": true,
// Terminal types (TERM signatures)
"xterm-kitty": true, "xterm-ghostty": true, "wezterm": true,
}
var (
eastAsianOnce sync.Once
eastAsianVal bool
// Legacy override control
// Renamed to cfgMu to avoid conflict with width.go's mu
cfgMu sync.RWMutex
forceLegacyEastAsian = false
)
type Enviroment struct {
GOOS string `json:"goos"`
LC_ALL string `json:"lc_all"`
LC_CTYPE string `json:"lc_ctype"`
LANG string `json:"lang"`
RUNEWIDTH_EASTASIAN string `json:"runewidth_eastasian"`
TERM string `json:"term"`
TERM_PROGRAM string `json:"term_program"`
}
// State captures the calculated internal state.
type State struct {
NormalizedLocale string `json:"normalized_locale"`
IsCJKLocale bool `json:"is_cjk_locale"`
IsModernEnv bool `json:"is_modern_env"`
LegacyOverrideMode bool `json:"legacy_override_mode"`
}
// Detection aggregates all debug information regarding East Asian width detection.
type Detection struct {
AutoUseEastAsian bool `json:"auto_use_east_asian"`
DetectionMode string `json:"detection_mode"`
Raw Enviroment `json:"raw"`
Derived State `json:"derived"`
}
// EastAsianForceLegacy forces the detection logic to ignore modern environment checks.
// It relies solely on Locale detection. This is useful for applications that need
// strict backward compatibility.
//
// Note: This does NOT override RUNEWIDTH_EASTASIAN. User environment variables take precedence.
// This should be called before the first table render.
func EastAsianForceLegacy(force bool) {
cfgMu.Lock()
defer cfgMu.Unlock()
forceLegacyEastAsian = force
}
// EastAsianDetect checks the environment variables to determine if
// East Asian width calculations should be enabled.
func EastAsianDetect() bool {
eastAsianOnce.Do(func() {
eastAsianVal = detectEastAsian()
})
return eastAsianVal
}
// EastAsianConservative is a stricter version that only defaults to Narrow
// if the terminal is definitely known to be modern (e.g. VSCode, iTerm2).
// It avoids heuristics like checking "xterm" in the TERM variable.
func EastAsianConservative() bool {
// Check overrides first
if val, found := checkOverrides(); found {
return val
}
// Stricter modern environment detection
if isConservativeModernEnvironment() {
return false
}
// Fall back to locale
return checkLocale()
}
// EastAsianMode returns the decision path used for the current environment.
// Useful for debugging why a specific width was chosen.
func EastAsianMode() string {
// Check override
if val, found := checkOverrides(); found {
if val {
return overwriteOn
}
return overwriteOff
}
cfgMu.RLock()
legacy := forceLegacyEastAsian
cfgMu.RUnlock()
if legacy {
if checkLocale() {
return envCjk
}
return envAscii
}
if isModernEnvironment() {
return envModern
}
if checkLocale() {
return envCjk
}
return envAscii
}
// Debugging returns detailed information about the detection decision.
// Useful for users to include in Github issues.
func Debugging() Detection {
locale := getNormalizedLocale()
cfgMu.RLock()
legacy := forceLegacyEastAsian
cfgMu.RUnlock()
return Detection{
AutoUseEastAsian: EastAsianDetect(),
DetectionMode: EastAsianMode(),
Raw: Enviroment{
GOOS: runtime.GOOS,
LC_ALL: os.Getenv(EnvLCAll),
LC_CTYPE: os.Getenv(EnvLCCtype),
LANG: os.Getenv(EnvLang),
RUNEWIDTH_EASTASIAN: os.Getenv(EnvRuneWidthEastAsian),
TERM: os.Getenv(EnvTerm),
TERM_PROGRAM: os.Getenv(EnvTermProgram),
},
Derived: State{
NormalizedLocale: locale,
IsCJKLocale: isCJKLocale(locale),
IsModernEnv: isModernEnvironment(),
LegacyOverrideMode: legacy,
},
}
}
// detectEastAsian evaluates the environment and locale settings to determine if East Asian width rules should apply.
func detectEastAsian() bool {
// User Override check (Highest Priority)
if val, found := checkOverrides(); found {
return val
}
// Force Legacy Mode check
cfgMu.RLock()
isLegacy := forceLegacyEastAsian
cfgMu.RUnlock()
if isLegacy {
// Legacy mode ignores modern environment checks,
// relying solely on locale.
return checkLocale()
}
// Modern Environment Detection
// If modern, we assume Single Width (return false)
if isModernEnvironment() {
return false
}
// 4. Locale Fallback
return checkLocale()
}
// checkOverrides looks for RUNEWIDTH_EASTASIAN
func checkOverrides() (bool, bool) {
if rw := os.Getenv(EnvRuneWidthEastAsian); rw != "" {
rw = strings.ToLower(rw)
if rw == "0" || rw == "off" || rw == "false" || rw == "no" {
return false, true
}
if rw == "1" || rw == "on" || rw == "true" || rw == "yes" {
return true, true
}
}
return false, false
}
// checkLocale performs the string analysis on LANG/LC_ALL
func checkLocale() bool {
locale := getNormalizedLocale()
if locale == "" {
return false
}
return isCJKLocale(locale)
}
// isModernEnvironment performs comprehensive checks for modern terminal capabilities.
func isModernEnvironment() bool {
// Check TERM_PROGRAM (Most reliable)
if termProg := os.Getenv(EnvTermProgram); termProg != "" {
termProgLower := strings.ToLower(termProg)
if modernEnvironments[termProgLower] {
return true
}
}
// Check WSL specific variable
if os.Getenv(EnvTermProgramWsl) != "" {
return true
}
// Windows Specifics
if runtime.GOOS == "windows" {
// Windows Terminal
if os.Getenv(EnvWTProfile) != "" {
return true
}
// ConEmu/Cmder
if os.Getenv(EnvConEmuANSI) == "ON" {
return true
}
// Modern Windows console (Windows 10+) check via TERM
if term := os.Getenv(EnvTerm); term != "" {
termLower := strings.ToLower(term)
if strings.Contains(termLower, "xterm") ||
strings.Contains(termLower, "vt") {
return true
}
}
}
// VTE-based terminals (GNOME Terminal, Tilix, etc.)
if os.Getenv(EnvVTEVersion) != "" {
return true
}
// Check for Alacritty specifically
if os.Getenv(EnvAlacritty) != "" {
return true
}
// Check TERM for modern terminal signatures
if term := os.Getenv(EnvTerm); term != "" {
termLower := strings.ToLower(term)
// Specific modern terminals often put their name in TERM
if modernEnvironments[termLower] {
return true
}
// Heuristics for standard modern-capable descriptors
if strings.Contains(termLower, "xterm") && !strings.Contains(termLower, "xterm-mono") {
return true
}
if strings.Contains(termLower, "screen") ||
strings.Contains(termLower, "tmux") {
return true
}
}
return false
}
// isConservativeModernEnvironment performs strict checks only for known modern terminals.
func isConservativeModernEnvironment() bool {
termProg := strings.ToLower(os.Getenv(EnvTermProgram))
// Allow-list of definitely modern terminals
switch termProg {
case "vscode", "visual studio code":
return true
case "iterm.app", "iterm2":
return true
case "windows terminal", "windowsterminal":
return true
case "alacritty", "wezterm", "kitty", "ghostty":
return true
case "warp", "tabby", "hyper":
return true
}
// Windows Terminal via specific Env
if os.Getenv(EnvWTProfile) != "" {
return true
}
return false
}
// isCJKLocale determines if a given locale string corresponds to a CJK (Chinese, Japanese, Korean) language or region.
func isCJKLocale(locale string) bool {
// Check Language Prefix
for _, prefix := range cjkPrefixes {
if strings.HasPrefix(locale, prefix) {
return true
}
}
// Check Regions
parts := strings.Split(locale, "_")
if len(parts) > 1 {
for _, part := range parts[1:] {
if cjkRegions[part] {
return true
}
}
}
return false
}
// getNormalizedLocale returns the normalized locale by inspecting environment variables LC_ALL, LC_CTYPE, and LANG.
func getNormalizedLocale() string {
var locale string
if loc := os.Getenv(EnvLCAll); loc != "" {
locale = loc
} else if loc := os.Getenv(EnvLCCtype); loc != "" {
locale = loc
} else if loc := os.Getenv(EnvLang); loc != "" {
locale = loc
}
// Fast fail for empty or standard C/POSIX locales
if locale == "" || locale == "C" || locale == "POSIX" {
return ""
}
// Strip encoding and modifiers
if idx := strings.IndexByte(locale, '.'); idx != -1 {
locale = locale[:idx]
}
if idx := strings.IndexByte(locale, '@'); idx != -1 {
locale = locale[:idx]
}
return strings.ToLower(locale)
}

View File

@@ -1,4 +1,3 @@
// width.go
package twwidth
import (
@@ -22,10 +21,6 @@ const (
// Options allows for configuring width calculation on a per-call basis.
type Options struct {
EastAsianWidth bool
// Explicitly force box drawing chars to be narrow
// regardless of EastAsianWidth setting.
ForceNarrowBorders bool
}
// globalOptions holds the global displaywidth configuration, including East Asian width settings.
@@ -41,25 +36,12 @@ var widthCache *twcache.LRU[string, int]
var ansi = Filter()
func init() {
isEastAsian := EastAsianDetect()
// Initialize global options by detecting from the environment,
// which is the one key feature we get from go-runewidth.
cond := runewidth.NewCondition()
cond.EastAsianWidth = isEastAsian
globalOptions = Options{
EastAsianWidth: isEastAsian,
// Auto-enable ForceNarrowBorders for edge cases.
// If EastAsianWidth is ON (e.g. forced via Env Var), but we detect
// a modern environment, we might technically want to narrow borders
// while keeping text wide.
//
// Note: In the standard EastAsian logic, isEastAsian will
// ALREADY be false for modern environments, so this boolean implies
// a specific "Forced On" scenario.
ForceNarrowBorders: isEastAsian && isModernEnvironment(),
EastAsianWidth: cond.EastAsianWidth,
}
widthCache = twcache.NewLRU[string, int](cacheCapacity)
}
@@ -73,14 +55,6 @@ func makeCacheKey(str string, eastAsianWidth bool) string {
return cachePrefix + str
}
// Display calculates the visual width of a string using a specific runewidth.Condition.
// Deprecated: use WidthWithOptions with the new twwidth.Options struct instead.
// This function is kept for backward compatibility.
func Display(cond *runewidth.Condition, str string) int {
opts := Options{EastAsianWidth: cond.EastAsianWidth}
return WidthWithOptions(str, opts)
}
// Filter compiles and returns a regular expression for matching ANSI escape sequences,
// including CSI (Control Sequence Introducer) and OSC (Operating System Command) sequences.
// The returned regex can be used to strip ANSI codes from strings.
@@ -99,15 +73,25 @@ func Filter() *regexp.Regexp {
return regexp.MustCompile("(" + regCSI + "|" + regOSC + ")")
}
// GetCacheStats returns current cache statistics
func GetCacheStats() (size, capacity int, hitRate float64) {
// SetOptions sets the global options for width calculation.
// This function is thread-safe.
func SetOptions(opts Options) {
mu.Lock()
defer mu.Unlock()
if widthCache == nil {
return 0, 0, 0
if globalOptions.EastAsianWidth != opts.EastAsianWidth {
globalOptions = opts
widthCache.Purge()
}
return widthCache.Len(), widthCache.Cap(), widthCache.HitRate()
}
// SetEastAsian enables or disables East Asian width handling globally.
// This function is thread-safe.
//
// Example:
//
// twdw.SetEastAsian(true) // Enable East Asian width handling
func SetEastAsian(enable bool) {
SetOptions(Options{EastAsianWidth: enable})
}
// IsEastAsian returns the current East Asian width setting.
@@ -124,22 +108,6 @@ func IsEastAsian() bool {
return globalOptions.EastAsianWidth
}
// SetCacheCapacity changes the cache size dynamically
// If capacity <= 0, disables caching entirely
func SetCacheCapacity(capacity int) {
mu.Lock()
defer mu.Unlock()
if capacity <= 0 {
widthCache = nil // nil = fully disabled
return
}
newCache := twcache.NewLRU[string, int](capacity)
widthCache = newCache
}
// SetCondition sets the global East Asian width setting based on a runewidth.Condition.
// Deprecated: use SetOptions with the new twwidth.Options struct instead.
// This function is kept for backward compatibility.
func SetCondition(cond *runewidth.Condition) {
@@ -152,33 +120,55 @@ func SetCondition(cond *runewidth.Condition) {
}
}
// SetEastAsian enables or disables East Asian width handling globally.
// Width calculates the visual width of a string using the global cache for performance.
// It excludes ANSI escape sequences and accounts for the global East Asian width setting.
// This function is thread-safe.
//
// Example:
//
// twdw.SetEastAsian(true) // Enable East Asian width handling
func SetEastAsian(enable bool) {
SetOptions(Options{EastAsianWidth: enable})
}
// width := twdw.Width("Hello\x1b[31mWorld") // Returns 10
func Width(str string) int {
currentEA := IsEastAsian()
key := makeCacheKey(str, currentEA)
// SetForceNarrow to preserve the new flag, or create a new setter
func SetForceNarrow(enable bool) {
mu.Lock()
defer mu.Unlock()
globalOptions.ForceNarrowBorders = enable
widthCache.Purge() // Clear cache because widths might change
}
// SetOptions sets the global options for width calculation.
// This function is thread-safe.
func SetOptions(opts Options) {
mu.Lock()
defer mu.Unlock()
if globalOptions.EastAsianWidth != opts.EastAsianWidth || globalOptions.ForceNarrowBorders != opts.ForceNarrowBorders {
globalOptions = opts
widthCache.Purge()
if w, found := widthCache.Get(key); found {
return w
}
opts := displaywidth.Options{EastAsianWidth: currentEA}
stripped := ansi.ReplaceAllLiteralString(str, "")
calculatedWidth := opts.String(stripped)
widthCache.Add(key, calculatedWidth)
return calculatedWidth
}
// WidthWithOptions calculates the visual width of a string with specific options,
// bypassing the global settings and cache. This is useful for one-shot calculations
// where global state is not desired.
func WidthWithOptions(str string, opts Options) int {
dwOpts := displaywidth.Options{EastAsianWidth: opts.EastAsianWidth}
stripped := ansi.ReplaceAllLiteralString(str, "")
return dwOpts.String(stripped)
}
// WidthNoCache calculates the visual width of a string without using the global cache.
//
// Example:
//
// width := twdw.WidthNoCache("Hello\x1b[31mWorld") // Returns 10
func WidthNoCache(str string) int {
// This function's behavior is equivalent to a one-shot calculation
// using the current global options. The WidthWithOptions function
// does not interact with the cache, thus fulfilling the requirement.
return WidthWithOptions(str, Options{EastAsianWidth: IsEastAsian()})
}
// Deprecated: use WidthWithOptions with the new twwidth.Options struct instead.
// This function is kept for backward compatibility.
func Display(cond *runewidth.Condition, str string) int {
opts := Options{EastAsianWidth: cond.EastAsianWidth}
return WidthWithOptions(str, opts)
}
// Truncate shortens a string to fit within a specified visual width, optionally
@@ -245,13 +235,11 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
// Case 4: String needs truncation (sDisplayWidth > maxWidth).
// maxWidth is the total budget for the final string (content + suffix).
mu.Lock()
currentOpts := globalOptions
mu.Unlock()
currentGlobalEastAsianWidth := IsEastAsian()
// Special case for EastAsianDetect true: if only suffix fits, return suffix.
// Special case for EastAsian true: if only suffix fits, return suffix.
// This was derived from previous test behavior.
if len(suffixStr) > 0 && currentOpts.EastAsianWidth {
if len(suffixStr) > 0 && currentGlobalEastAsianWidth {
provisionalContentWidth := maxWidth - suffixDisplayWidth
if provisionalContentWidth == 0 { // Exactly enough space for suffix only
return suffixStr
@@ -283,6 +271,8 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
inAnsiSequence := false
ansiWrittenToContent := false
dwOpts := displaywidth.Options{EastAsianWidth: currentGlobalEastAsianWidth}
for _, r := range s {
if r == '\x1b' {
inAnsiSequence = true
@@ -315,7 +305,7 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
ansiSeqBuf.Reset()
}
} else { // Normal character
runeDisplayWidth := calculateRunewidth(r, currentOpts)
runeDisplayWidth := dwOpts.Rune(r)
if targetContentForIteration == 0 { // No budget for content at all
break
}
@@ -352,81 +342,28 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
return result
}
// Width calculates the visual width of a string using the global cache for performance.
// It excludes ANSI escape sequences and accounts for the global East Asian width setting.
// This function is thread-safe.
//
// Example:
//
// width := twdw.Width("Hello\x1b[31mWorld") // Returns 10
func Width(str string) int {
// Fast path ASCII (Optimization)
if len(str) == 1 && str[0] < 0x80 {
return 1
}
// SetCacheCapacity changes the cache size dynamically
// If capacity <= 0, disables caching entirely
func SetCacheCapacity(capacity int) {
mu.Lock()
currentOpts := globalOptions
mu.Unlock()
defer mu.Unlock()
key := makeCacheKey(str, currentOpts.EastAsianWidth)
// Check Cache (Optimization)
if w, found := widthCache.Get(key); found {
return w
if capacity <= 0 {
widthCache = nil // nil = fully disabled
return
}
stripped := ansi.ReplaceAllLiteralString(str, "")
calculatedWidth := 0
for _, r := range stripped {
calculatedWidth += calculateRunewidth(r, currentOpts)
}
// Store in Cache
widthCache.Add(key, calculatedWidth)
return calculatedWidth
newCache := twcache.NewLRU[string, int](capacity)
widthCache = newCache
}
// WidthNoCache calculates the visual width of a string without using the global cache.
//
// Example:
//
// width := twdw.WidthNoCache("Hello\x1b[31mWorld") // Returns 10
func WidthNoCache(str string) int {
// This function's behavior is equivalent to a one-shot calculation
// using the current global options. The WidthWithOptions function
// does not interact with the cache, thus fulfilling the requirement.
// GetCacheStats returns current cache statistics
func GetCacheStats() (size, capacity int, hitRate float64) {
mu.Lock()
opts := globalOptions
mu.Unlock()
return WidthWithOptions(str, opts)
}
defer mu.Unlock()
// WidthWithOptions calculates the visual width of a string with specific options,
// bypassing the global settings and cache. This is useful for one-shot calculations
// where global state is not desired.
func WidthWithOptions(str string, opts Options) int {
stripped := ansi.ReplaceAllLiteralString(str, "")
calculatedWidth := 0
for _, r := range stripped {
calculatedWidth += calculateRunewidth(r, opts)
if widthCache == nil {
return 0, 0, 0
}
return calculatedWidth
}
// calculateRunewidth calculates the width of a single rune based on the provided options.
// It applies narrow overrides for box drawing characters if configured.
func calculateRunewidth(r rune, opts Options) int {
if opts.ForceNarrowBorders && isBoxDrawingChar(r) {
return 1
}
dwOpts := displaywidth.Options{EastAsianWidth: opts.EastAsianWidth}
return dwOpts.Rune(r)
}
// isBoxDrawingChar checks if a rune is within the Unicode Box Drawing range.
func isBoxDrawingChar(r rune) bool {
return r >= 0x2500 && r <= 0x257F
return widthCache.Len(), widthCache.Cap(), widthCache.HitRate()
}

View File

@@ -4,6 +4,7 @@ import (
"io"
"strings"
"github.com/fatih/color"
"github.com/olekukonko/ll"
"github.com/olekukonko/ll/lh"
"github.com/olekukonko/tablewriter/pkg/twwidth"
@@ -23,6 +24,28 @@ type ColorizedConfig struct {
Symbols tw.Symbols // Symbols for table drawing (e.g., corners, lines)
}
// Colors is a slice of color attributes for use with fatih/color, such as color.FgWhite or color.Bold.
type Colors []color.Attribute
// Tint defines foreground and background color settings for table elements, with optional per-column overrides.
type Tint struct {
FG Colors // Foreground color attributes
BG Colors // Background color attributes
Columns []Tint // Per-column color settings
}
// Apply applies the Tint's foreground and background colors to the given text, returning the text unchanged if no colors are set.
func (t Tint) Apply(text string) string {
if len(t.FG) == 0 && len(t.BG) == 0 {
return text
}
// Combine foreground and background colors
combinedColors := append(t.FG, t.BG...)
// Create a color function and apply it to the text
c := color.New(combinedColors...).SprintFunc()
return c(text)
}
// Colorized renders colored ASCII tables with customizable borders, colors, and alignments.
type Colorized struct {
config ColorizedConfig // Renderer configuration

View File

@@ -1,25 +0,0 @@
package renderer
import "github.com/fatih/color"
// Colors is a slice of color attributes for use with fatih/color, such as color.FgWhite or color.Bold.
type Colors []color.Attribute
// Tint defines foreground and background color settings for table elements, with optional per-column overrides.
type Tint struct {
FG Colors // Foreground color attributes
BG Colors // Background color attributes
Columns []Tint // Per-column color settings
}
// Apply applies the Tint's foreground and background colors to the given text, returning the text unchanged if no colors are set.
func (t Tint) Apply(text string) string {
if len(t.FG) == 0 && len(t.BG) == 0 {
return text
}
// Combine foreground and background colors
combinedColors := append(t.FG, t.BG...)
// Create a color function and apply it to the text
c := color.New(combinedColors...).SprintFunc()
return c(text)
}

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"io"
"math"
"os"
"reflect"
"runtime"
"strings"
@@ -418,6 +419,7 @@ func (t *Table) Options(opts ...Option) *Table {
}
// force debugging mode if set
// This should be move away form WithDebug
if t.config.Debug {
t.logger.Enable()
t.logger.Resume()
@@ -432,28 +434,11 @@ func (t *Table) Options(opts ...Option) *Table {
goArch := runtime.GOARCH
numCPU := runtime.NumCPU()
// Use the new struct-based info.
// No type assertions or magic strings needed.
info := twwidth.Debugging()
t.logger.Infof("Go Runtime: Version=%s, OS=%s, Arch=%s, CPUs=%d",
goVersion, goOS, goArch, numCPU)
t.logger.Infof("Environment: LC_CTYPE=%s, LANG=%s, TERM=%s, TERM_PROGRAM=%s",
info.Raw.LC_CTYPE,
info.Raw.LANG,
info.Raw.TERM,
info.Raw.TERM_PROGRAM,
)
t.logger.Infof("East Asian Detection: Auto=%v, Mode=%s, ModernEnv=%v, CJKLocale=%v",
info.AutoUseEastAsian,
info.DetectionMode,
info.Derived.IsModernEnv,
info.Derived.IsCJKLocale,
)
t.logger.Infof("Environment: LC_CTYPE=%s, LANG=%s, TERM=%s", os.Getenv("LC_CTYPE"), os.Getenv("LANG"), os.Getenv("TERM"))
t.logger.Infof("Go Runtime: Version=%s, OS=%s, Arch=%s, CPUs=%d", goVersion, goOS, goArch, numCPU)
// send logger to renderer
// this will overwrite the default logger
t.renderer.Logger(t.logger)
return t
}

View File

@@ -991,7 +991,7 @@ func (t *Table) calculateContentMaxWidth(colIdx int, config tw.CellConfig, padLe
constraintTotalCellWidth := 0
hasConstraint := false
// Check new Widths.PerColumn (highest priority)
// 1. Check new Widths.PerColumn (highest priority)
if t.config.Widths.Constrained() {
if colWidth, ok := t.config.Widths.PerColumn.OK(colIdx); ok && colWidth > 0 {
@@ -1001,7 +1001,7 @@ func (t *Table) calculateContentMaxWidth(colIdx int, config tw.CellConfig, padLe
colIdx, constraintTotalCellWidth)
}
// Check new Widths.Global
// 2. Check new Widths.Global
if !hasConstraint && t.config.Widths.Global > 0 {
constraintTotalCellWidth = t.config.Widths.Global
hasConstraint = true
@@ -1009,7 +1009,7 @@ func (t *Table) calculateContentMaxWidth(colIdx int, config tw.CellConfig, padLe
}
}
// Fall back to legacy ColMaxWidths.PerColumn (backward compatibility)
// 3. Fall back to legacy ColMaxWidths.PerColumn (backward compatibility)
if !hasConstraint && config.ColMaxWidths.PerColumn != nil {
if colMax, ok := config.ColMaxWidths.PerColumn.OK(colIdx); ok && colMax > 0 {
constraintTotalCellWidth = colMax
@@ -1019,7 +1019,7 @@ func (t *Table) calculateContentMaxWidth(colIdx int, config tw.CellConfig, padLe
}
}
// Fall back to legacy ColMaxWidths.Global
// 4. Fall back to legacy ColMaxWidths.Global
if !hasConstraint && config.ColMaxWidths.Global > 0 {
constraintTotalCellWidth = config.ColMaxWidths.Global
hasConstraint = true
@@ -1027,7 +1027,7 @@ func (t *Table) calculateContentMaxWidth(colIdx int, config tw.CellConfig, padLe
constraintTotalCellWidth)
}
// Fall back to table MaxWidth if auto-wrapping
// 5. Fall back to table MaxWidth if auto-wrapping
if !hasConstraint && t.config.MaxWidth > 0 && config.Formatting.AutoWrap != tw.WrapNone {
constraintTotalCellWidth = t.config.MaxWidth
hasConstraint = true
@@ -1217,10 +1217,14 @@ func (t *Table) convertToString(value interface{}) string {
// convertItemToCells is responsible for converting a single input item (which could be
// a struct, a basic type, or an item implementing Stringer/Formatter) into a slice
// of strings, where each string represents a cell for the table row.
// zoo.go
// convertItemToCells is responsible for converting a single input item into a slice of strings.
// It now uses the unified struct parser for structs.
func (t *Table) convertItemToCells(item interface{}) ([]string, error) {
t.logger.Debugf("convertItemToCells: Converting item of type %T", item)
// User-defined table-wide stringer (t.stringer) takes highest precedence.
// 1. User-defined table-wide stringer (t.stringer) takes highest precedence.
if t.stringer != nil {
res, err := t.convertToStringer(item)
if err == nil {
@@ -1230,13 +1234,13 @@ func (t *Table) convertItemToCells(item interface{}) ([]string, error) {
t.logger.Warnf("convertItemToCells: Custom table stringer was set but incompatible for type %T: %v. Will attempt other methods.", item, err)
}
// Handle untyped nil directly.
// 2. Handle untyped nil directly.
if item == nil {
t.logger.Debugf("convertItemToCells: Item is untyped nil. Returning single empty cell.")
return []string{""}, nil
}
// Use the new unified struct parser. It handles pointers and embedding.
// 3. Use the new unified struct parser. It handles pointers and embedding.
// We only care about the values it returns.
_, values := t.extractFieldsAndValuesFromStruct(item)
if values != nil {
@@ -1244,7 +1248,7 @@ func (t *Table) convertItemToCells(item interface{}) ([]string, error) {
return values, nil
}
// Fallback for any other single item (e.g., basic types, or types that implement Stringer/Formatter).
// 4. Fallback for any other single item (e.g., basic types, or types that implement Stringer/Formatter).
// This code path is now for non-struct types.
if formatter, ok := item.(tw.Formatter); ok {
t.logger.Debugf("convertItemToCells: Item (non-struct, type %T) is tw.Formatter. Using Format().", item)

14
vendor/modules.txt vendored
View File

@@ -255,7 +255,7 @@ github.com/cespare/xxhash/v2
# github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73
## explicit
github.com/cevaris/ordered_map
# github.com/clipperhouse/displaywidth v0.6.2
# github.com/clipperhouse/displaywidth v0.6.0
## explicit; go 1.18
github.com/clipperhouse/displaywidth
# github.com/clipperhouse/stringish v0.1.1
@@ -805,7 +805,7 @@ github.com/gorilla/schema
## explicit; go 1.14
github.com/grpc-ecosystem/go-grpc-middleware
github.com/grpc-ecosystem/go-grpc-middleware/recovery
# github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4
# github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3
## explicit; go 1.24.0
github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options
@@ -894,7 +894,7 @@ github.com/kovidgoyal/go-parallel
# github.com/kovidgoyal/go-shm v1.0.0
## explicit; go 1.24.0
github.com/kovidgoyal/go-shm
# github.com/kovidgoyal/imaging v1.8.19
# github.com/kovidgoyal/imaging v1.8.18
## explicit; go 1.24.0
github.com/kovidgoyal/imaging
github.com/kovidgoyal/imaging/apng
@@ -1197,12 +1197,12 @@ github.com/olekukonko/cat
# github.com/olekukonko/errors v1.1.0
## explicit; go 1.21
github.com/olekukonko/errors
# github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0
# github.com/olekukonko/ll v0.1.3
## explicit; go 1.21
github.com/olekukonko/ll
github.com/olekukonko/ll/lh
github.com/olekukonko/ll/lx
# github.com/olekukonko/tablewriter v1.1.3
# github.com/olekukonko/tablewriter v1.1.2
## explicit; go 1.21
github.com/olekukonko/tablewriter
github.com/olekukonko/tablewriter/pkg/twcache
@@ -2587,12 +2587,12 @@ golang.org/x/tools/internal/versions
# google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb
## explicit; go 1.23.0
google.golang.org/genproto/protobuf/field_mask
# google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b
# google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217
## explicit; go 1.24.0
google.golang.org/genproto/googleapis/api
google.golang.org/genproto/googleapis/api/annotations
google.golang.org/genproto/googleapis/api/httpbody
# google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b
# google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217
## explicit; go 1.24.0
google.golang.org/genproto/googleapis/rpc/errdetails
google.golang.org/genproto/googleapis/rpc/status