/ components / esp_ringbuf / test / test_ringbuf.c
test_ringbuf.c
  1  #include <stdio.h>
  2  #include <stdlib.h>
  3  #include "freertos/FreeRTOS.h"
  4  #include "freertos/task.h"
  5  #include "freertos/queue.h"
  6  #include "freertos/semphr.h"
  7  #include "freertos/ringbuf.h"
  8  #include "driver/timer.h"
  9  #include "esp_heap_caps.h"
 10  #include "esp_spi_flash.h"
 11  #include "unity.h"
 12  #include "test_utils.h"
 13  #include "esp_rom_sys.h"
 14  
 15  //Definitions used in multiple test cases
 16  #define TIMEOUT_TICKS               10
 17  #define NO_OF_RB_TYPES              3
 18  #define ITEM_HDR_SIZE               8
 19  #define SMALL_ITEM_SIZE             8
 20  #define LARGE_ITEM_SIZE             (2 * SMALL_ITEM_SIZE)
 21  #define BUFFER_SIZE                 160     //4Byte aligned size
 22  
 23  static const uint8_t small_item[SMALL_ITEM_SIZE] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
 24  static const uint8_t large_item[LARGE_ITEM_SIZE] = { 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
 25                                                       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};
 26  static RingbufHandle_t buffer_handles[NO_OF_RB_TYPES];
 27  static SemaphoreHandle_t done_sem;
 28  
 29  static void send_item_and_check(RingbufHandle_t handle, const uint8_t *item,  size_t item_size, TickType_t ticks_to_wait, bool in_isr)
 30  {
 31      BaseType_t ret;
 32      if (in_isr) {
 33          ret = xRingbufferSendFromISR(handle, (void *)item, item_size, NULL);
 34      } else {
 35          ret = xRingbufferSend(handle, (void *)item, item_size, ticks_to_wait);
 36      }
 37      TEST_ASSERT_MESSAGE(ret == pdTRUE, "Failed to send item");
 38  }
 39  
 40  static void receive_check_and_return_item_no_split(RingbufHandle_t handle, const uint8_t *expected_data, size_t expected_size, TickType_t ticks_to_wait, bool in_isr)
 41  {
 42      //Receive item from no-split buffer
 43      size_t item_size;
 44      uint8_t *item;
 45      if (in_isr) {
 46          item = (uint8_t *)xRingbufferReceiveFromISR(handle, &item_size);
 47      } else {
 48          item = (uint8_t *)xRingbufferReceive(handle, &item_size, ticks_to_wait);
 49      }
 50      TEST_ASSERT_MESSAGE(item != NULL, "Failed to receive item");
 51      TEST_ASSERT_MESSAGE(item_size == expected_size, "Item size is incorrect");
 52      //Check data of received item
 53      for (int i = 0; i < item_size; i++) {
 54          TEST_ASSERT_MESSAGE(item[i] == expected_data[i], "Item data is invalid");
 55      }
 56      //Return item
 57      if (in_isr) {
 58          vRingbufferReturnItemFromISR(handle, (void *)item, NULL);
 59      } else {
 60          vRingbufferReturnItem(handle, (void *)item);
 61      }
 62  
 63  }
 64  
 65  static void receive_check_and_return_item_allow_split(RingbufHandle_t handle, const uint8_t *expected_data, size_t expected_size, TickType_t ticks_to_wait, bool in_isr)
 66  {
 67      //Receive item
 68      size_t item_size1, item_size2;
 69      uint8_t *item1, *item2;
 70      BaseType_t ret;
 71      if (in_isr) {
 72          ret = xRingbufferReceiveSplitFromISR(handle, (void**)&item1, (void **)&item2, &item_size1, &item_size2);
 73      } else {
 74          ret = xRingbufferReceiveSplit(handle, (void**)&item1, (void **)&item2, &item_size1, &item_size2, ticks_to_wait);
 75      }
 76      //= xRingbufferReceiveSplit(handle, (void**)&item1, (void **)&item2, &item_size1, &item_size2, ticks_to_wait);
 77      TEST_ASSERT_MESSAGE(ret == pdTRUE, "Failed to receive item");
 78      TEST_ASSERT_MESSAGE(item1 != NULL, "Failed to receive item");
 79  
 80      //Check data of received item(s) and return them
 81      if (item2 == NULL) {
 82          TEST_ASSERT_MESSAGE(item_size1 == expected_size, "Item size is incorrect");
 83          for (int i = 0; i < item_size1; i++) {
 84              TEST_ASSERT_MESSAGE(item1[i] == expected_data[i], "Item data is invalid");
 85          }
 86          //Return item
 87          if (in_isr) {
 88              vRingbufferReturnItemFromISR(handle, (void *)item1, NULL);
 89          } else {
 90              vRingbufferReturnItem(handle, (void *)item1);
 91          }
 92      } else {
 93          //Item was split
 94          TEST_ASSERT_MESSAGE(item_size1 + item_size2 == expected_size, "Total item size is incorrect");
 95          for (int i = 0; i < item_size1; i++) {
 96              TEST_ASSERT_MESSAGE(item1[i] == expected_data[i], "Head item data is invalid");
 97          }
 98          for (int i = 0; i < item_size2; i++) {
 99              TEST_ASSERT_MESSAGE(item2[i] == expected_data[item_size1 + i], "Head item data is invalid");
100          }
101          //Return Items
102          if (in_isr) {
103              vRingbufferReturnItemFromISR(handle, (void *)item1, NULL);
104              vRingbufferReturnItemFromISR(handle, (void *)item2, NULL);
105          } else {
106              vRingbufferReturnItem(handle, (void *)item1);
107              vRingbufferReturnItem(handle, (void *)item2);
108          }
109      }
110  }
111  
112  static void receive_check_and_return_item_byte_buffer(RingbufHandle_t handle, const uint8_t *expected_data, size_t expected_size, TickType_t ticks_to_wait, bool in_isr)
113  {
114      //Receive item
115      size_t item_size;
116      uint8_t *item;
117      if (in_isr) {
118          item = (uint8_t *)xRingbufferReceiveUpToFromISR(handle, &item_size, expected_size);
119      } else {
120          item = (uint8_t *)xRingbufferReceiveUpTo(handle, &item_size, ticks_to_wait, expected_size);   //Limit amount of bytes returned to the size of one item
121      }
122      TEST_ASSERT_MESSAGE(item != NULL, "Failed to receive item");
123  
124      //Check data of received item
125      for (int i = 0; i < item_size; i++) {
126          TEST_ASSERT_MESSAGE(item[i] == expected_data[i], "Item data is invalid");
127      }
128      //Return item
129      if (in_isr) {
130          vRingbufferReturnItemFromISR(handle, (void *)item, NULL);
131      } else {
132          vRingbufferReturnItem(handle, (void *)item);
133      }
134  
135      //Check if item wrapped around
136      if (item_size < expected_size) {
137          //Item is wrapped, receive second portion
138          size_t item_size2;
139          uint8_t *item2;
140          if (in_isr) {
141              item2 = (uint8_t *)xRingbufferReceiveUpToFromISR(handle, &item_size2, expected_size - item_size);
142          } else {
143              item2 = (uint8_t *)xRingbufferReceiveUpTo(handle, &item_size2, ticks_to_wait, expected_size - item_size);
144          }
145          //= (uint8_t *)xRingbufferReceiveUpTo(handle, &item_size2, ticks_to_wait, expected_size - item_size);
146          TEST_ASSERT_MESSAGE(item2 != NULL, "Failed to receive item");
147          TEST_ASSERT_MESSAGE(item_size + item_size2 == expected_size, "Total item size is incorrect");
148          for (int i = 0; i < item_size2; i++) {
149              TEST_ASSERT_MESSAGE(item2[i] == expected_data[item_size + i], "Item data is invalid");
150          }
151          if (in_isr) {
152              vRingbufferReturnItemFromISR(handle, (void *)item2, NULL);
153          } else {
154              vRingbufferReturnItem(handle, (void *)item2);
155          }
156      } else {
157          TEST_ASSERT_MESSAGE(item_size == expected_size, "Item size is incorrect");
158      }
159  }
160  
161  /* ----------------- Basic ring buffer behavior tests cases --------------------
162   * The following test cases will test basic send, receive, and wrap around
163   * behavior of each type of ring buffer. Each test case will do the following
164   * 1) Send multiple items (nearly fill the buffer)
165   * 2) Receive and check the sent items (also prepares the buffer for a wrap around
166   * 3) Send a final item that causes a wrap around
167   * 4) Receive and check the wrapped item
168   */
169  
170  TEST_CASE("Test ring buffer No-Split", "[esp_ringbuf]")
171  {
172      //Create buffer
173      RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_NOSPLIT);
174      TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer");
175      //Calculate number of items to send. Aim to almost fill buffer to setup for wrap around
176      int no_of_items = (BUFFER_SIZE - (ITEM_HDR_SIZE + SMALL_ITEM_SIZE)) / (ITEM_HDR_SIZE + SMALL_ITEM_SIZE);
177  
178      //Test sending items
179      for (int i = 0; i < no_of_items; i++) {
180          send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
181      }
182      //Test receiving items
183      for (int i = 0; i < no_of_items; i++) {
184          receive_check_and_return_item_no_split(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
185      }
186  
187      //Write pointer should be near the end, test wrap around
188      uint32_t write_pos_before, write_pos_after;
189      vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_before, NULL, NULL);
190      //Send large item that causes wrap around
191      send_item_and_check(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
192      //Receive wrapped item
193      receive_check_and_return_item_no_split(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
194      vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_after, NULL, NULL);
195      TEST_ASSERT_MESSAGE(write_pos_after < write_pos_before, "Failed to wrap around");
196  
197      //Cleanup
198      vRingbufferDelete(buffer_handle);
199  }
200  
201  TEST_CASE("Test ring buffer Allow-Split", "[esp_ringbuf]")
202  {
203      //Create buffer
204      RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_ALLOWSPLIT);
205      TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer");
206      //Calculate number of items to send. Aim to almost fill buffer to setup for wrap around
207      int no_of_items = (BUFFER_SIZE - (ITEM_HDR_SIZE + SMALL_ITEM_SIZE)) / (ITEM_HDR_SIZE + SMALL_ITEM_SIZE);
208  
209      //Test sending items
210      for (int i = 0; i < no_of_items; i++) {
211          send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
212      }
213      //Test receiving items
214      for (int i = 0; i < no_of_items; i++) {
215          receive_check_and_return_item_allow_split(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
216      }
217  
218      //Write pointer should be near the end, test wrap around
219      uint32_t write_pos_before, write_pos_after;
220      vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_before, NULL, NULL);
221      //Send large item that causes wrap around
222      send_item_and_check(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
223      //Receive wrapped item
224      receive_check_and_return_item_allow_split(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
225      vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_after, NULL, NULL);
226      TEST_ASSERT_MESSAGE(write_pos_after < write_pos_before, "Failed to wrap around");
227  
228      //Cleanup
229      vRingbufferDelete(buffer_handle);
230  }
231  
232  TEST_CASE("Test ring buffer Byte Buffer", "[esp_ringbuf]")
233  {
234      //Create buffer
235      RingbufHandle_t buffer_handle = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_BYTEBUF);
236      TEST_ASSERT_MESSAGE(buffer_handle != NULL, "Failed to create ring buffer");
237      //Calculate number of items to send. Aim to almost fill buffer to setup for wrap around
238      int no_of_items = (BUFFER_SIZE - SMALL_ITEM_SIZE) / SMALL_ITEM_SIZE;
239  
240      //Test sending items
241      for (int i = 0; i < no_of_items; i++) {
242          send_item_and_check(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
243      }
244      //Test receiving items
245      for (int i = 0; i < no_of_items; i++) {
246          receive_check_and_return_item_byte_buffer(buffer_handle, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
247      }
248  
249      //Write pointer should be near the end, test wrap around
250      uint32_t write_pos_before, write_pos_after;
251      vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_before, NULL, NULL);
252      //Send large item that causes wrap around
253      send_item_and_check(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
254      //Receive wrapped item
255      receive_check_and_return_item_byte_buffer(buffer_handle, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS, false);
256      vRingbufferGetInfo(buffer_handle, NULL, NULL, &write_pos_after, NULL, NULL);
257      TEST_ASSERT_MESSAGE(write_pos_after < write_pos_before, "Failed to wrap around");
258  
259      //Cleanup
260      vRingbufferDelete(buffer_handle);
261  }
262  
263  /* ----------------------- Ring buffer queue sets test ------------------------
264   * The following test case will test receiving from ring buffers that have been
265   * added to a queue set. The test case will do the following...
266   * 1) Ring buffer of each type is created and added to the queue set
267   * 2) A receiving task is created to select from the queue set and read from the appropriate ring buffer
268   */
269  
270  static void queue_set_receiving_task(void *queue_set_handle)
271  {
272      QueueSetHandle_t queue_set = (QueueSetHandle_t)queue_set_handle;
273  
274      //Receive multiple items via queue set
275      BaseType_t done = pdFALSE;
276      int no_of_items = BUFFER_SIZE / SMALL_ITEM_SIZE;
277      int items_rec_count[NO_OF_RB_TYPES] = {0};
278      while (done != pdTRUE) {
279          xQueueSetMemberHandle member = xQueueSelectFromSet(queue_set, TIMEOUT_TICKS);
280          //Read from selected ring buffer
281          if (xRingbufferCanRead(buffer_handles[0], member) == pdTRUE) {
282              //No-split buffer
283              receive_check_and_return_item_no_split(buffer_handles[0], small_item, SMALL_ITEM_SIZE, 0, false);
284              items_rec_count[0] ++;
285          } else if (xRingbufferCanRead(buffer_handles[1], member) == pdTRUE) {
286              //Allow-split buffer
287              receive_check_and_return_item_allow_split(buffer_handles[1], small_item, SMALL_ITEM_SIZE, 0, false);
288              items_rec_count[1] ++;
289          } else if (xRingbufferCanRead(buffer_handles[2], member) == pdTRUE){
290              //Byte buffer
291              receive_check_and_return_item_byte_buffer(buffer_handles[2], small_item, SMALL_ITEM_SIZE, 0, false);
292              items_rec_count[2] ++;
293          } else {
294              TEST_ASSERT_MESSAGE( false, "Error with queue set member");
295          }
296  
297          //Check for completion
298          if (items_rec_count[0] == no_of_items &&
299              items_rec_count[1] == no_of_items &&
300              items_rec_count[2] == no_of_items) {
301              done = pdTRUE;
302          }
303      }
304  
305      xSemaphoreGive(done_sem);
306      vTaskDelete(NULL);
307  }
308  
309  TEST_CASE("Test ring buffer with queue sets", "[esp_ringbuf]")
310  {
311      QueueSetHandle_t queue_set = xQueueCreateSet(NO_OF_RB_TYPES);
312      done_sem = xSemaphoreCreateBinary();
313  
314      //Create ring buffer of each type, then add them to a queue set
315      for (int i = 0; i < NO_OF_RB_TYPES; i++) {
316          buffer_handles[i] = xRingbufferCreate(BUFFER_SIZE, i);
317          TEST_ASSERT_MESSAGE(buffer_handles[i] != NULL, "Failed to create ring buffer");
318          TEST_ASSERT_MESSAGE(xRingbufferAddToQueueSetRead(buffer_handles[i], queue_set) == pdPASS, "Failed to add to read queue set");
319      }
320      //Create a task to send items to each ring buffer
321      int no_of_items = BUFFER_SIZE / SMALL_ITEM_SIZE;
322      xTaskCreatePinnedToCore(queue_set_receiving_task, "rec tsk", 2048, (void *)queue_set, UNITY_FREERTOS_PRIORITY + 1 , NULL, 0);
323  
324      //Send multiple items to each type of ring buffer
325      for (int i = 0; i < no_of_items; i++) {
326          for (int j = 0; j < NO_OF_RB_TYPES; j++) {
327              send_item_and_check(buffer_handles[j], small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS, false);
328          }
329      }
330  
331      xSemaphoreTake(done_sem, portMAX_DELAY);
332      vSemaphoreDelete(done_sem);
333     //Remove and delete ring buffers from queue sets
334      for (int i = 0; i < NO_OF_RB_TYPES; i++) {
335          TEST_ASSERT_MESSAGE(xRingbufferRemoveFromQueueSetRead(buffer_handles[i], queue_set) == pdTRUE, "Failed to remove from read queue set");
336          vRingbufferDelete(buffer_handles[i]);
337      }
338      vQueueDelete(queue_set);
339  }
340  
341  /* -------------------------- Test ring buffer ISR -----------------------------
342   * The following test case tests ring buffer ISR API. A timer is used to trigger
343   * the ISR. The test case will do the following
344   * 1) ISR will be triggered periodically by timer
345   * 2) The ISR will iterate through all ring buffer types where each iteration
346   *    will send then receive an item to a ring buffer.
347   */
348  
349  #define TIMER_GROUP     0
350  #define TIMER_NUMBER    0
351  #define ISR_ITERATIONS  ((BUFFER_SIZE / SMALL_ITEM_SIZE) * 2)
352  
353  intr_handle_t ringbuffer_isr_handle;
354  static int buf_type;
355  static int iterations;
356  
357  static void ringbuffer_isr(void *arg)
358  {
359      //Clear timer interrupt
360      timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_0);
361      timer_group_enable_alarm_in_isr(TIMER_GROUP_0, xPortGetCoreID());
362  
363      //Test sending to buffer from ISR from ISR
364      if (buf_type < NO_OF_RB_TYPES) {
365          send_item_and_check(buffer_handles[buf_type], (void *)small_item, SMALL_ITEM_SIZE, 0, true);
366      }
367  
368      //Receive item from ISR
369      if (buf_type == RINGBUF_TYPE_NOSPLIT) {
370          //Test receive from ISR for no-split buffer
371          receive_check_and_return_item_no_split(buffer_handles[buf_type], (void *)small_item, SMALL_ITEM_SIZE, 0, true);
372          buf_type++;
373      } else if (buf_type == RINGBUF_TYPE_ALLOWSPLIT) {
374          //Test send from ISR to allow-split buffer
375          receive_check_and_return_item_allow_split(buffer_handles[buf_type], (void *)small_item, SMALL_ITEM_SIZE, 0, true);
376          buf_type++;
377      } else if (buf_type == RINGBUF_TYPE_BYTEBUF) {
378          //Test receive from ISR for byte buffer
379          receive_check_and_return_item_byte_buffer(buffer_handles[buf_type], (void *)small_item, SMALL_ITEM_SIZE, 0, true);
380          buf_type++;
381      } else if (buf_type == NO_OF_RB_TYPES) {
382          //Check if all iterations complete
383          if (iterations < ISR_ITERATIONS) {
384              iterations++;
385              buf_type = 0;   //Reset and iterate through each buffer type again
386              return;
387          } else {
388              //Signal complete
389              BaseType_t task_woken = pdFALSE;
390              xSemaphoreGiveFromISR(done_sem, &task_woken);
391              if (task_woken == pdTRUE) {
392                  buf_type++;
393                  portYIELD_FROM_ISR();
394              }
395          }
396      }
397  }
398  
399  static void setup_timer(void)
400  {
401      //Setup timer for ISR
402      int timer_group = TIMER_GROUP;
403      int timer_idx = TIMER_NUMBER;
404      timer_config_t config;
405      config.alarm_en = 1;
406      config.auto_reload = 1;
407      config.counter_dir = TIMER_COUNT_UP;
408      config.divider = 10000;
409      config.intr_type = TIMER_INTR_LEVEL;
410      config.counter_en = TIMER_PAUSE;
411      timer_init(timer_group, timer_idx, &config);    //Configure timer
412      timer_pause(timer_group, timer_idx);    //Stop timer counter
413      timer_set_counter_value(timer_group, timer_idx, 0x00000000ULL); //Load counter value
414      timer_set_alarm_value(timer_group, timer_idx, 20); //Set alarm value
415      timer_enable_intr(timer_group, timer_idx);  //Enable timer interrupt
416      timer_set_auto_reload(timer_group, timer_idx, 1);   //Auto Reload
417      timer_isr_register(timer_group, timer_idx, ringbuffer_isr, NULL, 0, &ringbuffer_isr_handle);    //Set ISR handler
418  }
419  
420  static void cleanup_timer(void)
421  {
422      timer_disable_intr(TIMER_GROUP, TIMER_NUMBER);
423      esp_intr_free(ringbuffer_isr_handle);
424  }
425  
426  TEST_CASE("Test ring buffer ISR", "[esp_ringbuf]")
427  {
428      for (int i = 0; i < NO_OF_RB_TYPES; i++) {
429          buffer_handles[i] = xRingbufferCreate(BUFFER_SIZE, i);
430      }
431      done_sem = xSemaphoreCreateBinary();
432      buf_type = 0;
433      iterations = 0;
434      setup_timer();
435      //Start timer to trigger ISR
436      timer_start(TIMER_GROUP, TIMER_NUMBER);
437      //Wait for ISR to complete multiple iterations
438      xSemaphoreTake(done_sem, portMAX_DELAY);
439  
440      //Cleanup
441      cleanup_timer();
442      vSemaphoreDelete(done_sem);
443      for (int i = 0; i < NO_OF_RB_TYPES; i++) {
444          vRingbufferDelete(buffer_handles[i]);
445      }
446  }
447  
448  /* ---------------------------- Test ring buffer SMP ---------------------------
449   * The following test case tests each type of ring buffer in an SMP fashion. A
450   * sending task and a receiving task is created. The sending task will split
451   * a continuous piece of data into items of random length and send it to a ring
452   * buffer. The receiving task will receive and check those items.
453   * Every permutation of core pinning of the sending and receiving task will be
454   * tested.
455   */
456  
457  #define SRAND_SEED                      3   //Arbitrarily chosen srand() seed
458  #define SMP_TEST_ITERATIONS             4
459  
460  static const char continuous_data[] = {"A_very_long_string_that_will_be_split_into_"
461                                         "items_of_random_lengths_and_sent_to_the_ring_"
462                                         "buffer._The_maximum_random_length_will_also_"
463                                         "be_increased_over_multiple_iterations_in_this"
464                                         "_test"};
465  #define CONT_DATA_LEN                   sizeof(continuous_data)
466  //32-bit aligned size that guarantees a wrap around at some point
467  #define CONT_DATA_TEST_BUFF_LEN         (((CONT_DATA_LEN/2) + 0x03) & ~0x3)
468  
469  typedef struct {
470      RingbufHandle_t buffer;
471      RingbufferType_t type;
472  } task_args_t;
473  
474  static SemaphoreHandle_t tasks_done;
475  static SemaphoreHandle_t tx_done;
476  static SemaphoreHandle_t rx_done;
477  
478  static void send_to_buffer(RingbufHandle_t buffer, size_t max_item_size)
479  {
480      for (int iter = 0; iter < SMP_TEST_ITERATIONS; iter++) {
481          size_t bytes_sent = 0;      //Number of data bytes sent in this iteration
482          size_t next_item_size;      //Size of next item to send
483  
484          while (bytes_sent < CONT_DATA_LEN) {
485              //Get size of next item
486              next_item_size = rand() % (max_item_size + 1);
487              if (next_item_size + bytes_sent > CONT_DATA_LEN) {
488                  next_item_size = CONT_DATA_LEN - bytes_sent;
489              }
490  
491              //Send item
492              TEST_ASSERT_MESSAGE(xRingbufferSend(buffer, (void *)&(continuous_data[bytes_sent]), next_item_size, TIMEOUT_TICKS) == pdTRUE, "Failed to send an item");
493              bytes_sent += next_item_size;
494          }
495          xSemaphoreGive(tx_done);
496          xSemaphoreTake(rx_done, portMAX_DELAY);
497      }
498  }
499  
500  static void read_from_buffer(RingbufHandle_t buffer, RingbufferType_t buf_type, size_t max_rec_size)
501  {
502      for (int iter = 0; iter < SMP_TEST_ITERATIONS; iter++) {
503          size_t bytes_rec = 0;      //Number of data bytes received in this iteration
504          while (bytes_rec < CONT_DATA_LEN) {
505              size_t item_size, item_size2;    //Possible for allow split buffers to receive two items
506              char *item_data, *item_data2;
507  
508              //Select appropriate receive function for type of ring buffer
509              if (buf_type ==  RINGBUF_TYPE_NOSPLIT) {
510                  item_data = (char *)xRingbufferReceive(buffer, &item_size, TIMEOUT_TICKS);
511              } else if (buf_type == RINGBUF_TYPE_ALLOWSPLIT) {
512                  BaseType_t ret = xRingbufferReceiveSplit(buffer, (void **)&item_data, (void **)&item_data2, &item_size, &item_size2, TIMEOUT_TICKS);
513                  TEST_ASSERT_MESSAGE(ret == pdTRUE, "Failed to receive any item");
514              } else {
515                  item_data = (char *)xRingbufferReceiveUpTo(buffer, &item_size, TIMEOUT_TICKS, max_rec_size);
516              }
517  
518              //Check received item and return it
519              TEST_ASSERT_MESSAGE(item_data != NULL, "Failed to receive an item");
520              if (buf_type == RINGBUF_TYPE_BYTEBUF) {
521                  TEST_ASSERT_MESSAGE(item_size <= max_rec_size, "Received data exceeds max size");
522              }
523              for (int i = 0; i < item_size; i++) {
524                  //Check item_data is valid
525                  TEST_ASSERT_MESSAGE(item_data[i] == continuous_data[bytes_rec + i], "Received data is corrupted");
526              }
527              bytes_rec += item_size;
528              vRingbufferReturnItem(buffer, item_data);
529              if (buf_type == RINGBUF_TYPE_ALLOWSPLIT && item_data2 != NULL) {
530                  //Check item_data2 is valid
531                  for (int i = 0; i < item_size2; i++) {
532                      TEST_ASSERT_MESSAGE(item_data2[i] == continuous_data[bytes_rec + i], "Received split data is corrupted");
533                  }
534                  bytes_rec += item_size2;
535                  vRingbufferReturnItem(buffer, item_data2);
536              }
537          }
538          TEST_ASSERT_MESSAGE(bytes_rec == CONT_DATA_LEN, "Total length of received data is incorrect");
539          xSemaphoreGive(rx_done);
540          xSemaphoreTake(tx_done, portMAX_DELAY);
541      }
542  }
543  
544  static void send_task(void *args)
545  {
546      RingbufHandle_t buffer = ((task_args_t *)args)->buffer;
547      size_t max_item_len = xRingbufferGetMaxItemSize(buffer);
548  
549      //Test sending short length items
550      send_to_buffer(buffer, 1);
551      //Test sending mid length items
552      send_to_buffer(buffer, max_item_len/2);
553      //Test sending long length items
554      send_to_buffer(buffer, max_item_len);
555      vTaskDelete(NULL);
556  }
557  
558  static void rec_task(void *args)
559  {
560      RingbufHandle_t buffer = ((task_args_t *)args)->buffer;
561      size_t max_rec_len = xRingbufferGetMaxItemSize(buffer);
562  
563      //Test receiving short length items
564      read_from_buffer(buffer, ((task_args_t *)args)->type, 1);
565      //Test receiving mid length items
566      read_from_buffer(buffer, ((task_args_t *)args)->type, max_rec_len/2);
567      //Test receiving long length items
568      read_from_buffer(buffer, ((task_args_t *)args)->type, max_rec_len);
569  
570      xSemaphoreGive(tasks_done);
571      vTaskDelete(NULL);
572  }
573  
574  static void setup(void)
575  {
576      esp_rom_printf("Size of test data: %d\n", CONT_DATA_LEN);
577      tx_done = xSemaphoreCreateBinary();                 //Semaphore to indicate send is done for a particular iteration
578      rx_done = xSemaphoreCreateBinary();                 //Semaphore to indicate receive is done for a particular iteration
579      tasks_done = xSemaphoreCreateBinary();              //Semaphore used to to indicate send and receive tasks completed running
580      srand(SRAND_SEED);                                  //Seed RNG
581  }
582  
583  static void cleanup(void)
584  {
585      //Cleanup
586      vSemaphoreDelete(tx_done);
587      vSemaphoreDelete(rx_done);
588      vSemaphoreDelete(tasks_done);
589  }
590  
591  TEST_CASE("Test ring buffer SMP", "[esp_ringbuf]")
592  {
593      setup();
594      //Iterate through buffer types (No split, split, then byte buff)
595      for (RingbufferType_t buf_type = 0; buf_type < RINGBUF_TYPE_MAX; buf_type++) {
596          //Create buffer
597          task_args_t task_args;
598          task_args.buffer = xRingbufferCreate(CONT_DATA_TEST_BUFF_LEN, buf_type); //Create buffer of selected type
599          task_args.type = buf_type;
600          TEST_ASSERT_MESSAGE(task_args.buffer != NULL, "Failed to create ring buffer");
601  
602          for (int prior_mod = -1; prior_mod < 2; prior_mod++) {  //Test different relative priorities
603              //Test every permutation of core affinity
604              for (int send_core = 0; send_core < portNUM_PROCESSORS; send_core++) {
605                  for (int rec_core = 0; rec_core < portNUM_PROCESSORS; rec_core ++) {
606                      esp_rom_printf("Type: %d, PM: %d, SC: %d, RC: %d\n", buf_type, prior_mod, send_core, rec_core);
607                      xTaskCreatePinnedToCore(send_task, "send tsk", 2048, (void *)&task_args, 10 + prior_mod, NULL, send_core);
608                      xTaskCreatePinnedToCore(rec_task, "rec tsk", 2048, (void *)&task_args, 10, NULL, rec_core);
609                      xSemaphoreTake(tasks_done, portMAX_DELAY);
610                      vTaskDelay(5);  //Allow idle to clean up
611                  }
612              }
613          }
614  
615          //Delete ring buffer
616          vRingbufferDelete(task_args.buffer);
617          vTaskDelay(10);
618      }
619      cleanup();
620  }
621  
622  #if ( configSUPPORT_STATIC_ALLOCATION == 1 )
623  TEST_CASE("Test static ring buffer SMP", "[esp_ringbuf]")
624  {
625      setup();
626      //Iterate through buffer types (No split, split, then byte buff)
627      for (RingbufferType_t buf_type = 0; buf_type < RINGBUF_TYPE_MAX; buf_type++) {
628          StaticRingbuffer_t *buffer_struct;
629          uint8_t *buffer_storage;
630          //Allocate memory and create semaphores
631  #if CONFIG_SPIRAM_USE_CAPS_ALLOC   //When SPIRAM can only be allocated using heap_caps_malloc()
632          buffer_struct = (StaticRingbuffer_t *)heap_caps_malloc(sizeof(StaticRingbuffer_t), MALLOC_CAP_SPIRAM);
633          buffer_storage = (uint8_t *)heap_caps_malloc(sizeof(uint8_t)*CONT_DATA_TEST_BUFF_LEN, MALLOC_CAP_SPIRAM);
634  #else   //Case where SPIRAM is disabled or when SPIRAM is allocatable through malloc()
635          buffer_struct = (StaticRingbuffer_t *)malloc(sizeof(StaticRingbuffer_t));
636          buffer_storage = (uint8_t *)malloc(sizeof(uint8_t)*CONT_DATA_TEST_BUFF_LEN);
637  #endif
638          TEST_ASSERT(buffer_struct != NULL && buffer_storage != NULL);
639  
640          //Create buffer
641          task_args_t task_args;
642          task_args.buffer = xRingbufferCreateStatic(CONT_DATA_TEST_BUFF_LEN, buf_type, buffer_storage, buffer_struct); //Create buffer of selected type
643          task_args.type = buf_type;
644          TEST_ASSERT_MESSAGE(task_args.buffer != NULL, "Failed to create ring buffer");
645  
646          for (int prior_mod = -1; prior_mod < 2; prior_mod++) {  //Test different relative priorities
647              //Test every permutation of core affinity
648              for (int send_core = 0; send_core < portNUM_PROCESSORS; send_core++) {
649                  for (int rec_core = 0; rec_core < portNUM_PROCESSORS; rec_core ++) {
650                      esp_rom_printf("Type: %d, PM: %d, SC: %d, RC: %d\n", buf_type, prior_mod, send_core, rec_core);
651                      xTaskCreatePinnedToCore(send_task, "send tsk", 2048, (void *)&task_args, 10 + prior_mod, NULL, send_core);
652                      xTaskCreatePinnedToCore(rec_task, "rec tsk", 2048, (void *)&task_args, 10, NULL, rec_core);
653                      xSemaphoreTake(tasks_done, portMAX_DELAY);
654                      vTaskDelay(5);  //Allow idle to clean up
655                  }
656              }
657          }
658  
659          //Delete ring buffer
660          vRingbufferDelete(task_args.buffer);
661  
662          //Deallocate memory
663          free(buffer_storage);
664          free(buffer_struct);
665          vTaskDelay(10);
666      }
667      cleanup();
668  }
669  #endif
670  
671  /* -------------------------- Test ring buffer IRAM ------------------------- */
672  
673  static IRAM_ATTR __attribute__((noinline)) bool iram_ringbuf_test(void)
674  {
675      bool result = true;
676  
677      RingbufHandle_t handle = xRingbufferCreate(CONT_DATA_TEST_BUFF_LEN, RINGBUF_TYPE_NOSPLIT);
678      result = result && (handle != NULL);
679      spi_flash_guard_get()->start(); // Disables flash cache
680      xRingbufferGetMaxItemSize(handle);
681      vRingbufferDelete(handle);
682      spi_flash_guard_get()->end(); // Re-enables flash cache
683  
684      return result;
685  }
686  
687  TEST_CASE("Test ringbuffer functions work with flash cache disabled", "[esp_ringbuf]")
688  {
689      TEST_ASSERT( iram_ringbuf_test() );
690  }