* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
*
* 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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include <cstdlib>
#include <string>
#include "user_common_func.h"
#include "utils.h"
#include "yr/yr.h"
using testing::HasSubstr;
class CollectiveTest : public testing::Test {
public:
CollectiveTest() {};
~CollectiveTest() {};
static void SetUpTestCase() {};
static void TearDownTestCase() {};
void SetUp()
{
YR::Config config;
config.mode = YR::Config::Mode::CLUSTER_MODE;
auto info = YR::Init(config);
std::cout << "job id: " << info.jobId << std::endl;
};
void TearDown()
{
YR::Finalize();
};
};
* @title: 试图创建非法groupName group
* @step: 1.创建CollectiveActor实例
* @step: 2.调用CollectiveActor::InitCollectiveGroup,在acto中创建非法group
* @step: 3.调用CreateCollectiveGroup,创建非法group
*
* @expect: 2.预期异常抛出
* @expect: 3.预期异常抛出
*/
TEST_F(CollectiveTest, InvalidGroupNameTest)
{
auto ins = YR::Instance(CollectiveActor::FactoryCreate).Invoke();
std::string groupName1 = "test@group1";
std::string groupName2 = "test/group1";
std::string groupName3 = "test-group1";
std::string groupName4 = "";
auto res = ins.Function(&CollectiveActor::InitCollectiveGroup).Invoke(groupName1, 0, 1);
EXPECT_THROW_WITH_CODE_AND_MSG(YR::Get(res), 2002, "groupName is invalid. It should match the regex");
res = ins.Function(&CollectiveActor::InitCollectiveGroup).Invoke(groupName2, 0, 1);
EXPECT_THROW_WITH_CODE_AND_MSG(YR::Get(res), 2002, "groupName is invalid. It should match the regex");
res = ins.Function(&CollectiveActor::InitCollectiveGroup).Invoke(groupName4, 0, 1);
EXPECT_THROW_WITH_CODE_AND_MSG(YR::Get(res), 2002, "groupName is invalid. It should match the regex");
YR::Collective::CollectiveGroupSpec spec1{
.worldSize = 1,
.groupName = groupName1,
};
EXPECT_THROW_WITH_CODE_AND_MSG(YR::Collective::CreateCollectiveGroup(spec1, {ins.GetInstanceId()}, {0}), 1001,
"groupName is invalid. It should match the regex");
YR::Collective::CollectiveGroupSpec spec2{
.worldSize = 1,
.groupName = groupName2,
};
EXPECT_THROW_WITH_CODE_AND_MSG(YR::Collective::CreateCollectiveGroup(spec2, {ins.GetInstanceId()}, {0}), 1001,
"groupName is invalid. It should match the regex");
YR::Collective::CollectiveGroupSpec spec4{
.worldSize = 1,
.groupName = groupName4,
};
EXPECT_THROW_WITH_CODE_AND_MSG(YR::Collective::CreateCollectiveGroup(spec4, {ins.GetInstanceId()}, {0}), 1001,
"groupName is invalid. It should match the regex");
YR::Collective::CollectiveGroupSpec spec3{
.worldSize = 1,
.groupName = groupName3,
};
YR::Collective::DestroyCollectiveGroup(groupName3);
YR::Collective::CreateCollectiveGroup(spec3, {ins.GetInstanceId()}, {0});
YR::Collective::DestroyCollectiveGroup(groupName3);
}
* @title: InitCollectiveGroup函数调用成功
* @step: 1.创建CollectiveActor实例
* @step: 2.调用CollectiveActor::InitCollectiveGroup,在acto中创建对应group
* @step: 3.调用CollectiveActor::Compute sum 函数,传入{1, 2, 3, 4, 5}
* @step: 4.调用CollectiveActor::Compute min 函数,传入{1, 2, 3, 4, 5}, {5, 4, 3, 2, 1}
* @step: 5.调用CollectiveActor::Compute max 函数,传入{5, 4, 3, 2, 1}, {1, 2, 3, 4, 5}
* @step: 6.调用CollectiveActor::Compute product 函数,传入{5, 4, 3, 2, 1}, {1, 2, 3, 4, 5}
*
* @expect: 3.预期无异常抛出,返回值为两个actor中各项之和,30
* @expect: 4.预期无异常抛出,返回值为两个actor中各项取最小值之和,9
* @expect: 5.预期无异常抛出,返回值为两个actor中各项取最大值之和,21
* @expect: 6.预期无异常抛出,返回值为两个actor中各项相乘之和,35
*/
TEST_F(CollectiveTest, InitGroupInActorTest)
{
auto ins1 = YR::Instance(CollectiveActor::FactoryCreate).Invoke();
auto ins2 = YR::Instance(CollectiveActor::FactoryCreate).Invoke();
std::string groupName = "test-group1";
YR::Collective::DestroyCollectiveGroup(groupName);
ins1.Function(&CollectiveActor::InitCollectiveGroup).Invoke(groupName, 0, 2);
auto ret = ins2.Function(&CollectiveActor::InitCollectiveGroup).Invoke(groupName, 1, 2);
YR::Get(ret);
std::vector<int> input = {1, 2, 3, 4, 5};
auto res1 =
ins1.Function(&CollectiveActor::Compute).Invoke(input, groupName, static_cast<uint8_t>(YR::ReduceOp::SUM));
auto res2 =
ins2.Function(&CollectiveActor::Compute).Invoke(input, groupName, static_cast<uint8_t>(YR::ReduceOp::SUM));
EXPECT_EQ(*YR::Get(res1), 30);
EXPECT_EQ(*YR::Get(res2), 30);
input = {1, 2, 3, 4, 5};
std::vector<int> input2 = {5, 4, 3, 2, 1};
res1 = ins1.Function(&CollectiveActor::Compute).Invoke(input, groupName, static_cast<uint8_t>(YR::ReduceOp::MIN));
res2 = ins2.Function(&CollectiveActor::Compute).Invoke(input2, groupName, static_cast<uint8_t>(YR::ReduceOp::MIN));
EXPECT_EQ(*YR::Get(res1), 9);
EXPECT_EQ(*YR::Get(res2), 9);
res1 = ins1.Function(&CollectiveActor::Compute).Invoke(input2, groupName, static_cast<uint8_t>(YR::ReduceOp::MAX));
res2 = ins2.Function(&CollectiveActor::Compute).Invoke(input, groupName, static_cast<uint8_t>(YR::ReduceOp::MAX));
EXPECT_EQ(*YR::Get(res1), 21);
EXPECT_EQ(*YR::Get(res2), 21);
res1 =
ins1.Function(&CollectiveActor::Compute).Invoke(input2, groupName, static_cast<uint8_t>(YR::ReduceOp::PRODUCT));
res2 =
ins2.Function(&CollectiveActor::Compute).Invoke(input, groupName, static_cast<uint8_t>(YR::ReduceOp::PRODUCT));
EXPECT_EQ(*YR::Get(res1), 35);
EXPECT_EQ(*YR::Get(res2), 35);
res1 = ins1.Function(&CollectiveActor::DestroyCollectiveGroup).Invoke(groupName);
res2 = ins2.Function(&CollectiveActor::DestroyCollectiveGroup).Invoke(groupName);
YR::Get(res1);
YR::Get(res2);
}
* @title: CreateCollectiveGroup函数调用成功
* @step: 1.创建CollectiveActor实例
* @step: 2.调用CreateCollectiveGroup,在driver中创建对应group
* @step: 3.调用CollectiveActor::Compute函数,传入{1, 2, 3, 4}
* @step: 4.调用CollectiveActor::ComputeDouble函数,传入{1.1, 2.2, 3.3, 4.4}
* @step: 5.调用DestroyCollectiveGroup,清理CollectiveGroup
*
* @expect: 预期无异常抛出,返回值为各个actor中各项之和,40 和 44
*/
TEST_F(CollectiveTest, CreateGroupInDriverTest)
{
std::vector<YR::NamedInstance<CollectiveActor>> instances;
std::vector<std::string> instanceIDs;
for (int i = 0; i < 4; ++i) {
auto ins = YR::Instance(CollectiveActor::FactoryCreate).Invoke();
instances.push_back(ins);
instanceIDs.push_back(ins.GetInstanceId());
}
std::string groupName = "test-group2";
YR::Collective::DestroyCollectiveGroup(groupName);
YR::Collective::CollectiveGroupSpec spec{
.worldSize = 4,
.groupName = groupName,
};
YR::Collective::CreateCollectiveGroup(spec, instanceIDs, {0, 1, 2, 3});
std::vector<int> input = {1, 2, 3, 4};
std::vector<YR::ObjectRef<int>> res;
for (int i = 0; i < 4; ++i) {
res.push_back(instances[i]
.Function(&CollectiveActor::Compute)
.Invoke(input, groupName, static_cast<uint8_t>(YR::ReduceOp::SUM)));
}
for (int i = 0; i < 4; ++i) {
EXPECT_EQ(*YR::Get(res[i]), 40);
}
std::vector<double> input2 = {1.1, 2.2, 3.3, 4.4};
std::vector<YR::ObjectRef<double>> res2;
for (int i = 0; i < 4; ++i) {
res2.push_back(instances[i]
.Function(&CollectiveActor::ComputeDouble)
.Invoke(input2, groupName, static_cast<uint8_t>(YR::ReduceOp::SUM)));
}
for (int i = 0; i < 4; ++i) {
EXPECT_EQ(*YR::Get(res2[i]), 44);
}
YR::Collective::DestroyCollectiveGroup(groupName);
}
* @title: Reduce函数调用成功
* @step: 1.创建CollectiveActor实例
* @step: 2.调用CreateCollectiveGroup,在driver中创建对应group
* @step: 3.调用CollectiveActor::Reduce函数,传入{1, 2, 3, 4}
* @step: 5.调用DestroyCollectiveGroup,清理CollectiveGroup
*
* @expect: 预期无异常抛出,rank 0的实例返回值为各个actor中各项之和,40
*/
TEST_F(CollectiveTest, ReduceTest)
{
std::vector<YR::NamedInstance<CollectiveActor>> instances;
std::vector<std::string> instanceIDs;
for (int i = 0; i < 4; ++i) {
auto ins = YR::Instance(CollectiveActor::FactoryCreate).Invoke();
instances.push_back(ins);
instanceIDs.push_back(ins.GetInstanceId());
}
std::string groupName = "test-group2";
YR::Collective::DestroyCollectiveGroup(groupName);
YR::Collective::CollectiveGroupSpec spec{
.worldSize = 4,
.groupName = groupName,
};
YR::Collective::CreateCollectiveGroup(spec, instanceIDs, {0, 1, 2, 3});
std::vector<int> input = {1, 2, 3, 4};
std::vector<YR::ObjectRef<int>> res;
for (int i = 0; i < 4; ++i) {
res.push_back(instances[i]
.Function(&CollectiveActor::Reduce)
.Invoke(input, groupName, static_cast<uint8_t>(YR::ReduceOp::SUM)));
}
EXPECT_EQ(*YR::Get(res[0]), 40);
YR::Collective::DestroyCollectiveGroup(groupName);
}
* @title: Send Recv函数调用成功
* @step: 1.创建CollectiveActor实例
* @step: 2.调用CreateCollectiveGroup,在driver中创建对应group
* @step: 3.调用Send函数,传入{1, 2, 3, 4},另一个actor调用Recv
*
* @expect: 预期无异常抛出,返回值为各项之和,10
*/
TEST_F(CollectiveTest, SendRecvTest)
{
auto ins1 = YR::Instance(CollectiveActor::FactoryCreate).Invoke();
auto ins2 = YR::Instance(CollectiveActor::FactoryCreate).Invoke();
std::string groupName = "test-group3";
YR::Collective::DestroyCollectiveGroup(groupName);
YR::Collective::CollectiveGroupSpec spec{
.worldSize = 2,
.groupName = groupName,
};
YR::Collective::CreateCollectiveGroup(spec, {ins1.GetInstanceId(), ins2.GetInstanceId()}, {0, 1});
std::vector<int> input = {1, 2, 3, 4};
ins1.Function(&CollectiveActor::Send).Invoke(groupName, input, 1, 1234);
auto ret = ins2.Function(&CollectiveActor::Recv).Invoke(groupName, 0, 1234, 4);
EXPECT_EQ(*YR::Get(ret), 10);
std::vector<int> input2 = {2, 2, 3, 4, 5};
ret = ins1.Function(&CollectiveActor::Recv).Invoke(groupName, 1, 1234, 5);
ins2.Function(&CollectiveActor::Send).Invoke(groupName, input2, 0, 1234);
EXPECT_EQ(*YR::Get(ret), 16);
YR::Collective::DestroyCollectiveGroup(groupName);
}
* @title: AllGather函数调用成功
* @step: 1.创建CollectiveActor实例
* @step: 2.调用CreateCollectiveGroup,在driver中创建对应group
* @step: 3.调用CollectiveActor::AllGather函数,传入{1, 2, 3, 4}
*
* @expect: 预期无异常抛出,返回值为各个actor中各项之和40, 总数 16个数据
*/
TEST_F(CollectiveTest, AllGatherTest)
{
std::vector<YR::NamedInstance<CollectiveActor>> instances;
std::vector<std::string> instanceIDs;
for (int i = 0; i < 4; ++i) {
auto ins = YR::Instance(CollectiveActor::FactoryCreate).Invoke();
instances.push_back(ins);
instanceIDs.push_back(ins.GetInstanceId());
}
std::string groupName = "test-group4";
YR::Collective::DestroyCollectiveGroup(groupName);
YR::Collective::CollectiveGroupSpec spec{
.worldSize = 4,
.groupName = groupName,
};
YR::Collective::CreateCollectiveGroup(spec, instanceIDs, {0, 1, 2, 3});
std::vector<int> input = {1, 2, 3, 4};
std::vector<YR::ObjectRef<std::pair<int, int>>> res;
for (int i = 0; i < 4; ++i) {
res.push_back(instances[i].Function(&CollectiveActor::AllGather).Invoke(groupName, input));
}
for (int i = 0; i < 4; ++i) {
EXPECT_EQ(YR::Get(res[i])->first, 40);
EXPECT_EQ(YR::Get(res[i])->second, 16);
}
YR::Collective::DestroyCollectiveGroup(groupName);
}
* @title: Broadcast函数调用成功
* @step: 1.创建CollectiveActor实例
* @step: 2.调用CreateCollectiveGroup,在driver中创建对应group
* @step: 3.调用CollectiveActor::Broadcast,ins1 传入{1, 2, 3, 4, 5}
*
* @expect: 预期无异常抛出,返回值为各个actor中各项之和15
*/
TEST_F(CollectiveTest, BroadcastTest)
{
std::vector<YR::NamedInstance<CollectiveActor>> instances;
std::vector<std::string> instanceIDs;
for (int i = 0; i < 4; ++i) {
auto ins = YR::Instance(CollectiveActor::FactoryCreate).Invoke();
instances.push_back(ins);
instanceIDs.push_back(ins.GetInstanceId());
}
std::string groupName = "test-group5";
YR::Collective::DestroyCollectiveGroup(groupName);
YR::Collective::CollectiveGroupSpec spec{
.worldSize = 4,
.groupName = groupName,
.timeout = 10000,
};
YR::Collective::CreateCollectiveGroup(spec, instanceIDs, {0, 1, 2, 3});
std::vector<int> input = {1, 2, 3, 4, 5};
std::vector<YR::ObjectRef<int>> res;
for (int i = 0; i < 4; ++i) {
res.push_back(instances[i].Function(&CollectiveActor::Broadcast).Invoke(groupName, input, 0));
}
for (int i = 0; i < 4; ++i) {
EXPECT_EQ(*YR::Get(res[i]), 15);
}
YR::Collective::DestroyCollectiveGroup(groupName);
}
* @title: Scatter函数调用成功
* @step: 1.创建CollectiveActor实例
* @step: 2.调用CreateCollectiveGroup,在driver中创建对应group
* @step: 3.调用CollectiveActor::Scatter,ins1 传入{1, 2, 3, 4, 5}
* @step: 4.调用CollectiveActor::Scatter,ins1 传入{1, 1, 2, 2, 3, 3, 4, 4, 5, 5}
*
* @expect: 预期无异常抛出,返回值为各个actor中各自分到一项
*/
TEST_F(CollectiveTest, ScatterTest)
{
std::vector<YR::NamedInstance<CollectiveActor>> instances;
std::vector<std::string> instanceIDs;
for (int i = 0; i < 4; ++i) {
auto ins = YR::Instance(CollectiveActor::FactoryCreate).Invoke();
instances.push_back(ins);
instanceIDs.push_back(ins.GetInstanceId());
}
std::string groupName = "test-group6";
YR::Collective::DestroyCollectiveGroup(groupName);
YR::Collective::CollectiveGroupSpec spec{
.worldSize = 4,
.groupName = groupName,
.timeout = 1000,
};
YR::Collective::CreateCollectiveGroup(spec, instanceIDs, {0, 1, 2, 3});
std::vector<std::vector<int>> input = {{1}, {2}, {3}, {4}};
std::vector<YR::ObjectRef<int>> res;
for (int i = 0; i < 4; ++i) {
res.push_back(instances[i].Function(&CollectiveActor::Scatter).Invoke(groupName, input, 0, 1));
}
for (int i = 0; i < 4; ++i) {
EXPECT_EQ(*YR::Get(res[i]), i + 1);
}
input = {{1, 1}, {2, 2}, {3, 3}, {4, 4}};
res = {};
for (int i = 0; i < 4; ++i) {
res.push_back(instances[i].Function(&CollectiveActor::Scatter).Invoke(groupName, input, 0, 2));
}
for (int i = 0; i < 4; ++i) {
EXPECT_EQ(*YR::Get(res[i]), (i + 1) * 2);
}
YR::Collective::DestroyCollectiveGroup(groupName);
}