Skip to content

Commit 59d1044

Browse files
committed
Add more tests for FdHandle, add copy assignment operator to FdHandle
1 parent f2c39f6 commit 59d1044

3 files changed

Lines changed: 193 additions & 10 deletions

File tree

include/fs/FdHandle.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ class FdHandle {
189189
*/
190190
FdHandle& operator=(FdHandle&& other) noexcept;
191191

192+
FdHandle& operator=(const FdHandle& other);
193+
192194
/**
193195
* @brief Checks if the handle is valid.
194196
* @return true if valid.

src/fs/FdHandle.cpp

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -376,17 +376,39 @@ static FdHandleData& getHandleData(int16_t fd) {
376376

377377

378378
FdHandle::FdHandle(int fd) : fd((int16_t)fd) {
379-
getHandleData((int16_t)fd).incRef();
379+
if (this->fd >= 0)
380+
getHandleData(this->fd).incRef();
380381
}
381382

382383
FdHandle::FdHandle(const FdHandle& other) : fd(other.fd) {
383-
getHandleData((int16_t)fd).incRef();
384+
if (fd >= 0)
385+
getHandleData(fd).incRef();
384386
}
385387

386388
FdHandle::FdHandle(FdHandle&& other) noexcept : fd(other.fd) {
387389
other.fd = -1;
388390
}
389391

392+
FdHandle &FdHandle::operator=(FdHandle&& other) noexcept {
393+
if (fd >= 0)
394+
getHandleData(fd).decRef();
395+
fd = other.fd;
396+
other.fd = -1;
397+
return *this;
398+
}
399+
400+
FdHandle &FdHandle::operator=(const FdHandle& other) {
401+
if (this == &other)
402+
return *this;
403+
404+
if (fd >= 0)
405+
getHandleData(fd).decRef();
406+
fd = other.fd;
407+
if (fd >= 0)
408+
getHandleData(fd).incRef();
409+
return *this;
410+
}
411+
390412
FdHandle FdHandle::open(const char* path, int mode) {
391413
int fd = ::open(path, mode);
392414
if (fd != -1) {
@@ -534,14 +556,6 @@ FdHandle::~FdHandle() {
534556
getHandleData(fd).decRef();
535557
}
536558

537-
FdHandle &FdHandle::operator=(FdHandle &&other) noexcept {
538-
if (fd != -1)
539-
getHandleData(fd).decRef();
540-
fd = other.fd;
541-
other.fd = -1;
542-
return *this;
543-
}
544-
545559
void FdHandle::markToClose() const {
546560
getHandleData(fd).markToClose();
547561
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/*
2+
* Copyright 2023-2025 komozoi
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "fs/FdHandle.h"
18+
#include <gtest/gtest.h>
19+
#include "fcntl.h"
20+
#include <unistd.h>
21+
22+
#define TEST_FILE "test_assignment.txt"
23+
24+
class FdHandleAssignmentTest : public ::testing::Test {
25+
protected:
26+
void SetUp() override {
27+
remove(TEST_FILE);
28+
remove("another_test.txt");
29+
}
30+
31+
void TearDown() override {
32+
remove(TEST_FILE);
33+
remove("another_test.txt");
34+
}
35+
};
36+
37+
TEST_F(FdHandleAssignmentTest, NormalAssignment) {
38+
FdHandle h1 = FdHandle::open(TEST_FILE, O_WRONLY | O_CREAT, 0660);
39+
ASSERT_TRUE(h1);
40+
int fd1 = h1.getFd();
41+
EXPECT_EQ(h1.numReferences(), 1);
42+
43+
FdHandle h2;
44+
EXPECT_FALSE(h2);
45+
46+
h2 = h1;
47+
EXPECT_TRUE(h2);
48+
EXPECT_EQ(h2.getFd(), fd1);
49+
EXPECT_EQ(h1.numReferences(), 2);
50+
EXPECT_EQ(h2.numReferences(), 2);
51+
}
52+
53+
TEST_F(FdHandleAssignmentTest, SelfAssignment) {
54+
FdHandle h1 = FdHandle::open(TEST_FILE, O_WRONLY | O_CREAT, 0660);
55+
int initialRefs = h1.numReferences();
56+
int fd1 = h1.getFd();
57+
58+
h1 = h1;
59+
60+
EXPECT_TRUE(h1);
61+
EXPECT_EQ(h1.getFd(), fd1);
62+
EXPECT_EQ(h1.numReferences(), initialRefs);
63+
}
64+
65+
TEST_F(FdHandleAssignmentTest, ReassignmentReleasesOld) {
66+
FdHandle h1 = FdHandle::open(TEST_FILE, O_WRONLY | O_CREAT, 0660);
67+
int fd1 = h1.getFd();
68+
69+
FdHandle h2 = FdHandle::open("another_test.txt", O_WRONLY | O_CREAT, 0660);
70+
int fd2 = h2.getFd();
71+
72+
h1 = h2;
73+
74+
EXPECT_EQ(h1.getFd(), fd2);
75+
EXPECT_EQ(h1.numReferences(), 2);
76+
EXPECT_EQ(h2.numReferences(), 2);
77+
78+
// fd1 should be closed because h1 was the only handle
79+
EXPECT_EQ(fcntl(fd1, F_GETFD), -1);
80+
EXPECT_EQ(errno, EBADF);
81+
}
82+
83+
TEST_F(FdHandleAssignmentTest, AssignmentFromInvalid) {
84+
FdHandle h1 = FdHandle::open(TEST_FILE, O_WRONLY | O_CREAT, 0660);
85+
int fd1 = h1.getFd();
86+
FdHandle h2; // invalid, fd = -1
87+
88+
h1 = h2;
89+
90+
EXPECT_FALSE(h1);
91+
EXPECT_EQ(h1.getFd(), -1);
92+
93+
// fd1 should be closed
94+
EXPECT_EQ(fcntl(fd1, F_GETFD), -1);
95+
EXPECT_EQ(errno, EBADF);
96+
}
97+
98+
TEST_F(FdHandleAssignmentTest, AssignmentToInvalid) {
99+
FdHandle h1;
100+
FdHandle h2 = FdHandle::open(TEST_FILE, O_WRONLY | O_CREAT, 0660);
101+
int fd2 = h2.getFd();
102+
103+
h1 = h2;
104+
105+
EXPECT_TRUE(h1);
106+
EXPECT_EQ(h1.getFd(), fd2);
107+
EXPECT_EQ(h2.numReferences(), 2);
108+
}
109+
110+
TEST_F(FdHandleAssignmentTest, ChainAssignment) {
111+
FdHandle h1 = FdHandle::open(TEST_FILE, O_WRONLY | O_CREAT, 0660);
112+
FdHandle h2, h3;
113+
114+
h3 = h2 = h1;
115+
116+
EXPECT_EQ(h1.numReferences(), 3);
117+
EXPECT_EQ(h1.getFd(), h2.getFd());
118+
EXPECT_EQ(h1.getFd(), h3.getFd());
119+
}
120+
121+
TEST_F(FdHandleAssignmentTest, SameFdDifferentHandleObjects) {
122+
FdHandle h1 = FdHandle::open(TEST_FILE, O_WRONLY | O_CREAT, 0660);
123+
int fd = h1.getFd();
124+
FdHandle h2 = FdHandle::from(fd);
125+
126+
EXPECT_EQ(h1.numReferences(), 2);
127+
128+
h1 = h2;
129+
130+
EXPECT_EQ(h1.getFd(), fd);
131+
EXPECT_EQ(h1.numReferences(), 2);
132+
}
133+
134+
TEST_F(FdHandleAssignmentTest, MultipleAssignmentSameHandle) {
135+
FdHandle h1 = FdHandle::open(TEST_FILE, O_WRONLY | O_CREAT, 0660);
136+
FdHandle h2 = FdHandle::open("another_test.txt", O_WRONLY | O_CREAT, 0660);
137+
138+
int fd2 = h2.getFd();
139+
140+
h1 = h2;
141+
h1 = h2;
142+
h1 = h2;
143+
144+
EXPECT_EQ(h1.getFd(), fd2);
145+
EXPECT_EQ(h1.numReferences(), 2);
146+
}
147+
148+
TEST_F(FdHandleAssignmentTest, AssignmentFromSelfViaAlias) {
149+
FdHandle h1 = FdHandle::open(TEST_FILE, O_WRONLY | O_CREAT, 0660);
150+
FdHandle& alias = h1;
151+
int initialRefs = h1.numReferences();
152+
153+
h1 = alias;
154+
155+
EXPECT_EQ(h1.numReferences(), initialRefs);
156+
}
157+
158+
TEST_F(FdHandleAssignmentTest, InvalidHandleRefcountGrowth) {
159+
FdHandle h1;
160+
int ref1 = h1.numReferences();
161+
162+
FdHandle h2;
163+
h1 = h2;
164+
165+
int ref2 = h1.numReferences();
166+
EXPECT_EQ(ref1, ref2) << "Refcount of invalid handle should not grow on assignment";
167+
}

0 commit comments

Comments
 (0)