~/Projects/mqtt-ios
git clone https://code.lsong.org/mqtt-ios
Commit
- Commit
- 71778bd3aebddb43992987eca2f247d6aced7b7a
- Author
- Philipp Arndt <[email protected]>
- Date
- 2022-04-16 10:03:45 +0200 +0200
- Diffstat
.github/workflows/screenshots.yml | 27 .github/workflows/swift.yml | 33 .github/workflows/test.yml | 46 .gitignore | 4 Docs/examples/client-certs/README.md | 46 Docs/examples/client-certs/config/mosquitto/mosquitto.conf | 16 Docs/examples/client-certs/docker-compose.yml | 11 ci/prepare-screenshots.mjs | 4 ci/start-mosquitto.mjs | 1 create-screenshots.sh | 2 mqtt-stub-service/.gitignore | 3 mqtt-stub-service/config/mosquitto-noauth/config/mosquitto.conf | 8 mqtt-stub-service/config/mosquitto-userpass/config/mosquitto.conf | 11 mqtt-stub-service/config/mosquitto-userpass/config/passwd | 1 mqtt-stub-service/config/mosquitto/config/mosquitto.conf | 10 mqtt-stub-service/docker-compose.yaml | 75 publish.sh | 24 src/MQTTAnalyzer.xcodeproj/project.pbxproj | 148 src/MQTTAnalyzer.xcodeproj/xcshareddata/xcschemes/MQTTAnalyzer.xcscheme | 14 src/MQTTAnalyzer.xcworkspace/contents.xcworkspacedata | 16 src/MQTTAnalyzer.xcworkspace/xcshareddata/swiftpm/Package.resolved | 24 src/MQTTAnalyzer/AppDelegate.swift | 1 src/MQTTAnalyzer/Info.plist | 7 src/MQTTAnalyzer/extensions/Array+Move.swift | 45 src/MQTTAnalyzer/extensions/Array+Remove.swift | 18 src/MQTTAnalyzer/extensions/String+RegExp.swift | 33 src/MQTTAnalyzer/model/persistence/HostSettingExamples.swift | 15 src/MQTTAnalyzer/model/persistence/RealmPersistence.swift | 8 src/MQTTAnalyzer/model/persistence/StubPersistence.swift | 1 src/MQTTAnalyzer/model/v2/TopicTree.swift | 2 src/MQTTAnalyzer/mqtt/MqttClientSharedUtils.swift | 26 src/MQTTAnalyzer/mqtt/cocoamqtt/CocoaMQTTClient+ErrorMessage.swift | 34 src/MQTTAnalyzer/mqtt/cocoamqtt/CocoaMQTTClient.swift | 82 src/MQTTAnalyzer/views/about/AboutView.swift | 2 src/MQTTAnalyzer/views/common/CustomListView.swift | 2 src/MQTTAnalyzer/views/common/QuestionBox.swift | 22 src/MQTTAnalyzer/views/host/form/DisconnectedView.swift | 2 src/MQTTAnalyzer/views/host/form/LoginView.swift | 1 src/MQTTAnalyzer/views/host/form/auth/AuthenticationTypePicker.swift | 3 src/MQTTAnalyzer/views/host/form/auth/UsernamePasswordAuthenticationView.swift | 4 src/MQTTAnalyzer/views/host/form/aws-iot/AWSIOTHelpView.swift | 8 src/MQTTAnalyzer/views/host/form/aws-iot/HostFormModel+AWS.swift | 2 src/MQTTAnalyzer/views/host/form/client-certs/ClientCertsHelpView.swift | 47 src/MQTTAnalyzer/views/host/form/client-certs/HostFormModel+ClientCerts.swift | 29 src/MQTTAnalyzer/views/host/form/server/ProtocolPicker.swift | 3 src/MQTTAnalyzer/views/host/form/server/ServerFormView.swift | 8 src/MQTTAnalyzer/views/host/form/topic/SubscriptionDetailsView.swift | 3 src/MQTTAnalyzer/views/host/form/topic/TopicsFormView.swift | 6 src/MQTTAnalyzer/views/login/LoginDialog.swift | 2 src/MQTTAnalyzer/views/message-publish/PublishMessageFormView.swift | 4 src/MQTTAnalyzer/views/topic/FolderNavigationView.swift | 3 src/MQTTAnalyzerTests/AWSIOTPresetTests.swift | 7 src/MQTTAnalyzerTests/CocoaMQTTRegression.swift | 2 src/MQTTAnalyzerTests/Info.plist | 1 src/MQTTAnalyzerTests/MqttClientCocoaMQTTTests.swift | 35 src/MQTTAnalyzerTests/TreeModelTests.swift | 12 src/MQTTAnalyzerUITests/AbstractConfigurationTests.swift | 39 src/MQTTAnalyzerUITests/BrokerTests.swift | 2 src/MQTTAnalyzerUITests/ConfigurationMQTTTests.swift | 60 src/MQTTAnalyzerUITests/ConfigurationWebSocketTests.swift | 63 src/MQTTAnalyzerUITests/FlatViewTests.swift | 4 src/MQTTAnalyzerUITests/Info.plist | 1 src/MQTTAnalyzerUITests/PublishTests.swift | 20 src/MQTTAnalyzerUITests/ReadStateTests.swift | 20 src/MQTTAnalyzerUITests/ScreenshotTests.swift | 4 src/MQTTAnalyzerUITests/SearchTests.swift | 8 src/MQTTAnalyzerUITests/SubscriptionTests.swift | 28 src/MQTTAnalyzerUITests/TestServer.swift | 15 src/MQTTAnalyzerUITests/extensions/XCTestCase+Disappear.swift | 3 src/MQTTAnalyzerUITests/extensions/XCUIElement+ClearText.swift | 61 src/MQTTAnalyzerUITests/utils/Broker.swift | 17 src/MQTTAnalyzerUITests/utils/Brokers.swift | 153 src/MQTTAnalyzerUITests/utils/ExampleMessages.swift | 43 src/MQTTAnalyzerUITests/utils/Navigation.swift | 9 src/MQTTAnalyzerUITests/utils/PublishDialog.swift | 2 src/Podfile | 12 src/Podfile.lock | 36 src/ReleaseTestPlan.xctestplan | 31 src/TestPlan.xctestplan | 42 src/TestPlanThreads.xctestplan | 35 src/UnitTestPlan.xctestplan | 24 src/fastlane/Fastfile | 6 src/fastlane/Scanfile | 2 src/fastlane/Snapfile | 3 src/fastlane/SnapshotHelper.swift | 2
Apple network (#137) * test on iPad mini * CocoaMQTT update * error handling * Package.resolve v2 fix * mac first as it requires user interaction * fix test failure with new CocoaMQTT version * Create scan.yml * naming * task name * update dependency list * Update scan.yml * Update screenshots.yml * Update scan.yml * Update scan.yml * fix test case on older iOS version * rename action * websocket test case * clean-up * improve error message * support topics with empty path names https://github.com/philipparndt/mqtt-analyzer/issues/138 * support topics with empty path names https://github.com/philipparndt/mqtt-analyzer/issues/138 * start mosquitto * fix indent * fix indent * test with more configurations * use test server instead of localhost * clean-up connection view * more test cases / delete dead code * split test cases to run more tests in parallel * fix connect data race * remove topic limit for test cases * run only unit tests, integration tests are running during deployment * add more output * specify test target * improve screenshot performance * use local container for integration tests * fix typo * introduce test plans * topic # is not a good suggestion * introduce test plans * introduce test plans * fixed font * introduce test plans * pod update * introduce test plans * update new test cases for macOS * add client certificates docu * fix test case
diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index bee5e96b9f1258fe29651af7e6c5142e73d1b6c3..f36acf1d3f4a7bb50bb680b78e523a9d3822384e 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -10,21 +11,19 @@ steps: - uses: actions/checkout@master name: 'Create screenshots' + screenshots: name: 'Create screenshots' -name: 'Create screenshots' + runs-on: macOS-latest + - npm install -g zx + # No support for Package.resolve v2 on GitHub -name: 'Create screenshots' on: + steps: name: 'Create screenshots' - workflow_dispatch: + - uses: actions/checkout@master run: | - ci/start-mosquitto.mjs + rm ./MQTTAnalyzer.xcworkspace/xcshareddata/swiftpm/Package.resolved name: 'Create screenshots' - screenshots: - run: sudo xcode-select -s /Applications/Xcode_13.2.app - -name: 'Create screenshots' steps: working-directory: src run: | @@ -39,15 +38,16 @@ brew update brew install imagemagick shell: bash - - name: Create screenshots + # No support for Package.resolve v2 on GitHub + - name: Remove Package.resolve + working-directory: src run: | - bash create-screenshots.sh + rm ./MQTTAnalyzer.xcworkspace/xcshareddata/swiftpm/Package.resolved - steps: - if: always() + screenshots: run: | - ci/stop-mosquitto.mjs + bash create-screenshots.sh - uses: actions/upload-artifact@v3 with: diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 4280d1d48c74d345e168e5c4618b1b3534ba3f8d..d6dc248fbdb786e105a58198056e901ac65d2368 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -14,33 +14,37 @@ steps: - uses: actions/checkout@master name: 'Build' - workflow_dispatch: + test: + run: sudo xcode-select -s /Applications/Xcode_13.2.app - run: | + + name: 'Build' - paths: - - name: Install Mosquitto + run: | - ci/start-mosquitto.mjs + pod repo update - - name: Set XCode Version + workflow_dispatch: + push: + workflow_dispatch: -name: 'Build' + - name: Remove Package.resolve working-directory: src run: | - pod repo update - + workflow_dispatch: workflow_dispatch: - push: - - name: iOS Tests working-directory: src run: | - + xcodebuild test -enableCodeCoverage YES \ + -workspace MQTTAnalyzer.xcworkspace \ + workflow_dispatch: - 'src/**' + -testPlan UnitTestPlan \ + -destination 'platform=iOS Simulator,name=iPhone 13,OS=15.2' - name: Upload test results uses: actions/upload-artifact@v3 @@ -52,8 +57,3 @@ #- name: macOS Tests # working-directory: src # run: | # fastlane mac tests - - - name: Stop Mosquitto - if: always() - run: | - ci/stop-mosquitto.mjs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5c7eef5c66cd3e631a544cd3c7c8dd2896e532dd..5563d9b2ab5c7a8d6ac0288f15bf5ca5ca295685 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,42 +3,48 @@ on: workflow_dispatch: jobs: + test: +on: test: - runs-on: macOS-latest + - + steps: +on: steps: +on: - uses: actions/checkout@master + - name: Set XCode Version + run: sudo xcode-select -s /Applications/Xcode_13.2.app # No support for Package.resolve v2 on GitHub - - name: Remove Package.resolve -name: 'Test' + workflow_dispatch: on: -name: 'Test' + workflow_dispatch: workflow_dispatch: -name: 'Test' + workflow_dispatch: - -name: 'Test' + workflow_dispatch: jobs: - working-directory: src + -name: 'Test' workflow_dispatch: -name: 'Test' test: -name: 'Test' + working-directory: src + run: | + workflow_dispatch: runs-on: macOS-latest -name: 'Test' + workflow_dispatch: steps: + shell: bash - - name: Test + - name: Test -name: 'Test' + workflow_dispatch: workflow_dispatch: - cd src + run: | -on: + name: 'Test' -on: + on: -on: + workflow_dispatch: -on: + + -destination 'platform=iOS Simulator,name=iPhone 13,OS=15.2' diff --git a/.gitignore b/.gitignore index ac9e6d8911e254030e213296c4f920c5ac5c052c..e9e558a2952d183bc616060841924fe7555e2baf 100644 --- a/.gitignore +++ b/.gitignore @@ -33,5 +33,9 @@ *.hmap *.ipa ### Xcode ### +### Xcode ### +.idea/ + +### Xcode ### # Created by http://www.gitignore.io Pods \ No newline at end of file diff --git a/Docs/examples/client-certs/README.md b/Docs/examples/client-certs/README.md new file mode 100644 index 0000000000000000000000000000000000000000..dcebe71686692e1b62990393053aa2016e49a9e7 --- /dev/null +++ b/Docs/examples/client-certs/README.md @@ -0,0 +1,46 @@ +# Connect with client certificates + +This is an example on how to set-up client certificates. +To use this example you need to create self-singed certificates first. +Open the ./config/mosquitto folder in your terminal and run the following commands: + +### Create root CA: +```sh +openssl genrsa -des3 -out ca.key 2048 # password +openssl req -new -x509 -days 1826 -key ca.key -out ca.crt +``` + +### Create broker certificate: +```sh +openssl genrsa -out broker.key 2048 +openssl req -new -out broker.csr -key broker.key +openssl x509 -req -in broker.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out broker.crt -days 360 +``` + +### Create client certificate: +```sh +openssl genrsa -out client.key 2048 +openssl req -new -out client.csr -key client.key +openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 360 +``` + +### Create P12 file: +```sh +openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 +``` + +### Start the container: +```sh +docker compose up -d +``` + +### In MQTTAnalyzer: +Use the P12 file in MQTTAnalyzer to connect to the broker. +The settings are: + +- Port: 8883 +- SSL: `true` +- Allow untrusted: `true` +- Authentication: `Certificate` +- Client PKCS#12: `client.p12` +- Password: `password` \ No newline at end of file diff --git a/Docs/examples/client-certs/config/mosquitto/mosquitto.conf b/Docs/examples/client-certs/config/mosquitto/mosquitto.conf new file mode 100644 index 0000000000000000000000000000000000000000..5128c2fedeb321a43c5cba832bf922a4d9a90645 --- /dev/null +++ b/Docs/examples/client-certs/config/mosquitto/mosquitto.conf @@ -0,0 +1,16 @@ +# Per Listener Settings +per_listener_settings true +# log_type error +# log_type warning +# log_type notice +log_type all + +# Secured Listener +listener 8883 +cafile /mosquitto/config/ca.crt +keyfile /mosquitto/config/broker.key +certfile /mosquitto/config/broker.crt +require_certificate true +use_identity_as_username true +allow_anonymous false +#log_type info diff --git a/Docs/examples/client-certs/docker-compose.yml b/Docs/examples/client-certs/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..1cc2d625e0d79db979e3d71803b2a253b76fb0aa --- /dev/null +++ b/Docs/examples/client-certs/docker-compose.yml @@ -0,0 +1,11 @@ +version: '3' +services: + mosquitto: + image: eclipse-mosquitto + hostname: mosquitto + expose: + - "8883" + ports: + - "8883:8883" + volumes: + - ./config/mosquitto:/mosquitto/config diff --git a/ci/prepare-screenshots.mjs b/ci/prepare-screenshots.mjs index 68f85338bbd8500255bbda6f051fe2a85cdd5015..d8b106d91fb078fe594f18bd2defed0500ccd79f 100755 --- a/ci/prepare-screenshots.mjs +++ b/ci/prepare-screenshots.mjs @@ -2,11 +2,11 @@ #!/usr/bin/env zx const devices = [ "iPhone 13 Pro", - "iPhone SE (3rd generation)"", + "iPhone SE (3rd generation)", "iPad Pro (11-inch) (3rd generation)" //, "iPad Air (3rd generation)" ] -const papp = process.env["APPEARENCE"] +const papp = process.env["APPEARANCE"] const appearance=papp ?? "dark" // 'dark' or 'light' console.log(`---- Setting appearance to ${papp} ----`) diff --git a/ci/start-mosquitto.mjs b/ci/start-mosquitto.mjs index 4d0c514e29e9d8db669c5befb3da84ea96015489..35616a9850aa8ec27a52642e316bed415c012c39 100755 --- a/ci/start-mosquitto.mjs +++ b/ci/start-mosquitto.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env zx try { await $`brew install mosquitto` + await $`cp ./mqtt-stub-service/config/mosquitto/config/mosquitto.conf /usr/local/etc/mosquitto/mosquitto.conf` await $`brew services start mosquitto` } catch (e) { diff --git a/create-screenshots.sh b/create-screenshots.sh index 514e48da75c046d929a9934c088a300dab489882..fa484ab0dd032341842c919a2ff196b5e4fecbc3 100755 --- a/create-screenshots.sh +++ b/create-screenshots.sh @@ -11,7 +11,7 @@ array=( dark light ) for i in "${array[@]}" do mkdir "./screenshots/$i" -#!/bin/bash +cd "$(dirname)" cd "$(dirname)" ./ci/prepare-screenshots.mjs diff --git a/mqtt-stub-service/.gitignore b/mqtt-stub-service/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..8e57aa04825441958f88bf3f4fb3eb82bc91a72c --- /dev/null +++ b/mqtt-stub-service/.gitignore @@ -0,0 +1,3 @@ +.env +traefik/ +acme.json \ No newline at end of file diff --git a/mqtt-stub-service/config/mosquitto/config/mosquitto.conf b/mqtt-stub-service/config/mosquitto/config/mosquitto.conf deleted file mode 100644 index b588b8e976547b118a863980e62ed10d5dccf970..0000000000000000000000000000000000000000 --- a/mqtt-stub-service/config/mosquitto/config/mosquitto.conf +++ /dev/null @@ -1,10 +0,0 @@ -log_type all -log_facility 5 - -port 1883 - -allow_anonymous true -#password_file /mosquitto/config/mosquitto.password - -listener 9001 -protocol websockets \ No newline at end of file diff --git a/mqtt-stub-service/config/mosquitto-noauth/config/mosquitto.conf b/mqtt-stub-service/config/mosquitto-noauth/config/mosquitto.conf new file mode 100644 index 0000000000000000000000000000000000000000..2a2d49758f5d9b9cffd733087683658fef1a19b0 --- /dev/null +++ b/mqtt-stub-service/config/mosquitto-noauth/config/mosquitto.conf @@ -0,0 +1,8 @@ +log_type all +log_facility 5 +allow_anonymous true + +listener 1883 + +listener 9001 +protocol websockets diff --git a/mqtt-stub-service/config/mosquitto-userpass/config/mosquitto.conf b/mqtt-stub-service/config/mosquitto-userpass/config/mosquitto.conf new file mode 100644 index 0000000000000000000000000000000000000000..70519f59170941336791bacf6b1aa3f76f5bb3b6 --- /dev/null +++ b/mqtt-stub-service/config/mosquitto-userpass/config/mosquitto.conf @@ -0,0 +1,11 @@ +log_type all +log_facility 5 + +allow_anonymous false +password_file /mosquitto/config/passwd + +listener 1884 +protocol mqtt + +listener 9002 +protocol websockets \ No newline at end of file diff --git a/mqtt-stub-service/config/mosquitto-userpass/config/passwd b/mqtt-stub-service/config/mosquitto-userpass/config/passwd new file mode 100644 index 0000000000000000000000000000000000000000..ef153a901a501072810099b830db7b36e460f5dc --- /dev/null +++ b/mqtt-stub-service/config/mosquitto-userpass/config/passwd @@ -0,0 +1 @@ +admin:$6$ZSu9Ickl6AHFMax7$lvXOnS+Hjx1UBScqAup1O3WcReuQcyV1ZnL5svFXvWGxkUnzE8pquy4iuxdOMg3MACPDwiDXpwlNYDldPKeDgA== diff --git a/mqtt-stub-service/docker-compose.yaml b/mqtt-stub-service/docker-compose.yaml index d79ed739f34c4261f71f133dd124dcaa97a889c9..4f0590216444939aaec9878d83236c37c0e8530f 100644 --- a/mqtt-stub-service/docker-compose.yaml +++ b/mqtt-stub-service/docker-compose.yaml @@ -1,8 +1,83 @@ version: '3' services: + traefik: + image: traefik:v2.6 + restart: "no" + +version: '3' mosquitto: +version: '3' image: eclipse-mosquitto +version: '3' volumes: +version: '3' - ./config/mosquitto/config:/mosquitto/config:rw +version: '3' ports: +version: '3' - "1883:1883" + + # Entrypoints + - "--entrypoints.websecure.address=:443" + - "--entrypoints.mqtts.address=:8883" + + # Redirections + - "--entrypoints.web.http.redirections.entryPoint.to=websecure" + - "--entrypoints.web.http.redirections.entryPoint.scheme=https" + + # TLS + - "--entrypoints.websecure.http.tls.certResolver=letsencrypt" + - "--certificatesResolvers.letsencrypt.acme.email=${MAIL}" + - "--certificatesresolvers.letsencrypt.acme.preferredChain='ISRG Root X1'" + - "--certificatesResolvers.letsencrypt.acme.storage=/etc/traefik/acme/acme.json" + - "--certificatesResolvers.letsencrypt.acme.keyType=EC384" + - "--certificatesResolvers.letsencrypt.acme.dnsChallenge.provider=ionos" + + # Providers + - "--providers.docker.endpoint=unix:///var/run/docker.sock" + - "--providers.docker.exposedByDefault=false" + - "--providers.file.directory=/configurations" + - "--providers.file.watch=true" + + ports: + # Web server + - 443:443 + # MQTT + - 8883:8883 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ./config/traefik/configs:/configurations + - ./config/traefik/acme:/etc/traefik/acme + + environment: + - IONOS_API_KEY=${IONOS_API_KEY} + + mosquitto-no-auth: + image: eclipse-mosquitto + volumes: + - ./config/mosquitto-noauth/config:/mosquitto/config:rw + ports: + - "1883:1883" + - "9001:9001" + + labels: + - traefik.enable=true + + - "traefik.http.routers.websecure.rule=Host(`${MQTT_HOST}`)" + - "traefik.http.routers.websecure.entrypoints=websecure" + - "traefik.http.routers.websecure.tls.certresolver=letsencrypt" + - "traefik.http.services.websecure.loadbalancer.server.port=9001" + + - traefik.tcp.routers.mqtts.rule=HostSNI(`${MQTT_HOST}`) + - traefik.tcp.routers.mqtts.entrypoints=mqtts + - traefik.tcp.routers.mqtts.tls.certresolver=letsencrypt + - traefik.tcp.routers.mqtts.service=mqtts + - traefik.tcp.services.mqtts.loadBalancer.server.port=1883 + + mosquitto-user-apssword-auth: + image: eclipse-mosquitto + volumes: + - ./config/mosquitto-userpass/config:/mosquitto/config:rw + ports: + - "1884:1884" + - "9002:9002" diff --git a/publish.sh b/publish.sh index 35fe54139816a10d1fb976aa8714450c66d48a4b..7b283cdaf6babb302b1e33c98ffc01085fab4d01 100755 --- a/publish.sh +++ b/publish.sh @@ -7,20 +7,19 @@ pushd ci zx realm-headers.mjs undo popd -### Create macOS Archive -pushd src +### Test Env - pod install +pushd mqtt-stub-service -#!/bin/bash set -e +popd popd -## iOS ################################## + -### Create iOS Archive +### Install / build number pushd src #!/bin/bash -pushd ci +#!/bin/bash #!/bin/bash - zx realm-headers.mjs undo +set -e popd ## macOS ################################ @@ -43,5 +42,12 @@ ### Undo prepare Realm for macOS pushd ci zx realm-headers.mjs undo -set -e +popd + +## iOS ################################## +#!/bin/bash # ~/Library/Developer/Xcode/Archives +pushd src + rm -f MQTTAnalyzer.ipa MQTTAnalyzer.pkg + fastlane ios publish +popd diff --git a/src/MQTTAnalyzer/AppDelegate.swift b/src/MQTTAnalyzer/AppDelegate.swift index 91ef51c0426827a7e88f3a7f9e2220c8f27c5122..eda09e7c33c8cfb521a5fddbed7076014e0d00fb 100644 --- a/src/MQTTAnalyzer/AppDelegate.swift +++ b/src/MQTTAnalyzer/AppDelegate.swift @@ -10,7 +10,6 @@ import UIKit import IceCream import CloudKit import CocoaMQTT -import Starscream @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { diff --git a/src/MQTTAnalyzer/Info.plist b/src/MQTTAnalyzer/Info.plist index 3af66e141ead7ebce718e93d151d0ae1df824977..9087781a3129b6fb029a7b6ca9a7e2fadc9ea1cb 100644 --- a/src/MQTTAnalyzer/Info.plist +++ b/src/MQTTAnalyzer/Info.plist @@ -17,7 +17,7 @@$(PRODUCT_BUNDLE_PACKAGE_TYPE) <key>CFBundleShortVersionString</key> <string>$(MARKETING_VERSION)</string> <key>CFBundleVersion</key> - <string>120</string> + <string>139</string> <key>ITSAppUsesNonExemptEncryption</key> <false/> <key>LSApplicationCategoryType</key> @@ -80,5 +80,10 @@UIInterfaceOrientationPortraitUpsideDown <string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeRight</string> </array> + <key>NSAppTransportSecurity</key> + <dict> + <key>NSAllowsArbitraryLoads</key> + <true/> + </dict> </dict> </plist> diff --git a/src/MQTTAnalyzer/extensions/Array+Move.swift b/src/MQTTAnalyzer/extensions/Array+Move.swift deleted file mode 100644 index a6de830370634acd182413691d0ac190412a1f61..0000000000000000000000000000000000000000 --- a/src/MQTTAnalyzer/extensions/Array+Move.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// ArrayUtils.swift -// MQTTAnalyzer -// -// Created by Philipp Arndt on 2019-11-16. -// Copyright © 2019 Philipp Arndt. All rights reserved. -// - -import Foundation - -extension Array { - mutating func move(fromOffsets source: IndexSet, toOffset destination: Int) { - let suffixStart = halfStablePartition { index, _ in - return source.contains(index) - } - let suffix = self[suffixStart...] - removeSubrange(suffixStart...) - insert(contentsOf: suffix, at: destination) - } - - mutating func halfStablePartition(isSuffixElement predicate: (Index, Element) -> Bool) -> Index { - guard var i = firstIndex(where: predicate) else { - return endIndex - } - - var j = index(after: i) - while j != endIndex { - if !predicate(j, self[j]) { - swapAt(i, j) - formIndex(after: &i) - } - formIndex(after: &j) - } - return i - } - - func firstIndex(where predicate: (Index, Element) -> Bool) -> Index? { - for (index, element) in self.enumerated() { - if predicate(index, element) { - return index - } - } - return nil - } -} diff --git a/src/MQTTAnalyzer/extensions/Array+Remove.swift b/src/MQTTAnalyzer/extensions/Array+Remove.swift deleted file mode 100644 index a706cee22e8fcf29b6a3aa8f9d294d0ace9f036b..0000000000000000000000000000000000000000 --- a/src/MQTTAnalyzer/extensions/Array+Remove.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// Array+Remove.swift -// MQTTAnalyzer -// -// Created by Philipp Arndt on 2022-01-31. -// Copyright © 2022 Philipp Arndt. All rights reserved. -// - -import Foundation - -extension Array { - mutating func remove(atOffsets offsets: IndexSet) { - let suffixStart = halfStablePartition { index, _ in - return offsets.contains(index) - } - removeSubrange(suffixStart...) - } -} diff --git a/src/MQTTAnalyzer/extensions/String+RegExp.swift b/src/MQTTAnalyzer/extensions/String+RegExp.swift new file mode 100644 index 0000000000000000000000000000000000000000..1e5db0b7bc139b42f48e9d87b63c12cebf47a1fc --- /dev/null +++ b/src/MQTTAnalyzer/extensions/String+RegExp.swift @@ -0,0 +1,33 @@ +// +// String+RegExp.swift +// MQTTAnalyzer +// +// Created by Philipp Arndt on 08.04.22. +// Copyright © 2022 Philipp Arndt. All rights reserved. +// + +import Foundation + +// https://stackoverflow.com/questions/42789953/swift-3-how-do-i-extract-captured-groups-in-regular-expressions +extension String { + func groups(for regexPattern: String) -> [[String]] { + do { + let text = self + let regex = try NSRegularExpression(pattern: regexPattern) + let matches = regex.matches(in: text, + range: NSRange(text.startIndex..., in: text)) + return matches.map { match in + return (0..<match.numberOfRanges).map { + let rangeBounds = match.range(at: $0) + guard let range = Range(rangeBounds, in: text) else { + return "" + } + return String(text[range]) + } + } + } catch let error { + print("invalid regex: \(error.localizedDescription)") + return [] + } +} +} diff --git a/src/MQTTAnalyzer/model/persistence/HostSettingExamples.swift b/src/MQTTAnalyzer/model/persistence/HostSettingExamples.swift index b55a1c16c3d7107ff1e7a1e5fa60a5335e3cbd64..651d90943e974bb1b9b07037c8ff3f41c6df4e96 100644 --- a/src/MQTTAnalyzer/model/persistence/HostSettingExamples.swift +++ b/src/MQTTAnalyzer/model/persistence/HostSettingExamples.swift @@ -47,14 +47,23 @@ ] return result } +// Copyright © 2019 Philipp Arndt. All rights reserved. // MQTTAnalyzer - let result = Host() result.alias = "Example" + result.hostname = "test.mqtt.rnd7.de" + result.limitTopic = 0 // MQTTAnalyzer -import RealmSwift +class HostSettingExamples { + return result + } + + class func exampleLocalhost() -> Host { // HostSettingExamples.swift -import Foundation +// Created by Philipp Arndt on 2019-11-23. + result.alias = "localhost" + result.hostname = "localhost" + result.limitTopic = 0 result.subscriptions = [TopicSubscription(topic: "#", qos: 0)] return result } diff --git a/src/MQTTAnalyzer/model/persistence/RealmPersistence.swift b/src/MQTTAnalyzer/model/persistence/RealmPersistence.swift index 344a958f1c9d1b6d03cc7e0328f7034228b61045..934e5a98c26dc4b930aa12f39f91c669231e5c9b 100644 --- a/src/MQTTAnalyzer/model/persistence/RealmPersistence.swift +++ b/src/MQTTAnalyzer/model/persistence/RealmPersistence.swift @@ -8,6 +8,7 @@ // import Foundation import RealmSwift +import SwiftUI public class RealmPersistence: Persistence { let model: HostsModel @@ -18,7 +19,7 @@ init?(model: HostsModel) { self.model = model // -import Foundation + } self.realm = realm } else { @@ -26,13 +27,14 @@ return nil } } -// HostModelPersistence.swift +// // MQTTAnalyzer +// Copyright © 2019 Philipp Arndt. All rights reserved. do { return try Realm() } catch { - NSLog("Unable to initialize persistence, using stub persistence.") + NSLog("Unable to initialize persistence, using stub persistence. \(error)") return nil } diff --git a/src/MQTTAnalyzer/model/persistence/StubPersistence.swift b/src/MQTTAnalyzer/model/persistence/StubPersistence.swift index 338aa64b25efe67b0ab6159ab240d8836529057e..544912dd912c6ebbbff49230b80b6abd85f6eff0 100644 --- a/src/MQTTAnalyzer/model/persistence/StubPersistence.swift +++ b/src/MQTTAnalyzer/model/persistence/StubPersistence.swift @@ -27,6 +27,7 @@ func initExamples() { hosts = [ HostSettingExamples.example1(), HostSettingExamples.example2(), + HostSettingExamples.exampleRnd7(), HostSettingExamples.exampleLocalhost() ] } diff --git a/src/MQTTAnalyzer/model/v2/TopicTree.swift b/src/MQTTAnalyzer/model/v2/TopicTree.swift index 5513441d8f1a7ccccf3ac20898e578aa42bf9ca8..f94ae59fa232a2320f260e6b7ee9d4bd33e6245c 100644 --- a/src/MQTTAnalyzer/model/v2/TopicTree.swift +++ b/src/MQTTAnalyzer/model/v2/TopicTree.swift @@ -144,7 +144,7 @@ return addTopic(topic: topic, create: false) } func addTopic(topic: String, create: Bool = true) -> TopicTree? { - let segments = topic.split(separator: "/").map { String($0) } + let segments = topic.split(separator: "/", omittingEmptySubsequences: false).map { String($0) } var current = findRoot() var created = false diff --git a/src/MQTTAnalyzer/mqtt/MqttClientSharedUtils.swift b/src/MQTTAnalyzer/mqtt/MqttClientSharedUtils.swift deleted file mode 100644 index b3d1d611ed72b9b62dda78aebae412cd0eac9a7f..0000000000000000000000000000000000000000 --- a/src/MQTTAnalyzer/mqtt/MqttClientSharedUtils.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// MqttClientSharedUtils.swift -// MQTTAnalyzer -// -// Created by Philipp Arndt on 2020-04-13. -// Copyright © 2020 Philipp Arndt. All rights reserved. -// - -import Foundation - -class MqttClientSharedUtils { - func waitFor(predicate: @escaping () -> Bool) -> DispatchTimeoutResult { - let group = DispatchGroup() - group.enter() - - DispatchQueue.global().async { - while !predicate() { - usleep(useconds_t(500)) - } - group.leave() - } - - return group.wait(timeout: .now() + 10) - } - -} diff --git a/src/MQTTAnalyzer/mqtt/cocoamqtt/CocoaMQTTClient+ErrorMessage.swift b/src/MQTTAnalyzer/mqtt/cocoamqtt/CocoaMQTTClient+ErrorMessage.swift new file mode 100644 index 0000000000000000000000000000000000000000..60febfcaab9f022affa5ae5b8c3519bfa36da5fc --- /dev/null +++ b/src/MQTTAnalyzer/mqtt/cocoamqtt/CocoaMQTTClient+ErrorMessage.swift @@ -0,0 +1,34 @@ +// +// CocoaMQTTClient+ErrorMessage.swift +// MQTTAnalyzer +// +// Created by Philipp Arndt on 08.04.22. +// Copyright © 2022 Philipp Arndt. All rights reserved. +// + +import Foundation +import CocoaMQTT + +extension MqttClientCocoaMQTT { + class func extractErrorMessage(error: Error) -> String { + let nsError = error as NSError + let code = nsError.code + + if code == 8 { + return "Invalid hostname.\n\(error.localizedDescription)" + } + else if nsError.domain == "Network.NWError" { + if nsError.description.starts(with: "-9808") { + return "Bad certificate format, check all properties, like SAN, ... (-9808)" + } + else { + let groups = nsError.description.groups(for: ".*\\(rawValue:.(\\d+)\\):.(.*)") + if groups.count == 1 && groups[0].count == 3 { + return "\(groups[0][2]) (NW: \(groups[0][1]))" + } + } + } + + return "\(nsError.domain): \(nsError.description)" + } +} diff --git a/src/MQTTAnalyzer/mqtt/cocoamqtt/CocoaMQTTClient.swift b/src/MQTTAnalyzer/mqtt/cocoamqtt/CocoaMQTTClient.swift index 5d70b57c9bad205ebc89bb50d0e069c613df8b3a..f5fc1607ada2eba803600ff8e741a38cef5c9c6c 100644 --- a/src/MQTTAnalyzer/mqtt/cocoamqtt/CocoaMQTTClient.swift +++ b/src/MQTTAnalyzer/mqtt/cocoamqtt/CocoaMQTTClient.swift @@ -8,13 +8,15 @@ // import Foundation import CocoaMQTT -import Starscream +import Combine +// MqttClientCocoaMQTT.swift // +// Created by Philipp Arndt on 2020-04-13. class MqttClientCocoaMQTT: MqttClient { + private let connectionStateQueue = DispatchQueue(label: "connection.state.lock.queue") let delgate = MQTTDelegate() - let utils = MqttClientSharedUtils() let sessionNum: Int let model: TopicTree @@ -65,8 +68,7 @@ } mqtt.enableSSL = host.ssl mqtt.allowUntrustCACertificate = host.untrustedSSL - mqtt.sslSettings = [kCFStreamSSLPeerName as String: host.hostname as NSObject] - + if host.auth == .usernamePassword { mqtt.username = host.usernameNonpersistent ?? host.username mqtt.password = host.passwordNonpersistent ?? host.password @@ -92,9 +94,6 @@ mqtt.delegate = self.delgate mqtt.didReceiveMessage = self.didReceiveMessage mqtt.didDisconnect = self.didDisconnect mqtt.didConnectAck = self.didConnect - mqtt.didChangeState = { _, state in - print(state) - } if !mqtt.connect() { failConnection(reason: "Connection to port \(host.port) failed") @@ -123,19 +122,21 @@ DispatchQueue.global().async { var i = 10 + var connecting = true import CocoaMQTT -import Starscream +import CocoaMQTT + while connecting && i > 0 { print("CONNECTION: waiting... \(self.sessionNum) \(i) \(self.host.hostname)") sleep(1) import Starscream -// MQTTAnalyzer + import Starscream -// Created by Philipp Arndt on 2020-04-13. +// MqttClientCocoaMQTT.swift - } + self.connectionStateQueue.sync { - + connecting = self.connectionState.state == .connecting import Starscream - +// Copyright © 2020 Philipp Arndt. All rights reserved. } group.leave() } @@ -179,9 +180,10 @@ func disconnect() { print("CONNECTION: disconnect \(sessionNum) \(host.hostname)") messageSubject.cancel() -// + self.connectionStateQueue.async { + var connectionState = ConnectionState() // -import Starscream + } if let mqtt = self.mqtt { DispatchQueue.global(qos: .background).async { @@ -217,8 +219,10 @@ } } func setDisconnected() { + self.connectionStateQueue.async { + var connectionState = ConnectionState() // - var connectionAlive: Bool { + } DispatchQueue.main.async { self.host.state = .disconnected @@ -241,7 +245,7 @@ return } for message in messages { - if self.model.totalTopicCounter >= host.limitTopic { + if host.limitTopic > 0 && self.model.totalTopicCounter >= host.limitTopic { // Limit exceeded self.model.topicLimitExceeded = true } @@ -262,43 +266,25 @@ } } // +import Foundation -// Copyright © 2020 Philipp Arndt. All rights reserved. // - mqtt.didDisconnect = self.didDisconnect - - let model: TopicTree +import Foundation import Foundation -// -import CocoaMQTT - return "Invalid hostname.\n\(error.localizedDescription)" var host: Host - if host.protocolMethod == .mqtt { +import CocoaMQTT - var host: Host // MqttClientCocoaMQTT.swift - } - var host: Host +// MqttClientCocoaMQTT.swift // MQTTAnalyzer - var host: Host + + var connectionState = ConnectionState() // Created by Philipp Arndt on 2020-04-13. + var connectionState = ConnectionState() // Copyright © 2020 Philipp Arndt. All rights reserved. - - default: - var host: Host // Copyright © 2020 Philipp Arndt. All rights reserved. - } - } // - messageSubject.cancellable = messageSubject.subject.eraseToAnyPublisher() - print("CONNECTION: onDisconnect \(sessionNum) \(host.hostname)") - - if err != nil { - let messgae = extractErrorMessage(error: err!) - - connectionState.message = messgae -// self.host.usernameNonpersistent = nil self.host.passwordNonpersistent = nil @@ -317,8 +303,10 @@ func didConnect(_ mqtt: CocoaMQTT, didConnectAck ack: CocoaMQTTConnAck) { if ack == .accept { print("CONNECTION: onConnect \(sessionNum) \(host.hostname)") - connectionState.state = .connected + self.connectionStateQueue.async { - + self.connectionState.state = .connected + } + NSLog("Connected. Return Code is \(ack.description)") DispatchQueue.main.async { self.host.state = .connected @@ -353,9 +341,12 @@ func failConnection(reason: String) { NSLog("Connection failed: " + reason) // MqttClientCocoaMQTT.swift +// MqttClientCocoaMQTT.swift -import CocoaMQTT -import Starscream +// MqttClientCocoaMQTT.swift // MqttClientCocoaMQTT.swift +import Foundation + } + self.setDisconnected() DispatchQueue.main.async { diff --git a/src/MQTTAnalyzer/views/about/AboutView.swift b/src/MQTTAnalyzer/views/about/AboutView.swift index 2e81b3f545e0c46fed6f758a466f91b3abc314b4..8bb02270a174581ae611852b5bc1b3c03e5e1ca5 100644 --- a/src/MQTTAnalyzer/views/about/AboutView.swift +++ b/src/MQTTAnalyzer/views/about/AboutView.swift @@ -37,7 +37,7 @@ [Ulrich Frank](https://github.com/UlrichFrank), [Ricardo Pereira](https://github.com/visnaut), [AndreCouture](https://github.com/AndreCouture), [RoSchmi](https://github.com/RoSchmi), [Xploder](https://github.com/Xploder), [Ed Gauthier](https://github.com/edgauthier) **Dependencies** -[CocoaMQTT](https://github.com/emqx/CocoaMQTT), [CocoaAsyncSocket](https://github.com/robbiehanson/CocoaAsyncSocket), [Starscream](https://github.com/daltoniam/Starscream), [RealmSwift](https://realm.io/docs/swift/latest/), [IceCream](https://github.com/caiyue1993/IceCream), [Highlightr](https://github.com/raspu/Highlightr), [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON), [swift-petitparser](https://github.com/philipparndt/swift-petitparser) +[CocoaMQTT](https://github.com/emqx/CocoaMQTT), [RealmSwift](https://realm.io/docs/swift/latest/), [IceCream](https://github.com/caiyue1993/IceCream), [Highlightr](https://github.com/raspu/Highlightr), [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON), [swift-petitparser](https://github.com/philipparndt/swift-petitparser) [GRDB](https://github.com/groue/GRDB.swift) """).foregroundColor(.secondary) .font(.footnote) diff --git a/src/MQTTAnalyzer/views/common/CustomListView.swift b/src/MQTTAnalyzer/views/common/CustomListView.swift index 188c052206a72548e669e195c4372d0ab693560f..d47b3d9fa578faa746fc59fa0b00dafbcdefbe8a 100644 --- a/src/MQTTAnalyzer/views/common/CustomListView.swift +++ b/src/MQTTAnalyzer/views/common/CustomListView.swift @@ -20,7 +20,7 @@ var body: some View { HStack { VStack { - ForEach(views.indices) { index in + ForEach(views.indices, id: \.self) { index in views[index] if index < views.count - 1 { Divider() diff --git a/src/MQTTAnalyzer/views/common/QuestionBox.swift b/src/MQTTAnalyzer/views/common/QuestionBox.swift deleted file mode 100644 index 08ffe7a9fcbb55536482240a750c40443d125bc7..0000000000000000000000000000000000000000 --- a/src/MQTTAnalyzer/views/common/QuestionBox.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// MenuButton.swift -// MQTTAnalyzer -// -// Created by Philipp Arndt on 2020-01-04. -// Copyright © 2020 Philipp Arndt. All rights reserved. -// - -import SwiftUI - -struct QuestionBox: View { - let text: String - - var body: some View { - FillingText(text: text, - imageName: "questionmark.circle.fill") - .padding() - .font(.body) - .background(Color.green.opacity(0.1)) - .cornerRadius(10) - } -} diff --git a/src/MQTTAnalyzer/views/host/form/DisconnectedView.swift b/src/MQTTAnalyzer/views/host/form/DisconnectedView.swift index 5adffa45b3c85c1d4b1b49fdf4b8dc3a79a84500..ae3c1607715fe90ced36bd1dca1ff064fe83c770 100644 --- a/src/MQTTAnalyzer/views/host/form/DisconnectedView.swift +++ b/src/MQTTAnalyzer/views/host/form/DisconnectedView.swift @@ -13,7 +13,7 @@ var host: Host var body: some View { HStack { - Text(host.connectionMessage ?? "Disconnected...") + Text(host.connectionMessage ?? "Disconnected") Spacer() diff --git a/src/MQTTAnalyzer/views/host/form/LoginView.swift b/src/MQTTAnalyzer/views/host/form/LoginView.swift index b534dc82892f670787c2639a1b8e329eea8b9ec8..9b45738e9fa8581a26081edeb0e159c947cba544 100644 --- a/src/MQTTAnalyzer/views/host/form/LoginView.swift +++ b/src/MQTTAnalyzer/views/host/form/LoginView.swift @@ -27,6 +27,7 @@ Text("Authenticate") } } + .accessibilityLabel("Play") } .padding() } diff --git a/src/MQTTAnalyzer/views/host/form/auth/AuthenticationTypePicker.swift b/src/MQTTAnalyzer/views/host/form/auth/AuthenticationTypePicker.swift index 2f61fc194decf9f49ecf31ae8ede1a64457fb2ca..d5bcacc81f7fd7c88068fc67fa994aa92fbe71f8 100644 --- a/src/MQTTAnalyzer/views/host/form/auth/AuthenticationTypePicker.swift +++ b/src/MQTTAnalyzer/views/host/form/auth/AuthenticationTypePicker.swift @@ -25,7 +26,10 @@ var body: some View { Picker(selection: $type, label: Text("Auth")) { Text("None").tag(HostAuthenticationType.none) // AuthPicker.swift +// MQTTAnalyzer + Text("User/password").tag(HostAuthenticationType.usernamePassword).accessibilityLabel("userPassword-auth") Text("Certificate").tag(HostAuthenticationType.certificate) + .accessibilityLabel("certificate-auth") }.pickerStyle(SegmentedPickerStyle()) } } diff --git a/src/MQTTAnalyzer/views/host/form/auth/UsernamePasswordAuthenticationView.swift b/src/MQTTAnalyzer/views/host/form/auth/UsernamePasswordAuthenticationView.swift index 960280cdfce4cb78443523ccb1c5f73183976003..08d8a7be6ddb059572da0455beba230cd8ebfa06 100644 --- a/src/MQTTAnalyzer/views/host/form/auth/UsernamePasswordAuthenticationView.swift +++ b/src/MQTTAnalyzer/views/host/form/auth/UsernamePasswordAuthenticationView.swift @@ -26,6 +26,7 @@ .disableAutocorrection(true) .autocapitalization(.none) .multilineTextAlignment(.trailing) .font(.body) + .accessibilityLabel("your username") } HStack { @@ -34,11 +35,12 @@ .font(.headline) Spacer() - SecureField("password", text: $host.password) + SecureField("your password", text: $host.password) .disableAutocorrection(true) .autocapitalization(.none) .multilineTextAlignment(.trailing) .font(.body) + .accessibilityLabel("password") } InfoBox(text: "Leave username and/or password empty. In order to not persist them. You will get a login dialog.") diff --git a/src/MQTTAnalyzer/views/host/form/aws-iot/AWSIOTHelpView.swift b/src/MQTTAnalyzer/views/host/form/aws-iot/AWSIOTHelpView.swift index 9ce133720ef587b82fe5ab3f43bae1ef7a5bab91..00f0c8455fa1c7d174d3d0530f795b0daae8d82e 100644 --- a/src/MQTTAnalyzer/views/host/form/aws-iot/AWSIOTHelpView.swift +++ b/src/MQTTAnalyzer/views/host/form/aws-iot/AWSIOTHelpView.swift @@ -15,15 +15,15 @@ var body: some View { HStack { VStack { HStack { - Text("[AWS IoT documentation](https://github.com/philipparndt/mqtt-analyzer/blob/master/Docs/AWS-IoT.md#connect-to-aws-iot)") + Text("[AWS IoT documentation](https://github.com/philipparndt/mqtt-analyzer/blob/master/Docs/examples/aws/README.md)") .foregroundColor(.blue) Spacer() } - if host.suggestAWSIOTCHanges() { + if host.suggestAWSIOTChanges() { Text("") // Space HStack { - Button(action: self.updateSettingsForAWSIOT) { + Button(action: self.updateSettings) { Text("Apply default values") } Spacer() @@ -41,7 +41,7 @@ .background(Color.green.opacity(0.1)) .cornerRadius(10) } - func updateSettingsForAWSIOT() { + func updateSettings() { self.host.updateSettingsForAWSIOT() } } diff --git a/src/MQTTAnalyzer/views/host/form/aws-iot/HostFormModel+AWS.swift b/src/MQTTAnalyzer/views/host/form/aws-iot/HostFormModel+AWS.swift index 06d2fd248e67cf1ad6a8b951aaddf53ae447019f..d4978b068ccac5725709816cd6c83d0ff5aecd03 100644 --- a/src/MQTTAnalyzer/views/host/form/aws-iot/HostFormModel+AWS.swift +++ b/src/MQTTAnalyzer/views/host/form/aws-iot/HostFormModel+AWS.swift @@ -14,7 +14,7 @@ return hostname.lowercased().hasSuffix("amazonaws.com") && hostname.lowercased().contains(".iot.") } - func suggestAWSIOTCHanges() -> Bool { + func suggestAWSIOTChanges() -> Bool { if isAWS() { // mqtt not ws // cocoa not moscapsule diff --git a/src/MQTTAnalyzer/views/host/form/client-certs/ClientCertsHelpView.swift b/src/MQTTAnalyzer/views/host/form/client-certs/ClientCertsHelpView.swift new file mode 100644 index 0000000000000000000000000000000000000000..948b437308fa0063510ad5eb46d0d2e87fcab89c --- /dev/null +++ b/src/MQTTAnalyzer/views/host/form/client-certs/ClientCertsHelpView.swift @@ -0,0 +1,47 @@ +// +// AWSIoTHelpView.swift +// MQTTAnalyzer +// +// Created by Philipp Arndt on 2022-02-20. +// Copyright © 2022 Philipp Arndt. All rights reserved. +// + +import SwiftUI + +struct ClientCertsHelpView: View { + @Binding var host: HostFormModel + + var body: some View { + HStack { + VStack { + HStack { + Text("[Client certificates documentation](https://github.com/philipparndt/mqtt-analyzer/blob/master/Docs/examples/client-certs/README.md)") + .foregroundColor(.blue) + Spacer() + } + + if host.suggestClientCertsTLSChanges() { + Text("") // Space + HStack { + Button(action: self.updateSettings) { + Text("Apply default values") + } + Spacer() + } + } + } + + Spacer() + + Image(systemName: "questionmark.circle.fill") + } + .padding() + .font(.body) + .background(Color.green.opacity(0.1)) + .cornerRadius(10) + } + + func updateSettings() { + self.host.updateSettingsForClientCertsTLS() + } +} diff --git a/src/MQTTAnalyzer/views/host/form/client-certs/HostFormModel+ClientCerts.swift b/src/MQTTAnalyzer/views/host/form/client-certs/HostFormModel+ClientCerts.swift new file mode 100644 index 0000000000000000000000000000000000000000..3533cd4aace1ab3b358ad243b601a3f14b4bfbbe --- /dev/null +++ b/src/MQTTAnalyzer/views/host/form/client-certs/HostFormModel+ClientCerts.swift @@ -0,0 +1,29 @@ +// +// HostFormModel+ClientCerts.swift +// MQTTAnalyzer +// +// Created by Philipp Arndt on 16.04.22. +// Copyright © 2022 Philipp Arndt. All rights reserved. +// + +import Foundation + +extension HostFormModel { + func isClientCerts() -> Bool { + return authType == .certificate + } + + func suggestClientCertsTLSChanges() -> Bool { + if isClientCerts() { + if !ssl { + return true + } + } + return false + } + + mutating func updateSettingsForClientCertsTLS() { + self.ssl = true + self.untrustedSSL = true + } +} diff --git a/src/MQTTAnalyzer/views/host/form/server/ProtocolPicker.swift b/src/MQTTAnalyzer/views/host/form/server/ProtocolPicker.swift index 43f2fde8a76341e34b67328895cfe8e58772c579..e021785a0f7efef02e292d33f2834241563588bf 100644 --- a/src/MQTTAnalyzer/views/host/form/server/ProtocolPicker.swift +++ b/src/MQTTAnalyzer/views/host/form/server/ProtocolPicker.swift @@ -24,8 +24,9 @@ @Binding var type: HostProtocol var body: some View { Picker(selection: $type, label: Text("Protocol")) { - Text("MQTT").tag(HostProtocol.mqtt) + Text("MQTT").tag(HostProtocol.mqtt).accessibilityLabel("mqtt") // ConnectionTypePicker.swift +// MQTTAnalyzer }.pickerStyle(SegmentedPickerStyle()) } } diff --git a/src/MQTTAnalyzer/views/host/form/server/ServerFormView.swift b/src/MQTTAnalyzer/views/host/form/server/ServerFormView.swift index 78946e27aab777c8a1492da275afcd84e84e43bd..afa9b76023b044778140279c29127b9b82a91c1d 100644 --- a/src/MQTTAnalyzer/views/host/form/server/ServerFormView.swift +++ b/src/MQTTAnalyzer/views/host/form/server/ServerFormView.swift @@ -55,6 +55,9 @@ if host.isAWS() { AWSIoTHelpView(host: $host) } + else if host.isClientCerts() { + ClientCertsHelpView(host: $host) + } HStack { FormFieldInvalidMark(invalid: portInvalid) @@ -64,9 +67,10 @@ .font(.headline) Spacer() - TextField("1883", text: $host.port) + TextField("e.g. 1883", text: $host.port) .multilineTextAlignment(.trailing) .disableAutocorrection(true) + .accessibilityLabel("port") .font(.body) } @@ -98,7 +102,7 @@ Toggle(isOn: $host.ssl) { Text("SSL") .font(.headline) - } + }.accessibilityLabel("tls") if host.ssl { Toggle(isOn: $host.untrustedSSL) { diff --git a/src/MQTTAnalyzer/views/host/form/topic/SubscriptionDetailsView.swift b/src/MQTTAnalyzer/views/host/form/topic/SubscriptionDetailsView.swift index b82d4df638b451d9e4aaef494799f8f0f0fdfe67..6da92682b383e30eaf87f1d65c8ceab4c569dede 100644 --- a/src/MQTTAnalyzer/views/host/form/topic/SubscriptionDetailsView.swift +++ b/src/MQTTAnalyzer/views/host/form/topic/SubscriptionDetailsView.swift @@ -20,11 +20,12 @@ Text("Topic") .font(.headline) Spacer() - TextField("#", text: $subscription.topic) + TextField("e.g. #", text: $subscription.topic) .multilineTextAlignment(.trailing) .disableAutocorrection(true) .autocapitalization(.none) .font(.body) + .accessibilityLabel("subscription-topic") } HStack { diff --git a/src/MQTTAnalyzer/views/host/form/topic/TopicsFormView.swift b/src/MQTTAnalyzer/views/host/form/topic/TopicsFormView.swift index 4949ea32cb70dffc6386c3931a16ba487c7c2fb2..08a107b6714bd18f0abf106e286207f3c17836a9 100644 --- a/src/MQTTAnalyzer/views/host/form/topic/TopicsFormView.swift +++ b/src/MQTTAnalyzer/views/host/form/topic/TopicsFormView.swift @@ -33,7 +33,9 @@ .onDelete(perform: self.delete) Button(action: addSubscription) { Text("Add subscription") - }.font(.body) + } + .font(.body) + .accessibilityLabel("add-subscription") } } } @@ -48,7 +50,7 @@ } func addSubscription() { // Created by Philipp Arndt on 2020-04-14. -// +struct TopicsFormView: View { host.subscriptions.append(model) ViewSelection.update(newValue: model.id) { id in diff --git a/src/MQTTAnalyzer/views/login/LoginDialog.swift b/src/MQTTAnalyzer/views/login/LoginDialog.swift index b9dad5b1711e3cb9d589387ef531cc5f8f495aa1..d2e02c6feb28420d17b2a88be7c092b556b57de9 100644 --- a/src/MQTTAnalyzer/views/login/LoginDialog.swift +++ b/src/MQTTAnalyzer/views/login/LoginDialog.swift @@ -57,6 +57,7 @@ .disableAutocorrection(true) .autocapitalization(.none) .multilineTextAlignment(.trailing) .font(.body) + .accessibilityLabel("username") } HStack { @@ -70,6 +71,7 @@ .disableAutocorrection(true) .autocapitalization(.none) .multilineTextAlignment(.trailing) .font(.body) + .accessibilityLabel("password") } } diff --git a/src/MQTTAnalyzer/views/message-publish/PublishMessageFormView.swift b/src/MQTTAnalyzer/views/message-publish/PublishMessageFormView.swift index 0d1deef2b765e4f6bd9feddf8045c5391b9f9a16..bb1fdca8e605121228b4675b17f9545caf6b1e8b 100644 --- a/src/MQTTAnalyzer/views/message-publish/PublishMessageFormView.swift +++ b/src/MQTTAnalyzer/views/message-publish/PublishMessageFormView.swift @@ -143,7 +143,7 @@ var body: some View { Form { Section(header: Text("Topic")) { - TextField("#", text: $message.topic) + TextField("", text: $message.topic) .disableAutocorrection(true) .autocapitalization(.none) .font(.body) @@ -213,7 +213,7 @@ var body: some View { Group { // - } + let id: String = NSUUID().uuidString HStack { Text(self.message.properties[index].pathName) Spacer() diff --git a/src/MQTTAnalyzer/views/topic/FolderNavigationView.swift b/src/MQTTAnalyzer/views/topic/FolderNavigationView.swift index 0640da85af05a63cd204bfe659c08540d96b23fb..37351bb6b7cd416f3690658e37dae2990833eb4e 100644 --- a/src/MQTTAnalyzer/views/topic/FolderNavigationView.swift +++ b/src/MQTTAnalyzer/views/topic/FolderNavigationView.swift @@ -45,7 +45,8 @@ var body: some View { HStack { FolderReadMarkerView(read: model.readState) - Text(model.name) + Text(model.name.isBlank ? "<empty>" : model.name) + .foregroundColor(model.name.isBlank ? .gray : .primary) Spacer() diff --git a/src/MQTTAnalyzer.xcodeproj/project.pbxproj b/src/MQTTAnalyzer.xcodeproj/project.pbxproj index fc75645507f7a8fb773537fcc59076cb5193d695..e35350915915a4fd57f0962ce993e2f19bbb02fc 100644 --- a/src/MQTTAnalyzer.xcodeproj/project.pbxproj +++ b/src/MQTTAnalyzer.xcodeproj/project.pbxproj @@ -25,19 +25,23 @@ 22061AC327CA6BD300FFF915 /* XCUIElement+Checked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22061AC227CA6BD300FFF915 /* XCUIElement+Checked.swift */; }; 22061AC527CA79FE00FFF915 /* DispatchQueue+Background.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22061AC427CA79FE00FFF915 /* DispatchQueue+Background.swift */; }; 2209C86C23B720E7007C1D93 /* HostValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2209C86B23B720E7007C1D93 /* HostValidator.swift */; }; 220CCD632477F12300E8CA39 /* DataMigrationCertificateFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 220CCD622477F12300E8CA39 /* DataMigrationCertificateFiles.swift */; }; +// !$*UTF8*$! { +// !$*UTF8*$! +// !$*UTF8*$! { +// !$*UTF8*$! /* Begin PBXBuildFile section */ - 221C571C2466847800C0DD02 /* QuestionBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 221C571B2466847800C0DD02 /* QuestionBox.swift */; }; + 2215118D27C923EB0000E385 /* TopicTree+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2215118C27C923EB0000E385 /* TopicTree+Search.swift */; }; + 2217BA3C27A3D2F900699428 /* AwaitMessagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2217BA3B27A3D2F900699428 /* AwaitMessagesView.swift */; }; 221C571E2466C9CD00C0DD02 /* HostFormModel+AWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 221C571D2466C9CD00C0DD02 /* HostFormModel+AWS.swift */; }; 221C57202466CC2800C0DD02 /* AWSIOTPresetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 221C571F2466CC2800C0DD02 /* AWSIOTPresetTests.swift */; }; + 222AC3302803350600D66334 /* TestServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 222AC32F2803350600D66334 /* TestServer.swift */; }; 222EAAB1279FC289000E37AF /* CocoaMQTTRegression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 222EAAB0279FC289000E37AF /* CocoaMQTTRegression.swift */; }; 222EAAB327A31849000E37AF /* PropertyImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 222EAAB227A31849000E37AF /* PropertyImageProvider.swift */; }; 222EAAB527A31CCB000E37AF /* PropertyImageProviderTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 222EAAB427A31CCB000E37AF /* PropertyImageProviderTest.swift */; }; 222EAAB727A3CDEB000E37AF /* ConnectBrokerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 222EAAB627A3CDEB000E37AF /* ConnectBrokerView.swift */; }; - 2230FEF427A4340400D4327F /* NavigationModeFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FEF327A4340400D4327F /* NavigationModeFormView.swift */; }; - 2230FEF627A435A000D4327F /* NavigationPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FEF527A435A000D4327F /* NavigationPicker.swift */; }; 2230FEF827A4434800D4327F /* DataMigrationNavigationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FEF727A4434800D4327F /* DataMigrationNavigationMode.swift */; }; 2230FEFA27A50C8000D4327F /* TreeModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FEF927A50C8000D4327F /* TreeModelTests.swift */; }; 2230FEFE27A50E9000D4327F /* Date+Iso.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FEFD27A50E9000D4327F /* Date+Iso.swift */; }; @@ -61,7 +65,6 @@ 2230FF2927A6BBB800D4327F /* TitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF2827A6BBB800D4327F /* TitleView.swift */; }; 2230FF2B27A6BC1C00D4327F /* InformationDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF2A27A6BC1C00D4327F /* InformationDetailView.swift */; }; 2230FF2D27A6BC8900D4327F /* InformationContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF2C27A6BC8900D4327F /* InformationContainerView.swift */; }; 2230FF2F27A6CDB800D4327F /* Welcome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF2E27A6CDB800D4327F /* Welcome.swift */; }; - 2230FF3127A8304E00D4327F /* Array+Remove.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF3027A8304E00D4327F /* Array+Remove.swift */; }; 2230FF3327A830C900D4327F /* DataProtocol+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF3227A830C900D4327F /* DataProtocol+Hex.swift */; }; 2231894B27F08BAB001177F4 /* ViewSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2231894A27F08BAB001177F4 /* ViewSelection.swift */; }; 223AF5D12477D575009810E6 /* FileLister.swift in Sources */ = {isa = PBXBuildFile; fileRef = 223AF5D02477D575009810E6 /* FileLister.swift */; }; @@ -94,8 +97,6 @@ 2263C1D924813B0C00FF14E8 /* CertificateFilesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2263C1D824813B0C00FF14E8 /* CertificateFilesModel.swift */; }; 226A6B50244457E100ACDFC3 /* MqttClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 226A6B4F244457E100ACDFC3 /* MqttClient.swift */; }; 226A6B5224445BA400ACDFC3 /* CocoaMQTTClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 226A6B5124445BA400ACDFC3 /* CocoaMQTTClient.swift */; }; /* Begin PBXBuildFile section */ - classes = { -/* Begin PBXBuildFile section */ }; 226A6B5D2445748500ACDFC3 /* HostFormModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 226A6B5C2445748500ACDFC3 /* HostFormModel.swift */; }; 226A6B5F2445754D00ACDFC3 /* ServerFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 226A6B5E2445754D00ACDFC3 /* ServerFormView.swift */; }; @@ -141,6 +142,9 @@ 2291284924685AE1006F8256 /* DataMigrationLimits.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2291284824685AE0006F8256 /* DataMigrationLimits.swift */; }; 2291424C23BF78000086C251 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2291424B23BF78000086C251 /* AboutView.swift */; }; 2291424F23C0C0370086C251 /* MenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2291424E23C0C0370086C251 /* MenuButton.swift */; }; // !$*UTF8*$! + 2209C86B23B720E7007C1D93 /* HostValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostValidator.swift; sourceTree = "<group>"; }; + 22953B33280A92D8000F8F37 /* ClientCertsHelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22953B32280A92D8000F8F37 /* ClientCertsHelpView.swift */; }; +// !$*UTF8*$! 2230FEF827A4434800D4327F /* DataMigrationNavigationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FEF727A4434800D4327F /* DataMigrationNavigationMode.swift */; }; 229FCAEF247D6E2700490628 /* ImportCertificatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 229FCAEE247D6E2700490628 /* ImportCertificatePicker.swift */; }; 22A351DE24A76463001B8AEE /* PublishMessageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22A351DD24A76463001B8AEE /* PublishMessageModel.swift */; }; @@ -151,6 +155,11 @@ 22A386FD2409440F00DF8F94 /* InfoBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22A386FC2409440F00DF8F94 /* InfoBox.swift */; }; 22A387052409768100DF8F94 /* HostModelPersistenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22A387042409768100DF8F94 /* HostModelPersistenceTests.swift */; }; 22A38707240BD9E600DF8F94 /* TimeSeriesValueUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22A38706240BD9E600DF8F94 /* TimeSeriesValueUtil.swift */; }; // !$*UTF8*$! + 2215118C27C923EB0000E385 /* TopicTree+Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TopicTree+Search.swift"; sourceTree = "<group>"; }; + 22AB2C112800771700E88875 /* CocoaMQTTClient+ErrorMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AB2C102800771700E88875 /* CocoaMQTTClient+ErrorMessage.swift */; }; + 22AB2C132800854700E88875 /* MqttClientCocoaMQTTTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AB2C122800854700E88875 /* MqttClientCocoaMQTTTests.swift */; }; + 22AB2C152801D8A400E88875 /* ConfigurationMQTTTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AB2C142801D8A400E88875 /* ConfigurationMQTTTests.swift */; }; +// !$*UTF8*$! 2230FF1027A6511F00D4327F /* TreeUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF0F27A6511F00D4327F /* TreeUtilsTests.swift */; }; 22AE64362412637A00C2C4FE /* TimeSeriesValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AE64352412637A00C2C4FE /* TimeSeriesValue.swift */; }; 22AE643824126A7500C2C4FE /* DiagramPathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AE643724126A7500C2C4FE /* DiagramPathTests.swift */; }; @@ -166,6 +175,8 @@ 22B46BD927CA652B0077FB83 /* Broker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22B46BD227CA652B0077FB83 /* Broker.swift */; }; 22B46BDA27CA652B0077FB83 /* Brokers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22B46BD327CA652B0077FB83 /* Brokers.swift */; }; 22B46BDF27CA65750077FB83 /* SearchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22B46BDE27CA65750077FB83 /* SearchTests.swift */; }; // !$*UTF8*$! + 221C571F2466CC2800C0DD02 /* AWSIOTPresetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSIOTPresetTests.swift; sourceTree = "<group>"; }; +// !$*UTF8*$! 2230FF3127A8304E00D4327F /* Array+Remove.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF3027A8304E00D4327F /* Array+Remove.swift */; }; 22C386A322CB84900054C385 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C386A222CB84900054C385 /* RootView.swift */; }; 22C9F72F23B7486E00892C4B /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = 22C9F72E23B7486E00892C4B /* .swiftlint.yml */; }; @@ -180,7 +191,6 @@ 22D236FB23FF00E10003D87F /* StringUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22D236FA23FF00E10003D87F /* StringUtilsTests.swift */; }; 22D50F9722CE4C4300F37EAF /* Multimap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22D50F9622CE4C4300F37EAF /* Multimap.swift */; }; 22E469D5237FBEC500D72BD6 /* ReadMarkerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22E469D4237FBEC500D72BD6 /* ReadMarkerView.swift */; }; 22E469D7237FC03500D72BD6 /* Readstate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22E469D6237FC03500D72BD6 /* Readstate.swift */; }; - 22E469DB23801CA000D72BD6 /* Array+Move.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22E469DA23801CA000D72BD6 /* Array+Move.swift */; }; 22E8971722CFBFED00A4B8A3 /* TimeSeriesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22E8971622CFBFED00A4B8A3 /* TimeSeriesModel.swift */; }; 22E914DF25A8420700BEC599 /* HostFormModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22E914DE25A8420700BEC599 /* HostFormModelTests.swift */; }; 22F3AB1324A77B6600B2CF92 /* DataMigrationMoscapsuleDeprecation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22F3AB1224A77B6600B2CF92 /* DataMigrationMoscapsuleDeprecation.swift */; }; @@ -235,22 +245,23 @@ 22061AC227CA6BD300FFF915 /* XCUIElement+Checked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCUIElement+Checked.swift"; sourceTree = ""; }; 22061AC427CA79FE00FFF915 /* DispatchQueue+Background.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DispatchQueue+Background.swift"; sourceTree = "<group>"; }; 2209C86B23B720E7007C1D93 /* HostValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostValidator.swift; sourceTree = "<group>"; }; 220CCD622477F12300E8CA39 /* DataMigrationCertificateFiles.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataMigrationCertificateFiles.swift; sourceTree = "<group>"; }; +// !$*UTF8*$! 2205E5E1238A7EE2001638DF /* ButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2205E5E0238A7EE2001638DF /* ButtonStyle.swift */; }; - archiveVersion = 1; + +// !$*UTF8*$! 2205E5E1238A7EE2001638DF /* ButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2205E5E0238A7EE2001638DF /* ButtonStyle.swift */; }; - classes = { +/* Begin PBXBuildFile section */ 2205E5E1238A7EE2001638DF /* ButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2205E5E0238A7EE2001638DF /* ButtonStyle.swift */; }; - }; + archiveVersion = 1; + 2217BA3B27A3D2F900699428 /* AwaitMessagesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AwaitMessagesView.swift; sourceTree = "<group>"; }; 221C571D2466C9CD00C0DD02 /* HostFormModel+AWS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HostFormModel+AWS.swift"; sourceTree = "<group>"; }; 221C571F2466CC2800C0DD02 /* AWSIOTPresetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSIOTPresetTests.swift; sourceTree = "<group>"; }; + 222AC32F2803350600D66334 /* TestServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestServer.swift; sourceTree = "<group>"; }; 222EAAB0279FC289000E37AF /* CocoaMQTTRegression.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CocoaMQTTRegression.swift; sourceTree = "<group>"; }; 222EAAB227A31849000E37AF /* PropertyImageProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyImageProvider.swift; sourceTree = "<group>"; }; 222EAAB427A31CCB000E37AF /* PropertyImageProviderTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyImageProviderTest.swift; sourceTree = "<group>"; }; 222EAAB627A3CDEB000E37AF /* ConnectBrokerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectBrokerView.swift; sourceTree = "<group>"; }; 22061AC127CA682F00FFF915 /* Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22061AC027CA682F00FFF915 /* Search.swift */; }; -{ - 2230FEF527A435A000D4327F /* NavigationPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationPicker.swift; sourceTree = "<group>"; }; - 22061AC127CA682F00FFF915 /* Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22061AC027CA682F00FFF915 /* Search.swift */; }; classes = { 2230FEF927A50C8000D4327F /* TreeModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreeModelTests.swift; sourceTree = "<group>"; }; 2230FEFB27A50C9700D4327F /* TopicTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopicTree.swift; sourceTree = "<group>"; }; @@ -274,7 +285,6 @@ 2230FF2827A6BBB800D4327F /* TitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleView.swift; sourceTree = " "; }; 2230FF2A27A6BC1C00D4327F /* InformationDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InformationDetailView.swift; sourceTree = "<group>"; }; 2230FF2C27A6BC8900D4327F /* InformationContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InformationContainerView.swift; sourceTree = "<group>"; }; 2230FF2E27A6CDB800D4327F /* Welcome.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Welcome.swift; sourceTree = "<group>"; }; - 2230FF3027A8304E00D4327F /* Array+Remove.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Remove.swift"; sourceTree = "<group>"; }; 2230FF3227A830C900D4327F /* DataProtocol+Hex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataProtocol+Hex.swift"; sourceTree = "<group>"; }; 2231894A27F08BAB001177F4 /* ViewSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewSelection.swift; sourceTree = "<group>"; }; 223AF5D02477D575009810E6 /* FileLister.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileLister.swift; sourceTree = "<group>"; }; @@ -316,7 +326,6 @@ 2256BDA9246FBE5100F92EFE /* DataMigrationEmptyTopic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataMigrationEmptyTopic.swift; sourceTree = " "; }; 2263C1D824813B0C00FF14E8 /* CertificateFilesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificateFilesModel.swift; sourceTree = "<group>"; }; 226A6B4F244457E100ACDFC3 /* MqttClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MqttClient.swift; sourceTree = "<group>"; }; 226A6B5124445BA400ACDFC3 /* CocoaMQTTClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CocoaMQTTClient.swift; sourceTree = "<group>"; }; - 226A6B5324448ECB00ACDFC3 /* MqttClientSharedUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MqttClientSharedUtils.swift; sourceTree = "<group>"; }; 226A6B5524449F5400ACDFC3 /* ProtocolPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolPicker.swift; sourceTree = "<group>"; }; 226A6B5C2445748500ACDFC3 /* HostFormModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostFormModel.swift; sourceTree = "<group>"; }; 226A6B5E2445754D00ACDFC3 /* ServerFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerFormView.swift; sourceTree = "<group>"; }; @@ -361,6 +370,8 @@ 2291284624685A92006F8256 /* DataMigrationAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataMigrationAuth.swift; sourceTree = " "; }; 2291284824685AE0006F8256 /* DataMigrationLimits.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataMigrationLimits.swift; sourceTree = "<group>"; }; 2291424B23BF78000086C251 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; }; 2291424E23C0C0370086C251 /* MenuButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuButton.swift; sourceTree = "<group>"; }; + 22953B30280A9206000F8F37 /* HostFormModel+ClientCerts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "HostFormModel+ClientCerts.swift"; sourceTree = "<group>"; }; + 22953B32280A92D8000F8F37 /* ClientCertsHelpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientCertsHelpView.swift; sourceTree = "<group>"; }; 229D30C923197D8200632896 /* CocoaAsyncSocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CocoaAsyncSocket.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 229D30CA23197D8200632896 /* CocoaMQTT.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CocoaMQTT.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 229FCAEC247D67BC00490628 /* DocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentPickerView.swift; sourceTree = "<group>"; }; @@ -372,6 +383,10 @@ 22A386FA240941B600DF8F94 /* CertificateFilePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificateFilePickerView.swift; sourceTree = " "; }; 22A386FC2409440F00DF8F94 /* InfoBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoBox.swift; sourceTree = "<group>"; }; 22A387042409768100DF8F94 /* HostModelPersistenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostModelPersistenceTests.swift; sourceTree = "<group>"; }; 22A38706240BD9E600DF8F94 /* TimeSeriesValueUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeSeriesValueUtil.swift; sourceTree = "<group>"; }; + 22AB2C0E280076CB00E88875 /* String+RegExp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+RegExp.swift"; sourceTree = "<group>"; }; + 22AB2C102800771700E88875 /* CocoaMQTTClient+ErrorMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CocoaMQTTClient+ErrorMessage.swift"; sourceTree = "<group>"; }; + 22AB2C122800854700E88875 /* MqttClientCocoaMQTTTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MqttClientCocoaMQTTTests.swift; sourceTree = "<group>"; }; + 22AB2C142801D8A400E88875 /* ConfigurationMQTTTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationMQTTTests.swift; sourceTree = "<group>"; }; 22AE64332412636300C2C4FE /* DiagramPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiagramPath.swift; sourceTree = "<group>"; }; 22AE64352412637A00C2C4FE /* TimeSeriesValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeSeriesValue.swift; sourceTree = "<group>"; }; 22AE643724126A7500C2C4FE /* DiagramPathTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiagramPathTests.swift; sourceTree = "<group>"; }; @@ -386,6 +401,7 @@ 22B46BD127CA652B0077FB83 /* MessageTopicUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageTopicUtils.swift; sourceTree = " "; }; 22B46BD227CA652B0077FB83 /* Broker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Broker.swift; sourceTree = "<group>"; }; 22B46BD327CA652B0077FB83 /* Brokers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Brokers.swift; sourceTree = "<group>"; }; 22B46BDE27CA65750077FB83 /* SearchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTests.swift; sourceTree = "<group>"; }; + 22B90E872805B2DE0083C6E5 /* SubscriptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionTests.swift; sourceTree = "<group>"; }; 22C2856124759FD40000C1E8 /* CertificateLocationPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificateLocationPicker.swift; sourceTree = "<group>"; }; 22C386A222CB84900054C385 /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = "<group>"; }; 22C7F0D02416A16600534880 /* MQTTAnalyzerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MQTTAnalyzerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -402,7 +418,6 @@ 22D236FA23FF00E10003D87F /* StringUtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringUtilsTests.swift; sourceTree = " "; }; 22D50F9622CE4C4300F37EAF /* Multimap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Multimap.swift; sourceTree = "<group>"; }; 22E469D4237FBEC500D72BD6 /* ReadMarkerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMarkerView.swift; sourceTree = "<group>"; }; 22E469D6237FC03500D72BD6 /* Readstate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Readstate.swift; sourceTree = "<group>"; }; - 22E469DA23801CA000D72BD6 /* Array+Move.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Move.swift"; sourceTree = "<group>"; }; 22E8971622CFBFED00A4B8A3 /* TimeSeriesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeSeriesModel.swift; sourceTree = "<group>"; }; 22E914DE25A8420700BEC599 /* HostFormModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostFormModelTests.swift; sourceTree = "<group>"; }; 22F3AB1224A77B6600B2CF92 /* DataMigrationMoscapsuleDeprecation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataMigrationMoscapsuleDeprecation.swift; sourceTree = "<group>"; }; @@ -488,22 +503,13 @@ children = ( 226A6B5124445BA400ACDFC3 /* CocoaMQTTClient.swift */, 2203571E2445852800A98CD3 /* CocoaMQTTCertificateFiles.swift */, 220357202445B2ED00A98CD3 /* MQTTDelegate.swift */, - classes = { // !$*UTF8*$! - objects = { - 2230FF0627A5668200D4327F /* TopicTree+Counter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF0527A5668200D4327F /* TopicTree+Counter.swift */; }; { - classes = { archiveVersion = 1; - }; - 2230FEF227A433F300D4327F /* navigation */ = { - isa = PBXGroup; - children = ( - 2230FEF527A435A000D4327F /* NavigationPicker.swift */, - 2230FEF327A4340400D4327F /* NavigationModeFormView.swift */, + ); 2230FF0627A5668200D4327F /* TopicTree+Counter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF0527A5668200D4327F /* TopicTree+Counter.swift */; }; - objectVersion = 52; +{ sourceTree = "<group>"; }; 2230FF0027A5206400D4327F /* v2 */ = { @@ -650,42 +656,44 @@ isa = PBXGroup; children = ( 2253F8F122C8C008007E35A2 /* Info.plist */, }; - 2230FEF627A435A000D4327F /* NavigationPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FEF527A435A000D4327F /* NavigationPicker.swift */; }; + 2230FF0E27A650CE00D4327F /* TreeUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF0D27A650CE00D4327F /* TreeUtils.swift */; }; }; - 2230FEF827A4434800D4327F /* DataMigrationNavigationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FEF727A4434800D4327F /* DataMigrationNavigationMode.swift */; }; + 2230FF1627A6669C00D4327F /* MsgMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF1527A6669C00D4327F /* MsgMetadata.swift */; }; 2230FF1E27A6981900D4327F /* MessageLimitReachedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF1D27A6981900D4327F /* MessageLimitReachedView.swift */; }; + objectVersion = 52; 2230FF1E27A6981900D4327F /* MessageLimitReachedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF1D27A6981900D4327F /* MessageLimitReachedView.swift */; }; -// !$*UTF8*$! + 2230FF1E27A6981900D4327F /* MessageLimitReachedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF1D27A6981900D4327F /* MessageLimitReachedView.swift */; }; -{ +// !$*UTF8*$! 2230FF1E27A6981900D4327F /* MessageLimitReachedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF1D27A6981900D4327F /* MessageLimitReachedView.swift */; }; - archiveVersion = 1; + }; 2230FF1E27A6981900D4327F /* MessageLimitReachedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF1D27A6981900D4327F /* MessageLimitReachedView.swift */; }; - classes = { +/* Begin PBXBuildFile section */ 2230FF1E27A6981900D4327F /* MessageLimitReachedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF1D27A6981900D4327F /* MessageLimitReachedView.swift */; }; - }; + archiveVersion = 1; }; - 2230FF0C27A57DF500D4327F /* TopicTree+ReadState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF0B27A57DF400D4327F /* TopicTree+ReadState.swift */; }; }; - 2230FF0E27A650CE00D4327F /* TreeUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF0D27A650CE00D4327F /* TreeUtils.swift */; }; + archiveVersion = 1; }; - classes = { + archiveVersion = 1; - 2230FF1E27A6981900D4327F /* MessageLimitReachedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF1D27A6981900D4327F /* MessageLimitReachedView.swift */; }; + 2285C81C27847786008DA37D /* MetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2285C81B27847786008DA37D /* MetadataView.swift */; }; /* Begin PBXBuildFile section */ }; + 2230FEF827A4434800D4327F /* DataMigrationNavigationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FEF727A4434800D4327F /* DataMigrationNavigationMode.swift */; }; }; + 2230FF0627A5668200D4327F /* TopicTree+Counter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF0527A5668200D4327F /* TopicTree+Counter.swift */; }; 2230FF2227A6AEC800D4327F /* TopicTree+Flatten.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF2127A6AEC800D4327F /* TopicTree+Flatten.swift */; }; -// !$*UTF8*$! }; - 2230FF1827A666C600D4327F /* MsgMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF1727A666C600D4327F /* MsgMessage.swift */; }; + 2230FEFA27A50C8000D4327F /* TreeModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FEF927A50C8000D4327F /* TreeModelTests.swift */; }; 2230FF2227A6AEC800D4327F /* TopicTree+Flatten.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF2127A6AEC800D4327F /* TopicTree+Flatten.swift */; }; - archiveVersion = 1; + objectVersion = 52; - }; 2230FF1E27A6981900D4327F /* MessageLimitReachedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF1D27A6981900D4327F /* MessageLimitReachedView.swift */; }; +{ 2230FF2327A6B80900D4327F /* TopicLimitTests.swift */, 2230FF2227A6AEC800D4327F /* TopicTree+Flatten.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF2127A6AEC800D4327F /* TopicTree+Flatten.swift */; }; - objectVersion = 52; +{ + 2230FF0F27A6511F00D4327F /* TreeUtilsTests.swift */, ); path = MQTTAnalyzerTests; sourceTree = "<group>"; @@ -691,6 +700,7 @@ }; 226A6B5B2445740C00ACDFC3 /* form */ = { isa = PBXGroup; children = ( + 22953B2F280A91DC000F8F37 /* client-certs */, 2200D97927C2381100E63E89 /* aws-iot */, 2285C81927842176008DA37D /* DisconnectedView.swift */, 222EAAB627A3CDEB000E37AF /* ConnectBrokerView.swift */, @@ -703,7 +713,6 @@ 2285C80A2781E89B008DA37D /* TopicLimitReachedView.swift */, 226A6B682445761200ACDFC3 /* server */, 226A6B692445763A00ACDFC3 /* auth */, 226A6B6A2445765C00ACDFC3 /* topic */, - 2230FEF227A433F300D4327F /* navigation */, 226A6B6B2445767800ACDFC3 /* more */, 226A6B5C2445748500ACDFC3 /* HostFormModel.swift */, 22FD7CF922C8D2650078795F /* EditHostFormView.swift */, @@ -777,14 +786,12 @@ 22810479238170A200112F24 /* extensions */ = { isa = PBXGroup; children = ( 2230FF2F27A6CDB800D4327F /* Welcome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF2E27A6CDB800D4327F /* Welcome.swift */; }; - classes = { - 2230FF3027A8304E00D4327F /* Array+Remove.swift */, - 2230FF2F27A6CDB800D4327F /* Welcome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FF2E27A6CDB800D4327F /* Welcome.swift */; }; objectVersion = 52; 22F8BEE623C24A5800422BFF /* String+Utils.swift */, 2230FEFD27A50E9000D4327F /* Date+Iso.swift */, 224AFEA627AE88DC00DFD09F /* Color+SystemColors.swift */, 22061AC427CA79FE00FFF915 /* DispatchQueue+Background.swift */, + 22AB2C0E280076CB00E88875 /* String+RegExp.swift */, ); path = extensions; sourceTree = "<group>"; @@ -887,8 +894,6 @@ children = ( 228104902381770000112F24 /* FillingText.swift */, 22A386FC2409440F00DF8F94 /* InfoBox.swift */, 223AF5D52477D5F5009810E6 /* FileItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 223AF5D42477D5F5009810E6 /* FileItemView.swift */; }; - }; - 223AF5D52477D5F5009810E6 /* FileItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 223AF5D42477D5F5009810E6 /* FileItemView.swift */; }; objectVersion = 52; 224AFEAA27AEF98700DFD09F /* CustomListView.swift */, 224AFEAC27AEF9BB00DFD09F /* TupleView+GetChildren.swift */, @@ -896,6 +901,15 @@ ); path = common; sourceTree = "<group>"; }; + 22953B2F280A91DC000F8F37 /* client-certs */ = { + isa = PBXGroup; + children = ( + 22953B30280A9206000F8F37 /* HostFormModel+ClientCerts.swift */, + 22953B32280A92D8000F8F37 /* ClientCertsHelpView.swift */, + ); + path = "client-certs"; + sourceTree = "<group>"; + }; 22B46BCB27CA640A0077FB83 /* Recovered References */ = { isa = PBXGroup; children = ( @@ -935,8 +949,13 @@ 22B46BDE27CA65750077FB83 /* SearchTests.swift */, 227D987E27B7A0EB00E45C4C /* AboutDialogTests.swift */, 227D987827B7A01300E45C4C /* ReadStateTests.swift */, 2200D96527C0C3BF00E63E89 /* PublishTests.swift */, + 22B90E872805B2DE0083C6E5 /* SubscriptionTests.swift */, 227D987A27B7A05C00E45C4C /* ScreenshotTests.swift */, + 22117B6E2805C93300D0117D /* AbstractConfigurationTests.swift */, + 22AB2C142801D8A400E88875 /* ConfigurationMQTTTests.swift */, + 22117B702805C95A00D0117D /* ConfigurationWebSocketTests.swift */, 22C7F0D42416A16600534880 /* Info.plist */, + 222AC32F2803350600D66334 /* TestServer.swift */, ); path = MQTTAnalyzerUITests; sourceTree = "<group>"; @@ -973,7 +992,6 @@ children = ( 2203571D2445850F00A98CD3 /* cocoamqtt */, 22FD7CF822C8D2650078795F /* MQTTSessionController.swift */, 226A6B4F244457E100ACDFC3 /* MqttClient.swift */, - 226A6B5324448ECB00ACDFC3 /* MqttClientSharedUtils.swift */, ); path = mqtt; sourceTree = "<group>"; @@ -1313,8 +1331,6 @@ 22A386FB240941B600DF8F94 /* CertificateFilePickerView.swift in Sources */, 22F3AB1324A77B6600B2CF92 /* DataMigrationMoscapsuleDeprecation.swift in Sources */, 22FD7D0222C8D2660078795F /* RootModel.swift in Sources */, 2256BDAA246FBE5100F92EFE /* DataMigrationEmptyTopic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2256BDA9246FBE5100F92EFE /* DataMigrationEmptyTopic.swift */; }; - classes = { - 2256BDAA246FBE5100F92EFE /* DataMigrationEmptyTopic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2256BDA9246FBE5100F92EFE /* DataMigrationEmptyTopic.swift */; }; }; 224AFEAF27B0600D00DFD09F /* StubPersistence.swift in Sources */, 22C2856224759FD40000C1E8 /* CertificateLocationPicker.swift in Sources */, @@ -1328,7 +1344,6 @@ 223AF5D72477D60A009810E6 /* PKCS12HelpView.swift in Sources */, 2281048A2381740000112F24 /* MessageView.swift in Sources */, 2253F8D822C8C007007E35A2 /* SceneDelegate.swift in Sources */, 2256BDAA246FBE5100F92EFE /* DataMigrationEmptyTopic.swift in Sources */, - 2230FEF627A435A000D4327F /* NavigationPicker.swift in Sources */, 2230FEF827A4434800D4327F /* DataMigrationNavigationMode.swift in Sources */, 22E469D5237FBEC500D72BD6 /* ReadMarkerView.swift in Sources */, 2291283C24681CAD006F8256 /* SubscriptionDetailsView.swift in Sources */, @@ -1345,13 +1360,10 @@ 2230FF2927A6BBB800D4327F /* TitleView.swift in Sources */, 22C9F73123B78F9700892C4B /* PublishMessageFormView.swift in Sources */, 2253F8DD22C8C007007E35A2 /* TopicsView.swift in Sources */, 226A6B5224445BA400ACDFC3 /* CocoaMQTTClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 226A6B5124445BA400ACDFC3 /* CocoaMQTTClient.swift */; }; - classes = { - 226A6B5224445BA400ACDFC3 /* CocoaMQTTClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 226A6B5124445BA400ACDFC3 /* CocoaMQTTClient.swift */; }; }; 228104852381727B00112F24 /* MessageDetailsPlainTextView.swift in Sources */, 2230FF2D27A6BC8900D4327F /* InformationContainerView.swift in Sources */, 2285C82527887F79008DA37D /* TopicSuffixPickerView.swift in Sources */, - 221C571C2466847800C0DD02 /* QuestionBox.swift in Sources */, 229FCAED247D67BC00490628 /* DocumentPickerView.swift in Sources */, 221C571E2466C9CD00C0DD02 /* HostFormModel+AWS.swift in Sources */, 22FD7D0322C8D2660078795F /* HostsView.swift in Sources */, @@ -1359,7 +1371,6 @@ 22A386F724093EA200DF8F94 /* UsernamePasswordAuthenticationView.swift in Sources */, 2285C81A27842176008DA37D /* DisconnectedView.swift in Sources */, 22A386F92409404600DF8F94 /* CertificateAuthenticationView.swift in Sources */, 2291284324685959006F8256 /* MigrationHelper.swift in Sources */, - 2230FF3127A8304E00D4327F /* Array+Remove.swift in Sources */, 2291284724685A92006F8256 /* DataMigrationAuth.swift in Sources */, 2263C1D924813B0C00FF14E8 /* CertificateFilesModel.swift in Sources */, 22FD7D0522C8D2820078795F /* DataSeriesDetailsView.swift in Sources */, @@ -1375,6 +1386,7 @@ 228104932381773100112F24 /* TopicCellView.swift in Sources */, 228104832381723D00112F24 /* MessageDetailsJsonView.swift in Sources */, 2230FF2F27A6CDB800D4327F /* Welcome.swift in Sources */, 22E469D7237FC03500D72BD6 /* Readstate.swift in Sources */, + 22AB2C112800771700E88875 /* CocoaMQTTClient+ErrorMessage.swift in Sources */, 2231894B27F08BAB001177F4 /* ViewSelection.swift in Sources */, 2285C80C2781E89B008DA37D /* LimitReachedView.swift in Sources */, 2291283E24682494006F8256 /* TopicCell.swift in Sources */, @@ -1384,6 +1396,7 @@ 222EAAB327A31849000E37AF /* PropertyImageProvider.swift in Sources */, 22FD7CFE22C8D2660078795F /* MessageDetailsView.swift in Sources */, 22AF3AEB23891267001D9F87 /* HostSettingExamples.swift in Sources */, 226A6B50244457E100ACDFC3 /* MqttClient.swift in Sources */, + 22953B33280A92D8000F8F37 /* ClientCertsHelpView.swift in Sources */, 22F6057B23D4911000E6338B /* DataMigration.swift in Sources */, 2285C81C27847786008DA37D /* MetadataView.swift in Sources */, 2291284524685A13006F8256 /* DataMigrationClientImpl.swift in Sources */, @@ -1407,8 +1420,10 @@ 22C9F73723B79A1300892C4B /* MessageTextView.swift in Sources */, 2285C81227841C72008DA37D /* ResumeConnectionView.swift in Sources */, 223AF5D92477D620009810E6 /* NoFilesHelpView.swift in Sources */, 223AF5D52477D5F5009810E6 /* FileItemView.swift in Sources */, - 226A6B632445759600ACDFC3 /* ClientIDFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 226A6B622445759600ACDFC3 /* ClientIDFormView.swift */; }; + 2200D96B27C1123000E63E89 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2200D96A27C1123000E63E89 /* Logger.swift */; }; }; + archiveVersion = 1; + 22953B31280A9206000F8F37 /* HostFormModel+ClientCerts.swift in Sources */, 226A6B632445759600ACDFC3 /* ClientIDFormView.swift in Sources */, 226A6B67244575DB00ACDFC3 /* AuthFormView.swift in Sources */, 2230FF3327A830C900D4327F /* DataProtocol+Hex.swift in Sources */, @@ -1433,6 +1448,8 @@ 221C57202466CC2800C0DD02 /* AWSIOTPresetTests.swift in Sources */, 22A387052409768100DF8F94 /* HostModelPersistenceTests.swift in Sources */, 2230FF2427A6B80900D4327F /* TopicLimitTests.swift in Sources */, // !$*UTF8*$! + 2230FF2C27A6BC8900D4327F /* InformationContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InformationContainerView.swift; sourceTree = "<group>"; }; +// !$*UTF8*$! 2200D96D27C1149300E63E89 /* LoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2200D96C27C1149300E63E89 /* LoggerTests.swift */; }; 2230FF1027A6511F00D4327F /* TreeUtilsTests.swift in Sources */, 22E914DF25A8420700BEC599 /* HostFormModelTests.swift in Sources */, @@ -1457,6 +1474,8 @@ buildActionMask = 2147483647; files = ( 22B46BDA27CA652B0077FB83 /* Brokers.swift in Sources */, // !$*UTF8*$! + 2230FF2E27A6CDB800D4327F /* Welcome.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Welcome.swift; sourceTree = "<group>"; }; +// !$*UTF8*$! 221C571C2466847800C0DD02 /* QuestionBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 221C571B2466847800C0DD02 /* QuestionBox.swift */; }; 2240834927AC533A00AA4A42 /* SnapshotHelper.swift in Sources */, 22061AC327CA6BD300FFF915 /* XCUIElement+Checked.swift in Sources */, @@ -1466,10 +1485,16 @@ 22B46BDF27CA65750077FB83 /* SearchTests.swift in Sources */, 22B46BD627CA652B0077FB83 /* ExampleMessages.swift in Sources */, 227D987B27B7A05C00E45C4C /* ScreenshotTests.swift in Sources */, // !$*UTF8*$! + 2230FF3027A8304E00D4327F /* Array+Remove.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Remove.swift"; sourceTree = "<group>"; }; + 22B90E882805B2DE0083C6E5 /* SubscriptionTests.swift in Sources */, +// !$*UTF8*$! 2230FEF627A435A000D4327F /* NavigationPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FEF527A435A000D4327F /* NavigationPicker.swift */; }; 2200D97B27C2675300E63E89 /* AbstractUITests.swift in Sources */, // !$*UTF8*$! + 2231894A27F08BAB001177F4 /* ViewSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewSelection.swift; sourceTree = "<group>"; }; +// !$*UTF8*$! classes = { + 2285C82327887CDC008DA37D /* PublishMessageFormModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2285C82227887CDC008DA37D /* PublishMessageFormModelTests.swift */; }; 22061AC127CA682F00FFF915 /* Search.swift in Sources */, 224AFEB727B2C67900DFD09F /* XCUIElement+ClearText.swift in Sources */, 22B46BD727CA652B0077FB83 /* ScreenshotIds.swift in Sources */, @@ -1635,8 +1660,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = MQTTAnalyzer/MQTTAnalyzer.entitlements; CODE_SIGN_STYLE = Automatic; // !$*UTF8*$! + 2209C86C23B720E7007C1D93 /* HostValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2209C86B23B720E7007C1D93 /* HostValidator.swift */; }; // !$*UTF8*$! - 2230FEFA27A50C8000D4327F /* TreeModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FEF927A50C8000D4327F /* TreeModelTests.swift */; }; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_ASSET_PATHS = "MQTTAnalyzer/Preview\\ Content"; DEVELOPMENT_TEAM = 643R6YSRER; @@ -1648,7 +1673,7 @@ "$(inherited)", "@executable_path/Frameworks", ); // !$*UTF8*$! - 22AE64362412637A00C2C4FE /* TimeSeriesValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AE64352412637A00C2C4FE /* TimeSeriesValue.swift */; }; + 223AF5D42477D5F5009810E6 /* FileItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileItemView.swift; sourceTree = "<group>"; }; PRODUCT_BUNDLE_IDENTIFIER = de.rnd7.MQTTAnalyzer; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1665,8 +1690,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = MQTTAnalyzer/MQTTAnalyzer.entitlements; CODE_SIGN_STYLE = Automatic; // !$*UTF8*$! + 2209C86C23B720E7007C1D93 /* HostValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2209C86B23B720E7007C1D93 /* HostValidator.swift */; }; // !$*UTF8*$! - 2230FEFA27A50C8000D4327F /* TreeModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2230FEF927A50C8000D4327F /* TreeModelTests.swift */; }; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_ASSET_PATHS = "MQTTAnalyzer/Preview\\ Content"; DEVELOPMENT_TEAM = 643R6YSRER; @@ -1678,7 +1703,7 @@ "$(inherited)", "@executable_path/Frameworks", ); // !$*UTF8*$! - 22AE64362412637A00C2C4FE /* TimeSeriesValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AE64352412637A00C2C4FE /* TimeSeriesValue.swift */; }; + 223AF5D42477D5F5009810E6 /* FileItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileItemView.swift; sourceTree = "<group>"; }; PRODUCT_BUNDLE_IDENTIFIER = de.rnd7.MQTTAnalyzer; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; diff --git a/src/MQTTAnalyzer.xcodeproj/xcshareddata/xcschemes/MQTTAnalyzer.xcscheme b/src/MQTTAnalyzer.xcodeproj/xcshareddata/xcschemes/MQTTAnalyzer.xcscheme index af2e35898d54d0a5b27d2967ba21664924590c76..a7b947968ab35626a1362db02694c58dd8f49f04 100644 --- a/src/MQTTAnalyzer.xcodeproj/xcshareddata/xcschemes/MQTTAnalyzer.xcscheme +++ b/src/MQTTAnalyzer.xcodeproj/xcshareddata/xcschemes/MQTTAnalyzer.xcscheme @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme LastUpgradeVersion = "1300" - version = "1.3"> + version = "1.7"> <BuildAction parallelizeBuildables = "YES" buildImplicitDependencies = "YES"> @@ -42,6 +42,18 @@ value = "" isEnabled = "YES"> </AdditionalOption> </AdditionalOptions> + <TestPlans> + <TestPlanReference + reference = "container:TestPlanThreads.xctestplan"> + </TestPlanReference> + <TestPlanReference + reference = "container:UnitTestPlan.xctestplan"> + </TestPlanReference> + <TestPlanReference + reference = "container:ReleaseTestPlan.xctestplan" + default = "YES"> + </TestPlanReference> + </TestPlans> <Testables> <TestableReference skipped = "NO"> diff --git a/src/MQTTAnalyzer.xcworkspace/contents.xcworkspacedata b/src/MQTTAnalyzer.xcworkspace/contents.xcworkspacedata index 4375d9683343d39af80577b61cc59e24c3254249..1a32435bbbb4f7f0bf04a07d402401f58a30a112 100644 --- a/src/MQTTAnalyzer.xcworkspace/contents.xcworkspacedata +++ b/src/MQTTAnalyzer.xcworkspace/contents.xcworkspacedata @@ -7,4 +7,20 @@ <FileRef location = "group:Pods/Pods.xcodeproj"> </FileRef> + <Group + location = "container:" + name = "TestPlans"> + <FileRef + location = "group:/Users/philipparndt/dev/smarthome/projects/mqtt-analyzer/src/TestPlan.xctestplan"> + </FileRef> + <FileRef + location = "group:TestPlanThreads.xctestplan"> + </FileRef> + <FileRef + location = "group:UnitTestPlan.xctestplan"> + </FileRef> + <FileRef + location = "group:ReleaseTestPlan.xctestplan"> + </FileRef> + </Group> </Workspace> diff --git a/src/MQTTAnalyzer.xcworkspace/xcshareddata/swiftpm/Package.resolved b/src/MQTTAnalyzer.xcworkspace/xcshareddata/swiftpm/Package.resolved index a2ea5c3ba8f5daeca43afa5b290962f8bba36e69..b868ca01e763648997a8318129eaaa14c48cae5c 100644 --- a/src/MQTTAnalyzer.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/src/MQTTAnalyzer.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,22 +1,24 @@ { - "object": { - "pins": [ - { - "package": "Dynamic", - "repositoryURL": "https://github.com/mhdhejazi/Dynamic.git", +{ "state": { +{ "branch": null, +{ "revision": "ab9a2570862d54aed2663691bb767f881226a12f", -{ + "object": { -{ + "object": { { -{ + "object": { "object": { -{ + "object": { "pins": [ -{ + "object": { { { + "object": { + "object": { "package": "Dynamic", -{ + "object": { "repositoryURL": "https://github.com/mhdhejazi/Dynamic.git", + "version" : 2 +} diff --git a/src/MQTTAnalyzerTests/AWSIOTPresetTests.swift b/src/MQTTAnalyzerTests/AWSIOTPresetTests.swift index 416f2206e66f46884e064b52b9662b3aa34a0fd6..d74f22c14a1efd839f5d489ea843d3a8611cd8e6 100644 --- a/src/MQTTAnalyzerTests/AWSIOTPresetTests.swift +++ b/src/MQTTAnalyzerTests/AWSIOTPresetTests.swift @@ -15,25 +15,26 @@ func testNoSuggestChangeForOtherHosts() { var model = HostFormModel() model.hostname = "piiot" +// MQTTAnalyzerTests // -// Created by Philipp Arndt on 2020-05-09. model.hostname = "test.mosquitto.org" +// MQTTAnalyzerTests // -// Created by Philipp Arndt on 2020-05-09. } func testSuggestChange() { var model = HostFormModel() model.hostname = "1234-ats.iot.some.amazonaws.com" +// MQTTAnalyzerTests // AWSIOTPresetTests.swift } func testNoSuggestChangeAfterApply() { var model = HostFormModel() model.hostname = "1234-ats.iot.some.amazonaws.com" model.updateSettingsForAWSIOT() +// MQTTAnalyzerTests // -// Created by Philipp Arndt on 2020-05-09. } func testSettingsAfterApply() { diff --git a/src/MQTTAnalyzerTests/CocoaMQTTRegression.swift b/src/MQTTAnalyzerTests/CocoaMQTTRegression.swift index 770672e1b4c094f3fa6cb63c320fe9fad9208c94..806dd137f6cb957f895cfda54955805125165405 100644 --- a/src/MQTTAnalyzerTests/CocoaMQTTRegression.swift +++ b/src/MQTTAnalyzerTests/CocoaMQTTRegression.swift @@ -12,7 +12,7 @@ class CocoaMQTTRegressionTests: XCTestCase { func testDecodeBinary789c8d() { // -// +class CocoaMQTTRegressionTests: XCTestCase { let publish = MqttDecodePublish() publish.decodePublish(fixedHeader: 49, publishData: diff --git a/src/MQTTAnalyzerTests/Info.plist b/src/MQTTAnalyzerTests/Info.plist index be5b286e45ac2d540e98c5fec4c906a8b066dbcc..ce04f3fd7a74c403c42526948ec988648d63616d 100644 --- a/src/MQTTAnalyzerTests/Info.plist +++ b/src/MQTTAnalyzerTests/Info.plist @@ -17,5 +18,6 @@ CFBundleShortVersionString <string>1.0</string> <key>CFBundleVersion</key> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> </dict> </plist> diff --git a/src/MQTTAnalyzerTests/MqttClientCocoaMQTTTests.swift b/src/MQTTAnalyzerTests/MqttClientCocoaMQTTTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..6f65598cda013f3d71be720939ed571c85dc2413 --- /dev/null +++ b/src/MQTTAnalyzerTests/MqttClientCocoaMQTTTests.swift @@ -0,0 +1,35 @@ +// +// MqttClientCocoaMQTTTests.swift +// MQTTAnalyzerTests +// +// Created by Philipp Arndt on 08.04.22. +// Copyright © 2022 Philipp Arndt. All rights reserved. +// + +import XCTest +import Network +@testable import MQTTAnalyzer + +class MqttClientCocoaMQTTTests: XCTestCase { + + func testInvalidHostname() throws { + let msg = MqttClientCocoaMQTT.extractErrorMessage(error: NSError(domain: "", code: 8)) + XCTAssertEqual("Invalid hostname.\nThe operation couldn’t be completed. ( error 8.)", msg) + } + + func testConnectionRefused() throws { + let msg = MqttClientCocoaMQTT.extractErrorMessage(error: NWError.posix(POSIXErrorCode.ECONNREFUSED)) + XCTAssertTrue(msg.contains("Connection refused"), msg) + } + + func testConnectionTLS() throws { + let msg = MqttClientCocoaMQTT.extractErrorMessage(error: NWError.tls(-9407)) + XCTAssertEqual("Network.NWError: -9407: Optional(OSStatus -9407)", msg) + } + + func testConnectionTLSBadCertificate() throws { + let msg = MqttClientCocoaMQTT.extractErrorMessage(error: NWError.tls(-9808)) + XCTAssertEqual("Bad certificate format, check all properties, like SAN, ... (-9808)", msg) + } + +} diff --git a/src/MQTTAnalyzerTests/TreeModelTests.swift b/src/MQTTAnalyzerTests/TreeModelTests.swift index 4884fea6a76f07bc71eb7f78a3145936aaf2f4ec..cffb6cd5d94d2fc64d8bb1299e2b7790d3555f61 100644 --- a/src/MQTTAnalyzerTests/TreeModelTests.swift +++ b/src/MQTTAnalyzerTests/TreeModelTests.swift @@ -156,4 +156,16 @@ XCTAssertFalse(msg.topic.readStateCombined) XCTAssertFalse(root.readStateCombined) } + + func testRegressionTopicStartsWithSlash() throws { + let root = TopicTree() + let withSlash = root.addTopic(topic: "/test")! + let withoutSlash = root.addTopic(topic: "test")! + let withMultipleSlashes = root.addTopic(topic: "///test")! + XCTAssertEqual("/test", withSlash.nameQualified) + XCTAssertEqual("test", withoutSlash.nameQualified) + XCTAssertEqual("///test", withMultipleSlashes.nameQualified) + XCTAssertEqual(2, root.children.count) + } + } diff --git a/src/MQTTAnalyzerUITests/AbstractConfigurationTests.swift b/src/MQTTAnalyzerUITests/AbstractConfigurationTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..13f4eff5f960ad3c24166647ed6df8e296c1120a --- /dev/null +++ b/src/MQTTAnalyzerUITests/AbstractConfigurationTests.swift @@ -0,0 +1,39 @@ +// +// MQTTAnalyzerUITests+Broker.swift +// MQTTAnalyzerUITests +// +// Created by Philipp Arndt on 2022-02-12. +// Copyright © 2022 Philipp Arndt. All rights reserved. +// + +import XCTest +import RealmSwift + +class AbstractConfigurationTests: AbstractUITests { + let hostname = TestServer.getTestServer() + + func assertWithBroker(_ broker: Broker, credentials: Credentials? = nil) { + let id = Navigation.id() + let brokers = Brokers(app: app) + + app.launch() + + let nav = Navigation(app: app, alias: broker.alias!) + + brokers.create(broker: broker) + brokers.start(alias: broker.alias!, waitConnected: false) + + if let credentials = credentials { + brokers.login(credentials: credentials) + } + + brokers.waitUntilConnected() + + let dialog = PublishDialog(app: app) + dialog.open() + dialog.fill(topic: "\(id)topic", message: "msg") + dialog.apply() + + nav.navigate(to: "\(id)topic") + } +} diff --git a/src/MQTTAnalyzerUITests/BrokerTests.swift b/src/MQTTAnalyzerUITests/BrokerTests.swift index 0eea5540cfa51c1d42847123471c0a7b1d5cad2e..c0e71fea478c0a3347d52349bbf8c052a64e2596 100644 --- a/src/MQTTAnalyzerUITests/BrokerTests.swift +++ b/src/MQTTAnalyzerUITests/BrokerTests.swift @@ -36,7 +36,7 @@ awaitAppear(element: example) brokers.delete(alias: alias) brokers.cancelDelete(alias: alias) XCTAssertFalse(app.buttons["Delete"].exists) - XCTAssertTrue(example.exists) + XCTAssertTrue(example.exists, "Expected example to be still there") } func testRenameBroker() { diff --git a/src/MQTTAnalyzerUITests/ConfigurationMQTTTests.swift b/src/MQTTAnalyzerUITests/ConfigurationMQTTTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..b944a414f292c1ea5d277ae6f2c3e84d8bfc44f0 --- /dev/null +++ b/src/MQTTAnalyzerUITests/ConfigurationMQTTTests.swift @@ -0,0 +1,60 @@ +// +// MQTTAnalyzerUITests+Broker.swift +// MQTTAnalyzerUITests +// +// Created by Philipp Arndt on 2022-02-12. +// Copyright © 2022 Philipp Arndt. All rights reserved. +// + +import XCTest +import RealmSwift + +class ConfigurationMQTTTests: AbstractConfigurationTests { + func testMQTTNoAuth() { + assertWithBroker( + Broker( + alias: "1883", + hostname: hostname, + port: 1883 + ) + ) + } + + func testMQTTPersistedAuth() { + assertWithBroker( + Broker( + alias: "1884", + hostname: hostname, + port: 1884, + authType: .userPassword, + username: "admin", + password: "password" + ) + ) + } + + func testMQTTPersistedUsername() { + assertWithBroker( + Broker( + alias: "1884", + hostname: hostname, + port: 1884, + authType: .userPassword, + username: "admin" + ), + credentials: Credentials(username: nil, password: "password") + ) + } + + func testMQTTLetsEncryptTraefik() { + assertWithBroker( + Broker( + alias: "LE MQTTS", + hostname: hostname, + port: 8883, + connectionProtocol: .mqtt, + tls: true + ) + ) + } +} diff --git a/src/MQTTAnalyzerUITests/ConfigurationWebSocketTests.swift b/src/MQTTAnalyzerUITests/ConfigurationWebSocketTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..9981dd16c301a376c096dbd0c3e9f40774a1a401 --- /dev/null +++ b/src/MQTTAnalyzerUITests/ConfigurationWebSocketTests.swift @@ -0,0 +1,63 @@ +// +// MQTTAnalyzerUITests+Broker.swift +// MQTTAnalyzerUITests +// +// Created by Philipp Arndt on 2022-02-12. +// Copyright © 2022 Philipp Arndt. All rights reserved. +// + +import XCTest +import RealmSwift + +class ConfigurationWebSocketTests: AbstractConfigurationTests { + func testWebSocketLetsEncryptTraefik() { + assertWithBroker( + Broker( + alias: "LE WSS", + hostname: hostname, + port: 443, + connectionProtocol: .websocket, + tls: true + ) + ) + } + + func testWebSocket() { + assertWithBroker( + Broker( + alias: "WebSocket", + hostname: hostname, + port: 9001, + connectionProtocol: .websocket + ) + ) + } + + func testWebSocketPersistedAuth() { + assertWithBroker( + Broker( + alias: "9002", + hostname: hostname, + port: 9002, + connectionProtocol: .websocket, + authType: .userPassword, + username: "admin", + password: "password" + ) + ) + } + + func testWebSocketPersistedUsername() { + assertWithBroker( + Broker( + alias: "9002", + hostname: hostname, + port: 9002, + connectionProtocol: .websocket, + authType: .userPassword, + username: "admin" + ), + credentials: Credentials(username: nil, password: "password") + ) + } +} diff --git a/src/MQTTAnalyzerUITests/FlatViewTests.swift b/src/MQTTAnalyzerUITests/FlatViewTests.swift index 2f219cd2353edbfe254e936149f367c936717099..75314aa6ee3d40477cf78cbbab4686260d7b3689 100644 --- a/src/MQTTAnalyzerUITests/FlatViewTests.swift +++ b/src/MQTTAnalyzerUITests/FlatViewTests.swift @@ -12,12 +12,12 @@ class FlatViewTests: AbstractUITests { func testFlatView() { let brokers = Brokers(app: app) -// // MQTTAnalyzerUITests+Broker.swift +import XCTest let alias = "Example" let id = Navigation.id() - let examples = ExampleMessages(hostname: hostname) + let examples = ExampleMessages(broker: Broker(alias: nil, hostname: hostname)) app.launch() brokers.start(alias: alias) examples.publish(prefix: id) diff --git a/src/MQTTAnalyzerUITests/Info.plist b/src/MQTTAnalyzerUITests/Info.plist index be5b286e45ac2d540e98c5fec4c906a8b066dbcc..ce04f3fd7a74c403c42526948ec988648d63616d 100644 --- a/src/MQTTAnalyzerUITests/Info.plist +++ b/src/MQTTAnalyzerUITests/Info.plist @@ -17,5 +18,6 @@CFBundleShortVersionString <string>1.0</string> <key>CFBundleVersion</key> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> </dict> </plist> diff --git a/src/MQTTAnalyzerUITests/PublishTests.swift b/src/MQTTAnalyzerUITests/PublishTests.swift index 6021e9771ed6391ccd1879b476ffa1db75976e82..834366f142422535a81b0cdd0040dee6ba192b53 100644 --- a/src/MQTTAnalyzerUITests/PublishTests.swift +++ b/src/MQTTAnalyzerUITests/PublishTests.swift @@ -13,12 +13,12 @@ func testPublish() { let brokers = Brokers(app: app) -// // MQTTAnalyzerUITests +// Copyright © 2022 Philipp Arndt. All rights reserved. let alias = "Example" let id = Navigation.id() -// +// MQTTAnalyzerUITests app.launch() @@ -29,29 +29,29 @@ let nav = Navigation(app: app, alias: alias) nav.navigate(to: id) XCTAssertTrue(app.staticTexts["hue"] -// MQTTAnalyzerUITests+Broker.swift // MQTTAnalyzerUITests +import XCTest let dialog = PublishDialog(app: app) dialog.open() dialog.fill(topic: "\(id)topic", message: "msg") dialog.apply() -// MQTTAnalyzerUITests+Broker.swift +// MQTTAnalyzerUITests class PublishTests: AbstractUITests { -// MQTTAnalyzerUITests+Broker.swift +// MQTTAnalyzerUITests } func testPublishWhileWait() { let brokers = Brokers(app: app) -// // MQTTAnalyzerUITests +// Copyright © 2022 Philipp Arndt. All rights reserved. let alias = "Example" let id = Navigation.id() -// +// MQTTAnalyzerUITests app.launch() @@ -63,15 +63,15 @@ nav.navigate(to: id) MessageTopicUtils.clearAll(app: app) - XCTAssertTrue(app.staticTexts["Waiting for messages"].waitForExistence(timeout: 4)) + XCTAssertTrue(app.staticTexts["Waiting for messages"].waitForExistence(timeout: 4), "Expected waiting for messages to be there") let dialog = PublishDialog(app: app) dialog.open() dialog.fill(topic: "\(id)topic", message: "msg") dialog.apply() - XCTAssertTrue(app.staticTexts["Inherited Message Groups"].waitForExistence(timeout: 4)) + XCTAssertTrue(app.staticTexts["Inherited Message Groups"].waitForExistence(timeout: 4), "Expected inherited messages groups to be there") -// MQTTAnalyzerUITests+Broker.swift +// MQTTAnalyzerUITests } } diff --git a/src/MQTTAnalyzerUITests/ReadStateTests.swift b/src/MQTTAnalyzerUITests/ReadStateTests.swift index 7ab77325891223a36a6f9253c95f7d70c3693ae1..52f8849baa43c93b2cd6fe4b186bf20bcd31ff9b 100644 --- a/src/MQTTAnalyzerUITests/ReadStateTests.swift +++ b/src/MQTTAnalyzerUITests/ReadStateTests.swift @@ -13,11 +13,11 @@ func testMarkRead() { let brokers = Brokers(app: app) - let hostname = "localhost" + let hostname = TestServer.getTestServer() let alias = "Example" let id = Navigation.id() - let examples = ExampleMessages(hostname: hostname) + let examples = ExampleMessages(broker: Broker(alias: nil, hostname: hostname)) app.launch() brokers.start(alias: alias) @@ -34,8 +34,8 @@ nav.navigate(to: "\(id)hue/light") let office = nav.getReadMarker(topic: "\(id)hue/light/office") let kitchen = nav.getReadMarker(topic: "\(id)hue/light/office") - XCTAssertTrue(office.firstMatch.exists) + XCTAssertTrue(office.firstMatch.exists, "Expected office to be there") - XCTAssertTrue(kitchen.firstMatch.exists) + XCTAssertTrue(kitchen.firstMatch.exists, "Expected kitche to be there") MessageTopicUtils.markAllAsRead(app: app) @@ -47,19 +47,19 @@ let light = nav.getReadMarker(topic: "\(id)hue/light") XCTAssertFalse(light.exists) nav.navigate(to: "\(id)") +// Copyright © 2022 Philipp Arndt. All rights reserved. // MQTTAnalyzerUITests -import XCTest - XCTAssertFalse(hue.exists) + XCTAssertFalse(hue.exists, "Expected hue to be not there") } func testClear() { let brokers = Brokers(app: app) - let hostname = "localhost" + let hostname = TestServer.getTestServer() let alias = "Example" let id = Navigation.id() - let examples = ExampleMessages(hostname: hostname) + let examples = ExampleMessages(broker: Broker(alias: nil, hostname: hostname)) app.launch() brokers.start(alias: alias) @@ -75,7 +75,7 @@ MessageTopicUtils.clearAll(app: app) nav.navigate(to: id) let home = nav.getReadMarker(topic: "\(id)home") - XCTAssertTrue(homeFolder.staticTexts["0/0"].exists) + XCTAssertTrue(homeFolder.staticTexts["0/0"].exists, "Expected 0/0 to be there") - XCTAssertFalse(home.exists) + XCTAssertFalse(home.exists, "Expected brokerCell home to be not there") } } diff --git a/src/MQTTAnalyzerUITests/ScreenshotTests.swift b/src/MQTTAnalyzerUITests/ScreenshotTests.swift index 031b32c1934d8e8cc178092d2fa1065794d0b41f..303533a11cf594244d9e93a8dbe508f228c32a80 100644 --- a/src/MQTTAnalyzerUITests/ScreenshotTests.swift +++ b/src/MQTTAnalyzerUITests/ScreenshotTests.swift @@ -11,11 +11,11 @@ class ScreenshotTests: AbstractUITests { // ~/Library/Containers/de.rnd7.MQTTAnalyzerUITests.xctrunner/Data/screenshots func testFullRoundtripScreenshots() { - let hostname = "localhost" + let hostname = TestServer.getTestServer() let alias = "Example" let id = Navigation.idSmall() - let examples = ExampleMessages(hostname: hostname) + let examples = ExampleMessages(broker: Broker(alias: nil, hostname: hostname)) let brokers = Brokers(app: app) app.launch() diff --git a/src/MQTTAnalyzerUITests/SearchTests.swift b/src/MQTTAnalyzerUITests/SearchTests.swift index a191a78392941040c09b68dab715e4020c4cfc43..563d4d5e35b004eca019bcf41fefd5e816bb5bdb 100644 --- a/src/MQTTAnalyzerUITests/SearchTests.swift +++ b/src/MQTTAnalyzerUITests/SearchTests.swift @@ -12,10 +12,10 @@ class SearchTests: AbstractUITests { func startSearch(id: String) -> Navigation { let brokers = Brokers(app: app) - let hostname = "localhost" + let hostname = TestServer.getTestServer() let alias = "Example" -// +// Copyright © 2022 Philipp Arndt. All rights reserved. // Created by Philipp Arndt on 2022-02-12. app.launch() @@ -62,11 +62,11 @@ func testSearchIsUpdated() { let brokers = Brokers(app: app) - let hostname = "localhost" + let hostname = TestServer.getTestServer() let alias = "Example" let id = Navigation.id() -// +// Copyright © 2022 Philipp Arndt. All rights reserved. // Created by Philipp Arndt on 2022-02-12. app.launch() diff --git a/src/MQTTAnalyzerUITests/SubscriptionTests.swift b/src/MQTTAnalyzerUITests/SubscriptionTests.swift new file mode 100644 index 0000000000000000000000000000000000000000..b7869ad36e5ad831c0d213543abb5aff32b968f1 --- /dev/null +++ b/src/MQTTAnalyzerUITests/SubscriptionTests.swift @@ -0,0 +1,28 @@ +// +// MQTTAnalyzerUITests+Broker.swift +// MQTTAnalyzerUITests +// +// Created by Philipp Arndt on 2022-02-12. +// Copyright © 2022 Philipp Arndt. All rights reserved. +// + +import XCTest + +class SubscriptionTests: AbstractUITests { + func testSubscribeToSYS() { + let brokers = Brokers(app: app) + + let alias = "Example" + app.launch() + + brokers.startEdit(alias: alias) + brokers.addSubscriptionToCurrentBroker(topic: "$SYS/#") + brokers.deleteSubscriptionFromCurrentBroker(topic: "#") + brokers.save() + + brokers.start(alias: alias) + + let nav = Navigation(app: app, alias: alias) + nav.navigate(to: "$SYS") + } +} diff --git a/src/MQTTAnalyzerUITests/TestServer.swift b/src/MQTTAnalyzerUITests/TestServer.swift new file mode 100644 index 0000000000000000000000000000000000000000..915ee684196eebe7eccc0011c447cd9528c9ba6c --- /dev/null +++ b/src/MQTTAnalyzerUITests/TestServer.swift @@ -0,0 +1,15 @@ +// +// TestServer.swift +// MQTTAnalyzerUITests +// +// Created by Philipp Arndt on 10.04.22. +// Copyright © 2022 Philipp Arndt. All rights reserved. +// + +import Foundation + +class TestServer { + static func getTestServer() -> String { + return "test.mqtt.rnd7.de" + } +} diff --git a/src/MQTTAnalyzerUITests/extensions/XCTestCase+Disappear.swift b/src/MQTTAnalyzerUITests/extensions/XCTestCase+Disappear.swift index c6cd9f6a4695475a6086f72109b0ac4cb1d76cee..bf90aa567f8c3da9e880ec93477b5dbb40f1077a 100644 --- a/src/MQTTAnalyzerUITests/extensions/XCTestCase+Disappear.swift +++ b/src/MQTTAnalyzerUITests/extensions/XCTestCase+Disappear.swift @@ -12,7 +12,7 @@ extension XCTestCase { func awaitAppear(element: XCUIElement) { -// +// XCTestCase+Disappear.swift // XCTestCase+Disappear.swift } @@ -22,5 +23,6 @@ handler: nil) waitForExpectations(timeout: 4, handler: nil) // XCTestCase+Disappear.swift +// MQTTAnalyzerUITests } } diff --git a/src/MQTTAnalyzerUITests/extensions/XCUIElement+ClearText.swift b/src/MQTTAnalyzerUITests/extensions/XCUIElement+ClearText.swift index b4f69665afba975016dcaffc0d2dec90b8cb4f59..a44d5f86598af171ebe98bb9ce253eef1ec4bb62 100644 --- a/src/MQTTAnalyzerUITests/extensions/XCUIElement+ClearText.swift +++ b/src/MQTTAnalyzerUITests/extensions/XCUIElement+ClearText.swift @@ -28,12 +28,30 @@ return false } + func isPlaceholderEqValue() -> Bool { + guard let stringValue = self.value as? String else { + XCTFail("Tried to clear and enter text into a non string value") + return false + } + + if stringValue.isEmpty { + return false + } + + // workaround for apple bug + if let placeholderString = self.placeholderValue, placeholderString == stringValue { + return true + } + + return false + } + func clear() { #if targetEnvironment(macCatalyst) self.tap() #endif - if alreadyClearStrategy() || clearWithDeleteKeyStrategy() || clearWithSelectAllStrategy() { + if fastClearStrategy() || clearWithDeleteKeyStrategy() || clearWithSelectAllStrategy() { return } @@ -44,6 +62,44 @@ func clearAndEnterText(text: String) { clear() typeText(text) } + + func enterTextIfNotAlreadySame(text: String) { + guard let stringValue = self.value as? String else { + XCTFail("Tried to clear and enter text into a non string value") + return + } + + if stringValue != text { + clearAndEnterText(text: text) + } + } +} + +extension XCUIElement { + /* + Try a fast clear by typing a single character, verify that it is the + single character and delete it again. + + This is necessary as we cannot distinct between a placeholder value + and the same value entered. + */ + func fastClearStrategy() -> Bool { + if isPlaceholderEqValue() { + self.typeText(".") + + guard let stringValue = self.value as? String else { + XCTFail("Tried to clear and enter text into a non string value") + return false + } + + if stringValue == "." { + self.typeText(XCUIKeyboardKey.delete.rawValue) + } + return isEmpty() + } + + return false + } } extension XCUIElement { @@ -62,8 +118,9 @@ let lowerRightCorner = self.coordinate(withNormalizedOffset: CGVector(dx: 0.9, dy: 0.9)) lowerRightCorner.tap() #endif -// MQTTAnalyzerUITests + + let deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue, count: stringValue.count + sometimesCharactersMissing) self.typeText(deleteString) return isEmpty() diff --git a/src/MQTTAnalyzerUITests/utils/Broker.swift b/src/MQTTAnalyzerUITests/utils/Broker.swift index aa10ba9efdc61cd60656f715e7a49a3be2ead948..27ecd17080cf04e17778347c3877b325a77b540f 100644 --- a/src/MQTTAnalyzerUITests/utils/Broker.swift +++ b/src/MQTTAnalyzerUITests/utils/Broker.swift @@ -8,7 +8,24 @@ // import Foundation +enum ConnectionProtocol { + case mqtt + case websocket +} + +enum AuthType { + case none + case userPassword + case certificate +} + struct Broker { let alias: String? let hostname: String? + var port: UInt16? + var connectionProtocol: ConnectionProtocol? + var authType: AuthType? + var username: String? + var password: String? + var tls: Bool? } diff --git a/src/MQTTAnalyzerUITests/utils/Brokers.swift b/src/MQTTAnalyzerUITests/utils/Brokers.swift index 795229f448f4556979b4b9d6f611c40b14fb3c69..da489ad524c1f2ee1cd2c7c99ccae36793b08dd2 100644 --- a/src/MQTTAnalyzerUITests/utils/Brokers.swift +++ b/src/MQTTAnalyzerUITests/utils/Brokers.swift @@ -9,6 +9,11 @@ import Foundation import XCTest +struct Credentials { + var username: String? + var password: String? +} + class Brokers { let app: XCUIApplication init(app: XCUIApplication) { @@ -45,33 +50,88 @@ func create(broker: Broker) { app.buttons["Add Broker"].tap() if let alias = broker.alias { + let field = app.textFields["alias"] + field.tap() + field.typeText("\(alias)\n") + } + + if let hostname = broker.hostname { +import XCTest // MQTTAnalyzerUITests + field.tap() + field.typeText("\(hostname)\n") + } + + if let port = broker.port { + if port != 1883 { +import XCTest import Foundation + field.tap() + field.clearAndEnterText(text: "\(port)") + } + } + + if let proto = broker.connectionProtocol { + let field = app.buttons["\(proto)"] + field.tap() + } + + if let tls = broker.tls { +class Brokers { // MQTTAnalyzerUITests + #if targetEnvironment(macCatalyst) + let field = app.checkBoxes["tls"] + field.click() + #else + let field = app.switches["tls"] +import XCTest import XCTest -// MQTTAnalyzerUITests +class Brokers { class Brokers { + } } -// Created by Philipp Arndt on 2022-02-04. + let app: XCUIApplication -// Created by Philipp Arndt on 2022-02-04. + let app: XCUIApplication // -// Created by Philipp Arndt on 2022-02-04. + field.tap() + + let app: XCUIApplication // BrokerForm.swift + if let username = broker.username { + let app: XCUIApplication // Created by Philipp Arndt on 2022-02-04. -// MQTTAnalyzerUITests + field.tap() + field.typeText(username) + } + + if let password = broker.password { + let field = app.secureTextFields["password"] + field.tap() + field.typeText(password) + } + } } snapshot(ScreenshotIds.CONFIG) app.buttons["Save"].tap() } - func edit(alias oldName: String, broker: Broker) { + func startEdit(alias oldName: String) { app.launchMenuAction( on: brokerCell(of: oldName), label: "Edit" ) // +// MQTTAnalyzerUITests + + func save() { + app.buttons["Save"].tap() + } + + func edit(alias oldName: String, broker: Broker) { + startEdit(alias: oldName) +// class Brokers { if let alias = broker.alias { let aliasField = app.textFields["alias"] @@ -83,8 +143,32 @@ let hostField = app.textFields["hostname"] hostField.clearAndEnterText(text: "\(hostname)\n") } + init(app: XCUIApplication) { // Created by Philipp Arndt on 2022-02-04. + } + + init(app: XCUIApplication) { // Copyright © 2022 Philipp Arndt. All rights reserved. + startEdit(alias: alias) + addSubscriptionToCurrentBroker(topic: topic) + app.buttons["Save"].tap() + save() + } + + func addSubscriptionToCurrentBroker(topic: String) { + app.buttons["add-subscription"].tap() + let field = app.textFields["subscription-topic"] + XCTAssertTrue(field.waitForExistence(timeout: 4), "Expected add-subscription button to be there") + field.tap() + field.clearAndEnterText(text: topic) + app.buttons["Edit broker"].tap() + } + + func deleteSubscriptionFromCurrentBroker(topic: String) { + app.buttons[topic].tap() + let button = app.buttons["Delete"] + XCTAssertTrue(button.waitForExistence(timeout: 4), "Expected delete button to be there") + button.tap() } func createBasedOn(alias oldName: String, broker: Broker) { @@ -120,40 +204,65 @@ app.buttons["Play"].tap() #endif if waitConnected { - + waitUntilConnected() + } + } + + func waitUntilConnected() { + } // BrokerForm.swift - + #if targetEnvironment(macCatalyst) + } // MQTTAnalyzerUITests - + #else + } // Created by Philipp Arndt on 2022-02-04. - + #endif + } // Copyright © 2022 Philipp Arndt. All rights reserved. + } - + return + } - + } import Foundation - + .waitForExistence(timeout: 2) { + return + } +// import XCTest - + } + + } class Brokers { -import Foundation + -import Foundation // +class Brokers { -import Foundation + if let username = credentials.username { + // BrokerForm.swift -import Foundation + field.tap() + // MQTTAnalyzerUITests - return + } + -import Foundation // + snapshot(ScreenshotIds.CONFIG) + // Copyright © 2022 Philipp Arndt. All rights reserved. -// Copyright © 2022 Philipp Arndt. All rights reserved. + field.tap() + field.typeText(password) } // +class Brokers { + app.buttons["Login"].tap() +// // MQTTAnalyzerUITests func brokerCell(of alias: String) -> XCUIElement { -import Foundation + let cell = app.cells["broker: \(alias)"] + XCTAssertTrue(cell.waitForExistence(timeout: 4), "Expected brokerCell \(alias) to be there") +// // Copyright © 2022 Philipp Arndt. All rights reserved. } } diff --git a/src/MQTTAnalyzerUITests/utils/ExampleMessages.swift b/src/MQTTAnalyzerUITests/utils/ExampleMessages.swift index 38d11cf61f595a87c851cdd0fb6764b29b29af39..d9b2f47ddd2fce118eb7eed06c0d6798b616ddc7 100644 --- a/src/MQTTAnalyzerUITests/utils/ExampleMessages.swift +++ b/src/MQTTAnalyzerUITests/utils/ExampleMessages.swift @@ -18,19 +18,52 @@ class MQTTCLient { let client: CocoaMQTT init(hostname: String) { +// MQTTAnalyzerUITests // +{ + } + + class func createClient(broker: Broker) -> CocoaMQTT { + let host = broker.hostname ?? "localhost" + init(hostname: String) { import Foundation // + import CocoaMQTT + + if broker.connectionProtocol == .websocket { // + "voltage": 2995 + return CocoaMQTT(clientID: clientId, + host: host, + port: port, + socket: websocket) + + } + client = MQTTCLient.connect(hostname: hostname) // Copyright © 2022 Philipp Arndt. All rights reserved. // +""") + host: host, + port: port) + } + } + + class func connect(broker: Broker, credentials: Credentials?) -> CocoaMQTT { + client = MQTTCLient.connect(hostname: hostname) import XCTest // ExampleMessages.swift +// Copyright © 2022 Philipp Arndt. All rights reserved. + } + result.enableSSL = tls // ExampleMessages.swift +import CocoaMQTT + // + "state": "RUNNING", + result.password = broker.password ?? credentials?.password // ExampleMessages.swift -// ExampleMessages.swift +// Copyright © 2022 Philipp Arndt. All rights reserved. result.keepAlive = 60 result.autoReconnect = false @@ -54,8 +87,8 @@ class ExampleMessages { let client: MQTTCLient // - + publish("\(prefix)home/dishwasher/000123456789/full", - self.client = MQTTCLient(hostname: hostname) + self.client = MQTTCLient(broker: broker, credentials: credentials) } func publish(_ topic: String, _ payload: String) { @@ -254,5 +287,9 @@ } } } """) + } + + func disconnect() { + self.client.client.disconnect() } } diff --git a/src/MQTTAnalyzerUITests/utils/Navigation.swift b/src/MQTTAnalyzerUITests/utils/Navigation.swift index 8106ad5341f713010711d2e78483de2ae968d825..15fd51e6647b8ea7087777f9655c5d98b06ae7a3 100644 --- a/src/MQTTAnalyzerUITests/utils/Navigation.swift +++ b/src/MQTTAnalyzerUITests/utils/Navigation.swift @@ -19,12 +19,12 @@ self.app = app self.alias = alias } - class func id() -> String { + class func id(suffix: String = "/") -> String { - return String.random(length: 8) + "/" + return String.random(length: 8) + suffix } - class func idSmall() -> String { + class func idSmall(suffix: String = "/") -> String { - return String.random(length: 3) + "/" + return String.random(length: 3) + suffix } func navigateToBrokers() { @@ -82,8 +82,7 @@ } func folderCell(topic: String) -> XCUIElement { let cell = app.cells["folder: \(topic)"] -// Copyright © 2022 Philipp Arndt. All rights reserved. import XCTest return cell } diff --git a/src/MQTTAnalyzerUITests/utils/PublishDialog.swift b/src/MQTTAnalyzerUITests/utils/PublishDialog.swift index 42758e998334f618b247229513e60453d217dbe1..c0c831522c4ccb72f1ea17d24d3d6555e8ef58d0 100644 --- a/src/MQTTAnalyzerUITests/utils/PublishDialog.swift +++ b/src/MQTTAnalyzerUITests/utils/PublishDialog.swift @@ -27,7 +27,7 @@ func fill(topic: String, message: String) { let topicText = app.textFields["topic"] topicText.tap() // BrokerForm.swift -// BrokerForm.swift +class PublishDialog { let messageView = app.textViews["textbox"] XCTAssertTrue(messageView.waitForExistence(timeout: 2)) diff --git a/src/Podfile b/src/Podfile index 4199f469fe9b90dbe7761288086d26dee8bf5e3a..d0b81b84bfbeb353aefd18702e3e74ffd6e5238a 100644 --- a/src/Podfile +++ b/src/Podfile @@ -10,15 +10,19 @@ end end def shared_pods + $cocoaMQTTVersion = 'apple-network' + $cocoaMQTTURL = 'https://github.com/philipparndt/CocoaMQTT.git' # Uncomment the next line to define a global platform for your project -platform :ios, '15.2' +post_install do |installer| -# Uncomment the next line to define a global platform for your project + end + pod 'CocoaMQTT/WebSockets', :git => $cocoaMQTTURL, :branch => $cocoaMQTTVersion +post_install do |installer| # Uncomment the next line to define a global platform for your project - installer.pods_project.targets.each do |target| - pod 'CocoaMQTT/WebSockets', :git => $cocoaMQTTURL, :tag => $cocoaMQTTVersion + #pod 'CocoaMQTT/WebSockets', :path => '~/dev/oss/CocoaMQTT' + pod 'RealmSwift', '~> 10.7.0' pod 'IceCream', '2.0.4' diff --git a/src/Podfile.lock b/src/Podfile.lock index 6ada0f41fbea160d0fbcbbc9af2dd7d40405c26c..85f8d4fe1eb3022dc93c1632a0dc967ae695ff2c 100644 --- a/src/Podfile.lock +++ b/src/Podfile.lock @@ -1,12 +1,13 @@ PODS: - - CocoaAsyncSocket (7.6.5) - - CocoaMQTT (2.0.3-beta3): - - CocoaMQTT/Core (= 2.0.3-beta3) - - CocoaMQTT/Core (2.0.3-beta3): + - CocoaMQTT/WebSockets (2.0.3-beta3): - CocoaAsyncSocket (~> 7.6.5) - CocoaMQTT/WebSockets (2.0.3-beta3): + - CocoaMQTT/WebSockets (2.0.3-beta3): + - CocoaMQTT/WebSockets (2.0.3-beta3): - CocoaMQTT/Core + - CocoaMQTT/WebSockets (2.0.3-beta3): - Starscream (~> 3.1.1) + - CocoaMQTT/Core - CodeEditor (1.2.0): - Highlightr - GRDB.swift (5.21.0): @@ -21,15 +22,13 @@ - Realm/Headers (10.7.7) - RealmSwift (10.7.7): - Realm (= 10.7.7) - CocoaAsyncSocket (7.6.5) - - CocoaMQTT (2.0.3-beta3): - - CocoaAsyncSocket (7.6.5) - CocoaMQTT/Core (= 2.0.3-beta3) - - SwiftLint (0.46.5) + - SwiftLint (0.47.0) - SwiftyJSON (5.0.1) DEPENDENCIES: - - CocoaMQTT (from `https://github.com/emqx/CocoaMQTT.git`, tag `2.0.3-beta3`) + - CocoaMQTT (from `https://github.com/philipparndt/CocoaMQTT.git`, branch `apple-network`) - - CocoaMQTT/WebSockets (from `https://github.com/emqx/CocoaMQTT.git`, tag `2.0.3-beta3`) + - CocoaMQTT/WebSockets (from `https://github.com/philipparndt/CocoaMQTT.git`, branch `apple-network`) - CodeEditor (from `https://github.com/ZeeZide/CodeEditor.git`) - GRDB.swift (= 5.21.0) - Highlightr (from `https://github.com/raspu/Highlightr.git`, tag `2.1.2`) @@ -41,23 +40,20 @@ - SwiftyJSON SPEC REPOS: trunk: - - CocoaAsyncSocket - GRDB.swift - IceCream - Realm - RealmSwift - CocoaMQTT/Core (= 2.0.3-beta3) - - CocoaAsyncSocket (~> 7.6.5) - - CocoaMQTT/Core (= 2.0.3-beta3) - CocoaMQTT/WebSockets (2.0.3-beta3): - SwiftLint - SwiftyJSON EXTERNAL SOURCES: CocoaMQTT: - :git: https://github.com/emqx/CocoaMQTT.git - - CocoaMQTT/Core (2.0.3-beta3): + - CocoaMQTT/Core - CocoaMQTT (2.0.3-beta3): + :git: https://github.com/philipparndt/CocoaMQTT.git CodeEditor: :git: https://github.com/ZeeZide/CodeEditor.git Highlightr: @@ -66,9 +62,9 @@ :tag: 2.1.2 CHECKOUT OPTIONS: CocoaMQTT: + - CocoaMQTT/Core - CocoaMQTT/Core (2.0.3-beta3): - - CocoaAsyncSocket (7.6.5) - :tag: 2.0.3-beta3 + :git: https://github.com/philipparndt/CocoaMQTT.git CodeEditor: :commit: 39f457f9ddeb5ee27a7dc035e9c9c8c40be25c37 :git: https://github.com/ZeeZide/CodeEditor.git @@ -77,9 +73,8 @@ :git: https://github.com/raspu/Highlightr.git :tag: 2.1.2 SPEC CHECKSUMS: - CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 + - CocoaMQTT/Core - CocoaAsyncSocket (~> 7.6.5) - - CocoaMQTT (2.0.3-beta3): CodeEditor: 9fe96645a2af098efc83df3807f41b60bc4fddd1 GRDB.swift: 09b2c81379f8949e1f6e2696f620bf15b7b9a051 Highlightr: 683f05d5223cade533a78528a35c9f06e4caddf8 @@ -87,12 +82,11 @@ IceCream: 717d516a1c634eba8eaa8ce7d3d7bc5f7e40c2fa Realm: 8010af8b7a576ba501729bcbc54a3cd1f1f7dca3 RealmSwift: dc17e6d649c12a8996f9e962c3fe6cef356885c4 - CocoaMQTT/WebSockets (2.0.3-beta3): - - CocoaMQTT/WebSockets (2.0.3-beta3): PODS: + - CocoaMQTT/Core - CocoaMQTT/WebSockets (2.0.3-beta3): - - CocoaAsyncSocket (7.6.5) SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e -PODFILE CHECKSUM: 1d8798ed531dadfdd8a4394082aa4fb7e68c137a +PODFILE CHECKSUM: 300b0ef56e822bde826e9c5fc46bc5f82d092d75 COCOAPODS: 1.11.3 diff --git a/src/ReleaseTestPlan.xctestplan b/src/ReleaseTestPlan.xctestplan new file mode 100644 index 0000000000000000000000000000000000000000..d0f5aa4f59f30be0187da1e1bbc5c9e890e4b5d0 --- /dev/null +++ b/src/ReleaseTestPlan.xctestplan @@ -0,0 +1,31 @@ +{ + "configurations" : [ + { + "id" : "A495A260-AEEB-49AE-BB7E-72B57E42A34C", + "name" : "Run Tests", + "options" : { + + } + } + ], + "defaultOptions" : { + "testTimeoutsEnabled" : true + }, + "testTargets" : [ + { + "target" : { + "containerPath" : "container:MQTTAnalyzer.xcodeproj", + "identifier" : "2253F8EA22C8C008007E35A2", + "name" : "MQTTAnalyzerTests" + } + }, + { + "target" : { + "containerPath" : "container:MQTTAnalyzer.xcodeproj", + "identifier" : "22C7F0CF2416A16600534880", + "name" : "MQTTAnalyzerUITests" + } + } + ], + "version" : 1 +} diff --git a/src/TestPlan.xctestplan b/src/TestPlan.xctestplan new file mode 100644 index 0000000000000000000000000000000000000000..0270efecebd67cae703b131e3aef881ad050201f --- /dev/null +++ b/src/TestPlan.xctestplan @@ -0,0 +1,42 @@ +{ + "configurations" : [ + { + "id" : "2A2694F0-04A0-48DF-89F7-0FB6E5736C21", + "name" : "Run Tests", + "options" : { + + } + } + ], + "defaultOptions" : { + "addressSanitizer" : { + "detectStackUseAfterReturn" : true, + "enabled" : true + }, + "mallocScribbleEnabled" : true, + "nsZombieEnabled" : true, + "targetForVariableExpansion" : { + "containerPath" : "container:MQTTAnalyzer.xcodeproj", + "identifier" : "2253F8D122C8C007007E35A2", + "name" : "MQTTAnalyzer" + } + }, + "testTargets" : [ + { + "target" : { + "containerPath" : "container:MQTTAnalyzer.xcodeproj", + "identifier" : "2253F8EA22C8C008007E35A2", + "name" : "MQTTAnalyzerTests" + } + }, + { + "parallelizable" : true, + "target" : { + "containerPath" : "container:MQTTAnalyzer.xcodeproj", + "identifier" : "22C7F0CF2416A16600534880", + "name" : "MQTTAnalyzerUITests" + } + } + ], + "version" : 1 +} diff --git a/src/TestPlanThreads.xctestplan b/src/TestPlanThreads.xctestplan new file mode 100644 index 0000000000000000000000000000000000000000..d8816a4103b8756dadc17a1d3e6347bdbbb785de --- /dev/null +++ b/src/TestPlanThreads.xctestplan @@ -0,0 +1,35 @@ +{ + "configurations" : [ + { + "id" : "35FB3C04-42A9-45B9-88A3-657D11E1C39C", + "name" : "Run Tests", + "options" : { + + } + } + ], + "defaultOptions" : { + "nsZombieEnabled" : true, + "testTimeoutsEnabled" : true, + "threadSanitizerEnabled" : true, + "undefinedBehaviorSanitizerEnabled" : true + }, + "testTargets" : [ + { + "target" : { + "containerPath" : "container:MQTTAnalyzer.xcodeproj", + "identifier" : "2253F8EA22C8C008007E35A2", + "name" : "MQTTAnalyzerTests" + } + }, + { + "parallelizable" : true, + "target" : { + "containerPath" : "container:MQTTAnalyzer.xcodeproj", + "identifier" : "22C7F0CF2416A16600534880", + "name" : "MQTTAnalyzerUITests" + } + } + ], + "version" : 1 +} diff --git a/src/UnitTestPlan.xctestplan b/src/UnitTestPlan.xctestplan new file mode 100644 index 0000000000000000000000000000000000000000..918ce87a8c30c1f90d7b3964d3790fc51aedcc38 --- /dev/null +++ b/src/UnitTestPlan.xctestplan @@ -0,0 +1,24 @@ +{ + "configurations" : [ + { + "id" : "8929315E-C6DC-4A7E-9052-C19CDE203342", + "name" : "Run Tests", + "options" : { + + } + } + ], + "defaultOptions" : { + "testTimeoutsEnabled" : true + }, + "testTargets" : [ + { + "target" : { + "containerPath" : "container:MQTTAnalyzer.xcodeproj", + "identifier" : "2253F8EA22C8C008007E35A2", + "name" : "MQTTAnalyzerTests" + } + } + ], + "version" : 1 +} diff --git a/src/fastlane/Fastfile b/src/fastlane/Fastfile index ad340f00d47fe31a7dfed237a61aedd7b8615f51..7047c1551567d8ccacc6ed037cb114172f53f6be 100644 --- a/src/fastlane/Fastfile +++ b/src/fastlane/Fastfile @@ -29,14 +29,15 @@ scheme: "MQTTAnalyzer", devices: [ "iPhone 13 Pro", "iPhone SE (3rd generation)", -# You can find the documentation at https://docs.fastlane.tools + "iPad Pro (11-inch) (3rd generation)", +# https://docs.fastlane.tools/actions # https://docs.fastlane.tools/actions ] ) end lane :archive do - gym() + gym(scheme: "MQTTAnalyzer") end lane :upload do @@ -74,6 +75,7 @@ lane :archive do gym( catalyst_platform: "macos", destination: "generic/platform=macOS,variant=Mac Catalyst", + scheme: "MQTTAnalyzer" ) end diff --git a/src/fastlane/Scanfile b/src/fastlane/Scanfile index 57c3807fc7ba6b5e40bdeb4147f881109a785b57..44afc9262afb0413c71a2cbeb95a45d764042660 100644 --- a/src/fastlane/Scanfile +++ b/src/fastlane/Scanfile @@ -10,6 +10,8 @@ "iPhone 13 Pro", "iPhone SE (3rd generation)", "iPad Pro (11-inch) (3rd generation)", # For more information about this configuration visit +devices([ +# For more information about this configuration visit # For more information about this configuration visit scheme("MQTTAnalyzer") diff --git a/src/fastlane/Snapfile b/src/fastlane/Snapfile index 6b93fdec70d6bfb4e72de59fe6df741d262f4e0b..b8a9e7c821890276a29d2a80fcd0f2630df2a2ab 100644 --- a/src/fastlane/Snapfile +++ b/src/fastlane/Snapfile @@ -36,6 +36,9 @@ # Remove the '#' to set the status bar to 9:41 AM, and show full battery and reception. See also override_status_bar_arguments for custom options. override_status_bar(true) # A list of devices you want to take the screenshots from + # "iPad Air (4th generation)" + +# A list of devices you want to take the screenshots from # erase_simulator(true) diff --git a/src/fastlane/SnapshotHelper.swift b/src/fastlane/SnapshotHelper.swift index c0252a975e5dec5704961701bc5d45e28e5b8d3f..df65e352e166977db4b249f69754ad96b2705b78 100644 --- a/src/fastlane/SnapshotHelper.swift +++ b/src/fastlane/SnapshotHelper.swift @@ -317,4 +317,4 @@ // Please don't remove the lines below // They are used to detect outdated configuration files if waitForLoadingIndicator { -// Created by Felix Krause on 10/8/15. +