单元测试框架gtest学习(三)—— 事件机制

news2024/11/22 3:43:31

前言

上节我们学习了gtest的各种宏断言

单元测试框架gtest学习(二)—— 认识断言-CSDN博客

本节我们介绍gtets的事件机制

虽然 Google Test 的核心是用来编写单元测试和断言的,但它也允许在测试执行过程中进行事件的钩取和自定义,其主要用于处理测试生命周期中的各种操作,如测试的初始化、清理、事件通知等

简单来讲,事件机制的作用就是帮我们进行一些测试前的初始化工作和测试后的清理工作

介绍与引出

我们直接上代码进行说明,如下我们实现了一个简单的线程安全队列

// safequeue.h
#ifndef SAFEQUEUE_H
#define SAFEQUEUE_H

#include <mutex>
#include <queue>
#include <stdexcept>

template <typename E>
class Queue {
 public:
    Queue() = default; // 在此定义默认构造函数
    // 入队操作
    void Enqueue(const E &element) {
        std::lock_guard<std::mutex> lock(mtx_);
        queue_.emplace(element);
    }
    // 出队操作
    E Dequeue() {
        std::lock_guard<std::mutex> lock(mtx_);
        if (!queue_.empty()) {
            E data = queue_.front();
            queue_.pop();
            return data;
        }
        throw std::runtime_error("Queue is empty");
    }
    // 获取队列容量
    size_t size() const { return queue_.size(); }

 private:
    std::queue<E> queue_;
    std::mutex mtx_;
};

#endif // SAFEQUEUE_H

代码写完之后,接下来我们自然想测试一下我们的队列入队操作、出队操作以及队列容量的获取操作这些功能是否符合我们的预期

因此,我们会写出以下测试案例

TEST(QueueTest, DeQueueTest) {
    Queue<int> queue;
    queue.Enqueue(1);
    queue.Enqueue(2);
    queue.Enqueue(3);

    for (int i = 1; i <= 3; ++i) {
        try {
            int n = queue.Dequeue();
            // 测试是否出队成功,并且出队的元素符合预期
            EXPECT_EQ(n, i);
        } catch (const std::runtime_error &e) {
            // 如果抛出异常,判断抛出异常的原因是否是因为队列为空
            EXPECT_STREQ(e.what(), "Queue is empty");
        }
    }
}

TEST(QueueTest, QueueSize) {
    Queue<int> queue;
    queue.Enqueue(1);
    queue.Enqueue(2);
    queue.Enqueue(3);

    EXPECT_EQ(queue.size(), 3);
}

编译运行,查看结果

从结果上看,

  • 我们进行了两个测试案例
    • 一个用于测试队列的出队操作
    • 另一个用于测试队列的容量是否符合预期
  • 这两个测试案例的被测对象都同属于一个,即QueueTest

由此,我们引出两个概念

  •  test suite:直译为测试套件,在上述代码中指QueueTest
  •  test case:直译为测试案例,上述代码中指DeQueueTest和QueueSize

因此,直观上看, test suite可以理解为我们被测的对象是谁,而 test case可以理解我们要测试的功能是哪些,其中这些功能是属于上述被测对象的

关于测试套件和测试案例的概念点到为止,接下来我们将目光继续放在代码上

观察上述两个功能的测试

  • 我们在对每个功能进行测试前,都对队列进行了入队操作
  • 这个入队操作从另一个角度可以理解为测试前的初始化操作(毕竟只有队列中有数据才能进行出队和容量测试) 

显而易见的,当我们的测试功能越来越多时,这种类同的初始化工作每次都要写一遍显然是在浪费时间和效率,同样的道理,在测试结束之后我们也是需要进行清理工作的,只是恰好我们这里的清理工作每次都由析构函数做了

因此,对于一些测试而言,我们需要在每个测试的前后进行一些初始化操作和清理工作,那么问题来了,有没有一种办法能够一次性将这些初始化和清理工作完成,而不必我们每次都要在写测试案例的手动进行处理呢?

这就引出了我们今天要说的事件机制,直观上看,事件机制的作用好像是构造函数和析构函数的作用

事件机制分类

事件机制分为三类:

1. 全局的,也就是在所有案例执行前进行初始化操作,在所有案例结束后进行清理操作。

2. TestSuite级别的,在某一批案例中第一个案例前进行初始化操作,最后一个案例执行后进行清理操作。

3. TestCase级别的,每个TestCase前后都进行一次初始化和清理。

全局事件

全局事件主要用于对全局环境的初始化和清理

要实现全局事件,必须写一个类,继承testing::Environment类,实现里面的SetUp和TearDown方法,其中

1. SetUp()方法在所有案例执行前执行

2. TearDown()方法在所有案例执行后执行

#if 1
class MyTestEnvironment : public testing::Environment {
 public:
    // 在所有测试开始之前调用
    void SetUp() override {
        std::cout << "Global SetUp: 初始化全局事件\n";
        // 在这里进行全局初始化,比如资源分配
    }

    // 在所有测试完成之后调用
    void TearDown() override {
        std::cout << "Global TearDown: 清理全局事件\n";
        // 在这里进行全局清理,比如释放资源
    }
};

// 测试夹具类
class QueueTest : public ::testing::Test {
 protected:
    void SetUp() override {
        // 在每个测试用例前执行
        std::cout << "Test SetUp: 准备测试环境\n";
    }

    void TearDown() override {
        // 在每个测试用例后执行
        std::cout << "Test TearDown: 清理测试环境\n";
    }
};

// 定义你的测试用例
TEST_F(QueueTest, IsEmptyInitially) {
    std::cout << "执行 IsEmptyInitially 测试\n";
    // 这里可以写你的测试逻辑
}

TEST_F(QueueTest, DequeueWorks) {
    std::cout << "执行 DequeueWorks 测试\n";
    // 这里可以写你的测试逻辑
}
#endif

我们还需要告诉gtest添加这个全局事件,我们需要在main函数中通过testing::AddGlobalTestEnvironment方法将事件挂进来,也就是说,我们可以写很多个这样的类,然后将他们的事件都挂上去。

int main(int argc, char **argv) {
    // 注册我们自定义的全局环境
    testing::AddGlobalTestEnvironment(new MyTestEnvironment);

    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

另外,我们观察上述代码,我们的断言宏已经不再是TEST,而是TEST_F了,这一点需要注意,如果我们使用事件机制进行测试,则需要使用TEST_F,而不能使用TEST宏 

编译运行

TestSuite事件

同理TestSuite事件也需要我们写一个类,继承testing::Test,然后实现两个静态方法

1. SetUpTestSuite() 方法在第一个TestCase之前执行

2. TearDownTestSuite() 方法在最后一个TestCase之后执行


#if 1
class QueueTest : public testing::Test {
 protected:
    // 这是每个测试用例的初始化
    void SetUp() override {
        std::cout << "  初始化每个TestCase环境...." << std::endl;
        q1_.Enqueue(1);
        q2_.Enqueue(2);
        q2_.Enqueue(3);
    }

    // 这是每个测试用例的清理
    void TearDown() override {
        std::cout << "  清理每个TestCase环境...." << std::endl;
    }

    // 用于测试的队列对象
    Queue<int> q0_;
    Queue<int> q1_;
    Queue<int> q2_;

 public:
    // 静态方法,用于测试套件级别的初始化
    static void SetUpTestSuite() {
        std::cout << "初始化整个TestSuite环境...." << std::endl;
    }

    // 静态方法,用于测试套件级别的清理
    static void TearDownTestSuite() {
        std::cout << "清理整个TestSuite环境...." << std::endl;
    }
};

TEST_F(QueueTest, IsEmptyInitially) { EXPECT_EQ(q0_.size(), 0); }

TEST_F(QueueTest, DequeueWorks) {
    try {
        int n = q0_.Dequeue();
        FAIL() << "Expected exception for empty queue";
    } catch (const std::runtime_error &e) {
        EXPECT_STREQ(e.what(), "Queue is empty");
    }

    int n = q1_.Dequeue();
    EXPECT_EQ(n, 1);
    EXPECT_EQ(q1_.size(), 0);

    n = q2_.Dequeue();
    EXPECT_EQ(n, 2);
    EXPECT_EQ(q2_.size(), 1);

    n = q2_.Dequeue();
    EXPECT_EQ(n, 3);
    EXPECT_EQ(q2_.size(), 0);
}
#endif

编译运行

TestCase事件

TestCase事件是挂在每个案例执行前后的,实现方式和上面的几乎一样,不过需要实现的是SetUp方法和TearDown方法:

1. SetUp()方法在每个TestCase之前执行

2. TearDown()方法在每个TestCase之后执行

仍旧使用上述案例代码,观察运行结果发现

实战

接下来我们写一个实际案例来运用上述事件机制

 首先我们实现了一个简单的线程池,然后我们来测试这个线程池的运行是否可以正常运行(注意这里是测试功能是否符合预期,而不是进行压测)

//threadPool.h
#ifndef THREADPOOL_H
#define THREADPOOL_H

#include <atomic>
#include <condition_variable>
#include <functional>
#include <future>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>

class ThreadPool {
 public:
    ThreadPool(int size = std::thread::hardware_concurrency())
        : pool_size_(size), isStop_(false) {
        for (int i = 0; i < pool_size_; ++i) {
            // threads_.push_back(std::thread(&ThreadPool::worker,this));
            threads_.emplace_back([this]() { worker(); });
        }
    }

    ~ThreadPool() { ShutDown(); }

    void ShutDown() {
        isStop_ = true;

        not_empty_cond_.notify_all();

        for (auto &thread : threads_) {
            if (thread.joinable())
                thread.join();
        }
    }

    template <typename F, typename... Args>
    auto Submit(F &&f, Args &&...args) -> std::future<decltype(f(args...))> {
        using func_type = decltype(f(args...));
        auto task_ptr = std::make_shared<std::packaged_task<func_type()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...));
        std::future<func_type> func_future = task_ptr->get_future();

        {
            std::lock_guard<std::mutex> lock(mtx_);
            if (isStop_)
                throw std::runtime_error("threadpool has stop!!!");
            task_queue_.emplace([task_ptr]() { (*task_ptr)(); });
        }
        not_empty_cond_.notify_one();

        return func_future;
    }

 private:
    std::atomic_bool isStop_;
    int pool_size_;

    using Task = std::function<void()>;

    std::vector<std::thread> threads_;
    std::queue<Task> task_queue_;

    std::mutex mtx_;
    std::condition_variable not_empty_cond_;

    void worker() {
        while (1) {
            std::unique_lock<std::mutex> lock(mtx_);

            not_empty_cond_.wait(
                lock, [this]() { return isStop_ || !task_queue_.empty(); });

            if (isStop_ && task_queue_.empty())
                return;

            Task task = task_queue_.front();
            task_queue_.pop();

            lock.unlock();

            task();
        }
        return;
    }
};

#endif // THREADPOOL_H

接下来我们写了两个函数来测试线程池是否运行正常

int add(const int &num1, const int &num2) { return num1 + num2; }

如果不使用gtest,我们对上述函数的测试代码是这样的

void addTest() {
    ThreadPool pool;
    auto task1 = pool.Submit(add, 1, 2);
    auto task2 = pool.Submit(add, 3, 4);
    int sum = task1.get() + task2.get();
    std::cout << "sum=" << sum << std::endl;
}

 显然,如果上述打印输出的结果sum等于10,说明符合预期,测试通过


// 矩阵乘法
std::vector<std::vector<int>>
MatrixMultiply(const std::vector<std::vector<int>> &A,
               const std::vector<std::vector<int>> &B, ThreadPool &pool) {
    size_t m = A.size();    // A的行数
    size_t n = A[0].size(); // A的列数
    size_t p = B[0].size(); // B的列数

    std::vector<std::vector<int>> C(m, std::vector<int>(p, 0)); // 结果矩阵

    std::vector<std::future<void>> futures;

    // 为结果矩阵中的每个元素提交一个计算任务
    for (size_t i = 0; i < m; ++i) {
        for (size_t j = 0; j < p; ++j) {
            futures.push_back(pool.Submit([i, j, &A, &B, &C, n]() {
                int sum = 0;
                for (size_t k = 0; k < n; ++k) {
                    sum += A[i][k] * B[k][j];
                }
                C[i][j] = sum;
            }));
        }
    }

    // 等待所有的任务完成
    for (auto &future : futures) {
        future.get();
    }

    return C;
}

同理,对于上述矩阵乘法,如果我们不使用gtest来测试,代码应该是这样的

std::ostream &operator<<(std::ostream &os,
                         std::vector<std::vector<int>> &nums) {
    int col = 0;
    int m = nums.size();
    int n = nums[0].size();
    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {
            os << nums[i][j] << " ";
            col++;
            if (col % n == 0)
                os << "\n";
        }
    }
    return os;
}
 
void multiTest() {
    // 定义两个矩阵 A 和 B
    std::vector<std::vector<int>> A = {{1, 2, 3}, {4, 5, 6}};
    std::vector<std::vector<int>> B = {{7, 8}, {9, 10}, {11, 12}};
 
    ThreadPool pool(A.size() * B[0].size());
    // 计算矩阵 A 和 B 的乘积
    std::vector<std::vector<int>> C =
        MatrixMultiply(A, B, pool); // 使用 *pool_ 来解引用智能指针
 
    // std::vector<std::vector<int>> expected = {{58, 64}, {139, 154}};
    std::cout << C;
}

其实就是很简单的,把函数运行一下,然后打印输出结果,观察运行结果是否符合预期

只是在gtest中使用各种断言宏来帮助我们判断,而不是打印出来然后肉眼我们判断

好,接下来我们使用gtest来进行测试

既然我们要进行两个功能的测试(加法功能和矩阵乘法功能),每个功能在在测试之前都需要对线程池进行初始化,那么我们使用上述事件机制,进行一些初始化工作

class ThreadPoolTest : public ::testing::Test {
 public:
    void SetUp() override {
        std::cout << ">>>> init threadpool...." << std::endl;
        pool_ = std::make_unique<ThreadPool>(4); // 假设线程池初始化为4个线程
    }
    void TearDown() override {
        std::cout << ">>>> shutdown threadPool...." << std::endl;
        pool_->ShutDown();
    }
    std::unique_ptr<ThreadPool> pool_;
};

这样我们就不用在每个测试案例进行初始化工作了,只需要关注测试代码的逻辑编写即可

以下是加法功能的测试代码 

TEST_F(ThreadPoolTest, TestThreadPoolSubmit) {
    // 提交加法任务
    auto task1 = pool_->Submit(add, 1, 2); // 1 + 2
    auto task2 = pool_->Submit(add, 3, 4); // 3 + 4

    // 获取任务的结果
    int sum = task1.get() + task2.get(); // 获取两个任务的结果

    // 验证最终结果
    EXPECT_EQ(sum, 10);
}

以下是矩阵乘法的测试代码 

TEST_F(ThreadPoolTest, TestMatrixMultiplication) {
    // 定义两个矩阵 A 和 B
    std::vector<std::vector<int>> A = {{1, 2, 3}, {4, 5, 6}};
    std::vector<std::vector<int>> B = {{7, 8}, {9, 10}, {11, 12}};

    // 计算矩阵 A 和 B 的乘积
    std::vector<std::vector<int>> C =
        MatrixMultiply(A, B, *pool_); // 使用 *pool_ 来解引用智能指针

    // 验证结果矩阵 C
    std::vector<std::vector<int>> expected = {{58, 64}, {139, 154}};
    EXPECT_EQ(C, expected);
}

完整代码如下

#include "threadPool.h"
#include <gtest/gtest.h>

#if 1
class ThreadPoolTest : public ::testing::Test {
 public:
    void SetUp() override {
        std::cout << ">>>> init threadpool...." << std::endl;
        pool_ = std::make_unique<ThreadPool>(4); // 假设线程池初始化为4个线程
    }
    void TearDown() override {
        std::cout << ">>>> shutdown threadPool...." << std::endl;
        pool_->ShutDown();
    }
    std::unique_ptr<ThreadPool> pool_;
};

// 矩阵乘法
std::vector<std::vector<int>>
MatrixMultiply(const std::vector<std::vector<int>> &A,
               const std::vector<std::vector<int>> &B, ThreadPool &pool) {
    size_t m = A.size();    // A的行数
    size_t n = A[0].size(); // A的列数
    size_t p = B[0].size(); // B的列数

    std::vector<std::vector<int>> C(m, std::vector<int>(p, 0)); // 结果矩阵

    std::vector<std::future<void>> futures;

    // 为结果矩阵中的每个元素提交一个计算任务
    for (size_t i = 0; i < m; ++i) {
        for (size_t j = 0; j < p; ++j) {
            futures.push_back(pool.Submit([i, j, &A, &B, &C, n]() {
                int sum = 0;
                for (size_t k = 0; k < n; ++k) {
                    sum += A[i][k] * B[k][j];
                }
                C[i][j] = sum;
            }));
        }
    }

    // 等待所有的任务完成
    for (auto &future : futures) {
        future.get();
    }

    return C;
}
// 验证矩阵乘法结果是否正确
TEST_F(ThreadPoolTest, TestMatrixMultiplication) {
    // 定义两个矩阵 A 和 B
    std::vector<std::vector<int>> A = {{1, 2, 3}, {4, 5, 6}};
    std::vector<std::vector<int>> B = {{7, 8}, {9, 10}, {11, 12}};

    // 计算矩阵 A 和 B 的乘积
    std::vector<std::vector<int>> C =
        MatrixMultiply(A, B, *pool_); // 使用 *pool_ 来解引用智能指针

    // 验证结果矩阵 C
    std::vector<std::vector<int>> expected = {{58, 64}, {139, 154}};
    EXPECT_EQ(C, expected);
}

int add(const int &num1, const int &num2) { return num1 + num2; }
TEST_F(ThreadPoolTest, TestThreadPoolSubmit) {
    // 提交加法任务
    auto task1 = pool_->Submit(add, 1, 2); // 1 + 2
    auto task2 = pool_->Submit(add, 3, 4); // 3 + 4

    // 获取任务的结果
    int sum = task1.get() + task2.get(); // 获取两个任务的结果

    // 验证最终结果
    EXPECT_EQ(sum, 10);
}
#endif

#if 1
int main(int argc, char **argv) {
    // testing::AddGlobalTestEnvironment(new FooEnvironment);
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
#endif

运行结果如下所示

[ 11:08上午 ]  [ pcl@robot:~/projects/myPro/threadPool/test(main✗) ]
 $ g++ ./gTest.cc -o gtest -std=c++14 -lgtest -lpthread
[ 11:08上午 ]  [ pcl@robot:~/projects/myPro/threadPool/test(main✗) ]
 $ ./gtest
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from ThreadPoolTest
[ RUN      ] ThreadPoolTest.TestMatrixMultiplication
>>>> init threadpool....
>>>> shutdown threadPool....
[       OK ] ThreadPoolTest.TestMatrixMultiplication (3 ms)
[ RUN      ] ThreadPoolTest.TestThreadPoolSubmit
>>>> init threadpool....
>>>> shutdown threadPool....
[       OK ] ThreadPoolTest.TestThreadPoolSubmit (1 ms)
[----------] 2 tests from ThreadPoolTest (4 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (5 ms total)
[  PASSED  ] 2 tests.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2245072.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity Inspector窗口可编辑的脚本变量

Inspector可编辑的脚本变量 默认会显示的变量 在Unity中&#xff0c;为了方便我们进行一些属性的设置及调试&#xff0c;我们所写的公有基础数据类型会显示在Inspector之中&#xff0c;我们可以对他进行设置来更改它的取值。 显示私有变量 在有些情况下&#xff0c;设计代码…

力扣 LeetCode 110. 平衡二叉树(Day8:二叉树)

解题思路&#xff1a; 等于 -1 时&#xff0c;直接 return -1 class Solution {public boolean isBalanced(TreeNode root) {return getHeight(root) ! -1;}public int getHeight(TreeNode root) {if (root null) return 0;int leftDepth getHeight(root.left);if (leftDep…

unity3d————基础篇小项目(设置界面)

代码示例&#xff1a; 设置界面 using System.Collections; using System.Collections.Generic; using UnityEngine;public class SettingPanel : BasePanel<SettingPanel> {public UIButton btnClose;public UISlider sliderMusic;public UISlider sliderSound;public…

商用密码产品认证名录说明

《商用密码产品认证目录》是为贯彻落实《中华人民共和国密码法》&#xff0c;进一步健全完善商用密码产品认证体系&#xff0c;更好满足商用密码产业发展需要&#xff0c;根据《国家密码管理局 市场监管总局关于调整商用密码产品管理方式的公告》《市场监管总局 国家密码管理局…

从零做一个遥控玩具,需要学什么

遥控玩具编程是一个充满乐趣和挑战的领域&#xff0c;它完美地结合了硬件控制和软件编程。记得小时候拿着遥控器操控玩具汽车&#xff0c;总觉得神奇。如今站在程序员的视角&#xff0c;终于明白这背后是怎样的技术在支撑。 这些有趣的遥控玩具&#xff0c;其核心都是基于单片…

java的web项目如何连接数据库

mysql-connector.jar 是一个Java库文件&#xff0c;它提供了Java应用程序与MySQL数据库进行交互的接口 首先准备好这个文件&#xff0c;没有的话在网上搜索 将它放在这个位置&#xff0c;MySQL连接配置就好了&#xff0c; 如果拖不进去&#xff0c;右击项目名称&#xff0c;…

视频修复技术和实时在线处理

什么是视频修复&#xff1f; 视频修复技术的目标是填补视频中的缺失部分&#xff0c;使视频内容连贯合理。这项技术在对象移除、视频修复和视频补全等领域有着广泛的应用。传统方法通常需要处理整个视频&#xff0c;导致处理速度慢&#xff0c;难以满足实时处理的需求。 技术发…

OpenTelemetry 赋能DevOps流程的可观测性革命

原作者&#xff1a;天颇 原出处&#xff1a;微信公众号 乘云数字DATABUFF 原文地址&#xff1a;https://mp.weixin.qq.com/s/D_f31EBtLu7Rr0gahuF-bw 引言 在当今快节奏的软件开发和运维环境中&#xff0c;DevOps 已经成为主流&#xff0c;它通过整合开发和运维流程&#xff0…

计算机视觉算法——基于深度学习的高精地图算法(MapTRv2 / PivotNet / BeMapNet)

计算机视觉算法——基于深度学习的高精地图算法&#xff08;MapTRv2 / PivotNet / BeMapNet&#xff09; 计算机视觉算法——基于深度学习的高精地图算法&#xff08;MapTRv2 / PivotNet / BeMapNet&#xff09;1. MapTRv21.1 网络结构及特点1.2 Decoupled Self-Attention1.3 O…

nodejs21: 快速构建自定义设计样式Tailwind CSS

Tailwind CSS 是一个功能强大的低级 CSS 框架&#xff0c;只需书写 HTML 代码&#xff0c;无需书写 CSS&#xff0c;即可快速构建美观的网站。 1. 安装 Tailwind CSS React 项目中安装 Tailwind CSS&#xff1a; 1.1 安装 Tailwind CSS 和相关依赖 安装 Tailwind CSS: npm…

《Python网络安全项目实战》项目6 编写密码工具程序

《Python网络安全项目实战》项目6 编写密码工具程序 项目6 编写密码工具程序任务6.1 猜数字游戏任务描述任务分析任务实施6.1.1 编写基本的猜数字程序 6.1.2 为猜数字程序加入连续猜数的功能6.1.3 测试并修改程序6.1.4 给程序增加注释 任务拓展 任务6.2 编写密码工具程序任务描…

MATLAB蒙特卡洛仿真计算投资组合的VaR(Value at Risk )

1. 计算VaR简介 VaR&#xff08;Value at Risk&#xff09;&#xff0c;一般被称为“风险价值”或“在险价值”&#xff0c;是指在一定的置信水平下&#xff0c;某一金融资产&#xff08;或证券组合&#xff09;在未来特定的一段时间内的最大可能损失。VaR提供了一个具体的数值…

【linux学习指南】VSCode部署Ubantu云服务器,与Xshell进行本地通信文件编写

文章目录 &#x1f4dd;前言&#x1f320; 步骤&#x1f309;测试同步 &#x1f6a9;总结 &#x1f4dd;前言 本文目的是讲使用Vscode连接Ubantu,与本地Xshell建立通信同步文件编写。 查看本机系统相关信息&#xff1a; cat /etc/lsb*DISTRIB_IDUbuntu: 表示这是 Ubuntu 发行…

stm32下的ADC转换(江科协 HAL版)

十二. ADC采样 文章目录 十二. ADC采样12.1 ADC的采样原理12.2 STM32的采样基本过程1.引脚与GPIO端口的对应关系2.ADC规则组的四种转换模式(**)2.2 关于转换模式与配置之间的关系 12.3 ADC的时钟12.4 代码实现(ADC单通道 & ADC多通道)1. 单通道采样2. 多通道采样 19.ADC模数…

DockerFile与容器构建技术

一、 Docker架构 二、容器镜像分类 操作系统类 CentOSUbuntu在dockerhub下载或自行制作 应用类 TomcatNginxMySQLRedis 三、容器镜像获取的方法 主要有以下几种&#xff1a; 1、在DockerHub直接下载 2、把操作系统中文件系统打包为容器镜像 3、把正在运行的容器打包为容器镜…

分布式数据库中间件可以用在哪些场景呢

在数字化转型的浪潮中&#xff0c;企业面临着海量数据的存储、管理和分析挑战。华为云分布式数据库中间件&#xff08;DDM&#xff09;作为一款高效的数据管理解决方案&#xff0c;致力于帮助企业在多个场景中实现数据的高效管理和应用&#xff0c;提升业务效率和用户体验。九河…

jmeter常用配置元件介绍总结之断言

系列文章目录 1.windows、linux安装jmeter及设置中文显示 2.jmeter常用配置元件介绍总结之安装插件 3.jmeter常用配置元件介绍总结之线程组 4.jmeter常用配置元件介绍总结之函数助手 5.jmeter常用配置元件介绍总结之取样器 6.jmeter常用配置元件介绍总结之jsr223执行pytho…

项目技术栈-解决方案-web3去中心化

web3去中心化 Web3 DApp区块链:钱包:智能合约:UI:ETH系开发技能树DeFi应用 去中心化金融P2P 去中心化网络参考Web3 DApp 区块链: 以以太坊(Ethereum)为主流,也包括Solana、Aptos等其他非EVM链。 区块链本身是软件,需要运行在一系列节点上,这些节点组成P2P网络或者半…

多目标优化算法:多目标蛇鹫优化算法(MOSBOA)求解DTLZ1-DTLZ9,提供完整MATLAB代码

一、蛇鹫优化算法 蛇鹫优化算法&#xff08;Secretary Bird Optimization Algorithm&#xff0c;简称SBOA&#xff09;由Youfa Fu等人于2024年4月发表在《Artificial Intelligence Review》期刊上的一种新型的元启发式算法。该算法旨在解决复杂工程优化问题&#xff0c;特别是…

数据集-目标检测系列- 花卉 鸡蛋花 检测数据集 frangipani >> DataBall

数据集-目标检测系列- 花卉 鸡蛋花 检测数据集 frangipani >> DataBall DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 贵在坚持&#xff01; 数据样例项目地址&#xff1a; * 相关项目 1&#xff09;数据集…