summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/e2e/integration_test.go7
-rw-r--r--test/fuse/BUILD8
-rw-r--r--test/fuse/linux/BUILD21
-rw-r--r--test/fuse/linux/fuse_base.cc13
-rw-r--r--test/fuse/linux/fuse_base.h6
-rw-r--r--test/fuse/linux/stat_test.cc169
-rw-r--r--test/packetimpact/testbench/layers.go22
-rw-r--r--test/packetimpact/testbench/layers_test.go2
-rw-r--r--test/packetimpact/tests/ipv6_fragment_reassembly_test.go6
-rw-r--r--test/packetimpact/tests/ipv6_unknown_options_action_test.go2
-rw-r--r--test/packetimpact/tests/tcp_network_unreachable_test.go6
-rw-r--r--test/packetimpact/tests/udp_icmp_error_propagation_test.go8
-rw-r--r--test/runner/defs.bzl45
-rw-r--r--test/runner/gtest/gtest.go8
-rw-r--r--test/runner/runner.go31
-rw-r--r--test/syscalls/BUILD1
-rw-r--r--test/syscalls/linux/open.cc52
-rw-r--r--test/syscalls/linux/socket_generic_stress.cc49
-rw-r--r--test/syscalls/linux/socket_inet_loopback.cc112
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc37
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h6
-rw-r--r--test/syscalls/linux/tcp_socket.cc24
22 files changed, 545 insertions, 90 deletions
diff --git a/test/e2e/integration_test.go b/test/e2e/integration_test.go
index 71ec4791e..809244bab 100644
--- a/test/e2e/integration_test.go
+++ b/test/e2e/integration_test.go
@@ -167,6 +167,13 @@ func TestCheckpointRestore(t *testing.T) {
t.Skip("Pause/resume is not supported.")
}
+ // TODO(gvisor.dev/issue/3373): Remove after implementing.
+ if usingVFS2, err := dockerutil.UsingVFS2(); usingVFS2 {
+ t.Skip("CheckpointRestore not implemented in VFS2.")
+ } else if err != nil {
+ t.Fatalf("failed to read config for runtime %s: %v", dockerutil.Runtime(), err)
+ }
+
ctx := context.Background()
d := dockerutil.MakeContainer(ctx, t)
defer d.CleanUp(ctx)
diff --git a/test/fuse/BUILD b/test/fuse/BUILD
index 34b950644..56157c96b 100644
--- a/test/fuse/BUILD
+++ b/test/fuse/BUILD
@@ -1 +1,9 @@
+load("//test/runner:defs.bzl", "syscall_test")
+
package(licenses = ["notice"])
+
+syscall_test(
+ fuse = "True",
+ test = "//test/fuse/linux:stat_test",
+ vfs2 = "True",
+)
diff --git a/test/fuse/linux/BUILD b/test/fuse/linux/BUILD
index 49dc96c20..4871bb531 100644
--- a/test/fuse/linux/BUILD
+++ b/test/fuse/linux/BUILD
@@ -1,20 +1,31 @@
-load("//tools:defs.bzl", "cc_library", "gtest")
+load("//tools:defs.bzl", "cc_binary", "cc_library", "gtest")
package(
default_visibility = ["//:sandbox"],
licenses = ["notice"],
)
+cc_binary(
+ name = "stat_test",
+ testonly = 1,
+ srcs = ["stat_test.cc"],
+ deps = [
+ gtest,
+ ":fuse_base",
+ "//test/util:test_main",
+ "//test/util:test_util",
+ ],
+)
+
cc_library(
name = "fuse_base",
testonly = 1,
- srcs = [
- "fuse_base.cc",
- "fuse_base.h",
- ],
+ srcs = ["fuse_base.cc"],
+ hdrs = ["fuse_base.h"],
deps = [
gtest,
"//test/util:posix_error",
+ "//test/util:temp_path",
"//test/util:test_util",
"@com_google_absl//absl/strings:str_format",
],
diff --git a/test/fuse/linux/fuse_base.cc b/test/fuse/linux/fuse_base.cc
index ce69276c9..9c3124472 100644
--- a/test/fuse/linux/fuse_base.cc
+++ b/test/fuse/linux/fuse_base.cc
@@ -12,23 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "fuse_base.h"
+#include "test/fuse/linux/fuse_base.h"
#include <fcntl.h>
#include <linux/fuse.h>
-#include <stdio.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <iostream>
-#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/strings/str_format.h"
#include "test/util/posix_error.h"
+#include "test/util/temp_path.h"
#include "test/util/test_util.h"
namespace gvisor {
@@ -78,13 +78,14 @@ void FuseTest::MountFuse() {
EXPECT_THAT(dev_fd_ = open("/dev/fuse", O_RDWR), SyscallSucceeds());
std::string mount_opts = absl::StrFormat("fd=%d,%s", dev_fd_, kMountOpts);
- EXPECT_THAT(mount("fuse", kMountPoint, "fuse", MS_NODEV | MS_NOSUID,
- mount_opts.c_str()),
+ mount_point_ = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ EXPECT_THAT(mount("fuse", mount_point_.path().c_str(), "fuse",
+ MS_NODEV | MS_NOSUID, mount_opts.c_str()),
SyscallSucceedsWithValue(0));
}
void FuseTest::UnmountFuse() {
- EXPECT_THAT(umount(kMountPoint), SyscallSucceeds());
+ EXPECT_THAT(umount(mount_point_.path().c_str()), SyscallSucceeds());
// TODO(gvisor.dev/issue/3330): ensure the process is terminated successfully.
}
diff --git a/test/fuse/linux/fuse_base.h b/test/fuse/linux/fuse_base.h
index b008778de..3a2f255a9 100644
--- a/test/fuse/linux/fuse_base.h
+++ b/test/fuse/linux/fuse_base.h
@@ -20,14 +20,13 @@
#include <vector>
-#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "test/util/posix_error.h"
+#include "test/util/temp_path.h"
namespace gvisor {
namespace testing {
-constexpr char kMountPoint[] = "/mnt";
constexpr char kMountOpts[] = "rootmode=755,user_id=0,group_id=0";
class FuseTest : public ::testing::Test {
@@ -55,6 +54,9 @@ class FuseTest : public ::testing::Test {
// complains if the FUSE server responds failure during tests.
void WaitCompleted();
+ protected:
+ TempPath mount_point_;
+
private:
void MountFuse();
void UnmountFuse();
diff --git a/test/fuse/linux/stat_test.cc b/test/fuse/linux/stat_test.cc
new file mode 100644
index 000000000..172e09867
--- /dev/null
+++ b/test/fuse/linux/stat_test.cc
@@ -0,0 +1,169 @@
+// Copyright 2020 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fuse.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "test/fuse/linux/fuse_base.h"
+#include "test/util/test_util.h"
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+class StatTest : public FuseTest {
+ public:
+ bool CompareRequest(void* expected_mem, size_t expected_len, void* real_mem,
+ size_t real_len) override {
+ if (expected_len != real_len) return false;
+ struct fuse_in_header* real_header =
+ reinterpret_cast<fuse_in_header*>(real_mem);
+
+ if (real_header->opcode != FUSE_GETATTR) {
+ std::cerr << "expect header opcode " << FUSE_GETATTR << " but got "
+ << real_header->opcode << std::endl;
+ return false;
+ }
+ return true;
+ }
+
+ bool StatsAreEqual(struct stat expected, struct stat actual) {
+ // device number will be dynamically allocated by kernel, we cannot know
+ // in advance
+ actual.st_dev = expected.st_dev;
+ return memcmp(&expected, &actual, sizeof(struct stat)) == 0;
+ }
+};
+
+TEST_F(StatTest, StatNormal) {
+ struct iovec iov_in[2];
+ struct iovec iov_out[2];
+
+ struct fuse_in_header in_header = {
+ .len = sizeof(struct fuse_in_header) + sizeof(struct fuse_getattr_in),
+ .opcode = FUSE_GETATTR,
+ .unique = 4,
+ .nodeid = 1,
+ .uid = 0,
+ .gid = 0,
+ .pid = 4,
+ .padding = 0,
+ };
+ struct fuse_getattr_in in_payload = {0};
+ iov_in[0].iov_len = sizeof(in_header);
+ iov_in[0].iov_base = &in_header;
+ iov_in[1].iov_len = sizeof(in_payload);
+ iov_in[1].iov_base = &in_payload;
+
+ mode_t expected_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+ struct timespec atime = {.tv_sec = 1595436289, .tv_nsec = 134150844};
+ struct timespec mtime = {.tv_sec = 1595436290, .tv_nsec = 134150845};
+ struct timespec ctime = {.tv_sec = 1595436291, .tv_nsec = 134150846};
+ struct fuse_out_header out_header = {
+ .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out),
+ .error = 0,
+ .unique = 4,
+ };
+ struct fuse_attr attr = {
+ .ino = 1,
+ .size = 512,
+ .blocks = 4,
+ .atime = static_cast<uint64_t>(atime.tv_sec),
+ .mtime = static_cast<uint64_t>(mtime.tv_sec),
+ .ctime = static_cast<uint64_t>(ctime.tv_sec),
+ .atimensec = static_cast<uint32_t>(atime.tv_nsec),
+ .mtimensec = static_cast<uint32_t>(mtime.tv_nsec),
+ .ctimensec = static_cast<uint32_t>(ctime.tv_nsec),
+ .mode = expected_mode,
+ .nlink = 2,
+ .uid = 1234,
+ .gid = 4321,
+ .rdev = 12,
+ .blksize = 4096,
+ };
+ struct fuse_attr_out out_payload = {
+ .attr = attr,
+ };
+ iov_out[0].iov_len = sizeof(out_header);
+ iov_out[0].iov_base = &out_header;
+ iov_out[1].iov_len = sizeof(out_payload);
+ iov_out[1].iov_base = &out_payload;
+
+ SetExpected(iov_in, 2, iov_out, 2);
+
+ struct stat stat_buf;
+ EXPECT_THAT(stat(mount_point_.path().c_str(), &stat_buf), SyscallSucceeds());
+
+ struct stat expected_stat = {
+ .st_ino = attr.ino,
+ .st_nlink = attr.nlink,
+ .st_mode = expected_mode,
+ .st_uid = attr.uid,
+ .st_gid = attr.gid,
+ .st_rdev = attr.rdev,
+ .st_size = static_cast<off_t>(attr.size),
+ .st_blksize = attr.blksize,
+ .st_blocks = static_cast<blkcnt_t>(attr.blocks),
+ .st_atim = atime,
+ .st_mtim = mtime,
+ .st_ctim = ctime,
+ };
+ EXPECT_TRUE(StatsAreEqual(stat_buf, expected_stat));
+ WaitCompleted();
+}
+
+TEST_F(StatTest, StatNotFound) {
+ struct iovec iov_in[2];
+ struct iovec iov_out[2];
+
+ struct fuse_in_header in_header = {
+ .len = sizeof(struct fuse_in_header) + sizeof(struct fuse_getattr_in),
+ .opcode = FUSE_GETATTR,
+ .unique = 4,
+ };
+ struct fuse_getattr_in in_payload = {0};
+ iov_in[0].iov_len = sizeof(in_header);
+ iov_in[0].iov_base = &in_header;
+ iov_in[1].iov_len = sizeof(in_payload);
+ iov_in[1].iov_base = &in_payload;
+
+ struct fuse_out_header out_header = {
+ .len = sizeof(struct fuse_out_header),
+ .error = -ENOENT,
+ .unique = 4,
+ };
+ iov_out[0].iov_len = sizeof(out_header);
+ iov_out[0].iov_base = &out_header;
+
+ SetExpected(iov_in, 2, iov_out, 1);
+
+ struct stat stat_buf;
+ EXPECT_THAT(stat(mount_point_.path().c_str(), &stat_buf),
+ SyscallFailsWithErrno(ENOENT));
+ WaitCompleted();
+}
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/packetimpact/testbench/layers.go b/test/packetimpact/testbench/layers.go
index 24aa46cce..a35562ca8 100644
--- a/test/packetimpact/testbench/layers.go
+++ b/test/packetimpact/testbench/layers.go
@@ -775,7 +775,7 @@ func (l *IPv6FragmentExtHdr) String() string {
type ICMPv6 struct {
LayerBase
Type *header.ICMPv6Type
- Code *byte
+ Code *header.ICMPv6Code
Checksum *uint16
Payload []byte
}
@@ -823,6 +823,12 @@ func ICMPv6Type(v header.ICMPv6Type) *header.ICMPv6Type {
return &v
}
+// ICMPv6Code is a helper routine that allocates a new ICMPv6Type value to store
+// v and returns a pointer to it.
+func ICMPv6Code(v header.ICMPv6Code) *header.ICMPv6Code {
+ return &v
+}
+
// Byte is a helper routine that allocates a new byte value to store
// v and returns a pointer to it.
func Byte(v byte) *byte {
@@ -834,7 +840,7 @@ func parseICMPv6(b []byte) (Layer, layerParser) {
h := header.ICMPv6(b)
icmpv6 := ICMPv6{
Type: ICMPv6Type(h.Type()),
- Code: Byte(h.Code()),
+ Code: ICMPv6Code(h.Code()),
Checksum: Uint16(h.Checksum()),
Payload: h.NDPPayload(),
}
@@ -861,11 +867,17 @@ func ICMPv4Type(t header.ICMPv4Type) *header.ICMPv4Type {
return &t
}
+// ICMPv4Code is a helper routine that allocates a new header.ICMPv4Code value
+// to store t and returns a pointer to it.
+func ICMPv4Code(t header.ICMPv4Code) *header.ICMPv4Code {
+ return &t
+}
+
// ICMPv4 can construct and match an ICMPv4 encapsulation.
type ICMPv4 struct {
LayerBase
Type *header.ICMPv4Type
- Code *uint8
+ Code *header.ICMPv4Code
Checksum *uint16
}
@@ -881,7 +893,7 @@ func (l *ICMPv4) ToBytes() ([]byte, error) {
h.SetType(*l.Type)
}
if l.Code != nil {
- h.SetCode(byte(*l.Code))
+ h.SetCode(*l.Code)
}
if l.Checksum != nil {
h.SetChecksum(*l.Checksum)
@@ -901,7 +913,7 @@ func parseICMPv4(b []byte) (Layer, layerParser) {
h := header.ICMPv4(b)
icmpv4 := ICMPv4{
Type: ICMPv4Type(h.Type()),
- Code: Uint8(h.Code()),
+ Code: ICMPv4Code(h.Code()),
Checksum: Uint16(h.Checksum()),
}
return &icmpv4, parsePayload
diff --git a/test/packetimpact/testbench/layers_test.go b/test/packetimpact/testbench/layers_test.go
index a2a763034..eca0780b5 100644
--- a/test/packetimpact/testbench/layers_test.go
+++ b/test/packetimpact/testbench/layers_test.go
@@ -594,7 +594,7 @@ func TestIPv6ExtHdrOptions(t *testing.T) {
},
&ICMPv6{
Type: ICMPv6Type(header.ICMPv6ParamProblem),
- Code: Byte(0),
+ Code: ICMPv6Code(header.ICMPv6ErroneousHeader),
Checksum: Uint16(0x5f98),
Payload: []byte{0x00, 0x00, 0x00, 0x06},
},
diff --git a/test/packetimpact/tests/ipv6_fragment_reassembly_test.go b/test/packetimpact/tests/ipv6_fragment_reassembly_test.go
index b5f94ad4b..a24c85566 100644
--- a/test/packetimpact/tests/ipv6_fragment_reassembly_test.go
+++ b/test/packetimpact/tests/ipv6_fragment_reassembly_test.go
@@ -67,7 +67,7 @@ func TestIPv6FragmentReassembly(t *testing.T) {
rIP := tcpip.Address(net.ParseIP(testbench.RemoteIPv6).To16())
icmpv6 := testbench.ICMPv6{
Type: testbench.ICMPv6Type(header.ICMPv6EchoRequest),
- Code: testbench.Byte(0),
+ Code: testbench.ICMPv6Code(header.ICMPv6UnusedCode),
Payload: icmpv6EchoPayload,
}
icmpv6Bytes, err := icmpv6.ToBytes()
@@ -89,7 +89,7 @@ func TestIPv6FragmentReassembly(t *testing.T) {
},
&testbench.ICMPv6{
Type: testbench.ICMPv6Type(header.ICMPv6EchoRequest),
- Code: testbench.Byte(0),
+ Code: testbench.ICMPv6Code(header.ICMPv6UnusedCode),
Payload: icmpv6EchoPayload,
Checksum: &cksum,
})
@@ -116,7 +116,7 @@ func TestIPv6FragmentReassembly(t *testing.T) {
},
&testbench.ICMPv6{
Type: testbench.ICMPv6Type(header.ICMPv6EchoReply),
- Code: testbench.Byte(0),
+ Code: testbench.ICMPv6Code(header.ICMPv6UnusedCode),
},
}, time.Second)
if err != nil {
diff --git a/test/packetimpact/tests/ipv6_unknown_options_action_test.go b/test/packetimpact/tests/ipv6_unknown_options_action_test.go
index d7d63cbd2..e79d74476 100644
--- a/test/packetimpact/tests/ipv6_unknown_options_action_test.go
+++ b/test/packetimpact/tests/ipv6_unknown_options_action_test.go
@@ -172,7 +172,7 @@ func TestIPv6UnknownOptionAction(t *testing.T) {
&testbench.IPv6{},
&testbench.ICMPv6{
Type: testbench.ICMPv6Type(header.ICMPv6ParamProblem),
- Code: testbench.Byte(2),
+ Code: testbench.ICMPv6Code(header.ICMPv6UnknownOption),
Payload: icmpv6Payload,
},
}, time.Second)
diff --git a/test/packetimpact/tests/tcp_network_unreachable_test.go b/test/packetimpact/tests/tcp_network_unreachable_test.go
index 900352fa1..2f57dff19 100644
--- a/test/packetimpact/tests/tcp_network_unreachable_test.go
+++ b/test/packetimpact/tests/tcp_network_unreachable_test.go
@@ -72,7 +72,9 @@ func TestTCPSynSentUnreachable(t *testing.T) {
if !ok {
t.Fatalf("expected %s to be TCP", tcpLayers[tcpLayer])
}
- var icmpv4 testbench.ICMPv4 = testbench.ICMPv4{Type: testbench.ICMPv4Type(header.ICMPv4DstUnreachable), Code: testbench.Uint8(header.ICMPv4HostUnreachable)}
+ var icmpv4 testbench.ICMPv4 = testbench.ICMPv4{
+ Type: testbench.ICMPv4Type(header.ICMPv4DstUnreachable),
+ Code: testbench.ICMPv4Code(header.ICMPv4HostUnreachable)}
layers = append(layers, &icmpv4, ip, tcp)
rawConn.SendFrameStateless(t, layers)
@@ -126,7 +128,7 @@ func TestTCPSynSentUnreachable6(t *testing.T) {
}
var icmpv6 testbench.ICMPv6 = testbench.ICMPv6{
Type: testbench.ICMPv6Type(header.ICMPv6DstUnreachable),
- Code: testbench.Uint8(header.ICMPv6NetworkUnreachable),
+ Code: testbench.ICMPv6Code(header.ICMPv6NetworkUnreachable),
// Per RFC 4443 3.1, the payload contains 4 zeroed bytes.
Payload: []byte{0, 0, 0, 0},
}
diff --git a/test/packetimpact/tests/udp_icmp_error_propagation_test.go b/test/packetimpact/tests/udp_icmp_error_propagation_test.go
index b47ddb6c3..df35d16c8 100644
--- a/test/packetimpact/tests/udp_icmp_error_propagation_test.go
+++ b/test/packetimpact/tests/udp_icmp_error_propagation_test.go
@@ -62,9 +62,13 @@ func (e icmpError) String() string {
func (e icmpError) ToICMPv4() *testbench.ICMPv4 {
switch e {
case portUnreachable:
- return &testbench.ICMPv4{Type: testbench.ICMPv4Type(header.ICMPv4DstUnreachable), Code: testbench.Uint8(header.ICMPv4PortUnreachable)}
+ return &testbench.ICMPv4{
+ Type: testbench.ICMPv4Type(header.ICMPv4DstUnreachable),
+ Code: testbench.ICMPv4Code(header.ICMPv4PortUnreachable)}
case timeToLiveExceeded:
- return &testbench.ICMPv4{Type: testbench.ICMPv4Type(header.ICMPv4TimeExceeded), Code: testbench.Uint8(header.ICMPv4TTLExceeded)}
+ return &testbench.ICMPv4{
+ Type: testbench.ICMPv4Type(header.ICMPv4TimeExceeded),
+ Code: testbench.ICMPv4Code(header.ICMPv4TTLExceeded)}
}
return nil
}
diff --git a/test/runner/defs.bzl b/test/runner/defs.bzl
index fa96b4ea2..2d64934b0 100644
--- a/test/runner/defs.bzl
+++ b/test/runner/defs.bzl
@@ -150,31 +150,12 @@ def syscall_test(
if not tags:
tags = []
- _syscall_test(
- test = test,
- shard_count = shard_count,
- size = size,
- platform = "native",
- use_tmpfs = False,
- add_uds_tree = add_uds_tree,
- tags = list(tags),
- )
-
- for (platform, platform_tags) in platforms.items():
- _syscall_test(
- test = test,
- shard_count = shard_count,
- size = size,
- platform = platform,
- use_tmpfs = use_tmpfs,
- add_uds_tree = add_uds_tree,
- tags = platform_tags + tags,
- )
-
vfs2_tags = list(tags)
if vfs2:
# Add tag to easily run VFS2 tests with --test_tag_filters=vfs2
vfs2_tags.append("vfs2")
+ if fuse:
+ vfs2_tags.append("fuse")
else:
# Don't automatically run tests tests not yet passing.
@@ -191,19 +172,31 @@ def syscall_test(
add_uds_tree = add_uds_tree,
tags = platforms[default_platform] + vfs2_tags,
vfs2 = True,
+ fuse = fuse,
+ )
+ if fuse:
+ # Only generate *_vfs2_fuse target if fuse parameter is enabled.
+ return
+
+ _syscall_test(
+ test = test,
+ shard_count = shard_count,
+ size = size,
+ platform = "native",
+ use_tmpfs = False,
+ add_uds_tree = add_uds_tree,
+ tags = list(tags),
)
- if vfs2 and fuse:
+ for (platform, platform_tags) in platforms.items():
_syscall_test(
test = test,
shard_count = shard_count,
size = size,
- platform = default_platform,
+ platform = platform,
use_tmpfs = use_tmpfs,
add_uds_tree = add_uds_tree,
- tags = platforms[default_platform] + vfs2_tags + ["fuse"],
- vfs2 = True,
- fuse = True,
+ tags = platform_tags + tags,
)
# TODO(gvisor.dev/issue/1487): Enable VFS2 overlay tests.
diff --git a/test/runner/gtest/gtest.go b/test/runner/gtest/gtest.go
index 869169ad5..e4445e01b 100644
--- a/test/runner/gtest/gtest.go
+++ b/test/runner/gtest/gtest.go
@@ -146,10 +146,13 @@ func ParseTestCases(testBin string, benchmarks bool, extraArgs ...string) ([]Tes
return nil, fmt.Errorf("could not enumerate gtest benchmarks: %v\nstderr\n%s", err, exitErr.Stderr)
}
- out = []byte(strings.Trim(string(out), "\n"))
+ benches := strings.Trim(string(out), "\n")
+ if len(benches) == 0 {
+ return t, nil
+ }
// Parse benchmark output.
- for _, line := range strings.Split(string(out), "\n") {
+ for _, line := range strings.Split(benches, "\n") {
// Strip comments.
line = strings.Split(line, "#")[0]
@@ -163,6 +166,5 @@ func ParseTestCases(testBin string, benchmarks bool, extraArgs ...string) ([]Tes
benchmark: true,
})
}
-
return t, nil
}
diff --git a/test/runner/runner.go b/test/runner/runner.go
index bc4b39cbb..5ac91310d 100644
--- a/test/runner/runner.go
+++ b/test/runner/runner.go
@@ -172,13 +172,14 @@ func runRunsc(tc gtest.TestCase, spec *specs.Spec) error {
args = append(args, "-fsgofer-host-uds")
}
- undeclaredOutputsDir, ok := syscall.Getenv("TEST_UNDECLARED_OUTPUTS_DIR")
- if ok {
- tdir := filepath.Join(undeclaredOutputsDir, strings.Replace(name, "/", "_", -1))
- if err := os.MkdirAll(tdir, 0755); err != nil {
+ testLogDir := ""
+ if undeclaredOutputsDir, ok := syscall.Getenv("TEST_UNDECLARED_OUTPUTS_DIR"); ok {
+ // Create log directory dedicated for this test.
+ testLogDir = filepath.Join(undeclaredOutputsDir, strings.Replace(name, "/", "_", -1))
+ if err := os.MkdirAll(testLogDir, 0755); err != nil {
return fmt.Errorf("could not create test dir: %v", err)
}
- debugLogDir, err := ioutil.TempDir(tdir, "runsc")
+ debugLogDir, err := ioutil.TempDir(testLogDir, "runsc")
if err != nil {
return fmt.Errorf("could not create temp dir: %v", err)
}
@@ -227,10 +228,10 @@ func runRunsc(tc gtest.TestCase, spec *specs.Spec) error {
dArgs := append([]string{}, args...)
dArgs = append(dArgs, "-alsologtostderr=true", "debug", "--stacks", id)
go func(dArgs []string) {
- cmd := exec.Command(*runscPath, dArgs...)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- cmd.Run()
+ debug := exec.Command(*runscPath, dArgs...)
+ debug.Stdout = os.Stdout
+ debug.Stderr = os.Stderr
+ debug.Run()
done <- true
}(dArgs)
@@ -245,17 +246,17 @@ func runRunsc(tc gtest.TestCase, spec *specs.Spec) error {
dArgs = append(args, "debug",
fmt.Sprintf("--signal=%d", syscall.SIGTERM),
id)
- cmd := exec.Command(*runscPath, dArgs...)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- cmd.Run()
+ signal := exec.Command(*runscPath, dArgs...)
+ signal.Stdout = os.Stdout
+ signal.Stderr = os.Stderr
+ signal.Run()
}()
err = cmd.Run()
- if err == nil {
+ if err == nil && len(testLogDir) > 0 {
// If the test passed, then we erase the log directory. This speeds up
// uploading logs in continuous integration & saves on disk space.
- os.RemoveAll(undeclaredOutputsDir)
+ os.RemoveAll(testLogDir)
}
return err
diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD
index 571785ad2..0eadc6b08 100644
--- a/test/syscalls/BUILD
+++ b/test/syscalls/BUILD
@@ -69,6 +69,7 @@ syscall_test(
# involve much concurrency, TSAN's usefulness here is limited anyway.
tags = ["nogotsan"],
test = "//test/syscalls/linux:socket_stress_test",
+ vfs2 = False,
)
syscall_test(
diff --git a/test/syscalls/linux/open.cc b/test/syscalls/linux/open.cc
index bf350946b..8f0c9cb49 100644
--- a/test/syscalls/linux/open.cc
+++ b/test/syscalls/linux/open.cc
@@ -95,6 +95,38 @@ TEST_F(OpenTest, OTruncAndReadOnlyFile) {
Open(dirpath.c_str(), O_TRUNC | O_RDONLY, 0666));
}
+TEST_F(OpenTest, OCreateDirectory) {
+ SKIP_IF(IsRunningWithVFS1());
+ auto dirpath = GetAbsoluteTestTmpdir();
+
+ // Normal case: existing directory.
+ ASSERT_THAT(open(dirpath.c_str(), O_RDWR | O_CREAT, 0666),
+ SyscallFailsWithErrno(EISDIR));
+ // Trailing separator on existing directory.
+ ASSERT_THAT(open(dirpath.append("/").c_str(), O_RDWR | O_CREAT, 0666),
+ SyscallFailsWithErrno(EISDIR));
+ // Trailing separator on non-existing directory.
+ ASSERT_THAT(open(JoinPath(dirpath, "non-existent").append("/").c_str(),
+ O_RDWR | O_CREAT, 0666),
+ SyscallFailsWithErrno(EISDIR));
+ // "." special case.
+ ASSERT_THAT(open(JoinPath(dirpath, ".").c_str(), O_RDWR | O_CREAT, 0666),
+ SyscallFailsWithErrno(EISDIR));
+}
+
+TEST_F(OpenTest, MustCreateExisting) {
+ auto dirPath = GetAbsoluteTestTmpdir();
+
+ // Existing directory.
+ ASSERT_THAT(open(dirPath.c_str(), O_RDWR | O_CREAT | O_EXCL, 0666),
+ SyscallFailsWithErrno(EEXIST));
+
+ // Existing file.
+ auto newFile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dirPath));
+ ASSERT_THAT(open(newFile.path().c_str(), O_RDWR | O_CREAT | O_EXCL, 0666),
+ SyscallFailsWithErrno(EEXIST));
+}
+
TEST_F(OpenTest, ReadOnly) {
char buf;
const FileDescriptor ro_file =
@@ -115,6 +147,26 @@ TEST_F(OpenTest, WriteOnly) {
EXPECT_THAT(write(wo_file.get(), &buf, 1), SyscallSucceedsWithValue(1));
}
+TEST_F(OpenTest, CreateWithAppend) {
+ std::string data = "text";
+ std::string new_file = NewTempAbsPath();
+ const FileDescriptor file = ASSERT_NO_ERRNO_AND_VALUE(
+ Open(new_file, O_WRONLY | O_APPEND | O_CREAT, 0666));
+ EXPECT_THAT(write(file.get(), data.c_str(), data.size()),
+ SyscallSucceedsWithValue(data.size()));
+ EXPECT_THAT(lseek(file.get(), 0, SEEK_SET), SyscallSucceeds());
+ EXPECT_THAT(write(file.get(), data.c_str(), data.size()),
+ SyscallSucceedsWithValue(data.size()));
+
+ // Check that the size of the file is correct and that the offset has been
+ // incremented to that size.
+ struct stat s0;
+ EXPECT_THAT(fstat(file.get(), &s0), SyscallSucceeds());
+ EXPECT_EQ(s0.st_size, 2 * data.size());
+ EXPECT_THAT(lseek(file.get(), 0, SEEK_CUR),
+ SyscallSucceedsWithValue(2 * data.size()));
+}
+
TEST_F(OpenTest, ReadWrite) {
char buf;
const FileDescriptor rw_file =
diff --git a/test/syscalls/linux/socket_generic_stress.cc b/test/syscalls/linux/socket_generic_stress.cc
index 6a232238d..19239e9e9 100644
--- a/test/syscalls/linux/socket_generic_stress.cc
+++ b/test/syscalls/linux/socket_generic_stress.cc
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <poll.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@@ -37,6 +38,14 @@ TEST_P(ConnectStressTest, Reset65kTimes) {
char sent_data[100] = {};
ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)),
SyscallSucceedsWithValue(sizeof(sent_data)));
+ // Poll the other FD to make sure that the data is in the receive buffer
+ // before closing it to ensure a RST is triggered.
+ const int kTimeout = 10000;
+ struct pollfd pfd = {
+ .fd = sockets->second_fd(),
+ .events = POLL_IN,
+ };
+ ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
}
}
@@ -58,7 +67,45 @@ INSTANTIATE_TEST_SUITE_P(
// a persistent listener (if applicable).
using PersistentListenerConnectStressTest = SocketPairTest;
-TEST_P(PersistentListenerConnectStressTest, 65kTimes) {
+TEST_P(PersistentListenerConnectStressTest, 65kTimesShutdownCloseFirst) {
+ for (int i = 0; i < 1 << 16; ++i) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+ ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds());
+ if (GetParam().type == SOCK_STREAM) {
+ // Poll the other FD to make sure that we see the FIN from the other
+ // side before closing the second_fd. This ensures that the first_fd
+ // enters TIME-WAIT and not second_fd.
+ const int kTimeout = 10000;
+ struct pollfd pfd = {
+ .fd = sockets->second_fd(),
+ .events = POLL_IN,
+ };
+ ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
+ }
+ ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds());
+ }
+}
+
+TEST_P(PersistentListenerConnectStressTest, 65kTimesShutdownCloseSecond) {
+ for (int i = 0; i < 1 << 16; ++i) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+ ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds());
+ if (GetParam().type == SOCK_STREAM) {
+ // Poll the other FD to make sure that we see the FIN from the other
+ // side before closing the first_fd. This ensures that the second_fd
+ // enters TIME-WAIT and not first_fd.
+ const int kTimeout = 10000;
+ struct pollfd pfd = {
+ .fd = sockets->first_fd(),
+ .events = POLL_IN,
+ };
+ ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
+ }
+ ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds());
+ }
+}
+
+TEST_P(PersistentListenerConnectStressTest, 65kTimesClose) {
for (int i = 0; i < 1 << 16; ++i) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
}
diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc
index 18b9e4b70..c3b42682f 100644
--- a/test/syscalls/linux/socket_inet_loopback.cc
+++ b/test/syscalls/linux/socket_inet_loopback.cc
@@ -865,7 +865,7 @@ TEST_P(SocketInetLoopbackTest, TCPResetAfterClose) {
// results in the stack.Seed() being different which can cause
// sequence number of final connect to be one that is considered
// old and can cause the test to be flaky.
-TEST_P(SocketInetLoopbackTest, TCPTimeWaitTest_NoRandomSave) {
+TEST_P(SocketInetLoopbackTest, TCPPassiveCloseNoTimeWaitTest_NoRandomSave) {
auto const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -920,14 +920,27 @@ TEST_P(SocketInetLoopbackTest, TCPTimeWaitTest_NoRandomSave) {
&conn_addrlen),
SyscallSucceeds());
- // close the accept FD to trigger TIME_WAIT on the accepted socket which
+ // shutdown the accept FD to trigger TIME_WAIT on the accepted socket which
// should cause the conn_fd to follow CLOSE_WAIT->LAST_ACK->CLOSED instead of
// TIME_WAIT.
- accepted.reset();
- absl::SleepFor(absl::Seconds(1));
+ ASSERT_THAT(shutdown(accepted.get(), SHUT_RDWR), SyscallSucceeds());
+ {
+ const int kTimeout = 10000;
+ struct pollfd pfd = {
+ .fd = conn_fd.get(),
+ .events = POLLIN,
+ };
+ ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
+ ASSERT_EQ(pfd.revents, POLLIN);
+ }
+
conn_fd.reset();
+ // This sleep is required to give conn_fd time to transition to TIME-WAIT.
absl::SleepFor(absl::Seconds(1));
+ // At this point conn_fd should be the one that moved to CLOSE_WAIT and
+ // eventually to CLOSED.
+
// Now bind and connect a new socket and verify that we can immediately
// rebind the address bound by the conn_fd as it never entered TIME_WAIT.
const FileDescriptor conn_fd2 = ASSERT_NO_ERRNO_AND_VALUE(
@@ -942,6 +955,97 @@ TEST_P(SocketInetLoopbackTest, TCPTimeWaitTest_NoRandomSave) {
SyscallSucceeds());
}
+TEST_P(SocketInetLoopbackTest, TCPActiveCloseTimeWaitTest_NoRandomSave) {
+ auto const& param = GetParam();
+ TestAddress const& listener = param.listener;
+ TestAddress const& connector = param.connector;
+
+ // Create the listening socket.
+ const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
+ sockaddr_storage listen_addr = listener.addr;
+ ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr),
+ listener.addr_len),
+ SyscallSucceeds());
+ ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
+
+ // Get the port bound by the listening socket.
+ socklen_t addrlen = listener.addr_len;
+ ASSERT_THAT(getsockname(listen_fd.get(),
+ reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
+ SyscallSucceeds());
+
+ uint16_t const port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
+
+ // Connect to the listening socket.
+ FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
+
+ // We disable saves after this point as a S/R causes the netstack seed
+ // to be regenerated which changes what ports/ISN is picked for a given
+ // tuple (src ip,src port, dst ip, dst port). This can cause the final
+ // SYN to use a sequence number that looks like one from the current
+ // connection in TIME_WAIT and will not be accepted causing the test
+ // to timeout.
+ //
+ // TODO(gvisor.dev/issue/940): S/R portSeed/portHint
+ DisableSave ds;
+
+ sockaddr_storage conn_addr = connector.addr;
+ ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
+ ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(),
+ reinterpret_cast<sockaddr*>(&conn_addr),
+ connector.addr_len),
+ SyscallSucceeds());
+
+ // Accept the connection.
+ auto accepted =
+ ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
+
+ // Get the address/port bound by the connecting socket.
+ sockaddr_storage conn_bound_addr;
+ socklen_t conn_addrlen = connector.addr_len;
+ ASSERT_THAT(
+ getsockname(conn_fd.get(), reinterpret_cast<sockaddr*>(&conn_bound_addr),
+ &conn_addrlen),
+ SyscallSucceeds());
+
+ // shutdown the conn FD to trigger TIME_WAIT on the connect socket.
+ ASSERT_THAT(shutdown(conn_fd.get(), SHUT_RDWR), SyscallSucceeds());
+ {
+ const int kTimeout = 10000;
+ struct pollfd pfd = {
+ .fd = accepted.get(),
+ .events = POLLIN,
+ };
+ ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
+ ASSERT_EQ(pfd.revents, POLLIN);
+ }
+ ScopedThread t([&]() {
+ constexpr int kTimeout = 10000;
+ constexpr int16_t want_events = POLLHUP;
+ struct pollfd pfd = {
+ .fd = conn_fd.get(),
+ .events = want_events,
+ };
+ ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
+ });
+
+ accepted.reset();
+ t.Join();
+ conn_fd.reset();
+
+ // Now bind and connect a new socket and verify that we can't immediately
+ // rebind the address bound by the conn_fd as it is in TIME_WAIT.
+ conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
+
+ ASSERT_THAT(bind(conn_fd.get(), reinterpret_cast<sockaddr*>(&conn_bound_addr),
+ conn_addrlen),
+ SyscallFailsWithErrno(EADDRINUSE));
+}
+
TEST_P(SocketInetLoopbackTest, AcceptedInheritsTCPUserTimeout) {
auto const& param = GetParam();
TestAddress const& listener = param.listener;
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc
index d690d9564..b206137eb 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc
@@ -42,7 +42,9 @@ TestAddress V4EmptyAddress() {
}
void IPv4UDPUnboundExternalNetworkingSocketTest::SetUp() {
- got_if_infos_ = false;
+ // FIXME(b/137899561): Linux instance for syscall tests sometimes misses its
+ // IPv4 address on eth0.
+ found_net_interfaces_ = false;
// Get interface list.
ASSERT_NO_ERRNO(if_helper_.Load());
@@ -71,7 +73,7 @@ void IPv4UDPUnboundExternalNetworkingSocketTest::SetUp() {
}
eth_if_addr_ = *reinterpret_cast<const sockaddr_in*>(eth_if_addr);
- got_if_infos_ = true;
+ found_net_interfaces_ = true;
}
// Verifies that a newly instantiated UDP socket does not have the
@@ -110,6 +112,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, SetUDPBroadcast) {
// the destination port number.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
UDPBroadcastReceivedOnExpectedPort) {
+ SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto rcvr1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto rcvr2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -185,9 +188,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// not a unicast address.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
UDPBroadcastReceivedOnExpectedAddresses) {
- // FIXME(b/137899561): Linux instance for syscall tests sometimes misses its
- // IPv4 address on eth0.
- SKIP_IF(!got_if_infos_);
+ SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto rcvr1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -272,6 +273,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// (UDPBroadcastSendRecvOnSocketBoundToAny).
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
UDPBroadcastSendRecvOnSocketBoundToBroadcast) {
+ SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
// Enable SO_BROADCAST.
@@ -313,6 +315,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// (UDPBroadcastSendRecvOnSocketBoundToBroadcast).
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
UDPBroadcastSendRecvOnSocketBoundToAny) {
+ SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
// Enable SO_BROADCAST.
@@ -351,6 +354,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// Verifies that a UDP broadcast fails to send on a socket with SO_BROADCAST
// disabled.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendBroadcast) {
+ SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
// Broadcast a test message without having enabled SO_BROADCAST on the sending
@@ -401,6 +405,8 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// multicast on gVisor.
SKIP_IF(IsRunningOnGvisor());
+ SKIP_IF(!found_net_interfaces_);
+
auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto bind_addr = V4Any();
@@ -435,6 +441,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// Check that multicast packets will be delivered to the sending socket without
// setting an interface.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastSelf) {
+ SKIP_IF(!found_net_interfaces_);
auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto bind_addr = V4Any();
@@ -478,6 +485,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastSelf) {
// set interface and IP_MULTICAST_LOOP disabled.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
TestSendMulticastSelfLoopOff) {
+ SKIP_IF(!found_net_interfaces_);
auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto bind_addr = V4Any();
@@ -528,6 +536,8 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastNoGroup) {
// multicast on gVisor.
SKIP_IF(IsRunningOnGvisor());
+ SKIP_IF(!found_net_interfaces_);
+
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -566,6 +576,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastNoGroup) {
// Check that multicast packets will be delivered to another socket without
// setting an interface.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticast) {
+ SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -613,6 +624,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticast) {
// set interface and IP_MULTICAST_LOOP disabled on the sending socket.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
TestSendMulticastSenderNoLoop) {
+ SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -664,6 +676,8 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// setting an interface and IP_MULTICAST_LOOP disabled on the receiving socket.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
TestSendMulticastReceiverNoLoop) {
+ SKIP_IF(!found_net_interfaces_);
+
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -716,6 +730,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// and both will receive data on it when bound to the ANY address.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
TestSendMulticastToTwoBoundToAny) {
+ SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
std::unique_ptr<FileDescriptor> receivers[2] = {
ASSERT_NO_ERRNO_AND_VALUE(NewSocket()),
@@ -782,6 +797,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// and both will receive data on it when bound to the multicast address.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
TestSendMulticastToTwoBoundToMulticastAddress) {
+ SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
std::unique_ptr<FileDescriptor> receivers[2] = {
ASSERT_NO_ERRNO_AND_VALUE(NewSocket()),
@@ -851,6 +867,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// multicast address, both will receive data.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
TestSendMulticastToTwoBoundToAnyAndMulticastAddress) {
+ SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
std::unique_ptr<FileDescriptor> receivers[2] = {
ASSERT_NO_ERRNO_AND_VALUE(NewSocket()),
@@ -924,6 +941,8 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// is not a multicast address.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
IpMulticastLoopbackFromAddr) {
+ SKIP_IF(!found_net_interfaces_);
+
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -991,9 +1010,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// interface, a multicast packet sent out uses the latter as its source address.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
IpMulticastLoopbackIfNicAndAddr) {
- // FIXME(b/137899561): Linux instance for syscall tests sometimes misses its
- // IPv4 address on eth0.
- SKIP_IF(!got_if_infos_);
+ SKIP_IF(!found_net_interfaces_);
// Create receiver, bind to ANY and join the multicast group.
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -1059,9 +1076,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// another interface.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
IpMulticastLoopbackBindToOneIfSetMcastIfToAnother) {
- // FIXME(b/137899561): Linux instance for syscall tests sometimes misses its
- // IPv4 address on eth0.
- SKIP_IF(!got_if_infos_);
+ SKIP_IF(!found_net_interfaces_);
// FIXME (b/137790511): When bound to one interface it is not possible to set
// IP_MULTICAST_IF to a different interface.
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h
index 10b90b1e0..0e9e70e8e 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h
@@ -29,9 +29,9 @@ class IPv4UDPUnboundExternalNetworkingSocketTest : public SimpleSocketTest {
IfAddrHelper if_helper_;
- // got_if_infos_ is set to false if SetUp() could not obtain all interface
- // infos that we need.
- bool got_if_infos_;
+ // found_net_interfaces_ is set to false if SetUp() could not obtain
+ // all interface infos that we need.
+ bool found_net_interfaces_;
// Interface infos.
int lo_if_idx_;
diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc
index 0cea7d11f..a6325a761 100644
--- a/test/syscalls/linux/tcp_socket.cc
+++ b/test/syscalls/linux/tcp_socket.cc
@@ -720,6 +720,30 @@ TEST_P(TcpSocketTest, TcpSCMPriority) {
ASSERT_EQ(cmsg, nullptr);
}
+TEST_P(TcpSocketTest, TimeWaitPollHUP) {
+ shutdown(s_, SHUT_RDWR);
+ ScopedThread t([&]() {
+ constexpr int kTimeout = 10000;
+ constexpr int16_t want_events = POLLHUP;
+ struct pollfd pfd = {
+ .fd = s_,
+ .events = want_events,
+ };
+ ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
+ });
+ shutdown(t_, SHUT_RDWR);
+ t.Join();
+ // At this point s_ should be in TIME-WAIT and polling for POLLHUP should
+ // return with 1 FD.
+ constexpr int kTimeout = 10000;
+ constexpr int16_t want_events = POLLHUP;
+ struct pollfd pfd = {
+ .fd = s_,
+ .events = want_events,
+ };
+ ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
+}
+
INSTANTIATE_TEST_SUITE_P(AllInetTests, TcpSocketTest,
::testing::Values(AF_INET, AF_INET6));