Skip to content

Commit d988a08

Browse files
authored
Merge pull request #9 from JSchaenzle/feature/configure-cedux-and-add-tests
Feature/configure cedux and add tests
2 parents 07cf188 + 6461cab commit d988a08

File tree

13 files changed

+373
-6
lines changed

13 files changed

+373
-6
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
*.out
2+
build/

.ruby-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2.6.3

Gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
source 'https://rubygems.org'
2+
3+
gem "ceedling"

Gemfile.lock

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
GEM
2+
remote: https://rubygems.org/
3+
specs:
4+
ceedling (0.28.3)
5+
constructor (>= 1.0.4)
6+
rake (>= 0.8.7)
7+
thor (>= 0.14.5)
8+
constructor (2.0.0)
9+
rake (12.3.3)
10+
thor (0.20.3)
11+
12+
PLATFORMS
13+
ruby
14+
15+
DEPENDENCIES
16+
ceedling
17+
18+
BUNDLED WITH
19+
1.17.2

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,40 @@ To use Cedux you'll need to copy the following files into your application.
7676

7777
See example.c for a demonstration.
7878

79+
### Configuration
80+
By default, cedux has the following maximum values set:
81+
- Maximum of **16** actions can be dispatched between runs of the `cedux_run_store`
82+
- Maximum of **32** reducers can be registered
83+
- Maximum of **32** subscribers can be registered
84+
85+
However, these limitations can be altered if needed. Cedux references the following defines:
86+
- CEDUX_MAX_ACTIONS
87+
- CEDUX_MAX_REDUCERS
88+
- CEDUX_MAX_SUBSCRIBERS
89+
90+
You must define these values before `#include`ing cedux.h like so:
91+
92+
```
93+
#define CEDUX_MAX_ACTIONS 256 // Must be a factor of 2
94+
#define CEDUX_MAX_REDUCERS 128 // Must be a factor of 2
95+
#define CEDUX_MAX_SUBSCRIBERS 256 // Must be a factor of 2
96+
#include "cedux.h"
97+
```
98+
99+
As noted above, the CEXUX_MAX_X values must be factors of 2 in order for the cedux internals to work correctly.
100+
79101
### Thread Safety
80102

81103
For applications that require thread safety guarantees, Cedux provides the option to register platform-specific locking functions around the action queue so that multiple threads can dispatch actions at the time that Cedux is processing them. If you don't need thread safety in your application, you can skip this step. Use the `cedux_set_threadsafe_x` function to provide a handle to a platform-specific lock variable and wrapper functions to acquire and release the lock. Note that the lock should be initialized before passing it to Cedux. See `threadsafe_example.c` for an implementation using POSIX threads.
104+
105+
106+
## Testing
107+
This project is setup with unit testing using Ceedling (Unity and CMock)
108+
109+
### To setup testing environment
110+
1. Setup rbenv. This will ensure the expected version or ruby is used.
111+
2. Install bundler with: `gem install bundler`
112+
3. Install gems with: `bundle install`
113+
114+
### To run the tests:
115+
> `bundle exec rake test:all`

Rakefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
require "bundler"
2+
require "pathname"
3+
Bundler.setup
4+
5+
CURRENT_DIR = Pathname.new(Dir.pwd)
6+
CEEDLING_PATH = Pathname.new(Gem.loaded_specs["ceedling"].gem_dir).relative_path_from(CURRENT_DIR).to_s
7+
8+
require "ceedling"
9+
Ceedling.load_project

cedux.h

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,24 @@
55
#include "queue.h"
66
#include "list.h"
77

8+
#ifndef CEDUX_MAX_REDUCERS
9+
// Maximum number of reducers that can be registered
10+
// IMPORTANT: MUST BE A FACTROR OF TWO
11+
#define CEDUX_MAX_REDUCERS 32
12+
#endif
13+
14+
#ifndef CEDUX_MAX_SUBSCRIBERS
15+
// Maximum number of subscribers that can be registered
16+
// IMPORTANT: MUST BE A FACTROR OF TWO
17+
#define CEDUX_MAX_SUBSCRIBERS 32
18+
#endif
19+
20+
#ifndef CEDUX_MAX_ACTIONS
21+
// Maximum number of actions that can be dispatched between runs of the cedux_run_store()
22+
// IMPORTANT: MUST BE A FACTROR OF TWO
23+
#define CEDUX_MAX_ACTIONS 16
24+
#endif
25+
826
#define CEDUX_DECLARE_STORE(TREE_TYPE, ACTION_TYPE, STORE_NAME) \
927
\
1028
struct STORE_NAME##_handle; \
@@ -17,10 +35,10 @@ struct STORE_NAME##_subscriber_container
1735
void *data; \
1836
STORE_NAME##_REDUCER linked_reducer; \
1937
}; \
20-
QUEUE_TYPE_DECLARATION(STORE_NAME##_action_queue, ACTION_TYPE, 16) \
38+
QUEUE_TYPE_DECLARATION(STORE_NAME##_action_queue, ACTION_TYPE, CEDUX_MAX_ACTIONS) \
2139
QUEUE_DECLARATION(STORE_NAME##_action_queue, ACTION_TYPE) \
22-
LIST_DECLARATION(STORE_NAME##_reducer_list, STORE_NAME##_REDUCER, 32) \
23-
LIST_DECLARATION(STORE_NAME##_subscriber_list, struct STORE_NAME##_subscriber_container, 32) \
40+
LIST_DECLARATION(STORE_NAME##_reducer_list, STORE_NAME##_REDUCER, CEDUX_MAX_REDUCERS) \
41+
LIST_DECLARATION(STORE_NAME##_subscriber_list, struct STORE_NAME##_subscriber_container, CEDUX_MAX_SUBSCRIBERS) \
2442
\
2543
void cedux_register_##STORE_NAME##_reducer(struct STORE_NAME##_handle * p_store, \
2644
STORE_NAME##_REDUCER reducer); \

project.yml

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
---
2+
3+
# Notes:
4+
# Sample project C code is not presently written to produce a release artifact.
5+
# As such, release build options are disabled.
6+
# This sample, therefore, only demonstrates running a collection of unit tests.
7+
8+
:project:
9+
:use_exceptions: FALSE
10+
:use_test_preprocessor: TRUE
11+
:use_auxiliary_dependencies: TRUE
12+
:build_root: build
13+
# :release_build: TRUE
14+
:test_file_prefix: test_
15+
# :which_ceedling: vendor/ceedling
16+
:default_tasks:
17+
- test:all
18+
19+
#:release_build:
20+
# :output: MyApp.out
21+
# :use_assembly: FALSE
22+
23+
:environment:
24+
25+
:extension:
26+
:executable: .out
27+
28+
:paths:
29+
:test:
30+
- +:test/**
31+
- -:test/support
32+
:source:
33+
- +:./
34+
:support:
35+
- test/support
36+
37+
:defines:
38+
# in order to add common defines:
39+
# 1) remove the trailing [] from the :common: section
40+
# 2) add entries to the :common: section (e.g. :test: has TEST defined)
41+
:commmon: &common_defines []
42+
:test:
43+
- *common_defines
44+
- TEST
45+
:test_preprocess:
46+
- *common_defines
47+
- TEST
48+
49+
:cmock:
50+
:mock_prefix: mock_
51+
:when_no_prototypes: :warn
52+
:enforce_strict_ordering: TRUE
53+
:plugins:
54+
- :ignore
55+
- :callback
56+
:treat_as:
57+
uint8: HEX8
58+
uint16: HEX16
59+
uint32: UINT32
60+
int8: INT8
61+
bool: UINT8
62+
63+
:gcov:
64+
:html_report_type: basic
65+
66+
#:tools:
67+
# Ceedling defaults to using gcc for compiling, linking, etc.
68+
# As [:tools] is blank, gcc will be used (so long as it's in your system path)
69+
# See documentation to configure a given toolchain for use
70+
71+
# LIBRARIES
72+
# These libraries are automatically injected into the build process. Those specified as
73+
# common will be used in all types of builds. Otherwise, libraries can be injected in just
74+
# tests or releases. These options are MERGED with the options in supplemental yaml files.
75+
:libraries:
76+
:placement: :end
77+
:flag: "${1}" # or "-L ${1}" for example
78+
:common: &common_libraries []
79+
:test:
80+
- *common_libraries
81+
:release:
82+
- *common_libraries
83+
84+
:plugins:
85+
:load_paths:
86+
- "#{CEEDLING_PATH}/plugins"
87+
:enabled:
88+
- stdout_pretty_tests_report
89+
- module_generator
90+
- raw_output_report
91+
...

test/support/cedux_test_helpers.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#ifndef CEDUX_TEXT_HELPERS_H
2+
#define CEDUX_TEXT_HELPERS_H
3+
#include <stdbool.h>
4+
5+
bool mockable_reducer1(int * p_state, int action);
6+
bool mockable_reducer2(int * p_state, int action);
7+
// p_store is a void pointer here because we don't have access to the correct store handle type... We'll have to use a cast in the test when registering this.
8+
void mockable_subscriber1(void * p_store, int const * const p_state, void * p_data);
9+
void mockable_subscriber2(void * p_store, int const * const p_state, void * p_data);
10+
11+
#endif

test/test_cedux_config.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#include "unity.h"
2+
#include "mock_cedux_test_helpers.h"
3+
4+
// Define max values _before_ including cedux. Otherwise, you need to #undef and #define the values.
5+
#define CEDUX_MAX_ACTIONS 256
6+
#define CEDUX_MAX_REDUCERS 128
7+
#define CEDUX_MAX_SUBSCRIBERS 256
8+
9+
#include "cedux.h"
10+
11+
CEDUX_DECLARE_STORE(int, int, int_store);
12+
CEDUX_DEFINE_STORE(int, int, int_store);
13+
14+
void test_cedux_can_configure_max_actions(void) {
15+
16+
// Initialize a store
17+
struct int_store_handle store = cedux_init_int_store();
18+
store.tree = 0;
19+
20+
// Setup an expectation that the reducer will be called CEDUX_MAX_ACTIONS times
21+
for (int i = 0; i < CEDUX_MAX_ACTIONS; i++) {
22+
mockable_reducer1_ExpectAndReturn(&store.tree, i, true);
23+
}
24+
25+
// Register the reducer
26+
cedux_register_int_store_reducer(&store, mockable_reducer1);
27+
28+
// Dispatch CEDUX_MAX_ACTIONS. This should result in the reducer being called that many times.
29+
for (int i = 0; i < CEDUX_MAX_ACTIONS; i++) {
30+
cedux_dispatch_int_store(&store, i);
31+
}
32+
33+
// Run the store
34+
cedux_run_int_store(&store);
35+
}
36+
37+
void test_cedux_can_configure_max_reducers(void) {
38+
39+
// Initialize a store
40+
struct int_store_handle store = cedux_init_int_store();
41+
store.tree = 0;
42+
43+
// Setup an expectation that the reducer will be called CEDUX_MAX_REDUCERS times
44+
for (int i = 0; i < CEDUX_MAX_REDUCERS; i++) {
45+
mockable_reducer1_ExpectAndReturn(&store.tree, 1, true);
46+
}
47+
48+
// Register the same reducer CEDUX_MAX_REDUCERS times
49+
for (int i = 0; i < CEDUX_MAX_REDUCERS; i++) {
50+
cedux_register_int_store_reducer(&store, mockable_reducer1);
51+
}
52+
53+
// Dispatch 1 action. This should result in the reducer being called CEDUX_MAX_REDUCERS times.
54+
cedux_dispatch_int_store(&store, 1);
55+
56+
// Run the store
57+
cedux_run_int_store(&store);
58+
}
59+
60+
void test_cedux_can_configure_max_subscribers(void) {
61+
62+
// Initialize a store
63+
struct int_store_handle store = cedux_init_int_store();
64+
store.tree = 0;
65+
66+
// Expect one reducer to be called
67+
mockable_reducer1_ExpectAndReturn(&store.tree, 1, true);
68+
69+
// Expect max subscrber calls
70+
for (int i = 0; i < CEDUX_MAX_SUBSCRIBERS; i++) {
71+
mockable_subscriber1_Expect(&store, &store.tree, &i);
72+
}
73+
74+
// Register the reducer
75+
cedux_register_int_store_reducer(&store, mockable_reducer1);
76+
77+
// Register max number of subscribers
78+
for (int i = 0; i < CEDUX_MAX_SUBSCRIBERS; i++) {
79+
cedux_register_int_store_subscriber(&store, (void (*)(struct int_store_handle *, const int * const, void *))mockable_subscriber1, &i);
80+
}
81+
82+
// Dispatch 1 action. This should result in the subscriber being called CEDUX_MAX_SUBSCRIBERS times.
83+
cedux_dispatch_int_store(&store, 1);
84+
85+
// Run the store
86+
cedux_run_int_store(&store);
87+
}

0 commit comments

Comments
 (0)