Skip to content

Commit

Permalink
Added Objective C class and interface test case (#296)
Browse files Browse the repository at this point in the history
  • Loading branch information
bkryza committed Sep 1, 2024
1 parent d6d46fc commit 5316e14
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 9 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ add_subdirectory(src)
option(BUILD_TESTS "" ON)
option(ENABLE_CXX_MODULES_TEST_CASES "" OFF)
option(ENABLE_CUDA_TEST_CASES "" OFF)
option(ENABLE_OBJECTIVE_C_TEST_CASES "" OFF)
option(ENABLE_OBJECTIVE_C_TEST_CASES "" ON)

#
# Setup CUDA if available
Expand Down
3 changes: 2 additions & 1 deletion src/class_diagram/model/diagram.cc
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@ void diagram::apply_filter()
bool diagram::is_empty() const
{
return element_view<class_>::is_empty() &&
element_view<enum_>::is_empty() && element_view<concept_>::is_empty();
element_view<enum_>::is_empty() && element_view<concept_>::is_empty() &&
element_view<objc_interface>::is_empty();
}
} // namespace clanguml::class_diagram::model

Expand Down
2 changes: 1 addition & 1 deletion src/class_diagram/visitor/translation_unit_visitor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1265,7 +1265,7 @@ void translation_unit_visitor::process_objc_ivar(

const auto field_name = ivar.getNameAsString();

objc_member field{common::access_specifier_to_access_t(ivar.getAccess()),
objc_member field{common::access_specifier_to_access_t(ivar.getAccessControl()),
field_name, field_type_str};

process_comment(ivar, field);
Expand Down
27 changes: 24 additions & 3 deletions src/common/clang_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,27 @@ model::access_t access_specifier_to_access_t(
return access;
}

model::access_t access_specifier_to_access_t(
clang::ObjCIvarDecl::AccessControl access_specifier)
{
auto access = model::access_t::kPublic;
switch (access_specifier) {
case clang::ObjCIvarDecl::AccessControl::Public:
access = model::access_t::kPublic;
break;
case clang::ObjCIvarDecl::AccessControl::Private:
access = model::access_t::kPrivate;
break;
case clang::ObjCIvarDecl::AccessControl::Protected:
access = model::access_t::kProtected;
break;
default:
break;
}

return access;
}

model::namespace_ get_tag_namespace(const clang::TagDecl &declaration)
{
model::namespace_ ns;
Expand Down Expand Up @@ -438,17 +459,17 @@ template <> eid_t to_id(const clang::RecordDecl &declaration)

template <> eid_t to_id(const clang::ObjCCategoryDecl &type)
{
return to_id(fmt::format("__objc__{}", type.getNameAsString()));
return to_id(fmt::format("__objc__category__{}", type.getNameAsString()));
}

template <> eid_t to_id(const clang::ObjCInterfaceDecl &type)
{
return to_id(fmt::format("__objc__{}", type.getNameAsString()));
return to_id(fmt::format("__objc__interface__{}", type.getNameAsString()));
}

template <> eid_t to_id(const clang::ObjCProtocolDecl &type)
{
return to_id(fmt::format("__objc__{}", type.getNameAsString()));
return to_id(fmt::format("__objc__protocol__{}", type.getNameAsString()));
}

template <> eid_t to_id(const clang::EnumDecl &declaration)
Expand Down
3 changes: 2 additions & 1 deletion src/common/clang_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ namespace clanguml::common {
*/
model::access_t access_specifier_to_access_t(
clang::AccessSpecifier access_specifier);

model::access_t access_specifier_to_access_t(
clang::ObjCIvarDecl::AccessControl access_specifier);
/**
* @brief Generate full qualified name for
* [clang::TagDecl](https://clang.llvm.org/doxygen/classclang_1_1TagDecl.html)
Expand Down
4 changes: 2 additions & 2 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ set(TEST_CASES_REQUIRING_CXX20 t00056 t00058 t00059 t00065 t00069 t00074 t00075)
set(TEST_CASES_REQUIRING_CXX20_MODULES t00070 t00071 t00072
t30012 t30013 t30014 t30015)
set(TEST_CASES_REQUIRING_CUDA t20049 t20050 t20051)
set(TEST_CASES_REQUIRING_OBJC t00084)
set(TEST_CASES_REQUIRING_OBJC t00084 t00085)

if(ENABLE_OBJECTIVE_C_TEST_CASES)
message(STATUS "Enabling Objective-C test cases: ${TEST_CASES_OBJC_SOURCES}")
Expand Down Expand Up @@ -113,7 +113,7 @@ foreach(TEST_NAME ${TEST_NAMES})
endif(ENABLE_CXX_MODULES_TEST_CASES)
if(ENABLE_OBJECTIVE_C_TEST_CASES)
add_library(test_cases_objc)
target_compile_options(test_cases_objc PUBLIC -MMD -MP -DGNUSTEP -DGNUSTEP_BASE_LIBRARY=1 -DGNU_GUI_LIBRARY=1 -DGNU_RUNTIME=1 -DGNUSTEP_BASE_LIBRARY=1 -fno-strict-aliasing -fexceptions -fobjc-exceptions -D_NATIVE_OBJC_EXCEPTIONS -pthread -fPIC -Wall -DGSWARN -DGSDIAGNOSE -Wno-import -g -O2 -fconstant-string-class=NSConstantString -I. -I/home/bartek/GNUstep/Library/Headers -I/usr/local/include/GNUstep -I/usr/include/GNUstep -I/usr/lib/gcc/x86_64-linux-gnu/12/include)
target_compile_options(test_cases_objc PUBLIC -MMD -MP -DGNUSTEP -DGNUSTEP_BASE_LIBRARY=1 -DGNU_GUI_LIBRARY=1 -DGNU_RUNTIME=1 -DGNUSTEP_BASE_LIBRARY=1 -fno-strict-aliasing -fexceptions -fblocks -fobjc-runtime=gnustep-2.0 -fobjc-exceptions -D_NATIVE_OBJC_EXCEPTIONS -pthread -fPIC -Wall -DGSWARN -DGSDIAGNOSE -Wno-import -g -O2 -fconstant-string-class=NSConstantString -I. -I/home/bartek/GNUstep/Library/Headers -I/usr/local/include/GNUstep -I/usr/include/GNUstep -I/usr/lib/gcc/x86_64-linux-gnu/12/include)
# Link the GNUstep libraries
target_link_libraries(test_cases_objc -shared-libgcc -pthread -fexceptions -rdynamic -L/home/bartek/GNUstep/Library/Libraries -L/usr/local/lib -L/usr/lib -lgnustep-base -lobjc -lm)
target_sources(test_cases_objc PUBLIC ${TEST_CASES_OBJC_SOURCES})
Expand Down
12 changes: 12 additions & 0 deletions tests/t00085/.clang-uml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
diagrams:
t00085_class:
type: class
glob:
- t00085.m
filter_mode: advanced
include:
anyof:
paths:
- .
elements:
- NSObject
33 changes: 33 additions & 0 deletions tests/t00085/t00085.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#import <Foundation/Foundation.h>

@interface It00085 : NSObject {
int _defaultMember;

@public
NSString *_publicMember;

@protected
NSString *_protectedMember;

@private
NSString *_privateMember;
}

@property (class, nonatomic, assign) NSInteger sharedCounter;

@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger value;

+ (void)incrementSharedCounter;
+ (NSInteger)currentSharedCounter;
+ (instancetype)sharedInstance;

- (instancetype)initWithName:(NSString *)name value:(NSInteger)value;
- (void)displayDetails;
- (NSInteger)addValue:(NSInteger)otherValue;
- (NSInteger)multiplyValueBy:(NSInteger)multiplier andAdd:(NSInteger)addition;
- (void)resetValue;

- (void)performOperationWithBlock:(void (^)(NSInteger result))block;

@end
72 changes: 72 additions & 0 deletions tests/t00085/t00085.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#import "t00085.h"

@implementation It00085

// Synthesize class property
static NSInteger _sharedCounter = 0;

+ (NSInteger)sharedCounter {
return _sharedCounter;
}

+ (void)setSharedCounter:(NSInteger)newValue {
_sharedCounter = newValue;
}

// Class method implementations
+ (void)incrementSharedCounter {
_sharedCounter++;
}

+ (NSInteger)currentSharedCounter {
return _sharedCounter;
}

+ (instancetype)sharedInstance {
static It00085 *sharedInstance = nil;
return sharedInstance;
}

// Instance method implementations
- (instancetype)initWithName:(NSString *)name value:(NSInteger)value {
self = [super init];
if (self) {
_name = name;
_value = value;

// Initialize members with different access scopes
_publicMember = @"Public Member";
_protectedMember = @"Protected Member";
_privateMember = @"Private Member";
}
return self;
}

- (void)displayDetails {
NSLog(@"Name: %@, Value: %ld", self.name, (long)self.value);

// Accessing members with different access scopes
NSLog(@"Public Member: %@", _publicMember);
NSLog(@"Protected Member: %@", _protectedMember);
NSLog(@"Private Member: %@", _privateMember);
}

- (NSInteger)addValue:(NSInteger)otherValue {
return self.value + otherValue;
}

- (NSInteger)multiplyValueBy:(NSInteger)multiplier andAdd:(NSInteger)addition {
return (self.value * multiplier) + addition;
}

- (void)resetValue {
self.value = 0;
}

// Method with a block parameter
- (void)performOperationWithBlock:(void (^)(NSInteger result))block {
NSInteger result = self.value * 2;
block(result);
}

@end
49 changes: 49 additions & 0 deletions tests/t00085/test_case.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* tests/t00085/test_case.h
*
* Copyright (c) 2021-2024 Bartek Kryza <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

TEST_CASE("t00085")
{
using namespace clanguml::test;
using namespace std::string_literals;

auto [config, db, diagram, model] =
CHECK_CLASS_MODEL("t00085", "t00085_class");

CHECK_CLASS_DIAGRAM(*config, diagram, *model, [](const auto &src) {
// REQUIRE(HasTitle(src, "Basic class diagram example"));
REQUIRE(IsObjCInterface(src, "NSObject"));

REQUIRE(IsObjCInterface(src, "It00085"));
// REQUIRE(IsBaseClass(src, "A", "B"));
REQUIRE(IsField<Protected>(src, "It00085", "_defaultMember", "int"));
REQUIRE(IsField<Public>(src, "It00085", "_publicMember", "NSString *"));
REQUIRE(IsField<Protected>(
src, "It00085", "_protectedMember", "NSString *"));
REQUIRE(
IsField<Private>(src, "It00085", "_privateMember", "NSString *"));

REQUIRE(IsMethod<Public, Static>(
src, "It00085", "currentSharedCounter", "NSInteger"));
REQUIRE(IsMethod<Public>(
src, "It00085", "addValue", "NSInteger", "NSInteger otherValue"));

// REQUIRE(IsAssociation<Private>(src, "D", "A", "as"));

// REQUIRE(HasNote(src, "A", "left", "This is class A"));
});
}
1 change: 1 addition & 0 deletions tests/test_cases.cc
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,7 @@ void CHECK_INCLUDE_DIAGRAM(const clanguml::config::config &config,
#include "t00083/test_case.h"

#include "t00084/test_case.h"
#include "t00085/test_case.h"

///
/// Sequence diagram tests
Expand Down
3 changes: 3 additions & 0 deletions tests/test_cases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@ test_cases:
- name: t00084
title: Objective-C overall use case
description:
- name: t00085
title: Objective-C test case for various class members and methods
description:
Sequence diagrams:
- name: t20001
title: Basic sequence diagram test case
Expand Down

0 comments on commit 5316e14

Please sign in to comment.