| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package opendal |
| |
| import ( |
| "context" |
| "net/http" |
| "testing" |
| "time" |
| "unsafe" |
| |
| "github.com/jupiterrider/ffi" |
| ) |
| |
| func TestEntryFreeUsesVoidReturnType(t *testing.T) { |
| if ffiEntryFree.opts.rType != &ffi.TypeVoid { |
| t.Fatalf("ffiEntryFree return type = %v, want void", ffiEntryFree.opts.rType) |
| } |
| } |
| |
| func TestCopyCStringAndFreeNil(t *testing.T) { |
| var freed int |
| freeCString := func(*byte) { |
| freed++ |
| } |
| |
| got := copyCStringAndFree(nil, freeCString) |
| if got != "" { |
| t.Fatalf("copyCStringAndFree(nil) = %q, want empty string", got) |
| } |
| if freed != 0 { |
| t.Fatalf("copyCStringAndFree(nil) freed %d pointers, want 0", freed) |
| } |
| } |
| |
| func TestOperatorInfoCopiesAndFreesOwnedStrings(t *testing.T) { |
| var freed []*byte |
| freeCString := func(ptr *byte) { |
| freed = append(freed, ptr) |
| } |
| |
| schemePtr := mustBytePtrFromString(t, "memory") |
| rootPtr := mustBytePtrFromString(t, "/tmp/") |
| namePtr := mustBytePtrFromString(t, "namespace") |
| infoInner := &opendalOperatorInfo{} |
| capability := &opendalCapability{stat: 1} |
| infoFreed := 0 |
| |
| ctx := context.Background() |
| ctx = context.WithValue(ctx, ffiStringFree.opts.sym, freeCString) |
| ctx = context.WithValue(ctx, ffiOperatorInfoNew.opts.sym, func(op *opendalOperator) *opendalOperatorInfo { |
| if op == nil { |
| t.Fatal("Info() passed nil operator") |
| } |
| return infoInner |
| }) |
| ctx = context.WithValue(ctx, ffiOperatorInfoFree.opts.sym, func(info *opendalOperatorInfo) { |
| if info != infoInner { |
| t.Fatalf("Info() freed unexpected operator info: %p", info) |
| } |
| infoFreed++ |
| }) |
| ctx = context.WithValue(ctx, ffiOperatorInfoGetScheme.opts.sym, ffiOperatorInfoGetScheme.withFunc(ctx, func(rValue unsafe.Pointer, aValues ...unsafe.Pointer) { |
| assertOperatorInfoPointer(t, infoInner, aValues...) |
| *(**byte)(rValue) = schemePtr |
| })) |
| ctx = context.WithValue(ctx, ffiOperatorInfoGetRoot.opts.sym, ffiOperatorInfoGetRoot.withFunc(ctx, func(rValue unsafe.Pointer, aValues ...unsafe.Pointer) { |
| assertOperatorInfoPointer(t, infoInner, aValues...) |
| *(**byte)(rValue) = rootPtr |
| })) |
| ctx = context.WithValue(ctx, ffiOperatorInfoGetName.opts.sym, ffiOperatorInfoGetName.withFunc(ctx, func(rValue unsafe.Pointer, aValues ...unsafe.Pointer) { |
| assertOperatorInfoPointer(t, infoInner, aValues...) |
| *(**byte)(rValue) = namePtr |
| })) |
| ctx = context.WithValue(ctx, ffiOperatorInfoGetCapability.opts.sym, func(info *opendalOperatorInfo) *opendalCapability { |
| if info != infoInner { |
| t.Fatalf("Info() requested full capability for unexpected operator info: %p", info) |
| } |
| return capability |
| }) |
| |
| op := &Operator{ |
| ctx: ctx, |
| inner: &opendalOperator{}, |
| } |
| |
| info := op.Info() |
| if info.GetScheme() != "memory" { |
| t.Fatalf("Info().GetScheme() = %q, want memory", info.GetScheme()) |
| } |
| if info.GetRoot() != "/tmp/" { |
| t.Fatalf("Info().GetRoot() = %q, want /tmp/", info.GetRoot()) |
| } |
| if info.GetName() != "namespace" { |
| t.Fatalf("Info().GetName() = %q, want namespace", info.GetName()) |
| } |
| if !info.GetCapability().Stat() { |
| t.Fatal("Info().GetCapability().Stat() = false, want true") |
| } |
| if infoFreed != 1 { |
| t.Fatalf("Info() freed operator info %d times, want 1", infoFreed) |
| } |
| assertFreedPointers(t, freed, schemePtr, rootPtr, namePtr) |
| } |
| |
| func TestNewEntryCopiesAndFreesOwnedStrings(t *testing.T) { |
| var freed []*byte |
| freeCString := func(ptr *byte) { |
| freed = append(freed, ptr) |
| } |
| |
| namePtr := mustBytePtrFromString(t, "file.txt") |
| pathPtr := mustBytePtrFromString(t, "dir/file.txt") |
| entryInner := &opendalEntry{} |
| entryFreed := 0 |
| |
| ctx := context.Background() |
| ctx = context.WithValue(ctx, ffiStringFree.opts.sym, freeCString) |
| ctx = context.WithValue(ctx, ffiEntryFree.opts.sym, func(entry *opendalEntry) { |
| if entry != entryInner { |
| t.Fatalf("newEntry() freed unexpected entry: %p", entry) |
| } |
| entryFreed++ |
| }) |
| ctx = context.WithValue(ctx, ffiEntryName.opts.sym, ffiEntryName.withFunc(ctx, func(rValue unsafe.Pointer, aValues ...unsafe.Pointer) { |
| assertEntryPointer(t, entryInner, aValues...) |
| *(**byte)(rValue) = namePtr |
| })) |
| ctx = context.WithValue(ctx, ffiEntryPath.opts.sym, ffiEntryPath.withFunc(ctx, func(rValue unsafe.Pointer, aValues ...unsafe.Pointer) { |
| assertEntryPointer(t, entryInner, aValues...) |
| *(**byte)(rValue) = pathPtr |
| })) |
| |
| // Mock the new metadata path exercised by newEntry() |
| metaInner := &opendalMetadata{} |
| metaFreed := 0 |
| ctx = context.WithValue(ctx, ffiEntryMetadata.opts.sym, func(e *opendalEntry) *opendalMetadata { |
| if e != entryInner { |
| t.Fatalf("ffiEntryMetadata called with unexpected entry") |
| } |
| return metaInner |
| }) |
| ctx = context.WithValue(ctx, ffiMetadataFree.opts.sym, func(m *opendalMetadata) { |
| if m != metaInner { |
| t.Fatalf("metadata freed unexpected pointer: %p", m) |
| } |
| metaFreed++ |
| }) |
| ctx = context.WithValue(ctx, ffiMetaCacheControl.opts.sym, func(m *opendalMetadata) *string { |
| assertMetadataPointer(t, metaInner, m) |
| return nil |
| }) |
| ctx = context.WithValue(ctx, ffiMetaContentDisposition.opts.sym, func(m *opendalMetadata) *string { |
| assertMetadataPointer(t, metaInner, m) |
| return nil |
| }) |
| ctx = context.WithValue(ctx, ffiMetaContentEncoding.opts.sym, func(m *opendalMetadata) *string { |
| assertMetadataPointer(t, metaInner, m) |
| return nil |
| }) |
| ctx = context.WithValue(ctx, ffiMetaContentLength.opts.sym, func(m *opendalMetadata) uint64 { |
| assertMetadataPointer(t, metaInner, m) |
| return 4096 |
| }) |
| ctx = context.WithValue(ctx, ffiMetaContentMD5.opts.sym, func(m *opendalMetadata) *string { |
| assertMetadataPointer(t, metaInner, m) |
| return nil |
| }) |
| contentType := "text/plain" |
| ctx = context.WithValue(ctx, ffiMetaContentType.opts.sym, func(m *opendalMetadata) *string { |
| assertMetadataPointer(t, metaInner, m) |
| return &contentType |
| }) |
| ctx = context.WithValue(ctx, ffiMetaEtag.opts.sym, func(m *opendalMetadata) *string { |
| assertMetadataPointer(t, metaInner, m) |
| return nil |
| }) |
| ctx = context.WithValue(ctx, ffiMetaIsCurrent.opts.sym, func(m *opendalMetadata) uint8 { |
| assertMetadataPointer(t, metaInner, m) |
| return 2 |
| }) |
| ctx = context.WithValue(ctx, ffiMetaIsDeleted.opts.sym, func(m *opendalMetadata) bool { |
| assertMetadataPointer(t, metaInner, m) |
| return false |
| }) |
| ctx = context.WithValue(ctx, ffiMetaIsFile.opts.sym, func(m *opendalMetadata) bool { |
| assertMetadataPointer(t, metaInner, m) |
| return true |
| }) |
| ctx = context.WithValue(ctx, ffiMetaIsDir.opts.sym, func(m *opendalMetadata) bool { |
| assertMetadataPointer(t, metaInner, m) |
| return false |
| }) |
| ctx = context.WithValue(ctx, ffiMetaLastModified.opts.sym, func(m *opendalMetadata) int64 { |
| assertMetadataPointer(t, metaInner, m) |
| return 1700000000000 |
| }) |
| ctx = context.WithValue(ctx, ffiMetaMode.opts.sym, func(m *opendalMetadata) EntryMode { |
| assertMetadataPointer(t, metaInner, m) |
| return EntryModeFile |
| }) |
| ctx = context.WithValue(ctx, ffiMetaVersion.opts.sym, func(m *opendalMetadata) *string { |
| assertMetadataPointer(t, metaInner, m) |
| return nil |
| }) |
| ctx = context.WithValue(ctx, ffiMetaUserMetadata.opts.sym, func(m *opendalMetadata) map[string]string { |
| assertMetadataPointer(t, metaInner, m) |
| return nil |
| }) |
| |
| entry := newEntry(ctx, entryInner) |
| if entry.Name() != "file.txt" { |
| t.Fatalf("newEntry().Name() = %q, want file.txt", entry.Name()) |
| } |
| if entry.Path() != "dir/file.txt" { |
| t.Fatalf("newEntry().Path() = %q, want dir/file.txt", entry.Path()) |
| } |
| if entryFreed != 1 { |
| t.Fatalf("newEntry() freed entry %d times, want 1", entryFreed) |
| } |
| assertFreedPointers(t, freed, namePtr, pathPtr) |
| |
| // Verify new Metadata() support |
| meta := entry.Metadata() |
| if meta == nil { |
| t.Fatal("newEntry().Metadata() returned nil, expected non-nil") |
| } |
| if meta.ContentLength() != 4096 { |
| t.Fatalf("Metadata().ContentLength() = %d, want 4096", meta.ContentLength()) |
| } |
| if !meta.IsFile() { |
| t.Fatal("Metadata().IsFile() = false, want true") |
| } |
| if meta.IsDir() { |
| t.Fatal("Metadata().IsDir() = true, want false") |
| } |
| if meta.LastModified().UnixMilli() != 1700000000000 { |
| t.Fatalf("Metadata().LastModified().UnixMilli() = %d, want 1700000000000", meta.LastModified().UnixMilli()) |
| } |
| if meta.Mode() != EntryModeFile { |
| t.Fatalf("Metadata().Mode() = %d, want %d", meta.Mode(), EntryModeFile) |
| } |
| if contentType, ok := meta.ContentType(); !ok || contentType != "text/plain" { |
| t.Fatalf("Metadata().ContentType() = %q, %v, want text/plain, true", contentType, ok) |
| } |
| if _, ok := meta.CacheControl(); ok { |
| t.Fatal("Metadata().CacheControl() ok = true, want false") |
| } |
| if metaFreed != 1 { |
| t.Fatalf("newEntry() freed metadata %d times, want 1", metaFreed) |
| } |
| } |
| |
| func TestNewMetadataCopiesAllFieldsAndFreesOwnedValues(t *testing.T) { |
| var freed []*byte |
| freeCString := func(ptr *byte) { |
| freed = append(freed, ptr) |
| } |
| |
| cacheControlPtr := mustBytePtrFromString(t, "max-age=60") |
| contentDispositionPtr := mustBytePtrFromString(t, "attachment") |
| contentEncodingPtr := mustBytePtrFromString(t, "gzip") |
| contentMD5Ptr := mustBytePtrFromString(t, "1B2M2Y8AsgTpgAmY7PhCfg==") |
| contentTypePtr := mustBytePtrFromString(t, "application/json") |
| etagPtr := mustBytePtrFromString(t, `"etag"`) |
| versionPtr := mustBytePtrFromString(t, "v42") |
| |
| metadataInner := &opendalMetadata{} |
| userMetadataInner := &opendalMetadataUserMetadata{} |
| pairs := []opendalMetadataUserMetadataPair{ |
| {key: mustBytePtrFromString(t, "foo"), value: mustBytePtrFromString(t, "bar")}, |
| {key: mustBytePtrFromString(t, "empty"), value: mustBytePtrFromString(t, "")}, |
| } |
| metadataFreed := 0 |
| userMetadataFreed := 0 |
| |
| ctx := context.Background() |
| ctx = context.WithValue(ctx, ffiStringFree.opts.sym, freeCString) |
| ctx = context.WithValue(ctx, ffiMetadataFree.opts.sym, func(m *opendalMetadata) { |
| assertMetadataPointer(t, metadataInner, m) |
| metadataFreed++ |
| }) |
| ctx = context.WithValue(ctx, ffiMetaCacheControl.opts.sym, ffiMetaCacheControl.withFunc(ctx, metadataStringFunc(t, metadataInner, cacheControlPtr))) |
| ctx = context.WithValue(ctx, ffiMetaContentDisposition.opts.sym, ffiMetaContentDisposition.withFunc(ctx, metadataStringFunc(t, metadataInner, contentDispositionPtr))) |
| ctx = context.WithValue(ctx, ffiMetaContentEncoding.opts.sym, ffiMetaContentEncoding.withFunc(ctx, metadataStringFunc(t, metadataInner, contentEncodingPtr))) |
| ctx = context.WithValue(ctx, ffiMetaContentLength.opts.sym, func(m *opendalMetadata) uint64 { |
| assertMetadataPointer(t, metadataInner, m) |
| return 4096 |
| }) |
| ctx = context.WithValue(ctx, ffiMetaContentMD5.opts.sym, ffiMetaContentMD5.withFunc(ctx, metadataStringFunc(t, metadataInner, contentMD5Ptr))) |
| ctx = context.WithValue(ctx, ffiMetaContentType.opts.sym, ffiMetaContentType.withFunc(ctx, metadataStringFunc(t, metadataInner, contentTypePtr))) |
| ctx = context.WithValue(ctx, ffiMetaEtag.opts.sym, ffiMetaEtag.withFunc(ctx, metadataStringFunc(t, metadataInner, etagPtr))) |
| ctx = context.WithValue(ctx, ffiMetaIsCurrent.opts.sym, func(m *opendalMetadata) uint8 { |
| assertMetadataPointer(t, metadataInner, m) |
| return 1 |
| }) |
| ctx = context.WithValue(ctx, ffiMetaIsDeleted.opts.sym, func(m *opendalMetadata) bool { |
| assertMetadataPointer(t, metadataInner, m) |
| return true |
| }) |
| ctx = context.WithValue(ctx, ffiMetaIsFile.opts.sym, func(m *opendalMetadata) bool { |
| assertMetadataPointer(t, metadataInner, m) |
| return false |
| }) |
| ctx = context.WithValue(ctx, ffiMetaIsDir.opts.sym, func(m *opendalMetadata) bool { |
| assertMetadataPointer(t, metadataInner, m) |
| return true |
| }) |
| ctx = context.WithValue(ctx, ffiMetaLastModified.opts.sym, func(m *opendalMetadata) int64 { |
| assertMetadataPointer(t, metadataInner, m) |
| return 1700000000000 |
| }) |
| ctx = context.WithValue(ctx, ffiMetaMode.opts.sym, func(m *opendalMetadata) EntryMode { |
| assertMetadataPointer(t, metadataInner, m) |
| return EntryModeDir |
| }) |
| ctx = context.WithValue(ctx, ffiMetaVersion.opts.sym, ffiMetaVersion.withFunc(ctx, metadataStringFunc(t, metadataInner, versionPtr))) |
| ctx = context.WithValue(ctx, ffiMetaUserMetadataPairs.opts.sym, ffiMetaUserMetadataPairs.withFunc(ctx, func(rValue unsafe.Pointer, aValues ...unsafe.Pointer) { |
| assertUserMetadataPointerFromArgs(t, userMetadataInner, aValues...) |
| *(**opendalMetadataUserMetadataPair)(rValue) = &pairs[0] |
| })) |
| ctx = context.WithValue(ctx, ffiMetaUserMetadataLen.opts.sym, ffiMetaUserMetadataLen.withFunc(ctx, func(rValue unsafe.Pointer, aValues ...unsafe.Pointer) { |
| assertUserMetadataPointerFromArgs(t, userMetadataInner, aValues...) |
| *(*uint64)(rValue) = uint64(len(pairs)) |
| })) |
| ctx = context.WithValue(ctx, ffiMetaUserMetadataFree.opts.sym, func(m *opendalMetadataUserMetadata) { |
| if m != userMetadataInner { |
| t.Fatalf("user metadata freed unexpected pointer: %p", m) |
| } |
| userMetadataFreed++ |
| }) |
| ctx = context.WithValue(ctx, ffiMetaUserMetadata.opts.sym, ffiMetaUserMetadata.withFunc(ctx, func(rValue unsafe.Pointer, aValues ...unsafe.Pointer) { |
| assertMetadataPointerFromArgs(t, metadataInner, aValues...) |
| *(**opendalMetadataUserMetadata)(rValue) = userMetadataInner |
| })) |
| |
| meta := newMetadata(ctx, metadataInner) |
| assertOptionalString(t, "CacheControl", meta.CacheControl, "max-age=60") |
| assertOptionalString(t, "ContentDisposition", meta.ContentDisposition, "attachment") |
| assertOptionalString(t, "ContentEncoding", meta.ContentEncoding, "gzip") |
| assertOptionalString(t, "ContentMD5", meta.ContentMD5, "1B2M2Y8AsgTpgAmY7PhCfg==") |
| assertOptionalString(t, "ContentType", meta.ContentType, "application/json") |
| assertOptionalString(t, "ETag", meta.ETag, `"etag"`) |
| assertOptionalString(t, "Version", meta.Version, "v42") |
| if meta.ContentLength() != 4096 { |
| t.Fatalf("ContentLength() = %d, want 4096", meta.ContentLength()) |
| } |
| current, ok := meta.IsCurrent() |
| if !ok || !current { |
| t.Fatalf("IsCurrent() = %v, %v, want true, true", current, ok) |
| } |
| if !meta.IsDeleted() { |
| t.Fatal("IsDeleted() = false, want true") |
| } |
| if meta.IsFile() { |
| t.Fatal("IsFile() = true, want false") |
| } |
| if !meta.IsDir() { |
| t.Fatal("IsDir() = false, want true") |
| } |
| if meta.LastModified().UnixMilli() != 1700000000000 { |
| t.Fatalf("LastModified().UnixMilli() = %d, want 1700000000000", meta.LastModified().UnixMilli()) |
| } |
| if meta.Mode() != EntryModeDir { |
| t.Fatalf("Mode() = %d, want %d", meta.Mode(), EntryModeDir) |
| } |
| assertStringMap(t, meta.UserMetadata(), map[string]string{"foo": "bar", "empty": ""}) |
| |
| userMetadata := meta.UserMetadata() |
| userMetadata["foo"] = "changed" |
| assertStringMap(t, meta.UserMetadata(), map[string]string{"foo": "bar", "empty": ""}) |
| |
| if metadataFreed != 1 { |
| t.Fatalf("metadata freed %d times, want 1", metadataFreed) |
| } |
| if userMetadataFreed != 1 { |
| t.Fatalf("user metadata freed %d times, want 1", userMetadataFreed) |
| } |
| assertFreedPointers(t, freed, cacheControlPtr, contentDispositionPtr, contentEncodingPtr, contentMD5Ptr, contentTypePtr, etagPtr, versionPtr) |
| } |
| |
| func TestNewMetadataOptionalValuesCanBeAbsent(t *testing.T) { |
| metadataInner := &opendalMetadata{} |
| metadataFreed := 0 |
| |
| ctx := context.Background() |
| ctx = context.WithValue(ctx, ffiMetadataFree.opts.sym, func(m *opendalMetadata) { |
| assertMetadataPointer(t, metadataInner, m) |
| metadataFreed++ |
| }) |
| ctx = context.WithValue(ctx, ffiMetaCacheControl.opts.sym, func(m *opendalMetadata) *string { |
| assertMetadataPointer(t, metadataInner, m) |
| return nil |
| }) |
| ctx = context.WithValue(ctx, ffiMetaContentDisposition.opts.sym, func(m *opendalMetadata) *string { |
| assertMetadataPointer(t, metadataInner, m) |
| return nil |
| }) |
| ctx = context.WithValue(ctx, ffiMetaContentEncoding.opts.sym, func(m *opendalMetadata) *string { |
| assertMetadataPointer(t, metadataInner, m) |
| return nil |
| }) |
| ctx = context.WithValue(ctx, ffiMetaContentLength.opts.sym, func(m *opendalMetadata) uint64 { |
| assertMetadataPointer(t, metadataInner, m) |
| return 0 |
| }) |
| ctx = context.WithValue(ctx, ffiMetaContentMD5.opts.sym, func(m *opendalMetadata) *string { |
| assertMetadataPointer(t, metadataInner, m) |
| return nil |
| }) |
| ctx = context.WithValue(ctx, ffiMetaContentType.opts.sym, func(m *opendalMetadata) *string { |
| assertMetadataPointer(t, metadataInner, m) |
| return nil |
| }) |
| ctx = context.WithValue(ctx, ffiMetaEtag.opts.sym, func(m *opendalMetadata) *string { |
| assertMetadataPointer(t, metadataInner, m) |
| return nil |
| }) |
| ctx = context.WithValue(ctx, ffiMetaIsCurrent.opts.sym, func(m *opendalMetadata) uint8 { |
| assertMetadataPointer(t, metadataInner, m) |
| return 2 |
| }) |
| ctx = context.WithValue(ctx, ffiMetaIsDeleted.opts.sym, func(m *opendalMetadata) bool { |
| assertMetadataPointer(t, metadataInner, m) |
| return false |
| }) |
| ctx = context.WithValue(ctx, ffiMetaIsFile.opts.sym, func(m *opendalMetadata) bool { |
| assertMetadataPointer(t, metadataInner, m) |
| return false |
| }) |
| ctx = context.WithValue(ctx, ffiMetaIsDir.opts.sym, func(m *opendalMetadata) bool { |
| assertMetadataPointer(t, metadataInner, m) |
| return false |
| }) |
| ctx = context.WithValue(ctx, ffiMetaLastModified.opts.sym, func(m *opendalMetadata) int64 { |
| assertMetadataPointer(t, metadataInner, m) |
| return -1 |
| }) |
| ctx = context.WithValue(ctx, ffiMetaMode.opts.sym, func(m *opendalMetadata) EntryMode { |
| assertMetadataPointer(t, metadataInner, m) |
| return EntryModeUnknown |
| }) |
| ctx = context.WithValue(ctx, ffiMetaVersion.opts.sym, func(m *opendalMetadata) *string { |
| assertMetadataPointer(t, metadataInner, m) |
| return nil |
| }) |
| ctx = context.WithValue(ctx, ffiMetaUserMetadata.opts.sym, func(m *opendalMetadata) map[string]string { |
| assertMetadataPointer(t, metadataInner, m) |
| return nil |
| }) |
| |
| meta := newMetadata(ctx, metadataInner) |
| if _, ok := meta.CacheControl(); ok { |
| t.Fatal("CacheControl() ok = true, want false") |
| } |
| if _, ok := meta.ContentDisposition(); ok { |
| t.Fatal("ContentDisposition() ok = true, want false") |
| } |
| if _, ok := meta.ContentEncoding(); ok { |
| t.Fatal("ContentEncoding() ok = true, want false") |
| } |
| if _, ok := meta.ContentMD5(); ok { |
| t.Fatal("ContentMD5() ok = true, want false") |
| } |
| if _, ok := meta.ContentType(); ok { |
| t.Fatal("ContentType() ok = true, want false") |
| } |
| if _, ok := meta.ETag(); ok { |
| t.Fatal("ETag() ok = true, want false") |
| } |
| if _, ok := meta.Version(); ok { |
| t.Fatal("Version() ok = true, want false") |
| } |
| if current, ok := meta.IsCurrent(); ok || current { |
| t.Fatalf("IsCurrent() = %v, %v, want false, false", current, ok) |
| } |
| if !meta.LastModified().IsZero() { |
| t.Fatalf("LastModified() = %v, want zero", meta.LastModified()) |
| } |
| if meta.UserMetadata() != nil { |
| t.Fatalf("UserMetadata() = %v, want nil", meta.UserMetadata()) |
| } |
| if metadataFreed != 1 { |
| t.Fatalf("metadata freed %d times, want 1", metadataFreed) |
| } |
| } |
| |
| func TestBoolPtrFromOptionalByte(t *testing.T) { |
| if got := boolPtrFromOptionalByte(2); got != nil { |
| t.Fatalf("boolPtrFromOptionalByte(2) = %v, want nil", *got) |
| } |
| for _, tc := range []struct { |
| value uint8 |
| want bool |
| }{ |
| {value: 0, want: false}, |
| {value: 1, want: true}, |
| } { |
| got := boolPtrFromOptionalByte(tc.value) |
| if got == nil || *got != tc.want { |
| t.Fatalf("boolPtrFromOptionalByte(%d) = %v, want %v", tc.value, got, tc.want) |
| } |
| } |
| } |
| |
| func mustBytePtrFromString(t *testing.T, value string) *byte { |
| t.Helper() |
| |
| ptr, err := BytePtrFromString(value) |
| if err != nil { |
| t.Fatalf("BytePtrFromString(%q) failed: %v", value, err) |
| } |
| return ptr |
| } |
| |
| func metadataStringFunc(t *testing.T, want *opendalMetadata, ptr *byte) func(rValue unsafe.Pointer, aValues ...unsafe.Pointer) { |
| t.Helper() |
| return func(rValue unsafe.Pointer, aValues ...unsafe.Pointer) { |
| assertMetadataPointerFromArgs(t, want, aValues...) |
| *(**byte)(rValue) = ptr |
| } |
| } |
| |
| func assertOptionalString(t *testing.T, name string, fn func() (string, bool), want string) { |
| t.Helper() |
| |
| got, ok := fn() |
| if !ok || got != want { |
| t.Fatalf("%s() = %q, %v, want %q, true", name, got, ok, want) |
| } |
| } |
| |
| func assertStringMap(t *testing.T, got, want map[string]string) { |
| t.Helper() |
| |
| if len(got) != len(want) { |
| t.Fatalf("map length = %d, want %d: %v", len(got), len(want), got) |
| } |
| for key, wantValue := range want { |
| if gotValue, ok := got[key]; !ok || gotValue != wantValue { |
| t.Fatalf("map[%q] = %q, %v, want %q, true", key, gotValue, ok, wantValue) |
| } |
| } |
| } |
| |
| func assertOperatorInfoPointer(t *testing.T, want *opendalOperatorInfo, aValues ...unsafe.Pointer) { |
| t.Helper() |
| |
| if len(aValues) != 1 { |
| t.Fatalf("operator info getter received %d arguments, want 1", len(aValues)) |
| } |
| got := *(**opendalOperatorInfo)(aValues[0]) |
| if got != want { |
| t.Fatalf("operator info getter received %p, want %p", got, want) |
| } |
| } |
| |
| func assertEntryPointer(t *testing.T, want *opendalEntry, aValues ...unsafe.Pointer) { |
| t.Helper() |
| |
| if len(aValues) != 1 { |
| t.Fatalf("entry getter received %d arguments, want 1", len(aValues)) |
| } |
| got := *(**opendalEntry)(aValues[0]) |
| if got != want { |
| t.Fatalf("entry getter received %p, want %p", got, want) |
| } |
| } |
| |
| func assertMetadataPointer(t *testing.T, want *opendalMetadata, got *opendalMetadata) { |
| t.Helper() |
| |
| if got != want { |
| t.Fatalf("metadata getter received %p, want %p", got, want) |
| } |
| } |
| |
| func assertMetadataPointerFromArgs(t *testing.T, want *opendalMetadata, aValues ...unsafe.Pointer) { |
| t.Helper() |
| |
| if len(aValues) != 1 { |
| t.Fatalf("metadata getter received %d arguments, want 1", len(aValues)) |
| } |
| got := *(**opendalMetadata)(aValues[0]) |
| assertMetadataPointer(t, want, got) |
| } |
| |
| func assertUserMetadataPointerFromArgs(t *testing.T, want *opendalMetadataUserMetadata, aValues ...unsafe.Pointer) { |
| t.Helper() |
| |
| if len(aValues) != 1 { |
| t.Fatalf("user metadata getter received %d arguments, want 1", len(aValues)) |
| } |
| got := *(**opendalMetadataUserMetadata)(aValues[0]) |
| if got != want { |
| t.Fatalf("user metadata getter received %p, want %p", got, want) |
| } |
| } |
| |
| func assertFreedPointers(t *testing.T, got []*byte, want ...*byte) { |
| t.Helper() |
| |
| if len(got) != len(want) { |
| t.Fatalf("freed %d pointers, want %d", len(got), len(want)) |
| } |
| for i := range want { |
| if got[i] != want[i] { |
| t.Fatalf("freed pointer[%d] = %p, want %p", i, got[i], want[i]) |
| } |
| } |
| } |
| |
| func TestWriteWithOptions(t *testing.T) { |
| o := &writeOptions{} |
| WriteWithAppend(true)(o) |
| WriteWithCacheControl("max-age=60")(o) |
| WriteWithContentType("text/plain")(o) |
| WriteWithContentDisposition("attachment")(o) |
| WriteWithContentEncoding("gzip")(o) |
| WriteWithUserMetadata(map[string]string{"foo": "bar"})(o) |
| WriteWithIfMatch("etag-a")(o) |
| WriteWithIfNoneMatch("etag-b")(o) |
| WriteWithIfNotExists(true)(o) |
| WriteWithConcurrent(4)(o) |
| WriteWithChunk(1024)(o) |
| |
| if !o.append { |
| t.Fatalf("append = false, want true") |
| } |
| if o.cacheControl != "max-age=60" { |
| t.Fatalf("cacheControl = %q, want max-age=60", o.cacheControl) |
| } |
| if o.contentType != "text/plain" { |
| t.Fatalf("contentType = %q, want text/plain", o.contentType) |
| } |
| if o.contentDisposition != "attachment" { |
| t.Fatalf("contentDisposition = %q, want attachment", o.contentDisposition) |
| } |
| if o.contentEncoding != "gzip" { |
| t.Fatalf("contentEncoding = %q, want gzip", o.contentEncoding) |
| } |
| assertStringMap(t, o.userMetadata, map[string]string{"foo": "bar"}) |
| if o.ifMatch != "etag-a" { |
| t.Fatalf("ifMatch = %q, want etag-a", o.ifMatch) |
| } |
| if o.ifNoneMatch != "etag-b" { |
| t.Fatalf("ifNoneMatch = %q, want etag-b", o.ifNoneMatch) |
| } |
| if !o.ifNotExists { |
| t.Fatalf("ifNotExists = false, want true") |
| } |
| if o.concurrent != 4 { |
| t.Fatalf("concurrent = %d, want 4", o.concurrent) |
| } |
| if o.chunk != 1024 { |
| t.Fatalf("chunk = %d, want 1024", o.chunk) |
| } |
| } |
| |
| func TestFfiOperatorWriterWithArgTypes(t *testing.T) { |
| aTypes := ffiOperatorWriterWith.opts.aTypes |
| if len(aTypes) != 3 { |
| t.Fatalf("ffiOperatorWriterWith aTypes len = %d, want 3", len(aTypes)) |
| } |
| for i, at := range aTypes { |
| if at != &ffi.TypePointer { |
| t.Fatalf("ffiOperatorWriterWith aTypes[%d] = %v, want TypePointer", i, at) |
| } |
| } |
| } |
| |
| func TestFfiOperatorWriteWithMetadataSignature(t *testing.T) { |
| if ffiOperatorWriteWithMetadata.opts.rType != &typeResultWrite { |
| t.Fatalf("ffiOperatorWriteWithMetadata rType = %v, want typeResultWrite", ffiOperatorWriteWithMetadata.opts.rType) |
| } |
| aTypes := ffiOperatorWriteWithMetadata.opts.aTypes |
| if len(aTypes) != 4 { |
| t.Fatalf("ffiOperatorWriteWithMetadata aTypes len = %d, want 4", len(aTypes)) |
| } |
| for i, at := range aTypes { |
| if at != &ffi.TypePointer { |
| t.Fatalf("ffiOperatorWriteWithMetadata aTypes[%d] = %v, want TypePointer", i, at) |
| } |
| } |
| } |
| |
| func TestFfiWriterCloseWithMetadataSignature(t *testing.T) { |
| if ffiWriterCloseWithMetadata.opts.rType != &typeResultWrite { |
| t.Fatalf("ffiWriterCloseWithMetadata rType = %v, want typeResultWrite", ffiWriterCloseWithMetadata.opts.rType) |
| } |
| aTypes := ffiWriterCloseWithMetadata.opts.aTypes |
| if len(aTypes) != 1 { |
| t.Fatalf("ffiWriterCloseWithMetadata aTypes len = %d, want 1", len(aTypes)) |
| } |
| if aTypes[0] != &ffi.TypePointer { |
| t.Fatalf("ffiWriterCloseWithMetadata aTypes[0] = %v, want TypePointer", aTypes[0]) |
| } |
| } |
| |
| func TestWriteOptionsSetterArgTypes(t *testing.T) { |
| stringSetters := []*FFI[func(*opendalWriteOptions, string) ([]byte, error)]{ |
| ffiWriteOptionsSetCacheControl, |
| ffiWriteOptionsSetContentType, |
| ffiWriteOptionsSetContentDisposition, |
| ffiWriteOptionsSetContentEncoding, |
| ffiWriteOptionsSetIfMatch, |
| ffiWriteOptionsSetIfNoneMatch, |
| } |
| for _, setter := range stringSetters { |
| aTypes := setter.opts.aTypes |
| if len(aTypes) != 2 { |
| t.Fatalf("%s aTypes len = %d, want 2", setter.opts.sym, len(aTypes)) |
| } |
| for i, at := range aTypes { |
| if at != &ffi.TypePointer { |
| t.Fatalf("%s aTypes[%d] = %v, want TypePointer", setter.opts.sym, i, at) |
| } |
| } |
| } |
| |
| boolSetters := []*FFI[func(*opendalWriteOptions, bool)]{ |
| ffiWriteOptionsSetAppend, |
| ffiWriteOptionsSetIfNotExists, |
| } |
| for _, setter := range boolSetters { |
| aTypes := setter.opts.aTypes |
| if len(aTypes) != 2 { |
| t.Fatalf("%s aTypes len = %d, want 2", setter.opts.sym, len(aTypes)) |
| } |
| if aTypes[0] != &ffi.TypePointer || aTypes[1] != &ffi.TypeUint8 { |
| t.Fatalf("%s aTypes = %v, want TypePointer, TypeUint8", setter.opts.sym, aTypes) |
| } |
| } |
| |
| uintSetters := []*FFI[func(*opendalWriteOptions, uint)]{ |
| ffiWriteOptionsSetConcurrent, |
| ffiWriteOptionsSetChunk, |
| } |
| for _, setter := range uintSetters { |
| aTypes := setter.opts.aTypes |
| if len(aTypes) != 2 { |
| t.Fatalf("%s aTypes len = %d, want 2", setter.opts.sym, len(aTypes)) |
| } |
| for i, at := range aTypes { |
| if at != &ffi.TypePointer { |
| t.Fatalf("%s aTypes[%d] = %v, want TypePointer", setter.opts.sym, i, at) |
| } |
| } |
| } |
| } |
| |
| func TestWriteOptionsSetUserMetadataArgTypes(t *testing.T) { |
| aTypes := ffiWriteOptionsSetUserMetadata.opts.aTypes |
| if len(aTypes) != 3 { |
| t.Fatalf("ffiWriteOptionsSetUserMetadata aTypes len = %d, want 3", len(aTypes)) |
| } |
| for i, at := range aTypes { |
| if at != &ffi.TypePointer { |
| t.Fatalf("ffiWriteOptionsSetUserMetadata aTypes[%d] = %v, want TypePointer", i, at) |
| } |
| } |
| } |
| |
| func TestListWithRecursiveDefaultNotRecursive(t *testing.T) { |
| o := &listOptions{} |
| if o.recursive { |
| t.Fatalf("default listOptions.recursive = true, want false") |
| } |
| } |
| |
| func TestListWithRecursiveTrue(t *testing.T) { |
| o := &listOptions{} |
| ListWithRecursive(true)(o) |
| if !o.recursive { |
| t.Fatalf("ListWithRecursive(true): recursive = false, want true") |
| } |
| } |
| |
| func TestListWithRecursiveFalse(t *testing.T) { |
| o := &listOptions{} |
| ListWithRecursive(true)(o) |
| ListWithRecursive(false)(o) |
| if o.recursive { |
| t.Fatalf("ListWithRecursive(false): recursive = true, want false") |
| } |
| } |
| |
| func TestListWithLimitSetsValue(t *testing.T) { |
| o := &listOptions{} |
| ListWithLimit(100)(o) |
| if o.limit != 100 { |
| t.Fatalf("ListWithLimit(100): limit = %d, want 100", o.limit) |
| } |
| } |
| |
| func TestListWithLimitOverwrite(t *testing.T) { |
| o := &listOptions{} |
| ListWithLimit(50)(o) |
| ListWithLimit(200)(o) |
| if o.limit != 200 { |
| t.Fatalf("ListWithLimit overwrite: limit = %d, want 200", o.limit) |
| } |
| } |
| |
| func TestListWithStartAfterSetsValue(t *testing.T) { |
| o := &listOptions{} |
| ListWithStartAfter("some/key")(o) |
| if o.startAfter == nil { |
| t.Fatal("ListWithStartAfter: startAfter = nil, want non-nil") |
| } |
| if *o.startAfter != "some/key" { |
| t.Fatalf("ListWithStartAfter: startAfter = %q, want some/key", *o.startAfter) |
| } |
| } |
| |
| func TestListWithStartAfterEmptyString(t *testing.T) { |
| o := &listOptions{} |
| ListWithStartAfter("")(o) |
| if o.startAfter == nil { |
| t.Fatal("ListWithStartAfter(empty): startAfter = nil, want non-nil pointer to empty string") |
| } |
| } |
| |
| func TestListWithVersionsTrue(t *testing.T) { |
| o := &listOptions{} |
| ListWithVersions(true)(o) |
| if !o.versions { |
| t.Fatalf("ListWithVersions(true): versions = false, want true") |
| } |
| } |
| |
| func TestListWithVersionsFalse(t *testing.T) { |
| o := &listOptions{} |
| ListWithVersions(true)(o) |
| ListWithVersions(false)(o) |
| if o.versions { |
| t.Fatalf("ListWithVersions(false): versions = true, want false") |
| } |
| } |
| |
| func TestListWithDeletedTrue(t *testing.T) { |
| o := &listOptions{} |
| ListWithDeleted(true)(o) |
| if !o.deleted { |
| t.Fatalf("ListWithDeleted(true): deleted = false, want true") |
| } |
| } |
| |
| func TestListWithDeletedFalse(t *testing.T) { |
| o := &listOptions{} |
| ListWithDeleted(true)(o) |
| ListWithDeleted(false)(o) |
| if o.deleted { |
| t.Fatalf("ListWithDeleted(false): deleted = true, want false") |
| } |
| } |
| |
| func TestFfiOperatorListWithReturnType(t *testing.T) { |
| if ffiOperatorListWith.opts.rType != &typeResultList { |
| t.Fatalf("ffiOperatorListWith rType = %v, want typeResultList", ffiOperatorListWith.opts.rType) |
| } |
| } |
| |
| func TestFfiOperatorListWithArgTypes(t *testing.T) { |
| aTypes := ffiOperatorListWith.opts.aTypes |
| if len(aTypes) != 3 { |
| t.Fatalf("ffiOperatorListWith aTypes len = %d, want 3", len(aTypes)) |
| } |
| for i, at := range aTypes { |
| if at != &ffi.TypePointer { |
| t.Fatalf("ffiOperatorListWith aTypes[%d] = %v, want TypePointer", i, at) |
| } |
| } |
| } |
| |
| func TestReadWithOptions(t *testing.T) { |
| modified := time.Unix(1700000000, 0) |
| unmodified := time.Unix(1700000123, 0) |
| |
| o := &readOptions{} |
| ReadWithRange(1024, 2048)(o) |
| ReadWithVersion("v1")(o) |
| ReadWithIfMatch("etag-a")(o) |
| ReadWithIfNoneMatch("etag-b")(o) |
| ReadWithIfModifiedSince(modified)(o) |
| ReadWithIfUnmodifiedSince(unmodified)(o) |
| ReadWithConcurrent(4)(o) |
| ReadWithChunk(1024)(o) |
| ReadWithGap(512)(o) |
| ReadWithContentLengthHint(4096)(o) |
| ReadWithOverrideContentType("text/plain")(o) |
| ReadWithOverrideCacheControl("max-age=60")(o) |
| ReadWithOverrideContentDisposition("attachment")(o) |
| |
| if !o.hasRange { |
| t.Fatalf("hasRange = false, want true") |
| } |
| if !o.hasRangeLength { |
| t.Fatalf("hasRangeLength = false, want true") |
| } |
| if o.rangeOffset != 1024 { |
| t.Fatalf("rangeOffset = %d, want 1024", o.rangeOffset) |
| } |
| if o.rangeLength != 2048 { |
| t.Fatalf("rangeLength = %d, want 2048", o.rangeLength) |
| } |
| if o.version != "v1" { |
| t.Fatalf("version = %q, want v1", o.version) |
| } |
| if o.ifMatch != "etag-a" { |
| t.Fatalf("ifMatch = %q, want etag-a", o.ifMatch) |
| } |
| if o.ifNoneMatch != "etag-b" { |
| t.Fatalf("ifNoneMatch = %q, want etag-b", o.ifNoneMatch) |
| } |
| if o.ifModifiedSince == nil || *o.ifModifiedSince != modified.UnixMilli() { |
| t.Fatalf("ifModifiedSince = %v, want %d", o.ifModifiedSince, modified.UnixMilli()) |
| } |
| if o.ifUnmodifiedSince == nil || *o.ifUnmodifiedSince != unmodified.UnixMilli() { |
| t.Fatalf("ifUnmodifiedSince = %v, want %d", o.ifUnmodifiedSince, unmodified.UnixMilli()) |
| } |
| if o.concurrent != 4 { |
| t.Fatalf("concurrent = %d, want 4", o.concurrent) |
| } |
| if o.chunk != 1024 { |
| t.Fatalf("chunk = %d, want 1024", o.chunk) |
| } |
| if o.gap != 512 { |
| t.Fatalf("gap = %d, want 512", o.gap) |
| } |
| if o.contentLengthHint == nil || *o.contentLengthHint != 4096 { |
| t.Fatalf("contentLengthHint = %v, want 4096", o.contentLengthHint) |
| } |
| if o.overrideContentType != "text/plain" { |
| t.Fatalf("overrideContentType = %q, want text/plain", o.overrideContentType) |
| } |
| if o.overrideCacheControl != "max-age=60" { |
| t.Fatalf("overrideCacheControl = %q, want max-age=60", o.overrideCacheControl) |
| } |
| if o.overrideContentDisposition != "attachment" { |
| t.Fatalf("overrideContentDisposition = %q, want attachment", o.overrideContentDisposition) |
| } |
| } |
| |
| func TestReadWithRangeFrom(t *testing.T) { |
| o := &readOptions{} |
| ReadWithRangeFrom(1024)(o) |
| |
| if !o.hasRange { |
| t.Fatalf("hasRange = false, want true") |
| } |
| if o.hasRangeLength { |
| t.Fatalf("hasRangeLength = true, want false") |
| } |
| if o.rangeOffset != 1024 { |
| t.Fatalf("rangeOffset = %d, want 1024", o.rangeOffset) |
| } |
| |
| // ReadWithRangeFrom overrides a previously set bounded range. |
| o = &readOptions{} |
| ReadWithRange(0, 2048)(o) |
| ReadWithRangeFrom(512)(o) |
| if o.hasRangeLength || o.rangeOffset != 512 || o.rangeLength != 0 { |
| t.Fatalf("after override: hasRangeLength=%v offset=%d length=%d, want false 512 0", |
| o.hasRangeLength, o.rangeOffset, o.rangeLength) |
| } |
| } |
| |
| func TestFfiOperatorReadWithReturnType(t *testing.T) { |
| if ffiOperatorReadWith.opts.rType != &typeResultRead { |
| t.Fatalf("ffiOperatorReadWith rType = %v, want typeResultRead", ffiOperatorReadWith.opts.rType) |
| } |
| } |
| |
| func TestFfiOperatorReadWithArgTypes(t *testing.T) { |
| aTypes := ffiOperatorReadWith.opts.aTypes |
| if len(aTypes) != 3 { |
| t.Fatalf("ffiOperatorReadWith aTypes len = %d, want 3", len(aTypes)) |
| } |
| for i, at := range aTypes { |
| if at != &ffi.TypePointer { |
| t.Fatalf("ffiOperatorReadWith aTypes[%d] = %v, want TypePointer", i, at) |
| } |
| } |
| } |
| |
| func TestReadOptionsSetterArgTypes(t *testing.T) { |
| stringSetters := []*FFI[func(*opendalReadOptions, string) ([]byte, error)]{ |
| ffiReadOptionsSetVersion, |
| ffiReadOptionsSetIfMatch, |
| ffiReadOptionsSetIfNoneMatch, |
| ffiReadOptionsSetOverrideContentType, |
| ffiReadOptionsSetOverrideCacheControl, |
| ffiReadOptionsSetOverrideContentDisposition, |
| } |
| for _, setter := range stringSetters { |
| aTypes := setter.opts.aTypes |
| if len(aTypes) != 2 { |
| t.Fatalf("%s aTypes len = %d, want 2", setter.opts.sym, len(aTypes)) |
| } |
| for i, at := range aTypes { |
| if at != &ffi.TypePointer { |
| t.Fatalf("%s aTypes[%d] = %v, want TypePointer", setter.opts.sym, i, at) |
| } |
| } |
| } |
| |
| int64Setters := []*FFI[func(*opendalReadOptions, int64)]{ |
| ffiReadOptionsSetIfModifiedSince, |
| ffiReadOptionsSetIfUnmodifiedSince, |
| } |
| for _, setter := range int64Setters { |
| aTypes := setter.opts.aTypes |
| if len(aTypes) != 2 { |
| t.Fatalf("%s aTypes len = %d, want 2", setter.opts.sym, len(aTypes)) |
| } |
| if aTypes[0] != &ffi.TypePointer || aTypes[1] != &ffi.TypeSint64 { |
| t.Fatalf("%s aTypes = %v, want TypePointer, TypeSint64", setter.opts.sym, aTypes) |
| } |
| } |
| |
| uintSetters := []*FFI[func(*opendalReadOptions, uint)]{ |
| ffiReadOptionsSetConcurrent, |
| ffiReadOptionsSetChunk, |
| ffiReadOptionsSetGap, |
| } |
| for _, setter := range uintSetters { |
| aTypes := setter.opts.aTypes |
| if len(aTypes) != 2 { |
| t.Fatalf("%s aTypes len = %d, want 2", setter.opts.sym, len(aTypes)) |
| } |
| for i, at := range aTypes { |
| if at != &ffi.TypePointer { |
| t.Fatalf("%s aTypes[%d] = %v, want TypePointer", setter.opts.sym, i, at) |
| } |
| } |
| } |
| |
| hintATypes := ffiReadOptionsSetContentLengthHint.opts.aTypes |
| if len(hintATypes) != 2 { |
| t.Fatalf("ffiReadOptionsSetContentLengthHint aTypes len = %d, want 2", len(hintATypes)) |
| } |
| if hintATypes[0] != &ffi.TypePointer || hintATypes[1] != &ffi.TypeUint64 { |
| t.Fatalf("ffiReadOptionsSetContentLengthHint aTypes = %v, want TypePointer, TypeUint64", hintATypes) |
| } |
| |
| rangeATypes := ffiReadOptionsSetRange.opts.aTypes |
| if len(rangeATypes) != 3 { |
| t.Fatalf("ffiReadOptionsSetRange aTypes len = %d, want 3", len(rangeATypes)) |
| } |
| if rangeATypes[0] != &ffi.TypePointer || rangeATypes[1] != &ffi.TypeUint64 || rangeATypes[2] != &ffi.TypeUint64 { |
| t.Fatalf("ffiReadOptionsSetRange aTypes = %v, want TypePointer, TypeUint64, TypeUint64", rangeATypes) |
| } |
| |
| rangeFromATypes := ffiReadOptionsSetRangeFrom.opts.aTypes |
| if len(rangeFromATypes) != 2 { |
| t.Fatalf("ffiReadOptionsSetRangeFrom aTypes len = %d, want 2", len(rangeFromATypes)) |
| } |
| if rangeFromATypes[0] != &ffi.TypePointer || rangeFromATypes[1] != &ffi.TypeUint64 { |
| t.Fatalf("ffiReadOptionsSetRangeFrom aTypes = %v, want TypePointer, TypeUint64", rangeFromATypes) |
| } |
| } |
| |
| func TestReaderWithOptions(t *testing.T) { |
| modified := time.Unix(1700000000, 0) |
| unmodified := time.Unix(1700000123, 0) |
| |
| o := &readerOptions{} |
| ReaderWithVersion("v1")(o) |
| ReaderWithIfMatch("etag-a")(o) |
| ReaderWithIfNoneMatch("etag-b")(o) |
| ReaderWithIfModifiedSince(modified)(o) |
| ReaderWithIfUnmodifiedSince(unmodified)(o) |
| ReaderWithContentLengthHint(4096)(o) |
| ReaderWithConcurrent(4)(o) |
| ReaderWithChunk(1024)(o) |
| ReaderWithGap(512)(o) |
| ReaderWithPrefetch(8)(o) |
| |
| if o.version != "v1" { |
| t.Fatalf("version = %q, want v1", o.version) |
| } |
| if o.ifMatch != "etag-a" { |
| t.Fatalf("ifMatch = %q, want etag-a", o.ifMatch) |
| } |
| if o.ifNoneMatch != "etag-b" { |
| t.Fatalf("ifNoneMatch = %q, want etag-b", o.ifNoneMatch) |
| } |
| if o.ifModifiedSince == nil || *o.ifModifiedSince != modified.UnixMilli() { |
| t.Fatalf("ifModifiedSince = %v, want %d", o.ifModifiedSince, modified.UnixMilli()) |
| } |
| if o.ifUnmodifiedSince == nil || *o.ifUnmodifiedSince != unmodified.UnixMilli() { |
| t.Fatalf("ifUnmodifiedSince = %v, want %d", o.ifUnmodifiedSince, unmodified.UnixMilli()) |
| } |
| if o.contentLengthHint == nil || *o.contentLengthHint != 4096 { |
| t.Fatalf("contentLengthHint = %v, want 4096", o.contentLengthHint) |
| } |
| if o.concurrent != 4 { |
| t.Fatalf("concurrent = %d, want 4", o.concurrent) |
| } |
| if o.chunk != 1024 { |
| t.Fatalf("chunk = %d, want 1024", o.chunk) |
| } |
| if o.gap != 512 { |
| t.Fatalf("gap = %d, want 512", o.gap) |
| } |
| if o.prefetch != 8 { |
| t.Fatalf("prefetch = %d, want 8", o.prefetch) |
| } |
| } |
| |
| func TestFfiOperatorReaderWithReturnType(t *testing.T) { |
| if ffiOperatorReaderWith.opts.rType != &typeResultOperatorReader { |
| t.Fatalf("ffiOperatorReaderWith rType = %v, want typeResultOperatorReader", ffiOperatorReaderWith.opts.rType) |
| } |
| } |
| |
| func TestFfiOperatorReaderWithArgTypes(t *testing.T) { |
| aTypes := ffiOperatorReaderWith.opts.aTypes |
| if len(aTypes) != 3 { |
| t.Fatalf("ffiOperatorReaderWith aTypes len = %d, want 3", len(aTypes)) |
| } |
| for i, at := range aTypes { |
| if at != &ffi.TypePointer { |
| t.Fatalf("ffiOperatorReaderWith aTypes[%d] = %v, want TypePointer", i, at) |
| } |
| } |
| } |
| |
| func TestReaderOptionsSetterArgTypes(t *testing.T) { |
| stringSetters := []*FFI[func(*opendalReaderOptions, string) ([]byte, error)]{ |
| ffiReaderOptionsSetVersion, |
| ffiReaderOptionsSetIfMatch, |
| ffiReaderOptionsSetIfNoneMatch, |
| } |
| for _, setter := range stringSetters { |
| aTypes := setter.opts.aTypes |
| if len(aTypes) != 2 { |
| t.Fatalf("%s aTypes len = %d, want 2", setter.opts.sym, len(aTypes)) |
| } |
| for i, at := range aTypes { |
| if at != &ffi.TypePointer { |
| t.Fatalf("%s aTypes[%d] = %v, want TypePointer", setter.opts.sym, i, at) |
| } |
| } |
| } |
| |
| int64Setters := []*FFI[func(*opendalReaderOptions, int64)]{ |
| ffiReaderOptionsSetIfModifiedSince, |
| ffiReaderOptionsSetIfUnmodifiedSince, |
| } |
| for _, setter := range int64Setters { |
| aTypes := setter.opts.aTypes |
| if len(aTypes) != 2 { |
| t.Fatalf("%s aTypes len = %d, want 2", setter.opts.sym, len(aTypes)) |
| } |
| if aTypes[0] != &ffi.TypePointer || aTypes[1] != &ffi.TypeSint64 { |
| t.Fatalf("%s aTypes = %v, want TypePointer, TypeSint64", setter.opts.sym, aTypes) |
| } |
| } |
| |
| uintSetters := []*FFI[func(*opendalReaderOptions, uint)]{ |
| ffiReaderOptionsSetConcurrent, |
| ffiReaderOptionsSetChunk, |
| ffiReaderOptionsSetGap, |
| ffiReaderOptionsSetPrefetch, |
| } |
| for _, setter := range uintSetters { |
| aTypes := setter.opts.aTypes |
| if len(aTypes) != 2 { |
| t.Fatalf("%s aTypes len = %d, want 2", setter.opts.sym, len(aTypes)) |
| } |
| for i, at := range aTypes { |
| if at != &ffi.TypePointer { |
| t.Fatalf("%s aTypes[%d] = %v, want TypePointer", setter.opts.sym, i, at) |
| } |
| } |
| } |
| |
| hintATypes := ffiReaderOptionsSetContentLengthHint.opts.aTypes |
| if len(hintATypes) != 2 { |
| t.Fatalf("ffiReaderOptionsSetContentLengthHint aTypes len = %d, want 2", len(hintATypes)) |
| } |
| if hintATypes[0] != &ffi.TypePointer || hintATypes[1] != &ffi.TypeUint64 { |
| t.Fatalf("ffiReaderOptionsSetContentLengthHint aTypes = %v, want TypePointer, TypeUint64", hintATypes) |
| } |
| } |
| |
| func TestFfiOperatorPresignWithSignatures(t *testing.T) { |
| for _, tc := range []struct { |
| name string |
| opts ffiOpts |
| }{ |
| { |
| name: "ffiOperatorPresignReadWith", |
| opts: ffiOperatorPresignReadWith.opts, |
| }, |
| { |
| name: "ffiOperatorPresignWriteWith", |
| opts: ffiOperatorPresignWriteWith.opts, |
| }, |
| { |
| name: "ffiOperatorPresignDeleteWith", |
| opts: ffiOperatorPresignDeleteWith.opts, |
| }, |
| { |
| name: "ffiOperatorPresignStatWith", |
| opts: ffiOperatorPresignStatWith.opts, |
| }, |
| } { |
| if tc.opts.rType != &typeResultPresign { |
| t.Fatalf("%s rType = %v, want typeResultPresign", tc.name, tc.opts.rType) |
| } |
| if len(tc.opts.aTypes) != 4 { |
| t.Fatalf("%s aTypes len = %d, want 4", tc.name, len(tc.opts.aTypes)) |
| } |
| if tc.opts.aTypes[0] != &ffi.TypePointer || |
| tc.opts.aTypes[1] != &ffi.TypePointer || |
| tc.opts.aTypes[2] != &ffi.TypeUint64 || |
| tc.opts.aTypes[3] != &ffi.TypePointer { |
| t.Fatalf("%s aTypes = %v, want pointer, pointer, uint64, pointer", tc.name, tc.opts.aTypes) |
| } |
| } |
| } |
| |
| func TestPresignWithoutOptionsUsesSimpleSymbols(t *testing.T) { |
| ctx, reqInner, freeCount := presignTestContext(t, "GET", "https://example.com/simple") |
| opInner := &opendalOperator{} |
| op := &Operator{ctx: ctx, inner: opInner} |
| |
| ctx = context.WithValue(ctx, ffiOperatorPresignRead.opts.sym, func(op *opendalOperator, path string, expire uint64) (*opendalPresignedRequest, error) { |
| assertPresignCall(t, opInner, op, "file.txt", path, uint64(60), expire) |
| return reqInner, nil |
| }) |
| ctx = context.WithValue(ctx, ffiOperatorPresignWrite.opts.sym, func(op *opendalOperator, path string, expire uint64) (*opendalPresignedRequest, error) { |
| assertPresignCall(t, opInner, op, "file.txt", path, uint64(60), expire) |
| return reqInner, nil |
| }) |
| ctx = context.WithValue(ctx, ffiOperatorPresignDelete.opts.sym, func(op *opendalOperator, path string, expire uint64) (*opendalPresignedRequest, error) { |
| assertPresignCall(t, opInner, op, "file.txt", path, uint64(60), expire) |
| return reqInner, nil |
| }) |
| ctx = context.WithValue(ctx, ffiOperatorPresignStat.opts.sym, func(op *opendalOperator, path string, expire uint64) (*opendalPresignedRequest, error) { |
| assertPresignCall(t, opInner, op, "file.txt", path, uint64(60), expire) |
| return reqInner, nil |
| }) |
| op.ctx = ctx |
| |
| for name, call := range map[string]func() (*http.Request, error){ |
| "read": func() (*http.Request, error) { return op.PresignRead("file.txt", time.Minute) }, |
| "write": func() (*http.Request, error) { return op.PresignWrite("file.txt", time.Minute) }, |
| "delete": func() (*http.Request, error) { return op.PresignDelete("file.txt", time.Minute) }, |
| "stat": func() (*http.Request, error) { return op.PresignStat("file.txt", time.Minute) }, |
| } { |
| req, err := call() |
| if err != nil { |
| t.Fatalf("Presign%s() failed: %v", name, err) |
| } |
| if req.Method != "GET" || req.URL.String() != "https://example.com/simple" { |
| t.Fatalf("Presign%s() request = %s %s", name, req.Method, req.URL.String()) |
| } |
| } |
| if *freeCount != 4 { |
| t.Fatalf("presigned request freed %d times, want 4", *freeCount) |
| } |
| } |
| |
| func TestPresignReadWithOptionsKeepsStringsUntilCall(t *testing.T) { |
| ctx, reqInner, _ := presignTestContext(t, "GET", "https://example.com/read") |
| opInner := &opendalOperator{} |
| cOpts := &opendalReadOptions{} |
| var versionPtr *byte |
| var overrideContentTypePtr *byte |
| var rangeOffset, rangeLength uint64 |
| |
| ctx = context.WithValue(ctx, ffiReadOptionsNew.opts.sym, func() *opendalReadOptions { return cOpts }) |
| ctx = context.WithValue(ctx, ffiReadOptionsFree.opts.sym, func(opts *opendalReadOptions) { |
| assertReadOptionsPointer(t, cOpts, opts) |
| }) |
| ctx = context.WithValue(ctx, ffiReadOptionsSetRange.opts.sym, func(opts *opendalReadOptions, offset, length uint64) { |
| assertReadOptionsPointer(t, cOpts, opts) |
| rangeOffset = offset |
| rangeLength = length |
| }) |
| ctx = context.WithValue(ctx, ffiReadOptionsSetVersion.opts.sym, ffiReadOptionsSetVersion.withFunc(ctx, func(_ unsafe.Pointer, aValues ...unsafe.Pointer) { |
| assertReadOptionsPointerFromArgs(t, cOpts, aValues...) |
| versionPtr = *(**byte)(aValues[1]) |
| })) |
| ctx = context.WithValue(ctx, ffiReadOptionsSetIfMatch.opts.sym, noopReadOptionsSetString) |
| ctx = context.WithValue(ctx, ffiReadOptionsSetIfNoneMatch.opts.sym, noopReadOptionsSetString) |
| ctx = context.WithValue(ctx, ffiReadOptionsSetOverrideContentType.opts.sym, ffiReadOptionsSetOverrideContentType.withFunc(ctx, func(_ unsafe.Pointer, aValues ...unsafe.Pointer) { |
| assertReadOptionsPointerFromArgs(t, cOpts, aValues...) |
| overrideContentTypePtr = *(**byte)(aValues[1]) |
| })) |
| ctx = context.WithValue(ctx, ffiReadOptionsSetOverrideCacheControl.opts.sym, noopReadOptionsSetString) |
| ctx = context.WithValue(ctx, ffiReadOptionsSetOverrideContentDisposition.opts.sym, noopReadOptionsSetString) |
| ctx = context.WithValue(ctx, ffiOperatorPresignReadWith.opts.sym, func(op *opendalOperator, path string, expire uint64, opts *opendalReadOptions) (*opendalPresignedRequest, error) { |
| assertPresignCall(t, opInner, op, "file.txt", path, uint64(60), expire) |
| assertReadOptionsPointer(t, cOpts, opts) |
| if rangeOffset != 1 || rangeLength != 2 { |
| t.Fatalf("read range = %d, %d, want 1, 2", rangeOffset, rangeLength) |
| } |
| if got := BytePtrToString(versionPtr); got != "v1" { |
| t.Fatalf("read version pointer = %q, want v1", got) |
| } |
| if got := BytePtrToString(overrideContentTypePtr); got != "text/plain" { |
| t.Fatalf("read override content type pointer = %q, want text/plain", got) |
| } |
| return reqInner, nil |
| }) |
| |
| op := &Operator{ctx: ctx, inner: opInner} |
| _, err := op.PresignRead("file.txt", time.Minute, |
| ReadWithRange(1, 2), |
| ReadWithVersion("v1"), |
| ReadWithOverrideContentType("text/plain"), |
| ) |
| if err != nil { |
| t.Fatalf("PresignRead with options failed: %v", err) |
| } |
| } |
| |
| func TestPresignWriteWithOptionsKeepsStringsUntilCall(t *testing.T) { |
| ctx, reqInner, _ := presignTestContext(t, "PUT", "https://example.com/write") |
| opInner := &opendalOperator{} |
| cOpts := &opendalWriteOptions{} |
| var contentTypePtr *byte |
| var userMetadata []opendalWriteUserMetadataPair |
| |
| ctx = context.WithValue(ctx, ffiWriteOptionsNew.opts.sym, func() *opendalWriteOptions { return cOpts }) |
| ctx = context.WithValue(ctx, ffiWriteOptionsFree.opts.sym, func(opts *opendalWriteOptions) { |
| assertWriteOptionsPointer(t, cOpts, opts) |
| }) |
| ctx = context.WithValue(ctx, ffiWriteOptionsSetAppend.opts.sym, func(opts *opendalWriteOptions, append bool) { |
| assertWriteOptionsPointer(t, cOpts, opts) |
| }) |
| ctx = context.WithValue(ctx, ffiWriteOptionsSetIfNotExists.opts.sym, func(opts *opendalWriteOptions, ifNotExists bool) { |
| assertWriteOptionsPointer(t, cOpts, opts) |
| }) |
| ctx = context.WithValue(ctx, ffiWriteOptionsSetContentType.opts.sym, ffiWriteOptionsSetContentType.withFunc(ctx, func(_ unsafe.Pointer, aValues ...unsafe.Pointer) { |
| assertWriteOptionsPointerFromArgs(t, cOpts, aValues...) |
| contentTypePtr = *(**byte)(aValues[1]) |
| })) |
| ctx = context.WithValue(ctx, ffiWriteOptionsSetCacheControl.opts.sym, noopWriteOptionsSetString) |
| ctx = context.WithValue(ctx, ffiWriteOptionsSetContentDisposition.opts.sym, noopWriteOptionsSetString) |
| ctx = context.WithValue(ctx, ffiWriteOptionsSetContentEncoding.opts.sym, noopWriteOptionsSetString) |
| ctx = context.WithValue(ctx, ffiWriteOptionsSetIfMatch.opts.sym, noopWriteOptionsSetString) |
| ctx = context.WithValue(ctx, ffiWriteOptionsSetIfNoneMatch.opts.sym, noopWriteOptionsSetString) |
| ctx = context.WithValue(ctx, ffiWriteOptionsSetUserMetadata.opts.sym, func(opts *opendalWriteOptions, pairs []opendalWriteUserMetadataPair) { |
| assertWriteOptionsPointer(t, cOpts, opts) |
| userMetadata = pairs |
| }) |
| ctx = context.WithValue(ctx, ffiOperatorPresignWriteWith.opts.sym, func(op *opendalOperator, path string, expire uint64, opts *opendalWriteOptions) (*opendalPresignedRequest, error) { |
| assertPresignCall(t, opInner, op, "file.txt", path, uint64(60), expire) |
| assertWriteOptionsPointer(t, cOpts, opts) |
| if got := BytePtrToString(contentTypePtr); got != "text/plain" { |
| t.Fatalf("write content type pointer = %q, want text/plain", got) |
| } |
| if len(userMetadata) != 1 { |
| t.Fatalf("write user metadata len = %d, want 1", len(userMetadata)) |
| } |
| if got := BytePtrToString(userMetadata[0].key); got != "foo" { |
| t.Fatalf("write user metadata key = %q, want foo", got) |
| } |
| if got := BytePtrToString(userMetadata[0].value); got != "bar" { |
| t.Fatalf("write user metadata value = %q, want bar", got) |
| } |
| return reqInner, nil |
| }) |
| |
| op := &Operator{ctx: ctx, inner: opInner} |
| _, err := op.PresignWrite("file.txt", time.Minute, |
| WriteWithContentType("text/plain"), |
| WriteWithUserMetadata(map[string]string{"foo": "bar"}), |
| ) |
| if err != nil { |
| t.Fatalf("PresignWrite with options failed: %v", err) |
| } |
| } |
| |
| func TestPresignStatWithOptionsKeepsStringsUntilCall(t *testing.T) { |
| ctx, reqInner, _ := presignTestContext(t, "HEAD", "https://example.com/stat") |
| opInner := &opendalOperator{} |
| cOpts := &opendalStatOptions{} |
| var versionPtr *byte |
| var overrideContentTypePtr *byte |
| |
| ctx = context.WithValue(ctx, ffiStatOptionsNew.opts.sym, func() *opendalStatOptions { return cOpts }) |
| ctx = context.WithValue(ctx, ffiStatOptionsFree.opts.sym, func(opts *opendalStatOptions) { |
| assertStatOptionsPointer(t, cOpts, opts) |
| }) |
| ctx = context.WithValue(ctx, ffiStatOptionsSetVersion.opts.sym, ffiStatOptionsSetVersion.withFunc(ctx, func(_ unsafe.Pointer, aValues ...unsafe.Pointer) { |
| assertStatOptionsPointerFromArgs(t, cOpts, aValues...) |
| versionPtr = *(**byte)(aValues[1]) |
| })) |
| ctx = context.WithValue(ctx, ffiStatOptionsSetIfMatch.opts.sym, noopStatOptionsSetString) |
| ctx = context.WithValue(ctx, ffiStatOptionsSetIfNoneMatch.opts.sym, noopStatOptionsSetString) |
| ctx = context.WithValue(ctx, ffiStatOptionsSetOverrideContentType.opts.sym, ffiStatOptionsSetOverrideContentType.withFunc(ctx, func(_ unsafe.Pointer, aValues ...unsafe.Pointer) { |
| assertStatOptionsPointerFromArgs(t, cOpts, aValues...) |
| overrideContentTypePtr = *(**byte)(aValues[1]) |
| })) |
| ctx = context.WithValue(ctx, ffiStatOptionsSetOverrideCacheControl.opts.sym, noopStatOptionsSetString) |
| ctx = context.WithValue(ctx, ffiStatOptionsSetOverrideContentDisposition.opts.sym, noopStatOptionsSetString) |
| ctx = context.WithValue(ctx, ffiOperatorPresignStatWith.opts.sym, func(op *opendalOperator, path string, expire uint64, opts *opendalStatOptions) (*opendalPresignedRequest, error) { |
| assertPresignCall(t, opInner, op, "file.txt", path, uint64(60), expire) |
| assertStatOptionsPointer(t, cOpts, opts) |
| if got := BytePtrToString(versionPtr); got != "v1" { |
| t.Fatalf("stat version pointer = %q, want v1", got) |
| } |
| if got := BytePtrToString(overrideContentTypePtr); got != "text/plain" { |
| t.Fatalf("stat override content type pointer = %q, want text/plain", got) |
| } |
| return reqInner, nil |
| }) |
| |
| op := &Operator{ctx: ctx, inner: opInner} |
| _, err := op.PresignStat("file.txt", time.Minute, |
| StatWithVersion("v1"), |
| StatWithOverrideContentType("text/plain"), |
| ) |
| if err != nil { |
| t.Fatalf("PresignStat with options failed: %v", err) |
| } |
| } |
| |
| func TestPresignDeleteWithOptionsKeepsStringsUntilCall(t *testing.T) { |
| ctx, reqInner, _ := presignTestContext(t, "DELETE", "https://example.com/delete") |
| opInner := &opendalOperator{} |
| cOpts := &opendalDeleteOptions{} |
| var versionPtr *byte |
| var recursive bool |
| |
| ctx = context.WithValue(ctx, ffiDeleteOptionsNew.opts.sym, func() *opendalDeleteOptions { return cOpts }) |
| ctx = context.WithValue(ctx, ffiDeleteOptionsFree.opts.sym, func(opts *opendalDeleteOptions) { |
| assertDeleteOptionsPointer(t, cOpts, opts) |
| }) |
| ctx = context.WithValue(ctx, ffiDeleteOptionsSetRecursive.opts.sym, func(opts *opendalDeleteOptions, value bool) { |
| assertDeleteOptionsPointer(t, cOpts, opts) |
| recursive = value |
| }) |
| ctx = context.WithValue(ctx, ffiDeleteOptionsSetVersion.opts.sym, ffiDeleteOptionsSetVersion.withFunc(ctx, func(_ unsafe.Pointer, aValues ...unsafe.Pointer) { |
| assertDeleteOptionsPointerFromArgs(t, cOpts, aValues...) |
| versionPtr = *(**byte)(aValues[1]) |
| })) |
| ctx = context.WithValue(ctx, ffiOperatorPresignDeleteWith.opts.sym, func(op *opendalOperator, path string, expire uint64, opts *opendalDeleteOptions) (*opendalPresignedRequest, error) { |
| assertPresignCall(t, opInner, op, "file.txt", path, uint64(60), expire) |
| assertDeleteOptionsPointer(t, cOpts, opts) |
| if !recursive { |
| t.Fatal("delete recursive = false, want true") |
| } |
| if got := BytePtrToString(versionPtr); got != "v1" { |
| t.Fatalf("delete version pointer = %q, want v1", got) |
| } |
| return reqInner, nil |
| }) |
| |
| op := &Operator{ctx: ctx, inner: opInner} |
| _, err := op.PresignDelete("file.txt", time.Minute, |
| DeleteWithVersion("v1"), |
| DeleteWithRecursive(true), |
| ) |
| if err != nil { |
| t.Fatalf("PresignDelete with options failed: %v", err) |
| } |
| } |
| |
| func presignTestContext(t *testing.T, method, uri string) (context.Context, *opendalPresignedRequest, *int) { |
| t.Helper() |
| |
| methodData, err := byteSliceFromString(method) |
| if err != nil { |
| t.Fatalf("byteSliceFromString(%q) failed: %v", method, err) |
| } |
| uriData, err := byteSliceFromString(uri) |
| if err != nil { |
| t.Fatalf("byteSliceFromString(%q) failed: %v", uri, err) |
| } |
| reqInner := &opendalPresignedRequest{} |
| freeCount := 0 |
| |
| ctx := context.Background() |
| ctx = context.WithValue(ctx, ffiPresignedRequestMethod.opts.sym, func(req *opendalPresignedRequest) *byte { |
| assertPresignedRequestPointer(t, reqInner, req) |
| return &methodData[0] |
| }) |
| ctx = context.WithValue(ctx, ffiPresignedRequestUri.opts.sym, func(req *opendalPresignedRequest) *byte { |
| assertPresignedRequestPointer(t, reqInner, req) |
| return &uriData[0] |
| }) |
| ctx = context.WithValue(ctx, ffiPresignedRequestHeaders.opts.sym, func(req *opendalPresignedRequest) *opendalHttpHeaderPair { |
| assertPresignedRequestPointer(t, reqInner, req) |
| return nil |
| }) |
| ctx = context.WithValue(ctx, ffiPresignedRequestHeadersLen.opts.sym, func(req *opendalPresignedRequest) uintptr { |
| assertPresignedRequestPointer(t, reqInner, req) |
| return 0 |
| }) |
| ctx = context.WithValue(ctx, ffiPresignedRequestFree.opts.sym, func(req *opendalPresignedRequest) { |
| assertPresignedRequestPointer(t, reqInner, req) |
| freeCount++ |
| }) |
| return ctx, reqInner, &freeCount |
| } |
| |
| func assertPresignCall(t *testing.T, wantOp *opendalOperator, gotOp *opendalOperator, wantPath, gotPath string, wantExpire, gotExpire uint64) { |
| t.Helper() |
| if gotOp != wantOp { |
| t.Fatalf("presign op = %p, want %p", gotOp, wantOp) |
| } |
| if gotPath != wantPath { |
| t.Fatalf("presign path = %q, want %q", gotPath, wantPath) |
| } |
| if gotExpire != wantExpire { |
| t.Fatalf("presign expire = %d, want %d", gotExpire, wantExpire) |
| } |
| } |
| |
| func assertPresignedRequestPointer(t *testing.T, want *opendalPresignedRequest, got *opendalPresignedRequest) { |
| t.Helper() |
| if got != want { |
| t.Fatalf("presigned request = %p, want %p", got, want) |
| } |
| } |
| |
| func assertReadOptionsPointer(t *testing.T, want *opendalReadOptions, got *opendalReadOptions) { |
| t.Helper() |
| if got != want { |
| t.Fatalf("read options = %p, want %p", got, want) |
| } |
| } |
| |
| func assertReadOptionsPointerFromArgs(t *testing.T, want *opendalReadOptions, aValues ...unsafe.Pointer) { |
| t.Helper() |
| if len(aValues) != 2 { |
| t.Fatalf("read option setter received %d arguments, want 2", len(aValues)) |
| } |
| assertReadOptionsPointer(t, want, *(**opendalReadOptions)(aValues[0])) |
| } |
| |
| func noopReadOptionsSetString(*opendalReadOptions, string) ([]byte, error) { |
| return nil, nil |
| } |
| |
| func assertWriteOptionsPointer(t *testing.T, want *opendalWriteOptions, got *opendalWriteOptions) { |
| t.Helper() |
| if got != want { |
| t.Fatalf("write options = %p, want %p", got, want) |
| } |
| } |
| |
| func assertWriteOptionsPointerFromArgs(t *testing.T, want *opendalWriteOptions, aValues ...unsafe.Pointer) { |
| t.Helper() |
| if len(aValues) != 2 { |
| t.Fatalf("write option setter received %d arguments, want 2", len(aValues)) |
| } |
| assertWriteOptionsPointer(t, want, *(**opendalWriteOptions)(aValues[0])) |
| } |
| |
| func noopWriteOptionsSetString(*opendalWriteOptions, string) ([]byte, error) { |
| return nil, nil |
| } |
| |
| func assertStatOptionsPointer(t *testing.T, want *opendalStatOptions, got *opendalStatOptions) { |
| t.Helper() |
| if got != want { |
| t.Fatalf("stat options = %p, want %p", got, want) |
| } |
| } |
| |
| func assertStatOptionsPointerFromArgs(t *testing.T, want *opendalStatOptions, aValues ...unsafe.Pointer) { |
| t.Helper() |
| if len(aValues) != 2 { |
| t.Fatalf("stat option setter received %d arguments, want 2", len(aValues)) |
| } |
| assertStatOptionsPointer(t, want, *(**opendalStatOptions)(aValues[0])) |
| } |
| |
| func noopStatOptionsSetString(*opendalStatOptions, string) ([]byte, error) { |
| return nil, nil |
| } |
| |
| func assertDeleteOptionsPointer(t *testing.T, want *opendalDeleteOptions, got *opendalDeleteOptions) { |
| t.Helper() |
| if got != want { |
| t.Fatalf("delete options = %p, want %p", got, want) |
| } |
| } |
| |
| func assertDeleteOptionsPointerFromArgs(t *testing.T, want *opendalDeleteOptions, aValues ...unsafe.Pointer) { |
| t.Helper() |
| if len(aValues) != 2 { |
| t.Fatalf("delete option setter received %d arguments, want 2", len(aValues)) |
| } |
| assertDeleteOptionsPointer(t, want, *(**opendalDeleteOptions)(aValues[0])) |
| } |