Swift linux unit test #testable import fails - swift

I try to add Unit tests to my Swift project on Linux Ubuntu 16.04. Now I have the directory structure:
MyProject
|-Sources
| └MyProject
| |-IPcalc.swift
| └ main.swift
|
|-Tests
| |-MyProjectTests
| | └IPcalcTests.swift
| └ LinuxMain.swift
|
└ Package.swift
IPcalcTests.swift file:
import XCTest
#testable import IPcalc
...
LinuxMain.swift file:
import XCTest
#testable import IPcalcTests
XCTMain([
testCase(IPcalcTests.allTests),
])
Package.swift file:
import PackageDescription
let package = Package(
name: "MyProject",
products: [],
dependencies: [],
targets: [
.target(
name: "MyProject",
dependencies: []),
.testTarget(
name: "MyProjectTests",
dependencies: ["MyProject"]),
]
)
When I try to execute swift test , I get:
$ swift test
Compile Swift Module 'MyProjectTests' (2 sources)
/home/user/MyProject/Tests/MyProjectTests/IPcalcTests.swift:2:18: error: no such module 'IPcalc'
#testable import IPcalc
Why I could not import IPcalc class to IPcalcTests.swift?

Issue happens because my app is executable, it has main.swift file. Unit Test don't work in this case, or I don't know how to use it. It is necessary to split project into projectApp target and projectLib target. projectApp will contain only main.swift and projectLib will contain the rest.
After that Unit Test works without any problems with projectLib target. More details about is here: https://riis.com/blog/swift-unit-testing-ubuntu/
https://www.raywenderlich.com/1072244-server-side-swift-testing-on-linux

Related

How to use a local Swift Package which provides two libraries?

I have a local swift package Foo which provides two different libraries Foo and FooB. Now I would like to use them in another local package Bar. I am only able to get the whole package by path declaration. Is there a way to name/specify which library should be used? I want to use FooB in my Bar Package.
let package = Package(
name: "Foo",
products: [
.library(name: "Foo", targets: ["Foo"]),
.library(name: "FooB", targets: ["FooB"])
]
...)
let package = Package(
name: "Bar",
dependencies: [
.package(path: "Foo"),
.package(path: "FooB") // this one does not work
],
...)
// inside package Bar
import Foo
import FooB // this is throwing "no such module 'FooB'"

I added Objective-C files to Swift package via modulemap file, but implementation wasn't connected

I have a Swift package, and try to add Objective-c files.
My package now:
Root
- Package.swift
+ Sources
+ ObjC
+ DataDeflate
- DataDeflate.h
- module.modulemap
- NSData+Deflate.h
- NSData+Deflate.m
+ Swift
+ DataDeflator
- DataDeflatow.swift
+ Tests
+ DataDeflatorTests
- DataDeflatorTests.swift
Package.swift content:
import PackageDescription
let package = Package(
name: "DataDeflator",
products: [
.library(name: "DataDeflator", targets: ["DataDeflator"]),
],
targets: [
.target(
name: "DataDeflator",
dependencies: ["DataDeflate"],
path: "Sources/Swift/DataDeflator"),
.systemLibrary(name: "DataDeflate", path: "Sources/ObjC/DataDeflate"),
.testTarget(
name: "DataDeflatorTests",
dependencies: ["DataDeflator"]),
]
)
module.modulemap content:
module DataDeflate [system] {
header "DataDeflate.h"
link "DataDeflate"
export *
}
DataDeflate.h content:
#ifndef DataDeflate_h
#define DataDeflate_h
#import "NSData+Deflate.h"
#endif /* DataDeflate_h */
Now, using these settings (module.modulemap and targets in Package.swift), Swift code has access to Objective-C NSData category.
But only for declaration (NSData+Deflate.h), and not to implementation (NSData+Deflate.m)!
When I add Objective-C method calling, it builds successfully, without any warnings. But unrecognized selector sent to instance error appears when unit tests runs.
What did I forget? How can I connect both declaration and implementation from Objective-C to Swift?
You don't need to use systemLibrary target with a modulemap file for your own ObjC source files because usually it's used as wrapper to available C system libraries such as sqlite3, curl etc. and the modulemap file can be created for your target automatically by XCode.
So you can use simple target:
...
targets: [
...
.target(name: "DataDeflate", path: "Sources/ObjC/DataDeflate"),
]
...
Next you should organise your files inside DataDeflate folder next way:
- Package.swift
+ Sources
+ ObjC
+ DataDeflate
+ include
- DataDeflate.h
- NSData+Deflate.h
- NSData+Deflate.m
...
Where include subfolder must contain <YourTargetName>.h (DataDeflate.h) file and all other public headers to include.

Xcode, clang, c++ stdlib: Swift Package using a system library, build fails with 'stdexcept' file not found

I am writing an executable Swift package where I need to use a system library (written in C++).
AFAIK I have the package.swift, module.modulemap and umbrella header file written correctly.
When I add an import for the library in my main.swift file I get an error 'stdexcept' file not found. The error comes from an #include <stdexcept> in one of the system library's public header files.
Currently running:
Xcode v13.2.1
macOS v12.2.1 (Monterey)
I think the problem is related to Xcode's Command Line Tools but how do I fix it?
Package.swift
// swift-tools-version:5.5
import PackageDescription
let package = Package(
name: "GeodesicApp",
platforms: [.macOS(.v11)],
dependencies: [
],
targets: [
.systemLibrary(name: "geographiclib",
pkgConfig: "geographiclib",
providers: [
.brew(["geographiclib"])
]
),
.executableTarget(
name: "GeodesicApp",
dependencies: ["geographiclib"])
]
)
module.modulemap
module geographiclib {
umbrella header "geographiclib.h"
export *
link "geographiclib"
}
Umbrella header (geographiclib.h)
#include <GeographicLib/Config.h>
#include <GeographicLib/Geodesic.hpp>
main.swift
import geographiclib // error: 'stdexcept' file not found
... //
Error.
Answering my own question.
I was only able to get a working solution by creating a C wrapper package around the system library; that C wrapper package, in turn, is then wrapped with another Swift wrapper to expose 'Swifty-style' code - I was not able to get a single package that included all the required parts.
My working solution is as follows...
Package: CGeographicLib
Folder structure for the the system library's C wrapper is:
.
├── Package.swift
├── README.md
└── Sources
├── CGeographicLib
│   ├── CGeodesic.cpp
│   └── include
│   └── CGeodesic.h
└── geographiclib
├── geographiclib.h
└── module.modulemap
Updated Package.swift:
import PackageDescription
let package = Package(
name: "CGeographicLib",
platforms: [.macOS(.v11)],
products: [
.library(name: "CGeographicLib", targets: ["CGeographicLib"])
],
targets: [
.systemLibrary(name: "geographiclib",
pkgConfig: "geographiclib",
providers: [
.brew(["geographiclib"])
]),
.target(name: "CGeographicLib", dependencies: ["geographiclib"])
],
cxxLanguageStandard: .cxx20
)
I added platforms: [.macOS(.v11)] as the latest version of the GeographicLib system library only supports macOS v11 or later.
The system library that I am using has some C++11 extensions, I added the language standard .cxx20, but this could equally be .cxx11 too and it should still work for the system library I am using.
Updated module.modulemap:
module geographiclib [system] {
umbrella header "geographiclib.h"
link "geographiclib"
export *
}
Umbrella header, geographiclib.h is unchanged.
For the new C wrapper elements:
CGeodesic.h:
#ifdef __cplusplus
extern "C" {
#endif
double geoLibInverse(double lat1, double lon1, double lat2, double lon2);
#ifdef __cplusplus
}
#endif
CGeodesic.cpp:
#include "include/CGeodesic.h"
#include "../../geographiclib/geographiclib.h"
double geoLibInverse(double lat1, double lon1, double lat2, double lon2) {
using namespace std;
using namespace GeographicLib;
Geodesic geod(Constants::WGS84_a(), Constants::WGS84_f());
double s12;
geod.Inverse(lat1, lon1, lat2, lon2, s12);
return s12;
}
Package: SwiftyGeographicLib
Folder structure for the Swift package that uses the C wrapper package is:
.
├── Package.swift
├── README.md
├── Sources
│   └── SwiftyGeographicLib
│   └── Geodesic.swift
└── Tests
└── SwiftyGeographicLibTests
└── SwiftyGeographicLibTests.swift
Package.swift:
import PackageDescription
let package = Package(
name: "SwiftyGeographicLib",
platforms: [.macOS(.v11)],
products: [
.library(
name: "SwiftyGeographicLib",
targets: ["SwiftyGeographicLib"]),
],
dependencies: [
.package(name: "CGeographicLib",
url: "/Users/kieran/codeProjects/z.TestProjects/SPM/CGeographicLib",
branch: "master")
],
targets: [
.target(
name: "SwiftyGeographicLib",
dependencies: ["CGeographicLib"]),
.testTarget(
name: "SwiftyGeographicLibTests",
dependencies: ["SwiftyGeographicLib"]),
]
)
The package dependency in this example is pointing to a local package - I could equally have uploaded and created a version tag on GitHub.
Geodesic.swift:
import Foundation
import CGeographicLib
public func geodesicInverse(lat1: Double, lon1: Double, lat2: Double, lon2: Double) -> Double {
return geoLibInverse(lat1, lon1, lat2, lon2)
}

Swift - Can't import packages

I have been trying out Swift on Ubuntu 20.10 and am having trouble importing packages. Swift is installed properly. I am always getting error: no such module, no matter which package I try.
Here is my Package.swift (Embassy as an example):
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "test",
/* products: [
.executable(name: "test", targets: ["test"])
],*/
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/envoy/Embassy.git",
from: "4.1.1"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "test",
dependencies: []),
.testTarget(
name: "testTests",
dependencies: ["test"]),
]
)
main.swift(under Sources/test)
import Foundation
import Embassy
let loop = try! SelectorEventLoop(selector: try! KqueueSelector())
let server = DefaultHTTPServer(eventLoop: loop, port: 8080) {
(
environ: [String: Any],
startResponse: ((String, [(String, String)]) -> Void),
sendBody: ((Data) -> Void)
) in
// Start HTTP response
startResponse("200 OK", [])
let pathInfo = environ["PATH_INFO"]! as! String
sendBody(Data("the path you're visiting is \(pathInfo.debugDescription)".utf8))
// send EOF
sendBody(Data())
}
// Start HTTP server to listen on the port
try! server.start()
// Run event loop
loop.runForever()
In your Package.swift file you are declaring Embassy as a dependency, but you are not referencing that dependency in any of your targets. In the example you provided, you can alter your package like this:
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "test",
dependencies: [
.package(url: "https://github.com/envoy/Embassy.git", from: "4.1.1"),
],
targets: [
// Reference the 'Embassy' package here.
.target(name: "test", dependencies: ["Embassy"]),
.testTarget(name: "testTests", dependencies: ["test", "Embassy"]),
]
)

Swift Package Manager with resources compile errors

I am trying to use resources inside my Package.swift file:
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "MyPackage",
products: [
.library(
name: "MyPackage",
targets: ["MyPackage"])
],
targets: [
.target(
name: "MyPackage",
resources: [
.process("Resources/one.xml"),
.process("Resources/two.json"),
.process("Resources/three.json"),
]
)
.testTarget(
name: "MyPackageTests",
dependencies: ["MyPackage"]
)
]
)
When I import and compile the package in another project, I am getting lots of errors, such as:
Cannot infer contextual base in reference to member 'target'
or:
Reference to member 'process' cannot be resolved without a contextual type
The files are located in my package project in Sources -> MyPackage -> Resources
I also tried .copy("Resources/one.xml"), etc
What am I missing?
You missed a , after the target close parentheses:
.target(
name: "BioSwift",
resources: [
.process("Resources/unimod.xml"),
.process("Resources/aminoacids.json"),
.process("Resources/elements.json"),
.process("Resources/enzymes.json"),
.process("Resources/functionalgroups.json"),
.process("Resources/hydropathy.json")
]
), // Here is the missed `,`
Also, you don't need to add files one by one! Instead, you can add a directory:
.process("Resources")