Skip to content

Commit

Permalink
Merge branch 'test-token-authority' into pass-xdg-activation-token-wh…
Browse files Browse the repository at this point in the history
…en-launching-apps
  • Loading branch information
tarek-y-ismail committed Jan 28, 2025
2 parents 94556f0 + 11b3fe0 commit 6b06163
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/server/shell/token_authority.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ bool msh::TokenAuthority::Token::operator==(Token const& token) const
}

msh::TokenAuthority::TokenAuthority(std::shared_ptr<MainLoop>&& main_loop) :
main_loop{std::move(main_loop)}
main_loop{main_loop}
{
}

Expand Down
1 change: 1 addition & 0 deletions tests/unit-tests/shell/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ list(APPEND UNIT_TEST_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/test_basic_idle_handler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_decoration_basic_manager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_decoration_basic_decoration.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_token_authority.cpp
)

set(
Expand Down
178 changes: 178 additions & 0 deletions tests/unit-tests/shell/test_token_authority.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
* Copyright © Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 or 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "mir/glib_main_loop.h"
#include "mir/main_loop.h"
#include "mir/shell/token_authority.h"
#include "mir/test/auto_unblock_thread.h"
#include "mir/test/spin_wait.h"
#include "mir/time/steady_clock.h"
#include <algorithm>
#include <chrono>
#include <gtest/gtest.h>
#include <iterator>
#include <memory>
#include <string>
#include <unistd.h>
#include <uuid.h>

struct TestTokenAuthority : testing::Test
{
TestTokenAuthority() :
clock{std::make_shared<mir::time::SteadyClock>()},
main_loop{std::make_shared<mir::GLibMainLoop>(clock)},
token_authority{std::forward<std::shared_ptr<mir::MainLoop>>(main_loop)},
main_loop_thread{
[this]()
{
main_loop->stop();
},
[this]()
{
main_loop->run();
}}
{
}

std::shared_ptr<mir::time::SteadyClock> clock;
std::shared_ptr<mir::MainLoop> main_loop;
mir::shell::TokenAuthority token_authority;
mir::test::AutoUnblockThread main_loop_thread;
};

TEST_F(TestTokenAuthority, can_issue_tokens)
{
auto const token = token_authority.issue_token({});

// Make sure the generated token is a valid UUID
uuid_t uuid;
auto const token_string = std::string(token);
auto ret = uuid_parse(token_string.c_str(), uuid);
ASSERT_EQ(ret, 0);
}

TEST_F(TestTokenAuthority, tokens_are_revoked_in_time)
{
using namespace std::chrono;

auto constexpr revocation_time = milliseconds(3000);
auto constexpr epsilon = milliseconds(10);

bool revoked = false;
auto const issuance_time = clock->now();

auto const token = token_authority.issue_token(
[&revoked, this, issuance_time, revocation_time, epsilon](auto&)
{
revoked = true;
auto const delta_ms = duration_cast<milliseconds>(clock->now() - issuance_time);

// The revocation time should be about 3 seconds, give or take a few ms
ASSERT_LT(delta_ms - revocation_time, epsilon);
});

mir::test::spin_wait_for_condition_or_timeout(
[&revoked]
{
return revoked;
},
revocation_time + epsilon);

auto empty_token_opt = token_authority.get_token_for_string(std::string(token));

// Test if the token authority has actually removed the token
ASSERT_FALSE(empty_token_opt.has_value());
}

TEST_F(TestTokenAuthority, manual_revocation_works_correctly)
{
bool revoked = false;
auto const token = token_authority.issue_token(
[&revoked](auto&)
{
revoked = true;
});

ASSERT_FALSE(revoked);

token_authority.revoke_token(token);

auto empty_token_opt = token_authority.get_token_for_string(std::string(token));
ASSERT_FALSE(empty_token_opt.has_value());
ASSERT_TRUE(revoked);
}

TEST_F(TestTokenAuthority, manual_revocation_doesnt_revoke_other_tokens)
{
bool revoked1 = false, revoked2 = false;
auto const token1 = token_authority.issue_token(
[&revoked1](auto&)
{
revoked1 = true;
});
auto const token2 = token_authority.issue_token(
[&revoked2](auto&)
{
revoked2 = true;
});

ASSERT_FALSE(revoked1);
ASSERT_FALSE(revoked2);

token_authority.revoke_token(token1);

auto empty_token_opt = token_authority.get_token_for_string(std::string(token1));
ASSERT_FALSE(empty_token_opt.has_value());
ASSERT_TRUE(revoked1);
ASSERT_FALSE(revoked2);
}

TEST_F(TestTokenAuthority, revoke_all_actually_revokes_all)
{
std::vector<mir::shell::TokenAuthority::Token> tokens;
std::ranges::generate_n(
std::back_inserter(tokens),
10,
[this]()
{
return token_authority.issue_token({});
});

auto const token_valid = [this](auto const& token)
{
return token_authority.get_token_for_string(std::string(token)).has_value();
};

ASSERT_TRUE(std::ranges::all_of(tokens, token_valid));

token_authority.revoke_all_tokens();

ASSERT_TRUE(std::ranges::none_of(tokens, token_valid));
}

TEST_F(TestTokenAuthority, bogus_token_is_actually_bogus)
{
auto const bogus_token = token_authority.get_bogus_token();

// Make sure the generated bogus token is a valid UUID
uuid_t uuid;
auto const bogus_string = std::string(bogus_token);
auto ret = uuid_parse(bogus_string.c_str(), uuid);
ASSERT_EQ(ret, 0);

// Mustn't be registered in the token authority as a genuine token
ASSERT_FALSE(token_authority.get_token_for_string(bogus_string).has_value());
}

0 comments on commit 6b06163

Please sign in to comment.