Skip to content

Commit d758745

Browse files
authored
Merge branch 'master' into work/more-logging
2 parents cfda5e4 + abfe443 commit d758745

34 files changed

+153
-153
lines changed

.github/workflows/main.yml

-16
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,6 @@ jobs:
3939
os: macos-latest
4040
container:
4141
useSonarCloud:
42-
- name: macos-clang-arm64-debug
43-
target: macos-clang-arm64-debug
44-
os: macos-latest
45-
container:
46-
useSonarCloud:
47-
- name: linux-gcc-x86_64
48-
target: linux-64-gcc
49-
os: ubuntu-latest
50-
container: owncloudci/appimage-build:sles15-amd64
51-
useSonarCloud:
5242

5343
name: ${{ matrix.name }}
5444

@@ -141,12 +131,6 @@ jobs:
141131
- name: Build
142132
if: ${{ !matrix.useSonarCloud }}
143133
run: |
144-
if ("${{ matrix.target }}" -eq "macos-64-clang-debug" ) {
145-
# https://api.kde.org/ecm/module/ECMEnableSanitizers.html
146-
# address;leak;undefined
147-
# clang: error: unsupported option '-fsanitize=leak' for target 'x86_64-apple-darwin21.6.0'
148-
& "${env:GITHUB_WORKSPACE}/.github/workflows/.craft.ps1" -c --set args="-DECM_ENABLE_SANITIZERS='address;undefined'" owncloud/owncloud-client
149-
}
150134
if ("${{ matrix.target }}" -eq "linux-64-gcc" ) {
151135
# build with appimage updater and others
152136
& "${env:GITHUB_WORKSPACE}/.github/workflows/.craft.ps1" -c --set enableAppImageUpdater=true owncloud-client

src/gui/folder.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ Folder::Folder(const FolderDefinition &definition, const AccountStatePtr &accoun
155155
connect(_engine.data(), &SyncEngine::itemCompleted,
156156
_localDiscoveryTracker.data(), &LocalDiscoveryTracker::slotItemCompleted);
157157

158-
connect(_accountState->account()->spacesManager(), &GraphApi::SpacesManager::spaceChanged, [this](GraphApi::Space *changedSpace) {
158+
connect(_accountState->account()->spacesManager(), &GraphApi::SpacesManager::spaceChanged, this, [this](GraphApi::Space *changedSpace) {
159159
if (_definition.spaceId() == changedSpace->id()) {
160160
Q_EMIT spaceChanged();
161161
}

src/libsync/graphapi/space.cpp

+79-46
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,15 @@ const auto personalC = QLatin1String("personal");
3232
const auto sharesIdC = QLatin1String("a0ca6a90-a365-4782-871e-d44447bbc668$a0ca6a90-a365-4782-871e-d44447bbc668");
3333
}
3434

35-
Space::Space(SpacesManager *spacesManager, const OpenAPI::OAIDrive &drive)
35+
Space::Space(SpacesManager *spacesManager, const OpenAPI::OAIDrive &drive, const bool hasManyPersonalSpaces)
3636
: QObject(spacesManager)
3737
, _spaceManager(spacesManager)
3838
, _image(new SpaceImage(this))
39+
, _hasManyPersonalSpaces(hasManyPersonalSpaces)
3940
{
41+
// todo future refactoring: get this setDrive out of the ctr since it potentially kicks off a job for the SpaceImage before the Space is fully constructed
42+
// propose removing the drive arg from the ctr completely, and moving the call to setDrive to the spacesmanager such that any call to
43+
// "new" space is immediately followed by setDrive.
4044
setDrive(drive);
4145
connect(_image, &SpaceImage::imageChanged, this, &Space::imageChanged);
4246
}
@@ -48,63 +52,34 @@ OpenAPI::OAIDrive Space::drive() const
4852

4953
void Space::setDrive(const OpenAPI::OAIDrive &drive)
5054
{
51-
_drive = drive;
52-
_image->update();
53-
}
5455

55-
SpaceImage::SpaceImage(Space *space)
56-
: QObject(space)
57-
, _space(space)
58-
{
59-
update();
60-
}
56+
// first config naturally has an empty drive - reality check that updated drives are always valid
57+
Q_ASSERT(drive.isValid());
6158

62-
QIcon SpaceImage::image() const
63-
{
64-
if (_image.isNull()) {
65-
return Resources::getCoreIcon(QStringLiteral("space"));
66-
}
67-
return _image;
68-
}
69-
70-
QUrl SpaceImage::qmlImageUrl() const
71-
{
72-
if (!_image.isNull()) {
73-
return QUrl(QStringLiteral("image://space/%1/%2").arg(etag(), _space->id()));
74-
} else {
75-
// invalid space id to display the placeholder
76-
return QUrl(QStringLiteral("image://space/placeholder"));
77-
}
78-
}
59+
QString curTag = _drive.getRoot().getETag();
60+
QString newTag = drive.getRoot().getETag();
61+
Q_ASSERT(!newTag.isEmpty());
62+
// the tag should change when the space is edited on the server. I verified that it changes on space rename as well
63+
// as on changing the space image so we may have further wrinkles if there is an error in logic server side, but for now
64+
// we want to reduce updates to "only when something changed" else everything is auto-refreshed periodically (eg every 30s)
65+
if (curTag == newTag)
66+
return;
7967

80-
void SpaceImage::update()
81-
{
82-
const auto &special = _space->drive().getSpecial();
83-
const auto img = std::find_if(special.cbegin(), special.cend(), [](const auto &it) { return it.getSpecialFolder().getName() == QLatin1String("image"); });
84-
if (img != special.cend()) {
85-
_url = QUrl(img->getWebDavUrl());
86-
_etag = Utility::normalizeEtag(img->getETag());
87-
auto job = _space->_spaceManager->account()->resourcesCache()->makeGetJob(_url, {}, _space);
88-
QObject::connect(job, &SimpleNetworkJob::finishedSignal, _space, [job, this] {
89-
if (job->httpStatusCode() == 200) {
90-
_image = job->asIcon();
91-
Q_EMIT imageChanged();
92-
}
93-
});
94-
job->start();
95-
}
68+
_drive = drive;
69+
_image->update();
9670
}
9771

9872
QString Space::displayName() const
9973
{
100-
auto hasManyPersonalSpaces = _spaceManager->account()->capabilities().spacesSupport().enabled;
101-
if (hasManyPersonalSpaces) {
74+
if (_hasManyPersonalSpaces) {
10275
return _drive.getName();
10376
}
10477

78+
// other systems like oCIS have one personal and one shared space and their names are hard coded
10579
if (_drive.getDriveType() == personalC) {
10680
return tr("Personal");
107-
} else if (_drive.getId() == sharesIdC) {
81+
}
82+
if (_drive.getId() == sharesIdC) {
10883
// don't call it ShareJail
10984
return tr("Shares");
11085
}
@@ -141,3 +116,61 @@ QUrl Space::webdavUrl() const
141116
{
142117
return QUrl(_drive.getRoot().getWebDavUrl());
143118
}
119+
120+
SpaceImage::SpaceImage(Space *space)
121+
: QObject(space)
122+
, _space(space)
123+
{
124+
}
125+
126+
QIcon SpaceImage::image() const
127+
{
128+
if (_image.isNull()) {
129+
return Resources::getCoreIcon(QStringLiteral("space"));
130+
}
131+
return _image;
132+
}
133+
134+
QUrl SpaceImage::qmlImageUrl() const
135+
{
136+
if (!_image.isNull()) {
137+
return QUrl(QStringLiteral("image://space/%1/%2").arg(etag(), _space->id()));
138+
} else {
139+
// invalid space id to display the placeholder
140+
return QUrl(QStringLiteral("image://space/placeholder"));
141+
}
142+
}
143+
144+
void SpaceImage::update()
145+
{
146+
const auto &special = _space->drive().getSpecial();
147+
const auto img = std::find_if(special.cbegin(), special.cend(), [](const auto &it) { return it.getSpecialFolder().getName() == QLatin1String("image"); });
148+
if (img != special.cend())
149+
{
150+
// ssue 12057: verified the image etag does change when the space's icon has been updated via the web interface
151+
// check the etag before updating the members and creating the job. This should eliminate *many* pointless resource jobs which
152+
// will exacerbate whatever is making the app crash when the space tries to clean up it's child jobs and one is already gone
153+
QString newEtag = Utility::normalizeEtag(img->getETag());
154+
if (_etag == newEtag)
155+
return;
156+
157+
_etag = newEtag;
158+
_url = QUrl(img->getWebDavUrl());
159+
// issue 12057: I am leaving the space as the parent of the job just to avoid having confusion about "new" destructor crashes. At least
160+
// we'll get any future crashes on the space still. I don't think that changing the parent is going to eliminate the associate crash - save
161+
// this for a future refactoring
162+
auto job = _space->_spaceManager->account()->resourcesCache()->makeGetJob(_url, {}, _space);
163+
164+
// TODO: next problem = this routine is correctly run when the icon has changed on the server, but the icon in the gui does not get refreshed!
165+
// The icon IS in the app cache, so it seems to have been brought back from the server correctly, but it is not shown until restart
166+
// I did have a quick look at Resources::getCoreIcon - that is used in the SpaceImage::image function. Also sus is that I can't find any slot
167+
// for the imageChanged signal but I think this may be connected in qml via the associated space image property.
168+
QObject::connect(job, &SimpleNetworkJob::finishedSignal, _space, [job, this] {
169+
if (job->httpStatusCode() == 200) {
170+
_image = job->asIcon();
171+
Q_EMIT imageChanged();
172+
}
173+
});
174+
job->start();
175+
}
176+
}

src/libsync/graphapi/space.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ namespace GraphApi {
7777
OpenAPI::OAIDrive drive() const;
7878

7979
/***
80-
* Asign a priority to a drive, used for sorting
80+
* Assign a priority to a drive, used for sorting
8181
*/
8282
uint32_t priority() const;
8383

@@ -94,14 +94,16 @@ namespace GraphApi {
9494
void imageChanged();
9595

9696
private:
97-
Space(SpacesManager *spaceManager, const OpenAPI::OAIDrive &drive);
97+
Space(SpacesManager *spaceManager, const OpenAPI::OAIDrive &drive, bool hasManyPersonalSpaces);
9898
void setDrive(const OpenAPI::OAIDrive &drive);
9999

100100
SpacesManager *_spaceManager;
101101
OpenAPI::OAIDrive _drive;
102102

103103
SpaceImage *_image;
104104

105+
bool _hasManyPersonalSpaces;
106+
105107
friend class SpacesManager;
106108
friend class SpaceImage;
107109
};

src/libsync/graphapi/spacesmanager.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,22 @@ void SpacesManager::refresh()
5555
if (!_account->credentials()->ready()) {
5656
return;
5757
}
58-
// TODO: leak the job until we fixed the onwership https://github.com/owncloud/client/issues/11203
58+
59+
// TODO: leak the job until we fixed the ownership https://github.com/owncloud/client/issues/11203
5960
auto drivesJob = new Drives(_account->sharedFromThis(), nullptr);
6061
drivesJob->setTimeout(refreshTimeoutC);
6162
connect(drivesJob, &Drives::finishedSignal, this, [drivesJob, this] {
63+
// a system which provides multiple personal spaces the name of the drive is always used as display name
64+
auto hasManyPersonalSpaces = this->account()->capabilities().spacesSupport().hasMultiplePersonalSpaces;
65+
6266
drivesJob->deleteLater();
6367
if (drivesJob->httpStatusCode() == 200) {
6468
auto oldKeys = _spacesMap.keys();
6569
for (const auto &dr : drivesJob->drives()) {
6670
auto *space = this->space(dr.getId());
6771
oldKeys.removeAll(dr.getId());
6872
if (!space) {
69-
space = new Space(this, dr);
73+
space = new Space(this, dr, hasManyPersonalSpaces);
7074
_spacesMap.insert(dr.getId(), space);
7175
} else {
7276
space->setDrive(dr);

test/gui/shared/scripts/bdd_hooks.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,7 @@ def hook(context):
112112
os.makedirs(tmp_dir)
113113

114114
# sync connection folder display name
115-
# For oCIS, it will be the user's display name
116-
# and will be set while adding the account or creating resources
117-
if not get_config("ocis"):
118-
set_config("syncConnectionName", "ownCloud")
115+
set_config("syncConnectionName", "Personal" if get_config("ocis") else "ownCloud")
119116

120117

121118
# determines if the test scenario failed or not

test/gui/shared/scripts/helpers/ConfigHelper.py

-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ def get_default_home_dir():
100100
'ocis': False,
101101
'record_video_on_failure': False,
102102
'files_for_upload': os.path.join(CURRENT_DIR.parent.parent, 'files-for-upload'),
103-
'syncConnectionName': '',
104103
}
105104
CONFIG.update(DEFAULT_PATH_CONFIG)
106105

test/gui/shared/scripts/helpers/SetupClientHelper.py

+11-23
Original file line numberDiff line numberDiff line change
@@ -74,18 +74,11 @@ def set_current_user_sync_path(sync_path):
7474

7575
def get_resource_path(resource='', user='', space=''):
7676
sync_path = get_config('currentUserSyncPath')
77-
if not sync_path.startswith(get_config('tempFolderPath')):
78-
if user:
79-
sync_path = user
80-
else:
81-
user = parse_username_from_sync_path(sync_path)
82-
if get_config('ocis'):
83-
space = (
84-
space
85-
or get_config('syncConnectionName')
86-
or get_displayname_for_user(user)
87-
)
88-
sync_path = join(sync_path, space)
77+
if user:
78+
sync_path = user
79+
if get_config('ocis'):
80+
space = space or get_config('syncConnectionName')
81+
sync_path = join(sync_path, space)
8982
sync_path = join(get_config('clientRootSyncPath'), sync_path)
9083
resource = resource.replace(sync_path, '').strip('/').strip('\\')
9184
if is_windows():
@@ -96,10 +89,6 @@ def get_resource_path(resource='', user='', space=''):
9689
)
9790

9891

99-
def parse_username_from_sync_path(sync_path):
100-
return sync_path.split('/').pop()
101-
102-
10392
def get_temp_resource_path(resource_name):
10493
return join(get_config('tempFolderPath'), resource_name)
10594

@@ -168,10 +157,12 @@ def generate_account_config(users, space='Personal'):
168157
server_url = get_config('localBackendUrl')
169158

170159
if is_ocis := get_config('ocis'):
171-
if space == 'Personal':
172-
space = get_displayname_for_user(username)
160+
set_config('syncConnectionName', space)
173161
sync_path = create_space_path(space)
174-
dav_endpoint = url_join('dav/spaces', get_space_id(space, username))
162+
space_name = space
163+
if space == 'Personal':
164+
space_name = get_displayname_for_user(username)
165+
dav_endpoint = url_join('dav/spaces', get_space_id(space_name, username))
175166

176167
args = {
177168
'url': url_join(server_url, dav_endpoint, ''),
@@ -199,10 +190,7 @@ def generate_account_config(users, space='Personal'):
199190
return sync_paths
200191

201192

202-
def setup_client(username, space=None):
203-
if not space or space == 'Personal':
204-
space = get_displayname_for_user(username)
205-
set_config('syncConnectionName', space)
193+
def setup_client(username, space='Personal'):
206194
sync_paths = generate_account_config([username], space)
207195
start_client()
208196
for _, sync_path in sync_paths.items():

test/gui/shared/steps/account_context.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,12 @@
1717
wait_for_initial_sync_to_complete,
1818
listen_sync_status_for_item,
1919
)
20-
from helpers.ConfigHelper import get_config, set_config, is_windows, is_linux
20+
from helpers.ConfigHelper import get_config, is_windows, is_linux
2121

2222

2323
@When('the user adds the following user credentials:')
2424
def step(context):
2525
account_details = get_client_details(context)
26-
set_config('syncConnectionName', get_displayname_for_user(account_details['user']))
2726
AccountConnectionWizard.add_user_credentials(
2827
account_details['user'], account_details['password']
2928
)
@@ -106,7 +105,6 @@ def step(context):
106105
@When('the user adds the following account:')
107106
def step(context):
108107
account_details = get_client_details(context)
109-
set_config('syncConnectionName', get_displayname_for_user(account_details['user']))
110108
AccountConnectionWizard.add_account(account_details)
111109
# wait for files to sync
112110
wait_for_initial_sync_to_complete(get_resource_path('/', account_details['user']))
@@ -115,10 +113,6 @@ def step(context):
115113
@Given('the user has entered the following account information:')
116114
def step(context):
117115
account_details = get_client_details(context)
118-
if account_details['user']:
119-
set_config(
120-
'syncConnectionName', get_displayname_for_user(account_details['user'])
121-
)
122116
AccountConnectionWizard.add_account_information(account_details)
123117

124118

test/gui/shared/steps/spaces_context.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
get_file_content,
1212
resource_exists,
1313
)
14-
from helpers.ConfigHelper import get_config, set_config
14+
from helpers.ConfigHelper import get_config
1515

1616

1717
@Given('the administrator has created a space "|any|"')
@@ -43,7 +43,6 @@ def step(context, user, space_name):
4343
enter_password = EnterPassword()
4444
if get_config('ocis'):
4545
enter_password.accept_certificate()
46-
set_config('syncConnectionName', space_name)
4746
enter_password.login_after_setup(user, password)
4847
# wait for files to sync
4948
wait_for_initial_sync_to_complete(get_resource_path('/', user, space_name))

test/gui/tst_addAccount/test.feature

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ Feature: adding accounts
7676
| user | Alice |
7777
| password | 1234 |
7878
When the user selects manual sync folder option in advanced section
79-
And the user syncs the "Alice Hansen" space
79+
And the user syncs the "Personal" space
8080
Then the folder "simple-folder" should exist on the file system
8181

8282
@skipOnOCIS

0 commit comments

Comments
 (0)